From 867b0ed78fa34b9d566af5095ba5e9bc71659f12 Mon Sep 17 00:00:00 2001 From: Stefan Ritt Date: Fri, 21 Dec 2001 15:14:04 +0000 Subject: [PATCH] Initial revision SVN revision: 2 --- COPYING | 339 ++ README.txt | 10 + build.bat | 13 + doc/CHANGELOG.TXT | 238 + doc/adminguide.html | 253 ++ doc/config.html | 682 +++ doc/dl-menu.html | 13 + doc/dl-sig.html | 6 + doc/download.html | 16 + doc/elog.css | 23 + doc/elog.gif | Bin 0 -> 88263 bytes doc/elog_thumb.jpg | Bin 0 -> 35766 bytes doc/faq.html | 155 + doc/index.html | 163 + doc/theme1.jpg | Bin 0 -> 26839 bytes doc/theme2.jpg | Bin 0 -> 26321 bytes doc/userguide.html | 269 ++ elog.c | 533 +++ elog.nsi | 152 + elogd.c | 8273 +++++++++++++++++++++++++++++++++++ elogd.cfg | 13 + eloghelp_en.html | 56 + eloghelp_fr.html | 66 + eloghelp_ge.html | 63 + eloglang.french | 139 + eloglang.german | 135 + logbooks/demo/011108.log | 15 + themes/default/elog.gif | Bin 0 -> 3060 bytes themes/default/first.gif | Bin 0 -> 941 bytes themes/default/first.psp | Bin 0 -> 5237 bytes themes/default/last.gif | Bin 0 -> 935 bytes themes/default/last.psp | Bin 0 -> 5605 bytes themes/default/next.gif | Bin 0 -> 890 bytes themes/default/next.psp | Bin 0 -> 4414 bytes themes/default/oldtheme.cfg | 37 + themes/default/previous.gif | Bin 0 -> 893 bytes themes/default/previous.psp | Bin 0 -> 4071 bytes themes/default/theme.cfg | 37 + 38 files changed, 11699 insertions(+) create mode 100755 COPYING create mode 100755 README.txt create mode 100755 build.bat create mode 100755 doc/CHANGELOG.TXT create mode 100755 doc/adminguide.html create mode 100755 doc/config.html create mode 100755 doc/dl-menu.html create mode 100755 doc/dl-sig.html create mode 100755 doc/download.html create mode 100755 doc/elog.css create mode 100755 doc/elog.gif create mode 100755 doc/elog_thumb.jpg create mode 100755 doc/faq.html create mode 100755 doc/index.html create mode 100755 doc/theme1.jpg create mode 100755 doc/theme2.jpg create mode 100755 doc/userguide.html create mode 100755 elog.c create mode 100755 elog.nsi create mode 100755 elogd.c create mode 100755 elogd.cfg create mode 100755 eloghelp_en.html create mode 100755 eloghelp_fr.html create mode 100755 eloghelp_ge.html create mode 100755 eloglang.french create mode 100755 eloglang.german create mode 100755 logbooks/demo/011108.log create mode 100755 themes/default/elog.gif create mode 100755 themes/default/first.gif create mode 100755 themes/default/first.psp create mode 100755 themes/default/last.gif create mode 100755 themes/default/last.psp create mode 100755 themes/default/next.gif create mode 100755 themes/default/next.psp create mode 100755 themes/default/oldtheme.cfg create mode 100755 themes/default/previous.gif create mode 100755 themes/default/previous.psp create mode 100755 themes/default/theme.cfg diff --git a/COPYING b/COPYING new file mode 100755 index 00000000..a43ea212 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/README.txt b/README.txt new file mode 100755 index 00000000..85605343 --- /dev/null +++ b/README.txt @@ -0,0 +1,10 @@ +Welcome to the ELOG package +=========================== + +For general documentation, see the file "index.html" in the "doc" subdirectory, +for installation instructions, see "adminguide.html" in the same directory. + + +Stefan Ritt +Dec. 2001 + \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100755 index 00000000..421d003b --- /dev/null +++ b/build.bat @@ -0,0 +1,13 @@ +@echo on +rem Usage: build_elog like "build 100" +rem Build ELOG distribution + +set version=%1 + +rem map network drive +net use m: /d > nul +net use m: \\pc2075\midas mi_das /user:midas + +"\program files\NSIS\makensis" /DVERSION=%version% elog.nsi + +copy elog%version%.exe m:\html\elog\download\ diff --git a/doc/CHANGELOG.TXT b/doc/CHANGELOG.TXT new file mode 100755 index 00000000..cdf768b8 --- /dev/null +++ b/doc/CHANGELOG.TXT @@ -0,0 +1,238 @@ +Version 1.3.2, released December xxth, 2001 +=========================================== + +- Added spaces around message number in find result page to make + link wider +- Added "preset = %03d" for automatic index generation + +Version 1.3.1, released December 14th, 2001 +=========================================== + +- Display URL links in search result page +- Fixed a few missing string translations +- Left-align text about attachments on edit page +- Fixed concatenation of lines in password file on password change, + thanks to Michael Buselli +- Added support for crypt() function +- Made "Required Attributes" working with "MOptions" +- Make relative paths work for "Data dir" and "Password file" + + +Version 1.3.0, released December 13th, 2001 +=========================================== + +- Changed global "Welcome page" to "Selection page" +- Removed most compiler warnings with "-Wall" +- Removed DWORD (needed for True64 Unix) +- Added localization support, use "language =" in [global] +- Added French and German files +- Added Per-Session cookies (when expiration = 0) +- Fixed major bug with memcpy(rem_addr,...) +- Fixed memory leak with ss_file_find +- "Entry date" is now formatted according to "Date format" +- Added "MOptions" for multi-valued attributes +- Added "HTML default" option + +Version 1.2.6, released December 4th, 2001 +========================================== + +- Fixed bug with "resubmit as new entry" and deleted attachments +- Fixed bug where "submit as HTML" got deleted on resubmit +- Display locked attributes as text only (no input field) +- Added FAQ about "Last 10" page as default logbook page +- Added -h flag for multi-homed hosts; thanks to Scott Erickson +- Fixed problem with "Date format" in short listings + +Version 1.2.5, released November 21st, 2001 +=========================================== + +- Removed "Revision date:" from edit form (should better be done via an + attribute, preset to $date) +- Fixed bug with "Welcome page" +- Added "resubmit as new entry" checkbox + +Version 1.2.4, released November 20th, 2001 +=========================================== + +- Added "date format" option +- Put "copy to" and "move to" under administrative password +- String "" now gets correctly converted into a hot link +- Use "comment =" string instead of logbook in page title +- Logout takes you back to logbook selection page +- Made http:////?cmd=Last&= working +- Added submit button also at bottom of page +- Added "Enable browsing" flag +- Implemeted default options in [global] section of elogd.cfg +- Fixed a bug that $logbook substitution did not work in email subject +- Added "Use Email from = " +- Made "bottom text" work with query page +- Added option "find menu commands" +- Added command "last x" to "find menu commands" +- Last x gets now multiplied by 2 each time instead of added one +- Added more spaces in attachment display of find result page + +Version 1.2.3, released November 15th, 2001 +=========================================== + +- Implemented quotation marks for "email " in case + attribute or value contains blanks +- Produce error if submitted attachment is too large +- Fixed crash when nonexisting logbook in URL +- Changed "options = " into + "Preset = " +- Added "Preset text = | " +- Added "Locked attributes" +- Added "ftp://" and "mailto:" as clickable links +- Added FAQs in documentation + +Version 1.2.2, released November 14th, 2001 +=========================================== + +- Added User level access control with login/logout and password file +- Added "Remove on reply" +- Added "options = $short_name etc." +- Fixed wrong numbering when searching in several logbooks +- Display "no entries found" in search page +- Added "subst " option +- Cookies are now stored in parameter list +- Email notification for logbooks with blanks now use %20 for blanks to + make link working in email +- Search result page puts links on number instead of dates +- Commands wich are not in the menu list cannot be executed now +- Added "Reverse sort" flag +- Added "Page title" in [global] +- Added "Show text" flag for attribute-only logbooks +- Made elogd.c compile under Cygwin +- Fixed bug that "p" was not working any more +- Changed "Email =" to "Email =" +- Added "main tab" in [global] +- Added "bottom text" option +- Added $logbook variable +- Added "number attachments" option + +Version 1.2.1, released November 6th, 2001 +========================================== + +- Fixed problem with long configuration files (use POST instead GET) +- Fixed bug with delete command and admin. password +- Fixed bug with reply/edit/delete commands which always used last message +- Change "use for title" into "page title", introduced attribute substitution +- Added attribute substitution to "use email subject" +- Changed elog program to deal with arbitrary attributes + +Version 1.2.0, released October 19th, 2001 +========================================== + +- added arbitrary attributes instead of fixed "author", "type", "category" and + "subject" +- logbook entries can now be moved or copied between logbooks, if configured in + elogd.cfg +- Added "Hosts allow" and "Hosts deny" for access control +- Any attribute or several attributes can be used for the "subject" field in + email notifications +- Any attribute can be used as a title for the web page +- A comment can be displayed above the text entry box +- A site-specifig URL can be defined for the Help button, can also point + directly to a file in the configuration directory, which gets served + by elogd +- On the search page, the result can be sorted in reverse order +- On full-text searches, the pattern is highlighted in the result +- If a "types" entry is present in elogd.cfg, a help page is displayed for + upgrading the configuration file +- Added "boolean" attributes which are represented by a checkbox +- Added "Filtered browsing = 0/1" flag +- Added "Display search" list +- Added "HELO" statement in SMTP communication, thanks to David Monk. +- Added "Welcome title" for logbook selection page +- Moved "SMTP host" to [global] section +- Added "Display email recipients = 0/1" +- Added "Suppress default = 2" for no checkbox at all +- Added "Show attachments" flag +- Added global and logbook "Welcome page" +- Added "Submit page" +- Added "Email message body" flag +- Added "config" menu to edit elogd.cfg over the web +- Changed "delete password" into "admin password" which is now used + both to delete messages and to config elogd.cfg + +Version 1.1.3, released October 12th, 2001 +========================================== + +- Various problems fixed with read passwords: + o First page of single logbook was always displayed even with read password + o Without "-k" flag, some browsers were hanging on password page +- Fixed CRLF problem with some SMTP hosts (courtesy of Michael Jones) +- Fixed a bug in the elog submission program which added a wrong line + at the end of a message +- Fixed bug which caused the "find" function to display only one entry if + a file xyx.log is present in the data directory + +Version 1.1.2, released October 9th, 2001 +========================================= + +- Implemented keep-alive of connection. This speeds up considerably especially + when browsing through a logbook. It could be that with the Opera browser + there are some problems. In this case, start elogd with the "-k" flag to + turn keep-alive off. + +Version 1.1.1, released August 31th, 2001 +========================================= + +- Fixed various bugs to make Elog work with the Opera browser + +Version 1.1.0, released August 28th, 2001 +========================================= + +- Added "themes" +- Added tabs for logbook selection +- Added graphical browse buttons +- Added "first" browse button +- Menu can be switched from buttons to links +- Replaced "query" by more appropriate "find" +- Search can span multiple logbooks +- Search can produce "printable output", which is optimized for printing +- Search has no default start date, which means that the whole logbook is + searched by default +- Configuration and themes file get re-read at each access + +Version 1.0.5, released August 8th, 2001 +======================================== + +- Added separate password for message deletion +- Added expiration time for passwords +- Added "suppress Email notification" for message entry + +Version 1.0.4, released August 7th, 2001 +======================================== + +- Fixed bug where Email notification only got sent to "Email All" +- Fixed bug with wrong display of logbook list if no logbook is + specified +- Categories and Types were mixed if several logbooks are used. Fixed. + +Version 1.0.3, released August 2nd, 2001 +======================================== + +- Added email facility, please refer to the documentation +- Added "Authors =" option in configuration file +- Added "Summary lines =" option in configuration file +- Fields in "New" page can be pre-populated by using pauthor=xxx etc. in the URL +- Added "back" button on certain pages + +Version 1.0.2, released July 26, 2001 +===================================== +- Added URL = flag in elgod.cfg file and update documentation accourdingly + to support secure connections over STUNNEL and SSH + +Version 1.0.1, released July 24, 2001 +===================================== + +- Use subject as page title, useful when bookmarking pages +- Display text with "http://xxx" in logbook pages as active links +- Increase password expiration from 1h to 1d for computers with daylight + savings time incorrectly set + +Version 1.0.0, released June 20, 2001 +===================================== + +Initial revision \ No newline at end of file diff --git a/doc/adminguide.html b/doc/adminguide.html new file mode 100755 index 00000000..8c3fe3e3 --- /dev/null +++ b/doc/adminguide.html @@ -0,0 +1,253 @@ + + + ELOG Administrator's Guide + + + + + + + + + + +
  ELOG Administrator's Guide  
+

+

+

+

+

+

How to set up and run your very own ELOG server
+ +
+
  Installing and running on UNIX  
+

+ELOG is being developed and tested under Linux, and is being used on (Sparc) Solaris +workstations. It should also compile and run on other UNIX flavours. +

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

+Expand the compressed TAR file with tar -xzvf elog-x.x.x.tar.gz. +This creates a subdirectory elog-x.x.x where x.x.x is the +version number. In that directory execute make, which +creates the executables elogd and elog +. These executables can then be copied to a convenient place like +/usr/local/bin or ~/bin. + +The elogd executable can be started manually for testing with : +

+

    elogd -p 8080
+

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

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

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

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

+

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

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

+To test the daemon, connect to your host via : +

+

    http://your.host:8080/
+

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

+

    http://your.host/<logbook>
+

+where <logbook> is the name of the logbook. +

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

+To start the daemon automatically, it can be run from the /etc/rc.d/init.d +system. Please consult your distribution to find out how to do that. +
+Under RedHat 6.x. I use a script which I put under /etc/rc.d/init.d. Then I call : +

    chkconfig --add elogd
+

+ +


Notes for the Solaris platform:

+ +

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

+

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

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

+ +


+
  Installing and running in Windows  
+

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

+Download the latest elogxxx.exe file and execute it. The installer puts the ELOG system into a directory you specify and adds some menu +shortcuts. With these shortcuts, the daemon elogd.exe can be started directly and the demo logbook can be accessed with the browser. Under Windows, the ports below 1024 can be used without restriction. So if no web server is running on the same PC the ELOG daemon listens under the standard +Web port 80. +

+The "-D" (daemon) command line option only works under UNIX. To start +elogd as a service under Windows NT/2000, you can do one of the following : +

+

    +
  • use the srvany.exe and instsrv.exe from the Windows +Resource Kit. If you don't have the Resource Kit, these programs can probably +be downloaded from various places like this one. Then follow these steps: +

    +

      +
    1. Copy SRVANY.EXE to %window%\system32\. + +
    2. Use INSTSRV to install SRVANY.EXE as a service named "elogd":

      +

      INSTSRV ELogd %windir%\system32\srvany.exe
      + +
    3. Run REGEDIT and find "My Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\elogd". + +
    4. Under the Elogd registry key, create a new key called Parameters via Edit/New/Key. +
    5. Under the new key Parameters, create the following three STRING values: +
        +
      • Application set to "c:\elog\elogd.exe" (or wherever you installed the elogd program) +
      • AppDirectory set to "c:\elog" (or wherever you installed the elogd file) +
      • AppParameters set to "-p 8080" (or any other port which you would like to use, + put an empty string for port 80) +
      + +
    6. Under NT4, open the Services entry in the control panel, under Windows 2000 right-click + on "My Computer", click to "Manage", select "Services and Applications", expand it and select + "Services". On the right panel, double-click "ELogd". Then start the service manually and + change the startup type to "Automatic". This way the elogd program gets started automatically + on every reboot. +
    +

    +

  • better still, use the free FireDaemon utility. Make sure elogd is not running, then start the FireDaemonUI application, and enter the following fields: +

    +

    +Service name: ELogd
    +Service description: "Electronic Logbook Daemon"
    +Startup type: Automatic
    +Interact with desktop: off
    +Start immediately: on
    +Auto restart application: off
    +Application working directory: c:\program files\elog\ (or wherever you installed elog)
    +Application executable: c:\program files\elog\elogd.exe (or wherever you installed elogd.exe)
    +
    +and keep the other options as they are. After pressing "OK" the daemon should be started immediately. +
+

+ +


+
  Server Configuration  
+

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

+This file may be edited directly from the file system, or from a form in the ELOG Web interface (when the Config menu item is available). In this case, changes are applied dynamically without having to restart the server. +

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

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

+ +


+
  Secure Connections HOWTO  
+

+Although the elogd program does not support secure +connections over SSL directly, it is possible to use it together with +stunnel to do so. Three steps are +necessary: +

    +
  1. Start elogd. If another Web server is running +on port 80, use a different port. +

    +

  2. Install the stunnel program if not already done + and start it with: +

    +

    stunnel -d 443 -r your.host.name:80
    + Substitute the port 80 if you started elogd on another port. This + causes stunnel to receive requests on the https port 443 and forward them to the elogd port. +

    +

  3. Connect to your logbook with a browser which supports SSL via: +

    +

    https://your.host.name/
    +
+

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

+

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

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

+ +


+
  How It All Works  
+

+For the technically curious : +

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

+The "database" in which ELOG saves its entries is in plain ASCII format. One file is +created for each day in the form YYMMDD.log (where +YY is the year, MM the month and DD the day). The only non-ASCII character +is a form-feed at the end of each message. If a log file is sent directly +to a printer, each message is therefore printed on a separate page. Note +that the log files should not be edited manually, since this could invalidate +the internal database structure (each header contains the message length +in bytes, which would then become incorrect). +

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

+ +


+
+ + + diff --git a/doc/config.html b/doc/config.html new file mode 100755 index 00000000..63ef5a55 --- /dev/null +++ b/doc/config.html @@ -0,0 +1,682 @@ + + + ELOG - Syntax of elogd.cfg + + + + + + + + + + +
  ELOG - Syntax of elogd.cfg  
+

+

+

+

+

+

Global and individual logbook options for an ELOG server
+ +
+

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

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

+

    +[global]
    +Logbook tabs = 1
    +Tab cellpadding = 2
    +SMTP host = mailsend.your.domain
    +
    +[Linux]
    +Theme = default
    +Comment = General linux tips and tricks
    +Data dir = /usr/local/elog/linux
    +Attributes = Author, Type, Category, Subject
    +Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other
    +Options Category = General, Hardware, Software, Network, Account, Other
    +Options Author = Stefan, Linus, unknown
    +Required Attributes = Author
    +
    +[PC]
    +Comment = Database PC installations
    +Data dir = /usr/local/elog/pc
    +Attributes = Location, OS, Owner
    +Options Location = Building1, Building2
    +Options OS = Linux, Windows ME, Windows 2000
    +Required Attributes = Location, Owner
    +Email All = name@address, othername@otheraddress
    +Mail Subject = Location
    +
+

+ +


+
  Global options  
+

+The following options are specific to the [global] section: +

+

    + +
  • Language = <name> +
    +The language setting determines the language of the elogd output. Not affected by this +setting are the configuration file options (they are always in English) and the +attribute names as well as the optional Menu commands. These have to be +translated manually in the configuration file. +

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

    +The online help for elogd is contained in the file eloghelp_xx.html where +xx are the first two letters of the language (like "en", "ge" and "fr"). For new languages, a new file of that type must be created as well. +

    + +

  • Tab cellpadding +
    +Specifies the empty space between the name in the logbook tab and its frame, in pixels. +

    + +

  • Logbook Tabs = 0/1 +
    +This flag controls the display of "tabs" on top of the logbook page which +allow to quickly switch between logbooks. +

    + +

  • Main Tab = <string> +
    +If this option is present, an additional first tab is displayed which takes you back +to the main logbook selection page. The string is used for the +contents of the tab. +

    + +

  • Welcome Title +
    +This optional HTML code gets displayed in the title of the logbook selection +page. It can contain images via <img src="welcome.gif">. +These images must be in the same directory as the elodg.cfg file. +The following line is an example Welcome Title: +

    +

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

    +This displays an image and a text below. +

    + +

  • Page title = <string> +
    +The string specified here is used for the title of the logbook selection page. It is also +used by most browsers for bookmark names. +

    + +

  • Selection page = <file> +
    +When this option is present, a user defined file is displayed instead of the logbook +selection page. This file must be in the same directory as the elogd +file. It can be completely customized in order to contain logos etc. As a template, +the standard selection page produced by elogd can be used. +

    + +

  • SMTP host +
    +This defines the SMTP host needed to send automatic email notifications. The host +name you can get from your email program or your local system administrator. +

    + +

+ +
+
  Individual logbook options  
+

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

+ +Here are the available options, by broad categories:

+ +


+
  General options  
+ +
+ +
+
  Attributes  
+ +
    +
  • Attributes = <list> +
    +Define a number of attributes for the logbook. A maximum of 20 attributes can be +defined. Typical values are "Author", "Subject" or "Type". +

    + +

  • Options <attribute> = <list> +
    +Usually, an text field is used for an attribute, where the user can fill in +text of up to 100 characters. If instead a drop-down box with preset items is +better for a given attribute, these items can be defined with this statement. +Up to 100 items can be defined, separated by commas. +

    + +

  • MOptions <attribute> = <list> +
    +This list allows for "Multiple Options", meaning that an attribute can have +several values simultaneously. When entering an entry with MOptions, each value +from the list is represented by a checkbox. Unlike with normal options, multiple +checkboxes can be checked for an entry. The attribue value then becomes +

    +

    +<value1> | <value2> | ...
    +
    +In the "find" page only one of these values can be specified, which is then +treated as a substring in the search filter. +

    + +

  • Options <attribute> = boolean +
    +If an attribute is marked "boolean" this way, a checkbox is displayed for +this attribute. +

    + +

  • Preset <attribute> = <string> +
    +This option uses a preset string for an attribute. The string can contain +subsitutions like the ones described under the "Subst <attribute>" +command. One possible application is to use the login name for the author +field like: +

    +

    +Preset Author = $long_name
    +
    +If the attribute should be locked at the Web submission, use the +"Locked Attributes = ..." option. If a preset value is given for an +attribute which has an options list, the preset value is selected in the drop +down box by default. +

    + +

  • Preset text = <string> or <file> +
    +This preset value is used for the main body text. It can be a string or a file, +which must be present in the same directory as the elogd file. +

    + +

  • Locked Attributes = <list> +
    +The attributes specified here cannot be modified when a new entry is submitted. +This makes only sense for preset attributes. +

    + +

  • Required Attributes = <list> +
    +The attributes specified here are required when a new entry is submitted. The +attribute names are marked with * on the entry form. +

    + +

  • Page title = <string> +
    +The string specified here is used for the title of the web page. It is also +used by most browsers for bookmark names. The string can contain substitutions +as described unter the "Subst <attribute>" option. +

    + +

  • Display search = <list> +
    +The display of attributes in a search result table can be restricted only to +certain attributes listed here. This can be helpful is many attributes are defined +in a logbook, which usually makes the table too big to fit in the browser. +

    + +

  • Subst <attribute> = <string> +
    +When submitting logbook entries, attribute values can be substituted by some +text. This text can contain arbitrary fixed text and following values: +

    +

      +
    • $<attribute>: The entered value of the attribute itself +
    • $host: The host name where elogd is running +
    • $remote_host: The host name of the host from with the entry was submitted +
    • $short_name: The login name (if password file is present) +
    • $long_name: The full name (if password file is present) +
    • $logbook: The name of the current logbook +
    • $date: The current date, formatted via "Date format" +
    +

    +Following example use this feature to add the remote host name to the author: +

    +Subst Author = $author from $remote_host +

    + +

  • Remove on reply = <list> +
    +This option clears one or more (separated by commata) attribute values from a logbook +entry when creating a reply to that entry. This can make sense for example for +the author, since the author of a reply can be different from the original author. +

    + +

+ +

+
  Access control  
+

+Reading and writing into logbooks can be constrained using two different access methods, +either with global passwords for read, write and admin (config, delete), or with user- +level passwords. For these two schemes to work properly, cookies have to be turned +on in your browser. Please consult your browser documentation about how to do that. +

+ +

    +
  • Read password +
  • Write password +
  • Admin password +
  • Write password expiration = <hours> +
  • Admin password expiration = <hours> +
+

+These optional password statements define passwords for reading and writing to the +logbook, to delete entries in the logbook and to configure a logbook via the Config +menu. The passwords are stored in an encoded form. To change them, use elogd +directly with the -r , -w and -a + flags. To set the write password of logbook "linux" to "test", enter: +

+

    elogd -w test -l linux
+

+The read password is queried by the browser with a pop-up window and usually stays +active for the entire browser session. The write and admin passwords are stored in +cookies on the browser side and have an expiration time of 24h. After that time, the +passwords have to be re-submitted. This time can be changed with the statement +Write Password Expiration = x or Admin Password Expiration += x, where x is the expiration time in hours. It should be noted +that on some systems the daylight savings time is calculated incorrectly, which can +cause time offsets of one hour between a server PC and a client PC. In this case one +hour must be added to the expiration time. If the expiration is set to "0", the +passwords are kept for the current browser session only. When the browser is restarted, +the password must be re-entered. +

+

    +
  • Password file = <file> +
  • Login expiration = <hours> +
+

+An alternative to the read/write/admin passwords is the user level access with a +password file. This file contains user names and passwords in following format: +

+

    <login name1>:<password1>:<full name1>
    +<login name2>:<password2>:<full name2>
    +<login name3>:<password3>:<full name3>
    +...
+

+The passwords are encoded. To create or change them, use the "Change +Password" menu point. To create a new user, edit the password file +manually and use an empty password (like "<login name>::<full name>"), which can then be changed via the "Change password" menu point. +

+The presence of a password file requires all users to "log in" using their name and +password. An additional advantage of this method is that the user name can be used as +an attribute value for creating logbook entries. For example, the following line could be +added to the configuration file to fill in the Author attribute with the user name: +

+

    Options Author = $long_name from $remote_host
+

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

+The user name and password are stored as cookies on the user side. They expire after 24 +hours by default, which can be changed with the Login expiration +options. If the expiration is set to "0", the passwords are kept for the current browser +session only. When the browser is restarted, the password must be re-entered. +

+

    +
  • Allow <command> = <user list> +
+

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

+

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

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

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

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

+ +


+
  EMail notification  
+ +
    +
  • Email <attribute> <value> = <list> +
  • Use Email Subject = <string> +
  • Use Email From = <string> +
+

+To send email automatically when new entries are created in a logbook, a SMTP host = + entry must be present in the [global] section +of the configuration file. To submit an email based on an attribute value, use the statement Email <attribute> <value> = +<list>. Whenever an entry is submitted where +attribute is equal to value, an email +notification is sent to the email addresses in list. +Several mail addresses may be supplied, separated by commas. Multiple +Email xxx statements may occur in a configuration +file. If either the attribute or the value contains one or more blanks the string +must be enclosed with quotation marks, as in: +

+

    Email type "Normal routine" = ...
+

+The statement Email All = <list> sends an +email notification independent of the type and category. The Use +Email Subject = <string> statement specifies which text is +used as the email subject. The text can contain $<attribute> +statements which are substituted with the current value of that +attribute. For a full list of possible substitutions, see the +"Subst <attribute>" option. The option Use Email From = <string> + is used for the "from" field in the email. +

+ +


+
  Flags  
+ +
    +
  • Show text = 0/1 +
    +This flag controls if logbook entries contain a body text. If an installation +only requires attributes, this flag can be set to 0. Default is +1. +

    + +

  • Show attachments = 0/1 +
    +This flag controls the display of attachments such as images on normal +logbook pages. For logbooks with large images, this flag can be turned off, +so that attachments are only displayed when they are clicked on. Default +is 1. +

    + +

  • Summary on default = 0/1 +
    +If this flag is 1, the buttons "Last day" and "Last 10" +produce summary listings instead of displaying full messages. In the "Find" +page, the "Summary only" check box is checked by default. +

    + +

  • Summary lines = x +
    +This specifies the number of text lines displayed in a summary page. Zero displays +no text at all. The default is 3. +

    + +

  • Reverse sort = 0/1 +
    +If this flag is 1, the result of the "Last day" and "Last 10" queries +is sorted in reverse order (newest entry down to oldest). Default is 0.

    + +

  • Enable browsing = 0/1 +
    +If this flag is 1, browsing (hitting the next/previous button) is enabled. +For some rare occasions it might be necessary to disable browsing. Default is +1. +

    + +

  • Filtered browsing = 0/1 +
    +If this flag is 1, browsing (hitting the next/previous button) can be +filtered by individual attributes. If the checkbox next to an attribute is checked, +only messages with the same attribute value are displayed. Default is 1. +

    + +

  • HTML default = 0/1/2 +
    +This specifies the default state of the "Submit as HTML text" button on the +new message entry from. For installations where entries are normally submitted +as HTML, the default can be set to 1. If this value is set to 2 +, the check box is not displayed at all, so that only text submissions +are possible. The default is 0. +

    + +

  • Suppress default = 0/1/2 +
    +This specifies the default state of the "Suppress Email notification" button on the +new message entry form. For installations where normally an email notification is +not necessary, the default can be set to 1. If an important entry is +entered, users can then uncheck the suppress box. If this value is set to 2 +, the suppress box is not displayed at all, so that an email notification is +always produced. The default is 0. +

    + +

  • Resubmit default = 0/1/2 +
    +This specifies the default state of the "Resubmit as new entry" button on the +edit message entry from. If this button is checked, the current message is removed +from its current position in the database and submitted as a new message. This +can for example be useful for applications where users want to see which records +have been updated recently. If this value is set to 2, the resubmit box +is not displayed at all. The default is 0. +

    + +

  • Display Email recipients = 0/1 +
    +If this flag is 1, the email recipients are displayed when a logbook +entry is entered which produces an email notification. Setting this flag to 0 +suppresses this display, in case users need not see that email is being sent and to whom. +The default is 1. +

    + +

  • Email message body = 0/1 +
    +If this flag is 1, the email notification send by elog contains the +full message body in addition to the attributes. The default is 0. +

    + +

  • Back to main = 0/1 +
    +If this flag is 1, the "Back" button takes you back to the logbook +selection page instead to the last entry of the current logbook. +The default is 0. +

    + +

+ +


+
  Themes  
+

+Themes are layout and color schemes which determine the look and feel of a logbook. +Each theme resides in a separate subdirectory and is specified with the +theme = <dir> option in the configuration file. The directory +contains the file theme.cfg and a few images, which are used +for the title banner and the browse buttons. A default theme is contained in the +distribution. If new themes are developed by users, they can be sent back to the author, +to be included in future releases. +

+Most of the options in the theme.cfg are self-explanatory. The colors +are coded as #RRGGBB, where RR is a 8-bit hexadecimal value for the red part, GG for the +green and BB for the blue part, repectively. Since theme configuration files are re-read +at each access, the elogd daemon does not have to be restarted after a change in the +theme configuration file. +

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

+   +

+ +


+
+ + diff --git a/doc/dl-menu.html b/doc/dl-menu.html new file mode 100755 index 00000000..63818c27 --- /dev/null +++ b/doc/dl-menu.html @@ -0,0 +1,13 @@ + +
  ELOG download page  
+

+

+

+

ELOG source code and Windows binaries
diff --git a/doc/dl-sig.html b/doc/dl-sig.html new file mode 100755 index 00000000..39998ab6 --- /dev/null +++ b/doc/dl-sig.html @@ -0,0 +1,6 @@ + + diff --git a/doc/download.html b/doc/download.html new file mode 100755 index 00000000..df137cb7 --- /dev/null +++ b/doc/download.html @@ -0,0 +1,16 @@ + + + ELOG download page + + + + + + + + + + + + + diff --git a/doc/elog.css b/doc/elog.css new file mode 100755 index 00000000..c32f1998 --- /dev/null +++ b/doc/elog.css @@ -0,0 +1,23 @@ + diff --git a/doc/elog.gif b/doc/elog.gif new file mode 100755 index 0000000000000000000000000000000000000000..10f203dbc48b2b9f27f90d537f2ec06ea4c4042b GIT binary patch literal 88263 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%0D=Ya;|D9w(dzp()GFU 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%0Db6;t`WLEF?a$h_i@d6a#9w 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�or21#S(*|_>ZYTLDrKv#z6xus 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 literal 0 HcmV?d00001 diff --git a/doc/elog_thumb.jpg b/doc/elog_thumb.jpg new file mode 100755 index 0000000000000000000000000000000000000000..e081f34747ad5005af0ba3693e1cc333a0745494 GIT binary patch literal 35766 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 literal 0 HcmV?d00001 diff --git a/doc/faq.html b/doc/faq.html new file mode 100755 index 00000000..f3e52a3c --- /dev/null +++ b/doc/faq.html @@ -0,0 +1,155 @@ + + + ELOG FAQ + + + + + + + + + + +
  ELOG FAQ  
+

+

+

+

Frequently Asked Questions about usage and administration
+
+ +
    + +
  • Are there any plans to implement a MySQL back end? +

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

    + +

  • Can I run the ELOG daemon from inside Apache or any other Web server? +

    +No. The ELOG daemon was designed as a standalone server and it will stay like that in the future. The reason for that is that elogd should not rely on any other software. This is for example important for many people running elogd under Windows, and they have no clue how to install Apache for Windows. The installation and maintenance for elogd therefore becomes much simpler. The disadvantage of this solution is that elogd needs to run on a different port than 80 in case it runs in parallel with a Web server. +

    + +

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

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

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

    + +

  • I want a bookmark pointing to the last page where an attribute has a certain value +

    +Use the URL: +

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

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

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

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

    + +

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

    +There is a trick for that: Define two logbooks, but with the same data directory. The first one has no passwords, but a restricted set of menu commands. The second logbook has a read and/or write password or user level access and a full menu (including edit, delete, etc.). So the public uses the first logbook, while the adminitrator uses the second logbook. Here is an example configuration file: +

    +

    +[global]
    +logbook tabs = 0
    +
    +[Public]
    +data dir = c:\elog
    +Comment = Public access
    +Attributes = Author, Type, Category, Subject
    +Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other
    +Options Category = General, Hardware, Software, Network, Other
    +Menu commands = Find, Last day, Last 10, Help
    +
    +[Restricted]
    +Data dir = c:\elog
    +Comment = Access only for administrator
    +Password file = passwd.txt
    +Attributes = Author, Type, Category, Subject
    +Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other
    +Options Category = General, Hardware, Software, Network, Other
    +Menu commands = New, Edit, Delete, Reply, Find, Last day, Last 10, Config, Change Password, Logout, Help
    +
    +Note the "logbook tabs = 0" which "hides" the restricted logbook so that public users don't get confused. +

    + +

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

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

    + +

  • By default, ELOG displays the last entry from a logbook. How can I configure it to +display the last 10 messages instead? +

    +There is a simple trick. You use the "Welcome page" option in the elogd file +to redirect the start page to the last 10 command. Put the statement: +

    +

    +Welcome page = redir.html
    +
    +into elogd and create that file with following contents: +

    +

    +<head>
    +<meta http-equiv="refresh" content="0; URL=/logbook/last10">
    +</head>
    +
    +This causes the welcome page being redirected to /logbook/last10, where logbook must be replaced by the current logbook name. Following other redirections are possible: +

    +

    +/logbook/last20                         for the last 20 messages and so on
    +/logbook/past1                          for the last day
    +/logbook/past2                          for the last two days and so on
    +/logbook?cmd=New                        for the new message entry form
    +/logbook?cmd=Find                       for the "find" page
    +/logbook?cmd=Search&<attrib>=<value>    for a search with <attrib>=<value>
    +
    + +
  • What new features are planned for future releases ? +

    +Here are some "wishlist" items requested by users. I will work down the list as time permits. If you find +a feature you find useful on the list, there is a good chance that it will appear in a future release. You can vote for a feature, or suggest one, by sending me an email. +

    + +

    + + + + + + + + + + + +
    FeatureVotes
    Get message from standard input in elog submission program1
    Users can only edit their own entries1
    Make user file editable through web interface1
    Implement groups of users1
    Add email address field to user file, so that user name can be + specified for "Email <attribute> <value> =" option + which gets automatically substituted by email address1
    Change internal database format from offset oriented to + index oriented (lots of work, need also conversion program)2
    Implement new listing mode in between "summary only" and "full listing" + where a few text lines are displayed below the attribute fields1

    + +

+ +
+ + + diff --git a/doc/index.html b/doc/index.html new file mode 100755 index 00000000..b1f36ebe --- /dev/null +++ b/doc/index.html @@ -0,0 +1,163 @@ + + + The ELOG Home Page + + + + + + + + + + +
  The ELOG Home Page  
+

+

+

+

+

Home of the Electronic Logbook package by Stefan Ritt

+
  Current version is : 1.3.2  
+ +
+
  What is ELOG ?  
+

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

+

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

    +

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

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

+

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

    +

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

+ +


+
  Screen shots  
+

+ + +
+elog.gif + +On the left upper panel is a typical logbook page displayed by Netscape Navigator. Each logbook page can contain attachments in a similar way to emails. This makes it possible to store images or text files and retrieve them easily. You could for example attach a sample configuration file which can later be copied to the local machine with the "Save As..." function of the Web browser. +

+Several logbooks can be served though a single ELOG server. Each +logbook can use different attributes for its entries. The logbook can then be searched using these attributes. The right pane on the left image shows a search for all +entries with attribute "Type" equal to "Configuration", and the lower pane +shows the search result. It is also possible to use full-text search in +attributes and the entry body. +

+While logbook entries are usually displayed one entry per page, they can +also be listed consecutively which makes it easy to produce a paper printout of a logbook. +

+Logbook pages can be edited or deleted. This feature can be turned off in +the configuration file so that a logbook entries cannot be changed +after being submitted. +

+An additional feature is the automatic generation of a notification email +messages based on a certain type or category of a logbook entry. +

+Also try out the online demo ! +

+

+ +


+
  Use cases  
+

+The features of ELOG make it useful for several applications: +

+ +


+
  License  
+

+ELOG is released under the +GNU Public License. +

+ +


+
  Links  
+

+See also the ELOG project pages on + +SourceForge +and + +Freshmeat + - + +Visits to this page since 23 May 2001:   + + +

+ +


+ + + diff --git a/doc/theme1.jpg b/doc/theme1.jpg new file mode 100755 index 0000000000000000000000000000000000000000..76736c6b734b27b43bb09fb794b318f1b201b76b GIT binary patch literal 26839 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

K4pkbK4o`nl?E^Cml-`- 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 literal 0 HcmV?d00001 diff --git a/doc/theme2.jpg b/doc/theme2.jpg new file mode 100755 index 0000000000000000000000000000000000000000..78521abf1c4ab6660f7db55e9a9a3b7a810f334f GIT binary patch literal 26321 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 Bl7s*N literal 0 HcmV?d00001 diff --git a/doc/userguide.html b/doc/userguide.html new file mode 100755 index 00000000..87daf3bf --- /dev/null +++ b/doc/userguide.html @@ -0,0 +1,269 @@ + + + ELOG User's Guide + + + + + + + + + + +

  ELOG User's Guide  
+

+

+

+

+

+

How to get the most from your ELOG server
+ +
+
 A Quick Intro 
+

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

+

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

    +

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

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

+

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

    +

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

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

+

+ +


+
 What Words Mean Here 
+

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

+

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

    +

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

    +

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

    +

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

+ +


+
 Accessing an ELOG server and its logbook(s) 
+

+Usually you will be given a URL (through mail, a Web page etc.) to access the ELOG server. +

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

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

+Note : it is generally not recommended to link directly to a specific entry in a logbook by its URL, because under certain circumstances such addresses might change. It is possible to achieve the same result with a "search" URL (see below). +

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

+ +


+
 Viewing information in ELOG 
+

+There are two main viewing modes in a logbook : +

+

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

    +

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

      +

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

      +

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

      +

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

      +

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

      +

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

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

    +

    +

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

    +

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

      +

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

      +

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

      +

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

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

    +
+

+ +


+
 Browsing around and finding things 
+

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

+

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

    +

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

    +

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

+ +


+
 Adding stuff to a logbook 
+

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

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

+

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

      +

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

      +

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

      +

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

      +

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

      +

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

      +

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

      +

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

      +

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

      +

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

      +

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

    +

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

      +

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

    +

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

+ +


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

+

    + +
  • as mentioned above, it is not recommended to link directly to a specific entry by its URL (from another entry or an external Web page). However, it is easy enough to achieve the same result with "search URLs" : use the "Search" form to compose a query that will result in exactly what you want (either a single entry or a list of entries). Copy the URL for that result page from your browser, and use that as the target for your link. +

    + +

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

    + +

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

    + +

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

    + +

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

    +

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

      +

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

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

    +

    + +

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

    +

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

    + +

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

    +

      +elog -h hostname [-p port] [-l logbook]
      +     [-w password]
      +     [-u username password]
      +     -a <attribute1>=<value1>
      +     -a <attribute2>=<value2>
      +     ...
      +     -f attachment1
      +     -f attachment2
      +     ...
      +     -m textfile | text
      +
    +The password must match the write password defined on the server. If +user level access is defined on the server (via the "password file =" option), +a valid user name / password pair must be specified with the -u option. +

    +Multiple attributes can be specified as well as several attachments. +The message text can be supplied directly at the command line or submitted +from a file with the -m flag. +

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

    + +

+

+


+
+ + + diff --git a/elog.c b/elog.c new file mode 100755 index 00000000..4cac020f --- /dev/null +++ b/elog.c @@ -0,0 +1,533 @@ +/********************************************************************\ + + Name: elog.c + Created by: Stefan Ritt + + Contents: Electronic logbook utility + + $Log$ + Revision 1.1 2001/12/21 15:14:04 midas + Initial revision + + Revision 1.23 2001/12/21 12:33:04 midas + Fixed bug with single logbook and user authentication, thanks to Glenn Horton-Smith + + Revision 1.22 2001/12/05 10:02:50 midas + Fixed -Wall compiler warning + + Revision 1.21 2001/12/05 09:58:57 midas + Fixed small bug + + Revision 1.20 2001/11/15 11:49:46 midas + Added -u flag for user name/password + + Revision 1.19 2001/11/14 15:35:37 midas + Fix error under Cygwin with O_BINARY + + Revision 1.18 2001/11/06 15:52:17 midas + Added general attributes + + Revision 1.17 2001/10/12 08:40:02 midas + Fixed bug when submitting text files and an additional (garbage) lines was + added + + Revision 1.16 2001/06/19 10:55:40 midas + Variable number of attachments, revised attachment editing + + Revision 1.15 2001/05/23 13:15:34 midas + Fixed bug with category + + Revision 1.14 2001/05/23 13:04:19 midas + Added password functionality + + Revision 1.13 2001/05/23 10:48:21 midas + elog can be used with elogd now + + Revision 1.12 2000/09/01 15:49:26 midas + Elog is now submitted via mhttpd + + Revision 1.1 1999/09/14 15:15:27 midas + Initial revision + + +\********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#include +#else +#include +#include +#include +#include +#include +#include +#define closesocket(s) close(s) +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#endif + +typedef int INT; + +#define MAX_ATTACHMENTS 10 +#define NAME_LENGTH 100 +#define MAX_N_ATTR 20 + +int verbose; + +/*------------------------------------------------------------------*/ + +char *map= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void base64_encode(char *s, char *d) +{ +unsigned int t, pad; + + pad = 3 - strlen(s) % 3; + while (*s) + { + t = (*s++) << 16; + if (*s) + t |= (*s++) << 8; + if (*s) + t |= (*s++) << 0; + + *(d+3) = map[t & 63]; + t >>= 6; + *(d+2) = map[t & 63]; + t >>= 6; + *(d+1) = map[t & 63]; + t >>= 6; + *(d+0) = map[t & 63]; + + d += 4; + } + *d = 0; + while (pad--) + *(--d) = '='; +} + +/*------------------------------------------------------------------*/ + +void sgets(char *string, int size) +{ +char *p; + + do + { + p = fgets(string, size, stdin); + } while (p == NULL); + + if (strlen(p) > 0 && p[strlen(p)-1] == '\n') + p[strlen(p)-1] = 0; +} + +/*------------------------------------------------------------------*/ + +char request[600000], response[10000], content[600000]; + +INT submit_elog(char *host, int port, char *experiment, char *passwd, + char *uname, char *upwd, + char attrib_name[MAX_N_ATTR][NAME_LENGTH], + char attrib[MAX_N_ATTR][NAME_LENGTH], + int n_attr, + char *text, + char afilename[MAX_ATTACHMENTS][256], + char *buffer[MAX_ATTACHMENTS], + INT buffer_size[MAX_ATTACHMENTS]) +/********************************************************************\ + + Routine: submit_elog + + Purpose: Submit an ELog entry + + Input: + char *host Host name where ELog server runs + in port ELog server port number + char *passwd Write password + char *uname User name + char *upwd User password + int run Run number + char *attrib_name Attribute names + char *attrib Attribute values + char *text Message text + + char afilename[] File names of attachments + char *buffer[] Attachment contents + INT buffer_size[] Size of buffer in bytes + + Function value: + EL_SUCCESS Successful completion + +\********************************************************************/ +{ +int status, sock, i, n, header_length, content_length; +struct hostent *phe; +struct sockaddr_in bind_addr; +char host_name[256], boundary[80], str[80], *p; + +#if defined( _MSC_VER ) + { + WSADATA WSAData; + + /* Start windows sockets */ + if ( WSAStartup(MAKEWORD(1,1), &WSAData) != 0) + return -1; + } +#endif + + /* get local host name */ + gethostname(host_name, sizeof(host_name)); + + phe = gethostbyname(host_name); + if (phe == NULL) + { + perror("Cannot retrieve host name"); + return -1; + } + phe = gethostbyaddr(phe->h_addr, sizeof(int), AF_INET); + if (phe == NULL) + { + perror("Cannot retrieve host name"); + return -1; + } + + /* if domain name is not in host name, hope to get it from phe */ + if (strchr(host_name, '.') == NULL) + strcpy(host_name, phe->h_name); + + /* create socket */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror("cannot create socket"); + return -1; + } + + /* compose remote address */ + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = 0; + bind_addr.sin_port = htons((unsigned short)port); + + phe = gethostbyname(host); + if (phe == NULL) + { + perror("cannot get host name"); + return -1; + } + memcpy((char *)&(bind_addr.sin_addr), phe->h_addr, phe->h_length); + + /* connect to server */ + status = connect(sock, (void *) &bind_addr, sizeof(bind_addr)); + if (status != 0) + { + printf("Cannot connect to host %s, port %d\n", host, port); + return -1; + } + + if (verbose) + printf("Successfully connected to host %s, port %d\n", host, port); + + /* compose content */ + strcpy(boundary, "---------------------------7d0bf1a60904bc"); + strcpy(content, boundary); + strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSubmit\r\n"); + + if (uname[0]) + sprintf(content+strlen(content), + "%s\r\nContent-Disposition: form-data; name=\"unm\"\r\n\r\n%s\r\n", boundary, uname); + + if (upwd[0]) + { + base64_encode(upwd, str); + sprintf(content+strlen(content), + "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n", boundary, str); + } + + if (experiment[0]) + sprintf(content+strlen(content), + "%s\r\nContent-Disposition: form-data; name=\"exp\"\r\n\r\n%s\r\n", boundary, experiment); + + for (i=0 ; i 0) + { + i = recv(sock, response+n, 10000, 0); + if (i > 0) + n += i; + } + response[n] = 0; + + closesocket(sock); + + if (verbose) + { + printf("Response received:\n"); + puts(response); + } + + /* check response status */ + if (strstr(response, "302 Found")) + printf("Message successfully transmitted\n"); + else if (strstr(response, "Logbook Selection")) + printf("No logbook specified\n\n"); + else if (strstr(response, "Enter password")) + printf("Missing or invalid password\n"); + else if (strstr(response, "login")) + printf("Missing or invalid user name/password\n"); + else + printf("Error transmitting message\n"); + + return 1; +} + +/*------------------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ +char str[1000], text[10000], uname[80], upwd[80]; +char host_name[256], logbook[32], textfile[256], password[80]; +char *buffer[MAX_ATTACHMENTS], attachment[MAX_ATTACHMENTS][256]; +INT att_size[MAX_ATTACHMENTS]; +INT i, n, fh, n_att, n_attr, size, port; +char attr_name[MAX_N_ATTR][NAME_LENGTH], attrib[MAX_N_ATTR][NAME_LENGTH]; + + text[0] = textfile[0] = uname[0] = upwd[0] = 0; + host_name[0] = logbook[0] = password[0] = n_att = n_attr = 0; + port = 80; + for (i=0 ; i= argc || argv[i+1][0] == '-') + goto usage; + if (argv[i][1] == 'h') + strcpy(host_name, argv[++i]); + else if (argv[i][1] == 'p') + port = atoi(argv[++i]); + else if (argv[i][1] == 'l') + strcpy(logbook, argv[++i]); + else if (argv[i][1] == 'w') + strcpy(password, argv[++i]); + else if (argv[i][1] == 'u') + { + strcpy(uname, argv[++i]); + strcpy(upwd, argv[++i]); + } + else if (argv[i][1] == 'a') + { + strcpy(str, argv[++i]); + if (strchr(str, '=')) + { + strcpy(attrib[n_attr], strchr(str, '=')+1); + *strchr(str, '=') = 0; + strcpy(attr_name[n_attr], str); + n_attr++; + } + else + { + printf("Error: Attributes must be supplied in the form \"-a =\".\n"); + return 0; + } + } + else if (argv[i][1] == 'f') + strcpy(attachment[n_att++], argv[++i]); + else if (argv[i][1] == 'm') + strcpy(textfile, argv[++i]); + else + { + usage: + printf("\nusage: elog -h [-p port]\n"); + printf(" [-l logbook/experiment]\n"); + printf(" [-v] for verbose output\n"); + printf(" [-w password] write password defined on server\n"); + printf(" [-u username password] user name and password\n"); + printf(" [-f ] (up to %d times)\n", MAX_ATTACHMENTS); + printf(" -a = (up to %d times)\n", MAX_N_ATTR); + printf(" -m ] | \n"); + printf("\nArguments with blanks must be enclosed in quotes\n"); + printf("The elog message can either be submitted on the command line\n"); + printf("or in a file with the -m flag. Multiple attributes and attachments\n"); + printf("can be supplied\n"); + return 0; + } + } + else + strcpy(text, argv[i]); + } + } + + if (host_name[0] == 0 || + n_attr == 0 || + (text[0] == 0 && textfile[0] == 0)) + goto usage; + + /*---- open text file ----*/ + + if (textfile[0]) + { + fh = open(textfile, O_RDONLY | O_BINARY); + if (fh < 0) + { + printf("Message file \"%s\" does not exist.\n", textfile); + return 0; + } + + size = lseek(fh, 0, SEEK_END); + lseek(fh, 0, SEEK_SET); + + if (size > sizeof(text)-1) + { + printf("Message file \"%s\" is too long (%d bytes max).\n", textfile, sizeof(text)); + return 0; + } + + memset(text, 0, sizeof(text)); + i = read(fh, text, size); + if (i < size) + { + printf("Cannot fully read message file \"%s\".\n", textfile); + return 0; + } + + close(fh); + } + + /*---- open attachment file ----*/ + + for (i = 0 ; i 500*1024) + { + printf("Attachment file \"%s\" is too long (500k max).\n", attachment[i]); + return 0; + } + + n = read(fh, buffer[i], att_size[i]); + if (n < att_size[i]) + { + printf("Cannot fully read attachment file \"%s\".\n", attachment[i]); + return 0; + } + buffer[i][n] = 0; + + close(fh); + } + + /* now submit message */ + submit_elog(host_name, port, logbook, password, + uname, upwd, + attr_name, attrib, n_attr, text, + attachment, buffer, att_size); + + for (i=0 ; i = %03d" + + Revision 1.96 2001/12/14 14:24:45 midas + Couple of changes, see CHANGELOG + + Revision 1.95 2001/12/14 12:48:44 midas + Fixed concatenation of lines in password file, thanks to Michael Buselli + + Revision 1.94 2001/12/14 11:47:01 midas + Display active URLs also in find page + + Revision 1.93 2001/12/13 13:05:18 midas + Added missing "reply" etc. translations + + Revision 1.92 2001/12/12 16:22:25 midas + Added "HTML default" option, fixed "reply" link display + + Revision 1.91 2001/12/12 16:01:18 midas + Fixed wrong help file name and preceeding " | " for MOptions + + Revision 1.90 2001/12/11 13:41:31 midas + Added "MOptions" multiple options + + Revision 1.89 2001/12/11 10:20:58 midas + Changed the way "language" is interpreted, no need to restart elogd after + language change any more. + + Revision 1.88 2001/12/11 09:39:28 midas + Format "entry date" according to "date format" + + Revision 1.87 2001/12/11 09:14:51 midas + Added elog language files + + Revision 1.86 2001/12/10 09:18:09 midas + Call tzset() only once + + Revision 1.85 2001/12/10 09:06:57 midas + Fixed memeory leak with ss_file_find + + Revision 1.84 2001/12/07 13:02:45 midas + Fixed crash with language file in Unix format + + Revision 1.83 2001/12/07 11:41:19 midas + Made "move to" and "copy to" work with other languages + + Revision 1.82 2001/12/07 10:05:30 midas + Fixed major bug with memcpy(rem_addr...), many thanks to Frank Schnell! + + Revision 1.81 2001/12/07 09:15:37 midas + Added Per-Session cookies (when expriation == 0) + + Revision 1.80 2001/12/06 15:47:24 midas + First version of localization + + Revision 1.79 2001/12/06 09:22:36 midas + Fixed bug with "full_name" + + Revision 1.78 2001/12/05 16:01:05 midas + Removed DWORD, changed (int)p1-(int)p2 to (int)(p1-p2) for True64 + + Revision 1.77 2001/12/05 10:37:09 midas + Removed most -Wall compiler warnings + + Revision 1.76 2001/12/04 14:43:53 midas + Changed global "Welcome page" into "Selection page" + + Revision 1.75 2001/12/04 12:13:21 midas + Fixed "Date format" problem in short listings + + Revision 1.74 2001/12/04 08:53:03 midas + Fixed bugs with resubmit, display locked attributes as text only, added + -h flag for multi-homes hosts thanks to Scott Erickson + + Revision 1.73 2001/11/21 16:26:29 midas + Removed "Revision date:" from edit form (should better be done via an + attribute, preset to $date) + + Revision 1.72 2001/11/21 15:23:48 midas + Fixed bug with "Welcome page" + + Revision 1.71 2001/11/21 15:03:35 midas + Added "resubmit as new entry" checkbox + + Revision 1.70 2001/11/21 13:31:47 midas + Made "date format" also work in find result page + + Revision 1.69 2001/11/21 08:21:42 midas + Specify %20 in menu commands explicitly + + Revision 1.68 2001/11/20 16:14:37 midas + Corrected "data:" format in sendmail + + Revision 1.67 2001/11/20 12:24:36 midas + Add more spaces between attachments in find result page + + Revision 1.66 2001/11/20 09:16:33 midas + Added "Last x" in "find menu commands" + + Revision 1.65 2001/11/19 17:14:25 midas + Made "bottom text" work with find page and added "find menu commands" + + Revision 1.64 2001/11/19 11:14:45 midas + Added "default" options, "use email from" and fixed a few small bugs + + Revision 1.63 2001/11/16 15:29:36 midas + First fine-tuning for GNU software directory + + Revision 1.62 2001/11/15 16:08:07 midas + Logout takes you back to the logbook selection page + + Revision 1.61 2001/11/15 11:54:25 midas + Version 1.2.3 + + Revision 1.60 2001/11/14 10:51:02 midas + Fixed bug with resubmission of attachments + + Revision 1.59 2001/11/14 08:58:56 midas + Version 1.2.2 + + Revision 1.58 2001/11/13 14:17:09 midas + More new features + + Revision 1.57 2001/11/12 15:22:27 midas + Fixed bug with admin password and checking of allowed commands + + Revision 1.56 2001/11/12 15:08:58 midas + Changes made in hospital + + Revision 1.55 2001/11/06 15:52:02 midas + Fixed small bug + + Revision 1.54 2001/11/05 16:31:27 midas + Added "page title" and changed "use email subject" + + Revision 1.53 2001/11/05 14:44:17 midas + Fixed problem with long configuration files + + Revision 1.52 2001/11/05 12:37:36 midas + Version 1.2.1 + + Revision 1.51 2001/11/05 12:36:15 midas + Added for Unix include files + + Revision 1.50 2001/11/05 12:33:30 midas + Fixed bugs concerning edit/reply/delete and admin password + + Revision 1.49 2001/10/31 11:12:02 midas + Changes made at home... + + Revision 1.48 2001/10/19 14:36:59 midas + Added "Display email recipients", "suppress default = 2", moved SMTP host to + [global] + + Revision 1.47 2001/10/19 11:34:07 midas + Added "Welcome title" for logbook selection page + + Revision 1.46 2001/10/19 10:33:02 midas + Added "boolean" attributes, "filtered browsing" and "display search" + + Revision 1.45 2001/10/18 13:06:32 midas + Added attributes, IP checking, reverse search, copy/move, highlight in search + + Revision 1.44 2001/10/12 10:48:27 midas + Version 1.1.3 + + Revision 1.43 2001/10/12 10:46:05 midas + Fixed bug which caused problems with the "find" function + + Revision 1.42 2001/10/12 07:18:47 midas + - Fixed CRLF problem with sendmail, thanks to Michael Jones + - Fixed problem that first logbook page can be viewed even with read PW + - Fixed problem that passwords only worked with -k flag + + Revision 1.41 2001/10/09 10:39:55 midas + Version 1.1.2 + + Revision 1.40 2001/10/05 13:35:50 midas + Implemented keep-alive + + Revision 1.39 2001/08/30 10:58:02 midas + Fixed bugs with Opera browser + + Revision 1.38 2001/08/29 07:53:49 midas + Use smaller font for printable search result display + + Revision 1.37 2001/08/28 11:41:49 midas + Optimized attachment display + + Revision 1.36 2001/08/28 11:36:18 midas + Treat configuration directory correctly + + Revision 1.35 2001/08/28 10:42:58 midas + Extract path for theme file from configuration file + + Revision 1.34 2001/08/28 10:20:59 midas + Changed default colors + + Revision 1.33 2001/08/28 08:32:00 midas + Major changes for revision 1.1.0 + + Revision 1.32 2001/08/14 09:37:22 midas + Mail notification did not contain host name. Fixed. + + Revision 1.31 2001/08/09 13:51:42 midas + Added "suppress default" flag + + Revision 1.30 2001/08/09 07:44:31 midas + Changed from absolute pathnames to relative pathnames + + Revision 1.29 2001/08/08 14:26:44 midas + Fixed bug which crashes server on long (>80 chars) attachments + + Revision 1.28 2001/08/08 11:02:11 midas + Added separate password for message deletion + Authors are displayed in a drop-down box on the query page + A button "suppress Email notification" has been added + + Revision 1.27 2001/08/07 13:26:27 midas + Increase size of author, type and cat. list to 100 + + Revision 1.26 2001/08/07 11:01:44 midas + Fixed bugs with strtok + + Revision 1.25 2001/08/07 08:03:40 midas + Fixed bug in el_retrieve in decoding attachments + + Revision 1.24 2001/08/07 07:10:03 midas + Fixed problem with emails + + Revision 1.23 2001/08/06 12:44:15 midas + Fixed small bug preventing the selection list for multiple logbooks + + Revision 1.22 2001/08/03 12:06:12 midas + Fixed bug where categories were wrong when multiple logbooks are used + + Revision 1.21 2001/08/03 06:44:48 midas + Changed mail notification author from "name@host" to "name from host" + + Revision 1.20 2001/08/02 12:30:00 midas + Fixed unix bug + + Revision 1.19 2001/08/02 12:26:43 midas + Fine-tunes mail facility, fixed a few bugs + + Revision 1.18 2001/08/02 11:45:47 midas + Added mail facility and author list + + Revision 1.17 2001/07/26 10:37:32 midas + Removed "status" button on query result + + Revision 1.16 2001/07/26 10:25:54 midas + Fixed bug which omitted logbook name if only one is defined in elogd.cfg + + Revision 1.15 2001/07/26 09:31:07 midas + Added URL = entry in elogd.cfg to support secure connections over stunnel + + Revision 1.14 2001/07/24 10:46:09 midas + Use subject as title, make http://xxx text aktive links + + Revision 1.13 2001/07/06 12:34:13 midas + Incresed cookie expiration from 1h to 1d + + Revision 1.12 2001/06/20 08:33:14 midas + Added "summary on last" + + Revision 1.11 2001/06/19 10:55:40 midas + Variable number of attachments, revised attachment editing + + Revision 1.10 2001/06/15 09:13:30 midas + Display "<" and ">" correctly + + Revision 1.9 2001/06/15 08:49:19 midas + Fixed bug when query gave no results if no message from yesterday + + Revision 1.8 2001/05/23 13:15:34 midas + Fixed bug with category + + Revision 1.7 2001/05/23 13:06:03 midas + Added "allow edit" functionality + + Revision 1.6 2001/05/23 10:48:48 midas + Added version + + Revision 1.5 2001/05/23 08:16:41 midas + Fixed bug when POST request comes in two blocks + + Revision 1.4 2001/05/22 11:32:59 midas + Added Help button + + Revision 1.3 2001/05/22 09:13:47 midas + Rearranged configuration file + + Revision 1.2 2001/05/21 15:28:45 midas + Implemented multiple logbooks + + Revision 1.1 2001/05/18 13:31:33 midas + Initial revision + +\********************************************************************/ + +/* Version of ELOG */ +#define VERSION "1.3.2" + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define OS_WINNT + +#define DIR_SEPARATOR '\\' +#define DIR_SEPARATOR_STR "\\" + +#define snprintf _snprintf + +#include +#include +#include +#else + +#define OS_UNIX + +#define TRUE 1 +#define FALSE 0 + +#define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_STR "/" + +#define __USE_XOPEN /* needed for crypt() */ + +typedef int BOOL; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define closesocket(s) close(s) +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#endif + +typedef int INT; + +#define TELL(fh) lseek(fh, 0, SEEK_CUR) + +#define NAME_LENGTH 256 +#define SUCCESS 1 + +#define EL_SUCCESS 1 +#define EL_FIRST_MSG 2 +#define EL_LAST_MSG 3 +#define EL_FILE_ERROR 4 + +#define WEB_BUFFER_SIZE 2000000 + +char return_buffer[WEB_BUFFER_SIZE]; +int strlen_retbuf; +int keep_alive; +char header_buffer[1000]; +int return_length; +char host_name[256]; +char elogd_url[256]; +char elogd_full_url[256]; +char logbook[256]; +char logbook_enc[256]; +char data_dir[256]; +char cfg_file[256]; +char cfg_dir[256]; +char tcp_hostname[256]; + +#define MAX_GROUPS 32 +#define MAX_PARAM 100 +#define MAX_ATTACHMENTS 10 +#define MAX_N_LIST 100 +#define MAX_N_ATTR 50 +#define VALUE_SIZE 256 +#define PARAM_LENGTH 256 +#define TEXT_SIZE 50000 +#define MAX_PATH_LENGTH 256 + +char _param[MAX_PARAM][PARAM_LENGTH]; +char _value[MAX_PARAM][VALUE_SIZE]; +char _text[TEXT_SIZE]; +char *_attachment_buffer[MAX_ATTACHMENTS]; +INT _attachment_size[MAX_ATTACHMENTS]; +struct in_addr rem_addr; +INT _sock; +BOOL verbose, use_keepalive; + +char *mname[] = { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +}; + +char type_list[MAX_N_LIST][NAME_LENGTH] = { + "Routine", + "Other" +}; + +char category_list[MAX_N_LIST][NAME_LENGTH] = { + "General", + "Other", +}; + +char author_list[MAX_N_LIST][NAME_LENGTH] = { + "" +}; + +/* attribute flags */ +#define AF_REQUIRED (1<<0) +#define AF_LOCKED (1<<1) +#define AF_MULTI (1<<2) +#define AF_FIXED (1<<3) + +char attr_list[MAX_N_ATTR][NAME_LENGTH]; +char attr_options[MAX_N_ATTR][MAX_N_LIST][NAME_LENGTH]; +int attr_flags[MAX_N_ATTR]; + +char attr_list_default[][NAME_LENGTH] = { + "Author", + "Type", + "Category", + "Subject", + "" +}; + +char attr_options_default[][MAX_N_LIST][NAME_LENGTH] = { + { "" }, + { "Routine", "Other" }, + { "General", "Other" }, + { "" } +}; + +int attr_flags_default[] = { + AF_REQUIRED, + 0, + 0, + 0 +}; + + +struct { + char ext[32]; + char type[32]; + } filetype[] = { + + { ".JPG", "image/jpeg" }, + { ".GIF", "image/gif" }, + { ".PNG", "image/png" }, + { ".PS", "application/postscript" }, + { ".EPS", "application/postscript" }, + { ".HTML", "text/html" }, + { ".HTM", "text/html" }, + { ".XLS", "application/x-msexcel" }, + { ".DOC", "application/msword" }, + { ".PDF", "application/pdf" }, + { ".TXT", "text/plain" }, + { ".ASC", "text/plain" }, + { ".ZIP", "application/x-zip-compressed" }, + { "" }, +}; + +int scan_attributes(char *logbook); + +/*---- Funcions from the MIDAS library -----------------------------*/ + +BOOL equal_ustring(char *str1, char *str2) +{ + if (str1 == NULL && str2 != NULL) + return FALSE; + if (str1 != NULL && str2 == NULL) + return FALSE; + if (str1 == NULL && str2 == NULL) + return TRUE; + + while (*str1) + if (toupper(*str1++) != toupper(*str2++)) + return FALSE; + + if (*str2) + return FALSE; + + return TRUE; +} + +/*-------------------------------------------------------------------*/ + +void strsubst(char *string, char name[][NAME_LENGTH], char value[][NAME_LENGTH], int n) +/* subsitute "$name" with value corresponding to name */ +{ +int i, j; +char tmp[1000], str[256], uattr[256], *ps, *pt, *p; + + pt = tmp; + ps = string; + for (p = strchr(ps, '$') ; p != NULL ; p = strchr(ps, '$')) + { + /* copy leading characters */ + j = (int)(p-ps); + memcpy(pt, ps, j); + pt += j; + p++; + + /* extract name */ + strcpy(str, p); + for (j=0 ; j<(int)strlen(str) ; j++) + str[j] = toupper(str[j]); + + /* search name */ + for (i=0 ; i 0) + { + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + + timeout.tv_sec = millisec / 1000; + timeout.tv_usec = (millisec % 1000) * 1000; + + select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); + + if (!FD_ISSET(sock, &readfds)) + break; + } + + i = recv(sock, buffer+n, 1, 0); + + if (i<=0) + break; + + n++; + + if (n >= buffer_size) + break; + + } while (buffer[n-1] && buffer[n-1] != 10); + + return n-1; +} + + +INT sendmail(char *smtp_host, char *from, char *to, char *subject, char *text) +{ +struct sockaddr_in bind_addr; +struct hostent *phe; +int s; +char buf[80]; +char str[10000]; +time_t now; +struct tm *ts; + + if (verbose) + printf("\n\nEmail from %s to %s, SMTP host %s:\n", from, to, smtp_host); + + /* create a new socket for connecting to remote server */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + return -1; + + /* connect to remote node port 25 */ + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons((short) 25); + + phe = gethostbyname(smtp_host); + if (phe == NULL) + return -1; + memcpy((char *)&(bind_addr.sin_addr), phe->h_addr, phe->h_length); + + if (connect(s, (void *) &bind_addr, sizeof(bind_addr)) < 0) + { + closesocket(s); + return -1; + } + + recv_string(s, str, sizeof(str), 10000); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "HELO %s\r\n", host_name); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + recv_string(s, str, sizeof(str), 3000); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "MAIL FROM: <%s>\r\n", from); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + recv_string(s, str, sizeof(str), 3000); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "RCPT TO: <%s>\r\n", to); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + recv_string(s, str, sizeof(str), 3000); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "DATA\r\n"); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + recv_string(s, str, sizeof(str), 3000); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "To: %s\r\nFrom: %s\r\nSubject: %s\r\n", to, from, subject); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + + time(&now); + ts = localtime(&now); + strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts); + snprintf(str, sizeof(str) - 1, "Date: %s %+03d%02d\r\n\r\n", buf, (int) (-timezone/3600), (int) ((abs(timezone)/60) % 60)); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "%s\r\n.\r\n", text); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + recv_string(s, str, sizeof(str), 3000); + if (verbose) puts(str); + + snprintf(str, sizeof(str) - 1, "QUIT\r\n"); + send(s, str, strlen(str), 0); + if (verbose) puts(str); + recv_string(s, str, sizeof(str), 3000); + if (verbose) puts(str); + + closesocket(s); + + return 1; +} + +/*-------------------------------------------------------------------*/ + +INT ss_daemon_init() +{ +#ifdef OS_UNIX + + /* only implemented for UNIX */ + int i, fd, pid; + + if ( (pid = fork()) < 0) + return 0; + else if (pid != 0) + exit(0); /* parent finished */ + + /* child continues here */ + + /* try and use up stdin, stdout and stderr, so other + routines writing to stdout etc won't cause havoc. Copied from smbd */ + for (i=0 ; i<3 ; i++) + { + close(i); + fd = open("/dev/null", O_RDWR, 0); + if (fd < 0) + fd = open("/dev/null", O_WRONLY, 0); + if (fd < 0) + { + printf("Can't open /dev/null"); + return 0; + } + if (fd != i) + { + printf("Did not get file descriptor"); + return 0; + } + } + + setsid(); /* become session leader */ + chdir("/"); /* change working direcotry (not on NFS!) */ + umask(0); /* clear our file mode createion mask */ + +#endif + + return SUCCESS; +} + +/*------------------------------------------------------------------*/ + +/* Parameter extraction from configuration file similar to win.ini */ + +char *cfgbuffer; + +int getcfg(char *group, char *param, char *value) +{ +char str[10000], *p, *pstr; +int length; +int fh; + + value[0] = 0; + + /* read configuration file on init */ + if (!cfgbuffer) + { + fh = open(cfg_file, O_RDONLY | O_BINARY); + if (fh < 0) + return 0; + length = lseek(fh, 0, SEEK_END); + lseek(fh, 0, SEEK_SET); + cfgbuffer = malloc(length+1); + if (cfgbuffer == NULL) + { + close(fh); + return 0; + } + read(fh, cfgbuffer, length); + cfgbuffer[length] = 0; + close(fh); + } + + /* search group */ + p = cfgbuffer; + do + { + if (*p == '[') + { + p++; + pstr = str; + while (*p && *p != ']' && *p != '\n') + *pstr++ = *p++; + *pstr = 0; + if (equal_ustring(str, group)) + { + /* search parameter */ + p = strchr(p, '\n'); + if (p) + p++; + while (p && *p && *p != '[') + { + pstr = str; + while (*p && *p != '=' && *p != '\n') + *pstr++ = *p++; + *pstr-- = 0; + while (pstr > str && (*pstr == ' ' || *pstr == '=')) + *pstr-- = 0; + + if (equal_ustring(str, param)) + { + if (*p == '=') + { + p++; + while (*p == ' ') + p++; + pstr = str; + while (*p && *p != '\n' && *p != '\r') + *pstr++ = *p++; + *pstr-- = 0; + while (*pstr == ' ') + *pstr-- = 0; + + strcpy(value, str); + return 1; + } + } + + if (p) + p = strchr(p, '\n'); + if (p) + p++; + } + } + } + if (p) + p = strchr(p, '\n'); + if (p) + p++; + } while (p); + + /* if parameter not found in logbook, look in [global] section */ + if (!equal_ustring(group, "global")) + return getcfg("global", param, value); + + return 0; +} + +/*------------------------------------------------------------------*/ + +int enumcfg(char *group, char *param, char *value, int index) +{ +char str[10000], *p, *pstr; +int i; + + /* open configuration file */ + if (!cfgbuffer) + getcfg("dummy", "dummy", str); + if (!cfgbuffer) + return 0; + + /* search group */ + p = cfgbuffer; + do + { + if (*p == '[') + { + p++; + pstr = str; + while (*p && *p != ']' && *p != '\n' && *p != '\r') + *pstr++ = *p++; + *pstr = 0; + if (equal_ustring(str, group)) + { + /* enumerate parameters */ + i = 0; + p = strchr(p, '\n'); + if (p) + p++; + while (p && *p && *p != '[') + { + pstr = str; + while (*p && *p != '=' && *p != '\n' && *p != '\r') + *pstr++ = *p++; + *pstr-- = 0; + while (pstr > str && (*pstr == ' ' || *pstr == '=')) + *pstr-- = 0; + + if (i == index) + { + strcpy(param, str); + if (*p == '=') + { + p++; + while (*p == ' ') + p++; + pstr = str; + while (*p && *p != '\n' && *p != '\r') + *pstr++ = *p++; + *pstr-- = 0; + while (*pstr == ' ') + *pstr-- = 0; + + if (value) + strcpy(value, str); + } + return 1; + } + + if (p) + p = strchr(p, '\n'); + if (p) + p++; + i++; + } + } + } + if (p) + p = strchr(p, '\n'); + if (p) + p++; + } while (p); + + return 0; +} + +/*------------------------------------------------------------------*/ + +int enumgrp(int index, char *group) +{ +char str[10000], *p, *pstr; +int i; + + /* open configuration file */ + if (!cfgbuffer) + getcfg("dummy", "dummy", str); + if (!cfgbuffer) + return 0; + + /* search group */ + p = cfgbuffer; + i = 0; + do + { + if (*p == '[') + { + p++; + pstr = str; + while (*p && *p != ']' && *p != '\n' && *p != '\r') + *pstr++ = *p++; + *pstr = 0; + + if (i == index) + { + strcpy(group, str); + return 1; + } + + i++; + } + + while (*p && *p != '\r' && *p != '\n') + p++; + while (*p && (*p == '\r' || *p == '\n')) + p++; + } while (*p); + + return 0; +} + +/*-------------------------------------------------------------------*/ + +char *locbuffer = NULL; +char **porig, **ptrans; + +/* localization support */ +char *loc(char *orig) +{ +char language[256], file_name[256], *p; +int fh, length, n; +static char old_language[256]; + + getcfg("global", "Language", language); + + if (!equal_ustring(language, old_language)) + { + if (equal_ustring(language, "english") || + language[0] == 0) + { + if (locbuffer) + { + free(locbuffer); + locbuffer = NULL; + } + } + else + { + strcpy(file_name, cfg_dir); + strcat(file_name, "eloglang."); + strcat(file_name, language); + + fh = open(file_name, O_RDONLY | O_BINARY); + if (fh < 0) + return orig; + + length = lseek(fh, 0, SEEK_END); + lseek(fh, 0, SEEK_SET); + locbuffer = malloc(length+1); + if (locbuffer == NULL) + { + close(fh); + return orig; + } + read(fh, locbuffer, length); + locbuffer[length] = 0; + close(fh); + + /* scan lines, setup orig-translated pointers */ + p = locbuffer; + n = 0; + do + { + while (*p && (*p == '\r' || *p == '\n')) + p++; + + if (*p && (*p == ';' || *p == '#' || *p == ' ' || *p == '\t')) + { + while (*p && *p != '\n' && *p != '\r') + p++; + continue; + } + + if (n == 0) + { + porig = malloc(sizeof(char *) * 2); + ptrans = malloc(sizeof(char *) * 2); + } + else + { + porig = realloc(porig, sizeof(char *) * (n + 2)); + ptrans = realloc(ptrans, sizeof(char *) * (n + 2)); + } + + porig[n] = p; + while (*p && (*p != '=' && *p != '\r' && *p != '\n')) + p++; + + if (*p && *p != '=') + continue; + + ptrans[n] = p + 1; + while (*ptrans[n] == ' ') + ptrans[n] ++; + + /* remove '=' and surrounding blanks */ + while (*p == '=' || *p == ' ') + *p-- = 0; + + p = ptrans[n]; + while (*p && *p != '\n' && *p != '\r') + p++; + + if (p) + *p++ = 0; + + n++; + } while (p && *p); + + porig[n] = NULL; + } + + strcpy(old_language, language); + } + + if (!locbuffer) + return orig; + + /* search string and return translation */ + for (n = 0; porig[n] ; n++) + if (strcmp(orig, porig[n]) == 0) + { + if (*ptrans[n]) + return ptrans[n]; + return orig; + } + + return orig; +} + +/*------------------------------------------------------------------*/ + +/* Parameter extraction from themes file */ + +typedef struct { + char name[32]; + char value[256]; + } THEME; + +THEME default_theme [] = { + + { "Table width", "100%" }, + { "Frame color", "#486090" }, + { "Cell BGColor", "#E6E6E6" }, + { "Border width", "0" }, + + { "Title BGColor", "#486090" }, + { "Title Fontcolor", "#FFFFFF" }, + { "Title Cellpadding", "0" }, + { "Title Image", "" }, + + { "Merge menus", "1" }, + { "Use buttons", "0" }, + + { "Menu1 BGColor", "#E0E0E0" }, + { "Menu1 Align", "left" }, + { "Menu1 Cellpadding", "0" }, + + { "Menu2 BGColor", "#FFFFB0" }, + { "Menu2 Align", "center" }, + { "Menu2 Cellpadding", "0" }, + { "Menu2 use images", "0" }, + + { "Categories cellpadding", "3" }, + { "Categories border", "0" }, + { "Categories BGColor1", "#CCCCFF" }, + { "Categories BGColor2", "#DDEEBB" }, + + { "Text BGColor", "#FFFFFF" }, + + { "List bgcolor1", "#DDEEBB" }, + { "List bgcolor2", "#FFFFB0" }, + + { "" } + +}; + +THEME *theme = NULL; +char theme_name[80]; + +/*------------------------------------------------------------------*/ + +void loadtheme(char *tn) +{ +FILE *f; +char *p, line[256], item[256], value[256], file_name[256]; +int i; + + if (theme == NULL) + theme = malloc(sizeof(default_theme)); + + /* copy default theme */ + memcpy(theme, default_theme, sizeof(default_theme)); + + /* use default if no name is given */ + if (tn == NULL) + return; + + memset(file_name, 0, sizeof(file_name)); + + strcpy(file_name, cfg_dir); + strcat(file_name, tn); + file_name[strlen(file_name)] = DIR_SEPARATOR; + strcat(file_name, "theme.cfg"); + + f = fopen(file_name, "r"); + if (f == NULL) + return; + + strcpy(theme_name, tn); + + do + { + line[0] = 0; + fgets(line, sizeof(line), f); + + /* ignore comments */ + if (line[0] == ';' || line[0] == '#') + continue; + + if (strchr(line, '=')) + { + /* split line into item and value, strop blanks */ + strcpy(item, line); + p = strchr(item, '='); + while ((*p == '=' || *p == ' ') && p > item) + *p-- = 0; + p = strchr(line, '='); + while (*p && (*p == '=' || *p == ' ')) + p++; + strcpy(value, p); + if (strlen(value) > 0) + { + p = value+strlen(value)-1; + while (p >= value && (*p == ' ' || *p == '\r' || *p == '\n')) + *(p--) = 0; + } + + /* find field in _theme and set it */ + for (i=0 ; theme[i].name[0] ; i++) + if (equal_ustring(item, theme[i].name)) + { + strcpy(theme[i].value, value); + break; + } + } + + } while (!feof(f)); + + fclose(f); + + return; +} + +/*------------------------------------------------------------------*/ + +/* get a certain theme attribute from currently loaded file */ + +char *gt(char *name) +{ +int i; + + if (theme == NULL) + loadtheme(NULL); + + for (i=0 ; theme[i].name[0] ; i++) + if (equal_ustring(theme[i].name, name)) + return theme[i].value; + + return ""; +} + +/*------------------------------------------------------------------*/ + +void el_decode(char *message, char *key, char *result) +{ +char *pc; + + if (result == NULL) + return; + + *result = 0; + + if (strstr(message, key)) + { + for (pc=strstr(message, key)+strlen(key) ; *pc != '\n' && *pc != '\r' ; ) + *result++ = *pc++; + *result = 0; + } +} + +/*------------------------------------------------------------------*/ + +/* Simplified copy of fnmatch() for Cygwin where fnmatch is not defined */ + +#define EOS '\0' + +int fnmatch1(const char *pattern, const char *string) +{ +const char *stringstart; +char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + return (*string == EOS ? 0 : 1); + case '?': + if (*string == EOS) + return (1); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) + return (0); + + /* General case, use recursion. */ + while ((test = *string) != EOS) + { + if (!fnmatch1(pattern, string)) + return (0); + ++string; + } + return (1); + /* FALLTHROUGH */ + default: + if (c != *string) + return (1); + string++; + break; + } +} + +/*------------------------------------------------------------------*/ + +INT ss_file_find(char * path, char * pattern, char **plist) +/********************************************************************\ + + Routine: ss_file_find + + Purpose: Return list of files matching 'pattern' from the 'path' location + + Input: + char *path Name of a file in file system to check + char *pattern pattern string (wildcard allowed) + + Output: + char **plist pointer to the lfile list + + Function value: + int Number of files matching request + +\********************************************************************/ +{ + int i; + +#ifdef OS_UNIX + DIR *dir_pointer; + struct dirent *dp; + + if ((dir_pointer = opendir(path)) == NULL) + return 0; + *plist = (char *) malloc(MAX_PATH_LENGTH); + i = 0; + for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) + { + if (fnmatch1(pattern, dp->d_name) == 0) + { + *plist = (char *)realloc(*plist, (i+1)*MAX_PATH_LENGTH); + strncpy(*plist+(i*MAX_PATH_LENGTH), dp->d_name, strlen(dp->d_name)); + *(*plist+(i*MAX_PATH_LENGTH)+strlen(dp->d_name)) = '\0'; + i++; + seekdir(dir_pointer, telldir(dir_pointer)); + } + } + closedir(dir_pointer); +#endif + +#ifdef OS_WINNT + HANDLE pffile; + LPWIN32_FIND_DATA lpfdata; + char str[255]; + int first; + + strcpy(str,path); + strcat(str,"\\"); + strcat(str,pattern); + first = 1; + i = 0; + lpfdata = malloc(sizeof(WIN32_FIND_DATA)); + *plist = (char *) malloc(MAX_PATH_LENGTH); + pffile = FindFirstFile(str, lpfdata); + if (pffile == INVALID_HANDLE_VALUE) + return 0; + first = 0; + *plist = (char *)realloc(*plist, (i+1)*MAX_PATH_LENGTH); + strncpy(*plist+(i*MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName)); + *(*plist+(i*MAX_PATH_LENGTH)+strlen(lpfdata->cFileName)) = '\0'; + i++; + while (FindNextFile(pffile, lpfdata)) + { + *plist = (char *)realloc(*plist, (i+1)*MAX_PATH_LENGTH); + strncpy(*plist+(i*MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName)); + *(*plist+(i*MAX_PATH_LENGTH)+strlen(lpfdata->cFileName)) = '\0'; + i++; + } + free(lpfdata); +#endif + return i; +} + +/*------------------------------------------------------------------*/ + +INT el_search_message(char *tag, int *fh, BOOL walk, BOOL first) +{ +int lfh, i, n, d, min, max, size, offset, direction, last, status, did_walk; +struct tm *tms, ltms; +time_t lt, ltime, lact; +char str[256], file_name[256], dir[256]; +char *file_list; + + did_walk = 0; + ltime = lfh = 0; + + /* get data directory */ + strcpy(dir, data_dir); + + /* check tag for direction */ + direction = 0; + if (strpbrk(tag, "+-")) + { + direction = atoi(strpbrk(tag, "+-")); + *strpbrk(tag, "+-") = 0; + } + + /* if tag is given, open file directly */ + if (tag[0]) + { + /* extract time structure from tag */ + tms = <ms; + memset(tms, 0, sizeof(struct tm)); + tms->tm_year = (tag[0]-'0')*10 + (tag[1]-'0'); + tms->tm_mon = (tag[2]-'0')*10 + (tag[3]-'0') -1; + tms->tm_mday = (tag[4]-'0')*10 + (tag[5]-'0'); + tms->tm_hour = 12; + + if (tms->tm_year < 90) + tms->tm_year += 100; + ltime = lt = mktime(tms); + + if (ltime < 0) + return -1; + + strcpy(str, tag); + if (strchr(str, '.')) + { + offset = atoi(strchr(str, '.')+1); + *strchr(str, '.') = 0; + } + else + return -1; + + do + { + tms = localtime(<ime); + + sprintf(file_name, "%s%02d%02d%02d.log", dir, + tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday); + lfh = open(file_name, O_RDWR | O_BINARY, 0644); + + if (lfh < 0) + { + if (!walk) + return EL_FILE_ERROR; + + did_walk = 1; + + if (direction == -1) + ltime -= 3600*24; /* one day back */ + else + ltime += 3600*24; /* go forward one day */ + + /* set new tag */ + tms = localtime(<ime); + sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday); + } + + /* in forward direction, stop today */ + if (direction != -1 && ltime > (time_t)(time(NULL)+3600*24)) + break; + + /* in backward direction, go back 10 years */ + if (direction == -1 && abs((int)(lt-ltime)) > 3600*24*365*10) + break; + + } while (lfh < 0); + + if (lfh < 0) + return EL_FILE_ERROR; + + if (direction == -1 && did_walk) + lseek(lfh, 0, SEEK_END); + else + lseek(lfh, offset, SEEK_SET); + } + + /* open most recent file if no tag given */ + if (tag[0] == 0) + { + if (first) + { + /* go through whole directory, find first file */ + + file_list = NULL; + n = ss_file_find(dir, "??????.log", &file_list); + if (n == 0) + { + if (file_list) + free(file_list); + return EL_FILE_ERROR; + } + + for (i=0,min=9999999 ; i max) + max = d; + } + free(file_list); + sprintf(file_name, "%s%06d.log", dir, max % 1000000); + lfh = open(file_name, O_RDWR | O_BINARY, 0644); + if (lfh < 0) + return EL_FILE_ERROR; + + lseek(lfh, 0, SEEK_END); + + /* remember tag */ + sprintf(tag, "%06d.%d", (int) (max % 1000000), (int) (TELL(lfh))); + } + } + + if (direction == -1) + { + /* seek previous message */ + + if (TELL(lfh) == 0) + { + /* go back one day */ + close(lfh); + + lt = ltime; + do + { + lt -= 3600*24; + tms = localtime(<); + sprintf(str, "%02d%02d%02d.0", + tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday); + + status = el_search_message(str, &lfh, FALSE, FALSE); + + } while (status != SUCCESS && + (INT)ltime-(INT)lt < 3600*24*365); + + if (status != EL_SUCCESS) + { + if (fh) + *fh = lfh; + else + close(lfh); + return EL_FIRST_MSG; + } + + /* adjust tag */ + strcpy(tag, str); + + /* go to end of current file */ + lseek(lfh, 0, SEEK_END); + } + + /* read previous message size */ + lseek(lfh, -17, SEEK_CUR); + i = read(lfh, str, 17); + if (i <= 0) + { + close(lfh); + return EL_FILE_ERROR; + } + + if (strncmp(str, "$End$: ", 7) == 0) + { + size = atoi(str+7); + lseek(lfh, -size, SEEK_CUR); + } + else + { + close(lfh); + return EL_FILE_ERROR; + } + + /* adjust tag */ + sprintf(strchr(tag, '.')+1, "%d", (int) (TELL(lfh))); + } + + if (direction == 1) + { + /* seek next message */ + + /* read current message size */ + last = TELL(lfh); + + i = read(lfh, str, 15); + if (i <= 0) + { + close(lfh); + return EL_FILE_ERROR; + } + lseek(lfh, -15, SEEK_CUR); + + if (strncmp(str, "$Start$: ", 9) == 0) + { + size = atoi(str+9); + lseek(lfh, size, SEEK_CUR); + } + else + { + close(lfh); + return EL_FILE_ERROR; + } + + /* if EOF, goto next day */ + i = read(lfh, str, 15); + if (i < 15) + { + close(lfh); + time(&lact); + + lt = ltime; + do + { + lt += 3600*24; + tms = localtime(<); + sprintf(str, "%02d%02d%02d.0", + tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday); + + status = el_search_message(str, &lfh, FALSE, FALSE); + + } while (status != EL_SUCCESS && + (INT)lt-(INT)lact < 3600*24); + + if (status != EL_SUCCESS) + { + if (fh) + *fh = lfh; + else + close(lfh); + return EL_LAST_MSG; + } + + /* adjust tag */ + strcpy(tag, str); + + /* go to beginning of current file */ + lseek(lfh, 0, SEEK_SET); + } + else + lseek(lfh, -15, SEEK_CUR); + + /* adjust tag */ + sprintf(strchr(tag, '.')+1, "%d", (int) (TELL(lfh))); + } + + if (fh) + *fh = lfh; + else + close(lfh); + + return EL_SUCCESS; +} + +/*------------------------------------------------------------------*/ + +INT el_submit(char attr_name[MAX_N_ATTR][NAME_LENGTH], + char attr_value[MAX_N_ATTR][NAME_LENGTH], + int n_attr, char *text, char *reply_to, char *encoding, + char afilename[MAX_ATTACHMENTS][256], + char *buffer[MAX_ATTACHMENTS], + INT buffer_size[MAX_ATTACHMENTS], + char *tag, INT tag_size) +/********************************************************************\ + + Routine: el_submit + + Purpose: Submit an ELog entry + + Input: + char attr_name[][] Name of attributes + char attr_value[][] Value of attributes + int n_attr Number of attributes + + char *text Message text + char *reply_to In reply to this message + char *encoding Text encoding, either HTML or plain + + char *afilename[] File name of attachments + char *buffer[] Attachment contents + INT *buffer_size[] Size of attachment in bytes + char *tag If given, edit existing message + INT *tag_size Maximum size of tag + + Output: + char *tag Message tag in the form YYMMDD.offset + INT *tag_size Size of returned tag + + Function value: + EL_SUCCESS Successful completion + +\********************************************************************/ +{ +INT n, i, size, fh, status, index, offset, tail_size; +struct tm *tms; +char file_name[256], afile_name[MAX_ATTACHMENTS][256], dir[256], str[256], + start_str[80], end_str[80], last[80], date[80], thread[80], attachment_all[64*MAX_ATTACHMENTS]; +time_t now; +char message[TEXT_SIZE+100], *p; +BOOL bedit; + + bedit = (tag[0] != 0); + + for (index = 0 ; index < MAX_ATTACHMENTS ; index++) + { + /* generate filename for attachment */ + afile_name[index][0] = file_name[0] = 0; + + if (equal_ustring(afilename[index], loc(""))) + { + strcpy(afile_name[index], loc("")); + } + else if (afilename[index][0] && buffer_size[index] == 0) + { + /* resubmission of existing attachment */ + strcpy(afile_name[index], afilename[index]); + } + else + { + if (afilename[index][0]) + { + /* strip directory, add date and time to filename */ + + strcpy(file_name, afilename[index]); + p = file_name; + while (strchr(p, ':')) + p = strchr(p, ':')+1; + while (strchr(p, '\\')) + p = strchr(p, '\\')+1; /* NT */ + while (strchr(p, '/')) + p = strchr(p, '/')+1; /* Unix */ + + /* assemble ELog filename */ + if (p[0]) + { + strcpy(dir, data_dir); + + time(&now); + tms = localtime(&now); + + strcpy(str, p); + sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s", + tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday, + tms->tm_hour, tms->tm_min, tms->tm_sec, str); + sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir, + tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday, + tms->tm_hour, tms->tm_min, tms->tm_sec, str); + + /* save attachment */ + fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644); + if (fh < 0) + { + printf("Cannot write attachment file \"%s\"", file_name); + } + else + { + write(fh, buffer[index], buffer_size[index]); + close(fh); + } + } + } + } + } + + /* generate new file name YYMMDD.log in data directory */ + strcpy(dir, data_dir); + + if (bedit) + { + /* edit existing message */ + strcpy(str, tag); + if (strchr(str, '.')) + { + offset = atoi(strchr(str, '.')+1); + *strchr(str, '.') = 0; + } + sprintf(file_name, "%s%s.log", dir, str); + fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644); + if (fh < 0) + return -1; + + lseek(fh, offset, SEEK_SET); + read(fh, str, 16); + size = atoi(str+9); + read(fh, message, size); + + el_decode(message, "Date: ", date); + el_decode(message, "Thread: ", thread); + el_decode(message, "Attachment: ", attachment_all); + + /* buffer tail of logfile */ + lseek(fh, 0, SEEK_END); + tail_size = TELL(fh) - (offset+size); + + if (tail_size > 0) + { + buffer = malloc(tail_size); + if (buffer == NULL) + { + close(fh); + return -1; + } + + lseek(fh, offset+size, SEEK_SET); + n = read(fh, buffer, tail_size); + } + lseek(fh, offset, SEEK_SET); + } + else + { + /* create new message */ + time(&now); + tms = localtime(&now); + + sprintf(file_name, "%s%02d%02d%02d.log", dir, + tms->tm_year % 100, tms->tm_mon+1, tms->tm_mday); + + fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644); + if (fh < 0) + return -1; + + strcpy(date, ctime(&now)); + date[24] = 0; + + if (reply_to[0]) + sprintf(thread, "%16s %16s", reply_to, "0"); + else + sprintf(thread, "%16s %16s", "0", "0"); + + lseek(fh, 0, SEEK_END); + } + + /* compose message */ + + sprintf(message, "Date: %s\n", date); + sprintf(message+strlen(message), "Thread: %s\n", thread); + + for (i=0 ; i")))) + { + /* delete old attachment */ + strcpy(str, data_dir); + strcat(str, p); + remove(str); + } + + if (afile_name[i][0] && !equal_ustring(afile_name[i], loc(""))) + { + if (n == 0) + { + sprintf(message+strlen(message), "Attachment: %s", afile_name[i]); + n++; + } + else + sprintf(message+strlen(message), ",%s", afile_name[i]); + } + + else if (p && !equal_ustring(afile_name[i], loc(""))) + { + if (n == 0) + { + sprintf(message+strlen(message), "Attachment: %s", p); + n++; + } + else + sprintf(message+strlen(message), ",%s", p); + } + + } + } + else + { + sprintf(message+strlen(message), "Attachment: %s", afile_name[0]); + for (i=1 ; itm_year % 100, tms->tm_mon+1, tms->tm_mday, (int) (TELL(fh))); + + sprintf(start_str, "$Start$: %6d\n", size); + sprintf(end_str, "$End$: %6d\n\f", size); + + write(fh, start_str, strlen(start_str)); + write(fh, message, strlen(message)); + write(fh, end_str, strlen(end_str)); + + if (bedit) + { + if (tail_size > 0) + { + n = write(fh, buffer, tail_size); + free(buffer); + } + + /* truncate file here */ +#ifdef _MSC_VER + chsize(fh, TELL(fh)); +#else + ftruncate(fh, TELL(fh)); +#endif + } + + close(fh); + + /* if reply, mark original message */ + if (reply_to[0] && !bedit) + { + strcpy(last, reply_to); + do + { + status = el_search_message(last, &fh, FALSE, FALSE); + if (status == EL_SUCCESS) + { + /* position to next thread location */ + lseek(fh, 72, SEEK_CUR); + memset(str, 0, sizeof(str)); + read(fh, str, 16); + lseek(fh, -16, SEEK_CUR); + + /* if no reply yet, set it */ + if (atoi(str) == 0) + { + sprintf(str, "%16s", tag); + write(fh, str, 16); + close(fh); + break; + } + else + { + /* if reply set, find last one in chain */ + strcpy(last, strtok(str, " ")); + close(fh); + } + } + else + /* stop on error */ + break; + + } while (TRUE); + } + + return EL_SUCCESS; +} + +/*------------------------------------------------------------------*/ + +INT el_retrieve(char *tag, char *date, char attr_list[MAX_N_ATTR][NAME_LENGTH], + char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, + char *text, int *textsize, + char *orig_tag, char *reply_tag, + char attachment[MAX_ATTACHMENTS][256], + char *encoding) +/********************************************************************\ + + Routine: el_retrieve + + Purpose: Retrieve an ELog entry by its message tab + + Input: + char *tag tag in the form YYMMDD.offset + int *size Size of text buffer + + Output: + char *tag tag of retrieved message + char *date Date/time of message recording + char attr_list Names of attributes + char attrib Values of attributes + int n_attr Number of attributes + char *text Message text + char *orig_tag Original message if this one is a reply + char *reply_tag Reply for current message + char *attachment[] File attachments + char *encoding Encoding of message + int *size Actual message text size + + Function value: + EL_SUCCESS Successful completion + EL_LAST_MSG Last message in log + +\********************************************************************/ +{ +int i, size, fh, offset, search_status; +char str[256], *p; +char message[TEXT_SIZE+100], thread[256], attachment_all[64*MAX_ATTACHMENTS]; + + if (tag[0]) + { + search_status = el_search_message(tag, &fh, TRUE, FALSE); + if (search_status != EL_SUCCESS) + return search_status; + } + else + { + /* open most recent message */ + strcpy(tag, "-1"); + search_status = el_search_message(tag, &fh, TRUE, FALSE); + if (search_status != EL_SUCCESS) + return search_status; + } + + /* extract message size */ + offset = TELL(fh); + i = read(fh, str, 16); + if (i <= 0) + { + close(fh); + return EL_FILE_ERROR; + } + + if (strncmp(str, "$Start$: ", 9) == 0) + size = atoi(str+9); + else + { + close(fh); + return EL_FILE_ERROR; + } + + /* read message */ + memset(message, 0, sizeof(message)); + read(fh, message, size); + close(fh); + + /* decode message */ + el_decode(message, "Date: ", date); + el_decode(message, "Thread: ", thread); + + for (i=0 ; i \r conversion (e.g. zipping/unzipping) */ + if (p == NULL) + p = strstr(message, "========================================\r"); + + if (text != NULL) + { + if (p != NULL) + { + p += 41; + if ((int) strlen(p) >= *textsize) + { + strncpy(text, p, *textsize-1); + text[*textsize-1] = 0; + return -1; + } + else + { + strcpy(text, p); + + /* strip end tag */ + if (strstr(text, "$End$")) + *strstr(text, "$End$") = 0; + + *textsize = strlen(text); + } + } + else + { + text[0] = 0; + *textsize = 0; + } + } + + if (search_status == EL_LAST_MSG) + return EL_LAST_MSG; + + return EL_SUCCESS; +} + +/*------------------------------------------------------------------*/ + +INT el_delete_message(char *tag) +/********************************************************************\ + + Routine: el_delete_message + + Purpose: Delete an ELog entry including attachments + + Input: + char *tag Message tage + + Output: + + + Function value: + EL_SUCCESS Successful completion + +\********************************************************************/ +{ +INT i, n, size, fh, offset, tail_size, status, n_attr; +char dir[256], str[256], file_name[256]; +char *buffer, attrib[MAX_N_ATTR][NAME_LENGTH]; +char date[80], text[TEXT_SIZE], orig_tag[80], reply_tag[80], + attachment[MAX_ATTACHMENTS][256], encoding[80]; + + n_attr = scan_attributes(logbook); + + /* get attachments */ + size = sizeof(text); + status = el_retrieve(tag, date, attr_list, attrib, n_attr, + text, &size, orig_tag, reply_tag, + attachment, + encoding); + if (status != SUCCESS) + return EL_FILE_ERROR; + + for (i=0 ; i 0) + { + buffer = malloc(tail_size); + if (buffer == NULL) + { + close(fh); + return EL_FILE_ERROR; + } + + lseek(fh, offset+size, SEEK_SET); + n = read(fh, buffer, tail_size); + } + lseek(fh, offset, SEEK_SET); + + if (tail_size > 0) + { + n = write(fh, buffer, tail_size); + free(buffer); + } + + /* truncate file here */ +#ifdef OS_WINNT + chsize(fh, TELL(fh)); +#else + ftruncate(fh, TELL(fh)); +#endif + + /* if file length gets zero, delete file */ + tail_size = lseek(fh, 0, SEEK_END); + close(fh); + + if (tail_size == 0) + remove(file_name); + + return EL_SUCCESS; +} + +/*------------------------------------------------------------------*/ + +INT el_delete_message_only(char *tag, char afilename[MAX_ATTACHMENTS][256]) +/********************************************************************\ + + Routine: el_delete_message_only + + Purpose: Delete an ELog entry, but keeping (and returning) attachments + + Input: + char *tag Message tage + + Output: + + + Function value: + EL_SUCCESS Successful completion + +\********************************************************************/ +{ +INT i, n, size, fh, offset, tail_size, status, n_attr; +char dir[256], str[256], file_name[256]; +char *buffer, attrib[MAX_N_ATTR][NAME_LENGTH]; +char date[80], text[TEXT_SIZE], orig_tag[80], reply_tag[80], + attachment[MAX_ATTACHMENTS][256], encoding[80]; + + n_attr = scan_attributes(logbook); + + /* get attachments */ + size = sizeof(text); + status = el_retrieve(tag, date, attr_list, attrib, n_attr, + text, &size, orig_tag, reply_tag, + attachment, + encoding); + if (status != SUCCESS) + return EL_FILE_ERROR; + + for (i=0 ; i 0) + { + buffer = malloc(tail_size); + if (buffer == NULL) + { + close(fh); + return EL_FILE_ERROR; + } + + lseek(fh, offset+size, SEEK_SET); + n = read(fh, buffer, tail_size); + } + lseek(fh, offset, SEEK_SET); + + if (tail_size > 0) + { + n = write(fh, buffer, tail_size); + free(buffer); + } + + /* truncate file here */ +#ifdef OS_WINNT + chsize(fh, TELL(fh)); +#else + ftruncate(fh, TELL(fh)); +#endif + + /* if file length gets zero, delete file */ + tail_size = lseek(fh, 0, SEEK_END); + close(fh); + + if (tail_size == 0) + remove(file_name); + + return EL_SUCCESS; +} + +/*------------------------------------------------------------------*/ + +void rsputs(const char *str) +{ + if (strlen_retbuf + strlen(str) > sizeof(return_buffer)) + strcpy(return_buffer, "

Error: return buffer too small

"); + else + strcpy(return_buffer+strlen_retbuf, str); + + strlen_retbuf += strlen(str); +} + +/*------------------------------------------------------------------*/ + +void rsputs2(const char *str) +{ +int i, j, k; +char *p, link[256]; + + if (strlen_retbuf + strlen(str) > sizeof(return_buffer)) + strcpy(return_buffer, "

Error: return buffer too small

"); + else + { + j = strlen_retbuf; + for (i=0 ; i<(int)strlen(str) ; i++) + { + if (strncmp(str+i, "http://", 7) == 0) + { + p = (char *) (str+i+7); + i += 7; + for (k=0 ; *p && *p != ' ' && *p != ',' && *p != '\t' && *p != '\n' && *p != '\r'; k++,i++) + link[k] = *p++; + link[k] = 0; + i--; + + sprintf(return_buffer+j, "http://%s", link, link); + j += strlen(return_buffer+j); + } + else if (strncmp(str+i, "https://", 8) == 0) + { + p = (char *) (str+i+8); + i += 8; + for (k=0 ; *p && *p != ' ' && *p != ',' && *p != '\t' && *p != '\n' && *p != '\r'; k++,i++) + link[k] = *p++; + link[k] = 0; + i--; + + sprintf(return_buffer+j, "https://%s", link, link); + j += strlen(return_buffer+j); + } + else if (strncmp(str+i, "ftp://", 6) == 0) + { + p = (char *) (str+i+6); + i += 6; + for (k=0 ; *p && *p != ' ' && *p != ',' && *p != '\t' && *p != '\n' && *p != '\r'; k++,i++) + link[k] = *p++; + link[k] = 0; + i--; + + sprintf(return_buffer+j, "ftp://%s", link, link); + j += strlen(return_buffer+j); + } + else if (strncmp(str+i, "mailto:", 7) == 0) + { + p = (char *) (str+i+7); + i += 7; + for (k=0 ; *p && *p != '>' && *p != ' ' && *p != ',' && *p != '\t' && *p != '\n' && *p != '\r'; k++,i++) + link[k] = *p++; + link[k] = 0; + i--; + + sprintf(return_buffer+j, "%s", link, link); + j += strlen(return_buffer+j); + } + else + switch (str[i]) + { + case 1 : strcat(return_buffer, "<"); j++; break; + case 2 : strcat(return_buffer, ">"); j++; break; + case '<': strcat(return_buffer, "<"); j+=4; break; + case '>': strcat(return_buffer, ">"); j+=4; break; + default: return_buffer[j++] = str[i]; + } + } + + return_buffer[j] = 0; + strlen_retbuf = j; + } +} + +/*------------------------------------------------------------------*/ + +void rsprintf(const char *format, ...) +{ +va_list argptr; +char str[10000]; + + va_start(argptr, format); + vsprintf(str, (char *) format, argptr); + va_end(argptr); + + if (strlen_retbuf + strlen(str) > sizeof(return_buffer)) + strcpy(return_buffer, "

Error: return buffer too small

"); + else + strcpy(return_buffer+strlen_retbuf, str); + + strlen_retbuf += strlen(str); +} + +/*------------------------------------------------------------------*/ + +/* Parameter handling functions similar to setenv/getenv */ + +void initparam() +{ + memset(_param, 0, sizeof(_param)); + memset(_value, 0, sizeof(_value)); + _text[0] = 0; +} + +void setparam(char *param, char *value) +{ +int i; + + if (equal_ustring(param, "text")) + { + if (strlen(value) >= TEXT_SIZE) + printf("Error: parameter value too big\n"); + + strncpy(_text, value, TEXT_SIZE); + _text[TEXT_SIZE-1] = 0; + return; + } + + for (i=0 ; i= VALUE_SIZE) + printf("Error: parameter value too big\n"); + + strncpy(_value[i], value, VALUE_SIZE); + _value[i][VALUE_SIZE-1] = 0; + } + else + { + printf("Error: parameter array too small\n"); + } +} + +char *getparam(char *param) +{ +int i; + + if (equal_ustring(param, "text")) + return _text; + + for (i=0 ; iredir\r\n", logbook_enc, path); +} + +void redirect2(char *path) +{ + redirect(path); + send(_sock, return_buffer, strlen(return_buffer)+1, 0); + closesocket(_sock); + return_length = -1; +} + +void redirect3(char *path) +{ + /* redirect */ + rsprintf("HTTP/1.1 302 Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + rsprintf("Location: %s\r\n\r\nredir\r\n", path); +} + +/*------------------------------------------------------------------*/ + +int strbreak(char *str, char list[][NAME_LENGTH], int size) +/* break comma-separated list into char array, stripping leading + and trailing blanks */ +{ +int i; +char *p; + + memset(list, 0, size*NAME_LENGTH); + p = strtok(str, ","); + if (!p) + return 0; + while (*p == ' ') + p++; + + for (i=0 ; p && i\n"); + rsprintf("ELOG Electronic Logbook Upgrade Information\n"); + rsprintf("\n\n"); + + rsprintf("\n"); + + rsprintf("", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + + rsprintf("\n", + gt("Title bgcolor"), gt("Title fontcolor")); + + rsprintf("\n"); + rsprintf("
ELog Electronic Logbook Upgrade Information

\n"); + + rsprintf("You probably use an elogd.cfg configuration file for a ELOG version\n"); + rsprintf("1.1.x, since it contains a \"Types = ...\" entry. From version\n"); + rsprintf("1.2.0 on, the fixed attributes Type and Category have been\n"); + rsprintf("replaced by arbitrary attributes. Please replace these two lines with the\n"); + rsprintf("following entries:

\n"); + rsprintf("

\n");
+  rsprintf("Attributes = Author, Type, Category, Subject\n");
+  rsprintf("Required Attributes = Author\n");
+  getcfg(logbook, "Types", str);
+  rsprintf("Options Type = %s\n", str);
+  getcfg(logbook, "Categories", str);
+  rsprintf("Options Category = %s\n", str);
+  rsprintf("Page title = $subject\n");
+  rsprintf("
\n"); + rsprintf("

\n"); + + rsprintf("It is of course possible to change the attributes or add new ones. The new\n"); + rsprintf("options in the configuration file are described under http://midas.psi.ch/elog/config.html\n"); + rsprintf(".\n"); + + rsprintf("

\n\n"); + + rsprintf("
\n"); + rsprintf("
\n"); + rsprintf("S. Ritt, 18 October 2001"); + rsprintf("
"); + + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +void show_standard_header(char *title, char *path) +{ + rsprintf("HTTP/1.1 200 Document follows\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + rsprintf("Content-Type: text/html\r\n"); + + rsprintf("Pragma: no-cache\r\n"); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n\r\n"); + + rsprintf("%s\n", title); + if (path) + rsprintf("
\n\n", logbook_enc, path); + else + rsprintf("\n\n", logbook_enc); +} + +/*------------------------------------------------------------------*/ + +void show_standard_title(char *logbook, char *text, int printable) +{ +char str[256], ref[256]; +int i; + + /* overall table, containing title, menu and message body */ + if (printable) + rsprintf("\n\n", + gt("Border width"), gt("Frame color")); + else + rsprintf("
\n\n", + gt("Table width"), gt("Border width"), gt("Frame color")); + + /*---- logbook selection row ----*/ + + if (!printable && getcfg("global", "logbook tabs", str) && atoi(str) == 1) + { + if (!getcfg("global", "tab cellpadding", str)) + strcpy(str, "5"); + rsprintf("\n\n"); + } + + /*---- title row ----*/ + + rsprintf("\n\n"); +} + +/*------------------------------------------------------------------*/ + +void show_error(char *error) +{ + /* header */ + show_standard_header("ELOG error", ""); + + rsprintf("

\n", str); + + if (getcfg("global", "main tab", str)) + { + rsprintf("\n", str); + rsprintf("\n"); + } + + for (i=0 ; ; i++) + { + if (!enumgrp(i, str)) + break; + + if (equal_ustring(str, "global")) + continue; + + strcpy(ref, str); + url_encode(ref); + + if (equal_ustring(str, logbook)) + rsprintf("\n", gt("Title BGColor"), gt("Title fontcolor"), str); + else + rsprintf("\n", ref, str); + rsprintf("\n"); + } + rsprintf("\n"); + rsprintf("
%s %s%s  
\n", + gt("Title cellpadding")); + + /* left cell */ + rsprintf("\n"); + + /* middle cell */ + if (*getparam("full_name")) + { + rsprintf("\n", + gt("Title fontcolor"), loc("Logged in as"), getparam("full_name")); + } + + /* right cell */ + rsprintf("\n"); + + rsprintf("
", gt("Title BGColor")); + + /* use comment as title if available, else logbook name */ + if (!getcfg(logbook, "Comment", str)) + strcpy(str, logbook); + + rsprintf("  %s%s\n", + gt("Title fontcolor"), str, text); + rsprintf(" ", gt("Title BGColor")); + rsprintf("  %s \"%s\"", gt("Title BGColor")); + if (*gt("Title image")) + rsprintf("", logbook_enc, gt("Title image")); + else + rsprintf("ELOG V%s  ", + gt("Title fontcolor"), VERSION); + rsprintf("
", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + rsprintf("\n", error); + + rsprintf("\n
"); + + rsprintf("%s
", gt("Cell BGColor")); + + rsprintf("\n", loc("Back")); + /* + rsprintf("", loc("Back")); + */ + + rsprintf("
\n"); + rsprintf("\n"); +} + +/*------------------------------------------------------------------*/ + +void send_file(char *file_name) +{ +int fh, i, length; +char str[256]; +time_t now; +struct tm *gmt; + + fh = open(file_name, O_RDONLY | O_BINARY); + if (fh > 0) + { + lseek(fh, 0, SEEK_END); + length = TELL(fh); + lseek(fh, 0, SEEK_SET); + + rsprintf("HTTP/1.1 200 Document follows\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + rsprintf("Accept-Ranges: bytes\r\n"); + + time(&now); + now += (int) (3600*3); + gmt = gmtime(&now); + strftime(str, sizeof(str), "%A, %d-%b-%y %H:%M:%S GMT", gmt); + rsprintf("Expires: %s\r\n", str); + + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + /* return proper header for file type */ + for (i=0 ; i<(int)strlen(file_name) ; i++) + str[i] = toupper(file_name[i]); + str[i] = 0; + + for (i=0 ; filetype[i].ext[0] ; i++) + if (strstr(str, filetype[i].ext)) + break; + + if (filetype[i].ext[0]) + rsprintf("Content-Type: %s\r\n", filetype[i].type); + else if (strchr(str, '.') == NULL) + rsprintf("Content-Type: text/plain\r\n"); + else + rsprintf("Content-Type: application/octet-stream\r\n"); + + rsprintf("Content-Length: %d\r\n\r\n", length); + + /* return if file too big */ + if (length > (int) (sizeof(return_buffer) - strlen(return_buffer))) + { + printf("return buffer too small\n"); + close(fh); + return; + } + + return_length = strlen(return_buffer)+length; + read(fh, return_buffer+strlen(return_buffer), length); + + close(fh); + } + else + { + rsprintf("HTTP/1.1 404 Not Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + rsprintf("Connection: close\r\n"); + rsprintf("Content-Type: text/html\r\n\r\n"); + rsprintf("404 Not Found\r\n"); + rsprintf("

Not Found

\r\n"); + rsprintf("The requested file %s was not found on this server

\r\n", file_name); + rsprintf("


ELOG version %s
\r\n\r\n", VERSION); + return_length = strlen_retbuf; + keep_alive = 0; + } +} + +/*------------------------------------------------------------------*/ + +void strencode(char *text) +{ +int i; + + for (i=0 ; i<(int) strlen(text) ; i++) + { + switch (text[i]) + { + case '\n': rsprintf("
\n"); break; + case '<': rsprintf("<"); break; + case '>': rsprintf(">"); break; + case '&': rsprintf("&"); break; + case '\"': rsprintf("""); break; + case ' ': rsprintf(" "); break; + default: rsprintf("%c", text[i]); + } + } +} + +/*------------------------------------------------------------------*/ + +void strencode2(char *b, char *text) +{ +int i; + + for (i=0 ; i<(int) strlen(text) ; b++,i++) + { + switch (text[i]) + { + case '\n': sprintf(b, "
\n"); break; + case '<': sprintf(b, "<"); break; + case '>': sprintf(b, ">"); break; + case '&': sprintf(b, "&"); break; + case '\"': sprintf(b, """); break; + default: sprintf(b, "%c", text[i]); + } + } + *b = 0; +} + +/*------------------------------------------------------------------*/ + +int build_subst_list(char list[][NAME_LENGTH], char value[][NAME_LENGTH], + char attrib[][NAME_LENGTH]) +{ +int i; +char str[256], format[256]; +time_t now; +struct tm *ts; +struct hostent *phe; + + /* copy attribute list */ + for (i=0 ; ih_name); + else + strcpy(str, (char *)inet_ntoa(rem_addr)); + + strcpy(value[i++], str); + + /* add local host */ + strcpy(list[i], "host"); + strcpy(value[i++], host_name); + + /* add user names */ + strcpy(list[i], "short_name"); + strcpy(value[i++], getparam("unm")); + strcpy(list[i], "long_name"); + strcpy(value[i++], getparam("full_name")); + + /* add logbook */ + strcpy(list[i], "logbook"); + strcpy(value[i++], logbook); + + /* add date */ + strcpy(list[i], "date"); + time(&now); + ts = localtime(&now); + if (getcfg(logbook, "Date format", format)) + strftime(str, sizeof(str), format, ts); + else + strcpy(str, ctime(&now)); + strcpy(value[i++], str); + + return i; +} + +/*------------------------------------------------------------------*/ + +void show_change_pwd_page() +{ +char str[256], file_name[256], line[256], *p, *pl, old_pwd[32], new_pwd[32]; +char *buf; +FILE *f; +int i, wrong_pwd, size; +double exp; +time_t now; +struct tm *gmt; + + + do_crypt(getparam("oldpwd"), old_pwd); + do_crypt(getparam("newpwd"), new_pwd); + + getcfg(logbook, "Password file", str); + + if (str[0] == DIR_SEPARATOR || str[1] == ':') + strcpy(file_name, str); + else + { + strcpy(file_name, cfg_dir); + strcat(file_name, str); + } + + wrong_pwd = FALSE; + + if (old_pwd[0] || new_pwd[0]) + { + f = fopen(file_name, "r+b"); + if (f != NULL) + { + fseek(f, 0, SEEK_END); + size = TELL(fileno(f)); + fseek(f, 0, SEEK_SET); + + buf = malloc(size+1); + fread(buf, 1, size, f); + buf[size] = 0; + pl = buf; + + while (pl < buf+size) + { + for (i=0 ; pl[i] && pl[i] != '\r' && pl[i] != '\n' ; i++) + line[i] = pl[i]; + line[i] = 0; + + if (line[0] == ';' || line[0] == '#' || line[0] == 0) + { + pl += strlen(line); + while (*pl && (*pl == '\r' || *pl == '\n')) + pl++; + continue; + } + + strcpy(str, line); + if (strchr(str, ':')) + *strchr(str, ':') = 0; + if (strcmp(str, getparam("unm")) == 0) + break; + + pl += strlen(line); + while (*pl && (*pl == '\r' || *pl == '\n')) + pl++; + } + + /* if user found, check old password */ + if (*getparam("unm") && (strcmp(str, getparam("unm")) == 0)) + { + p = line+strlen(str); + if (*p) + p++; + + strcpy(str, p); + if (strchr(str, ':')) + *strchr(str, ':') = 0; + + if (strcmp(old_pwd, str) != 0) + wrong_pwd = TRUE; + } + + /* replace password */ + if (!wrong_pwd) + { + fseek(f, 0, SEEK_SET); + fwrite(buf, 1, pl-buf, f); + + fprintf(f, "%s:%s:%s\n", getparam("unm"), new_pwd, getparam("full_name")); + + pl += strlen(line); + while (*pl && (*pl == '\r' || *pl == '\n')) + pl++; + + fwrite(pl, 1, strlen(pl), f); + +#ifdef _MSC_VER + chsize(fileno(f), TELL(fileno(f))); +#else + ftruncate(fileno(f), TELL(fileno(f))); +#endif + } + + free(buf); + fclose(f); + + if (!wrong_pwd) + { + rsprintf("HTTP/1.1 302 Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + /* get optional expriation from configuration file */ + exp = 24; + if (getcfg(logbook, "Login expiration", str)) + exp = atof(str); + + if (exp == 0) + { + rsprintf("Set-Cookie: upwd=%s; path=/%s\r\n", new_pwd, logbook_enc); + } + else + { + time(&now); + now += (int) (3600*exp); + gmt = gmtime(&now); + strftime(str, sizeof(str), "%A, %d-%b-%Y %H:%M:%S GMT", gmt); + + rsprintf("Set-Cookie: upwd=%s; path=/%s; expires=%s\r\n", new_pwd, logbook_enc, str); + } + + sprintf(str, "/%s/", logbook_enc); + rsprintf("Location: %s\r\n\r\nredir\r\n", str); + return; + } + } + } + + show_standard_header(loc("ELOG change password"), NULL); + + rsprintf("

", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + + if (wrong_pwd) + rsprintf("\n", loc("Wrong password")); + + rsprintf("\n", + gt("Title fontcolor"), loc("Change password for user"), getparam("full_name")); + + rsprintf("\n", + gt("Cell BGColor"), loc("Old Password")); + rsprintf("\n", + gt("Cell BGColor"), loc("New Password")); + + rsprintf("", + gt("Cell BGColor"), loc("Submit")); + + rsprintf("
%s!
\n", gt("Title bgcolor")); + + rsprintf("%s \"%s\"
%s:     
%s:     
\n"); + + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +BOOL allow_user(char *command) +{ +char str[1000], users[2000]; +char list[MAX_N_LIST][NAME_LENGTH]; +int i, n; + + /* check for user level access */ + if (!getcfg(logbook, "Password file", str)) + return TRUE; + + /* allow by default */ + sprintf(str, "Allow %s", command); + if (!getcfg(logbook, str, users)) + return TRUE; + + /* check if current user in list */ + n = strbreak(users, list, MAX_N_LIST); + for (i=0 ; i%s\" is not allowed for user \"%s\""), + command, getparam("full_name")); + show_error(str); + + return FALSE; +} + +/*------------------------------------------------------------------*/ + +int get_last_index(int index) +{ +int i, n_attr; +char str[80], date[80], attrib[MAX_N_ATTR][NAME_LENGTH], + orig_tag[80], reply_tag[80], att[MAX_ATTACHMENTS][256], encoding[80]; + + str[0] = 0; + n_attr = scan_attributes(logbook); + el_retrieve(str, date, attr_list, attrib, n_attr, + NULL, 0, orig_tag, reply_tag, att, encoding); + + strcpy(str, attrib[index]); + + /* look for first digit, return value */ + for (i=0 ; i<(int)strlen(str) ; i++) + if (isdigit(str[i])) + break; + + return atoi(str+i); +} + +/*------------------------------------------------------------------*/ + +void show_elog_new(char *path, BOOL bedit) +{ +int i, n, n_attr, index, size, wrap, fh, length; +char str[1000], preset[1000], *p, star[80], comment[10000]; +char list[MAX_N_ATTR][NAME_LENGTH], file_name[256], *buffer, format[256]; +char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], text[TEXT_SIZE], + orig_tag[80], reply_tag[80], att[MAX_ATTACHMENTS][256], encoding[80], + slist[MAX_N_ATTR+10][NAME_LENGTH], svalue[MAX_N_ATTR+10][NAME_LENGTH]; +time_t now; + + n_attr = scan_attributes(logbook); + + for (i=0 ; iELOG\n"); + rsprintf("\n", + logbook_enc); + + /*---- title row ----*/ + + show_standard_title(logbook, "", 0); + + /*---- menu buttons ----*/ + + rsprintf("\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + rsprintf("\n", loc("Submit")); + rsprintf("\n", loc("Back")); + rsprintf("
\n\n"); + + /*---- entry form ----*/ + + /* overall table for message giving blue frame */ + rsprintf("\n", + gt("Categories border"), gt("Categories cellpadding"), gt("Frame color")); + + /* print required message if one of the attributes has it set */ + for (i= 0 ; i < n_attr ; i++) + { + if (attr_flags[i] & AF_REQUIRED) + { + rsprintf("\n", + gt("Categories bgcolor1"), loc("Fields marked with"), loc("are required")); + break; + } + } + + time(&now); + if (bedit) + { + if (getcfg(logbook, "Date format", format)) + { + struct tm ts; + + memset(&ts, 0, sizeof(ts)); + + for (i=0 ; i<12 ; i++) + if (strncmp(date+4, mname[i], 3) == 0) + break; + ts.tm_mon = i; + + ts.tm_mday = atoi(date+8); + ts.tm_hour = atoi(date+11); + ts.tm_min = atoi(date+14); + ts.tm_sec = atoi(date+17); + ts.tm_year = atoi(date+20)-1900; + + mktime(&ts); + strftime(str, sizeof(str), format, &ts); + } + else + strcpy(str, date); + + rsprintf("\n\n", + gt("Categories bgcolor1"), loc("Entry date"), gt("Categories bgcolor2"), str); + } + else + { + if (getcfg(logbook, "Date format", format)) + strftime(str, sizeof(str), format, localtime(&now)); + else + strcpy(str, ctime(&now)); + + rsprintf("\n\n", + gt("Categories bgcolor1"), loc("Entry date"), gt("Categories bgcolor2"), str); + } + + /* display attributes */ + for (index = 0 ; index < n_attr ; index++) + { + strcpy(star, (attr_flags[index] & AF_REQUIRED) ? "*" : ""); + + /* check for preset string */ + sprintf(str, "Preset %s", attr_list[index]); + if (getcfg(logbook, str, preset)) + { + if (!bedit || (attr_flags[index] & AF_FIXED) == 0) + { + i = build_subst_list(slist, svalue, NULL); + strsubst(preset, slist, svalue, i); + + /* check for index substitution */ + if (!bedit && strchr(preset, '%')) + { + /* get index */ + i = get_last_index(index); + + strcpy(str, preset); + sprintf(preset, str, i+1); + } + + if (!strchr(preset, '%')) + strcpy(attrib[index], preset); + } + } + + if (attr_options[index][0][0] == 0) + { + /* display text box */ + rsprintf("", gt("Categories bgcolor1"), attr_list[index], star); + + if (attr_flags[index] & AF_LOCKED) + { + rsprintf("\n", + attr_list[index], attrib[index]); + } + else + rsprintf("\n", + gt("Categories bgcolor2"), NAME_LENGTH, attr_list[index], attrib[index]); + } + else + { + if (equal_ustring(attr_options[index][0], "boolean")) + { + /* display checkbox */ + rsprintf("\n"); + } + else + { + /* display drop-down box */ + rsprintf("\n"); + } + } + } + } + + if (path) + { + /* hidden text for original message */ + rsprintf("\n", path); + + if (bedit) + rsprintf("\n"); + } + + rsprintf("\n"); + + /* increased wrapping for replies (leave space for '> ' */ + wrap = (path && !bedit) ? 78 : 76; + + rsprintf("\n"); + + if (getcfg(logbook, "Number attachments", str)) + n = atoi(str); + else + n = 5; + + if (n > MAX_ATTACHMENTS) + n = MAX_ATTACHMENTS; + + if (n > 0) + { + if (bedit) + { + rsprintf("\n", loc("To delete an old attachment, enter <delete> in the new attachment field")); + + for (i=0 ; i", + gt("Categories bgcolor1"), loc("Original attachment"), i+1, loc("New attachment"), i+1); + + rsprintf("\n", + i+1); + } + else + rsprintf("\n", + gt("Categories bgcolor1"), loc("Attachment"), i+1, gt("Categories bgcolor2"), i+1); + } + + } + else + { + /* attachment */ + for (i=0 ; i\n", + gt("Categories bgcolor1"), loc("Attachment"), i+1, gt("Categories bgcolor2"), i+1); + } + } + + rsprintf("
%s * %s
%s:%s
%s:%s
%s%s:%s\n", gt("Categories bgcolor2"), attrib[index]); + rsprintf("
%s%s:\n", + gt("Categories bgcolor1"), attr_list[index], star, gt("Categories bgcolor2"), attr_list[index]); + } + else + { + if (attr_flags[index] & AF_MULTI) + { + /* display multiple check boxes */ + rsprintf("
%s%s:\n", + gt("Categories bgcolor1"), attr_list[index], star, gt("Categories bgcolor2")); + + for (i=0 ; i%s \n", str, attr_options[index][i], attr_options[index][i]); + else + rsprintf("%s \n", str, attr_options[index][i], attr_options[index][i]); + } + + rsprintf("
%s%s:
\n"); + + if (getcfg(logbook, "Message comment", comment) && !bedit && !path) + { + rsputs(comment); + rsputs("
\n"); + } + + if (!getcfg(logbook, "Show text", str) || atoi(str) == 1) + { + rsprintf("
\n"); + + /* HTML check box */ + if (bedit) + { + if (encoding[0] == 'H') + rsprintf("%s\n", loc("Submit as HTML text")); + else + rsprintf("%s\n", loc("Submit as HTML text")); + } + else + { + if (getcfg(logbook, "HTML default", str)) + { + if (atoi(str) == 0) + { + rsprintf("%s\n", loc("Submit as HTML text")); + } + else if (atoi(str) == 1) + { + rsprintf("%s\n", loc("Submit as HTML text")); + } + } + else + rsprintf("%s\n", loc("Submit as HTML text")); + } + } + + /* Suppress check box */ + if (getcfg(logbook, "Suppress default", str)) + { + if (atoi(str) == 0) + { + rsprintf("        \n"); + rsprintf("%s>\n", loc("Suppress Email notification")); + } + else if (atoi(str) == 1) + { + rsprintf("        \n"); + rsprintf("%s\n", loc("Suppress Email notification")); + } + } + else + { + rsprintf("        \n"); + rsprintf("%s\n", loc("Suppress Email notification")); + } + + if (bedit) + { + /* Resubmit check box */ + if (getcfg(logbook, "Resubmit default", str)) + { + if (atoi(str) == 0) + { + rsprintf("        \n"); + rsprintf("%s\n", loc("Resubmit as new entry")); + } + else if (atoi(str) == 1) + { + rsprintf("        \n"); + rsprintf("%s\n", loc("Resubmit as new entry")); + } + } + else + { + rsprintf("        \n"); + rsprintf("%s\n", loc("Resubmit as new entry")); + } + } + + rsprintf("
%s.
\n", + gt("Categories bgcolor1"), loc("If no attachments are resubmitted, the original ones are kept")); + rsprintf("%s.
%s %d:
%s %d:
%s
", gt("Categories bgcolor2"), att[i]+14); + rsprintf("
%s %d:
%s %d:
\n"); + + /*---- menu buttons again ----*/ + + rsprintf("\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + rsprintf("\n", loc("Submit")); + rsprintf("\n", loc("Back")); + rsprintf("
\n\n"); + + rsprintf("\n"); + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +void show_elog_find() +{ +int i, j, n_attr; +char str[256]; + + n_attr = scan_attributes(logbook); + + /*---- header ----*/ + + show_standard_header(loc("ELOG find"), NULL); + + /*---- title ----*/ + + show_standard_title(logbook, "", 0); + + /*---- menu buttons ----*/ + + rsprintf("\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + rsprintf("\n", loc("Search")); + rsprintf("\n", loc("Reset Form")); + rsprintf("\n", loc("Back")); + rsprintf("
\n\n"); + + /*---- entry form ----*/ + + /* overall table for message giving blue frame */ + rsprintf("\n", + gt("Categories border"), gt("Categories cellpadding"), gt("Frame color")); + + if (!getcfg(logbook, "Show text", str) || atoi(str) == 1) + { + rsprintf("\n"); + } + rsprintf("\n", loc("Show attachments")); + + rsprintf("\n", loc("Printable output")); + + rsprintf("\n", loc("Sort in reverse order")); + + /* count logbooks */ + for (i=0 ; ; i++) + { + if (!enumgrp(i, str)) + break; + + if (equal_ustring(str, "global")) + continue; + } + + if (i > 2) + { + rsprintf("\n", loc("Search all logbooks")); + } + + rsprintf("", gt("Categories bgcolor1"), loc("Start date")); + rsprintf("\n"); + + rsprintf("", gt("Categories bgcolor1"), loc("End date")); + rsprintf("\n"); + + rsprintf("\n"); + + for (i=0 ; i", gt("Categories bgcolor1"), attr_list[i]); + rsprintf("\n"); + } + + if (!getcfg(logbook, "Show text", str) || atoi(str) == 1) + { + rsprintf("", gt("Categories bgcolor1"), loc("Text")); + rsprintf("\n", loc("(case insensitive substring)")); + } + + rsprintf("
", gt("Categories bgcolor2")); + + if (getcfg(logbook, "Summary on default", str) && atoi(str) == 1) + rsprintf("%s\n", loc("Summary only")); + else + rsprintf("%s\n", loc("Summary only")); + rsprintf("
", gt("Categories bgcolor2")); + rsprintf("%s
", gt("Categories bgcolor2")); + rsprintf("%s
", gt("Categories bgcolor2")); + rsprintf("%s
", gt("Categories bgcolor2")); + rsprintf("%s
%s:\n"); + + rsprintf("\n"); + + rsprintf(" %s: ", loc("Year")); + rsprintf("
%s:\n"); + + rsprintf("\n"); + + rsprintf(" %s: ", loc("Year")); + rsprintf("
%s:", gt("Categories bgcolor2")); + if (attr_options[i][0][0] == 0) + { + rsprintf("\n", attr_list[i]); + rsprintf("%s\n", loc("(case insensitive substring)")); + } + else + { + if (equal_ustring(attr_options[i][0], "boolean")) + rsprintf("\n", attr_list[i]); + else + { + rsprintf("\n"); + } + } + rsprintf("
%s:\n", + gt("Categories bgcolor2")); + rsprintf("%s
\n"); + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +void show_config_page() +{ +int fh, length; +char *buffer; + + /*---- header ----*/ + + rsprintf("HTTP/1.1 200 Document follows\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + rsprintf("Content-Type: text/html\r\n\r\n"); + + rsprintf("ELOG config\n"); + rsprintf("\n", + logbook_enc); + + /*---- title ----*/ + + show_standard_title(logbook, "", 0); + + /*---- menu buttons ----*/ + + rsprintf("\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + rsprintf("\n", loc("Save")); + rsprintf("\n", loc("Cancel")); + rsprintf("
\n\n"); + + /*---- entry form ----*/ + + /* overall table for message giving blue frame */ + rsprintf("\n", + gt("Categories border"), gt("Categories cellpadding"), gt("Frame color")); + + rsprintf("
", gt("Categories bgcolor2")); + + fh = open(cfg_file, O_RDONLY | O_BINARY); + if (fh < 0) + { + rsprintf("Cannot read configuration file \"%s\"", cfg_file); + rsprintf("
\n"); + rsprintf("\r\n"); + return; + } + length = lseek(fh, 0, SEEK_END); + lseek(fh, 0, SEEK_SET); + buffer = malloc(length+1); + if (buffer == NULL) + { + close(fh); + rsprintf("Error: out of memory

"); + rsprintf("\n"); + rsprintf("\r\n"); + return; + } + read(fh, buffer, length); + buffer[length] = 0; + close(fh); + + rsprintf("
\n"); + + rsprintf("\n"); + + /*---- menu buttons ----*/ + + rsprintf("\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + rsprintf("\n", loc("Save")); + rsprintf("\n", loc("Cancel")); + rsprintf("
\n\n"); + + rsprintf("\n\n"); + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +int save_config() +{ +int fh, i; +char str[80]; + + fh = open(cfg_file, O_RDWR | O_BINARY | O_TRUNC, 644); + if (fh < 0) + { + sprintf(str, "%s: %s", loc("Cannot open file %s"), cfg_file, strerror(errno)); + show_error(str); + return 0; + } + i = write(fh, _text, strlen(_text)); + if (i < (int)strlen(_text)) + { + sprintf(str, "%s: %s", loc("Cannot write to %s"), cfg_file, strerror(errno)); + show_error(str); + close(fh); + return 0; + } + + close(fh); + return 1; +} + +/*------------------------------------------------------------------*/ + +void show_elog_delete(char *path) +{ +int status; +char str[256]; + + /* redirect if confirm = NO */ + if (getparam("confirm") && *getparam("confirm") && + strcmp(getparam("confirm"), loc("No")) == 0) + { + sprintf(str, "%s", path); + redirect(str); + return; + } + + /* header */ + sprintf(str, "%s", path); + show_standard_header("Delete ELog entry", str); + + rsprintf("

", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + + if (getparam("confirm") && *getparam("confirm")) + { + if (strcmp(getparam("confirm"), loc("Yes")) == 0) + { + /* delete message */ + status = el_delete_message(path); + rsprintf("\n", loc("Message successfully deleted")); + else + rsprintf("%s = %d\n", loc("Error deleting message: status"), status); + + rsprintf("\n", loc("Last")); + rsprintf("\n", + gt("Cell BGColor"), loc("Goto last message")); + } + } + else + { + /* define hidden field for command */ + rsprintf("\n", loc("Delete")); + + rsprintf("\n", gt("Title fontcolor"), + loc("Are you sure to delete this message?")); + + rsprintf("\n", gt("Cell BGColor"), path); + + rsprintf("\n\n"); + } + + rsprintf("
"); + if (status == EL_SUCCESS) + rsprintf("%s
", gt("Title bgcolor")); + rsprintf("%s
%s
\n", + gt("Cell BGColor"), loc("Yes")); + rsprintf("\n", loc("No")); + rsprintf("
\n"); + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +void show_elog_submit_find(INT past_n, INT last_n) +{ +int i, j, n, size, status, d1, m1, y1, d2, m2, y2, index, colspan, i_line, n_line, i_col; +int current_year, current_month, current_day, printable, n_logbook, lindex, n_attr, + reverse, n_attr_disp, n_found; +char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], disp_attr[MAX_N_ATTR][NAME_LENGTH], + list[10000], text[TEXT_SIZE], text1[TEXT_SIZE], text2[TEXT_SIZE], + orig_tag[80], reply_tag[80], attachment[MAX_ATTACHMENTS][256], encoding[80]; +char str[256], tag[256], ref[256], file_name[256], col[80], old_data_dir[256]; +char logbook_list[100][256], lb_enc[256], *nowrap, format[80]; +char menu_str[1000], menu_item[MAX_N_LIST][NAME_LENGTH]; +char *p , *pt, *pt1, *pt2; +BOOL full, show_attachments; +time_t ltime, ltime_start, ltime_end, ltime_current, now; +struct tm tms, *ptms; +FILE *f; + + n_attr = scan_attributes(logbook); + + printable = atoi(getparam("Printable")); + + if (*getparam("Reverse")) + reverse = atoi(getparam("Reverse")); + else + { + reverse = 0; + if (getcfg(logbook, "Reverse sort", str)) + reverse = atoi(str); + } + + /*---- header ----*/ + + show_standard_header(loc("ELOG search result"), NULL); + + /*---- title ----*/ + + strcpy(str, ", "); + if (past_n == 1) + strcat(str, loc("Last day")); + else if (past_n > 1) + sprintf(str+strlen(str), loc("Last %d days"), past_n); + else if (last_n) + sprintf(str+strlen(str), loc("Last %d entries"), last_n); + + if (printable) + show_standard_title(logbook, str, 1); + else + show_standard_title(logbook, str, 0); + + /* get mode */ + if (past_n || last_n) + { + if (getcfg(logbook, "Summary on default", str)) + full = !atoi(str); + else + full = TRUE; + + show_attachments = FALSE; + } + else + { + full = !(*getparam("mode")); + show_attachments = (*getparam("attach") > 0); + } + + time(&now); + ptms = localtime(&now); + current_year = ptms->tm_year+1900; + current_month = ptms->tm_mon+1; + current_day = ptms->tm_mday; + + /*---- menu buttons ----*/ + + if (!printable) + { + rsprintf("\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + if (getcfg(logbook, "Find menu commands", menu_str)) + { + n = strbreak(menu_str, menu_item, MAX_N_LIST); + + if (atoi(gt("Use buttons")) == 1) + { + for (i=0 ; i\n", str); + } + + if (last_n) + { + sprintf(str, loc("Last %d entries"), last_n*2); + rsprintf("\n", str); + } + } + else + rsprintf("\n", menu_item[i]); + } + } + else + { + rsprintf("\n"); + + for (i=0 ; i%s |\n", logbook_enc, past_n*2, str); + } + + if (last_n) + { + sprintf(str, loc("Last %d entries"), last_n*2); + rsprintf(" %s |\n", logbook_enc, last_n*2, str); + } + } + else + { + strcpy(str, menu_item[i]); + url_encode(str); + + if (i < n-1) + rsprintf(" %s |\n", logbook_enc, str, menu_item[i]); + else + rsprintf(" %s \n", logbook_enc, str, menu_item[i]); + } + } + + rsprintf("\n"); + } + } + else + { + if (atoi(gt("Use buttons")) == 1) + { + if (past_n) + { + sprintf(str, loc("Last %d days"), past_n*2); + rsprintf("\n", str); + } + + if (last_n) + { + sprintf(str, loc("Last %d entries"), last_n*2); + rsprintf("\n", str); + } + + rsprintf("\n", loc("Find")); + rsprintf("\n", loc("Back")); + } + else + { + rsprintf("\n"); + + if (past_n) + { + sprintf(str, loc("Last %d days"), past_n*2); + rsprintf(" %s |\n", logbook_enc, past_n*2, str); + } + + if (last_n) + { + sprintf(str, loc("Last %d entries"), last_n*2); + rsprintf(" %s |\n", logbook_enc, last_n*2, str); + } + + rsprintf(" %s |\n", logbook_enc, loc("Find"), loc("Find")); + rsprintf(" %s \n", logbook_enc, loc("Back"), loc("Back")); + + rsprintf("\n"); + } + + } + + rsprintf("
\n\n"); + } + + /*---- convert end date to ltime ----*/ + + ltime_end = ltime_start = 0; + + if (!past_n && !last_n) + { + if (*getparam("m1") || *getparam("y1") || *getparam("d1")) + { + /* if year not given, use current year */ + if (!*getparam("y1")) + y1 = current_year; + else + y1 = atoi(getparam("y1")); + if (y1 < 1990 || y1 > current_year) + { + rsprintf("\r\n"); + rsprintf("

Error: year %s out of range

", getparam("y1")); + rsprintf("\r\n"); + return; + } + + /* if month not given, use current month */ + if (*getparam("m1")) + { + strcpy(str, getparam("m1")); + for (m1=0 ; m1<12 ; m1++) + if (equal_ustring(str, mname[m1])) + break; + if (m1 == 12) + m1 = 0; + m1++; + } + else + m1 = current_month; + + /* if day not given, use 1 */ + if (*getparam("d1")) + d1 = atoi(getparam("d1")); + else + d1 = 1; + + memset(&tms, 0, sizeof(struct tm)); + tms.tm_year = y1 % 100; + tms.tm_mon = m1-1; + tms.tm_mday = d1; + tms.tm_hour = 0; + + if (tms.tm_year < 90) + tms.tm_year += 100; + ltime_start = mktime(&tms); + } + + if (*getparam("m2") || *getparam("y2") || *getparam("d2")) + { + /* if year not give, use current year */ + if (*getparam("y2")) + y2 = atoi(getparam("y2")); + else + y2 = current_year; + + if (y2 < 1990 || y2 > current_year) + { + rsprintf("\r\n"); + rsprintf("

Error: year %d out of range

", y2); + rsprintf("\r\n"); + return; + } + + /* if month not given, use current month */ + if (*getparam("m2")) + { + strcpy(str, getparam("m2")); + for (m2=0 ; m2<12 ; m2++) + if (equal_ustring(str, mname[m2])) + break; + if (m2 == 12) + m2 = 0; + m2++; + } + else + m2 = current_month; + + /* if day not given, use last day of month */ + if (*getparam("d2")) + d2 = atoi(getparam("d2")); + else + { + memset(&tms, 0, sizeof(struct tm)); + tms.tm_year = y2 % 100; + tms.tm_mon = m2-1+1; + tms.tm_mday = 1; + tms.tm_hour = 12; + + if (tms.tm_year < 90) + tms.tm_year += 100; + ltime = mktime(&tms); + ltime -= 3600*24; + memcpy(&tms, localtime(<ime), sizeof(struct tm)); + d2 = tms.tm_mday; + } + + memset(&tms, 0, sizeof(struct tm)); + tms.tm_year = y2 % 100; + tms.tm_mon = m2-1; + tms.tm_mday = d2; + tms.tm_hour = 0; + tms.tm_min = 0; + tms.tm_sec = 0; + + if (tms.tm_year < 90) + tms.tm_year += 100; + ltime_end = mktime(&tms); + + /* end time is first second of next day */ + ltime_end += 3600*24; + + memcpy(&tms, localtime(<ime_end), sizeof(struct tm)); + y2 = tms.tm_year + 1900; + m2 = tms.tm_mon +1; + d2 = tms.tm_mday; + } + } + + if (ltime_start && ltime_end && ltime_start > ltime_end) + { + rsprintf("\r\n"); + rsprintf("

Error: start time after end time

", y2); + rsprintf("\r\n"); + return; + } + + /*---- display filters ----*/ + + rsprintf("\n", + printable ? "1" : gt("Border width"), gt("Categories cellpadding")); + + if (*getparam("m1") || *getparam("y1") || *getparam("d1")) + rsprintf("", + gt("Categories bgcolor1"), loc("Start date"), gt("Categories bgcolor2"), mname[m1-1], d1, y1); + + if (*getparam("m2") || *getparam("y2") || *getparam("d2")) + { + /* calculate previous day */ + memset(&tms, 0, sizeof(struct tm)); + tms.tm_year = y2 % 100; + tms.tm_mon = m2-1; + tms.tm_mday = d2; + tms.tm_hour = 12; + + if (tms.tm_year < 90) + tms.tm_year += 100; + ltime = mktime(&tms); + ltime -= 3600*24; + memcpy(&tms, localtime(<ime), sizeof(struct tm)); + + rsprintf("", + gt("Categories bgcolor1"), loc("End date"), gt("Categories bgcolor2"), + mname[tms.tm_mon], tms.tm_mday, tms.tm_year + 1900); + } + + for (i=0 ; i", + gt("Categories bgcolor1"), attr_list[i], gt("Categories bgcolor2"), getparam(attr_list[i])); + } + + if (*getparam("subtext")) + { + rsprintf("", gt("Categories bgcolor1"), loc("Text")); + rsprintf("", + gt("Categories bgcolor2"), getparam("subtext")); + } + + rsprintf("
%s:%s %d, %d
%s:%s %d, %d
%s:%s
%s:%s
\n\n"); + + /* get number of summary lines */ + n_line = 3; + if (getcfg(logbook, "Summary lines", str)) + n_line = atoi(str); + + /*---- table titles ----*/ + + rsprintf("\n", + printable ? "1" : gt("Border width"), gt("Categories cellpadding"), gt("Frame color")); + + strcpy(col, gt("Categories bgcolor1")); + rsprintf("\n"); + + size = printable ? 2 : 3; + + rsprintf("", col, size); + + if (atoi(getparam("all")) == 1) + rsprintf("", col, size); + + rsprintf("", col, size); + + if (getcfg(logbook, "Display search", list)) + n_attr_disp = strbreak(list, disp_attr, MAX_N_ATTR); + else + { + n_attr_disp = n_attr; + memcpy(disp_attr, attr_list, sizeof(attr_list)); + } + + for (i=0 ; i%s", + col, size, attr_list[i]); + } + + if (!full && n_line > 0) + rsprintf("", + col, size); + + rsprintf("\n\n"); + + /*---- do find ----*/ + + i_col = 0; + n_found = 0; + + old_data_dir[0] = 0; + if (atoi(getparam("all")) == 1) + { + /* count logbooks */ + for (i=n_logbook=0 ; ; i++) + { + if (!enumgrp(i, str)) + break; + + if (equal_ustring(str, "global")) + continue; + + strcpy(logbook_list[n_logbook++], str); + } + + strcpy(old_data_dir, data_dir); + } + else + { + strcpy(logbook_list[0], logbook); + n_logbook = 1; + } + + /* loop through all logbooks */ + for (lindex=0 ; lindex 1) + { + /* set data_dir from logbook */ + getcfg(logbook_list[lindex], "Data dir", str); + if (str[0] == DIR_SEPARATOR || str[1] == ':') + strcpy(data_dir, str); + else + { + strcpy(data_dir, cfg_dir); + strcat(data_dir, str); + } + + if (data_dir[strlen(data_dir)-1] != DIR_SEPARATOR) + strcat(data_dir, DIR_SEPARATOR_STR); + } + + if (past_n) + { + if (reverse) + { + strcpy(tag, "-1"); + el_search_message(tag, NULL, TRUE, FALSE); + + time(&now); + ltime_start = now-3600*24*past_n; + } + else + { + time(&now); + ltime_start = now-3600*24*past_n; + ptms = localtime(<ime_start); + + sprintf(tag, "%02d%02d%02d.0", ptms->tm_year % 100, ptms->tm_mon+1, ptms->tm_mday); + } + } + else if (last_n) + { + strcpy(tag, "-1"); + el_search_message(tag, NULL, TRUE, FALSE); + + if (!reverse) + for (i=1 ; i 0) + if (ltime_current < ltime_start) + break; + + /* check for end date */ + if (ltime_end > 0) + { + if (ltime_current > ltime_end) + continue; + } + } + else + { + /* check for start date */ + if (ltime_start > 0) + if (ltime_current < ltime_start) + continue; + + /* check for end date */ + if (ltime_end > 0) + { + if (ltime_current > ltime_end) + break; + } + } + + if (status == EL_SUCCESS) + { + /* do filtering */ + for (i=0 ; i"); + + pt2 += strlen(pt2); + + /* copy origial search text */ + memcpy(pt2, pt, strlen(str)); + pt2 += strlen(str); + pt += strlen(str); + + /* add coloring 2nd part */ + if (equal_ustring(encoding, "plain")) + strcpy(pt2, "\001/B\002"); + else + strcpy(pt2, ""); + pt2 += strlen(pt2); + } + } while (p != NULL); + + strcpy(pt2, pt); + strcpy(text, text2); + } + + /* filter passed: display line */ + + n_found++; + + strcpy(str, tag); + if (strchr(str, '+')) + *strchr(str, '+') = 0; + if (strchr(str, '-')) + *strchr(str, '-') = 0; + + strcpy(lb_enc, logbook_list[lindex]); + url_encode(lb_enc); + sprintf(ref, "/%s/%s", lb_enc, str); + + if (full) + { + strcpy(col, gt("List bgcolor1")); + rsprintf(""); + + size = printable ? 2 : 3; + nowrap = printable ? "" : "nowrap"; + + rsprintf("", col, size, ref, n_found); + + if (atoi(getparam("all")) == 1) + rsprintf("", nowrap, col, size, logbook_list[lindex]); + + if (getcfg(logbook, "Date format", format)) + { + struct tm ts; + + memset(&ts, 0, sizeof(ts)); + + for (i=0 ; i<12 ; i++) + if (strncmp(date+4, mname[i], 3) == 0) + break; + ts.tm_mon = i; + + ts.tm_mday = atoi(date+8); + ts.tm_hour = atoi(date+11); + ts.tm_min = atoi(date+14); + ts.tm_sec = atoi(date+17); + ts.tm_year = atoi(date+20)-1900; + + mktime(&ts); + strftime(str, sizeof(str), format, &ts); + } + else + strcpy(str, date); + + rsprintf("", nowrap, col, size, str); + + for (i=0 ; i\n", col); + else + rsprintf("\n", col); + } + else + { + rsprintf(""); + } + } + + rsprintf("\n"); + + if (atoi(getparam("all")) == 1) + colspan = 3+n_attr_disp; + else + colspan = 2+n_attr_disp; + + if (!getcfg(logbook, "Show text", str) || atoi(str) == 1) + { + rsprintf("\n"); + } + + if (!show_attachments && attachment[0][0]) + { + rsprintf("", colspan, ref); + } + else + { + rsprintf("\n"); + } + } + } + } + + if (!show_attachments && attachment[0][0]) + rsprintf("\n"); + + } + else + { + if (i_col % 2 == 0) + strcpy(col, gt("List bgcolor1")); + else + strcpy(col, gt("List bgcolor2")); + i_col++; + + rsprintf(""); + + size = printable ? 2 : 3; + nowrap = printable ? "" : "nowrap"; + + rsprintf("", col, size, ref, n_found); + + if (atoi(getparam("all")) == 1) + rsprintf("", nowrap, col, logbook_list[lindex]); + + if (getcfg(logbook, "Date format", format)) + { + struct tm ts; + + memset(&ts, 0, sizeof(ts)); + + for (i=0 ; i<12 ; i++) + if (strncmp(date+4, mname[i], 3) == 0) + break; + ts.tm_mon = i; + + ts.tm_mday = atoi(date+8); + ts.tm_hour = atoi(date+11); + ts.tm_min = atoi(date+14); + ts.tm_sec = atoi(date+17); + ts.tm_year = atoi(date+20)-1900; + + mktime(&ts); + strftime(str, sizeof(str), format, &ts); + } + else + strcpy(str, date); + + rsprintf("", nowrap, col, size, str); + + for (i=0 ; i\n", col); + else + rsprintf("\n", col); + } + else + rsprintf("", col, size, attrib[i]); + } + + if (n_line > 0) + { + rsprintf("\n"); + } + + rsprintf("\n"); + } + } + + /* stop after all found (needed for reverse search) */ + if (last_n && n_found >= last_n) + break; + + } while (status == EL_SUCCESS); + } /* for () */ + + rsprintf("
#LogbookDateText
  %d  %s%s", col, size); + rsputs2(attrib[i]); + rsprintf(" 
", colspan, size); + + if (equal_ustring(encoding, "plain")) + { + rsputs("
");
+              rsputs2(text);
+              rsputs("
"); + } + else + rsputs(text); + + rsprintf("
", colspan, size); + + if (attachment[1][0]) + rsprintf("%s: ", loc("Attachments")); + else + rsprintf("%s: ", loc("Attachment")); + } + + for (index = 0 ; index < MAX_ATTACHMENTS ; index++) + { + if (attachment[index][0]) + { + strcpy(str, attachment[index]); + str[13] = 0; + sprintf(ref, "/%s/%s/%s", logbook_enc, str, attachment[index]+14); + + for (i=0 ; i<(int)strlen(attachment[index]) ; i++) + str[i] = toupper(attachment[index][i]); + str[i] = 0; + + if (!show_attachments) + { + rsprintf("%s     ", + ref, attachment[index]+14); + } + else + { + if (strstr(str, ".GIF") || + strstr(str, ".JPG") || + strstr(str, ".PNG")) + { + rsprintf("
%s %d: %s\n", + colspan, gt("List bgcolor2"), loc("Attachment"), index+1, ref, attachment[index]+14); + if (show_attachments) + rsprintf("
%s %d: %s\n", + colspan, gt("List bgcolor2"), loc("Attachment"), index+1, ref, attachment[index]+14); + + if ((strstr(str, ".TXT") || + strstr(str, ".ASC") || + strchr(str, '.') == NULL) && show_attachments) + { + /* display attachment */ + rsprintf("
", size);
+
+                    strcpy(file_name, data_dir);
+
+                    strcat(file_name, attachment[index]);
+
+                    f = fopen(file_name, "rt");
+                    if (f != NULL)
+                      {
+                      while (!feof(f))
+                        {
+                        str[0] = 0;
+                        fgets(str, sizeof(str), f);
+                        rsputs2(str);
+                        }
+                      fclose(f);
+                      }
+
+                    rsprintf("
\n"); + } + rsprintf("
  %d  %s%s%s ", col, size); + for (i=i_line=0 ; i
\n"); + + if (n_found == 0) + { + rsprintf("\n", + printable ? "1" : gt("Border width"), gt("Categories cellpadding"), gt("Frame color")); + + rsprintf("", loc("No entries found")); + + rsprintf("
%s
\n"); + } + + rsprintf("\n"); + + if (getcfg(logbook, "bottom text", str)) + { + FILE *f; + char file_name[256], *buf; + + strcpy(file_name, cfg_dir); + strcat(file_name, str); + + f = fopen(file_name, "r"); + if (f != NULL) + { + fseek(f, 0, SEEK_END); + size = TELL(fileno(f)); + fseek(f, 0, SEEK_SET); + + buf = malloc(size+1); + fread(buf, 1, size, f); + buf[size] = 0; + fclose(f); + + rsputs(buf); + } + } + else + /* add little logo */ + rsprintf("
ELOG V%s
", VERSION); + + rsprintf("\r\n"); + + /* restor old data_dir */ + if (old_data_dir[0]) + strcpy(data_dir, old_data_dir); +} + +/*------------------------------------------------------------------*/ + +void submit_elog() +{ +char str[256], mail_to[256], mail_from[256], file_name[256], error[1000], + mail_text[10000], mail_list[MAX_N_LIST][NAME_LENGTH], list[10000], smtp_host[256], + tag[80], subject[256], attrib[MAX_N_ATTR][NAME_LENGTH], subst_str[256]; +char *buffer[MAX_ATTACHMENTS], mail_param[1000]; +char att_file[MAX_ATTACHMENTS][256]; +char slist[MAX_N_ATTR+10][NAME_LENGTH], svalue[MAX_N_ATTR+10][NAME_LENGTH]; +int i, j, n, missing, first, index, n_attr, n_mail, suppress, status; + + n_attr = scan_attributes(logbook); + + /* check for required attributs */ + missing = 0; + for (i=0 ; i

", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + rsprintf("\n"); + + rsprintf("\n", loc("Back")); + + + rsprintf("
"); + + rsprintf(""); + rsprintf(loc("Error: Attribute %s not supplied"), attr_list[i]); + rsprintf(".

\n"); + rsprintf(loc("Please go back and enter the %s field"), attr_list[i]); + rsprintf(".

", gt("Cell BGColor")); + rsprintf("
\n"); + rsprintf("\n"); + return; + } + + /* check for valid attachment files */ + for (i=0 ; i"))) + { + sprintf(error, loc("Error: Attachment file %s invalid, please bo back and enter a proper file name"), getparam(str)); + show_error(error); + return; + } + } + + /* compile substitution list */ + n = build_subst_list(slist, svalue, NULL); + + /* retrieve attributes */ + for (i=0 ; i"); + strcat(str, loc("Please check that it exists and elogd has write access")); + show_error(str); + return; + } + + suppress = atoi(getparam("suppress")); + + /* check for mail submissions */ + mail_param[0] = 0; + n_mail = 0; + + if (suppress) + { + strcpy(mail_param, "?suppress=1"); + } + else + { + for (index=0 ; index <= n_attr ; index++) + { + if (index < n_attr) + { + strcpy(str, "Email "); + if (strchr(attr_list[index], ' ')) + sprintf(str+strlen(str), "\"%s\"", attr_list[index]); + else + strcat(str, attr_list[index]); + strcat(str, " "); + + if (strchr(getparam(attr_list[index]), ' ')) + sprintf(str+strlen(str), "\"%s\"", getparam(attr_list[index])); + else + strcat(str, getparam(attr_list[index])); + } + else + sprintf(str, "Email ALL"); + + if (getcfg(logbook, str, list)) + { + n = strbreak(list, mail_list, MAX_N_LIST); + + if (verbose) + printf("\n%s to %s\n\n", str, list); + + if (!getcfg("global", "SMTP host", smtp_host)) + if (!getcfg("global", "SMTP host", smtp_host)) + { + show_error(loc("No SMTP host defined in [global] section of configuration file")); + return; + } + + for (i=0 ; iredir\r\n", + logbook_enc, tag, mail_param); +} + +/*------------------------------------------------------------------*/ + +void copy_to(char *src_path, char *dst_logbook, int move) +{ +int size, i, status, fh, n_attr; +char str[256], file_name[256], attrib[MAX_N_ATTR][NAME_LENGTH]; +char date[80], text[TEXT_SIZE], old_data_dir[256], tag[32], + orig_tag[80], reply_tag[80], attachment[MAX_ATTACHMENTS][256], encoding[80]; + + n_attr = scan_attributes(logbook); + + /* get message */ + size = sizeof(text); + status = el_retrieve(src_path, date, attr_list, attrib, n_attr, + text, &size, orig_tag, reply_tag, + attachment, encoding); + + if (status != EL_SUCCESS) + { + sprintf(str, loc("Message %s cannot be read from logbook \"%s\""), src_path, logbook); + show_error(str); + return; + } + + /* read attachments */ + for (i=0 ; i 0) + { + lseek(fh, 0, SEEK_END); + _attachment_size[i] = TELL(fh); + lseek(fh, 0, SEEK_SET); + + _attachment_buffer[i] = malloc(_attachment_size[i]); + + if (_attachment_buffer[i]) + read(fh, _attachment_buffer[i], _attachment_size[i]); + + close(fh); + } + + /* stip date/time from file name */ + strcpy(str, attachment[i]); + strcpy(attachment[i], str+14); + } + + /* switch to destination logbook data directory */ + + strcpy(old_data_dir, data_dir); + getcfg(dst_logbook, "Data dir", str); + if (str[0] == DIR_SEPARATOR || str[1] == ':') + strcpy(data_dir, str); + else + { + strcpy(data_dir, cfg_dir); + strcat(data_dir, str); + } + + if (data_dir[strlen(data_dir)-1] != DIR_SEPARATOR) + strcat(data_dir, DIR_SEPARATOR_STR); + + tag[0] = 0; + status = el_submit(attr_list, attrib, n_attr, text, + orig_tag, encoding, + attachment, + _attachment_buffer, + _attachment_size, + tag, sizeof(tag)); + + for (i=0 ; i"); + strcat(str, loc("Please check that it exists and elogd has write access")); + show_error(str); + return; + } + + /* delete original message for move */ + if (move) + { + el_delete_message(src_path); + + /* check if this was the last message */ + size = sizeof(text); + status = el_retrieve(src_path, date, attr_list, attrib, n_attr, + text, &size, orig_tag, reply_tag, + attachment, encoding); + + /* if yes, force display of new last message */ + if (status != EL_SUCCESS) + src_path[0] = 0; + } + + /* display status message */ + show_standard_header(loc("Copy ELog entry"), src_path); + + rsprintf("

", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + + rsprintf("\n"); + rsprintf("\n", + gt("Cell BGColor"), loc("Go to"), logbook, src_path, logbook); + rsprintf("\n", + gt("Cell BGColor"), loc("Go to"), dst_logbook, dst_logbook); + + rsprintf("
"); + if (move) + rsprintf(loc("Message moved successfully from \"%s\" to \"%s\""), logbook, dst_logbook); + else + rsprintf(loc("Message copied successfully from \"%s\" to \"%s\""), logbook, dst_logbook); + + rsprintf("
%s %s
%s %s
\n"); + rsprintf("\r\n"); + + return; +} + +/*------------------------------------------------------------------*/ + +void show_elog_page(char *logbook, char *path) +{ +int size, i, j, len, n, msg_status, status, fh, length, first_message, last_message, index, n_attr; +char str[256], orig_path[256], command[80], ref[256], file_name[256], attrib[MAX_N_ATTR][NAME_LENGTH]; +char date[80], text[TEXT_SIZE], menu_str[1000], other_str[1000], cmd[256], + orig_tag[80], reply_tag[80], attachment[MAX_ATTACHMENTS][256], encoding[80], att[256], lattr[256]; +char menu_item[MAX_N_LIST][NAME_LENGTH], format[80], + slist[MAX_N_ATTR+10][NAME_LENGTH], svalue[MAX_N_ATTR+10][NAME_LENGTH]; +FILE *f; + + n_attr = scan_attributes(logbook); + + if (getcfg(logbook, "Types", str)) + { + show_upgrade_page(); + return; + } + + /*---- interprete commands ---------------------------------------*/ + + strcpy(command, getparam("cmd")); + + /* correct for image buttons */ + if (*getparam("cmd_first.x")) + strcpy(command, loc("First")); + if (*getparam("cmd_previous.x")) + strcpy(command, loc("Previous")); + if (*getparam("cmd_next.x")) + strcpy(command, loc("Next")); + if (*getparam("cmd_last.x")) + strcpy(command, loc("Last")); + + /* check if command allowed for current user */ + if (!allow_user(command)) + return; + + /* get menu command */ + getcfg(logbook, "Menu commands", menu_str); + if (menu_str[0] == 0) + { + strcpy(menu_str, loc("New")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Edit")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Delete")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Reply")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Find")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Last day")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Last 10")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Config")); + strcat(menu_str, ", "); + + if (getcfg(logbook, "Password file", str)) + { + strcat(menu_str, loc("Change Password")); + strcat(menu_str, ", "); + strcat(menu_str, loc("Logout")); + strcat(menu_str, ", "); + } + + strcat(menu_str, loc("Help")); + } + + strcpy(other_str, loc("Submit")); + strcat(other_str, " "); + strcat(other_str, loc("Back")); + strcat(other_str, " "); + strcat(other_str, loc("Search")); + strcat(other_str, " "); + strcat(other_str, loc("Save")); + strcat(other_str, " "); + strcat(other_str, loc("Cancel")); + strcat(other_str, " "); + strcat(other_str, loc("First")); + strcat(other_str, " "); + strcat(other_str, loc("Last")); + strcat(other_str, " "); + strcat(other_str, loc("Previous")); + strcat(other_str, " "); + strcat(other_str, loc("Next")); + + /* check if command is present in the menu list */ + if (command[0] && strstr(menu_str, command) == NULL && + strstr(other_str, command) == NULL) + { + sprintf(str, loc("Error: Command \"%s\" not allowed"), command); + show_error(str); + return; + } + + if (equal_ustring(command, loc("Help"))) + { + if (getcfg(logbook, "Help URL", str)) + { + /* if file is given, add '/' to make absolute path */ + if (strchr(str, '/') == NULL) + sprintf(ref, "/%s", str); + else + strcpy(ref, str); + + redirect3(ref); + return; + } + + /* send local help file */ + strcpy(file_name, cfg_dir); + strcat(file_name, "eloghelp_"); + + if (getcfg("global", "Language", str)) + { + str[2] = 0; + strcat(file_name, str); + } + else + strcat(file_name, "en"); + strcat(file_name, ".html"); + + f = fopen(file_name, "r"); + if (f == NULL) + redirect3("http://midas.psi.ch/elog/eloghelp_en.html"); + else + { + fclose(f); + send_file(file_name); + } + return; + } + + if (equal_ustring(command, loc("New"))) + { + show_elog_new(NULL, FALSE); + return; + } + + if (equal_ustring(command, loc("Edit"))) + { + if (path[0]) + { + show_elog_new(path, TRUE); + return; + } + } + + if (equal_ustring(command, loc("Reply"))) + { + show_elog_new(path, FALSE); + return; + } + + if (equal_ustring(command, loc("Submit"))) + { + submit_elog(); + return; + } + + if (equal_ustring(command, loc("Find"))) + { + show_elog_find(); + return; + } + + if (equal_ustring(command, loc("Search"))) + { + show_elog_submit_find(0, 0); + return; + } + + if (equal_ustring(command, loc("Last day"))) + { + redirect("past1"); + return; + } + + if (equal_ustring(command, loc("Last 10"))) + { + redirect("last10"); + return; + } + + if (equal_ustring(command, loc("Copy to"))) + { + copy_to(path, getparam("destc"), 0); + return; + } + + if (equal_ustring(command, loc("Move to"))) + { + copy_to(path, getparam("destm"), 1); + return; + } + + if (equal_ustring(command, loc("Config"))) + { + show_config_page(); + return; + } + + if (equal_ustring(command, loc("Change Password")) || + isparam("newpwd")) + { + show_change_pwd_page(); + return; + } + + if (equal_ustring(command, loc("Save"))) + { + if (!save_config()) + return; + redirect(""); + return; + } + + if (equal_ustring(command, loc("Logout"))) + { + rsprintf("HTTP/1.1 302 Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + /* delete user cookies */ + rsprintf("Set-Cookie: upwd=; path=/%s; expires=Fri, 01 Jan 1983 00:00:00 GMT\r\n", logbook_enc, str); + rsprintf("Set-Cookie: unm=; path=/%s; expires=Fri, 01 Jan 1983 00:00:00 GMT\r\n", logbook_enc, str); + + rsprintf("Location: /\r\n\r\nredir\r\n"); + return; + } + + strcpy(str, getparam("last")); + if (strchr(str, ' ')) + { + i = atoi(strchr(str, ' ')); + sprintf(str, "last%d", i); + redirect(str); + return; + } + + strcpy(str, getparam("past")); + if (strchr(str, ' ')) + { + i = atoi(strchr(str, ' ')); + sprintf(str, "past%d", i); + redirect(str); + return; + } + + if (equal_ustring(command, loc("Delete"))) + { + show_elog_delete(path); + return; + } + + if (strncmp(path, "past", 4) == 0) + { + show_elog_submit_find(atoi(path+4), 0); + return; + } + + if (strncmp(path, "last", 4) == 0 && strstr(path, ".gif") == NULL) + { + show_elog_submit_find(0, atoi(path+4)); + return; + } + + if (equal_ustring(command, loc("Back")) && + getcfg(logbook, "Back to main", str) && + atoi(str) == 1) + { + redirect3("/"); + return; + } + + /*---- check if file requested -----------------------------------*/ + + if ((strlen(path) > 13 && path[6] == '_' && path[13] == '_') || + (strlen(path) > 13 && path[6] == '_' && path[13] == '/') || + strstr(path, ".gif") || strstr(path, ".jpg") || strstr(path, ".png")) + { + if ((strlen(path) > 13 && path[6] == '_' && path[13] == '_') || + (strlen(path) > 13 && path[6] == '_' && path[13] == '/')) + { + if (path[13] == '/') + path[13] = '_'; + + /* file from data directory requested */ + strcpy(file_name, data_dir); + strcat(file_name, path); + } + else + { + /* file from theme directory requested */ + strcpy(file_name, cfg_dir); + strcat(file_name, theme_name); + strcat(file_name, DIR_SEPARATOR_STR); + strcat(file_name, path); + } + + send_file(file_name); + return; + } + + /*---- check next/previous message -------------------------------*/ + + last_message = first_message = FALSE; + if (equal_ustring(command, loc("Next")) || equal_ustring(command, loc("Previous")) || + equal_ustring(command, loc("Last")) || equal_ustring(command, loc("First"))) + { + strcpy(orig_path, path); + + if (equal_ustring(command, loc("Last")) || equal_ustring(command, loc("First"))) + path[0] = 0; + + do + { + if (equal_ustring(command, loc("Next")) || equal_ustring(command, loc("First"))) + strcat(path, "+1"); + if (equal_ustring(command, loc("Previous")) || equal_ustring(command, loc("Last"))) + strcat(path, "-1"); + + status = el_search_message(path, NULL, TRUE, equal_ustring(command, loc("First"))); + if (status != EL_SUCCESS) + { + if (equal_ustring(command, loc("Next"))) + last_message = TRUE; + else + first_message = TRUE; + strcpy(path, orig_path); + break; + } + + size = sizeof(text); + el_retrieve(path, date, attr_list, attrib, n_attr, + text, &size, orig_tag, reply_tag, + attachment, encoding); + + /* check for locked attributes */ + for (i=0 ; i\n", + gt("Menu1 cellpadding"), gt("Frame color")); + + rsprintf("\n"); + + if (atoi(gt("Merge menus")) != 1) + { + rsprintf("\n"); + rsprintf("
\n", gt("Menu1 Align"), gt("Menu1 BGColor")); + + n = strbreak(menu_str, menu_item, MAX_N_LIST); + + if (atoi(gt("Use buttons")) == 1) + { + for (i=0 ; i\n", cmd); + + if (equal_ustring(cmd, loc("Copy to")) || + equal_ustring(cmd, loc("Move to"))) + { + /* put one link for each logbook except current one */ + if (equal_ustring(cmd, loc("Copy to"))) + rsprintf("\n"); + + for (j=0 ; ; j++) + { + if (!enumgrp(j, str)) + break; + + if (equal_ustring(str, "global")) + continue; + + if (equal_ustring(str, logbook)) + continue; + + rsprintf("
\n\n"); + } + + /*---- next/previous buttons ----*/ + + if (!getcfg(logbook, "Enable browsing", str) || + atoi(str) == 1) + { + if (atoi(gt("Merge menus")) != 1) + { + rsprintf("\n", + gt("Menu2 cellpadding"), gt("Frame color")); + rsprintf("\n"); + } + + rsprintf("
\n", gt("Menu2 Align"), gt("Menu2 BGColor")); + } + else + rsprintf("\n", gt("Menu2 Align"), gt("Menu2 BGColor")); + + if (atoi(gt("Menu2 use images")) == 1) + { + rsprintf("\n", + loc("First entry"), logbook_enc); + rsprintf("\n", + loc("Previous entry"), logbook_enc); + rsprintf("\n", + loc("Next entry"), logbook_enc); + rsprintf("\n", + loc("Last entry"), logbook_enc); + } + else + { + rsprintf("\n", loc("First")); + rsprintf("\n", loc("Previous")); + rsprintf("\n", loc("Next")); + rsprintf("\n", loc("Last")); + } + + rsprintf("
\n\n"); + + /*---- message ----*/ + + /* overall table for message giving blue frame */ + rsprintf("\n", + gt("Categories border"), gt("Categories cellpadding"), gt("Frame color")); + + if (msg_status == EL_FILE_ERROR || + (msg_status != EL_SUCCESS && !last_message && !first_message)) + rsprintf("\n", loc("No message available")); + else + { + /* check for locked attributes */ + for (i=0 ; i\"%s = %s\"", loc("with"), attr_list[i], getparam(attr_list[i])); + else + str[0] = 0; + + if (last_message) + rsprintf("\n", + loc("This is the last entry"), str); + + if (first_message) + rsprintf("\n", + loc("This is the first entry"), str); + + /* check for mail submissions */ + if (*getparam("suppress")) + { + if (getcfg(logbook, "Suppress default", str) && atoi(str) == 1) + rsprintf("\n", loc("Email notification suppressed")); + else + rsprintf("\n", loc("Email notification suppressed")); + i = 1; + } + else + { + for (i=0 ; ; i++) + { + sprintf(str, "mail%d", i); + if (*getparam(str)) + { + if (i==0) + rsprintf("\n"); + + /*---- display date ----*/ + + if (getcfg(logbook, "Date format", format)) + { + struct tm ts; + + memset(&ts, 0, sizeof(ts)); + + for (i=0 ; i<12 ; i++) + if (strncmp(date+4, mname[i], 3) == 0) + break; + ts.tm_mon = i; + + ts.tm_mday = atoi(date+8); + ts.tm_hour = atoi(date+11); + ts.tm_min = atoi(date+14); + ts.tm_sec = atoi(date+17); + ts.tm_year = atoi(date+20)-1900; + + mktime(&ts); + strftime(str, sizeof(str), format, &ts); + + rsprintf("\n\n"); + + /*---- link to original message or reply ----*/ + + if (msg_status != EL_FILE_ERROR && (reply_tag[0] || orig_tag[0])) + { + + if (orig_tag[0]) + { + rsprintf("\n", ref, orig_tag); + } + if (reply_tag[0]) + { + rsprintf("\n", ref, reply_tag); + } + } + + /*---- display attributes ----*/ + + for (i=0 ; i\n", + attr_list[i], gt("Categories bgcolor2")); + else + rsprintf("%s:\n", + attr_list[i], gt("Categories bgcolor2")); + } + else + { + rsprintf("%s:\n"); + } + } + + rsprintf("\n"); + rsputs("

%s

%s %s
%s %s
%s
%s
"); + rsprintf("%s %s
\n", loc("Mail sent to"), getparam(str)); + } + else + break; + } + } + + if (i>0) + rsprintf("
%s:%s\n\n", + gt("Categories bgcolor1"), loc("Entry date"), gt("Categories bgcolor2"), str); + } + else + rsprintf("
%s:%s\n\n", + gt("Categories bgcolor1"), loc("Entry date"), gt("Categories bgcolor2"), date); + + for (i=0 ; i\n", attr_list[i], attrib[i]); + + /* browsing flag to distinguish "/../=" from browsing */ + rsprintf("\n"); + + rsprintf("
", gt("Categories bgcolor1")); + sprintf(ref, "/%s/%s", logbook_enc, orig_tag); + rsprintf("%s:", loc("In reply to"), gt("Menu2 bgcolor")); + rsprintf("%s
", gt("Categories bgcolor1")); + sprintf(ref, "/%s/%s", logbook_enc, reply_tag); + rsprintf("%s:", loc("Reply to this"), gt("Menu2 bgcolor")); + rsprintf("%s
", gt("Categories bgcolor1")); + + if (!getcfg(logbook, "Filtered browsing", str) || + atoi(str) == 1) + { + if (*getparam(lattr) == '1') + rsprintf(" ", lattr); + else + rsprintf(" ", lattr); + } + + /* display checkbox for boolean attributes */ + if (equal_ustring(attr_options[i][0], "boolean")) + { + if (atoi(attrib[i]) == 1) + rsprintf("%s:
\n", + attr_list[i], gt("Categories bgcolor2")); + rsputs2(attrib[i]); + rsprintf(" 
\n"); + + /*---- message text ----*/ + + if (!getcfg(logbook, "Show text", str) || atoi(str) == 1) + { + rsprintf("\n", gt("Frame color")); + rsprintf("\n"); + rsputs("

\n", gt("Text BGColor")); + + if (equal_ustring(encoding, "plain")) + { + rsputs("
");
+        rsputs2(text);
+        rsputs("
"); + } + else + rsputs(text); + + rsputs("
\n"); + } + + for (index = 0 ; index < MAX_ATTACHMENTS ; index++) + { + if (attachment[index][0]) + { + for (i=0 ; i<(int)strlen(attachment[index]) ; i++) + att[i] = toupper(attachment[index][i]); + att[i] = 0; + + /* determine size of attachment */ + strcpy(file_name, data_dir); + strcat(file_name, attachment[index]); + + length = 0; + fh = open(file_name, O_RDONLY | O_BINARY); + if (fh > 0) + { + lseek(fh, 0, SEEK_END); + length = TELL(fh); + close(fh); + } + + strcpy(str, attachment[index]); + str[13] = 0; + sprintf(ref, "/%s/%s/%s", logbook_enc, str, attachment[index]+14); + + rsprintf("\n", gt("Frame color")); + + rsprintf("", + gt("Categories bgcolor1"), loc("Attachment"), index+1); + rsprintf("
 %s %d: %s\n", + gt("Categories bgcolor2"), ref, attachment[index]+14); + + if (length < 1024) + rsprintf(" %d Bytes", length); + else if (length < 1024*1024) + rsprintf(" %d kB", length/1024); + else + rsprintf(" %1.3lf MB", length/1024.0/1024.0); + + rsprintf("
\n"); + + if (!getcfg(logbook, "Show attachments", str) || + atoi(str) == 1) + { + if (strstr(att, ".GIF") || + strstr(att, ".JPG") || + strstr(att, ".PNG")) + { + rsprintf("\n", gt("Frame color")); + rsprintf("", ref); + rsprintf("
", gt("Text bgcolor")); + rsprintf("
\n\n"); + } + else + { + if (strstr(att, ".TXT") || + strstr(att, ".ASC") || + strchr(att, '.') == NULL) + { + /* display attachment */ + rsprintf("\n", gt("Frame color")); + rsprintf("
", gt("Text bgcolor")); + if (!strstr(att, ".HTML")) + rsprintf("
");
+
+              strcpy(file_name, data_dir);
+              strcat(file_name, attachment[index]);
+
+              f = fopen(file_name, "rt");
+              if (f != NULL)
+                {
+                while (!feof(f))
+                  {
+                  str[0] = 0;
+                  fgets(str, sizeof(str), f);
+
+                  if (!strstr(att, ".HTML"))
+                    rsputs2(str);
+                  else
+                    rsputs(str);
+                  }
+                fclose(f);
+                }
+
+              if (!strstr(att, ".HTML"))
+                rsprintf("
"); + rsprintf("\n"); + rsprintf("
\n"); + } + } + } + } + } + } + + /* table for message body */ + rsprintf("\n\n"); + + /* overall table */ + rsprintf("\n"); + + if (getcfg(logbook, "bottom text", str)) + { + FILE *f; + char file_name[256], *buf; + + strcpy(file_name, cfg_dir); + strcat(file_name, str); + + f = fopen(file_name, "r"); + if (f != NULL) + { + fseek(f, 0, SEEK_END); + size = TELL(fileno(f)); + fseek(f, 0, SEEK_SET); + + buf = malloc(size+1); + fread(buf, 1, size, f); + buf[size] = 0; + fclose(f); + + rsputs(buf); + } + } + else + /* add little logo */ + rsprintf("

ELOG V%s
", VERSION); + + rsprintf("\r\n"); +} + +/*------------------------------------------------------------------*/ + +BOOL check_password(char *logbook, char *name, char *password, char *redir) +{ +char str[256]; + + /* get password from configuration file */ + if (getcfg(logbook, name, str)) + { + if (strcmp(password, str) == 0) + return TRUE; + + /* show web password page */ + show_standard_header(loc("ELOG password"), NULL); + + /* define hidden fields for current destination */ + if (redir[0]) + rsprintf("\n", redir); + + rsprintf("

", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + + if (password[0]) + rsprintf("\n", loc("Wrong password")); + + rsprintf("\n", + gt("Title fontcolor"), loc("Please enter password to obtain write access")); + rsprintf("\n", gt("Cell BGColor")); + } + else + { + rsprintf("%s\n", + gt("Title fontcolor"), loc("Please enter password to obtain administration access")); + rsprintf("\n", gt("Cell BGColor")); + } + + rsprintf("", gt("Cell BGColor"), loc("Submit")); + + rsprintf("
%s!
\n", gt("Title bgcolor")); + + if (strcmp(name, "Write password") == 0) + { + rsprintf("%s
\n"); + + rsprintf("\r\n"); + + return FALSE; + } + else + return TRUE; +} + +/*------------------------------------------------------------------*/ + +BOOL check_user_password(char *logbook, char *user, char *password, char *redir) +{ +char str[256], line[256], file_name[256], *p; +FILE *f; + + getcfg(logbook, "Password file", str); + + if (str[0] == DIR_SEPARATOR || str[1] == ':') + strcpy(file_name, str); + else + { + strcpy(file_name, cfg_dir); + strcat(file_name, str); + } + + f = fopen(file_name, "r"); + if (f != NULL) + { + while (!feof(f)) + { + line[0] = 0; + fgets(line, sizeof(line), f); + + if (line[0] == ';' || line[0] == '#' || line[0] == 0) + continue; + + strcpy(str, line); + if (strchr(str, ':')) + *strchr(str, ':') = 0; + if (strcmp(str, user) == 0) + break; + } + fclose(f); + + /* if user found, check password */ + if (user[0] && (strcmp(str, user) == 0)) + { + p = line+strlen(str); + if (*p) + p++; + + strcpy(str, p); + if (strchr(str, ':')) + *strchr(str, ':') = 0; + + if (strcmp(password, str) == 0) + { + p += strlen(str); + if (*p) + p++; + strcpy(str, p); + if (strchr(str, ':')) + *strchr(str, ':') = 0; + if (strchr(str, '\r')) + *strchr(str, '\r') = 0; + if (strchr(str, '\n')) + *strchr(str, '\n') = 0; + setparam("full_name", str); + return TRUE; + } + } + + /* show login password page */ + show_standard_header("ELOG login", NULL); + + /* define hidden fields for current destination */ + if (redir[0]) + rsprintf("\n", redir); + + rsprintf("

", + gt("Border width"), gt("Frame color")); + rsprintf("
\n", gt("Frame color")); + + if (password[0]) + rsprintf("\n", loc("Wrong password")); + + rsprintf("\n", + gt("Title fontcolor"), loc("Please login")); + rsprintf("\n", + gt("Cell BGColor"), loc("Username")); + rsprintf("\n", + gt("Cell BGColor"), loc("Password")); + + rsprintf("", gt("Cell BGColor"), loc("Submit")); + + rsprintf("
%s!
\n", gt("Title bgcolor")); + + rsprintf("%s
%s:   
%s:   
\n"); + + rsprintf("\r\n"); + + return FALSE; + } + else + { + sprintf(line, "Error: Password file \"%s\" not found", file_name); + show_error(line); + return FALSE; + } +} + +/*------------------------------------------------------------------*/ + +void show_selection_page() +{ +int i; +char str[10000], logbook[256]; + + rsprintf("HTTP/1.1 200 Document follows\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + rsprintf("Content-Type: text/html\r\n\r\n"); + + rsprintf("\n"); + rsprintf("\n"); + + if (getcfg("global", "Page Title", str)) + { + rsprintf(""); + rsputs(str); + rsprintf("\n"); + } + else + rsprintf("ELOG Logbook Selection\n"); + + rsprintf("\n\n"); + + rsprintf("\n\n"); + + rsprintf("

"); + rsprintf("
\n"); + + rsprintf("\n", loc("Please select the one to connect to")); + } + + for (i=0 ; ; i++) + { + if (!enumgrp(i, logbook)) + break; + + if (equal_ustring(logbook, "global")) + continue; + + strcpy(str, logbook); + url_encode(str); + rsprintf("", str, logbook); + + str[0] = 0; + getcfg(logbook, "Comment", str); + rsprintf("\n", str); + } + + rsprintf("
\n"); + + if (getcfg("global", "Welcome title", str)) + { + rsputs(str); + } + else + { + rsprintf("\n"); + rsprintf("%s.
\n", loc("Several logbooks are defined on this host")); + rsprintf("%s:
%s%s 
\n"); + rsprintf("\r\n\r\n"); + +} + +/*------------------------------------------------------------------*/ + +void get_password(char *password) +{ +static char last_password[32]; + + if (strncmp(password, "set=", 4) == 0) + strcpy(last_password, password+4); + else + strcpy(password, last_password); +} + +/*------------------------------------------------------------------*/ + +void interprete(char *path) +/********************************************************************\ + + Routine: interprete + + Purpose: Interprete parametersand generate HTML output. + + Input: + char *path Message path + + + _param/_value array accessible via getparam() + +\********************************************************************/ +{ +int i, n, index; +double exp; +char str[256], enc_pwd[80], file_name[256]; +char enc_path[256], dec_path[256]; +char *experiment, *command, *value, *group; +time_t now; +struct tm *gmt; + + /* encode path for further usage */ + strcpy(dec_path, path); + url_decode(dec_path); + url_decode(dec_path); /* necessary for %2520 -> %20 -> ' ' */ + strcpy(enc_path, dec_path); + url_encode(enc_path); + + experiment = getparam("exp"); + command = getparam("cmd"); + value = getparam("value"); + group = getparam("group"); + index = atoi(getparam("index")); + + /* if experiment given, use it as logbook (for elog!) */ + if (experiment && experiment[0]) + { + strcpy(logbook_enc, experiment); + strcpy(logbook, experiment); + url_decode(logbook); + } + + /* check for global selection page if no logbook given */ + if (!logbook[0] && getcfg("global", "Selection page", str)) + { + strcpy(file_name, cfg_dir); + strcat(file_name, str); + send_file(file_name); + return; + } + + /* if no logbook given, display logbook selection page */ + if (!logbook[0]) + { + for (i=n=0 ; ; i++) + { + if (!enumgrp(i, str)) + break; + if (!equal_ustring(str, "global")) + { + strcpy(logbook, str); + n++; + } + } + + if (n > 1) + { + show_selection_page(); + return; + } + + strcpy(logbook_enc, logbook); + url_encode(logbook_enc); + } + + /* get theme for logbook */ + if (getcfg(logbook, "Theme", str)) + loadtheme(str); + else + loadtheme(NULL); /* get default values */ + + /* get data dir from configuration file */ + getcfg(logbook, "Data dir", str); + if (str[0] == DIR_SEPARATOR || str[1] == ':') + strcpy(data_dir, str); + else + { + strcpy(data_dir, cfg_dir); + strcat(data_dir, str); + } + + if (data_dir[strlen(data_dir)-1] != DIR_SEPARATOR) + strcat(data_dir, DIR_SEPARATOR_STR); + + if (*getparam("wpassword")) + { + /* check if password correct */ + do_crypt(getparam("wpassword"), enc_pwd); + + if (!check_password(logbook, "Write password", enc_pwd, getparam("redir"))) + return; + + rsprintf("HTTP/1.1 302 Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + /* get optional expriation from configuration file */ + exp = 24; + if (getcfg(logbook, "Write password expiration", str)) + exp = atof(str); + + if (exp == 0) + { + rsprintf("Set-Cookie: wpwd=%s; path=/%s\r\n", enc_pwd, logbook_enc); + } + else + { + time(&now); + now += (int) (3600*exp); + gmt = gmtime(&now); + strftime(str, sizeof(str), "%A, %d-%b-%y %H:%M:%S GMT", gmt); + + rsprintf("Set-Cookie: wpwd=%s; path=/%s; expires=%s\r\n", enc_pwd, logbook_enc, str); + } + + sprintf(str, "/%s/%s", logbook_enc, getparam("redir")); + rsprintf("Location: %s\r\n\r\nredir\r\n", str); + return; + } + + if (*getparam("apassword")) + { + /* check if password correct */ + do_crypt(getparam("apassword"), enc_pwd); + + if (!check_password(logbook, "Admin password", enc_pwd, getparam("redir"))) + return; + + rsprintf("HTTP/1.1 302 Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + /* get optional expriation from configuration file */ + exp = 24; + if (getcfg(logbook, "Admin password expiration", str)) + exp = atof(str); + + if (exp == 0) + { + rsprintf("Set-Cookie: apwd=%s; path=/%s\r\n", enc_pwd, logbook_enc); + } + else + { + time(&now); + now += (int) (3600*exp); + gmt = gmtime(&now); + strftime(str, sizeof(str), "%A, %d-%b-%y %H:%M:%S GMT", gmt); + + rsprintf("Set-Cookie: apwd=%s; path=/%s; expires=%s\r\n", enc_pwd, logbook_enc, str); + } + + sprintf(str, "/%s/%s", logbook_enc, getparam("redir")); + rsprintf("Location: %s\r\n\r\nredir\r\n", str); + return; + } + + if (*getparam("uname") && getparam("upassword")) + { + /* check if password correct */ + do_crypt(getparam("upassword"), enc_pwd); + + if (!check_user_password(logbook, getparam("uname"), enc_pwd, getparam("redir"))) + return; + + rsprintf("HTTP/1.1 302 Found\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + if (use_keepalive) + { + rsprintf("Connection: Keep-Alive\r\n"); + rsprintf("Keep-Alive: timeout=60, max=10\r\n"); + } + + /* get optional expriation from configuration file */ + exp = 24; + if (getcfg(logbook, "Login expiration", str)) + exp = atof(str); + + if (exp == 0) + { + rsprintf("Set-Cookie: upwd=%s; path=/%s\r\n", enc_pwd, logbook_enc); + rsprintf("Set-Cookie: unm=%s; path=/%s\r\n", getparam("uname"), logbook_enc); + } + else + { + time(&now); + now += (int) (3600*exp); + gmt = gmtime(&now); + strftime(str, sizeof(str), "%A, %d-%b-%y %H:%M:%S GMT", gmt); + + rsprintf("Set-Cookie: upwd=%s; path=/%s; expires=%s\r\n", enc_pwd, logbook_enc, str); + rsprintf("Set-Cookie: unm=%s; path=/%s; expires=%s\r\n", getparam("uname"), logbook_enc, str); + } + + sprintf(str, "/%s/%s", logbook_enc, getparam("redir")); + rsprintf("Location: %s\r\n\r\nredir\r\n", str); + return; + } + + /*---- show ELog page --------------------------------------------*/ + + if (getcfg(logbook, "Password file", str)) + { + if (!check_user_password(logbook, getparam("unm"), getparam("upwd"), path)) + return; + } + + if (equal_ustring(command, loc("New")) || + equal_ustring(command, loc("Edit")) || + equal_ustring(command, loc("Reply")) || + equal_ustring(command, loc("Submit")) || + equal_ustring(command, loc("Delete"))) + { + sprintf(str, "%s?cmd=%s", path, command); + if (!check_password(logbook, "Write password", getparam("wpwd"), str)) + return; + } + + if (equal_ustring(command, loc("Delete")) || + equal_ustring(command, loc("Config")) || + equal_ustring(command, loc("Copy to")) || + equal_ustring(command, loc("Move to"))) + { + sprintf(str, "%s?cmd=%s", path, command); + if (!check_password(logbook, "Admin password", getparam("apwd"), str)) + return; + } + + show_elog_page(logbook, dec_path); + return; +} + +/*------------------------------------------------------------------*/ + +void decode_get(char *string) +{ +char path[256]; +char *p, *pitem; + + strncpy(path, string, sizeof(path)); + path[255] = 0; + if (strchr(path, '?')) + *strchr(path, '?') = 0; + setparam("path", path); + + if (strchr(string, '?')) + { + p = strchr(string, '?')+1; + + /* cut trailing "/" from netscape */ + if (p[strlen(p)-1] == '/') + p[strlen(p)-1] = 0; + + p = strtok(p,"&"); + while (p != NULL) + { + pitem = p; + p = strchr(p,'='); + if (p != NULL) + { + *p++ = 0; + url_decode(pitem); + url_decode(p); + + setparam(pitem, p); + + p = strtok(NULL,"&"); + } + } + } + + interprete(path); +} + +/*------------------------------------------------------------------*/ + +void decode_post(char *string, char *boundary, int length) +{ +char *pinit, *p, *pitem, *ptmp, file_name[256], str[256]; +int i, n; + + for (i=0 ; ih_addrtype != AF_INET) + { + printf("Non Internet address for -h %s\n", tcp_hostname); + return; + } + memcpy(&serv_addr.sin_addr.s_addr, phe->h_addr_list[0], phe->h_length); + } + + serv_addr.sin_port = htons((short) tcp_port); + + status = bind(lsock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (status < 0) + { + /* try reusing address */ + flag = 1; + setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, + (char *) &flag, sizeof(INT)); + status = bind(lsock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + + if (status < 0) + { + printf("Cannot bind to port %d.\nPlease try later or use the \"-p\" flag to specify a different port\n", tcp_port); + return; + } + else + printf("Warning: port %d already in use\n", tcp_port); + } + + /* get host name for mail notification */ + gethostname(host_name, sizeof(host_name)); + + phe = gethostbyname(host_name); + if (phe != NULL) + phe = gethostbyaddr(phe->h_addr, sizeof(int), AF_INET); + + /* if domain name is not in host name, hope to get it from phe */ + if (strchr(host_name, '.') == NULL && phe != NULL) + strcpy(host_name, phe->h_name); + + /* open configuration file */ + getcfg("dummy", "dummy", str); + +#ifdef OS_UNIX + /* give up root privilege */ + setuid(getuid()); + setgid(getgid()); +#endif + + if (daemon) + { + printf("Becoming a daemon...\n"); + ss_daemon_init(); + } + + /* listen for connection */ + status = listen(lsock, SOMAXCONN); + if (status < 0) + { + printf("Cannot listen\n"); + return; + } + + printf("Server listening...\n"); + do + { + FD_ZERO(&readfds); + FD_SET(lsock, &readfds); + + for (i=0 ; i 0) + FD_SET(ka_sock[i], &readfds); + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + + status = select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); + + if (FD_ISSET(lsock, &readfds)) + { + len = sizeof(acc_addr); + _sock = accept(lsock, (struct sockaddr *) &acc_addr, &len); + + /* turn on lingering (borrowed from NCSA httpd code) */ + ling.l_onoff = 1; + ling.l_linger = 600; + setsockopt(_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling)); + + /* find new entry in socket table */ + for (i=0 ; i 0 && FD_ISSET(ka_sock[i], &readfds)) + break; + + if (i == N_MAX_CONNECTION) + { + _sock = 0; + } + else + { + i_conn = i; + _sock = ka_sock[i_conn]; + ka_time[i_conn] = (int) time(NULL); + memcpy(&rem_addr, &remote_addr[i_conn], sizeof(rem_addr)); + +#ifdef DEBUG_CONN + printf("## received request on connection %d\n", i_conn); +#endif + } + } + + /* turn off keep_alive by default */ + keep_alive = FALSE; + + if (_sock > 0) + { + memset(net_buffer, 0, sizeof(net_buffer)); + len = 0; + header_length = 0; + n_error = 0; + do + { + FD_ZERO(&readfds); + FD_SET(_sock, &readfds); + + timeout.tv_sec = 6; + timeout.tv_usec = 0; + + status = select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); + + if (FD_ISSET(_sock, &readfds)) + i = recv(_sock, net_buffer+len, sizeof(net_buffer)-len, 0); + else + goto error; + + /* abort if connection got broken */ + if (i<0) + goto error; + + if (i>0) + len += i; + + /* check if net_buffer too small */ + if (len >= sizeof(net_buffer)) + { + /* drain incoming remaining data */ + do + { + FD_ZERO(&readfds); + FD_SET(_sock, &readfds); + + timeout.tv_sec = 2; + timeout.tv_usec = 0; + + status = select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); + + if (FD_ISSET(_sock, &readfds)) + i = recv(_sock, net_buffer, sizeof(net_buffer), 0); + else + break; + } while (i); + + memset(return_buffer, 0, sizeof(return_buffer)); + strlen_retbuf = 0; + return_length = 0; + + show_error("Submitted attachment too large, please increase WEB_BUFFER_SIZE in elogd.c and recompile"); + send(_sock, return_buffer, strlen_retbuf+1, 0); + keep_alive = 0; + if (verbose) + { + printf("==== Return ================================\n"); + puts(return_buffer); + printf("\n\n"); + } + goto error; + } + + if (i == 0) + { + n_error++; + if (n_error == 100) + goto error; + } + + /* finish when empty line received */ + if (strstr(net_buffer, "GET") != NULL && strstr(net_buffer, "POST") == NULL) + { + if (len>4 && strcmp(&net_buffer[len-4], "\r\n\r\n") == 0) + break; + if (len>6 && strcmp(&net_buffer[len-6], "\r\r\n\r\r\n") == 0) + break; + } + else if (strstr(net_buffer, "POST") != NULL) + { + if (header_length == 0) + { + /* extract logbook */ + strncpy(str, net_buffer+6, sizeof(str)); + if (strstr(str, "HTTP")) + *(strstr(str, "HTTP")-1) = 0; + strcpy(logbook, str); + strcpy(logbook_enc, str); + url_decode(logbook); + + /* extract header and content length */ + if (strstr(net_buffer, "Content-Length:")) + content_length = atoi(strstr(net_buffer, "Content-Length:") + 15); + else if (strstr(net_buffer, "Content-length:")) + content_length = atoi(strstr(net_buffer, "Content-length:") + 15); + + boundary[0] = 0; + if (strstr(net_buffer, "boundary=")) + { + strncpy(boundary, strstr(net_buffer, "boundary=")+9, sizeof(boundary)); + if (strchr(boundary, '\r')) + *strchr(boundary, '\r') = 0; + } + + if (strstr(net_buffer, "\r\n\r\n")) + header_length = (INT)strstr(net_buffer, "\r\n\r\n") - (INT)net_buffer + 4; + + if (strstr(net_buffer, "\r\r\n\r\r\n")) + header_length = (INT)strstr(net_buffer, "\r\r\n\r\r\n") - (INT)net_buffer + 6; + + if (header_length) + net_buffer[header_length-1] = 0; + } + + if (header_length > 0 && len >= header_length+content_length) + break; + } + else if (strstr(net_buffer, "OPTIONS") != NULL) + goto error; + else + { + printf(net_buffer); + goto error; + } + + } while (1); + + if (!strchr(net_buffer, '\r')) + goto error; + + /* initialize parametr array */ + initparam(); + + /* extract cookies */ + if ((p = strstr(net_buffer, "Cookie:")) != NULL) + { + p += 6; + do + { + p++; + while (*p && *p == ' ') + p++; + strncpy(str, p, sizeof(str)); + + for (i=0 ; i<(int)strlen(str) ; i++) + if (str[i] == '=' || str[i] == ';') + break; + + if (str[i] == '=') + { + str[i] = 0; + p += i+1; + for (i=0 ; *p && *p != ';' && *p != '\r' && *p != '\n' ; i++) + cookie[i] = *p++; + cookie[i] = 0; + } + else + { + /* empty cookie */ + str[i] = 0; + cookie[0] = 0; + p += i; + } + + /* store cookie as parameter */ + setparam(str, cookie); + } while (*p && *p == ';'); + } + + memset(return_buffer, 0, sizeof(return_buffer)); + strlen_retbuf = 0; + + if (strncmp(net_buffer, "GET", 3) != 0 && + strncmp(net_buffer, "POST", 4) != 0) + goto error; + + return_length = 0; + + /* extract logbook */ + if (strncmp(net_buffer, "GET", 3) == 0) + { + p = net_buffer+5; + logbook[0] = 0; + for (i=0 ; *p && *p != '/' && *p != '?' && *p != ' '; i++) + logbook[i] = *p++; + logbook[i] = 0; + strcpy(logbook_enc, logbook); + url_decode(logbook); + + /* check if logbook exists */ + for (i=0 ; ; i++) + { + if (!enumgrp(i, str)) + break; + if (equal_ustring(logbook, str)) + break; + } + + if (strstr(logbook, ".gif") || strstr(logbook, ".jpg") || strstr(logbook, ".png") || + strstr(logbook, ".htm")) + { + /* server file directly */ + strcpy(str, cfg_dir); + strcat(str, logbook); + send_file(str); + send(_sock, return_buffer, return_length, 0); + + goto error; + } + else + if (!equal_ustring(logbook, str) && logbook[0]) + { + if (verbose) + printf("\n\n\n%s\n", net_buffer); + + sprintf(str, "Error: logbook \"%s\" not defined in elogd.cfg", logbook); + show_error(str); + send(_sock, return_buffer, strlen(return_buffer), 0); + + if (verbose) + { + printf("==== Return ================================\n"); + puts(return_buffer); + printf("\n\n"); + } + goto error; + } + } + + /* if no logbook is given and only one logbook defined, use this one */ + if (!logbook[0]) + { + for (i=n=0 ; ; i++) + { + if (!enumgrp(i, str)) + break; + if (!equal_ustring(str, "global")) + n++; + } + + if (n == 1) + { + if (verbose) + printf("\n\n\n%s\n", net_buffer); + + strcpy(logbook, str); + strcpy(logbook_enc, logbook); + url_encode(logbook_enc); + + /* redirect to logbook, necessary to get optionsl cookies for that logbook */ + redirect(""); + + send(_sock, return_buffer, strlen(return_buffer), 0); + + if (verbose) + { + printf("==== Return ================================\n"); + puts(return_buffer); + printf("\n\n"); + } + goto error; + } + } + + /* force re-read configuration file */ + if (cfgbuffer) + { + free(cfgbuffer); + cfgbuffer = NULL; + } + + /* set my own URL */ + getcfg("global", "URL", str); + if (str[0]) + { + if (str[strlen(str)-1] != '/') + strcat(str, "/"); + strcpy(elogd_url, str); + strcpy(elogd_full_url, str); + } + else + { + /* use relative pathnames */ + sprintf(elogd_url, "/"); + + if (tcp_port == 80) + sprintf(elogd_full_url, "http://%s/", host_name); + else + sprintf(elogd_full_url, "http://%s:%d/", host_name, tcp_port); + } + + /*---- check "hosts deny" ----*/ + + authorized = 1; + if (getcfg(logbook, "Hosts deny", list)) + { + phe = gethostbyaddr((char *) &rem_addr, 4, PF_INET); + if (phe != NULL) + strcpy(rem_host_name, phe->h_name); + else + strcpy(rem_host_name, ""); + + strcpy(rem_host_ip, (char *)inet_ntoa(rem_addr)); + + n = strbreak(list, host_list, MAX_N_LIST); + + /* check if current connection matches anyone on the list */ + for (i=0 ; i strlen(host_list[i]) && + equal_ustring(host_list[i], rem_host_name+strlen(rem_host_name)-strlen(host_list[i]))) + { + if (verbose) + printf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n", + rem_host_name, host_list[i]); + authorized = 0; + break; + } + } + if (host_list[i][strlen(host_list[i])-1] == '.') + { + strcpy(str, rem_host_ip); + if (strlen(str) > strlen(host_list[i])) + str[strlen(host_list[i])] = 0; + + if (equal_ustring(host_list[i], str)) + { + if (verbose) + printf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n", + rem_host_ip, host_list[i]); + authorized = 0; + break; + } + } + } + } + + /*---- check "hosts allow" ----*/ + + if (getcfg(logbook, "Hosts allow", list)) + { + phe = gethostbyaddr((char *) &rem_addr, 4, PF_INET); + if (phe != NULL) + strcpy(rem_host_name, phe->h_name); + else + strcpy(rem_host_name, ""); + + strcpy(rem_host_ip, (char *)inet_ntoa(acc_addr.sin_addr)); + + n = strbreak(list, host_list, MAX_N_LIST); + + /* check if current connection matches anyone on the list */ + for (i=0 ; i strlen(host_list[i]) && + equal_ustring(host_list[i], rem_host_name+strlen(rem_host_name)-strlen(host_list[i]))) + { + if (verbose) + printf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n", + rem_host_name, host_list[i]); + authorized = 1; + break; + } + } + if (host_list[i][strlen(host_list[i])-1] == '.') + { + strcpy(str, rem_host_ip); + if (strlen(str) > strlen(host_list[i])) + str[strlen(host_list[i])] = 0; + + if (equal_ustring(host_list[i], str)) + { + if (verbose) + printf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n", + rem_host_ip, host_list[i]); + authorized = 1; + break; + } + } + } + } + + if (!authorized) + { + keep_alive = 0; + goto error; + } + + /* ask for password if configured */ + authorized = 1; + if (getcfg(logbook, "Read Password", pwd)) + { + authorized = 0; + + /* decode authorization */ + if (strstr(net_buffer, "Authorization:")) + { + p = strstr(net_buffer, "Authorization:")+14; + if (strstr(p, "Basic")) + { + p = strstr(p, "Basic")+6; + while (*p == ' ') + p++; + i = 0; + while (*p && *p != ' ' && *p != '\r') + str[i++] = *p++; + str[i] = 0; + } + base64_decode(str, cl_pwd); + if (strchr(cl_pwd, ':')) + { + p = strchr(cl_pwd, ':')+1; + do_crypt(p, str); + strcpy(cl_pwd, str); + + /* check authorization */ + if (strcmp(str, pwd) == 0) + authorized = 1; + } + } + } + + /* check for Keep-alive */ + if (strstr(net_buffer, "Keep-Alive") != NULL && use_keepalive) + keep_alive = TRUE; + + if (!authorized) + { + /* return request for authorization */ + rsprintf("HTTP/1.1 401 Authorization Required\r\n"); + rsprintf("Server: ELOG HTTP %s\r\n", VERSION); + rsprintf("WWW-Authenticate: Basic realm=\"%s\"\r\n", logbook); + rsprintf("Connection: close\r\n"); + rsprintf("Content-Type: text/html\r\n\r\n"); + + rsprintf("\r\n"); + rsprintf("401 Authorization Required\r\n"); + rsprintf("\r\n"); + rsprintf("

Authorization Required

\r\n"); + rsprintf("This server could not verify that you\r\n"); + rsprintf("are authorized to access the document\r\n"); + rsprintf("requested. Either you supplied the wrong\r\n"); + rsprintf("credentials (e.g., bad password), or your\r\n"); + rsprintf("browser doesn't understand how to supply\r\n"); + rsprintf("the credentials required.

\r\n"); + rsprintf("\r\n"); + + keep_alive = FALSE; + } + else + { + if (verbose) + printf("\n\n\n%s\n", net_buffer); + + if (strncmp(net_buffer, "GET", 3) == 0) + { + /* extract path and commands */ + *strchr(net_buffer, '\r') = 0; + + if (!strstr(net_buffer, "HTTP")) + goto error; + *(strstr(net_buffer, "HTTP")-1)=0; + + /* skip logbook from path */ + p = net_buffer+5; + for (i=0 ; *p && *p != '/' && *p != '?'; p++); + while (*p && *p == '/') + p++; + + /* decode command and return answer */ + decode_get(p); + } + else if (strncmp(net_buffer, "POST", 4) == 0) + { + if (verbose) + printf("%s\n", net_buffer+header_length); + decode_post(net_buffer+header_length, boundary, content_length); + } + else + { + net_buffer[50] = 0; + sprintf(str, "Unknown request:

%s", net_buffer); + show_error(str); + } + } + + if (return_length != -1) + { + if (return_length == 0) + return_length = strlen_retbuf+1; + + if (keep_alive && strstr(return_buffer, "Content-Length") == NULL) + { + /*---- add content-length ----*/ + + p = strstr(return_buffer, "\r\n\r\n"); + + if (p != NULL) + { + length = strlen(p+4); + + header_length = (int) (p - return_buffer); + memcpy(header_buffer, return_buffer, header_length); + + sprintf(header_buffer+header_length, "\r\nContent-Length: %d\r\n\r\n", length); + + send(_sock, header_buffer, strlen(header_buffer), 0); + send(_sock, p+4, length, 0); + + if (verbose) + { + printf("==== Return ================================\n"); + puts(header_buffer); + puts(p+2); + printf("\n"); + } + } + else + printf("Internal error, no valid header!\n"); + } + else + { + send(_sock, return_buffer, return_length, 0); + + if (verbose) + { + printf("==== Return ================================\n"); + puts(return_buffer); + printf("\n\n"); + } + } + error: + + if (!keep_alive) + { + closesocket(_sock); + ka_sock[i_conn] = 0; + +#ifdef DEBUG_CONN + printf("## close connection %d\n", i_conn); +#endif + } + } + } + + } while (!_abort); +} + +/*------------------------------------------------------------------*/ + +void create_password(char *logbook, char *name, char *pwd) +{ +int fh, length, i; +char *cfgbuffer, str[256], *p; + + fh = open(cfg_file, O_RDONLY); + if (fh < 0) + { + /* create new file */ + fh = open(cfg_file, O_CREAT | O_WRONLY, 0640); + if (fh < 0) + { + printf("Cannot create \"%s\".\n", cfg_file); + return; + } + sprintf(str, "[%s]\n%s=%s\n", logbook, name, pwd); + write(fh, str, strlen(str)); + close(fh); + printf("File \"%s\" created with password in logbook \"%s\".\n", cfg_file, logbook); + return; + } + + /* read existing file and add password */ + length = lseek(fh, 0, SEEK_END); + lseek(fh, 0, SEEK_SET); + cfgbuffer = malloc(length+1); + if (cfgbuffer == NULL) + { + close(fh); + return; + } + length = read(fh, cfgbuffer, length); + cfgbuffer[length] = 0; + close(fh); + + fh = open(cfg_file, O_TRUNC | O_WRONLY, 0640); + + sprintf(str, "[%s]", logbook); + + /* check if logbook exists already */ + if (strstr(cfgbuffer, str)) + { + p = strstr(cfgbuffer, str); + + /* search password in current logbook */ + do + { + while (*p && *p != '\n') + p++; + if (*p && *p == '\n') + p++; + + if (strncmp(p, name, strlen(name)) == 0) + { + /* replace existing password */ + i = (int) (p - cfgbuffer); + write(fh, cfgbuffer, i); + sprintf(str, "%s=%s\n", name, pwd); + write(fh, str, strlen(str)); + + printf("Password replaced in logbook \"%s\".\n", logbook); + + while (*p && *p != '\n') + p++; + if (*p && *p == '\n') + p++; + + /* write remainder of file */ + write(fh, p, strlen(p)); + + free(cfgbuffer); + close(fh); + return; + } + + } while (*p && *p != '['); + + if (!*p || *p == '[') + { + /* enter password into current logbook */ + p = strstr(cfgbuffer, str); + while (*p && *p != '\n') + p++; + if (*p && *p == '\n') + p++; + + i = (int) (p - cfgbuffer); + write(fh, cfgbuffer, i); + sprintf(str, "%s=%s\n", name, pwd); + write(fh, str, strlen(str)); + + printf("Password added to logbook \"%s\".\n", logbook); + + /* write remainder of file */ + write(fh, p, strlen(p)); + + free(cfgbuffer); + close(fh); + return; + } + } + else /* write new logbook entry */ + { + write(fh, cfgbuffer, strlen(cfgbuffer)); + sprintf(str, "\n[%s]\n%s=%s\n\n", logbook, name, pwd); + write(fh, str, strlen(str)); + + printf("Password added to new logbook \"%s\".\n", logbook); + } + + free(cfgbuffer); + close(fh); +} + +/*------------------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ +int i; +int tcp_port = 80, daemon = FALSE; +char read_pwd[80], write_pwd[80], admin_pwd[80], str[80]; +time_t now; +struct tm *tms; + + tzset(); + + read_pwd[0] = write_pwd[0] = admin_pwd[0] = logbook[0] = 0; + + strcpy(cfg_file, "elogd.cfg"); + + use_keepalive = TRUE; + + /* parse command line parameters */ + for (i=1 ; itm_year % 100, tms->tm_mon+1, tms->tm_mday, + tms->tm_hour, tms->tm_min, tms->tm_sec); + return 0; + } + else if (argv[i][0] == '-') + { + if (i+1 >= argc || argv[i+1][0] == '-') + goto usage; + if (argv[i][1] == 'p') + tcp_port = atoi(argv[++i]); + else if (argv[i][1] == 'c') + strcpy(cfg_file, argv[++i]); + else if (argv[i][1] == 'r') + strcpy(read_pwd, argv[++i]); + else if (argv[i][1] == 'w') + strcpy(write_pwd, argv[++i]); + else if (argv[i][1] == 'a') + strcpy(admin_pwd, argv[++i]); + else if (argv[i][1] == 'l') + strcpy(logbook, argv[++i]); + else if (argv[i][1] == 'h') + strncpy(tcp_hostname, argv[++i], sizeof(tcp_hostname)); + else + { +usage: + printf("usage: %s [-p port] [-h hostname] [-D] [-c file] [-r pwd] [-w pwd] [-a pwd] [-l loggbook]\n\n", argv[0]); + printf(" -p TCP/IP port\n"); + printf(" -h TCP/IP hostname\n"); + printf(" -D become a daemon\n"); + printf(" -c specify configuration file\n"); + printf(" -v debugging output\n"); + printf(" -r create/overwrite read password in config file\n"); + printf(" -w create/overwrite write password in config file\n"); + printf(" -a create/overwrite admin password in config file\n"); + printf(" -l specify logbook for -r and -w commands\n\n"); + printf(" -k do not use keep-alive\n\n"); + return 0; + } + } + } + + if (read_pwd[0]) + { + if (!logbook[0]) + { + printf("Must specify a lookbook via the -l parameter.\n"); + return 0; + } + do_crypt(read_pwd, str); + create_password(logbook, "Read Password", str); + return 0; + } + + if (write_pwd[0]) + { + if (!logbook[0]) + { + printf("Must specify a lookbook via the -l parameter.\n"); + return 0; + } + do_crypt(write_pwd, str); + create_password(logbook, "Write Password", str); + return 0; + } + + if (admin_pwd[0]) + { + if (!logbook[0]) + { + printf("Must specify a lookbook via the -l parameter.\n"); + return 0; + } + do_crypt(admin_pwd, str); + create_password(logbook, "Admin Password", str); + return 0; + } + + /* extract directory from configuration file */ + if (cfg_file[0] && strchr(cfg_file, DIR_SEPARATOR)) + { + strcpy(cfg_dir, cfg_file); + for (i=strlen(cfg_dir)-1 ; i>0 ; i--) + { + if (cfg_dir[i] == DIR_SEPARATOR) + break; + cfg_dir[i] = 0; + } + } + + server_loop(tcp_port, daemon); + + return 0; +} diff --git a/elogd.cfg b/elogd.cfg new file mode 100755 index 00000000..f808d0f2 --- /dev/null +++ b/elogd.cfg @@ -0,0 +1,13 @@ +[global] +logbook tabs = 0 +tab cellpadding = 2 + +[demo] +Theme = default +Comment = General linux tips & tricks +Data dir = logbooks/demo +Attributes = Author, Type, Category, Subject +Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other +Options Category = General, Hardware, Software, Network, Other +Required Attributes = Author, Type +Page Title = ELOG - $subject \ No newline at end of file diff --git a/eloghelp_en.html b/eloghelp_en.html new file mode 100755 index 00000000..26d6796e --- /dev/null +++ b/eloghelp_en.html @@ -0,0 +1,56 @@ + + + +ELOG Electronic Logbook Help + + + + + + +
+ + + + + + + + + + + +
+ +ELOG Electronic Logbook Help

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

Quick overview

Per default, the last entry in a logbook is displayed. One can use the +browse buttons to display the first, previous, next and last message.

+ +If one of the checkboxes next to one of the attribute fields is checked, only messages of that +attribute are displayed with the browse buttons. This can be used as a quick filter to display only +pages from a certain author or category.

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

+ +The Find button opens a query page, where messages from a logbook can be displayed based on +filter rules. Each non-empty field works like an additional filter, which is and-ed with the other +rules. If no filter is selected, all messages from a logbook are displayed. The Last day and +Last 10 buttons display all messages from the last 24 hours and the last ten messages, +respectively.

+ +

More information

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

+ +

+ +


+
+S. Ritt, 28 August 2001 +
+ + diff --git a/eloghelp_fr.html b/eloghelp_fr.html new file mode 100755 index 00000000..b8f9a71f --- /dev/null +++ b/eloghelp_fr.html @@ -0,0 +1,66 @@ + + + +Aide sur la main-courante électronique ELOG + + + + + + +
+ + + + + + + + + + + +
+ +Aide sur la main-courante électronique ELOG

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

+ +

Aperçu rapide

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

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

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

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

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

+ +

Plus d'informations

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

+ +

+ +
+
+S. Ritt, 28 August 2001 +
Traduit en français par Fred Pacquier, décembre 2001 +
+ + diff --git a/eloghelp_ge.html b/eloghelp_ge.html new file mode 100755 index 00000000..4d86b7a6 --- /dev/null +++ b/eloghelp_ge.html @@ -0,0 +1,63 @@ + + + +ELOG Electronisches Logbuch Hilfe + + + + + + +
+ + + + + + + + + + + +
+ +ELOG Electronisches Logbuch Hilfe

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

Schnellüberblich

Standardmäßig wird der letzte Eintrag eines +Logbuches dargestellt. Man kann die Pfeiltasten verwenden, um den ersten, +vorhergenden, nächsten oder letzten Eintrag darzustellen.

+ +Falls einer der Schalter bei den Attributen angekreuzt ist, werden nur Einträge +mit diesem Attribut gezeigt. Diese Eigenschaft kann schnell dazu verwendet werden, +nur Seiten mit einem bestimmten Auto oder Kategorie darzustellen.

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

+ +Die Finden Schaltfläche öffnet ein Formular, das die Suche von +Einträgen aus einem Logbuch über Filterregeln erlaubt. Jedes ausgefü +llte Feld wirkt wie ein zusätzlicher Filter, das UND-verknüpft wird mit den +anderen Regeln. Falls kein Filter ausgewählt ist werden alle Einträge eines +Logbuches ausgewählt. Die Letzter Tag und Letzte 10 +Schaltflächen zeigen alle Einträge der letzten 24 Stunden bzw. die letzten +zehn Einträge.

+ +

More information

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

+ +

+ +
+
+S. Ritt, 28 August 2001 +
+ + diff --git a/eloglang.french b/eloglang.french new file mode 100755 index 00000000..4fc292eb --- /dev/null +++ b/eloglang.french @@ -0,0 +1,139 @@ +# +# $Revision$ +# + +# ElogLang.French - French interface file for ELOG 1.2.7+ +# version 1.2 - 13/12/01 by Fred Pacquier +# + +# +# Menu commands +# +New = Créer +Edit = Modifier +Delete = Supprimer +Reply = Répondre +Find = Chercher +Last day = Dernier jour +Last 10 = 10 Dernières entrées +Config = Configuration +Change Password = Changer le mot de passe +Logout = Se déconnecter +Help = Aide +Back = Retour +Submit = Valider +First = Première +First entry = Première Entrée +Last = Dernière +Last entry = Dernière Entrée +Previous = Précédente +Previous entry = Entrée Précédente +Next = Suivante +Next entry = Entrée Suivante +Copy to = Copier vers +Move to = Déplacer vers +Save = Enregistrer +Cancel = Annuler +Go to = Aller à + +# +# Login & password screens +# +Wrong password = Mot de passe incorrect +Please login = Connexion +Username = Utilisateur +Password = Mot de passe +Old Password = Ancien mot de passe +New Password = Nouveau mot de passe +ELOG password = mot de passe ELOG +ELOG change password = Changer le mot de passe ELOG +Please enter password to obtain write access = Entrez le mot de passe pour l'accès en écriture +Please enter password to obtain administration access = Entrez le mot de passe pour l'accès administrateur +Several logbooks are defined on this host = Il y a plusieurs registres sur ce serveur +Please select the one to connect to = Choisissez celui auquel vous souhaitez vous connecter +Change password for user = Changer le mot de passe de l'utilisateur + +# +# Entry display +# +Logged in as = Connecté sous +Entry date = Date de création +with = avec +This is the last entry = Ceci est la dernière entrée +This is the first entry = Ceci est la première entrée +Email sent to = Mél envoyé à +Email notification suppressed = Notification mél désactivée +please select = choisissez +Submit as HTML text = Code HTML +Suppress Email notification = Désactiver la notification mél. +Resubmit as new entry = Positionner comme une nouvelle entrée +If no attachments are resubmitted, the original ones are kept = Si un nouvel attachement n'est pas spécifié, l'ancien est conservé +To delete an old attachment, enter <delete> in the new attachment field = Pour supprimer un attachement existant, saisir comme nouvelle valeur <plonk> + = +Attachment = Attachement +Attachments = Attachements +No message available = Aucune entrée disponible +Reply to this = Réponse +In reply to = En réponse à + +# +# Submit entry +# +ELOG Error = Erreur ELOG +Fields marked with = Les champs marqués +are required = sont obligatoires +Error: Attachment file %s invalid, please bo back and enter a proper file name = Erreur : le fichier %s est invalide, revenez en arrière et spécifiez un nom de fichier correct +New message cannot be written to directory "%s" = Le nouveau message ne peut pas être écrit dans le répertoire "%s" +Please check that it exists and elogd has write access = Vérifier qu'il existe bien et qu'elogd a le droit d'y écrire +Error: Attribute %s not supplied = Erreur : l'attribut %s n'est pas spécifié +Please go back and enter the %s field = Revenez en arrière et renseignez le champ %s +Original attachment = Ancien attachement +New attachment = Nouvel attachement + +# +# Find display +# +ELOG search result = ELOG - résultat de la recherche +ELOG find = Recherche ELOG +Find = Chercher +Search = Lancer la recherche +Reset Form = Réinitaliser le formulaire +Summary only = Résumé seulement +Show attachments = Montrer les attachements +Printable output = Sortie pour impression +Sort in reverse order = Tri inversé +Start date = Date de début +End date = Date de fin +Year = Année +Text = Texte +(case insensitive substring) = (casse non prise en compte) +Search all logbooks = Chercher dans tous les registres +Last %d days = %d Derniers jours +Last %d entries = %d Dernières entrées +No entries found = Aucune entrée trouvée + +# +# Email, Copy/Move, Delete +# +A new entry has been submitted on %s = Une nouvelle entrée a été créée le %s +Logbook = Registre +Copy ELog entry = Copier l'entrée ELog +Message moved successfully from "%s" to "%s" = Entrée déplacée de "%s" vers "%s" +Message copied successfully from "%s" to "%s" = Entrée copiée de "%s" vers "%s" +Are you sure to delete this message? = Confirmez-vous la suppression de cette entrée ? +Yes = Oui +No = Non +Message successfully deleted = Entrée supprimée +Error deleting message: status = Erreur lors de la suppression de l'entrée : Status +Goto last message = Aller à la dernière entrée + +# +# Error messages +# +Error: Command "%s" not allowed = Erreur : commande "%s" non autorisée +Error: Command "%s" is not allowed for user "%s" = Erreur: commande "%s" non autorisée pour l'utilisateur "%s" +Cannot open file %s = Erreur d'ouverture du fichier %s +Cannot write to %s = Erreur d'écriture dans le fichier %s +No SMTP host defined in [global] section of configuration file = Aucun serveur SMTP n'est défini dans la section [global] du fichier de configuration +Message %s cannot be read from logbook "%s" = L'entrée %s ne peut pas être lue dans le registre "%s" +New message cannot be written to directory "%s" = La nouvelle entrée %s ne peut pas être créée dans le registre "%s" diff --git a/eloglang.german b/eloglang.german new file mode 100755 index 00000000..5c89cb26 --- /dev/null +++ b/eloglang.german @@ -0,0 +1,135 @@ +# +# $Revision$ +# + +# +# Menu commands +# +New = Neu +Edit = Ändern +Delete = Löschen +Reply = Antworten +Find = Finden +Last day = Letzter Tag +Last 10 = Letzte 10 +Config = Konfigurieren +Change Password = Passwort ändern +Logout = Abmelden +Help = Hilfe +Back = Zurück +Submit = Abschicken +First = Erster +First entry = Erster Eintrag +Last = Letzter +Last entry = Letzter Eintrag +Previous = Vorheriger +Previous entry = Vorheriger Eintrag +Next = Nächster +Next entry = Nächster Eintrag +Copy to = Kopieren nach +Move to = Verschieben nach +Save = Speichern +Cancel = Abbrechen +Go to = Nach + +# +# Login & password screens +# +Wrong password = Falsches Kennwort +Please login = Bitte Anmelden +Username = Benutzername +Password = Kennwort +Old Password = Altes Kennwort +New Password = Neues Kennwort +ELOG password = ELOG Kennwort +ELOG change password = ELOG Kennwort ändern +Please enter password to obtain write access = Bitte Kennwort eingeben für Schreibzugriff +Please enter password to obtain administration access = Bitte Kennwort eingeben für Administrationszugriff +Several logbooks are defined on this host = Mehrere Logbücher sind auf diesem Computer eingerichtet +Please select the one to connect to = Bitte wählen Sie eines aus +Change password for user = Kennwort ändern für Benutzer + +# +# Entry display +# +Logged in as = Angemeldet als +Entry date = Eingabedatum +with = mit +This is the last entry = Dies ist der letzte Eintrag +This is the first entry = Dies ist der erste Eintrag +Email sent to = Email gesendet an +Email notification suppressed = Email Benachrichtigung untedrückt +please select = bitte auswählen +Submit as HTML text = Als HTML Text abschicken +Suppress Email notification = Email Benachrichtigung unterdrücken +Resubmit as new entry = Als neuen Eintrag abschicken +If no attachments are resubmitted, the original ones are kept = Falls keine neuen Anhänge abgeschickt werden, werden die Originale behalten +To delete an old attachment, enter <delete> in the new attachment field = Um einen alten Anhang zu löschen, bitte <entfernen> eingeban + = +Attachment = Anhang +Attachments = Anhänge +No message available = Kein Entrag vorhanden +Reply to this = Antwort hierzu +In reply to = Als Antwort auf + +# +# Submit entry +# +ELOG Error = ELOG Fehler +Fields marked with = Felder markiert mit +are required = sind Pflichteinträge +Error: Attachment file %s invalid, please bo back and enter a proper file name = Fehler: Datei %s ist ungültig, bitte zurück und korrekten Dateinamen eingeben +New message cannot be written to directory "%s" = Neuer Entrag kann nicht ins Verzeichnig "%s" geschrieben werden +Please check that it exists and elogd has write access = Bitte überprüfen, ob es existiert und ob elogd Schreibzugriff hat +Error: Attribute %s not supplied = Fehler: Attribut %s nicht ausgefüllt +Please go back and enter the %s field = Bitte zurückgehen und das Feld %s ausfüllen +Original attachment = Alter Anhang +New attachment = Neuer Anhang + +# +# Find display +# +ELOG search result = ELOG Suchergebnis +ELOG find = ELOG suchen +Find = Finden +Search = Suchen +Reset Form = Eingabe zurücksetzen +Summary only = Nur Zusammenfassung +Show attachments = Anhänge anzeigen +Printable output = Druckfähige Anzeige +Sort in reverse order = Sortieren in umgekehrter Reihenfolge +Start date = Anfangsdatum +End date = Enddatum +Year = Jahr +Text = Text +(case insensitive substring) = (Teilzeichenkette Großschreibung ignorieren) +Search all logbooks = Alle Logbücher durchsuchen +Last %d days = Letzte %d Tage +Last %d entries = Letzte %d Einträge +No entries found = Keine Einträge gefunden + +# +# Email, Copy/Move, Delete +# +A new entry has been submitted on %s = Ein neuer Eintrag wurde an %s geschickt +Logbook = Logbuch +Copy ELog entry = ELog Eintrag Kopieren +Message moved successfully from "%s" to "%s" = Eintrag erfolgreich von "%s" nach "%s" verschoben +Message copied successfully from "%s" to "%s" = Eintrag erfolgreich von "%s" nach "%s" kopiert +Are you sure to delete this message? = Diesen Eintrag wirklich löschen? +Yes = Ja +No = Nein +Message successfully deleted = Eintrag erfolgreich gelöscht +Error deleting message: status = Fehler beim Löschen: Status +Goto last message = Zum letzten Eintrag + +# +# Error messages +# +Error: Command "%s" not allowed = Fehler: Kommando "%s" nicht erlaubt +Error: Command "%s" is not allowed for user "%s" = Fehler: Kommando "%s" nicht erlaubt für Benutzer "%s" +Cannot open file %s = Datei %s kann nicht geöffnet werden +Cannot write to %s = Datei %s kann nicht geschrieben werden +No SMTP host defined in [global] section of configuration file = Kein SMTP Host im [global] Bereich der Konfigurationsdatei definiert +Messags %s cannot be read from logook "%s" = Eintrag %s kann nicht aus dem Logbuch "%s" gelesen werden +New message cannot be written to directory "%s" = Neuer Eintrag kann nicht ins Verzeichnis "%s" geschrieben werden diff --git a/logbooks/demo/011108.log b/logbooks/demo/011108.log new file mode 100755 index 00000000..efb4f8f0 --- /dev/null +++ b/logbooks/demo/011108.log @@ -0,0 +1,15 @@ +$Start$: 419 +Date: Thu Nov 08 18:37:57 2001 +Thread: 0 0 +Author: Stefan Ritt +Type: Routine +Category: General +Subject: Welcome + +Encoding: plain +======================================== +Congratulations for installing ELOG sucessfully! + +This is a demo entry to ensure the elogd server is working correctly. +Click "New" to add new pages and "Delete" to delete this page.$End$: 419 + \ No newline at end of file diff --git a/themes/default/elog.gif b/themes/default/elog.gif new file mode 100755 index 0000000000000000000000000000000000000000..b37573e939d978b52b7724a648b4c9eedfe9e28d GIT binary patch literal 3060 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 literal 0 HcmV?d00001 diff --git a/themes/default/first.gif b/themes/default/first.gif new file mode 100755 index 0000000000000000000000000000000000000000..8891a48d7c2c4d5321433bcd7d65fc0af02a569b GIT binary patch literal 941 zcmchWPm9fQ6vmG+i4@8di?NuUg_1ecOlEQA&$vitLF3;-O(*dNG_r6lCKolESsY2X znG_MmzmyG&sYz$bQn6v>9Z0F;bAG%A-_7lGzTfA3p6C4Tc>B=E=!F5+4~1)J%+#l literal 0 HcmV?d00001 diff --git a/themes/default/first.psp b/themes/default/first.psp new file mode 100755 index 0000000000000000000000000000000000000000..3e4267b3884f000efe17f06ae29824853f3a86c2 GIT binary patch literal 5237 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 literal 0 HcmV?d00001 diff --git a/themes/default/next.gif b/themes/default/next.gif new file mode 100755 index 0000000000000000000000000000000000000000..69640222dd6bcd8c18e777b3a8eb0430e9ca3f2e GIT binary patch literal 890 zcmchWv1*k;5QUEtBw7WHl7Juz2?`eCSQye=EMl~?(16&8IEvcDM+kz2h3U+6YA+;m zv9L2>q09qV*!c!FVl{rV`V4M!8D{s)ch3Cx>b14YD|hF$pkMlXOmZotluA@)GM9xc zWz|hLxywVI^6I-`3Ri?871dAEl&%bADhnl*sazGRR8gRfG+I37cJ-V-ZWd-~Ry~%= z-8{_Gyqbb$xJ6i`MKu(6(=Ed?Eel4TTDetNr4>`5Mhs{u!J@}tw2?-O$JK_q=7f8? zSNmfuRfI=+RNIAuW`<{a7Vd%Ku2*=a7xu^lOkNWQ{(-@UjVYrcphhF}`Y6oKy z(tHkViSY2ms7X!!eT@J2=k)KH1JXC0@3H-#n9Ocy@9@d>-PfPC-W~n$@#*fH{byT; zrVrQm&g?%wvUuys@y)lNU!FSlXy)A9k1N~DE7!lzZZ3U!b>ZIa`Aa|Vuk9>6exHjM ZmrlG`UOjm8?DWBn?TtHMrwemh{R2nCY*PRL literal 0 HcmV?d00001 diff --git a/themes/default/next.psp b/themes/default/next.psp new file mode 100755 index 0000000000000000000000000000000000000000..b2933c8841703f40d26a30ec090bf22b47c9fb29 GIT binary patch literal 4414 zcmWGY%*-oM2+qhaPzWf>SMbbDOixvC%gjmTl41Y>Rv@l(@&=N6P@Dp!K^Ovp92l7x zBp4V$JVpkCQiv!cNPrcjj|(UXG{MaWD8dJk1d1_$F~np>1`D_f79i^$Q>!&dJre_n zH}e;S#|GwstO3cfgLy!Cc90zc46nEu7&w4(Odxrnh#=Tjkj)^Mg0KVwGt`lc1XV!H z5MnqA^gPJT+ktch&}Xa+|8M+%z#!n|=IO@32=t>4qYeYZ|Jw}C46Mv7EG*2dK!TN( zm5rT;gB=L?xw$!c1o?%81o;I8g~epVg+-)91qCG(C8T6!<>lpt#g$Z)?##OBQ$B1Q)gT|dNqvbb$ozg<_|_fXu%|Bm41#pUiMUz9)bIVvVt9rnHca`nO`t7Sgg zt8v~kxEcLTIj3)4LQ`=%r&-pHavP^bN{18$1q1~d!9>&!?|WREuYQ}iYSq<4aUXK$ zt>5xx-uA;^r9bsw5v$^UyUx4zkY2_79m?r$@A@)dxId9;a+=_(^moqA;}My={+gDU zOiiAr{^X#wo3wV_k=%)w&j|Qvz3g>NU$F1ilkInuo_GFkGAqe@@oZj8o#P1=9d=y0 z{H7Hw-yDB^=Oa-;F@cVbhK>%7j)t`{s_)ruzWVJS8k+S`zryT%_^m4c=!d_0e+r7M zeuU43P3K}BuQ9n>H*1%a*T(b0pAzOS=9;=c$?U|-XB=vxCC3(Qz7U^V6#Y)H_{iUb z(pKgr#r{tA3QvT@4QD?1cj;+v&Y4=*ZR?`E+WPvFPkYKgs`O-k$02n`U*^2Xw%5Pz z?Ws~yx(akAK{qNL-=|P<*nd%GeBy?aqHarP&Yr_=)Yy{qz3SMER`}mKl>6NEi$<~X@5wp+L|@<%3WUiObrdP*!jt3&fN1?KF^s{Z9Msc zY+Tp0d0o@2q9SIC81@+UzWn`Ym%O%aym;uv_R9FIb$5QgeACh0)8OL5;^GjtPUw4c z&ez)It5$6-n7&6kef3*g^|c@Vy8Y?c_`XBV;PQ)E;Wu)OTmx68r9EdnvmjC0_VI>2 z&C@pgw7i{QcW!ND?y2OSw^jG}38}T8_Jx4E#b$2`f7QL@>eR{SUd+{;v}%>RxSjjt z$vc0|E1u+^R#e5j`eM-Ki$TY|y@iw74BHI%+Si2pm%Xv$lRq6^c|U90*;Y|qTTz{o zEgQ`=k|mO*FMmH;t)wTr?OxUnaQt<2a1}e;d6{!-SJUIDxLXx-9(*_5smqrr>?(*H!g;Q14|uy<@mZ#P^o z`bNmL%kRE5d%Y#6@1fVtBk4Mpy1F0l2-H8H@FCt6!+$G#gc8(*8}Ru82#5U&z-?;v5`OLk<5i7))P9b z8HE2IP(8@#y@+{g3%f=qzt1F@2`bhCo>dH!{=86cT|76WFE7Yymgn0Ssz(=34e9p_ zvXeTQFI#2MxBBGD$4N!27RX$mRDASJ;Tieke=cfe9XG5u={|K)m&<+kiT=8Yat%t> z0{b#2m`PzUBje%tN^YhpZ>G7CUhNJ|OmtQEv@%tOWa>2>zOlGR#Mx z8wPSL6mU{(apGtd;8GNE(r5|TGwJ4|i9X`xEsy5*h@DT_abw%t{da*O^Z!3HL(Fko zZBSHV3ppl~kh{9jYBc1KLM~x1*8*H2$BZ0uSIk+`b4Jq~BF$Z!<@5*=a^}EP2+FPM pKy0B6%Bu{ZkUPi-8mBn4;NQx8KJx`YCImdV52QhfcAN5RZ2&Cln9=|M literal 0 HcmV?d00001 diff --git a/themes/default/oldtheme.cfg b/themes/default/oldtheme.cfg new file mode 100755 index 00000000..a390946e --- /dev/null +++ b/themes/default/oldtheme.cfg @@ -0,0 +1,37 @@ +; General attributes of displayed tables +Table width = 700 +Frame color = #FFFFFF +Cell bgcolor = #FFFFFF +Border width = 3 + +; Look of Title Row +Title bgcolor = #A0A0FF +Title fontcolor = #000000 +Title cellpadding = 5 +Title image = + +; Look of Menus +Merge menus = 0 +Use buttons = 1 + +Menu1 bgcolor = #C0C0C0 +Menu1 Align = left +Menu1 cellpadding = 3 + +Menu2 bgcolor = #E0E0E0 +Menu2 Align = left +Menu2 cellpadding = 3 +Menu2 use images = 0 + +; Categories +Categories cellpadding = 3 +Categories border = 0 +Categories bgcolor1 = #CCCCFF +Categories bgcolor2 = #A0FFA0 + +; Text +Text bgcolor = #FFFFFF + +; Search results +List bgcolor1 = #FFFFFF +List bgcolor2 = #FFFFFF \ No newline at end of file diff --git a/themes/default/previous.gif b/themes/default/previous.gif new file mode 100755 index 0000000000000000000000000000000000000000..6fd2decd8baf9e99a2be7411dade051b1c068f7a GIT binary patch literal 893 zcmchWyK0qD5QUdu5wVHETR_1Jhye?6R1`r@5ROq$EW|)MFANBRjc=e>7*Mb086p4w-G@YzuEc>?&dJe-fO!p`#0SzTs^cajb(rEFx+ECY= za8LJYe~hJy@JNqpyHL=~@J!FbJy6{B3a|9S9(jPtYvRB^5IBSyF`%L3DjW<(8)>wv zlj}A$CnKtZ#2;g+G9!yzu?q#wsEi^{sX4{Q2oiscrOK*a;8=fCjE(j_$C4Uq-TSeJovnJ`u_8+V>2hFYx^Hx e&xdp0x8KY!U%ov*_wLqendFd~xW79GKI3JVAhVsS%4gXA?JoJfF^Ia@(#w9I=!jkAnAY+(Y#gkZb} zL&mW(DU1t9_&j-_1Qxh1K%yj50vWW#GRrVz{&EBnwCKbPOME;5!h891vL1PT^PjO~K$g-30TSv%j&>;+L9`~66>YZR`BuLsN zeF)KeV_rIx1?IKBld&rLCil8-c-83Ynrrr7CGNP=@xuCh2turGnk=MPLjf zpfG~M(q>2lcNGEripV4i6(Ith5e^MMV|U zGYyF1%;o_B|)s zH+}u)f8$MCA6(x%LrLl>b*#2KBdU2*94`8wbD;*XV?Gffe00%7ZpxAf# zPkcfN%qfld4youLiMQIV&Uxf0`lUpP`lPieY?eZ)v|9Xb{BDr%S&x(QqG4XygiB&V z+KeSFlFMj*5)|i3A7OaRQM_+-Jd3X}$q)-axooS2-aTp6GohrsXP;VFC7WG?qRzW5 zUt#9<8kE(Uv&NH$LpM!R1@)bKmQQxJdfUpGFq6YAm;uJr~7MKT9>{KY{GQ+~sG zbyKqgiHa93|MzJBaqaG1tFb>_60g|X8FYxZ@*ydNiumG4*K9}j=A*}a%#+`%*DOrz z8?>lCcHz9__X+xAk1#i+X)pl`FAwC04G)%9U7& zr#N`jT#l6p6af-fJZci5AkazySr-{{3D_SL&3>6Wy+~V^axzf7=u2fu>wc*0jrvz##3HAA&f5^>%J-9WUT1mUhqSe&#ok=)UJEb?Ib3@ z>$)X1UlP!K$*=9a%fTyp$K&+x=G13eDOpBoyrZ9@sF1JLbntW^uOTm4@45J>G1H6_ z(JNQm&g$R_X`0(OAAKWN-md#O_b^Svj}ts4cbv_7Uo4=h+j9c5@<)Un@olVp6U`e6k0^vo zpE~8uD;7+Nczro7IufmnmNdz3VapKj_Q3%I-bC7fH&5x%Kqk-VvjGv$HG5!O@&M{i zVI>y!SpWF-G?v9$`u?C>>shC_n+uIojR~x{@q5z?2>=?Y6oI&3u}AS!BtidK22K4X zXoMxuZlO)a)bxM?=km{2`RBV_P*eXRCFahiXQx6N_ryh4dH2F)T2ULf^xn!d`}G$u z*Iq8#Z^M=ASN;;3OlTUFzid|jAf8U*+7D-ck*m3y6E;PyY&ZCZTb!$ba6+=ErEL8e z@v&TW6;4nKHIHrZuDCFlslW-iK;>8J|0X`c(}b~)90aFPESj6eQ@6tc9Wv9=cV;&A zH?tRQK_waNv-qunvnZ0x^XtlBA55Oi8`4Wn8kzA9I&j3&yTWl5?gMaXhVg zDH&6dIH+a32H{i(K(<#tcb*dcL&$`?!Kdi*cOY|fleI%>)FI<6V?~y$tLsdgf()`= zmH9jwWPiVB$ZPkk{bxUdG8(sf@8qn>M literal 0 HcmV?d00001 diff --git a/themes/default/theme.cfg b/themes/default/theme.cfg new file mode 100755 index 00000000..d2547553 --- /dev/null +++ b/themes/default/theme.cfg @@ -0,0 +1,37 @@ +; General attributes of displayed tables +Table width = 100% +Frame color = #486090 +Cell bgcolor = #E6E6E6 +Border width = 0 + +; Look of Title Row +Title bgcolor = #486090 +Title fontcolor = #FFFFFF +Title cellpadding = 0 +Title image = elog.gif + +; Look of Menus +Merge menus = 1 +Use buttons = 0 + +Menu1 bgcolor = #E0E0E0 +Menu1 Align = left +Menu1 cellpadding = 5 + +Menu2 bgcolor = #FFFFB0 +Menu2 Align = center +Menu2 cellpadding = 0 +Menu2 use images = 1 + +; Categories +Categories cellpadding = 3 +Categories border = 0 +Categories bgcolor1 = #CCCCFF +Categories bgcolor2 = #DDEEBB + +; Text +Text bgcolor = #FFFFFF + +; Search results +List bgcolor1 = #DDEEBB +List bgcolor2 = #FFFFB0 \ No newline at end of file