pax_global_header00006660000000000000000000000064123061714450014515gustar00rootroot0000000000000052 comment=3df9a9f984c9b92bcc22ba1d0fff58a19933bc3f crashmail-1.5/000077500000000000000000000000001230617144500133255ustar00rootroot00000000000000crashmail-1.5/ChangeLog000066400000000000000000000530331230617144500151030ustar00rootroot000000000000002014-03-06 16:45 Robert James Clay - Release as version 1.5 2014-03-06 14:14 Lars Kellogg-Stedman - Merge fix of type mismatches and warnings through the code. - Merge using off_t for seek and tell operations. - Merge adding missing commands to man page. 2014-03-06 13:49 Robert James Clay - Add a text file with message testing content. - Correct name of crashwrite option for msg text in tests/02-crashwrite-test.sh. 2014-03-06 08:15 Lars Kellogg-Stedman - Merge conversion from ulong/ushort to stdint types in src/cmnllib/, src/jamlib/, src/crashmail/, src/shared/, and src/tools/. 2013-02-17 19:21 Robert James Clay - Release as version 1.4 - README, doc/ReadMe.txt: lars@oddbit.com is the email address preferred by Lars Kellogg-Stedman. 2013-02-17 18:56 Lars Kellogg-Stedman - tests/02-crashwrite.sh: added tests for named packets 2013-02-15 12:08 Robert James Clay - Release as version 1.3 - Adapt tests/ directory to this version of Crashmail II. - Add copyright information for tests/roundup to README file. 2013-02-15 10:02 Lars Kellogg-Stedman - tests/*: Merge in the addition of testing and a tests/ subdirectory done by Lars Kellogg-Stedman in his Crashmail port (github.com/larsks/crashmail.git). 2013-02-12 14:27 Robert James Clay - Release as version 1.2.1 2013-02-12 14:06 Lars Kellogg-Stedman - src/crashmail/areafix.c: more pointer size fixes - src/tools/crashmaint.c: more pointer size fixes 2013-02-12 13:40 Robert James Clay - Release as version 1.2 - src/tools/crashmaint.c: crashmaint has already had its version set as v1.2; with the release of Crashmail with an overall version of v1.2, synchronize its version with crashmail itself by setting it from src/crashmail/version.h instead of setting it directly in its code. 2013-02-12 12:03 Robert James Clay - Release as version 1.1.1 2013-02-12 12:27 Lars Kellogg-Stedman - src/tools/crashwrite.c: Fix integer-length issues in crashwrite. Crashwrite declares pktnum as type ulong when it should really be uint32_t. Using "%lx" instead of "%x" to display this was resulting in invalid packet filenames. 2013-02-12 11:18 Robert James Clay - Release as version 1.1 - src/tools/crashexport.c: crashexport has already had its version set as v1.1; with the release of Crashmail with an overall version of v1.1, synchronize its version with crashmail itself by setting it from src/crashmail/version.h instead of setting it directly in its code. 2013-02-12 11:18 Robert James Clay - Release as version 1.1 - src/tools/crashexport.c: crashexport has already had its version set as v1.1; with the release of Crashmail with an overall version of v1.1, synchronize its version with crashmail itself by setting it from src/crashmail/version.h instead of setting it directly in its code. 2013-02-11 Robert James Clay - Release as version 1.0 - Move, rename, and update doc/History.txt to be the top level ChangeLog file. - The tools crashexport, crashgetnode, crashlist, crashlistout, and crashstats have already had their versions set as v1.0; with the release of Crashmail v1.0, synchronize their versions with crashmail itself by setting their versions from src/crashmail/version.h instead of setting it directly in their code. 2013-02-10 20:14 Lars Kellogg-Stedman - src/tools/crashstats.c: Fixed 64-bit problems in crashstats, which made assumptions about the size of pointers that would cause it to fail on 64bit systems. 2013-02-10 17:30 Robert James Clay - Release version 0.76 - Add a top level Makefile to the distribution. - Update and rename src/ReadMe.txt as the top level INSTALL file. - Derive new top level README file from the content of doc/ReadMe.txt. - Move the definition of the Crashmail version from src/crashmail/crashmail.h to the new file src/crashmail/version.h. 2013-02-10 12:55 Robert James Clay - Release version 0.75.1 - src/crashmail/crashmail.h: Correct VERSION entries for v0.75 and add a definition for VERSION_PATCH. 2013-02-09 Robert James Clay - Release version 0.75 - Update copyright information in doc/ReadMe.txt and convert the file to UTF-8. - Change the format of the doc/History.txt changelog entries since v0.71 to include date and author information as the header line, instead of just using a "Changes in version m.nn" line. 2013-01-21 Robert James Clay - Release version 0.74 - Add usage of CPPFLAGS, CFLAGS, & LDFLAGS to the source files Makefile.linux, cmnllib/Makefile.linux, jamlib/Makefile.linux, and oslib_linux/Makefile. 2010-12-24 Robert James Clay - Release version 0.73 2010-08-10 Petr Salinger - Fix for a 'fails to build on GNU/kFreeBSD' bug on Debian (Bug#592481). 2009-09-19 Robert James Clay - Release version 0.72 2009-09-11 Peter Krefting - Fix usage of stdarg macros. This fixes a segmentation fault when writing to the log file. - Fix duplicate argument to LogWrite. - Make the "obj" directory if it does not already exist. - Use stdint types to get sizes with known number of bits. This fixes data handling issues on 64-bit architectures. - Fix printf format strings for node numbers. This fixes log and error messages displaying 4D node numbers in 64-bit builds. 2004-06-10 Johan Billing Version 0.7 was probably the last major revision of CrashMail. If you are using CrashMail, please send me an e-mail (billing@df.lth.se). I am curious about how many CrashMail users there still are... Changes in version 0.71 - Fixed a bug that could cause CrashMail to crash when exporting messages from a JAM area with deleted messages. - Updated JAMLIB to 1.3.2 - Win32 binaries are now compiled with MinGW 3.1.0-1 instead of gcc-2.95-mingw32. Changes in version 0.7 - Implemented a message filter. Using this feature, you can filter out messages that match specified criteria and perform a number of commands on them. This filter will for example copy echomail messages addressed to me to a special area (the area needs to be configured as a local area using LOCALAREA): FILTER SOURCE=TOSSED and TYPE=ECHOMAIL and TONAME="Johan Billing" COPY PERSONAL_MESSAGES See doc/filter.txt for a complete description of the new message filter. - The new message filter replaces the configuration keywords ROBOTNAME, REMAP and REMAPNODE since the message filter can perform these tasks. Replacement for ROBOTNAME : FILTER TYPE=NETMAIL and TOLOCALAKA and TONAME= EXECUTE Replacement for REMAP FILTER TYPE=NETMAIL and TOLOCALAKA and TONAME= REMAPMSG Replacement for REMAPNODE FILTER TYPE=NETMAIL and TOADDR= REMAPMSG "*" - Rewrote the dupe-checking. CrashMail now no longer needs to load the entire dupe buffer into memory. This means that you now can keep a much large dupe buffer without using a lot of memory. CrashMail will not read your old dupe file, you have to delete it. Also note that the second argument for DUPEFILE has changed from the size of the dupe buffer in KB to the maximum number of messages that should be stored in the dupe buffer. If you don't update your configuration, you will only have a very small dupe buffer. - CrashMail no longer keeps zero-length files in the packet directory for use in naming new bundles. Instead it relies solely on the cmindex file. - CrashMail now also supports nodelists in the Version 7+ format in addition to its own format. (Note: It seems like the Linux version of FastLst does not include hub/region in the compiled nodelist for some reason. This means that node patterns with HUB/REGION won't work.) - New tool in the CrashMail suite: crashlistout. This tool can list your outbound directory in a pretty way. - New configuration keywords: BEFORETOSS and BEFOREPACK. You can use these to execute custom commands before tossing/packing a *.pkt file. - New keyword PKTFROM in node configuration. You can use this if you want to set the originating address of .pkt files to something other than the default. - New configuration switches: FLOWCRLF and NOEXPORTNETMAIL. FLOWCRLF makes CrashMail use CRLF as end-of-line characters in flow files instead of just LF. NOEXPORTNETMAIL makes CrashMail ignore netmail areas with SCAN and SCANLIST. This together with NOROUTE makes it possible to use an external program for handling netmail. - New switch NEEDSPLUS for the REMOTEAF keyword. - New commandline argument: SCANDOTJAM. This can be used for scanning areas listed in echomail.jam/netmail.jam files. - In addition to "programname ?", you can now also use -h, --help, help, /? and /h with CrashMail and the related utilities to show the commandline help. - Improved the time it takes for CrashMail to load the configuration file. Only noticable if you have a huge number of pass-through areas. - Fixed some bugs found in the ChangeLog from CrashEcho (thanks guys!) - SCANLIST does not abort anymore if an area cannot be scanned - RESCAN was broken and always returned an "unknown area" error - Added sanity checks to JAM linking to prevent CrashMail from crashing on messagebases with broken reply links - Fixed some memory leaks in the JAM handling. - Upgraded JAMLIB to version 1.3.1 - Some minor cosmetic changes. - Cleaned up the source in various places. Changes in version 0.62: CrashMail - CrashMail would check packet passwords even if the command-line option NOSECURITY was used. Fixed. - The Local flag is now cleared and the Sent flag is set before messages are imported to the messagebase. In previous versions of CrashMail, imported messages with Local flag and no Sent flag were re-exported the next time CrashMail SCAN was run. Fixed. - CrashMail no longer leaves empty lines in the config file when areas are removed by the AreaFix. CrashMaint - CrashMaint used local time instead of UTC when deleting old messages in JAM areas. This could cause messages to be deleted ahead of time. Thanks to Alexander S Aganichev for noticing this and sending me a patch. JAMLIB - There was a bug in JAMLIB that caused the Linux version of CrashMail to fail when trying to import an empty message to the messagebase. The bug has been fixed and CrashMail now uses the new version of JAMLIB. Changes in version 0.61: CrashMail: - CrashMail incorrectly bounced netmails to nodes listed as hold in the nodelist are no longer bounced. Fixed. Netmails to nodes listed as down are still bounced. - The CrashMail REMOVE command didn't work. Fixed. - The AreaFix would sometimes miss some areas when you unsubscribed to areas using patterns. - The AreaFix no longer crashes when it tries to remove areas after the feed has unsubscribed to them. - JAM_LINK and JAM_HIGHWATER would turn on both linking and the use of highwater marks. Fixed. - TINYSEENBY didn't work. Fixed. - CrashMail had problems with backslashes in file-attaches. Fixed. JAMLIB - Changed to latest version of JAMLIB. CrashWrite: - PKTFROMADDR/PKTTOADDR didn't work. Fixed. - CrashWrite had problems with paragraphs longer than 100 characters. Fixed. Changes in version 0.6 CrashMail: - The AreaFix no longer truncates area names longer than 30 characters in the response messages. - The Linux version of CrashMail now shows the real return code (using WEXITSTATUS()) when an external command returns an error. - Two default areas may now have the same path - Cleaned up the Makefiles - CrashMail no longer sets the Sent flag on all imported message. CrashMail now only exports messages with the Local flag set and the Sent flag cleared. - CrashMail can now also export JAM messages with very long kludges without problems. Previous versions had problems with kludges longer than 200 chars. - LF characters are stripped when importing messsages to a JAM messagebase. - Space characters are stripped in AreaFix commands. This means that AreaFix commands will work even if they begin with one or several space characters. - Node numbers can now be abbreviated on the EXPORT lines. See example.prefs for an example on how this works. Thanks to Per Lundberg for idea and original patch. - You can now define local areas in crashmail.prefs using the keyword LOCALAREA. Local areas are not used by CrashMail, but are included in config files created by CrashExport and are maintained when running CrashMaint. CrashWrite: - There are two new options PKTFROMADDR and PKTTOADDR that can be used to set the origin and destination address to something other than the origin and destination address of the message inside the packet. - A new option PASSWORD that can be used to set the password of the created packet. JAMLIB: - JAMLIB could crash on corrupt messagebases. Fixed. - JAMLIB lo longer reads or writes structures directly from/to disk. This makes JAMLIB even more portable and ensures that the Intel byte-order will be used on all platforms regardless of the processor used. All programs: - The Win32 version of CrashMail did not work if you were tossing mail to many downlinks since you could only open a limited number of files with fopen() (about 40). Because of this, the Win32 version has been rewritten to use Windows' I/O functions rather than the standard C stdio. The problem is solved and the Win32 version is now also slightly faster. Note that JAMLIB still uses fopen() so do not use too high a value for JAM_MAXOPEN. - Improved the error messages in all programs. Now CrashMail will also tell you why an I/O error occurred using the response given by the operating system. (To do this, the functions osError() and osErrorMsg() were added to oslib.) - Added function osVFPrintf() to oslib - Changed error message "Too many arguments" in command line parsing to to "Unknown keyword %s". This should make it easier to find the problem. - Several cosmetic changes to program output and source code Changes in version 0.52 CrashMail: - Three new flags for the NODE keyword: CRASH, DIRECT and HOLD. With these flags, you can set the priority for packed and unpacked netmail. - When the packet directory was missing, the error message said that the outbound directory was missing. This could be very confusing. Fixed. - Fixed a Y2K-related bug in the JAM import function. - Node patterns with REGION or HUB did not work for points. Fixed. - Added the new %-code %R for the ROBOTNAME keyword. %R gives the message in RFC-format with the addresses in RFC-format (name@pX.fX.nX.zX.domain). - When importing messages, CrashMail now checks for robot and AreaFix names before messages are remapped. If you want to remap messages to AreaFix, you have to make sure that no AreaFix names are configured. REMAPNODE still works as before. - CrashMail now updates the DateProcessed field when exporting messages from a JAM area. - CrashMail no longer is so strict with names of packet files names. Packet names are now considered valid even if they contain non-hexadecimal characters. - When glibc 2.1 was used, the Linux version of CrashMail exported messages from JAM messagebases with an incorrect date. The time was one hour ahead. This has now been fixed. - Dates in Via lines were one month early. Fixed. - If a netmail area had an IMPORT line, all netmails would be imported to that area. This has been fixed and IMPORT now works as intended. - CrashMail no longer aborts if a JAM area could not be opened when exporting messages. CrashMail instead continues and exports mail from all other areas. - CrashMail used to create an invalid configuration file when auto-adding areas to a config file where the last line did not end with an end-of-line character. This has now been fixed. - You can now use the environment variable CMCONFIGFILE to set the default configuration file for CrashMail. - CrashMail would incorrectly import FMPT kludges to JAM areas. Fixed. CrashExport: - CrashExport did not write netmail areas correctly to a GoldED configuration file. This has now been fixed. - CrashExport can now also create en area configuration file for timEd. - CrashExport no longer exports areas with the UNCONFIRMED keyword. CrashStats: - The total number of messages per day were not calculated correctly in the LAST7 mode, instead a random number would be shown. The average total number of messages per day was also incorrect. These bugs have now been fixed. CrashMaint: - You can now use the environment variable CMCONFIGFILE to set the default configuration file for CrashMail. All programs: - The argument parsing has been improved. The main change for the user is that "programname ?" now gives a more helpful output. - The functions remove(), rename() and system() has been moved to the oslib. This change should not be noticable for the user. - Peter Karlsson has prepared man-pages for CrashMail and all related tools. Thanks, Peter! These are handy under Linux, but can also be used on other platforms if you install a "man"-program. - Peter Karlsson has ported CrashMail II to OS/2. Thanks, Peter! Changes in version 0.5 CrashMail: - Because of a bug in the pattern matching, an empty string would match any pattern. Fixed. - Areas belonging to the same group were not listed together in the area lists generated by the Areafix. - CrashMail showed the same error message twice when the packer returned an error when unarchiving a bundle. Fixed. - If the highwater mark was lower than the lowest messagenumber in a JAM area, CrashMail would still try to export messages beginning from the highwater mark. Unfortunately this would cause a crash because of another bug that appeared when CrashMail tried to export an unreadable message. All this has now been fixed. - CrashMail no longer stops exporting when it encounters an unreadable message - CrashMail no longer adds its own node number to the path if it already exists as the last node in the path lines. - CrashMail could sometimes create circular reply links and get caught in infinite loops when dupes were imported to a JAM messagebase. Fixed. - New config option: ALLOWKILLSENT - New command-line keyword for CrashMail: TOSSDIR - New command-line keyword for CrashMail: NOSECURITY. If this keyword is used, all packets will be tossed without security checks. This is mainly intended to be used together with TOSSFILE/TOSSDIR and packets generated by CrashWrite. CrashMaint: - CrashMaint no longer checks the ActiveMsgs counter in the JAM messagebase to determine if the area needs to be packed. This check did not always work, among other things because all programs did not update the counter properly. JAM areas are now always packed when "crashmaint pack" is used. - CrashMaint now rebuilds the ActiveMsgs counter when packing a JAM area. - CrashMaint did not update the messagebase header when removing all messages from a JAM area. This would cause some messages not to be exported when highwater marks were used. (If you suspect that this has happened to you, remove all *.cmhw files.) - Improved the error handling a bit in CrashMaint. Now you get an error if an area couldn't be updated since it was in use by another program. CrashWrite: - Included CrashWrite, a new tool for CrashMail. CrashWrite reads a text file and then creates a packet file that can be tossed by CrashMail. CrashExport: - CrashAreasBBS and CrashForward have been replaced by the new program CrashExport. In addition to the old formats, CrashExport can also create arealists in GoldED format. The old version of CrashAreasBBS had some trouble with pass-through areas, this is gone in CrashExport. Changes in version 0.42 - Fixed bug that occurred when sending mail to nodes with long addresses (e.g. 9999:8888/7777.6666) - Fixed an error message in the outbound handling - Fixed a bug that occurred when auto-adding pass-through areas - Fixed a small memory leak - CrashMail got caught in an infinite loop when an unknown nodelist format was specified in the config. This is now fixed. - CrashMail no longer segfaults at startup when pass-through areas are used! - CrashMail now adds packets to bundles in the correct order - CrashMail would under some circumstances use the same bundle for more than one node. This has been fixed and a mechanism that checks that the same bundle is not used for more than one node has been added. - Routing did not work properly when BOUNCEPOINTS was turned on. Fixed. - Removed an unnecessary linebreak from the bounce messages. - New configuration keyword: NOMAXOUTBOUNDZONE. See example.prefs for more information. - CrashList did not handle Pvt nodes correctly. Fixed. - There was a small mistake in the example configuration file. The keyword is REMOTEAF, not REMOTEAREAFIX. Changes in version 0.4 - First public release crashmail-1.5/INSTALL000066400000000000000000000011161230617144500143550ustar00rootroot00000000000000How to compile: For win32 and os2 targets: first create a bin/ and src/obj/ directories. Then just type make linux or make win32 or make os2 depending on the platform which you want to compile CrashMail for. You will now find the binaries in the 'bin' directory. If you want to remove all object files that were created during the compilation, type make cleanlinux or make cleanwin32 or make cleanos2 Note: CrashMail was developed using gcc. If you are using another compiler, you will probably have to make some adjustments to the makefiles and perhaps also to the source. crashmail-1.5/Makefile000066400000000000000000000015401230617144500147650ustar00rootroot00000000000000# type either "make linux", "make win32", or "make os2" to compile help: @echo You can use this Makefile in the following ways: @echo make linux ............ Make Linux binaries @echo make win32 ............ Make Win32 binaries @echo make os2 .............. Make OS/2 binaries @echo make cleanlinux ....... Remove object files under Linux @echo make cleanwin32 ....... Remove object files under Win32 @echo make cleanos2 ......... Remove object files under OS/2 @echo make tests ............ Run Tests (requires /bin/sh) linux : mkdir -p bin make -C src -f Makefile linux win32 : make -C src -f Makefile win32 os2 : make -C src -f Makefile os2 cleanlinux : rm -rf bin make -C src -f Makefile cleanlinux cleanwin32 : make -C src -f Makefile cleanwin32 cleanos2 : make -C src -f Makefile cleanos2 .PHONY: tests tests: linux make -C tests crashmail-1.5/README000066400000000000000000000054201230617144500142060ustar00rootroot00000000000000 CrashMail II The Next Generation! ...a stranger in a strange land... ============ Introduction ============ Welcome to CrashMail II! CrashMail II is basically a more portable version of CrashMail, a tosser for Amiga computers. Users of the old Amiga version will probably find some things familiar while some features are gone such as the ARexx port (for obvious reasons!) and the GUI configuration editor. The only feature that CrashMail II has and the old CrashMail hasn't is support for JAM messagebases. Homepage: http://ftnapps.sourceforge.net/crashmail.html Code: http://sourceforge.net/p/ftnapps/crashmail/code/ Downloads: http://sourceforge.net/projects/ftnapps/files/crashmail/ ========= Copyright ========= Copyright (C) 1998-2004, Johan Billing Copyright (C) 1999-2010, Peter Krefting Copyright (C) 2009-2014, Robert James Clay Copyright (C) 2013, Lars Kellogg-Stedman JAMLIB is copyright (c) 1999 Björn Stenberg. JAMLIB is released under the GNU Lesser General Public License, See src/jamlib/jamlib.doc for more information. tests/roundup is copyright (c) 2010 Blake Mizerany - MIT License Except where explicitly stated otherwise, all other parts of CrashMail are copyright 1998-2004 Johan Billing. Permission to use, copy and distribute CrashMail is granted provided that this copyright notice is included. Permission to modify CrashMail is granted. Distributing modified versions of CrashMail is allowed provided that the documentation clearly states that it is a modified version. Parts of CrashMail may be freely used in other projects as long as the documentation mentions the original copyright holder. ================ Acknowledgements ================ Many thanks to Björn Stenberg for creating the excellent subroutine library JAMLIB which CrashMail uses for handling JAM messagebases. Thanks for Peter Karlsson for porting CrashMail II to OS/2 and the man pages. ============= Documentation ============= The documentation is very brief and CrashMail probably isn't the ideal choice for Fidonet beginners. All documentation of the available keywords in the configuration file can be found in the doc/example.prefs file, and other information can be found in the doc/ReadMe.txt file.. ========= Platforms ========= This version of CrashMail can be compiled for Win32, Linux and OS/2; see the INSTALL file for details. If you are interested in running CrashMail on another platform, please contact me if you are willing to do the work necessary to adapt CrashMail to your platform. The amount of work required mostly depends on whether your C-compiler supports some common POSIX-functions which CrashMail uses. crashmail-1.5/doc/000077500000000000000000000000001230617144500140725ustar00rootroot00000000000000crashmail-1.5/doc/AreafixHelp.txt000066400000000000000000000102001230617144500170140ustar00rootroot00000000000000 Description of CrashMail's built-in AreaFix =========================================== What is AreaFix? ---------------- AreaFix is a feature present at most FidoNet nodes, either built-in in the tosser or as a stand-alone program. AreaFix allows you to connect and disconnect to echomail areas, change your password and change some other things in your configuration without asking your feed/boss to do it for you. How do I talk to AreaFix? ------------------------- You communicate with AreaFix using netmail messages. The message header of a message to AreaFix should look like this: From: Johan Billing To: AreaFix Subj: [] ... --- is your private AreaFix password that you need to make sure that nobody else alters your configuration. Your AreaFix password is assigned to you by your boss/feed, so you may have to ask him about a password. [] are extra commands that you can send to AreaFix. Currently there is only one switch: -l or -q Send list of all areas This switch is only here to be compatible with other AreaFix programs. It is recommended that you use the %-commands described later instead. "---" marks the end of an AreaFix message. NOTE: The name doesn't necessarily have to be AreaFix, your boss may have configured CrashMail to use other names instead. What commands can I put in the text? ------------------------------------ Change connected areas: [+]areaname[,R=] Connect to an area. The '+' is optional. If the R option is specified after the area, the specified number of old messages in the area will be rescanned and sent to you. -areaname Disconnect from an area =areaname[,R=] Update. You can use this to rescan an area which you already are connected to. Special commands %HELP Send this text %LIST Send list of all areas %QUERY Send list of all linked areas %UNLINKED Send list of all unlinked areas %PAUSE Do not send any echomail until a %RESUME is sent %RESUME Send echomail again. These commands can be useful if you are away for a few weeks and don't want to get any echomail. %PWD Changes your AreaFix password to the new password specified after the command. %COMPRESS Changes the packer used to compress your mail to the packer specified after the command. Send "%COMPRESS ?" to get a list of packers. %RESCAN All areas that are added or updated after this line will be rescanned, that is all old messages in those areas will be sent to you. This can be very dangerous since you don't know how many messages you will get so in most cases, it is better to use the R option when adding/updating areas instead. Examples: ========= R20_TRASHCAN Connect to R20_TRASHCAN %LIST Send list of areas R20_AMIGA,R=50 Connect to R20_AMIGA and get the last 50 messages in the area. %PWD xyzzy Change your AreaFix password to "xyzzy". %COMPRESS ZIP Change your compression method to ZIP. %PAUSE Turn off echomail for a while %RESUME Turn on echomail again =R20_AMIGA,R=100 Get the last 100 messages in R20_AMIGA (if you are already connected to the area) A few words on rescan ===================== Messages that are rescanned might not look exactly like they originally did because of the way they are stored locally. When messages are rescanned from a JAM messagebase, all control lines ("kludges") will be at the beginning of the message regardless of where they originally were. You can easily tell if a message has been rescanned, just look for the RESCANNED control line. crashmail-1.5/doc/ReadMe.txt000066400000000000000000000405151230617144500157750ustar00rootroot00000000000000 CrashMail II The Next Generation! ...a stranger in a strange land... ============ Introduction ============ Welcome to CrashMail II! CrashMail II is basically a more portable version of CrashMail, my tosser for Amiga computers. Users of the old Amiga version will probably find some things familiar while some features are gone such as the ARexx port (for obvious reasons!) and the GUI configuration editor. The only feature that CrashMail II has and the old CrashMail hasn't is support for JAM messagebases. (By the way, I have also written a tick file processor called CrashTick for the Amiga. If someone wants to port it, contect me and I'll give you the source.) For suggestions, bug reports and questions, don't hesitate to contact me at: billing@df.lth.se ========= Copyright ========= Copyright (C) 1998-2004, Johan Billing Copyright (C) 1999-2010, Peter Krefting Copyright (C) 2009-2013, Robert James Clay Copyright (C) 2013, Lars Kellogg-Stedman JAMLIB is copyright (c) 1999 Björn Stenberg. JAMLIB is released under the GNU Lesser General Public License, See src/jamlib/jamlib.doc for more information. Except where explicitly stated otherwise, all other parts of CrashMail are copyright 1998-2004 Johan Billing. Permission to use, copy and distribute CrashMail is granted provided that this copyright notice is included. Permission to modify CrashMail is granted. Distributing modified versions of CrashMail is allowed provided that the documentation clearly states that it is a modified version. Parts of CrashMail may be freely used in other projects as long as the documentation mentions the original copyright holder. ================ Acknowledgements ================ Many thanks to Björn Stenberg for creating the excellent subroutine library JAMLIB which CrashMail uses for handling JAM messagebases. Thanks for Peter Karlsson for porting CrashMail II to OS/2 and the man pages. ============= Documentation ============= The documentation is very brief and CrashMail probably isn't the ideal choice for Fidonet beginners. All documentation of the available keywords in the configuration file can be found in the example crashmail.prefs file. Some other items about CrashMail that are worth mentioning can be found in the section below. Items that need to be discussed =============================== Platforms --------- This version of CrashMail can be compiled for Win32, Linux and OS/2. If you are interested in running CrashMail on another platform, please contact me if you are willing to do the work necessary to adapt CrashMail to your platform. The amount of work required mostly depends on whether your C-compiler supports some common POSIX-functions which CrashMail uses. Some notes on different platforms: Win32 & OS/2 If you want to use an old reader that only can handle 8+3 filenames, you have to use %8 in the path of your DEFAULT area if you are using the auto-add feature. This creates an 8 digit serial number to use as the path for the area. Note that if CrashMail is run twice in a short period of time (a few seconds), it might create duplicate paths. Avoid %8 if it is at all possible. Linux Don't use the ~ character in paths. Such paths are expanded to point to your home directory by the shell and not by the i/o functions in the system. They will not work in CrashMail. In *.msg areas, make sure that all files are named *.msg and not *.MSG! If they are not named in lowercase, CrashMail will not export them. As an extra bonus, the Linux version of CrashMail can use the syslog instead of using its own log file. Just use "syslog" as the name of your log file. If the precompiled binaries in the CrashMail archive don't work on your system, you will have to compile your own. See INSTALL for more information about this. Arguments --------- Available arguments for CrashMail: SCAN Scan all areas for messages to export. TOSS Toss all .pkt files and bundles in inbound directory. TOSSFILE Toss the specified file. TOSSDIR Toss all files in the specified directory. SCANAREA Scan the specified area. SCANLIST Scan all areas listed in the specified file. SCANDOTJAM Scan all areas listed in an echomail.jam/netmail.jam file. The main difference between SCANDOTJAM and SCANLIST is that a *.jam file contains the paths to the messagebases instead of tagnames. Areas are only scanned once even if listed multiple times. RESCAN RESCANNODE RESCANMAX Rescans the specied area for the specied node. If RESCANMAX is specified, it sets the maximum number of messages to rescan. SETTINGS Use this configuration file instead of the default. You can use the environment variable CMCONFIGFILE to set the default configuration file. VERSION Show version information about CrashMail. LOCK Locks CrashMail's configuration file and then exits. CrashMail has a simple locking mechanism to ensure that two instances of CrashMail never use the same configuration file at the same time. You can use this if you want to temporarily want to stop CrashMail from running, e.g. when updating the nodelist. UNLOCK Removes the lock on CrashMail's configuration file. Only use this when the configuration file previously has been locked with LOCK, otherwise terrible things might happen. NOSECURITY Process all packets without security checks. This is intended to be used mainly with TOSSDIR/TOSSFILE and with packets created by CrashWrite. Support programs ---------------- crashexport [GROUP ] This command reads a CrashMail configuration file and creates an arealist. If the GROUP keyword is used, only areas in the specified groups are included. CrashExport can create lists in these formats: AREASBBS A standard areas.bbs file that can be read by many programs FORWARD A list of areas that can be used for forward-requests on other nodes. The file is a pure ASCII file where each line contains the name of the area and its description. FORWARDNODESC Same as FORWARD but without area descriptions. GOLDED Creates an area configuration file in GoldED format. TIMED Creates an area configuration file in timEd format. crashstats [SORT ] [LAST7] [NONODES] [NOAREAS] This command displays the statistics file created by CrashMail. With the SORT keyword you can specify these sort modes: a Sort alphabetically t Sort by total number of messages m Sort by msgs/day d Sort by first time messages were imported l Sort by last time messages were importd u Sort by number of dupes With LAST7, you can see detailed information about the flow of messages in area areas for the last seven days. With NONODES and NOAREAS you can decide to hide node or area statistics. crashlist [] Builds an index for the nodelists in the specified directory (or in the current directory if no directory is specified). To find out what nodelists to read, CrashList uses a file called cmnodelist.prefs in the nodelist directory. The format of this file is as follows: [] As the name of the nodelist, you can either specify the full name of the nodelist or just the base name of the nodelist (without .xxx at the end). If you just specify the base name, CrashList will use the latest nodelist with that name (selected by date, not the extension). A default zone can be used for regional nodelists without a Zone line. All lines beginning with a semicolon are treated as comments. Pointlists should be in BinkleyTerm format and should be specified after the real nodelists. Example cmnodelist.prefs: ; Configuration for CrashList ; ; Format: [] NODELIST BTPOINT crashgetnode [] Looks up the specified node in the nodelist and prints the information that was found. If no nodelist directory is specified, CrashGetNode uses the path specified in the environment variable CMNODELISTDIR. crashmaint [MAINT] [PACK] [VERBOSE] [SETTINGS ] [PATTERN ] Deletes old messages according to KEEPNUM and KEEPDAYS in crashmail.prefs. The program can do two different operations on a messagebase, MAINT and PACK. The meaning of these two modes are different for different messagebase formats. *.msg MAINT deletes messages and PACK renumbers the area. JAM MAINT sets the Deleted flag for the messages. PACK removes all messages with the Deleted flag from the messagebase. Both MAINT and PACK can be specified at the same time. You can specify a config file other than the default with the SETTINGS keyword (use the environment variable CMCONFIGFILE to set the default configuration file). Using the PATTERN keyword, you can perform the operations on only some of your areas. VERBOSE gives you a lot of information which you don't really need. Example: crashmaint MAINT PACK PATTERN R20_AMIGA* crashwrite DIR ... CrashWrite reads a text file and creates a .pkt file that can be processed by CrashMail. This can be used to post announcements and other messages in areas. The best way to use CrashWrite is to let it generate packets in a separate directory and then toss them with TOSSDIR NOSECURITY. There are many keywords for CrashWrite. All keywords are optional except for DIRECTORY. If you do not enter a keyword, a default value will be used. FROMNAME FROMADDR TONAME TOADDR SUBJECT Use these keywords to set the header of the message. You only need to enter TONAME and TOADDR for netmails. PKTFROMADDR PKTTOADDR Use these if you want to set the origin and destination address of the packet to something other than the origin and destination address of the message inside the packet. If you do not specify these keywords, FROMADDR and TOADDR will be used for the packet as well. PASSWORD You can use this keyword to set a password for the packet. The maximum length of the password is eight characters. AREA The area the message should be posted in. If you do not enter an area, the message will be sent as a netmail. ORIGIN The origin line for the message. This keyword has no effect for netmail messages. DIR The directory where the packet should be placed. TEXT The name of a text file that should be included as the message text. NOMSGID Prevents CrashWrite from adding a MSGID line. FILEATTACH Sets the file-attach flag for netmails. The filename should be put in the subject line. crashlistout [] [] VERBOSE This command lists the contents of a outbound directory. Use zone to specify which zone the directory is for (the default is 2). It is possible to only list files for nodes that match a specified pattern. If you use the VERBOSE switch, crashlistout will also list the contents of any *.req and flow files. Paths ----- You should always use absolute paths in crashmail.prefs, otherwise CrashMail will fail to unpack incoming bundles. If you use relative paths, CrashMail will also use relative paths in flow files which might confuse your mailer. Outbound -------- CrashMail uses a 5D BinkleyTerm outbound. If there is a demand for FrontDoor style outbounds (*.msg based), it might be implemented in a future version. Messagebase formats ------------------- CrashMail currently can use *.msg messagebase and JAM messagebases. Some notes on the different messagebase formats: *.msg *.msg is the most basic format for Fidonet messages. It is specified in FTS-0001 and most Fidonet programs can handle this. There are however some variations. There are Zone and Point fields in the message header, but since some programs use them for other purposes, CrashMail doesn't read them. This means that CrashMail won't work if your reader doesn't create INTL, TOPT and FMPT kludge lines. Most readers do so this probably won't be a problem. JAM JAM is a newer messageformat which while not perfect at least is much better than *.msg. It provides reply-linking, but unfortunately not between areas. JAM has a few odd features which CrashMail does not support. CrashMail will not create TRACE fields from Via kludges, it does not support messages with multiple recipients (carbon copies) and it does not support file-attaches with wildcards, indirect file-attaches or file-attaches with aliases. CrashMail also handles only one attached file/file request per message. Highwater marks --------------- CrashMail can use highwater marks to speed up the exporting of messages. The highwater mark is only the number of the highest exported message in an area. If you decide to use highwater marks, CrashMail will only export messages with a message number that is higher that the old highwater mark. If you want to export messages with a lower number than the highwater mark, you have to force CrashMail to scan the whole area by deleting the file where the highwater mark is stored. In *.msg areas the highwater mark is stored in the first message of the area (1.msg) and in JAM areas it is stored in the .cmhw file. (Also note that this is why the first message in a *.msg area never is exported.) Nodelists --------- CrashMail can use two nodelist formats: 1) Its own nodelist format ("CMNL"). The format consists of a rather simple index which is created by the program CrashList. See the descriptions of CrashList and CrashGetNode for more information. 2) A nodelist in the Version7+ format ("V7+") used by BinkleyTerm and other programs. Patterns -------- String patterns String patterns are rather primitive in CrashMail. There are two available wildcards, ? and *. ? matches any character and * matches the rest of the string. ab*, ab*de and ab*de* are therefore equivalent and all match all strings beginning with ab. String patterns are used for robot names, remap names etc. Node patterns CrashMail has very powerful pattern matching for nodes. "*" and "?" can be used as wildcards and there a special keywords that matches all nodes that belongs to a zone, region, net, hub or a node. 2:200/207.* This would match 2:200/207.1, 2:200/207.2, 2:200/207.42 etc 2:200/2*.* This would match 2:200/213.99, 2:200/224.48, 2:200/207.0 etc. This would NOT match 2:200/103.42. 2:200/2?.* This would match 2:200/24.42, 2:200/25.52 but not 2:200/200.0. 2:*/100.0 This would match 2:200/100.0, 2:200/100.0, 2:300/100.0 etc. ZONE 2 This matches everything in zone 2. This has the same effect as 2:*/*.*. REGION 2:20 This matches everything in region 2:20. You can only use the REGION keyword if you use a nodelist. NET 2:200 Matches everything in net 2:200. This is the same as 2:200/*.*. HUB 2:205/300 Matches all node that belongs to the hub 2:205/300. You can only use the HUB keyword if you use a nodelist. NODE 2:200/108 Matches the node 2:200/108 and all its points. This does exactly the same as 2:200/108.*. *:*/*.* This would match everything. Destination node patterns These are a bit more complicated since the destination node of the operation is also involved. This is best explained with netmail routing as an example. In CrashMail, destination node patterns are also used in the remap function, but it works very similarly there. *:*/*.0, netmail for 2:200/108.7 This netmail would be routed to 2:200/108.0 *:*/0.0, netmail for 2:200/108.7 This netmail would be routed to 2:200/0.0 ZONE, netmail for 2:201/274 This netmail is routed to the Zone Coordinator, in this case 2:2/0. REGION, netmail for 2:200/207.5 This netmail is routed to the Region Coordinator, in this case 2:20/0. You can only use this keyword if you use a nodelist. NET, netmail for 2:200/108.7 This netmail is routed to the host of the net, in this case 2:200/0. This is the same as *:*/0.0 HUB, netmail for 2:200/108.7 This netmail is routed to the hub of the node, in this case 2:200/100. You can only use this keyword if you use a nodelist. NODE, netmail for 2:200/108.7 This netmail is routed to the boss of the point, in this case 2:200/108.0. This is equivalent to *:*/*.0. *:*/*.*, mail for 2:203/699.0 This would be routed to 2:203/699.0 crashmail-1.5/doc/example.prefs000066400000000000000000000453761230617144500166050ustar00rootroot00000000000000; Example configuration file for CrashMail II ; ; This file demonstrates all keywords you can use in the configuration file. ; ; General configuration ; ===================== ; ; SYSOP ; ; This keyword lets you configure the name of the system operator. It is used ; as the sender name of all messages that CrashMail generates. (Bounce ; messages, receipts, AreaFix responses.) Max 36 characters. SYSOP "Johan Billing" ; LOGFILE ; LOGLEVEL ; ; Here you can configure the logging in CrashMail. CrashMail will write all ; log messages with a level lower than the level configured here both to the ; console and to the specified file. ; ; The following loglevels exist: ; ; 1 Minimum ; 2 Small ; 3 Normal ; 4 Extra ; 5 Extreme ; 6 Debug ; ; In the Linux version of CrashMail, it is possible to enter "syslog" as the ; filename. If you do this, everything will be logged to the syslog instead. LOGFILE "c:\\fido\\logs\\crashmail.log" LOGLEVEL 5 ; DUPEFILE ; DUPEMODE BAD/KILL/IGNORE ; ; Here the file that CrashMail uses for its duplicate detection is specified. ; Maxnumber is the maximum number of the messages that should be stored in the ; dupe buffer. Each message in the dupe buffer consumes 8 bytes of RAM and on ; average about 40 bytes of disk space. ; ; These are the available modes for dupe checking: ; ; BAD Dupes are moved to the BAD area ; KILL Dupes are killed ; IGNORE No dupechecking DUPEFILE "c:\\fido\\logs\\crashmail.dupes" 200 DUPEMODE BAD ; LOOPMODE IGNORE/LOG/LOG+BAD ; ; Loop-mails are netmails that are routed between two systems in an infite ; loop. CrashMail can detect such mails by checking if you system is already ; listed in the ^Via lines of the message. This keyword decides what ; CrashMail should do when such a message is encountered. ; ; IGNORE do nothing at all ; LOG CrashMail logs that it has encountered a loop-mail. ; LOG+BAD CrashMail logs the loop-mail and imports a copy to your BAD area. ; Only the kludges are imported to preserve privacy. LOOPMODE LOG+BAD ; MAXPKTSIZE ; MAXBUNDLESIZE ; ; Here you can configure the maximum size of the .pkt files and bundles that ; CrashMail generates. If a file grows bigger than this limit, CrashMail ; starts a new bundle/pkt instead. The limits are in KB. MAXPKTSIZE 50 MAXBUNDLESIZE 100 ; DEFAULTZONE ; ; If CrashMail can't figure out the zone of a message in another way, the ; zone configured here is used. DEFAULTZONE 2 ; NODELIST ; ; This is the nodelist that CrashMail should use. To see the supported ; nodelist formats, type "crashmail version". The meaning of path may ; be different for different nodelist formats. ; Paths ; ===== ; ; INBOUND ; ; The inbound directory is the directory where CrashMail looks for .pkt files ; and bundles to toss. INBOUND "c:\\fido\\inbound" ; OUTBOUND ; ; The outbound directory is where CrashMail writes the flow files that tells ; the mailer what files to send. OUTBOUND "c:\\fido\\outbound" ; TEMPDIR ; ; This is the directory where CrashMail unpacks incoming bundles. ; TEMPDIR "c:\\fido\\temp" ; CREATEPKTDIR ; ; This is the directory where CrashMail stores created .pkt files until they ; are stored in the packet directory. CREATEPKTDIR "c:\\fido\\temp" ; PACKETDIR ; ; This is the directory where CrashMail stores generated bundles. PACKETDIR "c:\\fido\\packets" ; STATSFILE ; ; This is the file where CrashMail stores statistics about areas and nodes. ; You can display the contents of this file with CrashStats. STATSFILE "c:\\fido\\crashmail.stats" ; BEFORETOSS ; ; CrashMail will execute this command before a *.pkt file is tossed. You can ; use %f for the filename of the packet. CrashMail will abort tossing if this ; command returns an error. ;BEFORETOSS "cp %f /fido/toss-backup" ; BEFOREPACK ; ; CrashMail will execute this command before a *.pkt file is added to an ; archive. You can use %f for the filename of the packet. If the command ; fails, CrashMail will try again the next time it processes the outbound ; directory. ;BEFOREPACK "cp %f /fido/pack-backup" ; Switches ; ======== ; ; STRIPRE ; ; CrashMail should strip all occurences of "Re:", "Re[x]:" and "Re^x:" ; in the subject of messages before they are imported. ; STRIPRE ; FORCEINTL ; ; CrashMail should add an INTL line to all messages even when the sender ; and the destination are in the same zone. FORCEINTL ; NOROUTE ; ; CrashMail should never route netmails and just import them instead. NOROUTE ; ; ANSWERRECEIPT ; ; CrashMail should honor receipt requests. ; ANSWERRECEIPT ; ANSWERAUDIT ; ; CrashMail should honor audit requests. ; ANSWERAUDIT ; CHECKSEENBY ; ; CrashMail should never send echomail to nodes that already are in the ; SEEN-BY lines. CHECKSEENBY ; CHECKPKTDEST ; ; CrashMail should check the destination node of all incoming .pkt files and ; only toss them if they are adressed to one of the local AKAs. ;CHECKPKTDEST ; PATH3D ; ; CrashMail also adds points to ^PATH lines. Not always a good idea since it ; is not allowed in the echomail standard. PATH3D ; IMPORTEMPTYNETMAIL ; ; Some mailers like FrontDoor like to send meaningless empty netmails with ; no text. Spefify this options if you for some reason want to import such ; mails. ;IMPORTEMPTYNETMAIL ; IMPORTAREAFIX ; ; Use this if you want messages to CrashMail's internal AreaFix to be ; imported to your netmail area. IMPORTAREAFIX ; NODIRECTATTACH ; ; Normally CrashMail changes all netmail messages with attached file to ; direct status. Use this to turn off this behaviour. ;NODIRECTATTACH ; BOUNCEPOINTS ; ; Use this to bounce netmail messages to non-existing points with one of ; your AKAs as boss. ;BOUNCEPOINTS ; IMPORTSEENBY ; ; Use this if you want to import the SEEN-BY lines. IMPORTSEENBY ; AREAFIXREMOVE ; ; Use this to allow the AreaFix to remove areas when the last downlink ; unsubscribed. AREAFIXREMOVE ; WEEKDAYNAMING ; ; Name bundles according to the day of the week they are created. WEEKDAYNAMING ; ADDTID ; ; Add a ^TID line to all messages exported by CrashMail. ADDTID ; ALLOWRESCAN ; ; Allow nodes to rescan areas in the AreaFix. ALLOWRESCAN ; FORWARDPASSTHRU ; ; Make areas created when they were forward-requested by a downlink pass-thru ; rather than importing them to your messagebase. ;FORWARDPASSTHRU ; BOUNCEHEADERONLY ; ; Only write the header and do not include message text when messages are ; bounced. ;BOUNCEHEADERONLY ; REMOVEWHENFEED ; ; CrashMail should remove areas when the feed unsubscribes to them. AreaFix ; messages or notification messages can be sent to your downlinks, see the ; Node configuration. ;REMOVEWHENFEED ; INCLUDEFORWARD ; ; Include all forward-requestable areas in the area lists generated by the ; AreaFix. INCLUDEFORWARD ; NOMAXOUTBOUNDZONE ; ; CrashMail normally puts outgoing mail for all zones >4095 in the outbound ; directory for zone 4095 (usually outbound.fff). If this switch is turned ; on, CrashMail will use separate outbound directories also for zones >4095. ; The background of this option is that the Binkley outbound style originated ; on platforms where file names were limited to 8+3 characters. For ; compatibility with many older mailers, leave this switch turned off. ; There are however some mailers that also expect to find mail for zones ; >4095 in separate outbound directories. ;NOMAXOUTBOUNDZONE ; ALLOWKILLSENT ; ; If this option is used, CrashMail will delete all netmail messages with ; the killsent flag after they have been expored ;ALLOWKILLSENT ; FLOWCRLF ; ; If this option is used, CrashMail writes CRLF as the end-of-line character ; in flow files instead of just LF. Apparently some mailers need this. ;FLOWCRLF ; NOEXPORTNETMAIL ; ; If this option is used, CrashMail will skip netmail areas when using the ; SCAN or SCANLIST arguments. Use this if you want to use another program ; to handle netmail. ;NOEXPORTNETMAIL ; Groupnames ; ========== ; ; GROUPNAME ; ; Here you can describe you groups. These descriptions are used in the area ; lists created by the AreaFix. GROUPNAME A "Molia" GROUPNAME H "Lokala" ; Bounce ; ====== ; ; BOUNCE ... ; ; Bounce messages that match one of these pattern if the destination node ; doesn't exist in the nodelist. This only works if you use a nodelist with ; CrashMail. You can have multiple BOUNCE lines. BOUNCE "1:*/*.*" "2:*/*.*" "3:*/*.*" "4:*/*.*" "5:*/*.*" "6:*/*.*" ; Fileattach ; ========== ; ; FILEATTACH ... ; ; CrashMail will refuse to route files to nodes that are not configured ; here and will send a bounce messages. Multiple FILEATTACH lines are ; allowed. FILEATTACH "2:200/207.5" ; Change ; ====== ; ; CHANGE ; ; CrashMail can change the flavour (also known as priority) of netmail. Note ; that this does not affect echomail. ;CHANGE * 2:200/207.5 Hold ;CHANGE Normal,Direct 2:200/*.* Crash ; Packers ; ======= ; ; PACKER ; ; Here you configure the external packers that CrashMail uses. %a stands for ; archive name and %f stands for file name. The recog string is used when ; CrashMail detects the packer used to pack a bundle. If the beginning of ; the bundle matches the recog string, CrashMail uses that packer. ? can be ; used as a wildcard and you can use $xx to specify a hexadecimal number. PACKER "LHA" "c:\\fido\\bin\\lha a %a %f" "c:\\fido\\bin\\lha x %a" "??-lh?-" PACKER "ZIP" "c:\\fido\\bin\\pkzip %a %f" "c:\\fido\\bin\\pkunzip %a" "PK" ; AKA ; === ; ; AKA ; DOMAIN ; ADDNODE node1 node2 ... ; REMNODE nodepattern1 nodepattern2 ... ; ; Here you configure the adresses of your node. ADDNODE is used to add nodes ; the the SEEN-BY lines in areas with this AKA and REMNODE is used to remove ; nodes. Nodes for ADDNODE and REMNODE has to be 2D, that is only net/node ; should be specified. Patterns are allowed for REMNODE. AKA 2:200/207.6 DOMAIN "FidoNet" AKA 2:200/108.7 DOMAIN "FidoNet" ; Nodes ; ===== ; ; NODE [] ; PKTFROM ; AREAFIXINFO ; DEFAULTGROUP ; REMOTEAF [NEEDSPLUS] ; REMOTESYSOP ; ; These are the nodes you interchange mail with. You can only send echomail ; to nodes specified here, but netmail can also be sent to other nodes. ; ; If PKTFROM is used, the node specified there will be used as the originating ; node in all packet files sent to this node. ; ; The groups decide what areas a node may subscribe to in the AreaFix. If the ; area is in one of the read-only groups, the node will be added as read-only. ; If a new area is added in one of the add groups, this node will automatically ; be subscribed to it. The default group is the group used for areas that are ; autoadded from this node. ; ; REMOTEAF and REMOTESYSOP are used when CrashMail needs to send messages to ; the AreaFix or the sysop of this node. Use NEEDSPLUS if the remote AreaFix ; needs commands wants "+area" when subscribing to new areas instead of just ; "area". ; ; The following flags can be set for a node: ; ; NOTIFY ; ; When a SENDQUERY, SENDLIST, SENDUNLINKED, SENDINFO or SENDHELP with the ; argument ALL is used on the command-line, messages will be sent to all ; nodes with this flag set. ; ; PASSIVE ; ; Echomail is never sent to this node. Used for the %PAUSE and %RESUME ; commands in the AreaFix. ; ; NOSEENBY ; ; No SEEN-BY lines are included in messages sent to this node. Should ; normally not be used. ; ; TINYSEENBY ; ; Only sender and destination is included in SEEN-BY lines in messages ; sent to this node. Should normally not be used. ; ; FORWARDREQ ; ; This node is allowed to do forward-requests. ; ; PACKNETMAIL ; ; Netmail to this node should be packed along with the echomail. ; ; SENDAREAFIX ; ; AreaFix disconnect requests should be sent to this node when the feed ; unsubscribes from an area. ; ; SENDTEXT ; ; Notification messages should be sent to the sysop of this node when the ; feed unsubscribes from an area. ; ; AUTOADD ; ; New areas from this node should be auto-added to the configuration. If ; auto-add is not set, the areas will still be added but with an UNCONFIRMED ; line. ; ; CRASH ; ; Send echomail to this node with priority Crash. ; ; DIRECT ; ; Send echomail to this node with priority Direct. ; ; HOLD ; ; Send echomail to this node with priority Hold. ; NODE 2:200/100.0 "ZIP" "" PACKNETMAIL AUTOADD DEFAULTGROUP A NODE 2:201/128.0 "ZIP" "" PACKNETMAIL AUTOADD DEFAULTGROUP B NODE 2:200/207.5 "LHA" "secret" NOTIFY FORWARDREQ PACKNETMAIL AUTOADD AREAFIXINFO "secret" "" "" "" REMOTESYSOP "Johannes Nilsson" ; AreaFix ; ======= ; ; AREAFIXHELP ; ; The file that is sent when a downlink issues a %HELP command. AREAFIXHELP "c:\\fido\\AreafixHelp.txt" ; AREAFIXMAXLINES ; ; The maximum number of lines in an AreaFix response. CrashMail splits the ; response if it exceeds this number. ; AREAFIXMAXLINES 50 ; AREAFIXNAME ; ; A name that CrashMail's AreaFix should respond to. AREAFIXNAME "AreaFix" AREAFIXNAME "AreaMgr" AREAFIXNAME "CrashMail" ; AREALIST [GROUP ] [FORWARD] [DESC] ; ; This is a list of the areas that are available at a node. It should contain ; lines with the format " ". If DESC is specified, ; descriptions are taken from this file when CrashMail auto-adds areas. If ; FORWARD is specified, this file is used to determine what files are ; available for forward-requests. GROUP specifies the group needed to be ; allowed to forward-requests areas in this list. AREALIST 2:200/100.0 "c:\\fido\\lists\\R20Desc.lst" GROUP A FORWARD DESC ; Routing ; ======= ; ; ROUTE ; ; Netmail messages with a destination that match the pattern are routed to ; the destination pattern using the specified AKA. See descriptions of how ; patterns work elsewhere. ROUTE "2:200/207.5" "2:200/207.5" 2:200/108.7 ROUTE "2:200/*.*" "2:200/100.0" 2:200/108.7 ROUTE "*:*/*.*" "2:201/128.0" 2:200/108.7 ; MSG ; === ; ; MSG_HIGHWATER ; ; Use 1.msg as highwater mark in *.msg areas. MSG_HIGHWATER ; MSG_WRITEBACK ; ; Overwrite the old message with the new message when it is imported instead ; of just changing the SENT flag. Use this to see the new SEEN-BY:s and PATH ; in the messagebase. ;MSG_WRITEBACK ; JAM ; === ; ; JAM_HIGHWATER ; ; Use highwater marks to speed up scanning. The highwater mark is stored in ; a file called .cmhw. JAM_HIGHWATER ; JAM_LINK ; ; Do reply-linking based on MSGID and REPLY after import. JAM_LINK ; JAM_QUICKLINK ; ; Just compare the CRC of MSGID/REPLY when linking and don't read the strings ; from the messagebase. This makes linking quicker, but messages that don't ; match may be linked by mistake. ; JAM_QUICKLINK ; JAM_MAXOPEN ; ; This is the number of JAM messagebases that CrashMail keeps open at a time. ; A higher number speeds up tossing, but since CrashMail keeps four files ; open for each area, don't use a too high number if you only can have a ; limited number of files open... JAM_MAXOPEN 5 ; Filter ; ====== ; ; CrashMail has a message filter that can be used for filtering out messages ; that match the specified criteria and perform a number of commands on them. ; See file doc/filter.txt for a complete description of the message filter. ; ; General syntax: ; ; FILTER ; ; ; ... ;FILTER SOURCE=TOSSED and TYPE=ECHOMAIL and TONAME="Johan Billing" ;COPY PERSONAL_MESSAGES ; Areas ; ===== ; ; AREA/NETMAIL/LOCALAREA [ ] ; IMPORT/EXPORT ... ; BANNED ... ; DESCRIPTION ; GROUP ; KEEPNUM ; KEEPDAYS ; UNCONFIRMED ; MANDATORY ; DEFREADONLY ; IGNOREDUPES ; IGNORESEENBY ; ; Here you configure all areas that CrashMail knows. Area definitions begin ; with AREA for echomail areas and NETMAIL for netmail areas. Local areas ; defined with LOCALAREA are not used by CrashMail, but are included in ; config files created by CrashExport and are maintained when running ; CrashMaint. ; ; To see the supported messagebase formats in your version of CrashMail, ; type "crashmail version". What path should be used depends on the used ; messagebase formats. ; ; Netmail messages addressed to the Aka or to one of the nodes specified on ; an IMPORT line are imported in netmail areas. Echomail areas cannot have ; an IMPORT line but instead has one or more EXPORT lines where the nodes ; that this area should be sent to are listed. Each node on an export line ; has the format "[]" where modifier may be !, @ or %. ; ! means that the node is read-only, @ means that the node is write-only ; and % means that the node is the feed for this area. ; ; Note that nodes on the EXPORT line may be abbreviated. And example: ; ; EXPORT 2:2/2 1 .5 3/2 .22 3 .33 ; ; will be expanded to ; ; EXPORT 2:2/2 2:2/1 2:2/1.5 2:3/2 2:3/2.22 2:3/3 2:3/3.33 ; ; Nodes in the BANNED line may not subscribe to this area with the AreaFix. ; MANDATORY means that nodes may not unsubscribe from this area in the ; AreaFix. DEFREADONLY means that nodes that subscribe to this area in the ; AreaFix will be added as read-only. ; ; Areas with UNCONFIRMED are areas that have been auto-added by CrashMail ; but not yet confirmed. Areas get this flag when the node didn't have the ; flag AUTOADD set. CrashMail treats unconfirmed areas as if they didn't ; exist at all. ; KEEPNUM and KEEPDAYS are used by CrashMaint to decide how long messages ; should be kept in the messagebase. ; ; An area with the tagname BAD is a special area that are used for messages ; that for some reason are considered "bad" by CrashMail. ; ; Another special kind of areas are the default areas. When CrashMail adds ; an area, it searches for a default area to use as a template. First it ; looks for an area named DEFAULT_ where contains the group ; of the new area. If such an area doesn't exist, it looks for an area called ; DEFAULT. If a default area was found, CrashMail copies this configuration ; for this area to the new area. In the path of the default area, you can ; use the following %-codes: ; ; %a Name of the area ; %l Name of the area in lowercase letters ; %8 Eight digit serial number ; ; You must use one of these %-codes or the new path will not be unique. NETMAIL "NETMAIL" 2:200/108.7 JAM "c:\\fido\\areas\\NETMAIL" AREA "BAD" 2:200/108.7 JAM "c:\\fido\\areas\\BAD" AREA "DEFAULT_A" 2:200/108.7 JAM "c:\\fido\\areas\\%8" AREA "R20_INTRESSE" 2:200/108.7 JAM "c:\\fido\\areas\\36124179" EXPORT %2:200/100.0 DESCRIPTION "Intresseklubben" GROUP A AREA "R20_TRASHCAN" 2:200/108.7 JAM "c:\\fido\\areas\\3612417a" EXPORT %2:200/100.0 DESCRIPTION "Soptunnan" GROUP A crashmail-1.5/doc/filter.txt000066400000000000000000000202711230617144500161220ustar00rootroot00000000000000 Description of the message filter in CrashMail II ================================================= Introduction ------------ The message filter makes it possible filter out messages that match a set of critera and perform a number of commands on them. All messages that are handled by CrashMail are checked against the filter statements in the configuration. Filter statements have this general syntax in the configuration: FILTER ... This file will start by describing how expressions work and what variables you can use in them and then continue by describing the available commands. This file relies heavily on examples to describe how the filter works. Expressions ----------- Expressions have this general syntax: CrashMail understands the operator "=" for all variable types expect text variables and also "|"(substring search) for string and text variables. Boolean variables can be used without an operator in which case "variable" equals "variable=TRUE". You cannot have space characters around the operator, CrashMail will not understand for example "FILEATTACH = TRUE". A few examples: TONAME="Johan Billing" SUBJECT=CrashMail TEXT|"CrashMail" TOLOCALAKA If the variable matches the pattern, the expression is TRUE and the commands for the filter will be performed. You can also use NOT to negate a statement and perform the commands if the expression is not true: NOT FROMNAME="AreaFix" Expressions can be linked using AND and OR. Parentheses can be used to set the evaluation order. (Default is evaluation from left to right.) Examples: TYPE=NETMAIL and TOLOCALAKA and TONAME=Raid TYPE=ECHOMAIL and (TONAME="Johan Billing" or TONAME="Billing Johan") TYPE=NETMAIL and NOT (FROMNAME=AreaFix or FROMNAME=Raid) Variables --------- String variables FROMNAME TONAME SUBJECT AREA TYPE SOURCE FROMNAME, TONAME and SUBJECT should be self-explanatory. AREA is the tagname of the area for echomail messages and for netmail messages it will be empty. TYPE can be either NETMAIL or ECHOMAIL. SOURCE describes where the message came from and can have these values: TOSSED message was read from a *.pkt file EXPORTED message was exported from a messagebase CRASHMAIL message was generated by CrashMail (bounce messages, AreaFix responses etc) The "=" operator matches a variable against a pattern (see description of CrashMail's patterns in ReadMe.txt). "|" makes a substring search. Examples: FROMNAME=Johan* SOURCE=CRASHMAIL SUBJECT|AreaFix Node variables FROMADDR TOADDR For netmail messages, these are the addresses found in the message header. For echomail messages, FROMADDR is taken from the Origin line and TOADDR will be empty. Node variables are matched against a node pattern using the "=" operator. Examples: FROMADDR="1:*/*.*" TOADDR="2:200/207.6" Text variables TEXT KLUDGES TEXT is the message text without kludges and KLUDGES contains all the kludges (lines beginning with 0x01). You can only do substring searches on text variables using the "|" operator. Examples: TEXT|CrashMail KLUDGES|"TID: CrashMail" Boolean variables FILEATTACH TOLOCALAKA FROMLOCALAKA TOLOCALPOINT FROMLOCALPOINT EXISTSCFG_FROMADDR EXISTSCFG_FROMBOSS EXISTSCFG_TOADDR EXISTSCFG_TOBOSS EXISTSNL_FROMADDR EXISTSNL_FROMBOSS EXISTSNL_TOADDR EXISTSNL_TOBOSS Boolean variables are always TRUE or FALSE. Boolean variables can be used either alone (variable and variable=TRUE is the same thing) or as variable=TRUE/FALSE. FILEATTACH will be TRUE if the message has an attached file. FROMLOCALAKA/TOLOCALAKA will be TRUE if the message is from/to one of the AKAs configured in the configuration file. FROMLOCALPOINT/TOLOCALPOINT will be TRUE if the message is from/to a point under one of the AKAs configured in the configuration file. EXISTSCFG_FROMADDR will be TRUE if the message is from an address configured as a NODE in the configuration file. EXISTSCFG_FROMBOSS is similar, but if the node is a point (x:y/z.p), CrashMail will look for the boss (x:y/z.0) in the node configuration instead. EXISTSCFG_TOADDR/EXISTSCFG_TOBOSS are similar, but for the destination address. EXISTSNL_* are similar to EXISTSCFG_*, but instead of looking for the nodes in the node configuration, the variable will only the TRUE if the node is found in the external nodelist. Examples: TOLOCALAKA or TOLOCALAKA=TRUE (equivalent) FROMLOCALPOINT=FALSE or NOT FROMLOCALPOINT (equivalent) Commands -------- TWIT Don't import the message. The message will be sent to downlinks as usual. Example: FILTER TYPE=ECHOMAIL and FROMNAME="Hubba Hopp" TWIT KILL Completely discard the message. Example: FILTER SOURCE=CRASHMAIL KILL COPY Write a copy of the message to a local area. The local area needs to be configured in the configuration using LOCALAREA. Example: FILTER SOURCE=TOSSED and TYPE=ECHOMAIL and TONAME="Johan Billing" COPY PERSONAL_MESSAGES EXECUTE Executes an external command. The following codes can be used in the command: %r RFC-style with Fidonet addresses. Name of a file that contains a message in a text format similar to that used for e-mail messages on the internet, but addresses are still in x:x/x.x@domain format. %R RFC-style with RFC-style addresses. Name of a file that contains a message in a text format similar to that used for e-mail messages on the internet, but addresses are in name@pX.fX.nX.zX.domain format. %m Name of a file that contains the message as a *.msg file %a Area name (echomail only, will be empty for netmail areas) %f From name %o Originating node %t To name %d Destination node %s Subject %x Date and time of the message CrashMail will react differently depending on the exit code of the executed command: 0 The message is not imported 10 The message is imported 20 CrashMail aborts Example: FILTER TYPE=NETMAIL and TOLOCALAKA and TONAME="Raid" EXECUTE "raid %r" WRITELOG Writes a message to the logfile (loglevel 1). The same %-codes as for the EXECUTE command can be used here as well except for %r, %R and %m. Example: FILTER TYPE=ECHOMAIL and SOURCE=TOSSED WRITELOG "From: %f Subj: %s Area: %a" WRITEBAD Writes the message to the BAD area with specified as the reason. The message is not deleted unless you also use the KILL command. FILTER TYPE=NETMAIL and NOT EXISTSNL_TOBOSS WRITEBAD "Warning: Destination not in nodelist, message might not arrive" BOUNCEMSG BOUNCEHEADER These commands writes an error message to the sender of the filtered message. BOUNCEMSG includes a full copy of the original message and BOUNCEHEADER only includes the message header. In the message to the sender, you can use the same %-codes as for the EXECUTE command except for %r, %R and %m. The message will not be deleted unless you also use the KILL command. Example: FILTER TYPE=NETMAIL and not TOLOCALAKA and not EXISTSCFG_TOADDR BOUNCEMSG "Destination node %d does not exist, message can not be delivered" KILL REMAPMSG This command can change the destination name and node of a netmail message. If the new name is "*", the old name will be kept. Examples: FILTER TYPE=NETMAIL and TOLOCALAKA and TONAME="Johannes" REMAPMSG "Johannes Nilsson" 2:200/2075 FILTER TYPE=NETMAIL and TOADDR=2:999/*.* REMAPMSG "*" 2:200/*.* Additional examples ------------------- Copy all talk about me to a special area: FILTER TYPE=ECHOMAIL and SOURCE=TOSSED and TEXT|billing COPY BILLING_DISCUSSIONS Keep a copy of all sent netmails: FILTER TYPE=NETMAIL and SOURCE=EXPORTED COPY SENT_NETMAILS Log all routed messages: FILTER TYPE=NETMAIL and SOURCE=TOSSED WRITELOG "Routed message from %f (%o) to %t (%d)" Search all messages for a string: FILTER TEXT|CrashMail COPY CRASHMAIL_DISCUSSIONS Make customized bounce messages: FILTER TYPE=NETMAIL AND NOT EXISTSNL_TOBOSS BOUNCEMSG "Destination node %d does not exist in nodelist, your message cannot be delivered" KILL Make it possible to inspect all messages generated by CrashMail: FILTER SOURCE=CRASHMAIL COPY CRASHMAIL_MESSAGES crashmail-1.5/man/000077500000000000000000000000001230617144500141005ustar00rootroot00000000000000crashmail-1.5/man/crashexport.1000066400000000000000000000023651230617144500165320ustar00rootroot00000000000000.TH CRASHEXPORT 1 1999-08-01 "Johan Billing" "CrashMail" .SH NAME crashexport \- Export CrashMail configuration .SH SYNOPSIS .B crashexport crashmail.prefs outputfile format [GROUP groups] .SH DESCRIPTION This command reads a CrashMail configuration file and creates an arealist. .SH OPTIONS .TP .I GROUP group Only areas in the specified groups are included. .SH FORMATS CrashExport can create lists in these formats: .TP .I AREASBBS A standard areas.bbs file that can be read by many programs .TP .I FORWARD A list of areas that can be used for forward-requests on other nodes. The file is a pure ASCII file where each line contains the name of the area and its description. .TP .I FORWARDNODESC Same as FORWARD but without area descriptions. .TP .I GOLDED Creates an area configuration file in GoldED format. .TP .I TIMED Creates an area configuration file in timEd format. .SH EXAMPLES .TP .I crashexport crashmail/crashmail.prefs golded.areas GOLDED Exports the configuration file .I crashmail/crashmail.prefs to a GoldEd area configuration in .IR golded.areas . .SH "SEE ALSO" .BR crashmail (1) .\"SH FILES .\"SH BUGS .SH AUTHOR CrashMail is written by Johan Billing .PP This manual page was written by Peter Karlsson crashmail-1.5/man/crashgetnode.1000066400000000000000000000013321230617144500166270ustar00rootroot00000000000000.TH CRASHGETNODE 1 1999-08-01 "Johan Billing" "CrashMail" .SH NAME crashgetnode \- Lookup node in CrashMail nodelist .SH SYNOPSIS .B crashgetnode node [nodelistdir] .SH DESCRIPTION Looks up the specified node in the nodelist and prints the information that was found. If no nodelist directory is specified, CrashGetNode uses the path specified in the environment variable CMNODELISTDIR. .SH OPTIONS .TP .I node The node address to be looked up. .TP .I nodelistdir The directory where the nodelist resides. .SH "SEE ALSO" .BR crashmail (1), .BR crashlist (1) .\".SH FILES .\".SH BUGS .SH AUTHOR CrashMail is written by Johan Billing . .PP This manual page was written by Peter Karlsson . crashmail-1.5/man/crashlist.1000066400000000000000000000024731230617144500161640ustar00rootroot00000000000000.TH CRASHLIST 1 1999-08-01 "Johan Billing" "CrashMail" .SH NAME crashlist \- Compile a CrashMail nodelist .SH SYNOPSIS .B crashlist dir .SH DESCRIPTION Builds an index for the nodelists in the specified directory (or in the current directory if no directory is specified). To find out what nodelists to read, CrashList uses a file called cmnodelist.prefs in the nodelist directory. The format of this file is as follows: .PP [] .PP As the name of the nodelist, you can either specify the full name of the nodelist or just the base name of the nodelist (without .xxx at the end). If you just specify the base name, CrashList will use the latest nodelist with that name (selected by date, not the extension). A default zone can be used for regional nodelists without a Zone line. All lines beginning with a semicolon are treated as comments. Pointlists should be in BinkleyTerm format and should be specified after the real nodelists. .PP .\"SH OPTIONS .\"SH EXAMPLES .SH "SEE ALSO" .BR crashmail (1), .BR crashgetnode (1) .SH FILES Example cmnodelist.prefs: .nf ; Configuration for CrashList ; ; Format: [] NODELIST BTPOINT .\"SH BUGS .SH AUTHOR CrashMail is written by Johan Billing .PP This manual page was written by Peter Karlsson crashmail-1.5/man/crashmail.1000066400000000000000000000101701230617144500161240ustar00rootroot00000000000000.TH CRASHMAIL 1 2013-02-11 "Johan Billing" "CrashMail" .SH NAME crashmail \- A Fidonet *.JAM and MSG tosser .SH SYNOPSIS .B crashmail [SETTINGS filename] [SCAN] [TOSS] [TOSSFILE filename] [TOSSDIR directory] [SCANAREA area] [SCANLIST filename] [SENDLIST address] [SENDQUERY address] [SENDUNLINKED address] [SENDHELP address] [SENDINFO address] [REMOVE address] [RESCAN area RESCANNODE node [RESCANMAX max]] [VERSION] [LOCK] [UNLOCK] [NOSECURITY] .SH DESCRIPTION Welcome to CrashMail II! CrashMail II is basically a more portable version of CrashMail, my tosser for Amiga computers. Users of the old Amiga version will probably find some things familiar while some features are gone such as the ARexx port (for obvious reasons!) and the GUI configuration editor. The only feature that CrashMail II has and the old CrashMail hasn't is support for JAM messagebases. .SH OPTIONS .TP .I SCAN Scan all areas for messages to export. .TP .I TOSS Toss all .pkt files and bundles in inbound directory. .TP .I TOSSFILE filename Toss the specified file. .TP .I TOSSDIR directory Toss all files in the specified directory. .TP .I SCANAREA area Scan the specified area. .TP .I SCANLIST filename Scan all areas listed in the specified file. .TP .I RESCAN area .PD 0 .TP .I RESCANNODE node .TP .I RESCANMAX max Rescans the specied area for the specied node. If RESCANMAX is specified, it sets the maximum number of messages to rescan. .PD .TP .I SENDQUERY address Send a list of linked areas to the REMOTESYSOP of the specified node. .PD .TP .I SENDLIST address Send a list of available areas to the REMOTESYSTEOP of specified node. .PD .TP .I SENDUNLINKED address Send a list of unlinked areas to the REMOTESYSOP of the specified node. .PD .TP .I SENDHELP address Send Areafix help file to the REMOTESYSOP on the specified node. .PD .TP .I SENDINFO address Send Areafix information (password, group access) to the REMOTESYSOP on the specified node. .PD .TP .I SETTINGS filename Use this configuration file instead of the default.You can use the environment variable CMCONFIGFILE to set the default configuration file. .TP .I VERSION Show version information about CrashMail. .TP .I LOCK Locks CrashMail's configuration file and then exits. CrashMail has a simple locking mechanism to ensure that two instances of CrashMail never use the same configuration file at the same time. You can use this if you want to temporarily want to stop CrashMail from running, e.g. when updating the nodelist. .TP .I UNLOCK Removes the lock on CrashMail's configuration file. Only use this when the configuration file previously has been locked with LOCK, otherwise terrible things might happen. .TP .I NOSECURITY Process all packets without security checks. This is intended to be used mainly with TOSSDIR/TOSSFILE and with packets created by CrashWrite. .\"SH EXAMPLES .SH "SEE ALSO" .BR crashexport (1), .BR crashlist (1), .BR crashmaint (1), .BR crashwrite (1), .BR crashgetnode (1), .BR crashstats (1) .SH FILES .I crashmail.prefs .SH BUGS .SS Win32 If you want to use an old reader that only can handle 8+3 filenames, you have to use %8 in the path of your .I DEFAULT area if you are using the auto-add feature. This creates an 8 digit serial number to use as the path for the area. Note that if CrashMail is run twice in a short period of time (a few seconds), it might create duplicate paths. Avoid %8 if it is at all possible. .SS Linux Don't use the ~ character in paths. Such paths are expanded to point to your home directory by the shell and not by the i/o functions in the system. They will not work in CrashMail. .PP In *.msg areas, make sure that all files are named *.msg and not *.MSG! If they are not named in lowercase, CrashMail will not export them. .PP As an extra bonus, the Linux version of CrashMail can use the syslog instead of using its own log file. Just use "syslog" as the name of your log file. .PP If the precompiled binaries in the CrashMail archive don't work on your system, you will have to compile your own. See .I src/ReadMe.txt for more information about this. .SH AUTHOR CrashMail is written by Johan Billing .PP This manual page was written by Peter Karlsson crashmail-1.5/man/crashmaint.1000066400000000000000000000025451230617144500163210ustar00rootroot00000000000000.TH CRASHMAINT 1 1999-08-01 "Johan Billing" "CrashMail" .SH NAME crashmaint \- Do maintanence on CrashMail message bases .SH SYNOPSIS .B crashmaint [MAINT] [PACK] [VERBOSE] [SETTINGS filename] [PATTERN pattern] .SH DESCRIPTION Deletes old messages according to .B KEEPNUM and .B KEEPDAYS in .IR crashmail.prefs . The program can do two different operations on a messagebase, .I MAINT and .IR PACK . The meaning of these two modes are different for different messagebase formats. .TP .B *.msg .I MAINT deletes messages and .I PACK renumbers the area. .TP .B JAM .I MAINT sets the Deleted flag for the messages. .I PACK removes all messages with the Deleted flag from the messagebase. .PP Both .I MAINT and .I PACK can be specified at the same time. .SH OPTIONS .TP .I SETTINGS filename Specifies that another config file than the default should be used. You can use the environment variable CMCONFIGFILE to set the default configuration file. .TP .I PATTERN pattern Lets you perform the operations on only some of your areas. .TP .I VERBOSE Gives you a lot of information which you don't really need. .SH EXAMPLES crashmaint MAINT PACK PATTERN R20_AMIGA* .SH "SEE ALSO" .BR crashmail (1) .\"BR crashmail.prefs (5) .\"SH FILES .\"SH BUGS .SH AUTHOR CrashMail is written by Johan Billing .PP This manual page was written by Peter Karlsson crashmail-1.5/man/crashstats.1000066400000000000000000000021141230617144500163370ustar00rootroot00000000000000.TH CRASHSTATS 1 1999-08-01 "Johan Billing" "CrashMail" .SH NAME crashstats \- Display CrashMail statistics .SH SYNOPSIS .B crashstats statsfile [SORT mode] [LAST7] [NONODES] [NOAREAS] .SH DESCRIPTION This command displays the statistics file created by CrashMail. .SH OPTIONS .TP .I statsfile The statistics file to look up information from. .TP .I SORT mode This keywords specifies the sort mode (see below for list of sort modes). .TP .I LAST7 Displays detailed information about the flow of messages in areas for the last seven days. .TP .I NONODES Hide node statistics. .TP .I NOAREAS Hide area statistics. .SH "SORT MODES" .TP .B a Sort alphabetically. .TP .B t Sort by total number of messages. .TP .B m Sort by msgs/day. .TP .B d Sort by first time messages were imported. .TP .B l Sort by last time messages were imported. .TP .B u Sort by number of dupes. .\"SH EXAMPLES .SH "SEE ALSO" .BR crashmail (1) .SH FILES .I crashmail.stats .\"SH BUGS .SH AUTHOR CrashMail is written by Johan Billing .PP This manual page was written by Peter Karlsson crashmail-1.5/man/crashwrite.1000066400000000000000000000043551230617144500163440ustar00rootroot00000000000000.TH CRASHWRITE 1 1999-08-01 "Johan Billing" "CrashMail" .SH NAME crashwrite \- Creates PKT file from text file .SH SYNOPSIS .B crashwrite DIR directory [FROMNAME string] [FROMADDR node] [TONAME string] [TOADDR node] [SUBJECT string] [AREA area] [ORIGIN origin] [TEXT filename] [NOMSGID] [FILEATTACH] [PKTFROMADDR node] [PKTTOADDR node] PASSWORD [string] .SH DESCRIPTION CrashWrite reads a text file and creates a .pkt file that can be processed by CrashMail. This can be used to post announcements and other messages in areas. The best way to use CrashWrite is to let it generate packets in a separate directory and then toss them with TOSSDIR NOSECURITY. .PP There are many keywords for CrashWrite. All keywords are optional except for DIRECTORY. If you do not enter a keyword, a default value will be used. .SH OPTIONS .PD 0 .TP .I FROMNAME string .TP .I FROMADDR node .TP .I TONAME string .TP .I TOADDR node .TP .I SUBJECT string Use these keywords to set the header of the message. You only need to enter TONAME and TOADDR for netmails. .PD .TP .PD 0 .TP .I PKTFROMADDR node .TP .I PKTTOADDR node Use these if you want to set the origin and destination address of the packet to something other than the origin and destination address of the message inside the packet. If you do not specify these keywords, FROMADDR and TOADDR will be used for the packet as well. .PD .TP .I PASSWORD string You can use this keyword to set a password for the packet. The maximum length of the password is eight characters. .TP .I AREA area The area the message should be posted in. If you do not enter an area, the message will be sent as a netmail. .TP .I ORIGIN origin The origin line for the message. This keyword has no effect for netmail messages. .TP .I DIR directory The directory where the packet should be placed. .TP .I TEXT filename The name of a text file that should be included as the message text. .TP .I NOMSGID Prevents CrashWrite from adding a MSGID line. .TP .I FILEATTACH Sets the file-attach flag for netmails. The filename should be put in the subject line. .\"SH EXAMPLES .SH "SEE ALSO" .BR crashmail (1) .\"SH FILES .\"SH BUGS .SH AUTHOR CrashMail is written by Johan Billing .PP This manual page was written by Peter Karlsson crashmail-1.5/src/000077500000000000000000000000001230617144500141145ustar00rootroot00000000000000crashmail-1.5/src/Makefile000066400000000000000000000024101230617144500155510ustar00rootroot00000000000000# type either "make linux" or "make win32" to compile help: @echo You can use this Makefile in the following ways: @echo make linux ............ Make Linux binaries @echo make win32 ............ Make Win32 binaries @echo make os2 .............. Make OS/2 binaries @echo make cleanlinux ....... Remove object files under Linux @echo make cleanwin32 ....... Remove object files under Win32 @echo make cleanos2 ......... Remove object files under OS/2 linux : mkdir -p obj make -C cmnllib -f Makefile.linux make -C jamlib -f Makefile.linux make -C oslib_linux make -f Makefile.linux win32 : make -C cmnllib -f Makefile.win32 make -C jamlib -f Makefile.win32 make -C oslib_win32 make -f Makefile.win32 os2 : make -C cmnllib -f Makefile.os2 make -C jamlib -f Makefile.os2 make -C oslib_os2 make -f Makefile.os2 cleanlinux : make -C cmnllib -f Makefile.linux clean make -C jamlib -f Makefile.linux clean make -C oslib_linux clean make -f Makefile.linux clean cleanwin32 : make -C cmnllib -f Makefile.win32 clean make -C jamlib -f Makefile.win32 clean make -C oslib_win32 clean make -f Makefile.win32 clean cleanos2 : make -C cmnllib -f Makefile.os2 clean make -C jamlib -f Makefile.os2 clean make -C oslib_os2 clean make -f Makefile.os2 clean crashmail-1.5/src/Makefile.linux000066400000000000000000000137711230617144500167230ustar00rootroot00000000000000# Makefile for Linux # General PLATFORMDEF = -DPLATFORM_LINUX EXESUFFIX = BINDIR = ../bin OBJDIR = obj INCDIR = ./ OSLIB = oslib_linux/oslib.a JAMLIB = jamlib/jamlib.a CMNLLIB = cmnllib/cmnllib.a # Nodelists NLDEFS = -DNODELIST_CMNL -DNODELIST_V7P NLOBJS = obj/nl_v7p.o obj/nl_cmnl.o $(CMNLLIB) # Messagebases # *.msg MBDEFS_MSG = -DMSGBASE_MSG MBOBJS_MSG = obj/mb_msg.o # JAM MBDEFS_JAM = -DMSGBASE_JAM MBOBJS_JAM = $(OBJDIR)/mb_jam.o $(JAMLIB) # Sum them up. Only include the messagebases you want to use MBDEFS = $(MBDEFS_MSG) $(MBDEFS_JAM) MBOBJS = $(MBOBJS_MSG) $(MBOBJS_JAM) DEFS = $(PLATFORMDEF) $(MBDEFS) $(NLDEFS) # Commands CC = gcc $(CPPFLAGS) $(CFLAGS) $(DEFS) $(LDFLAGS) -I $(INCDIR) -Wall RM = rm -f STRIP = strip # Objects SHOBJS = $(OBJDIR)/jblist.o \ $(OBJDIR)/jbstrcpy.o \ $(OBJDIR)/mystrncpy.o \ $(OBJDIR)/parseargs.o \ $(OBJDIR)/node4d.o \ $(OBJDIR)/expr.o \ $(OBJDIR)/path.o CMOBJS = $(OBJDIR)/crashmail.o \ $(OBJDIR)/logwrite.o \ $(OBJDIR)/dupe.o \ $(OBJDIR)/stats.o \ $(OBJDIR)/misc.o \ $(OBJDIR)/safedel.o \ $(OBJDIR)/toss.o \ $(OBJDIR)/pkt.o \ $(OBJDIR)/mb.o \ $(OBJDIR)/nl.o \ $(OBJDIR)/handle.o \ $(OBJDIR)/node4dpat.o \ $(OBJDIR)/config.o \ $(OBJDIR)/memmessage.o \ $(OBJDIR)/scan.o \ $(OBJDIR)/outbound.o \ $(OBJDIR)/filter.o \ $(OBJDIR)/areafix.o files : $(BINDIR)/crashmail$(EXESUFFIX) \ $(BINDIR)/crashstats$(EXESUFFIX) \ $(BINDIR)/crashlist$(EXESUFFIX) \ $(BINDIR)/crashgetnode$(EXESUFFIX) \ $(BINDIR)/crashmaint$(EXESUFFIX) \ $(BINDIR)/crashwrite$(EXESUFFIX) \ $(BINDIR)/crashexport$(EXESUFFIX) \ $(BINDIR)/crashlistout$(EXESUFFIX) $(BINDIR)/crashmail$(EXESUFFIX) : $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashmail$(EXESUFFIX) $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashmail$(EXESUFFIX) $(BINDIR)/crashstats$(EXESUFFIX) : tools/crashstats.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashstats$(EXESUFFIX) tools/crashstats.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashstats$(EXESUFFIX) $(BINDIR)/crashlist$(EXESUFFIX) : tools/crashlist.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashlist$(EXESUFFIX) tools/crashlist.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashlist$(EXESUFFIX) $(BINDIR)/crashgetnode$(EXESUFFIX) : tools/crashgetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) $(CC) -o $(BINDIR)/crashgetnode$(EXESUFFIX) tools/crashgetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) $(STRIP) $(BINDIR)/crashgetnode$(EXESUFFIX) $(BINDIR)/crashmaint$(EXESUFFIX) : tools/crashmaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) $(CC) -o $(BINDIR)/crashmaint$(EXESUFFIX) tools/crashmaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) $(STRIP) $(BINDIR)/crashmaint$(EXESUFFIX) $(BINDIR)/crashwrite$(EXESUFFIX) : tools/crashwrite.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashwrite$(EXESUFFIX) tools/crashwrite.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashwrite$(EXESUFFIX) $(BINDIR)/crashexport$(EXESUFFIX) : tools/crashexport.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashexport$(EXESUFFIX) tools/crashexport.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashexport$(EXESUFFIX) $(BINDIR)/crashlistout$(EXESUFFIX) : tools/crashlistout.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashlistout$(EXESUFFIX) tools/crashlistout.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashlistout$(EXESUFFIX) # crashmail $(OBJDIR)/crashmail.o : crashmail/crashmail.c $(CC) -c crashmail/crashmail.c -o $(OBJDIR)/crashmail.o $(OBJDIR)/logwrite.o : crashmail/logwrite.c $(CC) -c crashmail/logwrite.c -o $(OBJDIR)/logwrite.o $(OBJDIR)/dupe.o : crashmail/dupe.c $(CC) -c crashmail/dupe.c -o $(OBJDIR)/dupe.o $(OBJDIR)/stats.o : crashmail/stats.c $(CC) -c crashmail/stats.c -o $(OBJDIR)/stats.o $(OBJDIR)/misc.o : crashmail/misc.c $(CC) -c crashmail/misc.c -o $(OBJDIR)/misc.o $(OBJDIR)/safedel.o : crashmail/safedel.c $(CC) -c crashmail/safedel.c -o $(OBJDIR)/safedel.o $(OBJDIR)/toss.o : crashmail/toss.c $(CC) -c crashmail/toss.c -o $(OBJDIR)/toss.o $(OBJDIR)/scan.o : crashmail/scan.c $(CC) -c crashmail/scan.c -o $(OBJDIR)/scan.o $(OBJDIR)/pkt.o : crashmail/pkt.c $(CC) -c crashmail/pkt.c -o $(OBJDIR)/pkt.o $(OBJDIR)/memmessage.o : crashmail/memmessage.c $(CC) -c crashmail/memmessage.c -o $(OBJDIR)/memmessage.o $(OBJDIR)/handle.o : crashmail/handle.c $(CC) -c crashmail/handle.c -o $(OBJDIR)/handle.o $(OBJDIR)/node4dpat.o : crashmail/node4dpat.c $(CC) -c crashmail/node4dpat.c -o $(OBJDIR)/node4dpat.o $(OBJDIR)/config.o : crashmail/config.c $(CC) -c crashmail/config.c -o $(OBJDIR)/config.o $(OBJDIR)/outbound.o : crashmail/outbound.c $(CC) -c crashmail/outbound.c -o $(OBJDIR)/outbound.o $(OBJDIR)/areafix.o : crashmail/areafix.c $(CC) -c crashmail/areafix.c -o $(OBJDIR)/areafix.o $(OBJDIR)/filter.o : crashmail/filter.c $(CC) -c crashmail/filter.c -o $(OBJDIR)/filter.o # shared $(OBJDIR)/jblist.o : shared/jblist.c $(CC) -c shared/jblist.c -o $(OBJDIR)/jblist.o $(OBJDIR)/jbstrcpy.o : shared/jbstrcpy.c $(CC) -c shared/jbstrcpy.c -o $(OBJDIR)/jbstrcpy.o $(OBJDIR)/mystrncpy.o : shared/mystrncpy.c $(CC) -c shared/mystrncpy.c -o $(OBJDIR)/mystrncpy.o $(OBJDIR)/parseargs.o : shared/parseargs.c $(CC) -c shared/parseargs.c -o $(OBJDIR)/parseargs.o $(OBJDIR)/path.o : shared/path.c $(CC) -c shared/path.c -o $(OBJDIR)/path.o $(OBJDIR)/node4d.o : shared/node4d.c $(CC) -c shared/node4d.c -o $(OBJDIR)/node4d.o $(OBJDIR)/expr.o : shared/expr.c $(CC) -c shared/expr.c -o $(OBJDIR)/expr.o # mb $(OBJDIR)/mb.o : crashmail/mb.c $(CC) -c crashmail/mb.c -o $(OBJDIR)/mb.o $(OBJDIR)/mb_msg.o : crashmail/mb_msg.c $(CC) -c crashmail/mb_msg.c -o $(OBJDIR)/mb_msg.o $(OBJDIR)/mb_jam.o : crashmail/mb_jam.c $(CC) -c crashmail/mb_jam.c -o $(OBJDIR)/mb_jam.o # nl $(OBJDIR)/nl.o : crashmail/nl.c $(CC) -c crashmail/nl.c -o $(OBJDIR)/nl.o $(OBJDIR)/nl_cmnl.o : crashmail/nl_cmnl.c $(CC) -c crashmail/nl_cmnl.c -o $(OBJDIR)/nl_cmnl.o $(OBJDIR)/nl_v7p.o : crashmail/nl_v7p.c $(CC) -c crashmail/nl_v7p.c -o $(OBJDIR)/nl_v7p.o # Clean clean : $(RM) $(OBJDIR)/*.o crashmail-1.5/src/Makefile.os2000066400000000000000000000137311230617144500162630ustar00rootroot00000000000000# Makefile for Linux # General PLATFORMDEF = -DPLATFORM_OS2 EXESUFFIX = .exe BINDIR = ../bin OBJDIR = obj INCDIR = ./ OSLIB = oslib_os2/oslib.a JAMLIB = jamlib/jamlib.a CMNLLIB = cmnllib/cmnllib.a # Nodelists NLDEFS = -DNODELIST_CMNL -DNODELIST_V7P NLOBJS = obj/nl_v7p.o obj/nl_cmnl.o $(CMNLLIB) # Messagebases # *.msg MBDEFS_MSG = -DMSGBASE_MSG MBOBJS_MSG = obj/mb_msg.o # JAM MBDEFS_JAM = -DMSGBASE_JAM MBOBJS_JAM = $(OBJDIR)/mb_jam.o $(JAMLIB) # Sum them up. Only include the messagebases you want to use MBDEFS = $(MBDEFS_MSG) $(MBDEFS_JAM) MBOBJS = $(MBOBJS_MSG) $(MBOBJS_JAM) DEFS = $(PLATFORMDEF) $(MBDEFS) $(NLDEFS) # Commands CC = gcc $(DEFS) -I $(INCDIR) -Wall RM = del STRIP = emxbind -s # Objects SHOBJS = $(OBJDIR)/jblist.o \ $(OBJDIR)/jbstrcpy.o \ $(OBJDIR)/mystrncpy.o \ $(OBJDIR)/parseargs.o \ $(OBJDIR)/node4d.o \ $(OBJDIR)/expr.o \ $(OBJDIR)/path.o CMOBJS = $(OBJDIR)/crashmail.o \ $(OBJDIR)/logwrite.o \ $(OBJDIR)/dupe.o \ $(OBJDIR)/stats.o \ $(OBJDIR)/misc.o \ $(OBJDIR)/safedel.o \ $(OBJDIR)/toss.o \ $(OBJDIR)/pkt.o \ $(OBJDIR)/mb.o \ $(OBJDIR)/nl.o \ $(OBJDIR)/handle.o \ $(OBJDIR)/node4dpat.o \ $(OBJDIR)/config.o \ $(OBJDIR)/memmessage.o \ $(OBJDIR)/scan.o \ $(OBJDIR)/outbound.o \ $(OBJDIR)/filter.o \ $(OBJDIR)/areafix.o files : $(BINDIR)/crashmail$(EXESUFFIX) \ $(BINDIR)/crashstats$(EXESUFFIX) \ $(BINDIR)/crashlist$(EXESUFFIX) \ $(BINDIR)/crashgetnode$(EXESUFFIX) \ $(BINDIR)/crashmaint$(EXESUFFIX) \ $(BINDIR)/crashwrite$(EXESUFFIX) \ $(BINDIR)/crashexport$(EXESUFFIX) \ $(BINDIR)/crashlistout$(EXESUFFIX) $(BINDIR)/crashmail$(EXESUFFIX) : $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashmail$(EXESUFFIX) $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashmail$(EXESUFFIX) $(BINDIR)/crashstats$(EXESUFFIX) : tools/crashstats.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashstats$(EXESUFFIX) tools/crashstats.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashstats$(EXESUFFIX) $(BINDIR)/crashlist$(EXESUFFIX) : tools/crashlist.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashlist$(EXESUFFIX) tools/crashlist.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashlist$(EXESUFFIX) $(BINDIR)/crashgetnode$(EXESUFFIX) : tools/crashgetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) $(CC) -o $(BINDIR)/crashgetnode$(EXESUFFIX) tools/crashgetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) $(STRIP) $(BINDIR)/crashgetnode$(EXESUFFIX) $(BINDIR)/crashmaint$(EXESUFFIX) : tools/crashmaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) $(CC) -o $(BINDIR)/crashmaint$(EXESUFFIX) tools/crashmaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) $(STRIP) $(BINDIR)/crashmaint$(EXESUFFIX) $(BINDIR)/crashwrite$(EXESUFFIX) : tools/crashwrite.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashwrite$(EXESUFFIX) tools/crashwrite.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashwrite$(EXESUFFIX) $(BINDIR)/crashexport$(EXESUFFIX) : tools/crashexport.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashexport$(EXESUFFIX) tools/crashexport.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashexport$(EXESUFFIX) $(BINDIR)/crashlistout$(EXESUFFIX) : tools/crashlistout.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashlistout$(EXESUFFIX) tools/crashlistout.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashlistout$(EXESUFFIX) # crashmail $(OBJDIR)/crashmail.o : crashmail/crashmail.c $(CC) -c crashmail/crashmail.c -o $(OBJDIR)/crashmail.o $(OBJDIR)/logwrite.o : crashmail/logwrite.c $(CC) -c crashmail/logwrite.c -o $(OBJDIR)/logwrite.o $(OBJDIR)/dupe.o : crashmail/dupe.c $(CC) -c crashmail/dupe.c -o $(OBJDIR)/dupe.o $(OBJDIR)/stats.o : crashmail/stats.c $(CC) -c crashmail/stats.c -o $(OBJDIR)/stats.o $(OBJDIR)/misc.o : crashmail/misc.c $(CC) -c crashmail/misc.c -o $(OBJDIR)/misc.o $(OBJDIR)/safedel.o : crashmail/safedel.c $(CC) -c crashmail/safedel.c -o $(OBJDIR)/safedel.o $(OBJDIR)/toss.o : crashmail/toss.c $(CC) -c crashmail/toss.c -o $(OBJDIR)/toss.o $(OBJDIR)/scan.o : crashmail/scan.c $(CC) -c crashmail/scan.c -o $(OBJDIR)/scan.o $(OBJDIR)/pkt.o : crashmail/pkt.c $(CC) -c crashmail/pkt.c -o $(OBJDIR)/pkt.o $(OBJDIR)/memmessage.o : crashmail/memmessage.c $(CC) -c crashmail/memmessage.c -o $(OBJDIR)/memmessage.o $(OBJDIR)/handle.o : crashmail/handle.c $(CC) -c crashmail/handle.c -o $(OBJDIR)/handle.o $(OBJDIR)/node4dpat.o : crashmail/node4dpat.c $(CC) -c crashmail/node4dpat.c -o $(OBJDIR)/node4dpat.o $(OBJDIR)/config.o : crashmail/config.c $(CC) -c crashmail/config.c -o $(OBJDIR)/config.o $(OBJDIR)/outbound.o : crashmail/outbound.c $(CC) -c crashmail/outbound.c -o $(OBJDIR)/outbound.o $(OBJDIR)/areafix.o : crashmail/areafix.c $(CC) -c crashmail/areafix.c -o $(OBJDIR)/areafix.o $(OBJDIR)/filter.o : crashmail/filter.c $(CC) -c crashmail/filter.c -o $(OBJDIR)/filter.o # shared $(OBJDIR)/jblist.o : shared/jblist.c $(CC) -c shared/jblist.c -o $(OBJDIR)/jblist.o $(OBJDIR)/jbstrcpy.o : shared/jbstrcpy.c $(CC) -c shared/jbstrcpy.c -o $(OBJDIR)/jbstrcpy.o $(OBJDIR)/mystrncpy.o : shared/mystrncpy.c $(CC) -c shared/mystrncpy.c -o $(OBJDIR)/mystrncpy.o $(OBJDIR)/parseargs.o : shared/parseargs.c $(CC) -c shared/parseargs.c -o $(OBJDIR)/parseargs.o $(OBJDIR)/path.o : shared/path.c $(CC) -c shared/path.c -o $(OBJDIR)/path.o $(OBJDIR)/node4d.o : shared/node4d.c $(CC) -c shared/node4d.c -o $(OBJDIR)/node4d.o $(OBJDIR)/expr.o : shared/expr.c $(CC) -c shared/expr.c -o $(OBJDIR)/expr.o # mb $(OBJDIR)/mb.o : crashmail/mb.c $(CC) -c crashmail/mb.c -o $(OBJDIR)/mb.o $(OBJDIR)/mb_msg.o : crashmail/mb_msg.c $(CC) -c crashmail/mb_msg.c -o $(OBJDIR)/mb_msg.o $(OBJDIR)/mb_jam.o : crashmail/mb_jam.c $(CC) -c crashmail/mb_jam.c -o $(OBJDIR)/mb_jam.o # nl $(OBJDIR)/nl.o : crashmail/nl.c $(CC) -c crashmail/nl.c -o $(OBJDIR)/nl.o $(OBJDIR)/nl_cmnl.o : crashmail/nl_cmnl.c $(CC) -c crashmail/nl_cmnl.c -o $(OBJDIR)/nl_cmnl.o $(OBJDIR)/nl_v7p.o : crashmail/nl_v7p.c $(CC) -c crashmail/nl_v7p.c -o $(OBJDIR)/nl_v7p.o # Clean clean : $(RM) $(OBJDIR)/*.o crashmail-1.5/src/Makefile.win32000066400000000000000000000136751230617144500165310ustar00rootroot00000000000000# Makefile for Linux # General PLATFORMDEF = -DPLATFORM_WIN32 EXESUFFIX = .exe BINDIR = ../bin OBJDIR = obj INCDIR = ./ OSLIB = oslib_win32/oslib.a JAMLIB = jamlib/jamlib.a CMNLLIB = cmnllib/cmnllib.a # Nodelists NLDEFS = -DNODELIST_CMNL -DNODELIST_V7P NLOBJS = obj/nl_v7p.o obj/nl_cmnl.o $(CMNLLIB) # Messagebases # *.msg MBDEFS_MSG = -DMSGBASE_MSG MBOBJS_MSG = obj/mb_msg.o # JAM MBDEFS_JAM = -DMSGBASE_JAM MBOBJS_JAM = $(OBJDIR)/mb_jam.o $(JAMLIB) # Sum them up. Only include the messagebases you want to use MBDEFS = $(MBDEFS_MSG) $(MBDEFS_JAM) MBOBJS = $(MBOBJS_MSG) $(MBOBJS_JAM) DEFS = $(PLATFORMDEF) $(MBDEFS) $(NLDEFS) # Commands CC = gcc $(DEFS) -I $(INCDIR) -Wall RM = del STRIP = strip # Objects SHOBJS = $(OBJDIR)/jblist.o \ $(OBJDIR)/jbstrcpy.o \ $(OBJDIR)/mystrncpy.o \ $(OBJDIR)/parseargs.o \ $(OBJDIR)/node4d.o \ $(OBJDIR)/expr.o \ $(OBJDIR)/path.o CMOBJS = $(OBJDIR)/crashmail.o \ $(OBJDIR)/logwrite.o \ $(OBJDIR)/dupe.o \ $(OBJDIR)/stats.o \ $(OBJDIR)/misc.o \ $(OBJDIR)/safedel.o \ $(OBJDIR)/toss.o \ $(OBJDIR)/pkt.o \ $(OBJDIR)/mb.o \ $(OBJDIR)/nl.o \ $(OBJDIR)/handle.o \ $(OBJDIR)/node4dpat.o \ $(OBJDIR)/config.o \ $(OBJDIR)/memmessage.o \ $(OBJDIR)/scan.o \ $(OBJDIR)/outbound.o \ $(OBJDIR)/filter.o \ $(OBJDIR)/areafix.o files : $(BINDIR)/crashmail$(EXESUFFIX) \ $(BINDIR)/crashstats$(EXESUFFIX) \ $(BINDIR)/crashlist$(EXESUFFIX) \ $(BINDIR)/crashgetnode$(EXESUFFIX) \ $(BINDIR)/crashmaint$(EXESUFFIX) \ $(BINDIR)/crashwrite$(EXESUFFIX) \ $(BINDIR)/crashexport$(EXESUFFIX) \ $(BINDIR)/crashlistout$(EXESUFFIX) $(BINDIR)/crashmail$(EXESUFFIX) : $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashmail$(EXESUFFIX) $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashmail$(EXESUFFIX) $(BINDIR)/crashstats$(EXESUFFIX) : tools/crashstats.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashstats$(EXESUFFIX) tools/crashstats.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashstats$(EXESUFFIX) $(BINDIR)/crashlist$(EXESUFFIX) : tools/crashlist.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashlist$(EXESUFFIX) tools/crashlist.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashlist$(EXESUFFIX) $(BINDIR)/crashgetnode$(EXESUFFIX) : tools/crashgetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) $(CC) -o $(BINDIR)/crashgetnode$(EXESUFFIX) tools/crashgetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) $(STRIP) $(BINDIR)/crashgetnode$(EXESUFFIX) $(BINDIR)/crashmaint$(EXESUFFIX) : tools/crashmaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) $(CC) -o $(BINDIR)/crashmaint$(EXESUFFIX) tools/crashmaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) $(STRIP) $(BINDIR)/crashmaint$(EXESUFFIX) $(BINDIR)/crashwrite$(EXESUFFIX) : tools/crashwrite.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashwrite$(EXESUFFIX) tools/crashwrite.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashwrite$(EXESUFFIX) $(BINDIR)/crashexport$(EXESUFFIX) : tools/crashexport.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashexport$(EXESUFFIX) tools/crashexport.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashexport$(EXESUFFIX) $(BINDIR)/crashlistout$(EXESUFFIX) : tools/crashlistout.c $(SHOBJS) $(OSLIB) $(CC) -o $(BINDIR)/crashlistout$(EXESUFFIX) tools/crashlistout.c $(SHOBJS) $(OSLIB) $(STRIP) $(BINDIR)/crashlistout$(EXESUFFIX) # crashmail $(OBJDIR)/crashmail.o : crashmail/crashmail.c $(CC) -c crashmail/crashmail.c -o $(OBJDIR)/crashmail.o $(OBJDIR)/logwrite.o : crashmail/logwrite.c $(CC) -c crashmail/logwrite.c -o $(OBJDIR)/logwrite.o $(OBJDIR)/dupe.o : crashmail/dupe.c $(CC) -c crashmail/dupe.c -o $(OBJDIR)/dupe.o $(OBJDIR)/stats.o : crashmail/stats.c $(CC) -c crashmail/stats.c -o $(OBJDIR)/stats.o $(OBJDIR)/misc.o : crashmail/misc.c $(CC) -c crashmail/misc.c -o $(OBJDIR)/misc.o $(OBJDIR)/safedel.o : crashmail/safedel.c $(CC) -c crashmail/safedel.c -o $(OBJDIR)/safedel.o $(OBJDIR)/toss.o : crashmail/toss.c $(CC) -c crashmail/toss.c -o $(OBJDIR)/toss.o $(OBJDIR)/scan.o : crashmail/scan.c $(CC) -c crashmail/scan.c -o $(OBJDIR)/scan.o $(OBJDIR)/pkt.o : crashmail/pkt.c $(CC) -c crashmail/pkt.c -o $(OBJDIR)/pkt.o $(OBJDIR)/memmessage.o : crashmail/memmessage.c $(CC) -c crashmail/memmessage.c -o $(OBJDIR)/memmessage.o $(OBJDIR)/handle.o : crashmail/handle.c $(CC) -c crashmail/handle.c -o $(OBJDIR)/handle.o $(OBJDIR)/node4dpat.o : crashmail/node4dpat.c $(CC) -c crashmail/node4dpat.c -o $(OBJDIR)/node4dpat.o $(OBJDIR)/config.o : crashmail/config.c $(CC) -c crashmail/config.c -o $(OBJDIR)/config.o $(OBJDIR)/outbound.o : crashmail/outbound.c $(CC) -c crashmail/outbound.c -o $(OBJDIR)/outbound.o $(OBJDIR)/filter.o : crashmail/filter.c $(CC) -c crashmail/filter.c -o $(OBJDIR)/filter.o $(OBJDIR)/areafix.o : crashmail/areafix.c $(CC) -c crashmail/areafix.c -o $(OBJDIR)/areafix.o # shared $(OBJDIR)/jblist.o : shared/jblist.c $(CC) -c shared/jblist.c -o $(OBJDIR)/jblist.o $(OBJDIR)/jbstrcpy.o : shared/jbstrcpy.c $(CC) -c shared/jbstrcpy.c -o $(OBJDIR)/jbstrcpy.o $(OBJDIR)/mystrncpy.o : shared/mystrncpy.c $(CC) -c shared/mystrncpy.c -o $(OBJDIR)/mystrncpy.o $(OBJDIR)/parseargs.o : shared/parseargs.c $(CC) -c shared/parseargs.c -o $(OBJDIR)/parseargs.o $(OBJDIR)/path.o : shared/path.c $(CC) -c shared/path.c -o $(OBJDIR)/path.o $(OBJDIR)/node4d.o : shared/node4d.c $(CC) -c shared/node4d.c -o $(OBJDIR)/node4d.o $(OBJDIR)/expr.o : shared/expr.c $(CC) -c shared/expr.c -o $(OBJDIR)/expr.o # mb $(OBJDIR)/mb.o : crashmail/mb.c $(CC) -c crashmail/mb.c -o $(OBJDIR)/mb.o $(OBJDIR)/mb_msg.o : crashmail/mb_msg.c $(CC) -c crashmail/mb_msg.c -o $(OBJDIR)/mb_msg.o $(OBJDIR)/mb_jam.o : crashmail/mb_jam.c $(CC) -c crashmail/mb_jam.c -o $(OBJDIR)/mb_jam.o # nl $(OBJDIR)/nl.o : crashmail/nl.c $(CC) -c crashmail/nl.c -o $(OBJDIR)/nl.o $(OBJDIR)/nl_cmnl.o : crashmail/nl_cmnl.c $(CC) -c crashmail/nl_cmnl.c -o $(OBJDIR)/nl_cmnl.o $(OBJDIR)/nl_v7p.o : crashmail/nl_v7p.c $(CC) -c crashmail/nl_v7p.c -o $(OBJDIR)/nl_v7p.o # Clean clean : $(RM) $(OBJDIR)/*.o crashmail-1.5/src/cmnllib/000077500000000000000000000000001230617144500155345ustar00rootroot00000000000000crashmail-1.5/src/cmnllib/Makefile.linux000066400000000000000000000004111230617144500203260ustar00rootroot00000000000000INCDIR = ../ CC = gcc $(CPPFLAGS) $(CFLAGS) -DPLATFORM_LINUX $(LDFLAGS) -I $(INCDIR) -Wall AR = ar -ru RM = rm -f OBJS = cmnllib.o cmnllib.a : $(OBJS) $(AR) cmnllib.a $(OBJS) cmnllib.o: cmnllib.c $(CC) -c cmnllib.c -o cmnllib.o clean : $(RM) *.o *.a crashmail-1.5/src/cmnllib/Makefile.os2000066400000000000000000000003431230617144500176760ustar00rootroot00000000000000INCDIR = ../ CC = gcc -DPLATFORM_OS2 -I $(INCDIR) -Wall AR = ar -ru RM = del OBJS = cmnllib.o cmnllib.a: $(OBJS) $(AR) cmnllib.a $(OBJS) cmnllib.o: cmnllib.c $(CC) -c cmnllib.c -o cmnllib.o clean : $(RM) *.o *.a crashmail-1.5/src/cmnllib/Makefile.win32000066400000000000000000000003461230617144500201400ustar00rootroot00000000000000INCDIR = ../ CC = gcc -DPLATFORM_WIN32 -I $(INCDIR) -Wall AR = ar -ru RM = del OBJS = cmnllib.o cmnllib.a : $(OBJS) $(AR) cmnllib.a $(OBJS) cmnllib.o: cmnllib.c $(CC) -c cmnllib.c -o cmnllib.o clean : $(RM) *.o *.a crashmail-1.5/src/cmnllib/cmnllib.c000066400000000000000000000050321230617144500173200ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "cmnllib.h" struct idx { uint16_t zone,net,node,point,region,hub; uint32_t offset; }; uint32_t cmnlerr; char *cmnlerrstr[] = { "", "Failed to open nodelist index", "Unknown format of nodelist index", "Unexpected end of file", "Node not found", "Failed to open nodelist" }; uint16_t cmnlgetuword(uint8_t *buf,uint32_t offset) { return (uint16_t)(buf[offset]+256*buf[offset+1]); } uint32_t cmnlgetlong(uint8_t *buf,uint32_t offset) { return (long) buf[offset]+ buf[offset+1]*256+ buf[offset+2]*256*256+ buf[offset+3]*256*256*256; } osFile cmnlOpenNL(char *dir) { osFile fh; char buf[200]; MakeFullPath(dir,"cmnodelist.index",buf,200); if(!(fh=osOpen(buf,MODE_OLDFILE))) { cmnlerr=CMNLERR_NO_INDEX; return(NULL); } osRead(fh,buf,4); buf[4]=0; if(strcmp(buf,"CNL1")!=0) { osClose(fh); cmnlerr=CMNLERR_WRONG_TYPE; return(NULL); } return(fh); } void cmnlCloseNL(osFile nl) { osClose(nl); } bool cmnlFindNL(osFile nl,char *dir,struct cmnlIdx *cmnlidx,char *line,uint32_t len) { char buf[200]; char nlname[100]; struct idx idx; bool found; osFile fh; uint8_t binbuf[16]; osSeek(nl,4,OFFSET_BEGINNING); found=FALSE; while(!found) { if(osRead(nl,nlname,100)!=100) { cmnlerr=CMNLERR_NODE_NOT_FOUND; return(FALSE); } idx.offset=0; while(!found && idx.offset != 0xffffffff) { if(osRead(nl,binbuf,sizeof(binbuf)) != sizeof(binbuf)) { cmnlerr=CMNLERR_UNEXPECTED_EOF; return(FALSE); } idx.zone=cmnlgetuword(binbuf,0); idx.net=cmnlgetuword(binbuf,2); idx.node=cmnlgetuword(binbuf,4); idx.point=cmnlgetuword(binbuf,6); idx.region=cmnlgetuword(binbuf,8); idx.hub=cmnlgetuword(binbuf,10); idx.offset=cmnlgetlong(binbuf,12); found=TRUE; if(cmnlidx->zone != idx.zone) found=FALSE; if(cmnlidx->net != idx.net) found=FALSE; if(cmnlidx->node != idx.node) found=FALSE; if(cmnlidx->point != idx.point) found=FALSE; } } cmnlidx->region=idx.region; cmnlidx->hub=idx.hub; if(!line) { return(TRUE); } MakeFullPath(dir,nlname,buf,200); if(!(fh=osOpen(buf,MODE_OLDFILE))) { cmnlerr=CMNLERR_NO_NODELIST; return(FALSE); } osSeek(fh,idx.offset,OFFSET_BEGINNING); osFGets(fh,line,len); osClose(fh); return(TRUE); } char *cmnlLastError(void) { return cmnlerrstr[cmnlerr]; } crashmail-1.5/src/cmnllib/cmnllib.h000066400000000000000000000010011230617144500173150ustar00rootroot00000000000000#include "shared/types.h" struct cmnlIdx { uint16_t zone,net,node,point,region,hub; }; #define CMNLERR_NO_INDEX 1 #define CMNLERR_WRONG_TYPE 2 #define CMNLERR_UNEXPECTED_EOF 3 #define CMNLERR_NODE_NOT_FOUND 4 #define CMNLERR_NO_NODELIST 5 osFile cmnlOpenNL(char *dir); void cmnlCloseNL(osFile nl); bool cmnlFindNL(osFile nl,char *dir,struct cmnlIdx *idx,char *line,uint32_t len); char *cmnlLastError(void); extern uint32_t cmnlerr; crashmail-1.5/src/crashmail/000077500000000000000000000000001230617144500160575ustar00rootroot00000000000000crashmail-1.5/src/crashmail/areafix.c000066400000000000000000001363101230617144500176460ustar00rootroot00000000000000#include "crashmail.h" static size_t ptrsize = sizeof(void *); void SendRemoteAreafix(void); struct Arealist *FindForward(char *tagname,char *flags); void RemoteAreafixAdd(char *area,struct ConfigNode *node); void RemoteAreafixRemove(char *area,struct ConfigNode *node); bool CheckFlags(char group,char *node); struct afReply { struct MemMessage *mm; char subject[72]; uint32_t lines; uint32_t part; }; struct afReply *afInitReply(char *fromname,struct Node4D *from4d,char *toname,struct Node4D *to4d,char *subject); void afFreeReply(struct afReply *af); void afAddLine(struct afReply *af,char *fmt,...); void afSendMessage(struct afReply *af); void AddCommandReply(struct afReply *af,char *cmd,char *reply); void rawSendList(short type,struct Node4D *from4d,char *toname,struct ConfigNode *cnode); void rawSendHelp(struct Node4D *from4d,char *toname,struct ConfigNode *cnode); void rawSendInfo(struct Node4D *from4d,char *toname,struct ConfigNode *cnode); void SendRemoveMessages(struct Area *area); void RemoveDeletedAreas(void); #define COMMAND_UPDATE 1 #define COMMAND_ADD 2 #define COMMAND_REMOVE 3 bool AreaFix(struct MemMessage *mm) { struct Arealist *arealist; struct ConfigNode *cnode; struct Area *area; struct TossNode *temptnode; struct BannedNode *bannednode; struct TextChunk *chunk; uint32_t c,d,q,jbcpos; char password[100],buf[100],buf2[100]; bool stop,sendareaquery,sendarealist,sendareaunlinked,sendhelp,sendinfo,done,iswild; bool globalrescan,wasfeed; char *opt,areaname[100],command; struct Route *tmproute; struct afReply *afr; Print4D(&mm->OrigNode,buf); LogWrite(4,AREAFIX,"AreaFix: Request from %s",buf); /* Init reply structure */ for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&mm->OrigNode)==0) break; if(!tmproute) { Print4D(&mm->OrigNode,buf); LogWrite(1,TOSSINGERR,"No route found for %s",buf); return(TRUE); } if(!(afr=afInitReply(config.cfg_Sysop,&tmproute->Aka->Node,mm->From,&mm->OrigNode,"AreaFix response"))) return(FALSE); for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,&mm->OrigNode)==0) break; if(!cnode) { Print4D(&mm->OrigNode,buf); LogWrite(2,AREAFIX,"AreaFix: Unknown node %s",buf); afAddLine(afr,"Sorry, your node is not configured here."); afSendMessage(afr); afFreeReply(afr); return(TRUE); } jbcpos=0; jbstrcpy(password,mm->Subject,100,&jbcpos); if(stricmp(password,cnode->AreafixPW)!=0) { Print4D(&mm->OrigNode,buf); LogWrite(2,AREAFIX,"AreaFix: Wrong password \"%s\" from %s",password,buf); afAddLine(afr,"Sorry, wrong password."); afSendMessage(afr); afFreeReply(afr); return(TRUE); } sendarealist=FALSE; sendareaquery=FALSE; sendareaunlinked=FALSE; sendinfo=FALSE; sendhelp=FALSE; globalrescan=FALSE; done=FALSE; if(jbstrcpy(password,mm->Subject,100,&jbcpos)) { if(stricmp(password,"-q")==0 || stricmp(password,"-l")==0) { done=TRUE; sendarealist=TRUE; AddCommandReply(afr,password,"Sending list of all areas"); } } stop=FALSE; for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk && !stop && !ctrlc;chunk=chunk->Next) { for(c=0;cLength && !stop && !ctrlc;c++) { for(d=0;d<100 && chunk->Data[c+d]!=13 && chunk->Data[c+d]!=10 && c+dLength;d++) buf[d]=chunk->Data[c+d]; buf[d]=0; c+=d; if(strncmp(buf,"---",3)==0) stop=TRUE; stripleadtrail(buf); if(buf[0]=='%') { jbcpos=0; jbstrcpy(buf2,buf,100,&jbcpos); if(stricmp(buf2,"%PAUSE")==0) { if(cnode->Flags & NODE_PASSIVE) { AddCommandReply(afr,buf,"Your system is already passive"); } else { cnode->Flags|=NODE_PASSIVE; cnode->changed=TRUE; config.changed=TRUE; done=TRUE; AddCommandReply(afr,buf,"Your system is marked as passive"); AddCommandReply(afr,"","Send %%RESUME to get echomail again"); } } else if(stricmp(buf2,"%RESUME")==0) { if(cnode->Flags & NODE_PASSIVE) { cnode->Flags&=~NODE_PASSIVE; cnode->changed=TRUE; config.changed=TRUE; done=TRUE; AddCommandReply(afr,buf,"Your system is active again"); } else { AddCommandReply(afr,buf,"Your system is not paused"); } } else if(stricmp(buf2,"%PWD")==0) { if(jbstrcpy(buf2,buf,40,&jbcpos)) { strcpy(cnode->AreafixPW,buf2); cnode->changed=TRUE; config.changed=TRUE; done=TRUE; AddCommandReply(afr,buf,"AreaFix password changed"); } else { AddCommandReply(afr,buf,"No new password specified"); } } else if(stricmp(buf2,"%RESCAN")==0) { if(config.cfg_Flags & CFG_ALLOWRESCAN) { AddCommandReply(afr,buf,"Will rescan all areas added after this line"); globalrescan=TRUE; } else { AddCommandReply(afr,buf,"No rescanning allowed"); } } else if(stricmp(buf2,"%COMPRESS")==0) { if(jbstrcpy(buf2,buf,40,&jbcpos)) { bool gotpacker; struct Packer *tmppacker; gotpacker=FALSE; tmppacker=NULL; if(buf2[0]!='?') { if(stricmp(buf2,"NONE")==0) { tmppacker=NULL; gotpacker=TRUE; } else { for(tmppacker=(struct Packer *)config.PackerList.First;tmppacker;tmppacker=tmppacker->Next) if(stricmp(buf2,tmppacker->Name)==0 && tmppacker->Packer[0]) break; if(tmppacker) gotpacker=TRUE; else AddCommandReply(afr,buf,"Unknown packer. Choose from this list:"); } } else { AddCommandReply(afr,buf,"Sending list of packers:"); } if(gotpacker) { cnode->Packer=tmppacker; cnode->changed=TRUE; config.changed=TRUE; AddCommandReply(afr,buf,"Packed changed"); } else { for(tmppacker=(struct Packer *)config.PackerList.First;tmppacker;tmppacker=tmppacker->Next) { if(tmppacker->Packer[0]) AddCommandReply(afr,"",tmppacker->Name); } AddCommandReply(afr,"","NONE"); } done=TRUE; } else { AddCommandReply(afr,buf,"No new method specified"); } } else if(stricmp(buf2,"%LIST")==0) { sendarealist=TRUE; done=TRUE; AddCommandReply(afr,buf,"Sending list of all areas"); } else if(stricmp(buf2,"%QUERY")==0) { sendareaquery=TRUE; done=TRUE; AddCommandReply(afr,buf,"Sending query"); } else if(stricmp(buf2,"%UNLINKED")==0) { sendareaunlinked=TRUE; done=TRUE; AddCommandReply(afr,buf,"Sending list of all unlinkedareas"); } else if(stricmp(buf2,"%HELP")==0) { sendhelp=TRUE; done=TRUE; AddCommandReply(afr,buf,"Sending help file"); } else if(stricmp(buf2,"%INFO")==0) { sendinfo=TRUE; done=TRUE; AddCommandReply(afr,buf,"Sending configuration info"); } else { done=TRUE; AddCommandReply(afr,buf,"Unknown command"); } } else if(buf[0]!=1 && buf[0]!=0) { uint32_t rescannum; bool patterndone,dorescan,areaexists,success; struct Area *rescanarea; done=TRUE; rescannum=0; dorescan=FALSE; /* Separate command, name and opt */ mystrncpy(areaname,buf,100); opt=""; for(q=0;areaname[q];q++) if(areaname[q]==',') opt=&areaname[q]; if(opt[0]==',') { opt[0]=0; opt=&opt[1]; while(opt[0]==32) opt=&opt[1]; } striptrail(areaname); striptrail(opt); if(areaname[0]=='-') { command=COMMAND_REMOVE; strcpy(areaname,&areaname[1]); } else if(areaname[0]=='=') { command=COMMAND_UPDATE; strcpy(areaname,&areaname[1]); } else { command=COMMAND_ADD; if(areaname[0]=='+') strcpy(areaname,&areaname[1]); } if(!osCheckPattern(areaname)) { afAddLine(afr,"%-30.30s Invalid pattern",buf); } else { iswild=osIsPattern(areaname); if(iswild) { afAddLine(afr,"%s",buf); afAddLine(afr,""); } patterndone=FALSE; areaexists=FALSE; rescanarea=NULL; for(area=(struct Area *)config.AreaList.First;area && !ctrlc;area=area->Next) if(area->AreaType == AREATYPE_ECHOMAIL) { if(osMatchPattern(areaname,area->Tagname)) { areaexists=TRUE; for(temptnode=(struct TossNode *)area->TossNodes.First;temptnode;temptnode=temptnode->Next) if(temptnode->ConfigNode == cnode) break; switch(command) { case COMMAND_ADD: if(!temptnode) { /* Do we have access? */ if(CheckFlags(area->Group,cnode->Groups) || CheckFlags(area->Group,cnode->ReadOnlyGroups)) { patterndone=TRUE; for(bannednode=(struct BannedNode *)area->BannedNodes.First;bannednode;bannednode=bannednode->Next) if(bannednode->ConfigNode == cnode) break; /* Are we banned? */ if(bannednode) { if(iswild) afAddLine(afr," You have been banned from %s",area->Tagname); else AddCommandReply(afr,buf,"You have been banned from that area"); LogWrite(3,AREAFIX,"AreaFix: This node is banned in %s",area->Tagname); } else { if((area->Flags & AREA_DEFREADONLY) || CheckFlags(area->Group,cnode->ReadOnlyGroups)) { LogWrite(4,AREAFIX,"AreaFix: Attached to %s as read-only",area->Tagname); if(iswild) afAddLine(afr," Attached to %s as read-only",area->Tagname); else AddCommandReply(afr,buf,"Attached as read-only"); } else { LogWrite(4,AREAFIX,"AreaFix: Attached to %s",area->Tagname); if(iswild) afAddLine(afr," Attached to %s",area->Tagname); else AddCommandReply(afr,buf,"Attached"); } if(!(temptnode=osAllocCleared(sizeof(struct TossNode)))) { afFreeReply(afr); nomem=TRUE; return(FALSE); } temptnode->ConfigNode=cnode; if((area->Flags & AREA_DEFREADONLY) || CheckFlags(area->Group,cnode->ReadOnlyGroups)) temptnode->Flags=TOSSNODE_READONLY; jbAddNode(&area->TossNodes,(struct jbNode *)temptnode); rescanarea=area; area->changed=TRUE; config.changed=TRUE; } } else if(!iswild) { AddCommandReply(afr,buf,"You don't have access to that area"); } } else { patterndone=TRUE; if(iswild) afAddLine(afr," You are already attached to %s",area->Tagname); else AddCommandReply(afr,buf,"You are already attached to that area"); } break; case COMMAND_REMOVE: if(!temptnode) { if(!iswild) { AddCommandReply(afr,buf,"You are not attached to that area"); patterndone=TRUE; } } else { patterndone=TRUE; if((area->Flags & AREA_MANDATORY) && !(temptnode->Flags & TOSSNODE_FEED)) { if(iswild) afAddLine(afr," You are not allowed to detach from %s",area->Tagname); else AddCommandReply(afr,buf,"You are not allowed to detach from that area"); } else { LogWrite(4,AREAFIX,"AreaFix: Detached from %s",area->Tagname); if(iswild) afAddLine(afr," Detached from %s",area->Tagname); else AddCommandReply(afr,buf,"Detached"); wasfeed=FALSE; if(temptnode->Flags & TOSSNODE_FEED) wasfeed=TRUE; jbFreeNode(&area->TossNodes,(struct jbNode *)temptnode); area->changed=TRUE; config.changed=TRUE; if(wasfeed && (config.cfg_Flags & CFG_REMOVEWHENFEED)) { LogWrite(2,AREAFIX,"AreaFix: Feed disconnected, removing area %s",area->Tagname); SendRemoveMessages(area); area->AreaType=AREATYPE_DELETED; } else if(config.cfg_Flags & CFG_AREAFIXREMOVE) { if(area->TossNodes.First == NULL || (((struct TossNode*)area->TossNodes.First)->Next==NULL && ((struct TossNode*)area->TossNodes.First)->Flags & TOSSNODE_FEED)) { if(!area->Messagebase) { if(area->TossNodes.First) { LogWrite(3,AREAFIX,"AreaFix: Area %s removed, message sent to areafix",area->Tagname); RemoteAreafixRemove(area->Tagname,((struct TossNode*)area->TossNodes.First)->ConfigNode); } else { LogWrite(3,AREAFIX,"AreaFix: Area %s removed",area->Tagname); } area->AreaType=AREATYPE_DELETED; } } } } } break; case COMMAND_UPDATE: if(temptnode) { patterndone=TRUE; if(iswild) { afAddLine(afr," Nothing to do with %s",area->Tagname); } else { AddCommandReply(afr,buf,"Will rescan area"); rescanarea=area; } } break; } } } if(command==COMMAND_UPDATE || command==COMMAND_ADD) { if(!iswild && patterndone && rescanarea) { if(strnicmp(opt,"r=",2)==0) { rescannum=atoi(&opt[2]); dorescan=TRUE; } else if(opt[0]) { afAddLine(afr,"%-30.30s Unknown option %s","",opt); } if(globalrescan || dorescan) { if(config.cfg_Flags & CFG_ALLOWRESCAN) { if(!rescanarea->Messagebase) { afAddLine(afr,"%-30.30s Can't rescan, area is pass-through",""); } else if(!rescanarea->Messagebase->rescanfunc) { afAddLine(afr,"%-30.30s Can't rescan, messagebase does not support rescan",""); } else { LogWrite(4,AREAFIX,"AreaFix: Rescanning %s",rescanarea->Tagname); RescanNode=cnode; rescan_total=0; success=(*rescanarea->Messagebase->rescanfunc)(rescanarea,rescannum,HandleRescan); RescanNode=NULL; if(!success) { afFreeReply(afr); return(FALSE); } LogWrite(4,AREAFIX,"AreaFix: Rescanned %u messages",rescan_total); afAddLine(afr,"%-30.30s Rescanned %u messages","",rescan_total); } } else { afAddLine(afr,"%-30.30s No rescanning allowed",""); } } } } switch(command) { case COMMAND_ADD: if(!patterndone) { if(iswild) { afAddLine(afr," There were no matching areas to connect to"); } else { if(!areaexists) { if(cnode->Flags & NODE_FORWARDREQ) { arealist=FindForward(areaname,cnode->Groups); if(arealist) { char buf2[100]; LogWrite(3,AREAFIX,"AreaFix: %s requested from %u:%u/%u.%u",areaname,arealist->Node->Node.Zone,arealist->Node->Node.Net,arealist->Node->Node.Node,arealist->Node->Node.Point); sprintf(buf2,"Request sent to %u:%u/%u.%u",arealist->Node->Node.Zone,arealist->Node->Node.Net,arealist->Node->Node.Node,arealist->Node->Node.Point); AddCommandReply(afr,buf,buf2); RemoteAreafixAdd(areaname,arealist->Node); area=AddArea(areaname,&arealist->Node->Node,&tmproute->Aka->Node,TRUE,config.cfg_Flags & CFG_FORWARDPASSTHRU); area->Group=arealist->Group; if(area) { uint16_t flags; flags=0; if(CheckFlags(area->Group,cnode->ReadOnlyGroups)) flags|=TOSSNODE_READONLY; AddTossNode(area,cnode,flags); config.changed=TRUE; area->changed=TRUE; areaexists=TRUE; } } } } if(!areaexists) { AddCommandReply(afr,buf,"Unknown area"); LogWrite(3,AREAFIX,"AreaFix: Unknown area %s",areaname); } } } break; case COMMAND_REMOVE: if(!patterndone) { if(iswild) afAddLine(afr," There were no matching areas to detach from"); else AddCommandReply(afr,buf,"Unknown area"); } break; case COMMAND_UPDATE: if(!patterndone) { if(iswild) afAddLine(afr," There were no matching areas"); else AddCommandReply(afr,buf,"You are not attached to this area"); } else { if(rescanarea && !globalrescan && opt[0]==0) AddCommandReply(afr,buf,"Nothing to do"); } break; } if(iswild) afAddLine(afr,""); } } RemoveDeletedAreas(); } } if(done==FALSE) afAddLine(afr,"Nothing to do."); if(nomem || ioerror || ctrlc) { afFreeReply(afr); return(FALSE); } afSendMessage(afr); afFreeReply(afr); if(sendarealist) rawSendList(SENDLIST_FULL,&tmproute->Aka->Node,mm->From,cnode); if(sendareaquery) rawSendList(SENDLIST_QUERY,&tmproute->Aka->Node,mm->From,cnode); if(sendareaunlinked) rawSendList(SENDLIST_UNLINKED,&tmproute->Aka->Node,mm->From,cnode); if(sendhelp) rawSendHelp(&tmproute->Aka->Node,mm->From,cnode); if(sendinfo) rawSendInfo(&tmproute->Aka->Node,mm->From,cnode); /* Restore old MemMessage */ SendRemoteAreafix(); return(TRUE); } void SendRemoveMessages(struct Area *area) { struct TossNode *tn; char buf[100]; struct MemMessage *mm; for(tn=(struct TossNode *)area->TossNodes.First;tn;tn=tn->Next) { if(tn->ConfigNode->Flags & NODE_SENDAREAFIX) { LogWrite(5,AREAFIX,"AreaFix: Sending message to AreaFix at %d:%d/%d.%d", tn->ConfigNode->Node.Zone, tn->ConfigNode->Node.Net, tn->ConfigNode->Node.Node, tn->ConfigNode->Node.Point); if(!(mm=mmAlloc())) { nomem=TRUE; return; } Copy4D(&mm->DestNode,&tn->ConfigNode->Node); Copy4D(&mm->OrigNode,&area->Aka->Node); strcpy(mm->From,config.cfg_Sysop); strcpy(mm->To,tn->ConfigNode->RemoteAFName); strcpy(mm->Subject,tn->ConfigNode->RemoteAFPw); mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL),mm->DateTime); mm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(mm); if(config.cfg_Flags & CFG_ADDTID) AddTID(mm); sprintf(buf,"-%s\x0d",area->Tagname); mmAddLine(mm,buf); sprintf(buf,"---\x0dGenerated by CrashMail "VERSION"\x0d"); mmAddLine(mm,buf); HandleMessage(mm); mmFree(mm); } if((tn->ConfigNode->Flags & NODE_SENDTEXT) && !(tn->Flags & TOSSNODE_FEED)) { LogWrite(5,AREAFIX,"AreaFix: Notifying sysop at %d:%d/%d.%d", tn->ConfigNode->Node.Zone, tn->ConfigNode->Node.Net, tn->ConfigNode->Node.Node, tn->ConfigNode->Node.Point); if(!(mm=mmAlloc())) return; Copy4D(&mm->DestNode,&tn->ConfigNode->Node); Copy4D(&mm->OrigNode,&area->Aka->Node); strcpy(mm->From,config.cfg_Sysop); strcpy(mm->To,tn->ConfigNode->SysopName); strcpy(mm->Subject,"Area removed"); mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL),mm->DateTime); mm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(mm); if(config.cfg_Flags & CFG_ADDTID) AddTID(mm); sprintf(buf,"The area \"%s\" has been removed by the uplink.\x0d",area->Tagname); mmAddLine(mm,buf); if(tn->ConfigNode->Flags & NODE_SENDAREAFIX) { sprintf(buf,"A message has been sent to your AreaFix.\x0d"); mmAddLine(mm,buf); } HandleMessage(mm); mmFree(mm); } } } void RemoveDeletedAreas(void) { struct Area *area,*temparea; area=(struct Area *)config.AreaList.First; while(area) { temparea=area->Next; if(area->AreaType == AREATYPE_DELETED) { jbFreeList(&area->TossNodes); jbFreeNode(&config.AreaList,(struct jbNode *)area); config.changed=TRUE; } area=temparea; } } struct StatsNode { struct StatsNode *Next; char *Tagname; char *Desc; char Group; bool Attached; bool Feed; uint32_t WeekKB; /* -1 means unknown */ }; struct jbList SortList; bool AddSortList(char *tagname,char *desc,char group,bool attached,bool feed,long weekkb) { char *mtagname,*mdesc; struct StatsNode *ss; mtagname=(char *)osAlloc(strlen(tagname)+1); mdesc=(char *)osAlloc(strlen(desc)+1); ss=(struct StatsNode *)osAlloc(sizeof(struct StatsNode)); if(!mtagname || !mdesc || !ss) { if(mtagname) osFree(mtagname); if(mdesc) osFree(mdesc); if(ss) osFree(ss); return(FALSE); } strcpy(mtagname,tagname); strcpy(mdesc,desc); ss->Tagname=mtagname; ss->Desc=mdesc; ss->Group=group; ss->Attached=attached; ss->Feed=feed; ss->WeekKB=weekkb; jbAddNode(&SortList,(struct jbNode *)ss); return(TRUE); } void FreeSortList(void) { struct StatsNode *ss; for(ss=(struct StatsNode *)SortList.First;ss;ss=ss->Next) { if(ss->Tagname) osFree(ss->Tagname); if(ss->Desc) osFree(ss->Desc); } jbFreeList(&SortList); } int CompareAreas(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; if((*s1)->Group < (*s2)->Group) return(-1); if((*s1)->Group > (*s2)->Group) return(1); return(stricmp((*s1)->Tagname,(*s2)->Tagname)); } void SortSortList(void) { uint32_t nc; struct StatsNode *ss,**buf,**work; nc=0; for(ss=(struct StatsNode *)SortList.First;ss;ss=ss->Next) nc++; if(nc==0) return; if(!(buf=(struct StatsNode **)osAlloc(nc*sizeof(struct StatsNode *)))) { nomem=TRUE; return; } work=buf; for(ss=(struct StatsNode *)SortList.First;ss;ss=ss->Next) *work++=ss; qsort(buf,nc,ptrsize,CompareAreas); jbNewList(&SortList); for(work=buf;nc--;) jbAddNode(&SortList,(struct jbNode *)*work++); osFree(buf); } long CalculateWeekKB(struct Area *area) { if(area->FirstTime == 0 || DayStatsWritten == 0) { return(-1); } else { int days,c; unsigned long sum; days=DayStatsWritten-area->FirstTime / (24*60*60); if(days > 7) days=7; sum=0; for(c=1;cLast8Days[c]; if(sum == 0 && area->Texts!=0) { days=DayStatsWritten-area->FirstTime / (24*60*60); if(days==0) days=1; return(area->Texts/days); } else { if(days == 0) days=1; return(sum/days); } } } bool AddForwardList(struct Arealist *arealist) { bool res; osFile fh; char buf[200]; char desc[100]; uint32_t c,d; struct Area *area; struct StatsNode *ss; if(!(fh=osOpen(arealist->AreaFile,MODE_OLDFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"AreaFix: File %s not found",arealist->AreaFile); LogWrite(1,SYSTEMERR,"AreaFix: Error: %s",osErrorMsg(err)); return(TRUE); } while(osFGets(fh,buf,199)) { desc[0]=0; for(c=0;buf[c]>32;c++); if(buf[c]!=0) { buf[c]=0; c++; while(buf[c]<=32 && buf[c]!=0) c++; if(buf[c]!=0) { d=0; while(buf[c]!=0 && buf[c]!=10 && buf[c]!=13 && d<77) desc[d++]=buf[c++]; desc[d]=0; } } if(buf[0]!=0) { /* Don't add areas that exist locally */ for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(stricmp(buf,area->Tagname)==0) break; for(ss=(struct StatsNode *)SortList.First;ss;ss=ss->Next) if(stricmp(buf,ss->Tagname)==0) break; if(!area && !ss) { if(arealist->Flags & AREALIST_DESC) res=AddSortList(buf,desc,arealist->Group,FALSE,FALSE,-1); else res=AddSortList(buf,"",arealist->Group,FALSE,FALSE,-1); if(!res) { osClose(fh); return(FALSE); } } } } osClose(fh); return(TRUE); } void AddCommandReply(struct afReply *afr,char *cmd,char *reply) { if(strlen(cmd) <= 30) { afAddLine(afr,"%-30s %s",cmd,reply); } else { afAddLine(afr,"%s",cmd); afAddLine(afr,"%-30s %s","",reply); } } void rawSendList(short type,struct Node4D *from4d,char *toname,struct ConfigNode *cnode) { char buf[50]; struct TossNode *tn; struct Area *area; struct StatsNode *ss,*lastss; struct Arealist *arealist; short sendlisttotal,sendlistlinked; char ast; struct afReply *afr; /* Log action */ switch(type) { case SENDLIST_QUERY: LogWrite(4,AREAFIX,"AreaFix: Sending query to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; case SENDLIST_UNLINKED: LogWrite(4,AREAFIX,"AreaFix: Sending list of unlinked areas to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; case SENDLIST_FULL: LogWrite(4,AREAFIX,"AreaFix: Sending list of areas to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; } /* Start building reply message */ if(!(afr=afInitReply(config.cfg_Sysop,from4d,toname,&cnode->Node,"AreaFix list of areas"))) return; switch(type) { case SENDLIST_QUERY: afAddLine(afr,"This is a list of all connected areas at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; case SENDLIST_FULL: afAddLine(afr,"This is a list of all available areas at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; case SENDLIST_UNLINKED: afAddLine(afr,"This is a list of all unlinked areas at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; } afAddLine(afr,""); /* Init list */ jbNewList(&SortList); /* Add local areas */ for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(area->AreaType == AREATYPE_ECHOMAIL) { short add; bool attached,feed; for(tn=(struct TossNode *)area->TossNodes.First;tn;tn=tn->Next) if(tn->ConfigNode == cnode) break; add=FALSE; switch(type) { case SENDLIST_QUERY: if(tn) add=TRUE; break; case SENDLIST_UNLINKED: if(!tn && (CheckFlags(area->Group,cnode->Groups) || CheckFlags(area->Group,cnode->ReadOnlyGroups))) add=TRUE; break; case SENDLIST_FULL: if(tn || (CheckFlags(area->Group,cnode->Groups) || CheckFlags(area->Group,cnode->ReadOnlyGroups))) add=TRUE; break; } if(add) { attached=FALSE; feed=FALSE; if(tn) attached=TRUE; if(tn && tn->Flags & TOSSNODE_FEED) feed=TRUE; if(!AddSortList(area->Tagname,area->Description,area->Group,attached,feed,CalculateWeekKB(area))) { LogWrite(1,SYSTEMERR,"AreaFix: Out of memory when building list of areas"); afAddLine(afr,"Failed to build list of areas, out of memory"); afSendMessage(afr); afFreeReply(afr); FreeSortList(); return; } } } /* Add forward-requestable areas */ if(config.cfg_Flags & CFG_INCLUDEFORWARD && (type == SENDLIST_UNLINKED || type == SENDLIST_FULL) && cnode->Flags & NODE_FORWARDREQ) { for(arealist=(struct Arealist *)config.ArealistList.First;arealist;arealist=arealist->Next) if((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group,cnode->Groups)) { if(!AddForwardList(arealist)) { LogWrite(1,SYSTEMERR,"AreaFix: Out of memory when building list of areas"); afAddLine(afr,"Failed to build list of areas, out of memory"); afSendMessage(afr); afFreeReply(afr); FreeSortList(); return; } } } /* Generate list */ SortSortList(); lastss=NULL; sendlisttotal=0; sendlistlinked=0; for(ss=(struct StatsNode *)SortList.First;ss;ss=ss->Next) { if(!lastss || lastss->Group!=ss->Group) { if(lastss) afAddLine(afr,""); if(ss->Group) afAddLine(afr," Group: %s",config.cfg_GroupNames[ss->Group-'A']); else afAddLine(afr," Group: %s",""); afAddLine(afr,""); afAddLine(afr," Tagname Description KB/week"); afAddLine(afr," ---------------------------- --------------------------------- -------"); } ast=' '; if(type == SENDLIST_FULL && ss->Attached) { ast='*'; sendlistlinked++; } if(ss->Feed) ast='%'; sendlisttotal++; if(ss->WeekKB == -1) strcpy(buf,"?"); else sprintf(buf,"%d",ss->WeekKB); if(strlen(ss->Tagname)<=28) { afAddLine(afr,"%lc%-28.28s %-33.33s %8.8s",ast,ss->Tagname,ss->Desc,buf); } else { afAddLine(afr,"%lc%-70.70s",ast,ss->Tagname); afAddLine(afr,"%lc%-28.28s %-33.33s %8.8s",' ',"",ss->Desc,buf); } lastss=ss; } switch(type) { case SENDLIST_QUERY: afAddLine(afr,"\x0d%u linked areas.",sendlisttotal); afAddLine(afr,"A '%%' means that you are the feed for the area."); break; case SENDLIST_UNLINKED: afAddLine(afr,"\x0d%u unlinked areas.",sendlisttotal); break; case SENDLIST_FULL: afAddLine(afr,"\x0dTotally %u areas, you are connected to %u of them.",sendlisttotal,sendlistlinked); afAddLine(afr,"A '*' means that you are connected to the area."); afAddLine(afr,"A '%%' means that you are the feed for the area."); break; } afSendMessage(afr); afFreeReply(afr); FreeSortList(); } void rawSendHelp(struct Node4D *from4d,char *toname,struct ConfigNode *cnode) { char helpbuf[100]; osFile fh; struct afReply *afr; LogWrite(4,AREAFIX,"AreaFix: Sending help file to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); if(!(afr=afInitReply(config.cfg_Sysop,from4d,toname,&cnode->Node,"AreaFix help"))) return; if(!(fh=osOpen(config.cfg_AreaFixHelp,MODE_OLDFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"AreaFix: Unable to open %s",config.cfg_AreaFixHelp); LogWrite(1,SYSTEMERR,"AreaFix: Error: %s",osErrorMsg(err)); afAddLine(afr,"*** Error *** : Couldn't open help file"); } else { while(osFGets(fh,helpbuf,100) && !nomem) { if(helpbuf[0]!=0) helpbuf[strlen(helpbuf)-1]=0; afAddLine(afr,"%s",helpbuf); } osClose(fh); } afSendMessage(afr); afFreeReply(afr); } void rawSendInfo(struct Node4D *from4d,char *toname,struct ConfigNode *cnode) { int c; struct afReply *afr; LogWrite(4,AREAFIX,"AreaFix: Sending configuration info to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); if(!(afr=afInitReply(config.cfg_Sysop,from4d,toname,&cnode->Node,"AreaFix configuration info"))) return; afAddLine(afr,"Configuration for %u:%u/%u.%u:", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); afAddLine(afr,""); afAddLine(afr," Sysop: %s",cnode->SysopName); afAddLine(afr,"Packet password: %s",cnode->PacketPW); afAddLine(afr,"Areafix password: %s",cnode->AreafixPW); if(!cnode->Packer) { afAddLine(afr," Packer: No packer"); } else { afAddLine(afr," Packer: %s",cnode->Packer->Name); } afAddLine(afr,""); if(cnode->Flags & NODE_PASSIVE) afAddLine(afr," * You are passive and will not receive any echomail messages"); if(cnode->Flags & NODE_TINYSEENBY) afAddLine(afr," * You receive messages with tiny SEEN-BY lines"); if(cnode->Flags & NODE_NOSEENBY) afAddLine(afr," * You receive messages without SEEN-BY lines"); if(cnode->Flags & NODE_FORWARDREQ) afAddLine(afr," * You may do forward-requests"); if(cnode->Flags & NODE_NOTIFY) afAddLine(afr," * You will receive notifications"); if(cnode->Flags & NODE_PACKNETMAIL) afAddLine(afr," * Netmail to you will be packed"); if(cnode->Flags & NODE_AUTOADD) afAddLine(afr," * New areas from you will be auto-added"); afAddLine(afr,""); afAddLine(afr,"You have full access to these groups:"); afAddLine(afr,""); for(c='A';c<='Z';c++) if(CheckFlags(c,cnode->Groups) && !CheckFlags(c,cnode->ReadOnlyGroups)) { if(config.cfg_GroupNames[c-'A'][0]!=0) afAddLine(afr,"%lc: %s",c,config.cfg_GroupNames[c-'A']); else afAddLine(afr,"%lc",c); } afAddLine(afr,""); afAddLine(afr,"You have read-only access to these groups:"); afAddLine(afr,""); for(c='A';c<='Z';c++) if(CheckFlags(c,cnode->ReadOnlyGroups)) { if(config.cfg_GroupNames[c-'A']) afAddLine(afr,"%lc: %s",c,config.cfg_GroupNames[c-'A']); else afAddLine(afr,"%lc",c); } afSendMessage(afr); afFreeReply(afr); } void afRawPrepareMessage(void); struct afReply *afInitReply(char *fromname,struct Node4D *from4d,char *toname,struct Node4D *to4d,char *subject) { struct afReply *afr; if(!(afr=osAllocCleared(sizeof(struct afReply)))) { nomem=TRUE; return(NULL); } if(!(afr->mm=mmAlloc())) { nomem=TRUE; osFree(afr); return(NULL); } strcpy(afr->mm->From,fromname); Copy4D(&afr->mm->OrigNode,from4d); strcpy(afr->mm->To,toname); Copy4D(&afr->mm->DestNode,to4d); strcpy(afr->subject,subject); afr->mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL),afr->mm->DateTime); afr->mm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(afr->mm); afr->lines=0; afr->part=1; return(afr); } void afFreeReply(struct afReply *afr) { mmFree(afr->mm); } void afAddLine(struct afReply *afr,char *fmt,...) { va_list args; char buf[200]; if(afr->lines >= config.cfg_AreaFixMaxLines-2 && config.cfg_AreaFixMaxLines!=0) { strcpy(buf,"\x0d(Continued in next message)\x0d"); mmAddLine(afr->mm,buf); sprintf(afr->mm->Subject,"%s (part %d)",afr->subject,afr->part); afSendMessage(afr); jbFreeList(&afr->mm->TextChunks); MakeFidoDate(time(NULL),afr->mm->DateTime); afr->mm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(afr->mm); strcpy(buf,"(Continued from previous message)\x0d\x0d"); mmAddLine(afr->mm,buf); afr->lines=2; afr->part++; } va_start(args, fmt); vsprintf(buf,fmt,args); va_end(args); strcat(buf,"\x0d"); mmAddLine(afr->mm,buf); afr->lines++; } void afSendMessage(struct afReply *afr) { if(afr->part != 1) sprintf(afr->mm->Subject,"%s (part %d)",afr->subject,afr->part); else strcpy(afr->mm->Subject,afr->subject); HandleMessage(afr->mm); } void RemoteAreafixAdd(char *area,struct ConfigNode *node) { struct RemoteAFCommand *cmd; if(!(cmd=(struct RemoteAFCommand *)osAllocCleared(sizeof(struct RemoteAFCommand)))) { nomem=TRUE; return; } if(node->Flags & NODE_AFNEEDSPLUS) sprintf(cmd->Command,"+%.78s",area); else sprintf(cmd->Command,"%.78s",area); cmd->Command[70]=0; jbAddNode(&node->RemoteAFList,(struct jbNode *)cmd); } void RemoteAreafixRemove(char *area,struct ConfigNode *node) { struct RemoteAFCommand *cmd; if(!(cmd=(struct RemoteAFCommand *)osAllocCleared(sizeof(struct RemoteAFCommand)))) { nomem=TRUE; return; } sprintf(cmd->Command,"-%.78s",area); cmd->Command[70]=0; jbAddNode(&node->RemoteAFList,(struct jbNode *)cmd); } void SendRemoteAreafix(void) { struct Route *tmproute; struct ConfigNode *node; struct RemoteAFCommand *cmd; char buf[200]; struct MemMessage *mm; for(node=(struct ConfigNode *)config.CNodeList.First;node;node=node->Next) if(node->RemoteAFList.First) { if(!(mm=mmAlloc())) { nomem=TRUE; return; } for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&node->Node)==0) break; Copy4D(&mm->DestNode,&node->Node); Copy4D(&mm->OrigNode,&tmproute->Aka->Node); strcpy(mm->From,config.cfg_Sysop); strcpy(mm->To,node->RemoteAFName); strcpy(mm->Subject,node->RemoteAFPw); mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL),mm->DateTime); mm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(mm); if(config.cfg_Flags & CFG_ADDTID) AddTID(mm); for(cmd=(struct RemoteAFCommand *)node->RemoteAFList.First;cmd;cmd=cmd->Next) { sprintf(buf,"%s\x0d",cmd->Command); mmAddLine(mm,buf); } mmAddLine(mm,"---\x0dGenerated by CrashMail "VERSION"\x0d"); HandleMessage(mm); mmFree(mm); jbFreeList(&node->RemoteAFList); } } bool CheckFlags(char group,char *node) { uint8_t c; for(c=0;cNext) { if((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group,flags)) { if((fh=osOpen(arealist->AreaFile,MODE_OLDFILE))) { while(osFGets(fh,buf,199)) { for(c=0;buf[c]>32;c++); buf[c]=0; if(stricmp(buf,tagname)==0) { osClose(fh); return(arealist); } } osClose(fh); } else { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open file %s",arealist->AreaFile); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); } } } return(NULL); } void DoSendAFList(short type,struct ConfigNode *cnode) { struct Route *tmproute; for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&cnode->Node)==0) break; if(!tmproute) { LogWrite(1,TOSSINGERR,"No route found for %d:%d/%d.%d", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); return; } switch(type) { case SENDLIST_FULL: rawSendList(SENDLIST_FULL,&tmproute->Aka->Node,cnode->SysopName,cnode); break; case SENDLIST_QUERY: rawSendList(SENDLIST_QUERY,&tmproute->Aka->Node,cnode->SysopName,cnode); break; case SENDLIST_UNLINKED: rawSendList(SENDLIST_UNLINKED,&tmproute->Aka->Node,cnode->SysopName,cnode); break; case SENDLIST_INFO: rawSendInfo(&tmproute->Aka->Node,cnode->SysopName,cnode); break; case SENDLIST_HELP: rawSendHelp(&tmproute->Aka->Node,cnode->SysopName,cnode); break; } } crashmail-1.5/src/crashmail/areafix.h000066400000000000000000000004661230617144500176550ustar00rootroot00000000000000 #define SENDLIST_FULL 1 #define SENDLIST_QUERY 2 #define SENDLIST_UNLINKED 3 #define SENDLIST_HELP 4 #define SENDLIST_INFO 5 void DoSendAFList(short type,struct ConfigNode *cnode); bool AreaFix(struct MemMessage *mm); void SendRemoveMessages(struct Area *area); void RemoveDeletedAreas(void); crashmail-1.5/src/crashmail/config.c000066400000000000000000001712501230617144500174760ustar00rootroot00000000000000#include "crashmail.h" extern char *config_version; bool CorrectFlags(char *flags) { uint32_t c; for(c=0;c'Z') return(FALSE); } return(TRUE); } char cfgbuf[4000]; bool ReadConfig(char *filename,struct Config *cfg,short *seconderr,uint32_t *cfgline,char *cfgerr) { char buf2[200],cfgword[30]; uint32_t c,d,jbcpos; osFile cfgfh; struct Aka *tmpaka, *LastAka=NULL; struct ConfigNode *tmpnode, *LastCNode=NULL; struct Area *tmparea, *LastArea=NULL; struct Packer *tmppacker, *LastPacker=NULL; struct Route *tmproute; struct ImportNode *tmpinode; struct PatternNode *tmppatternnode; struct Change *tmpchange; struct AreaFixName *tmpareafixname; struct Arealist *tmparealist; struct Filter *tmpfilter, *LastFilter=NULL; struct Command *tmpcommand; struct AddNode *tmpaddnode; struct RemNode *tmpremnode; struct TossNode *tmptnode; struct BannedNode *tmpbnode; struct Node4D tmp4d; uint16_t flags; cfg->changed=FALSE; mystrncpy(cfg->filename,filename,100); *cfgline=0; if(!(cfgfh=osOpen(filename,MODE_OLDFILE))) { *seconderr=READCONFIG_NOT_FOUND; return(FALSE); } *seconderr=READCONFIG_INVALID; while(osFGets(cfgfh,cfgbuf,4000)) { jbcpos=0; (*cfgline)++; jbstrcpy(cfgword,cfgbuf,30,&jbcpos); if(stricmp(cfgword,"AKA")==0) { if(!(tmpaka=(struct Aka *)osAllocCleared(sizeof(struct Aka)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->AkaList,(struct jbNode *)tmpaka); LastAka=tmpaka; if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4D(buf2,&LastAka->Node))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } jbNewList(&LastAka->AddList); jbNewList(&LastAka->RemList); } else if(stricmp(cfgword,"ADDNODE")==0) { if(!LastAka) { strcpy(cfgerr,"No previous AKA line"); osClose(cfgfh); return(FALSE); } while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { if(!(tmpaddnode=(struct AddNode *)osAllocCleared(sizeof(struct AddNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastAka->AddList,(struct jbNode *)tmpaddnode); if(!(Parse4D(buf2,&tmpaddnode->Node))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"REMNODE")==0) { if(!LastAka) { strcpy(cfgerr,"No previous AKA line"); osClose(cfgfh); return(FALSE); } while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { if(!(tmpremnode=(struct RemNode *)osAllocCleared(sizeof(struct RemNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastAka->RemList,(struct jbNode *)tmpremnode); if(!Parse2DPat(buf2,&tmpremnode->NodePat)) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"DOMAIN")==0) { if(!LastAka) { strcpy(cfgerr,"No previous AKA line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastAka->Domain,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"GROUPNAME")==0) { if(!(jbstrcpy(buf2,cfgbuf,2,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!CorrectFlags(buf2)) { sprintf(cfgerr,"Invalid group \"%s\"",buf2); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(cfg->cfg_GroupNames[buf2[0]-'A'],cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"NODE")==0) { if(!(tmpnode=(struct ConfigNode *)osAllocCleared(sizeof(struct ConfigNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbNewList(&tmpnode->RemoteAFList); jbAddNode(&cfg->CNodeList,(struct jbNode *)tmpnode); LastCNode=tmpnode; if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4D(buf2,&LastCNode->Node))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,10,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } tmppacker=NULL; if(buf2[0]!=0) { for(tmppacker=(struct Packer *)cfg->PackerList.First;tmppacker;tmppacker=tmppacker->Next) if(stricmp(buf2,tmppacker->Name)==0) break; if(!tmppacker) { sprintf(cfgerr,"Unknown packer \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } LastCNode->Packer=tmppacker; if(!(jbstrcpy(LastCNode->PacketPW,cfgbuf,9,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } LastCNode->EchomailPri=PKTS_NORMAL; while(jbstrcpy(buf2,cfgbuf,200,&jbcpos)) { if(stricmp(buf2,"NOTIFY")==0) LastCNode->Flags|=NODE_NOTIFY; else if(stricmp(buf2,"PASSIVE")==0) LastCNode->Flags|=NODE_PASSIVE; else if(stricmp(buf2,"NOSEENBY")==0) LastCNode->Flags|=NODE_NOSEENBY; else if(stricmp(buf2,"TINYSEENBY")==0) LastCNode->Flags|=NODE_TINYSEENBY; else if(stricmp(buf2,"FORWARDREQ")==0) LastCNode->Flags|=NODE_FORWARDREQ; else if(stricmp(buf2,"PACKNETMAIL")==0) LastCNode->Flags|=NODE_PACKNETMAIL; else if(stricmp(buf2,"SENDAREAFIX")==0) LastCNode->Flags|=NODE_SENDAREAFIX; else if(stricmp(buf2,"SENDTEXT")==0) LastCNode->Flags|=NODE_SENDTEXT; else if(stricmp(buf2,"AUTOADD")==0) LastCNode->Flags|=NODE_AUTOADD; else if(stricmp(buf2,"CRASH")==0) LastCNode->EchomailPri=PKTS_CRASH; else if(stricmp(buf2,"DIRECT")==0) LastCNode->EchomailPri=PKTS_DIRECT; else if(stricmp(buf2,"HOLD")==0) LastCNode->EchomailPri=PKTS_HOLD; else { sprintf(cfgerr,"Unknown switch \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"PKTFROM")==0) { if(!LastCNode) { strcpy(cfgerr,"No previous NODE line"); osClose(cfgfh); return(FALSE); } LastCNode->Flags|=NODE_PKTFROM; if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4D(buf2,&LastCNode->PktFrom))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"AREAFIXINFO")==0) { if(!LastCNode) { strcpy(cfgerr,"No previous NODE line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastCNode->AreafixPW,cfgbuf,40,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(jbstrcpy(LastCNode->Groups,cfgbuf,70,&jbcpos)) { if(!CorrectFlags(LastCNode->Groups)) { sprintf(cfgerr,"Invalid groups \"%s\"",LastCNode->Groups); osClose(cfgfh); return(FALSE); } } if(jbstrcpy(LastCNode->ReadOnlyGroups,cfgbuf,70,&jbcpos)) { if(!CorrectFlags(LastCNode->ReadOnlyGroups)) { sprintf(cfgerr,"Invalid groups \"%s\"",LastCNode->ReadOnlyGroups); osClose(cfgfh); return(FALSE); } } if(jbstrcpy(LastCNode->AddGroups,cfgbuf,70,&jbcpos)) { if(!CorrectFlags(LastCNode->AddGroups)) { sprintf(cfgerr,"Invalid groups \"%s\"",LastCNode->AddGroups); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"DEFAULTGROUP")==0) { if(!LastCNode) { strcpy(cfgerr,"No previous NODE line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,2,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!CorrectFlags(buf2)) { sprintf(cfgerr,"Invalid group \"%s\"",buf2); osClose(cfgfh); return(FALSE); } LastCNode->DefaultGroup=buf2[0]; } else if(stricmp(cfgword,"AREA")==0 || stricmp(cfgword,"NETMAIL")==0 || stricmp(cfgword,"LOCALAREA")==0) { if(!(tmparea=(struct Area *)osAllocCleared(sizeof(struct Area)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbNewList(&tmparea->TossNodes); jbNewList(&tmparea->BannedNodes); jbAddNode(&cfg->AreaList,(struct jbNode *)tmparea); LastArea=tmparea; if(!(jbstrcpy(LastArea->Tagname,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4D(buf2,&tmp4d))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } for(tmpaka=(struct Aka *)cfg->AkaList.First;tmpaka;tmpaka=tmpaka->Next) if(Compare4D(&tmp4d,&tmpaka->Node)==0) break; if(!tmpaka) { sprintf(cfgerr,"Unknown AKA \"%s\"",buf2); osClose(cfgfh); return(FALSE); } LastArea->Aka=tmpaka; if(jbstrcpy(buf2,cfgbuf,200,&jbcpos)) { int c; for(c=0;AvailMessagebases[c].name;c++) if(stricmp(buf2,AvailMessagebases[c].name)==0) break; if(AvailMessagebases[c].name) { LastArea->Messagebase= &AvailMessagebases[c]; } else { sprintf(cfgerr,"Unknown messagebase format \"%s\"",buf2); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastArea->Path,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } if(stricmp(cfgword,"NETMAIL")==0) { if(!LastArea->Messagebase) { sprintf(cfgerr,"Netmail area cannot be pass-through"); osClose(cfgfh); return(FALSE); } LastArea->AreaType=AREATYPE_NETMAIL; } else if(stricmp(cfgword,"LOCALAREA")==0) { if(!LastArea->Messagebase) { sprintf(cfgerr,"Local area cannot be pass-through"); osClose(cfgfh); return(FALSE); } LastArea->AreaType=AREATYPE_LOCAL; } else if(stricmp(LastArea->Tagname,"BAD")==0) { if(LastArea->Path[0]==0) { sprintf(cfgerr,"BAD area cannot be pass-through"); osClose(cfgfh); return(FALSE); } LastArea->AreaType=AREATYPE_BAD; } else if(stricmp(LastArea->Tagname,"DEFAULT")==0 || strnicmp(LastArea->Tagname,"DEFAULT_",8)==0) { LastArea->AreaType=AREATYPE_DEFAULT; } else { LastArea->AreaType=AREATYPE_ECHOMAIL; } } else if(stricmp(cfgword,"UNCONFIRMED")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } LastArea->Flags|=AREA_UNCONFIRMED; } else if(stricmp(cfgword,"MANDATORY")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } LastArea->Flags|=AREA_MANDATORY; } else if(stricmp(cfgword,"DEFREADONLY")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } LastArea->Flags|=AREA_DEFREADONLY; } else if(stricmp(cfgword,"IGNOREDUPES")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } LastArea->Flags|=AREA_IGNOREDUPES; } else if(stricmp(cfgword,"IGNORESEENBY")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } LastArea->Flags|=AREA_IGNORESEENBY; } else if(stricmp(cfgword,"EXPORT")==0) { struct Node4D tpl4d; if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } if(LastArea->AreaType != AREATYPE_ECHOMAIL && LastArea->AreaType != AREATYPE_DEFAULT) { strcpy(cfgerr,"Not an echomail or default area"); osClose(cfgfh); return(FALSE); } tpl4d.Zone=0; tpl4d.Net=0; tpl4d.Node=0; tpl4d.Point=0; while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { flags=0; if(buf2[0]=='!') { flags=TOSSNODE_READONLY; strcpy(buf2,&buf2[1]); } if(buf2[0]=='@') { flags=TOSSNODE_WRITEONLY; strcpy(buf2,&buf2[1]); } if(buf2[0]=='%') { flags=TOSSNODE_FEED; strcpy(buf2,&buf2[1]); } if(!(Parse4DTemplate(buf2,&tmp4d,&tpl4d))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } Copy4D(&tpl4d,&tmp4d); tpl4d.Point=0; for(tmpnode=(struct ConfigNode *)cfg->CNodeList.First;tmpnode;tmpnode=tmpnode->Next) if(Compare4D(&tmp4d,&tmpnode->Node)==0) break; if(!tmpnode) { sprintf(cfgerr,"Unconfigured node \"%s\"",buf2); osClose(cfgfh); return(FALSE); } if(!(tmptnode=(struct TossNode *)osAllocCleared(sizeof(struct TossNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastArea->TossNodes,(struct jbNode *)tmptnode); tmptnode->ConfigNode=tmpnode; tmptnode->Flags=flags; } } else if(stricmp(cfgword,"IMPORT")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } if(LastArea->AreaType != AREATYPE_NETMAIL) { strcpy(cfgerr,"Not a netmail area"); osClose(cfgfh); return(FALSE); } while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { if(!(tmpinode=(struct ImportNode *)osAllocCleared(sizeof(struct ImportNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastArea->TossNodes,(struct jbNode *)tmpinode); if(!(Parse4D(buf2,&tmpinode->Node))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"BANNED")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { if(!(Parse4D(buf2,&tmp4d))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } for(tmpnode=(struct ConfigNode *)cfg->CNodeList.First;tmpnode;tmpnode=tmpnode->Next) if(Compare4D(&tmp4d,&tmpnode->Node)==0) break; if(!tmpnode) { sprintf(cfgerr,"Unconfigured node \"%s\"",buf2); osClose(cfgfh); return(FALSE); } if(!(tmpbnode=(struct BannedNode *)osAllocCleared(sizeof(struct BannedNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastArea->BannedNodes,(struct jbNode *)tmpbnode); tmpbnode->ConfigNode=tmpnode; } } else if(stricmp(cfgword,"DESCRIPTION")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastArea->Description,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"GROUP")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,30,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!CorrectFlags(buf2)) { sprintf(cfgerr,"Invalid group \"%s\"",buf2); osClose(cfgfh); return(FALSE); } LastArea->Group=buf2[0]; } else if(stricmp(cfgword,"KEEPNUM")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,30,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } LastArea->KeepNum=atol(buf2); } else if(stricmp(cfgword,"KEEPDAYS")==0) { if(!LastArea) { strcpy(cfgerr,"No previous AREA line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,30,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } LastArea->KeepDays=atol(buf2); } else if(stricmp(cfgword,"AREAFIXNAME")==0) { if(!(tmpareafixname=(struct AreaFixName *)osAllocCleared(sizeof(struct AreaFixName)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->AreaFixList,(struct jbNode *)tmpareafixname); if(!(jbstrcpy(tmpareafixname->Name,cfgbuf,36,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"FILEATTACH")==0) { while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { if(!(tmppatternnode=(struct PatternNode *)osAllocCleared(sizeof(struct PatternNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } if(!(Parse4DPat(buf2,&tmppatternnode->Pattern))) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->FileAttachList,(struct jbNode *)tmppatternnode); } } else if(stricmp(cfgword,"BOUNCE")==0) { while(jbstrcpy(buf2,cfgbuf,100,&jbcpos)) { if(!(tmppatternnode=(struct PatternNode *)osAllocCleared(sizeof(struct PatternNode)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } if(!(Parse4DPat(buf2,&tmppatternnode->Pattern))) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->BounceList,(struct jbNode *)tmppatternnode); } } else if(stricmp(cfgword,"INBOUND")==0) { if(!(jbstrcpy(cfg->cfg_Inbound,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"OUTBOUND")==0) { if(!(jbstrcpy(cfg->cfg_Outbound,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"SYSOP")==0) { if(!(jbstrcpy(cfg->cfg_Sysop,cfgbuf,35,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"BEFORETOSS")==0) { if(!(jbstrcpy(cfg->cfg_BeforeToss,cfgbuf,79,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"BEFOREPACK")==0) { if(!(jbstrcpy(cfg->cfg_BeforePack,cfgbuf,79,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"NODELIST")==0) { int i; if(!(jbstrcpy(cfg->cfg_Nodelist,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_NodelistType=NULL; if(!(jbstrcpy(buf2,cfgbuf,40,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } for(i=0;AvailNodelists[i].name;i++) if(stricmp(buf2,AvailNodelists[i].name)==0) break; if(!AvailNodelists[i].name) { sprintf(cfgerr,"Unsupported nodelist type \"%s\"",buf2); osClose(cfgfh); return(FALSE); } cfg->cfg_NodelistType= &AvailNodelists[i]; } else if(stricmp(cfgword,"AREAFIXHELP")==0) { if(!(jbstrcpy(cfg->cfg_AreaFixHelp,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"LOGFILE")==0) { if(!(jbstrcpy(cfg->cfg_LogFile,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"LOGLEVEL")==0) { if(!(jbstrcpy(buf2,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(atoi(buf2)<1 || atoi(buf2)>6) { strcpy(cfgerr,"Loglevel out of range"); osClose(cfgfh); return(FALSE); } cfg->cfg_LogLevel=atoi(buf2); } else if(stricmp(cfgword,"STATSFILE")==0) { if(!(jbstrcpy(cfg->cfg_StatsFile,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"DUPEFILE")==0) { if(!(jbstrcpy(cfg->cfg_DupeFile,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_DupeSize=atoi(buf2); } else if(stricmp(cfgword,"DUPEMODE")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(stricmp(buf2,"IGNORE")==0) cfg->cfg_DupeMode=DUPE_IGNORE; else if(stricmp(buf2,"KILL")==0) cfg->cfg_DupeMode=DUPE_KILL; else if(stricmp(buf2,"BAD")==0) cfg->cfg_DupeMode=DUPE_BAD; else { sprintf(cfgerr,"Unknown dupemode \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"LOOPMODE")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(stricmp(buf2,"IGNORE")==0) cfg->cfg_LoopMode=LOOP_IGNORE; else if(stricmp(buf2,"LOG")==0) cfg->cfg_LoopMode=LOOP_LOG; else if(stricmp(buf2,"LOG+BAD")==0) cfg->cfg_LoopMode=LOOP_LOGBAD; else { sprintf(cfgerr,"Unknown loopmode \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"TEMPDIR")==0) { if(!(jbstrcpy(cfg->cfg_TempDir,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"CREATEPKTDIR")==0) { if(!(jbstrcpy(cfg->cfg_PacketCreate,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"PACKETDIR")==0) { if(!(jbstrcpy(cfg->cfg_PacketDir,cfgbuf,100,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"PACKER")==0) { if(!(tmppacker=(struct Packer *)osAllocCleared(sizeof(struct Packer)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->PackerList,(struct jbNode *)tmppacker); LastPacker=tmppacker; if(!(jbstrcpy(LastPacker->Name,cfgbuf,10,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastPacker->Packer,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastPacker->Unpacker,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastPacker->Recog,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"ROUTE")==0) { if(!(tmproute=(struct Route *)osAllocCleared(sizeof(struct Route)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->RouteList,(struct jbNode *)tmproute); /* Pattern */ if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4DPat(buf2,&tmproute->Pattern))) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } /* Dest */ if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4DDestPat(buf2,&tmproute->DestPat))) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } /* Aka */ if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4D(buf2,&tmp4d))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } for(tmpaka=(struct Aka *)cfg->AkaList.First;tmpaka;tmpaka=tmpaka->Next) if(Compare4D(&tmp4d,&tmpaka->Node)==0) break; if(!tmpaka) { sprintf(cfgerr,"Unconfigured AKA \"%s\"",buf2); osClose(cfgfh); return(FALSE); } tmproute->Aka=tmpaka; } else if(stricmp(cfgword,"CHANGE")==0) { if(!(tmpchange=(struct Change *)osAllocCleared(sizeof(struct Change)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->ChangeList,(struct jbNode *)tmpchange); /* Type pattern */ if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(buf2[0]=='*') { tmpchange->ChangeNormal=TRUE; tmpchange->ChangeCrash=TRUE; tmpchange->ChangeHold=TRUE; tmpchange->ChangeDirect=TRUE; } else { c=0; while(buf2[c]!=0) { d=c; while(buf2[d]!=',' && buf2[d]!=0) d++; if(buf2[d]==',') { buf2[d]=0; d++; } if(stricmp(&buf2[c],"NORMAL")==0) tmpchange->ChangeNormal=TRUE; else if(stricmp(&buf2[c],"CRASH")==0) tmpchange->ChangeCrash=TRUE; else if(stricmp(&buf2[c],"HOLD")==0) tmpchange->ChangeHold=TRUE; else if(stricmp(&buf2[c],"DIRECT")==0) tmpchange->ChangeDirect=TRUE; else { sprintf(cfgerr,"Unknown mail flavour \"%s\"",buf2); osClose(cfgfh); return(FALSE); } c=d; } } /* Node pattern */ if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4DPat(buf2,&tmpchange->Pattern))) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } /* New type */ if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(stricmp(buf2,"NORMAL")==0) tmpchange->DestPri=PKTS_NORMAL; else if(stricmp(buf2,"CRASH")==0) tmpchange->DestPri=PKTS_CRASH; else if(stricmp(buf2,"HOLD")==0) tmpchange->DestPri=PKTS_HOLD; else if(stricmp(buf2,"DIRECT")==0) tmpchange->DestPri=PKTS_DIRECT; else { sprintf(cfgerr,"Unknown mail flavour \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"AREALIST")==0) { if(!(tmparealist=(struct Arealist *)osAllocCleared(sizeof(struct Arealist)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->ArealistList,(struct jbNode *)tmparealist); if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4D(buf2,&tmp4d))) { sprintf(cfgerr,"Invalid node number \"%s\"",buf2); osClose(cfgfh); return(FALSE); } for(tmpnode=(struct ConfigNode *)cfg->CNodeList.First;tmpnode;tmpnode=tmpnode->Next) if(Compare4D(&tmp4d,&tmpnode->Node)==0) break; if(!tmpnode) { sprintf(cfgerr,"Unconfigured node \"%s\"",buf2); osClose(cfgfh); return(FALSE); } tmparealist->Node=tmpnode; if(!(jbstrcpy(tmparealist->AreaFile,cfgbuf,80,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } while(jbstrcpy(buf2,cfgbuf,200,&jbcpos)) { if(stricmp(buf2,"FORWARD")==0) { tmparealist->Flags|=AREALIST_FORWARD; } else if(stricmp(buf2,"DESC")==0) { tmparealist->Flags|=AREALIST_DESC; } else if(stricmp(buf2,"GROUP")==0) { if(!jbstrcpy(buf2,cfgbuf,200,&jbcpos)) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!CorrectFlags(buf2)) { sprintf(cfgerr,"Invalid group \"%s\"",buf2); osClose(cfgfh); return(FALSE); } tmparealist->Group=buf2[0]; } else { sprintf(cfgerr,"Unknown switch \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"FILTER")==0) { if(!(tmpfilter=(struct Filter *)osAllocCleared(sizeof(struct Filter)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&cfg->FilterList,(struct jbNode *)tmpfilter); jbNewList(&tmpfilter->CommandList); LastFilter=tmpfilter; if(!(jbstrcpyrest(tmpfilter->Filter,cfgbuf,400,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"KILL")==0 || stricmp(cfgword,"TWIT")==0) { if(!LastFilter) { strcpy(cfgerr,"No previous FILTER line"); osClose(cfgfh); return(FALSE); } if(!(tmpcommand=(struct Command *)osAllocCleared(sizeof(struct Command)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastFilter->CommandList,(struct jbNode *)tmpcommand); if(stricmp(cfgword,"KILL")==0) tmpcommand->Cmd=COMMAND_KILL; if(stricmp(cfgword,"TWIT")==0) tmpcommand->Cmd=COMMAND_TWIT; } else if(stricmp(cfgword,"COPY")==0 || stricmp(cfgword,"EXECUTE")==0 || stricmp(cfgword,"WRITELOG")==0 || stricmp(cfgword,"WRITEBAD")==0 || stricmp(cfgword,"BOUNCEMSG")==0 || stricmp(cfgword,"BOUNCEHEADER")==0) { if(!LastFilter) { strcpy(cfgerr,"No previous FILTER line"); osClose(cfgfh); return(FALSE); } if(!(tmpcommand=(struct Command *)osAllocCleared(sizeof(struct Command)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastFilter->CommandList,(struct jbNode *)tmpcommand); if(stricmp(cfgword,"COPY")==0) tmpcommand->Cmd=COMMAND_COPY; if(stricmp(cfgword,"EXECUTE")==0) tmpcommand->Cmd=COMMAND_EXECUTE; if(stricmp(cfgword,"WRITELOG")==0) tmpcommand->Cmd=COMMAND_WRITELOG; if(stricmp(cfgword,"WRITEBAD")==0) tmpcommand->Cmd=COMMAND_WRITEBAD; if(stricmp(cfgword,"BOUNCEMSG")==0) tmpcommand->Cmd=COMMAND_BOUNCEMSG; if(stricmp(cfgword,"BOUNCEHEADER")==0) tmpcommand->Cmd=COMMAND_BOUNCEHEADER; if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(tmpcommand->string=(char *)osAlloc(strlen(buf2)+1))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } strcpy(tmpcommand->string,buf2); } else if(stricmp(cfgword,"REMAPMSG")==0) { if(!LastFilter) { strcpy(cfgerr,"No previous FILTER line"); osClose(cfgfh); return(FALSE); } if(!(tmpcommand=(struct Command *)osAllocCleared(sizeof(struct Command)))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } jbAddNode(&LastFilter->CommandList,(struct jbNode *)tmpcommand); tmpcommand->Cmd=COMMAND_REMAPMSG; if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(tmpcommand->string=(char *)osAlloc(strlen(buf2)+1))) { *seconderr=READCONFIG_NO_MEM; osClose(cfgfh); return(FALSE); } strcpy(tmpcommand->string,buf2); if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(Parse4DDestPat(buf2,&tmpcommand->n4ddestpat))) { sprintf(cfgerr,"Invalid node pattern \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"REMOTEAF")==0) { if(!LastCNode) { strcpy(cfgerr,"No previous NODE line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastCNode->RemoteAFName,cfgbuf,36,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastCNode->RemoteAFPw,cfgbuf,72,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } while(jbstrcpy(buf2,cfgbuf,200,&jbcpos)) { if(stricmp(buf2,"NEEDSPLUS")==0) LastCNode->Flags|=NODE_AFNEEDSPLUS; else { sprintf(cfgerr,"Unknown switch \"%s\"",buf2); osClose(cfgfh); return(FALSE); } } } else if(stricmp(cfgword,"REMOTESYSOP")==0) { if(!LastCNode) { strcpy(cfgerr,"No previous NODE line"); osClose(cfgfh); return(FALSE); } if(!(jbstrcpy(LastCNode->SysopName,cfgbuf,36,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } } else if(stricmp(cfgword,"STRIPRE")==0) { cfg->cfg_Flags|=CFG_STRIPRE; } else if(stricmp(cfgword,"FORCEINTL")==0) { cfg->cfg_Flags|=CFG_FORCEINTL; } else if(stricmp(cfgword,"BOUNCEHEADERONLY")==0) { cfg->cfg_Flags|=CFG_BOUNCEHEADERONLY; } else if(stricmp(cfgword,"REMOVEWHENFEED")==0) { cfg->cfg_Flags|=CFG_REMOVEWHENFEED; } else if(stricmp(cfgword,"INCLUDEFORWARD")==0) { cfg->cfg_Flags|=CFG_INCLUDEFORWARD; } else if(stricmp(cfgword,"NOMAXOUTBOUNDZONE")==0) { cfg->cfg_Flags|=CFG_NOMAXOUTBOUNDZONE; } else if(stricmp(cfgword,"ALLOWKILLSENT")==0) { cfg->cfg_Flags|=CFG_ALLOWKILLSENT; } else if(stricmp(cfgword,"FLOWCRLF")==0) { cfg->cfg_Flags|=CFG_FLOWCRLF; } else if(stricmp(cfgword,"NOEXPORTNETMAIL")==0) { cfg->cfg_Flags|=CFG_NOEXPORTNETMAIL; } else if(stricmp(cfgword,"NOROUTE")==0) { cfg->cfg_Flags|=CFG_NOROUTE; } else if(stricmp(cfgword,"ANSWERRECEIPT")==0) { cfg->cfg_Flags|=CFG_ANSWERRECEIPT; } else if(stricmp(cfgword,"ANSWERAUDIT")==0) { cfg->cfg_Flags|=CFG_ANSWERAUDIT; } else if(stricmp(cfgword,"CHECKSEENBY")==0) { cfg->cfg_Flags|=CFG_CHECKSEENBY; } else if(stricmp(cfgword,"CHECKPKTDEST")==0) { cfg->cfg_Flags|=CFG_CHECKPKTDEST; } else if(stricmp(cfgword,"PATH3D")==0) { cfg->cfg_Flags|=CFG_PATH3D; } else if(stricmp(cfgword,"IMPORTEMPTYNETMAIL")==0) { cfg->cfg_Flags|=CFG_IMPORTEMPTYNETMAIL; } else if(stricmp(cfgword,"IMPORTAREAFIX")==0) { cfg->cfg_Flags|=CFG_IMPORTAREAFIX; } else if(stricmp(cfgword,"AREAFIXREMOVE")==0) { cfg->cfg_Flags|=CFG_AREAFIXREMOVE; } else if(stricmp(cfgword,"NODIRECTATTACH")==0) { cfg->cfg_Flags|=CFG_NODIRECTATTACH; } else if(stricmp(cfgword,"BOUNCEPOINTS")==0) { cfg->cfg_Flags|=CFG_BOUNCEPOINTS; } else if(stricmp(cfgword,"IMPORTSEENBY")==0) { cfg->cfg_Flags|=CFG_IMPORTSEENBY; } else if(stricmp(cfgword,"WEEKDAYNAMING")==0) { cfg->cfg_Flags|=CFG_WEEKDAYNAMING; } else if(stricmp(cfgword,"ADDTID")==0) { cfg->cfg_Flags|=CFG_ADDTID; } else if(stricmp(cfgword,"ALLOWRESCAN")==0) { cfg->cfg_Flags|=CFG_ALLOWRESCAN; } else if(stricmp(cfgword,"FORWARDPASSTHRU")==0) { cfg->cfg_Flags|=CFG_FORWARDPASSTHRU; } else if(stricmp(cfgword,"MAXPKTSIZE")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_MaxPktSize=atoi(buf2)*1024; } else if(stricmp(cfgword,"MAXBUNDLESIZE")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_MaxBundleSize=atoi(buf2)*1024; } else if(stricmp(cfgword,"DEFAULTZONE")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_DefaultZone=atoi(buf2); } else if(stricmp(cfgword,"AREAFIXMAXLINES")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_AreaFixMaxLines=atoi(buf2); } /*************************** MSG ******************************/ #ifdef MSGBASE_MSG else if(stricmp(cfgword,"MSG_HIGHWATER")==0) { cfg->cfg_msg_Flags|=CFG_MSG_HIGHWATER; } else if(stricmp(cfgword,"MSG_WRITEBACK")==0) { cfg->cfg_msg_Flags|=CFG_MSG_WRITEBACK; } #endif /*************************** JAM ******************************/ #ifdef MSGBASE_JAM else if(stricmp(cfgword,"JAM_HIGHWATER")==0) { cfg->cfg_jam_Flags|=CFG_JAM_HIGHWATER; } else if(stricmp(cfgword,"JAM_LINK")==0) { cfg->cfg_jam_Flags|=CFG_JAM_LINK; } else if(stricmp(cfgword,"JAM_QUICKLINK")==0) { cfg->cfg_jam_Flags|=CFG_JAM_QUICKLINK; } else if(stricmp(cfgword,"JAM_MAXOPEN")==0) { if(!(jbstrcpy(buf2,cfgbuf,200,&jbcpos))) { strcpy(cfgerr,"Missing argument"); osClose(cfgfh); return(FALSE); } cfg->cfg_jam_MaxOpen=atoi(buf2); } #endif /*************************** Unknown *****************************/ else if(cfgword[0]!=0 && cfgword[0]!=';') { sprintf(cfgerr,"Unknown keyword \"%s\"",cfgword); osClose(cfgfh); return(FALSE); } } osClose(cfgfh); return(TRUE); } bool CheckConfig(struct Config *cfg,char *cfgerr) { struct Area *a1,*a2; struct PatternNode *patternnode; struct Route *route; struct Change *change; struct Filter *filter; struct Command *command; char buf[50]; if(cfg->cfg_TempDir[0]==0) strcpy(cfg->cfg_TempDir,cfg->cfg_Inbound); if(cfg->cfg_PacketCreate[0]==0) strcpy(cfg->cfg_PacketCreate,cfg->cfg_Outbound); if(cfg->cfg_PacketDir[0]==0) strcpy(cfg->cfg_PacketDir,cfg->cfg_Outbound); if(!cfg->AkaList.First) { sprintf(cfgerr,"No AKAs configured"); return(FALSE); } if(!cfg->cfg_NodelistType) { /* Check if any pattern needs a nodelist */ for(patternnode=(struct PatternNode *)cfg->FileAttachList.First;patternnode;patternnode=patternnode->Next) if(!Check4DPatNodelist(&patternnode->Pattern)) { Print4DPat(&patternnode->Pattern,buf); sprintf(cfgerr,"Nodelist needed for pattern \"%s\"",buf); return(FALSE); } for(patternnode=(struct PatternNode *)cfg->BounceList.First;patternnode;patternnode=patternnode->Next) if(!Check4DPatNodelist(&patternnode->Pattern)) { Print4DPat(&patternnode->Pattern,buf); sprintf(cfgerr,"Nodelist needed for pattern \"%s\"",buf); return(FALSE); } for(route=(struct Route *)cfg->RouteList.First;route;route=route->Next) { if(!Check4DPatNodelist(&route->Pattern)) { Print4DPat(&route->Pattern,buf); sprintf(cfgerr,"Nodelist needed for pattern \"%s\"",buf); return(FALSE); } if(!Check4DPatNodelist(&route->DestPat)) { Print4DDestPat(&route->DestPat,buf); sprintf(cfgerr,"Nodelist needed for pattern \"%s\"",buf); return(FALSE); } } for(change=(struct Change *)cfg->ChangeList.First;change;change=change->Next) if(!Check4DPatNodelist(&change->Pattern)) { Print4DPat(&change->Pattern,buf); sprintf(cfgerr,"Nodelist needed for pattern \"%s\"",buf); return(FALSE); } } /* Check for areas with same path */ for(a1=(struct Area *)cfg->AreaList.First;a1;a1=a1->Next) { if(a1->AreaType != AREATYPE_DEFAULT && a1->Messagebase) { for(a2=a1->Next;a2;a2=a2->Next) if(a2->Messagebase && stricmp(a1->Path,a2->Path)==0) { sprintf(cfgerr,"The areas %s and %s both have the same path",a1->Tagname,a2->Tagname); return(FALSE); } } } for(filter=(struct Filter *)cfg->FilterList.First;filter;filter=filter->Next) { if(!CheckFilter(filter->Filter,cfgerr)) return(FALSE); for(command=(struct Command *)filter->CommandList.First;command;command=command->Next) { if(command->Cmd == COMMAND_COPY) { for(a1=(struct Area *)config.AreaList.First;a1;a1=a1->Next) if(stricmp(a1->Tagname,command->string)==0) break; if(!a1) { sprintf(cfgerr,"Filter: Area %s for COPY command not found",command->string); return(FALSE); } if(a1->AreaType != AREATYPE_LOCAL) { sprintf(cfgerr,"Filter: Area %s for COPY command is not a local area",command->string); return(FALSE); } } if(command->Cmd == COMMAND_REMAPMSG) { if(!cfg->cfg_NodelistType && !Check4DPatNodelist(&command->n4ddestpat)) { Print4DDestPat(&command->n4ddestpat,buf); sprintf(cfgerr,"Filter: Nodelist needed for pattern \"%s\"",buf); return(FALSE); } } } } return(TRUE); } void InitConfig(struct Config *cfg) { memset(cfg,0,sizeof(struct Config)); strcpy(cfg->cfg_Sysop,"Sysop"); cfg->cfg_LogLevel=3; cfg->cfg_DupeSize=10000; cfg->cfg_DefaultZone=2; jbNewList(&cfg->AkaList); jbNewList(&cfg->AreaList); jbNewList(&cfg->CNodeList); jbNewList(&cfg->PackerList); jbNewList(&cfg->RouteList); jbNewList(&cfg->FileAttachList); jbNewList(&cfg->BounceList); jbNewList(&cfg->ChangeList); jbNewList(&cfg->AreaFixList); jbNewList(&cfg->ArealistList); jbNewList(&cfg->FilterList); } void WriteSafely(osFile fh,char *str) { char buf[300]; uint16_t c,d; d=0; for(c=0;str[c];c++) { if(str[c]=='"' || str[c]=='\\') buf[d++]='\\'; buf[d++]=str[c]; } buf[d]=0; osFPrintf(fh,"\"%s\"",buf); } void WriteNode4D(osFile fh,struct Node4D *n4d) { osFPrintf(fh,"%u:%u/%u.%u",n4d->Zone,n4d->Net,n4d->Node,n4d->Point); } char *nodekeywords[]={"DEFAULTGROUP","REMOTESYSOP","REMOTEAF", "AREAFIXINFO","NODE","PKTFROM",NULL}; char *areakeywords[]={"IGNORESEENBY","IGNOREDUPES","DEFREADONLY", "MANDATORY","UNCONFIRMED","KEEPNUM","KEEPDAYS", "GROUP","DESCRIPTION","BANNED","EXPORT","IMPORT", "AREA","NETMAIL",NULL}; void WriteNode(struct ConfigNode *tmpnode,osFile osfh) { osFPrintf(osfh,"NODE "); WriteNode4D(osfh,&tmpnode->Node); osFPrintf(osfh," "); if(tmpnode->Packer) WriteSafely(osfh,tmpnode->Packer->Name); else WriteSafely(osfh,""); osFPrintf(osfh," "); WriteSafely(osfh,tmpnode->PacketPW); if(tmpnode->Flags & NODE_PASSIVE) osFPrintf(osfh," PASSIVE"); if(tmpnode->Flags & NODE_NOTIFY) osFPrintf(osfh," NOTIFY"); if(tmpnode->Flags & NODE_NOSEENBY) osFPrintf(osfh," NOSEENBY"); if(tmpnode->Flags & NODE_TINYSEENBY) osFPrintf(osfh," TINYSEENBY"); if(tmpnode->Flags & NODE_FORWARDREQ) osFPrintf(osfh," FORWARDREQ"); if(tmpnode->Flags & NODE_PACKNETMAIL) osFPrintf(osfh," PACKNETMAIL"); if(tmpnode->Flags & NODE_SENDAREAFIX) osFPrintf(osfh," SENDAREAFIX"); if(tmpnode->Flags & NODE_SENDTEXT) osFPrintf(osfh," SENDTEXT"); if(tmpnode->Flags & NODE_AUTOADD) osFPrintf(osfh," AUTOADD"); if(tmpnode->EchomailPri == PKTS_CRASH) osFPrintf(osfh," CRASH"); if(tmpnode->EchomailPri == PKTS_DIRECT) osFPrintf(osfh," DIRECT"); if(tmpnode->EchomailPri == PKTS_HOLD) osFPrintf(osfh," HOLD"); osFPrintf(osfh,"\n"); if(tmpnode->Flags & NODE_PKTFROM) { osFPrintf(osfh,"PKTFROM "); WriteNode4D(osfh,&tmpnode->PktFrom); osFPrintf(osfh,"\n"); } if(tmpnode->AreafixPW[0] || tmpnode->Groups[0] || tmpnode->ReadOnlyGroups[0] || tmpnode->AddGroups[0]) { osFPrintf(osfh,"AREAFIXINFO "); WriteSafely(osfh,tmpnode->AreafixPW); osFPrintf(osfh," "); WriteSafely(osfh,tmpnode->Groups); osFPrintf(osfh," "); WriteSafely(osfh,tmpnode->ReadOnlyGroups); osFPrintf(osfh," "); WriteSafely(osfh,tmpnode->AddGroups); osFPrintf(osfh," "); osFPrintf(osfh,"\n"); } if(tmpnode->RemoteAFName[0]) { osFPrintf(osfh,"REMOTEAF "); WriteSafely(osfh,tmpnode->RemoteAFName); osFPrintf(osfh," "); WriteSafely(osfh,tmpnode->RemoteAFPw); if(tmpnode->Flags & NODE_AFNEEDSPLUS) osFPrintf(osfh," NEEDSPLUS"); osFPrintf(osfh,"\n"); } if(tmpnode->SysopName[0]) { osFPrintf(osfh,"REMOTESYSOP "); WriteSafely(osfh,tmpnode->SysopName); osFPrintf(osfh,"\n"); } if(tmpnode->DefaultGroup) osFPrintf(osfh,"DEFAULTGROUP %lc\n",tmpnode->DefaultGroup); } void WriteArea(struct Area *tmparea,osFile osfh) { struct ImportNode *tmpinode; struct TossNode *tmptnode; struct BannedNode *tmpbnode; uint32_t c; if(tmparea->AreaType == AREATYPE_NETMAIL) osFPrintf(osfh,"NETMAIL "); if(tmparea->AreaType == AREATYPE_LOCAL) osFPrintf(osfh,"LOCALAREA "); else osFPrintf(osfh,"AREA "); WriteSafely(osfh,tmparea->Tagname); osFPrintf(osfh," "); WriteNode4D(osfh,&tmparea->Aka->Node); if(tmparea->Messagebase) { osFPrintf(osfh," %s ",tmparea->Messagebase->name); WriteSafely(osfh,tmparea->Path); osFPrintf(osfh,"\n"); } else { osFPrintf(osfh,"\n"); } if(tmparea->AreaType == AREATYPE_NETMAIL) { c=0; for(tmpinode=(struct ImportNode *)tmparea->TossNodes.First;tmpinode;tmpinode=tmpinode->Next) { if(c%10==0) { if(c!=0) osFPrintf(osfh,"\n"); osFPrintf(osfh,"IMPORT"); } osFPrintf(osfh," "); WriteNode4D(osfh,&tmpinode->Node); c++; } if(c!=0) osFPrintf(osfh,"\n"); } else if(tmparea->AreaType == AREATYPE_ECHOMAIL || tmparea->AreaType == AREATYPE_DEFAULT) { c=0; for(tmptnode=(struct TossNode *)tmparea->TossNodes.First;tmptnode;tmptnode=tmptnode->Next) { if(c%10==0) { if(c!=0) osFPrintf(osfh,"\n"); osFPrintf(osfh,"EXPORT"); } osFPrintf(osfh," "); if(tmptnode->Flags & TOSSNODE_READONLY) osFPrintf(osfh,"!"); if(tmptnode->Flags & TOSSNODE_WRITEONLY) osFPrintf(osfh,"@"); if(tmptnode->Flags & TOSSNODE_FEED) osFPrintf(osfh,"%%"); WriteNode4D(osfh,&tmptnode->ConfigNode->Node); c++; } if(c!=0) osFPrintf(osfh,"\n"); } c=0; for(tmpbnode=(struct BannedNode *)tmparea->BannedNodes.First;tmpbnode;tmpbnode=tmpbnode->Next) { if(c%10==0) { if(c!=0) osFPrintf(osfh,"\n"); osFPrintf(osfh,"BANNED"); } osFPrintf(osfh," "); WriteNode4D(osfh,&tmpbnode->ConfigNode->Node); c++; } if(c!=0) osFPrintf(osfh,"\n"); if(tmparea->Description[0]!=0) { osFPrintf(osfh,"DESCRIPTION "); WriteSafely(osfh,tmparea->Description); osFPrintf(osfh,"\n"); } if(tmparea->Group) osFPrintf(osfh,"GROUP %lc\n",tmparea->Group); if(tmparea->KeepNum) osFPrintf(osfh,"KEEPNUM %u\n",tmparea->KeepNum); if(tmparea->KeepDays) osFPrintf(osfh,"KEEPDAYS %u\n",tmparea->KeepDays); if(tmparea->Flags & AREA_UNCONFIRMED) osFPrintf(osfh,"UNCONFIRMED\n"); if(tmparea->Flags & AREA_MANDATORY) osFPrintf(osfh,"MANDATORY\n"); if(tmparea->Flags & AREA_DEFREADONLY) osFPrintf(osfh,"DEFREADONLY\n"); if(tmparea->Flags & AREA_IGNOREDUPES) osFPrintf(osfh,"IGNOREDUPES\n"); if(tmparea->Flags & AREA_IGNORESEENBY) osFPrintf(osfh,"IGNORESEENBY\n"); } bool UpdateConfig(struct Config *cfg,char *cfgerr) { char cfgtemp[110],cfgbak[110]; char cfgword[30],buf[100]; osFile oldfh,newfh; bool skiparea,skipnode,dontwrite,copyres; struct ConfigNode *cnode; struct Area *area; struct Node4D n4d; uint32_t jbcpos,c; strcpy(cfgtemp,cfg->filename); strcat(cfgtemp,".tmp"); strcpy(cfgbak,cfg->filename); strcat(cfgbak,".bak"); if(!(oldfh=osOpen(cfg->filename,MODE_OLDFILE))) { uint32_t err=osError(); sprintf(cfgerr,"Unable to read file %s (error: %s)",cfg->filename,osErrorMsg(err)); return(FALSE); } if(!(newfh=osOpen(cfgtemp,MODE_NEWFILE))) { uint32_t err=osError(); sprintf(cfgerr,"Unable to write to config file %s (error: %s)",cfgtemp,osErrorMsg(err)); osClose(oldfh); return(FALSE); } skiparea=FALSE; skipnode=FALSE; while(osFGets(oldfh,cfgbuf,4000)) { jbcpos=0; copyres=jbstrcpy(cfgword,cfgbuf,30,&jbcpos); if(stricmp(cfgword,"AREA")==0 || stricmp(cfgword,"NETMAIL")==0 || stricmp(cfgword,"LOCALAREA")==0) { skiparea=FALSE; if(jbstrcpy(buf,cfgbuf,100,&jbcpos)) { for(area=(struct Area *)cfg->AreaList.First;area;area=area->Next) if(stricmp(buf,area->Tagname)==0) break; /* Area has been changed */ if(area && area->changed) { skiparea=TRUE; WriteArea(area,newfh); osFPrintf(newfh,"\n"); area->changed=FALSE; } /* Area has been removed */ if(!area) { skiparea=TRUE; } } } if(stricmp(cfgword,"NODE")==0) { skipnode=FALSE; if(jbstrcpy(buf,cfgbuf,100,&jbcpos)) { if(Parse4D(buf,&n4d)) { for(cnode=(struct ConfigNode *)cfg->CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&n4d,&cnode->Node)==0) break; if(cnode && cnode->changed) { skipnode=TRUE; WriteNode(cnode,newfh); osFPrintf(newfh,"\n"); cnode->changed=FALSE; } } } } dontwrite=FALSE; if(skiparea) { for(c=0;areakeywords[c];c++) if(stricmp(cfgword,areakeywords[c])==0) dontwrite=TRUE; if(!copyres) dontwrite=TRUE; /* Skip empty lines */ } if(skipnode) { for(c=0;nodekeywords[c];c++) if(stricmp(cfgword,nodekeywords[c])==0) dontwrite=TRUE; if(!copyres) dontwrite=TRUE; /* Skip empty lines */ } if(!dontwrite) osPuts(newfh,cfgbuf); } for(area=(struct Area *)cfg->AreaList.First;area;area=area->Next) if(area->changed) { osFPrintf(newfh,"\n"); WriteArea(area,newfh); area->changed=FALSE; } osClose(oldfh); osClose(newfh); osDelete(cfgbak); osRename(cfg->filename,cfgbak); osRename(cfgtemp,cfg->filename); cfg->changed=FALSE; return(TRUE); } void FreeConfig(struct Config *cfg) { struct Area *area; struct Aka *aka; struct ConfigNode *cnode; struct Filter *filter; struct Command *command; /* Config */ for(area=(struct Area *)cfg->AreaList.First;area;area=area->Next) { jbFreeList(&area->TossNodes); jbFreeList(&area->BannedNodes); } for(aka=(struct Aka *)cfg->AkaList.First;aka;aka=aka->Next) { jbFreeList(&aka->AddList); jbFreeList(&aka->RemList); } for(cnode=(struct ConfigNode *)cfg->CNodeList.First;cnode;cnode=cnode->Next) jbFreeList(&cnode->RemoteAFList); for(filter=(struct Filter *)cfg->FilterList.First;filter;filter=filter->Next) { for(command=(struct Command *)filter->CommandList.First;command;command=command->Next) if(command->string) osFree(command->string); jbFreeList(&filter->CommandList); } jbFreeList(&cfg->AkaList); jbFreeList(&cfg->AreaList); jbFreeList(&cfg->CNodeList); jbFreeList(&cfg->PackerList); jbFreeList(&cfg->RouteList); jbFreeList(&cfg->RouteList); jbFreeList(&cfg->FileAttachList); jbFreeList(&cfg->BounceList); jbFreeList(&cfg->ChangeList); jbFreeList(&cfg->AreaFixList); jbFreeList(&cfg->ArealistList); jbFreeList(&cfg->FilterList); } crashmail-1.5/src/crashmail/config.h000066400000000000000000000173341230617144500175050ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H #define READCONFIG_NOT_FOUND 1 #define READCONFIG_INVALID 2 #define READCONFIG_NO_MEM 3 #define CFG_CHECKSEENBY (1L<<0) #define CFG_CHECKPKTDEST (1L<<1) #define CFG_STRIPRE (1L<<2) #define CFG_FORCEINTL (1L<<3) #define CFG_NOROUTE (1L<<4) #define CFG_PATH3D (1L<<5) #define CFG_IMPORTSEENBY (1L<<6) #define CFG_IMPORTEMPTYNETMAIL (1L<<7) #define CFG_BOUNCEPOINTS (1L<<8) #define CFG_ANSWERRECEIPT (1L<<9) #define CFG_ANSWERAUDIT (1L<<10) #define CFG_NODIRECTATTACH (1L<<11) #define CFG_IMPORTAREAFIX (1L<<12) #define CFG_AREAFIXREMOVE (1L<<13) #define CFG_WEEKDAYNAMING (1L<<14) #define CFG_ADDTID (1L<<16) #define CFG_ALLOWRESCAN (1L<<18) #define CFG_FORWARDPASSTHRU (1L<<19) #define CFG_BOUNCEHEADERONLY (1L<<21) #define CFG_REMOVEWHENFEED (1L<<22) #define CFG_INCLUDEFORWARD (1L<<23) #define CFG_NOMAXOUTBOUNDZONE (1L<<24) #define CFG_ALLOWKILLSENT (1L<<25) #define CFG_FLOWCRLF (1L<<26) #define CFG_NOEXPORTNETMAIL (1L<<27) #ifdef MSGBASE_MSG #define CFG_MSG_HIGHWATER (1L<<1) #define CFG_MSG_WRITEBACK (1L<<2) #endif #ifdef MSGBASE_UMS #define CFG_UMS_MAUSGATE (1L<<0) #define CFG_UMS_KEEPORIGIN (1L<<1) #define CFG_UMS_CHANGEMSGID (1L<<2) #define CFG_UMS_IGNOREORIGINDOMAIN (1L<<3) #define CFG_UMS_EMPTYTOALL (1L<<4) #endif #ifdef MSGBASE_JAM #define CFG_JAM_HIGHWATER (1L<<0) #define CFG_JAM_LINK (1L<<1) #define CFG_JAM_QUICKLINK (1L<<2) #endif #ifdef PLATFORM_AMIGA #define CFG_AMIGA_UNATTENDED (1L<<0) #endif #define AREA_UNCONFIRMED 1 #define AREA_MANDATORY 2 #define AREA_DEFREADONLY 4 #define AREA_IGNOREDUPES 8 #define AREA_IGNORESEENBY 16 #define AREATYPE_NETMAIL 1 #define AREATYPE_ECHOMAIL 2 #define AREATYPE_DEFAULT 3 #define AREATYPE_BAD 4 #define AREATYPE_LOCAL 5 #define AREATYPE_DELETED 6 struct Area { struct Area *Next; bool changed; struct Aka *Aka; char Flags; char AreaType; char Tagname[80]; char Description[80]; struct Messagebase *Messagebase; char Path[80]; char Group; struct jbList TossNodes; struct jbList BannedNodes; /* Maint */ uint32_t KeepNum,KeepDays; /* Stats */ uint32_t Texts; uint32_t NewTexts; uint32_t Dupes; uint32_t NewDupes; uint16_t Last8Days[8]; time_t FirstTime; time_t LastTime; /* Other */ bool scanned; }; struct Aka { struct Aka *Next; struct Node4D Node; char Domain[20]; struct jbList AddList; struct jbList RemList; }; #define TOSSNODE_READONLY 1 #define TOSSNODE_WRITEONLY 2 #define TOSSNODE_FEED 4 struct TossNode { struct TossNode *Next; struct ConfigNode *ConfigNode; uint16_t Flags; }; struct ImportNode { struct ImportNode *Next; struct Node4D Node; }; struct BannedNode { struct BannedNode *Next; struct ConfigNode *ConfigNode; }; #define NODE_AUTOADD 1 #define NODE_PASSIVE 2 #define NODE_TINYSEENBY 4 #define NODE_NOSEENBY 8 #define NODE_FORWARDREQ 16 #define NODE_NOTIFY 32 #define NODE_PACKNETMAIL 64 #define NODE_SENDAREAFIX 128 #define NODE_SENDTEXT 256 #define NODE_PKTFROM 512 #define NODE_AFNEEDSPLUS 1024 struct RemoteAFCommand { struct RemoteAFCommand *Next; char Command[80]; }; struct ConfigNode { struct ConfigNode *Next; bool changed; struct Node4D Node; char PacketPW[9]; struct Packer *Packer; bool IsInSeenBy; char AreafixPW[40]; char Groups[30]; char ReadOnlyGroups[30]; char AddGroups[30]; char DefaultGroup; uint32_t Flags; char SysopName[36]; char EchomailPri; struct Node4D PktFrom; char RemoteAFName[36]; char RemoteAFPw[72]; struct jbList RemoteAFList; char LastArcName[12]; /* Stats */ uint32_t GotNetmails; uint32_t GotNetmailBytes; uint32_t SentNetmails; uint32_t SentNetmailBytes; uint32_t GotEchomails; uint32_t GotEchomailBytes; uint32_t SentEchomails; uint32_t SentEchomailBytes; uint32_t Dupes; time_t FirstTime; }; struct Packer { struct Packer *Next; char Name[10]; char Packer[80]; char Unpacker[80]; char Recog[80]; }; struct AddNode { struct AddNode *Next; struct Node4D Node; }; struct RemNode { struct RemNode *Next; struct Node2DPat NodePat; }; struct Route { struct Route *Next; struct Node4DPat Pattern; struct Node4DPat DestPat; struct Aka *Aka; }; struct PatternNode { struct PatternNode *Next; struct Node4DPat Pattern; }; #define PKTS_ECHOMAIL 0 #define PKTS_HOLD 1 #define PKTS_NORMAL 2 #define PKTS_DIRECT 3 #define PKTS_CRASH 4 struct Change { struct Change *Next; struct Node4DPat Pattern; bool ChangeNormal; bool ChangeCrash; bool ChangeDirect; bool ChangeHold; char DestPri; }; struct AreaFixName { struct AreaFixName *Next; char Name[36]; }; #define AREALIST_DESC 1 #define AREALIST_FORWARD 2 struct Arealist { struct Arealist *Next; struct ConfigNode *Node; char AreaFile[80]; uint16_t Flags; char Group; }; #define COMMAND_KILL 1 #define COMMAND_TWIT 2 #define COMMAND_COPY 3 #define COMMAND_EXECUTE 4 #define COMMAND_WRITELOG 5 #define COMMAND_WRITEBAD 6 #define COMMAND_BOUNCEMSG 7 #define COMMAND_BOUNCEHEADER 8 #define COMMAND_REMAPMSG 9 struct Command { struct Command *Next; uint16_t Cmd; char *string; struct Node4D n4d; struct Node4DPat n4ddestpat; }; struct Filter { struct Filter *Next; char Filter[400]; struct jbList CommandList; }; #define DUPE_IGNORE 0 #define DUPE_BAD 1 #define DUPE_KILL 2 #define LOOP_IGNORE 0 #define LOOP_LOG 1 #define LOOP_LOGBAD 2 struct Config { bool changed; char filename[100]; char cfg_Sysop[36]; char cfg_Inbound[100]; char cfg_Outbound[100]; char cfg_TempDir[100]; char cfg_PacketCreate[100]; char cfg_PacketDir[100]; char cfg_LogFile[100]; char cfg_StatsFile[100]; char cfg_DupeFile[100]; char cfg_BeforeToss[80]; char cfg_BeforePack[80]; uint32_t cfg_LogLevel; uint32_t cfg_DupeSize; uint32_t cfg_MaxPktSize; uint32_t cfg_MaxBundleSize; char cfg_AreaFixHelp[100]; char cfg_Nodelist[100]; struct Nodelist *cfg_NodelistType; uint32_t cfg_AreaFixMaxLines; char cfg_GroupNames[30][80]; uint32_t cfg_Flags; uint16_t cfg_DupeMode; uint16_t cfg_LoopMode; uint32_t cfg_DefaultZone; struct jbList AkaList; struct jbList AreaList; struct jbList CNodeList; struct jbList PackerList; struct jbList RouteList; struct jbList FileAttachList; struct jbList BounceList; struct jbList ChangeList; struct jbList AreaFixList; struct jbList ArealistList; struct jbList FilterList; #ifdef PLATFORM_AMIGA uint32_t cfg_amiga_LogBufferLines; uint32_t cfg_amiga_LogBufferSecs; uint32_t cfg_amiga_Flags; #endif #ifdef MSGBASE_UMS char cfg_ums_RFCGatewayName[40]; struct Node4D cfg_ums_RFCGatewayNode; char cfg_ums_LoginName[80]; char cfg_ums_LoginPassword[80]; char cfg_ums_LoginServer[80]; char cfg_ums_GatewayName[36]; uint32_t cfg_ums_Flags; #endif #ifdef MSGBASE_MSG uint32_t cfg_msg_Flags; #endif #ifdef MSGBASE_JAM uint32_t cfg_jam_MaxOpen; uint32_t cfg_jam_Flags; #endif }; bool ReadConfig(char *filename,struct Config *cfg,short *seconderr,uint32_t *cfgline,char *cfgerr); bool UpdateConfig(struct Config *cfg,char *cfgerr); void InitConfig(struct Config *cfg); void FreeConfig(struct Config *cfg); bool CheckConfig(struct Config *cfg,char *cfgerr); /* Should not be called in prefs */ #endif crashmail-1.5/src/crashmail/crashmail.c000066400000000000000000000370661230617144500202020ustar00rootroot00000000000000#include "crashmail.h" #ifdef PLATFORM_AMIGA const char ver[]="\0$VER: CrashMail II/" OS_PLATFORM_NAME " " VERSION " " __AMIGADATE__; #endif /*********************************** Global *******************************/ struct jbList PktList; struct jbList DeleteList; bool nomem; bool ioerror; uint32_t ioerrornum; uint32_t toss_read; uint32_t toss_bad; uint32_t toss_route; uint32_t toss_import; uint32_t toss_written; uint32_t toss_dupes; uint32_t scan_total; uint32_t rescan_total; bool no_security; int handle_nesting; struct ConfigNode *RescanNode; uint32_t DayStatsWritten; /* The area statistics are updated until this day */ struct Config config; bool ctrlc; bool nodelistopen; char *prinames[]={"Normal","Hold","Normal","Direct","Crash"}; /**************************** Local for this file ****************************/ #define ARG_SCAN 0 #define ARG_TOSS 1 #define ARG_TOSSFILE 2 #define ARG_TOSSDIR 3 #define ARG_SCANAREA 4 #define ARG_SCANLIST 5 #define ARG_SCANDOTJAM 6 #define ARG_RESCAN 7 #define ARG_RESCANNODE 8 #define ARG_RESCANMAX 9 #define ARG_SENDQUERY 10 #define ARG_SENDLIST 11 #define ARG_SENDUNLINKED 12 #define ARG_SENDHELP 13 #define ARG_SENDINFO 14 #define ARG_REMOVE 15 #define ARG_SETTINGS 16 #define ARG_VERSION 17 #define ARG_LOCK 18 #define ARG_UNLOCK 19 #define ARG_NOSECURITY 20 struct argument args[] = { { ARGTYPE_BOOL, "SCAN", 0, 0 }, { ARGTYPE_BOOL, "TOSS", 0, 0 }, { ARGTYPE_STRING, "TOSSFILE", 0, NULL }, { ARGTYPE_STRING, "TOSSDIR", 0, NULL }, { ARGTYPE_STRING, "SCANAREA", 0, NULL }, { ARGTYPE_STRING, "SCANLIST", 0, NULL }, { ARGTYPE_STRING, "SCANDOTJAM", 0, NULL }, { ARGTYPE_STRING, "RESCAN", 0, NULL }, { ARGTYPE_STRING, "RESCANNODE", 0, NULL }, { ARGTYPE_STRING, "RESCANMAX", 0, NULL }, { ARGTYPE_STRING, "SENDQUERY", 0, NULL }, { ARGTYPE_STRING, "SENDLIST", 0, NULL }, { ARGTYPE_STRING, "SENDUNLINKED", 0, NULL }, { ARGTYPE_STRING, "SENDHELP", 0, NULL }, { ARGTYPE_STRING, "SENDINFO", 0, NULL }, { ARGTYPE_STRING, "REMOVE", 0, NULL }, { ARGTYPE_STRING, "SETTINGS", 0, NULL }, { ARGTYPE_BOOL, "VERSION", 0, 0 }, { ARGTYPE_BOOL, "LOCK", 0, 0 }, { ARGTYPE_BOOL, "UNLOCK", 0, 0 }, { ARGTYPE_BOOL, "NOSECURITY", 0, 0 }, { ARGTYPE_END, NULL, 0, 0 } }; bool init_openlog; bool init_dupedb; void Free(void) { if(init_dupedb) { CloseDupeDB(); init_dupedb=0; } if(init_openlog) { CloseLogfile(); init_openlog=FALSE; } jbFreeList(&PktList); jbFreeList(&DeleteList); } bool Init(void) { struct Area *area; if(!OpenLogfile(config.cfg_LogFile)) { Free(); return(FALSE); } init_openlog=TRUE; if(config.cfg_DupeMode!=DUPE_IGNORE) { if(!OpenDupeDB()) { Free(); return(FALSE); } init_dupedb=TRUE; } if(!ReadStats(config.cfg_StatsFile)) return(FALSE); nomem=FALSE; ioerror=FALSE; for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(area->Messagebase) area->Messagebase->active=TRUE; return(TRUE); } void AfterScanToss(bool success) { struct Area *area; struct ConfigNode *cnode; char errbuf[200]; uint32_t day,d,e,i; ClosePackets(); if(success) { ArchiveOutbound(); ProcessSafeDelete(); } for(i=0;AvailMessagebases[i].name;i++) if(AvailMessagebases[i].active && AvailMessagebases[i].afterfunc) (*AvailMessagebases[i].afterfunc)(success); if(success) { /* Rotate last8 if needed */ if(DayStatsWritten == 0) /* First time we use this statsfile */ DayStatsWritten = time(NULL) / (24*60*60); day=time(NULL) / (24*60*60); for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(day > DayStatsWritten) { for(d=DayStatsWritten;dLast8Days[7-e]=area->Last8Days[7-e-1]; area->Last8Days[0]=0; } } DayStatsWritten=day; /* Areas */ for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(area->NewTexts || area->NewDupes) { area->Texts+=area->NewTexts; area->Last8Days[0]+=area->NewTexts; area->Dupes+=area->NewDupes; if(area->NewTexts) area->LastTime=time(NULL); if(area->NewTexts && area->FirstTime==0) area->FirstTime=time(NULL); } for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) { if(cnode->FirstTime==0 && (cnode->GotEchomails!=0 || cnode->GotNetmails!=0 || cnode->SentEchomails!=0 || cnode->SentNetmails)!=0) cnode->FirstTime=time(NULL); } WriteStats(config.cfg_StatsFile); if(config.changed) { LogWrite(2,SYSTEMINFO,"Updating configuration file \"%s\"",config.filename); if(!UpdateConfig(&config,errbuf)) LogWrite(1,SYSTEMERR,errbuf); } } if(config.cfg_NodelistType) (*config.cfg_NodelistType->nlEnd)(); nodelistopen=FALSE; jbFreeList(&PktList); jbFreeList(&DeleteList); } bool BeforeScanToss(void) { struct Area *area; struct jbList NewPktFEList; struct osFileEntry *fe; char buf[200]; int i; /* Open nodelist */ if(config.cfg_NodelistType) { if(!(*config.cfg_NodelistType->nlStart)(buf)) { LogWrite(1,SYSTEMERR,"%s",buf); return(FALSE); } nodelistopen=TRUE; } toss_read=0; toss_bad=0; toss_route=0; toss_import=0; toss_dupes=0; toss_written=0; scan_total=0; handle_nesting=0; for(area=(struct Area *)config.AreaList.First;area;area=area->Next) { area->NewDupes=0; area->NewTexts=0; area->scanned=FALSE; } jbNewList(&PktList); /* Created packets */ jbNewList(&DeleteList); /* For SafeDelete() */ /* Delete orphan files */ if(!osReadDir(config.cfg_PacketCreate,&NewPktFEList,IsNewPkt)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",config.cfg_PacketCreate); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); if(config.cfg_NodelistType) (*config.cfg_NodelistType->nlEnd)(); nodelistopen=FALSE; return(FALSE); } for(fe=(struct osFileEntry *)NewPktFEList.First;fe;fe=fe->Next) { LogWrite(1,SYSTEMINFO,"Deleting orphan tempfile %s",fe->Name); MakeFullPath(config.cfg_PacketCreate,fe->Name,buf,200); osDelete(buf); } jbFreeList(&NewPktFEList); for(i=0;AvailMessagebases[i].name;i++) if(AvailMessagebases[i].active) { if(AvailMessagebases[i].beforefunc) if(!(*AvailMessagebases[i].beforefunc)()) { if(config.cfg_NodelistType) (*config.cfg_NodelistType->nlEnd)(); nodelistopen=FALSE; return(FALSE); } } return(TRUE); } void Version(void) { int i; printf("This is CrashMail II version %s\n",VERSION); printf("\n"); printf("Operating system: %s\n",OS_PLATFORM_NAME); printf("Compilation date: %s\n",__DATE__); printf("Compilation time: %s\n",__TIME__); printf("\n"); printf("Available messagebase formats:\n"); for(i=0;AvailMessagebases[i].name;i++) printf(" %-10.10s %s\n",AvailMessagebases[i].name,AvailMessagebases[i].desc); printf("\n"); printf("Available nodelist formats:\n"); for(i=0;AvailNodelists[i].name;i++) printf(" %-10.10s %s\n",AvailNodelists[i].name,AvailNodelists[i].desc); } bool Rescan(char *areaname,char *node,uint32_t max) { struct Area *area; struct ConfigNode *cnode; struct Node4D n4d; bool success; if(!Parse4D(node,&n4d)) { LogWrite(1,USERERR,"Invalid node number %s",node); return(FALSE); } for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,&n4d)==0) break; if(!cnode) { LogWrite(1,USERERR,"Unknown node %u:%u/%u.%u",n4d.Zone,n4d.Net,n4d.Node,n4d.Point); return(FALSE); } for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(stricmp(areaname,area->Tagname)==0) break; if(!area) { LogWrite(1,USERERR,"Unknown area %s",areaname); return(FALSE); } if(area->AreaType != AREATYPE_ECHOMAIL) { LogWrite(1,USERERR,"Area %s is not an echomail area",area->Tagname); return(FALSE); } if(!area->Messagebase) { LogWrite(1,USERERR,"Can't rescan %s, area is pass-through",areaname); return(FALSE); } if(!area->Messagebase->rescanfunc) { LogWrite(1,USERERR,"Can't rescan %s, messagebase does not support rescan",areaname); return(FALSE); } if(!BeforeScanToss()) return(FALSE); RescanNode=cnode; rescan_total=0; success=(*area->Messagebase->rescanfunc)(area,max,HandleRescan); RescanNode=NULL; if(success) LogWrite(4,TOSSINGINFO,"Rescanned %u messages",rescan_total); AfterScanToss(success); return(success); } bool SendAFList(char *node,short type) { struct Node4D n4d; struct ConfigNode *cnode; if(!BeforeScanToss()) return(FALSE); if(stricmp(node,"ALL")==0) { for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(cnode->Flags & NODE_NOTIFY) DoSendAFList(type,cnode); } else { if(!Parse4D(node,&n4d)) { LogWrite(1,USERERR,"Invalid node number \"%s\"",node); return(FALSE); } for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,&n4d)==0) break; if(cnode) { DoSendAFList(type,cnode); } else { LogWrite(1,USERERR,"Unknown node %u:%u/%u.%u",n4d.Zone,n4d.Net,n4d.Node,n4d.Point); return(FALSE); } } AfterScanToss(TRUE); if(nomem || ioerror) return(FALSE); return(TRUE); } bool RemoveArea(char *areaname) { struct Area *area; if(!BeforeScanToss()) return(FALSE); for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(area->AreaType == AREATYPE_ECHOMAIL) if(stricmp(areaname,area->Tagname)==0) break; if(!area) { LogWrite(1,USERERR,"Unknown area %s",areaname); return(TRUE); } LogWrite(1,AREAFIX,"AreaFix: Removing area %s",area->Tagname); SendRemoveMessages(area); area->AreaType=AREATYPE_DELETED; RemoveDeletedAreas(); AfterScanToss(TRUE); return(TRUE); } bool done_initconfig; bool done_osinit; bool done_welcomemsg; bool done_init; bool done_lockconfig; bool LockConfig(char *file) { char buf[200]; osFile fp; strcpy(buf,file); strcat(buf,".busy"); while(osExists(buf)) { printf("Configuration file %s is already in use, waiting 10 seconds...\n",file); osSleep(10); if(ctrlc) return(FALSE); } if(!(fp=osOpen(buf,MODE_NEWFILE))) { printf("Failed to create lock file %s\n",buf); return(FALSE); } osClose(fp); return(TRUE); } void UnlockConfig(char *file) { char buf[200]; strcpy(buf,file); strcat(buf,".busy"); osDelete(buf); } void CleanUp(int err) { if(done_welcomemsg) LogWrite(2,SYSTEMINFO,"CrashMail end"); if(done_lockconfig) UnlockConfig(config.filename); if(done_init) Free(); if(done_initconfig) FreeConfig(&config); if(done_osinit) osEnd(); exit(err); } void breakfunc(int x) { ctrlc=TRUE; } int main(int argc, char **argv) { char *cfg; uint32_t cfgline; short seconderr; char errorbuf[500]; signal(SIGINT,breakfunc); if(!osInit()) CleanUp(OS_EXIT_ERROR); done_osinit=TRUE; if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); CleanUp(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) CleanUp(OS_EXIT_ERROR); if(args[ARG_VERSION].data) { Version(); CleanUp(OS_EXIT_OK); } cfg=getenv(OS_CONFIG_VAR); if(!cfg) cfg=OS_CONFIG_NAME; if(args[ARG_SETTINGS].data) cfg=(char *)args[ARG_SETTINGS].data; if(args[ARG_LOCK].data) { if(!LockConfig(cfg)) { printf("Failed to lock configuration file %s\n",cfg); CleanUp(OS_EXIT_ERROR); } printf("CrashMail is now locked, use UNLOCK to unlock\n"); CleanUp(OS_EXIT_OK); } if(args[ARG_UNLOCK].data) { UnlockConfig(cfg); CleanUp(OS_EXIT_OK); } InitConfig(&config); done_initconfig=TRUE; if(!(done_lockconfig=LockConfig(cfg))) { printf("Failed to lock configuration file %s\n",cfg); CleanUp(OS_EXIT_ERROR); } if(!ReadConfig(cfg,&config,&seconderr,&cfgline,errorbuf)) { if(seconderr == READCONFIG_INVALID) printf("Configuration error in %s on line %d:\n%s\n",cfg,cfgline,errorbuf); else if(seconderr == READCONFIG_NO_MEM) printf("Out of memory\n"); else printf("Failed to read configuration file %s\n",cfg); CleanUp(OS_EXIT_ERROR); } if(!CheckConfig(&config,errorbuf)) { printf("Configuration error in %s:\n%s\n",cfg,errorbuf); CleanUp(OS_EXIT_ERROR); } if(!Init()) CleanUp(OS_EXIT_ERROR); done_init=TRUE; LogWrite(2,SYSTEMINFO,"CrashMail II %s started successfully!",VERSION); done_welcomemsg=TRUE; no_security=FALSE; if(args[ARG_NOSECURITY].data) { LogWrite(2,TOSSINGINFO,"Packets will be tossed without security checks"); no_security=TRUE; } if(args[ARG_TOSS].data) TossDir(config.cfg_Inbound); else if(args[ARG_TOSSFILE].data) TossFile((char *)args[ARG_TOSSFILE].data); if(args[ARG_TOSSDIR].data) TossDir((char *)args[ARG_TOSSDIR].data); else if(args[ARG_SCAN].data) Scan(); else if(args[ARG_SCANAREA].data) ScanArea((char *)args[ARG_SCANAREA].data); else if(args[ARG_SCANLIST].data) ScanList((char *)args[ARG_SCANLIST].data); else if(args[ARG_SCANDOTJAM].data) ScanDotJam((char *)args[ARG_SCANDOTJAM].data); else if(args[ARG_RESCAN].data) { uint32_t num; num=0; if(args[ARG_RESCANMAX].data) num=atoi((char *)args[ARG_RESCANMAX].data); if(!args[ARG_RESCANNODE].data) LogWrite(1,USERERR,"No RESCANNODE specified"); else Rescan((char *)args[ARG_RESCAN].data,(char *)args[ARG_RESCANNODE].data,num); } else if(args[ARG_SENDLIST].data) SendAFList((char *)args[ARG_SENDLIST].data,SENDLIST_FULL); else if(args[ARG_SENDQUERY].data) SendAFList((char *)args[ARG_SENDQUERY].data,SENDLIST_QUERY); else if(args[ARG_SENDUNLINKED].data) SendAFList((char *)args[ARG_SENDUNLINKED].data,SENDLIST_UNLINKED); else if(args[ARG_SENDHELP].data) SendAFList((char *)args[ARG_SENDHELP].data,SENDLIST_HELP); else if(args[ARG_SENDINFO].data) SendAFList((char *)args[ARG_SENDINFO].data,SENDLIST_INFO); else if(args[ARG_REMOVE].data) RemoveArea((char *)args[ARG_REMOVE].data); if(nomem) LogWrite(1,SYSTEMERR,"Out of memory"); if(ioerror) LogWrite(1,SYSTEMERR,"I/O error: %s",osErrorMsg(ioerrornum)); if(ctrlc) LogWrite(1,SYSTEMERR,"*** User break ***"); CleanUp(OS_EXIT_OK); /* The next line is never actually executed. It is just there to stop gcc from giving a warning. */ return(0); } crashmail-1.5/src/crashmail/crashmail.h000066400000000000000000000032401230617144500201720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "node4dpat.h" #include "nl.h" #include "mb.h" #include "memmessage.h" #include "config.h" #include "memmessage.h" #include "logwrite.h" #include "dupe.h" #include "stats.h" #include "misc.h" #include "safedel.h" #include "toss.h" #include "scan.h" #include "pkt.h" #include "handle.h" #include "outbound.h" #include "areafix.h" #include "filter.h" #include "version.h" extern struct jbList PktList; extern struct jbList DeleteList; extern bool nomem; extern bool ioerror; extern uint32_t ioerrornum; extern uint32_t toss_read; extern uint32_t toss_bad; extern uint32_t toss_route; extern uint32_t toss_import; extern uint32_t toss_written; extern uint32_t toss_dupes; extern uint32_t scan_total; extern uint32_t rescan_total; extern bool no_security; extern int handle_nesting; extern struct ConfigNode *RescanNode; extern uint32_t DayStatsWritten; extern struct Nodelist AvailNodelists[]; extern struct Messagebase AvailMessagebases[]; extern struct Config config; extern bool ctrlc; extern bool nodelistopen; bool BeforeScanToss(void); void AfterScanToss(bool success); extern char *prinames[]; crashmail-1.5/src/crashmail/dupe.c000066400000000000000000000212511230617144500171610ustar00rootroot00000000000000#include "crashmail.h" #define DUPES_IDENTIFIER "CDU2" unsigned long cm_crc32tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; uint32_t calcstringcrc(char *str) { uint32_t crc; int c; crc=0xffffffff; for(c=0;str[c];c++) crc=(crc>>8) ^ cm_crc32tab[(unsigned char)crc ^ str[c]]; return(crc); } struct dupeentry { uint32_t offset; uint32_t crc32; }; bool dupechanged; struct dupeentry *dupebuf; uint32_t dupeentrynum,dupeentrymax; osFile dupefh; void adddupeindex(uint32_t offset,uint32_t crc32) { dupebuf[dupeentrynum].offset=offset; dupebuf[dupeentrynum].crc32=crc32; dupeentrynum++; if(dupeentrynum > dupeentrymax) dupeentrymax=dupeentrynum; if(dupeentrynum == config.cfg_DupeSize) dupeentrynum=0; } void copydupe(uint16_t c,osFile oldfh,osFile newfh) { char buf[300]; uint16_t size; osSeek(oldfh,dupebuf[c].offset,OFFSET_BEGINNING); if(osRead(oldfh,&size,sizeof(uint16_t))!=sizeof(uint16_t)) return; if(osRead(oldfh,buf,size) != size) return; if(!osWrite(newfh,&size,sizeof(uint16_t))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to write to temporary dupe file"); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return; } if(!osWrite(newfh,buf,size)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to write to temporary dupe file"); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return; } } bool OpenDupeDB(void) { char buf[300]; uint32_t offset,crc32,*crc32p; uint16_t size,res; if(!(dupebuf=osAlloc(config.cfg_DupeSize*sizeof(struct dupeentry)))) { LogWrite(1,SYSTEMERR,"Not enough memory for dupe-check buffer\n"); return(FALSE); } dupeentrynum=0; dupeentrymax=0; dupechanged=FALSE; if(!(dupefh=osOpen(config.cfg_DupeFile,MODE_READWRITE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open dupe file %s in read/write mode",config.cfg_DupeFile); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } res=osRead(dupefh,buf,4); buf[4]=0; if(res == 0) { /* New file */ LogWrite(3,TOSSINGINFO,"Creating new dupe file %s",config.cfg_DupeFile); strcpy(buf,DUPES_IDENTIFIER); osWrite(dupefh,buf,4); } else if(res != 4 || strcmp(buf,DUPES_IDENTIFIER)!=0) { LogWrite(1,SYSTEMERR,"Invalid format of dupe file %s, exiting...",config.cfg_DupeFile); osClose(dupefh); return(FALSE); } offset=4; while(osRead(dupefh,&size,sizeof(uint16_t))==sizeof(uint16_t)) { if(size == 0 || size > 300) /* Unreasonably big */ { LogWrite(1,SYSTEMERR,"Error in dupe file %s, exiting...",config.cfg_DupeFile); osClose(dupefh); return(FALSE); } if(osRead(dupefh,buf,(uint32_t)size) != size) { LogWrite(1,SYSTEMERR,"Error in dupe file %s, exiting...",config.cfg_DupeFile); osClose(dupefh); return(FALSE); } crc32p=(uint32_t *)buf; crc32=*crc32p; adddupeindex(offset,crc32); offset += size+2; } dupechanged=FALSE; return(TRUE); } void CloseDupeDB(void) { osFile newfh; uint32_t c; char duptemp[200]; if(!dupechanged) { osClose(dupefh); return; } strcpy(duptemp,config.cfg_DupeFile); strcat(duptemp,".tmp"); if(!(newfh=osOpen(duptemp,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open temporary dupe file %s for writing",duptemp); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); osFree(dupebuf); osClose(dupefh); return; } osWrite(newfh,DUPES_IDENTIFIER,4); for(c=dupeentrynum;cMSGID[0] == 0) return(FALSE); /* No dupechecking for messages without MSGID */ crc32=calcstringcrc(mm->MSGID); crc32p=(uint32_t *)dbuf; *crc32p=crc32; dsize=4; strcpy(&dbuf[dsize],mm->MSGID); dsize += strlen(mm->MSGID) + 1; strcpy(&dbuf[dsize],mm->Area); dsize += strlen(mm->Area) + 1; for(c=0;c=0;j--) if(tolower(text[i+j])!=tolower(pat[j])) break; if(j == -1) return(i); i+=bmstep[tolower(text[i+j])]; } return(-1); } struct MemMessage *filter_mm; int filter_nllookup(struct Node4D *node) { if(!config.cfg_NodelistType) return(-1); if(!nodelistopen) return(1); return (*config.cfg_NodelistType->nlCheckNode)(node); } int filter_comparenode(char *var,struct Node4D *node,char *operator,char *data,int opn,int datan,int *errpos,char **errstr) { static char errbuf[100]; struct Node4DPat pat; if(operator[0]==0) { sprintf(errbuf,"%s is not a boolean variable",var); *errstr=errbuf; *errpos=opn; return(-1); } if(strcmp(operator,"=")!=0) { sprintf(errbuf,"%s is not a valid operator for %s",operator,var); *errstr=errbuf; *errpos=opn; return(-1); } if(!(Parse4DPat(data,&pat))) { sprintf(errbuf,"Invalid node pattern \"%s\"",data); *errstr=errbuf; *errpos=datan; return(-1); } if(!config.cfg_NodelistType && !Check4DPatNodelist(&pat)) { sprintf(errbuf,"Nodelist needed for pattern \"%s\"",data); *errstr=errbuf; *errpos=datan; return(-1); } if(Compare4DPat(&pat,node)==0) return(1); return(0); } int filter_comparestring(char *var,char *str,char *operator,char *data,int opn,int datan,int *errpos,char **errstr) { static char errbuf[100]; if(operator[0]==0) { sprintf(errbuf,"%s is not a boolean variable",var); *errstr=errbuf; *errpos=opn; return(-1); } if(strcmp(operator,"|")==0) { bminit(data); if(bmfind(data,str,strlen(str),0) != -1) return(1); return(0); } else if(strcmp(operator,"=")==0) { if(!(osCheckPattern(data))) { sprintf(errbuf,"Invalid pattern \"%s\"",data); *errstr=errbuf; *errpos=datan; return(-1); } if(osMatchPattern(data,str)) return(1); return(0); } else { sprintf(errbuf,"%s is not a valid operator for %s",operator,var); *errstr=errbuf; *errpos=opn; return(-1); } } int filter_comparebool(char *var,bool bl,char *operator,char *data,int opn,int datan,int *errpos,char **errstr) { static char errbuf[100]; if(operator[0]==0) { if(bl) return(1); return(0); } else if(strcmp(operator,"=")==0) { if(stricmp(data,"TRUE")==0) { if(bl) return(1); return(0); } else if(stricmp(data,"FALSE")==0) { if(bl) return(0); return(1); } else { sprintf(errbuf,"Boolean variable %s can only be TRUE or FALSE",var); *errstr=errbuf; *errpos=datan; return(-1); } } else { sprintf(errbuf,"%s is not a valid operator for %s",operator,var); *errstr=errbuf; *errpos=opn; return(-1); } } int filter_comparetext(char *var,struct MemMessage *mm,bool kludge,char *operator,char *data,int opn,int datan,int *errpos,char **errstr) { static char errbuf[100]; struct TextChunk *chunk; long start,pos,c; if(operator[0]==0) { sprintf(errbuf,"%s is not a boolean variable",var); *errstr=errbuf; *errpos=opn; return(-1); } if(strcmp(operator,"|")!=0) { sprintf(errbuf,"%s is not a valid operator for %s",operator,var); *errstr=errbuf; *errpos=opn; return(-1); } bminit(data); for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) { /* Search chunk */ start=0; while((pos=bmfind(data,chunk->Data,chunk->Length,start)) != -1) { start=pos+1; for(c=pos;c>0 && chunk->Data[c]!=1 && chunk->Data[c]!=13;c--); if(chunk->Data[c]==1 && kludge) return(1); if(chunk->Data[c]!=1 && !kludge) return(1); } } return(0); } int filter_evalfunc(char *str,int *errpos,char **errstr) { char type[20],source[20]; static char errbuf[100]; struct Aka *aka; struct ConfigNode *cnode; bool fileattach,tolocalaka,fromlocalaka,tolocalpoint,fromlocalpoint; bool existscfg_fromaddr,existscfg_fromboss,existscfg_toaddr,existscfg_toboss; char var[100],operator[2],data[100]; int opn,datan,res; int c,d; struct Node4D from4d,fromboss,toboss; /* Parse statement */ c=0; d=0; while(str[c]!=0 && (isalpha(str[c]) || str[c] == '_') && d<99) var[d++]=str[c++]; var[d]=0; opn=c; if(str[c]!=0) { operator[0]=str[c++]; operator[1]=0; } else { operator[0]=0; } datan=c; if(str[c]=='"') c++; mystrncpy(data,&str[c],100); if(data[0]!=0) { if(data[strlen(data)-1]=='"') data[strlen(data)-1]=0; } /* Set real fromaddr and fromboss, toboss */ if(filter_mm->Area[0] == 0) Copy4D(&from4d,&filter_mm->OrigNode); else Copy4D(&from4d,&filter_mm->Origin4D); Copy4D(&fromboss,&from4d); fromboss.Point=0; Copy4D(&toboss,&filter_mm->DestNode); toboss.Point=0; /* Make local variables */ tolocalaka=FALSE; fromlocalaka=FALSE; tolocalpoint=FALSE; fromlocalpoint=FALSE; existscfg_fromaddr=FALSE; existscfg_fromboss=FALSE; existscfg_toaddr=FALSE; existscfg_toboss=FALSE; fileattach=FALSE; for(aka=(struct Aka *)config.AkaList.First;aka;aka=aka->Next) { if(Compare4D(&filter_mm->DestNode,&aka->Node)==0) tolocalaka=TRUE; if(Compare4D(&from4d,&aka->Node)==0) fromlocalaka=TRUE; if(filter_mm->DestNode.Point != 0) if(Compare4D(&aka->Node,&toboss)==0 && aka->Node.Point==0) tolocalpoint=TRUE; if(from4d.Point != 0) if(Compare4D(&aka->Node,&fromboss)==0 && aka->Node.Point==0) fromlocalpoint=TRUE; } for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) { if(Compare4D(&cnode->Node,&from4d)==0) existscfg_fromaddr=TRUE; if(Compare4D(&cnode->Node,&fromboss)==0) existscfg_fromboss=TRUE; if(Compare4D(&cnode->Node,&filter_mm->DestNode)==0) existscfg_toaddr=TRUE; if(Compare4D(&cnode->Node,&toboss)==0) existscfg_toboss=TRUE; } if(filter_mm->Attr & FLAG_FILEATTACH) fileattach=TRUE; if(filter_mm->Area[0]==0) strcpy(type,"NETMAIL"); else strcpy(type,"ECHOMAIL"); strcpy(source,"OTHER"); if(filter_mm->Flags & MMFLAG_EXPORTED) strcpy(source,"EXPORTED"); if(filter_mm->Flags & MMFLAG_AUTOGEN) strcpy(source,"CRASHMAIL"); if(filter_mm->Flags & MMFLAG_TOSSED) strcpy(source,"TOSSED"); /* Compare */ if(stricmp(var,"FROMADDR")==0) return filter_comparenode("FROMADDR",&from4d,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"TOADDR")==0) return filter_comparenode("TOADDR",&filter_mm->DestNode,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"FROMNAME")==0) return filter_comparestring("FROMNAME",filter_mm->From,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"TONAME")==0) return filter_comparestring("TONAME",filter_mm->To,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"SUBJECT")==0) return filter_comparestring("SUBJECT",filter_mm->Subject,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"AREA")==0) return filter_comparestring("AREA",filter_mm->Area,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"TYPE")==0) return filter_comparestring("TYPE",type,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"SOURCE")==0) return filter_comparestring("SOURCE",source,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"FILEATTACH")==0) return filter_comparebool("FILEATTACH",fileattach,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"TOLOCALAKA")==0) return filter_comparebool("TOLOCALAKA",tolocalaka,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"FROMLOCALAKA")==0) return filter_comparebool("FROMLOCALAKA",fromlocalaka,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"TOLOCALPOINT")==0) return filter_comparebool("TOLOCALPOINT",tolocalpoint,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"FROMLOCALPOINT")==0) return filter_comparebool("FROMLOCALPOINT",fromlocalpoint,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSCFG_FROMADDR")==0) return filter_comparebool("EXISTSCFG_FROMADDR",existscfg_fromaddr,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSCFG_FROMBOSS")==0) return filter_comparebool("EXISTSCFG_FROMBOSS",existscfg_fromboss,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSCFG_TOADDR")==0) return filter_comparebool("EXISTSCFG_TOADDR",existscfg_toaddr,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSCFG_TOBOSS")==0) return filter_comparebool("EXISTSCFG_TOBOSS",existscfg_toboss,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSNL_FROMADDR")==0) { res=filter_nllookup(&from4d); if(res == -1) { sprintf(errbuf,"Nodelist required for variable %s",var); *errstr=errbuf; *errpos=0; return(-1); } return res; } if(stricmp(var,"EXISTSNL_FROMBOSS")==0) { res=filter_nllookup(&fromboss); if(res == -1) { sprintf(errbuf,"Nodelist required for variable %s",var); *errstr=errbuf; *errpos=0; return(-1); } return res; } if(stricmp(var,"EXISTSNL_TOADDR")==0) { res=filter_nllookup(&filter_mm->DestNode); if(res == -1) { sprintf(errbuf,"Nodelist required for variable %s",var); *errstr=errbuf; *errpos=0; return(-1); } return res; } if(stricmp(var,"EXISTSNL_TOBOSS")==0) { res=filter_nllookup(&toboss); if(res == -1) { sprintf(errbuf,"Nodelist required for variable %s",var); *errstr=errbuf; *errpos=0; return(-1); } return res; } if(stricmp(var,"EXISTSCFG_FROMBOSS")==0) return filter_comparebool("EXISTSCFG_FROMBOSS",existscfg_fromboss,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSCFG_TOADDR")==0) return filter_comparebool("EXISTSCFG_TOADDR",existscfg_toaddr,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"EXISTSCFG_TOBOSS")==0) return filter_comparebool("EXISTSCFG_TOBOSS",existscfg_toboss,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"TEXT")==0) return filter_comparetext("TEXT",filter_mm,FALSE,operator,data,opn,datan,errpos,errstr); if(stricmp(var,"KLUDGES")==0) return filter_comparetext("KLUDGES",filter_mm,TRUE,operator,data,opn,datan,errpos,errstr); sprintf(errbuf,"Unknown variable %s",var); *errstr=errbuf; *errpos=0; return(-1); } bool Filter_Kill(struct MemMessage *mm) { char buf[200]; if(mm->Area[0] == 0) { Print4D(&mm->OrigNode,buf); LogWrite(4,TOSSINGINFO,"Filter: Killing netmail from \"%s\" at %s", mm->From, buf); } else { LogWrite(4,TOSSINGINFO,"Filter: Killing message from \"%s\" in %s", mm->From, mm->Area); } mm->Flags |= MMFLAG_KILL; return(TRUE); } bool Filter_Twit(struct MemMessage *mm) { char buf[200]; if(mm->Area[0] == 0) { Print4D(&mm->OrigNode,buf); LogWrite(4,TOSSINGINFO,"Filter: Twitting netmail from \"%s\" at %s", mm->From, buf); } else { LogWrite(4,TOSSINGINFO,"Filter: Twitting message from \"%s\" in %s", mm->From, mm->Area); } mm->Flags |= MMFLAG_TWIT; return(TRUE); } bool Filter_Copy(struct MemMessage *mm,char *tagname) { struct Area *area; struct TextChunk *tmp; struct jbList oldlist; char buf[200]; LogWrite(4,TOSSINGINFO,"Filter: Copying message to area %s",tagname); for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(stricmp(area->Tagname,tagname)==0) break; if(!area) return(TRUE); /* We have already checked this in CheckConfig(), the area should exist */ oldlist.First=mm->TextChunks.First; oldlist.Last=mm->TextChunks.Last; jbNewList(&mm->TextChunks); if(mm->Area[0]) { sprintf(buf,"AREA:%s\x0d",mm->Area); mmAddBuf(&mm->TextChunks,buf,strlen(buf)); } for(tmp=(struct TextChunk *)oldlist.First;tmp;tmp=tmp->Next) mmAddBuf(&mm->TextChunks,tmp->Data,tmp->Length); if(!((*area->Messagebase->importfunc)(mm,area))) return(FALSE); area->NewTexts++; jbFreeList(&mm->TextChunks); mm->TextChunks.First=oldlist.First; mm->TextChunks.Last=oldlist.Last; return(TRUE); } bool Filter_WriteBad(struct MemMessage *mm,char *reason) { LogWrite(4,TOSSINGINFO,"Filter: Writing message to BAD area (\"%s\")",reason); return WriteBad(mm,reason); } bool Filter_WriteLog(struct MemMessage *mm,char *cmd) { char buf[400]; char origbuf[30],destbuf[30]; if(mm->Area[0] == 0) Print4D(&mm->OrigNode,origbuf); else Print4D(&mm->Origin4D,origbuf); Print4D(&mm->DestNode,destbuf); ExpandFilter(cmd,buf,400, "", "", "", mm->Area, mm->Subject, mm->DateTime, mm->From, mm->To, origbuf, destbuf); LogWrite(1,TOSSINGINFO,"%s",buf); return(TRUE); } bool Filter_Execute(struct MemMessage *mm,char *cmd) { bool msg,rfc1,rfc2; char msgbuf[L_tmpnam],rfcbuf1[L_tmpnam],rfcbuf2[L_tmpnam]; char origbuf[30],destbuf[30]; char buf[400]; int arcres; msg=FALSE; rfc1=FALSE; rfc2=FALSE; msgbuf[0]=0; rfcbuf1[0]=0; rfcbuf2[0]=0; if(strstr(cmd,"%m")) msg=TRUE; if(strstr(cmd,"%r")) rfc1=TRUE; if(strstr(cmd,"%R")) rfc2=TRUE; if(rfc1) tmpnam(rfcbuf1); if(rfc2) tmpnam(rfcbuf2); if(msg) tmpnam(msgbuf); if(mm->Area[0] == 0) Print4D(&mm->OrigNode,origbuf); else Print4D(&mm->Origin4D,origbuf); Print4D(&mm->DestNode,destbuf); ExpandFilter(cmd,buf,400, rfcbuf1, rfcbuf2, msgbuf, mm->Area, mm->Subject, mm->DateTime, mm->From, mm->To, origbuf, destbuf); if(rfc1) WriteRFC(mm,rfcbuf1,FALSE); if(rfc2) WriteRFC(mm,rfcbuf2,TRUE); if(msg) WriteMSG(mm,msgbuf); LogWrite(4,SYSTEMINFO,"Filter: Executing external command \"%s\"",buf); arcres=osExecute(buf); if(rfc1) osDelete(rfcbuf1); if(rfc2) osDelete(rfcbuf2); if(msg) osDelete(msgbuf); if(arcres == 0) { /* Command ok */ LogWrite(1,SYSTEMERR,"Filter: External command returned without error, killing message\n"); mm->Flags |= MMFLAG_KILL; return(TRUE); } if(arcres >= 20) { LogWrite(1,SYSTEMERR,"Filter: External command failed with error %u, exiting...",arcres); return(FALSE); } return(TRUE); } bool Filter_Bounce(struct MemMessage *mm,char *reason,bool headeronly) { char reasonbuf[400]; char origbuf[30],destbuf[30]; if(mm->Area[0] == 0) Print4D(&mm->OrigNode,origbuf); else Print4D(&mm->Origin4D,origbuf); Print4D(&mm->DestNode,destbuf); ExpandFilter(reason,reasonbuf,400, "", "", "", mm->Area, mm->Subject, mm->DateTime, mm->From, mm->To, origbuf, destbuf); LogWrite(4,TOSSINGINFO,"Filter: Bouncing message (\"%s\")",reasonbuf); return Bounce(mm,reasonbuf,headeronly); } bool Filter_Remap(struct MemMessage *mm,char *namepat,struct Node4DPat *destpat) { struct Route *tmproute; struct jbList oldlist; struct TextChunk *tmp; char buf[100]; char oldto[36],newto[36]; struct Node4D olddest4d,newdest4d,my4d; uint32_t c,d; bool skip; if(mm->Area[0]) { LogWrite(1,SYSTEMERR,"Filter: Only netmails can be remapped"); return(TRUE); } strcpy(oldto,mm->To); Copy4D(&olddest4d,&mm->DestNode); ExpandNodePat(destpat,&mm->DestNode,&newdest4d); if(strcmp(namepat,"*")==0) strcpy(newto,mm->To); else strcpy(newto,namepat); my4d.Zone=0; my4d.Net=0; my4d.Node=0; my4d.Point=0; for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&newdest4d)==0) break; if(tmproute) Copy4D(&my4d,&tmproute->Aka->Node); LogWrite(4,SYSTEMINFO,"Filter: Remapping message to %s at %u:%u/%u.%u", newto, newdest4d.Zone, newdest4d.Net, newdest4d.Node, newdest4d.Point); LogWrite(4,SYSTEMINFO,"Filter: Message originally to %s at %u:%u/%u.%u", oldto, olddest4d.Zone, olddest4d.Net, olddest4d.Node, olddest4d.Point); oldlist.First=mm->TextChunks.First; oldlist.Last=mm->TextChunks.Last; jbNewList(&mm->TextChunks); Copy4D(&mm->DestNode,&newdest4d); strcpy(mm->To,newto); MakeNetmailKludges(mm); for(tmp=(struct TextChunk *)oldlist.First;tmp;tmp=tmp->Next) { c=0; while(cLength) { for(d=c;dLength && tmp->Data[d]!=13;d++); if(tmp->Data[d]==13) d++; skip=FALSE; if(d-c > 5) { if(strncmp(&tmp->Data[c],"\x01""INTL",5)==0) skip=TRUE; if(strncmp(&tmp->Data[c],"\x01""FMPT",5)==0) skip=TRUE; if(strncmp(&tmp->Data[c],"\x01""TOPT",5)==0) skip=TRUE; } if(d-c!=0 && !skip) mmAddBuf(&mm->TextChunks,&tmp->Data[c],d-c); c=d; } } sprintf(buf,"\x01Remapped to %s at %u:%u/%u.%u by %u:%u/%u.%u\x0d", newto, newdest4d.Zone, newdest4d.Net, newdest4d.Node, newdest4d.Point, my4d.Zone, my4d.Net, my4d.Node, my4d.Point); mmAddLine(mm,buf); sprintf(buf,"\x01Message originally to %s at %u:%u/%u.%ud", oldto, olddest4d.Zone, olddest4d.Net, olddest4d.Node, olddest4d.Point); mmAddLine(mm,buf); jbFreeList(&oldlist); if(nomem) return(FALSE); return(TRUE); } bool Filter(struct MemMessage *mm) { struct Filter *filter; struct Command *command; struct expr *expr; char *errstr; int errpos,res; for(filter=(struct Filter *)config.FilterList.First;filter;filter=filter->Next) { if(!(expr=expr_makeexpr(filter->Filter))) { nomem=TRUE; return(FALSE); } filter_mm=mm; res=expr_eval(expr,filter_evalfunc,&errpos,&errstr); expr_free(expr); if(res == -1) { /* Error. All these should be caught in config.c/CheckConfig() */ LogWrite(3,TOSSINGERR,"Syntax error in filter"); return(TRUE); } if(res == 1) { /* Matches filter */ for(command=(struct Command *)filter->CommandList.First;command;command=command->Next) { switch(command->Cmd) { case COMMAND_KILL: Filter_Kill(mm); break; case COMMAND_TWIT: Filter_Twit(mm); break; case COMMAND_COPY: Filter_Copy(mm,command->string); break; case COMMAND_EXECUTE: if(!Filter_Execute(mm,command->string)) return(FALSE); break; case COMMAND_WRITELOG: if(!Filter_WriteLog(mm,command->string)) return(FALSE); break; case COMMAND_WRITEBAD: if(!Filter_WriteBad(mm,command->string)) return(FALSE); break; case COMMAND_BOUNCEMSG: if(!Filter_Bounce(mm,command->string,FALSE)) return(FALSE); break; case COMMAND_BOUNCEHEADER: if(!Filter_Bounce(mm,command->string,TRUE)) return(FALSE); break; case COMMAND_REMAPMSG: if(!Filter_Remap(mm,command->string,&command->n4ddestpat)) return(FALSE); break; } if(mm->Flags & MMFLAG_KILL) return(TRUE); } } } return(TRUE); } bool CheckFilter(char *filter,char *cfgerr) { struct expr *expr; char *errstr; int errpos,res; struct MemMessage *mm; int c; if(!(expr=expr_makeexpr(filter))) { nomem=TRUE; return(FALSE); } if(!(mm=mmAlloc())) { expr_free(expr); return(FALSE); } filter_mm=mm; res=expr_eval(expr,filter_evalfunc,&errpos,&errstr); expr_free(expr); mmFree(mm); if(res == -1) { if(strlen(filter) > 200) { strcpy(cfgerr,"Syntax error in filter"); } else { sprintf(cfgerr," Syntax error in filter:\n %s\n",filter); for(c=0;cArea[0]==0) res=HandleNetmail(mm); else res=HandleEchomail(mm); handle_nesting--; return(res); } /**************************** auto-add *****************************/ bool GetDescription(char *area,struct ConfigNode *node,char *desc) { struct Arealist *arealist; char buf[200]; uint32_t c,d; osFile fh; for(arealist=(struct Arealist *)config.ArealistList.First;arealist;arealist=arealist->Next) { if(arealist->Node == node && (arealist->Flags & AREALIST_DESC)) { if((fh=osOpen(arealist->AreaFile,MODE_OLDFILE))) { while(osFGets(fh,buf,199)) { for(c=0;buf[c]>32;c++); if(buf[c]!=0) { buf[c]=0; if(stricmp(buf,area)==0) { c++; while(buf[c]<=32 && buf[c]!=0) c++; if(buf[c]!=0) { d=0; while(buf[c]!=0 && buf[c]!=10 && buf[c]!=13 && d<77) desc[d++]=buf[c++]; desc[d]=0; osClose(fh); return(TRUE); } } } } osClose(fh); } else { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open file \"%s\"\n",arealist->AreaFile); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); } } } return(FALSE); } bool AddTossNode(struct Area *area,struct ConfigNode *cnode,uint16_t flags) { struct TossNode *tnode; /* Check if it already exists */ for(tnode=(struct TossNode *)area->TossNodes.First;tnode;tnode=tnode->Next) if(tnode->ConfigNode == cnode) return(TRUE); if(!(tnode=(struct TossNode *)osAllocCleared(sizeof(struct TossNode)))) { nomem=TRUE; return(FALSE); } jbAddNode((struct jbList *)&area->TossNodes,(struct jbNode *)tnode), tnode->ConfigNode=cnode; tnode->Flags=flags; return(TRUE); } time_t lastt; void MakeDirectory(char *dest,uint32_t destsize,char *defdir,char *areaname) { uint32_t c,d; char lowercase[200],shortname[50]; /* Convert to lower case */ strcpy(lowercase,areaname); for(c=0;lowercase[c]!=0;c++) lowercase[c]=tolower(lowercase[c]); /* Make 8 digit serial number */ if(lastt == 0) lastt=time(NULL); else lastt++; sprintf(shortname,"%08lx",lastt); d=0; for(c=0;cTossNodes); jbNewList(&temparea->BannedNodes); jbAddNode(&config.AreaList,(struct jbNode *)temparea); for(tempaka=(struct Aka *)config.AkaList.First;tempaka;tempaka=tempaka->Next) if(Compare4D(&tempaka->Node,mynode)==0) break; if(!tempaka) tempaka=(struct Aka *)config.AkaList.First; for(tempcnode=(struct ConfigNode *)config.CNodeList.First;tempcnode;tempcnode=tempcnode->Next) if(Compare4D(&tempcnode->Node,node)==0) break; /* Find default area to use */ defarea=NULL; /* First we try to find one for specific groups */ if(tempcnode && tempcnode->DefaultGroup) { char groups[100]; for(defarea=(struct Area *)config.AreaList.First;defarea;defarea=defarea->Next) if(strnicmp(defarea->Tagname,"DEFAULT_",8)==0) { mystrncpy(groups,&defarea->Tagname[8],50); if(MatchFlags(tempcnode->DefaultGroup,groups)) break; } } /* If not found, we try to find the general default area */ if(!defarea) { for(defarea=(struct Area *)config.AreaList.First;defarea;defarea=defarea->Next) if(stricmp(defarea->Tagname,"DEFAULT")==0) break; } if(defarea) { struct TossNode *tnode; uint32_t c; char *forbiddenchars="\"#'`()*,./:;<>|"; char buf[100],buf2[100]; strcpy(buf,name); for(c=0;buf[c]!=0;c++) if(buf[c]<33 || buf[c]>126 || strchr(forbiddenchars,buf[c])) buf[c]='_'; /* Cannot create directory directly into temparea->Path. MakeDirectory checks for duplicate area names in the AreaList and would get confused */ MakeDirectory(buf2,80,defarea->Path,buf); strcpy(temparea->Path,buf2); if(!forcepassthru) temparea->Messagebase=defarea->Messagebase; strcpy(temparea->Description,defarea->Description); if(defarea->Flags & AREA_MANDATORY) temparea->Flags |= AREA_MANDATORY; if(defarea->Flags & AREA_DEFREADONLY) temparea->Flags |= AREA_DEFREADONLY; if(defarea->Flags & AREA_IGNOREDUPES) temparea->Flags |= AREA_IGNOREDUPES; if(defarea->Flags & AREA_IGNORESEENBY) temparea->Flags |= AREA_IGNORESEENBY; temparea->KeepDays=defarea->KeepDays; temparea->KeepNum=defarea->KeepNum; for(tnode=(struct TossNode *)defarea->TossNodes.First;tnode;tnode=tnode->Next) AddTossNode(temparea,tnode->ConfigNode,tnode->Flags); } GetDescription(name,tempcnode,temparea->Description); if(!active) temparea->Flags=AREA_UNCONFIRMED; strcpy(temparea->Tagname,name); temparea->Aka=tempaka; temparea->AreaType = AREATYPE_ECHOMAIL; if(tempcnode) { temparea->Group=tempcnode->DefaultGroup; AddTossNode(temparea,tempcnode,TOSSNODE_FEED); for(tempcnode=(struct ConfigNode *)config.CNodeList.First;tempcnode;tempcnode=tempcnode->Next) if(MatchFlags(temparea->Group,tempcnode->AddGroups)) { uint16_t flags; flags=0; if((temparea->Flags & AREA_DEFREADONLY) || MatchFlags(temparea->Group,tempcnode->ReadOnlyGroups)) flags=TOSSNODE_READONLY; AddTossNode(temparea,tempcnode,flags); } } config.changed=TRUE; temparea->changed=TRUE; return(temparea); } /**************************** Echomail *****************************/ bool FindNodes2D(struct jbList *list,struct Node4D *node) { struct Nodes2D *tmp; uint16_t c; for(tmp=(struct Nodes2D *)list->First;tmp;tmp=tmp->Next) for(c=0;cNodes;c++) if(tmp->Net[c]==node->Net && tmp->Node[c]==node->Node) return(TRUE); return(FALSE); } bool WriteBad(struct MemMessage *mm,char *reason) { struct Area *temparea; struct TextChunk *chunk; for(temparea=(struct Area *)config.AreaList.First;temparea;temparea=temparea->Next) if(temparea->AreaType == AREATYPE_BAD) break; if(!temparea) { LogWrite(2,TOSSINGERR,"No BAD area configured, message lost"); return(TRUE); } /* Insert a new textchunk with information first in the message */ if(!(chunk=(struct TextChunk *)osAlloc(sizeof(struct TextChunk)))) { nomem=TRUE; return(FALSE); } chunk->Next=(struct TextChunk *)mm->TextChunks.First; mm->TextChunks.First = (struct jbNode *)chunk; if(!mm->TextChunks.Last) mm->TextChunks.Last=(struct jbNode *)chunk; if(mm->Area[0]==0) { sprintf(chunk->Data,"DEST:%u:%u/%u.%u\x0d" "ORIG:%u:%u/%u.%u\x0d" "PKTORIG:%u:%u/%u.%u\x0d" "PKTDEST:%u:%u/%u.%u\x0d" "ERROR:%s\x0d", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point, mm->OrigNode.Zone, mm->OrigNode.Net, mm->OrigNode.Node, mm->OrigNode.Point, mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->PktDest.Zone, mm->PktDest.Net, mm->PktDest.Node, mm->PktDest.Point, reason); chunk->Length=strlen(chunk->Data); } else { sprintf(chunk->Data,"AREA:%s\x0d" "PKTORIG:%u:%u/%u.%u\x0d" "PKTDEST:%u:%u/%u.%u\x0d" "ERROR:%s\x0d", mm->Area, mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->PktDest.Zone, mm->PktDest.Net, mm->PktDest.Node, mm->PktDest.Point, reason); chunk->Length=strlen(chunk->Data); } if(temparea->Messagebase) { if(!((*temparea->Messagebase->importfunc)(mm,temparea))) return(FALSE); } /* Remove first chunk again */ chunk=(struct TextChunk *)mm->TextChunks.First; mm->TextChunks.First=(struct jbNode *)chunk->Next; if((struct TextChunk *)mm->TextChunks.Last == chunk) mm->TextChunks.Last = NULL; osFree(chunk); temparea->NewTexts++; return(TRUE); } bool AddNodePath(struct jbList *list,struct Node4D *node) { char buf[40],buf2[10]; struct Path *path; struct Node4D n4d; uint16_t lastnet,num; bool lastok; uint32_t jbcpos; lastok=FALSE; lastnet=0; /* Find last entry in Path */ path=(struct Path *)list->Last; if(path && path->Paths!=0) { num=path->Paths-1; jbcpos=0; while(jbstrcpy(buf,path->Path[num],40,&jbcpos)) { if(Parse4D(buf,&n4d)) { if(n4d.Net == 0) n4d.Net=lastnet; else lastnet=n4d.Net; lastok=TRUE; } else { lastok=FALSE; } } } /* Are we already in the PATH line? */ if(lastok) { if(n4d.Net == node->Net && n4d.Node == node->Node && n4d.Point == node->Point) return(TRUE); } /* Make address */ if(lastok && n4d.Net == node->Net) sprintf(buf,"%u",node->Node); else sprintf(buf,"%u/%u",node->Net,node->Node); if(node->Point != 0) { sprintf(buf2,".%u",node->Point); strcat(buf,buf2); } /* Add new */ path=(struct Path *)list->Last; if(path) { if(path->Paths != 0) { if(strlen(buf)+strlen(path->Path[path->Paths-1])<=70) { /* Add to old path */ strcat(path->Path[path->Paths-1]," "); strcat(path->Path[path->Paths-1],buf); return(TRUE); } } } if(path && path->Paths == PKT_NUMPATH) path=NULL; /* Chunk is full */ if(!path) { /* Alloc new path */ if(!(path=(struct Path *)osAlloc(sizeof(struct Path)))) { nomem=TRUE; return(FALSE); } jbAddNode(list,(struct jbNode *)path); path->Next=NULL; path->Paths=0; } /* Always net/node when a new line */ sprintf(path->Path[path->Paths],"%u/%u",node->Net,node->Node); if(node->Point != 0) { sprintf(buf2,".%u",node->Point); strcat(path->Path[path->Paths],buf2); } path->Paths++; return(TRUE); } char *StripRe(char *str) { for (;;) { if(strnicmp (str, "Re:", 3)==0) { str += 3; if (*str == ' ') str++; } else if(strnicmp (str, "Re^", 3)==0 && str[4]==':') { str += 5; if (*str == ' ') str++; } else if(strnicmp (str, "Re[", 3)==0 && str[4]==']' && str[5]==':') { str += 6; if (*str == ' ') str++; } else break; } return (str); } bool HandleEchomail(struct MemMessage *mm) { struct Area *temparea; struct TossNode *temptnode; struct AddNode *tmpaddnode; struct RemNode *tmpremnode; struct ConfigNode *tempcnode; mm->Type=PKTS_ECHOMAIL; /* Find orignode */ for(tempcnode=(struct ConfigNode *)config.CNodeList.First;tempcnode;tempcnode=tempcnode->Next) if(Compare4D(&mm->PktOrig,&tempcnode->Node)==0) break; /* Find area */ for(temparea=(struct Area *)config.AreaList.First;temparea;temparea=temparea->Next) if(stricmp(temparea->Tagname,mm->Area)==0) break; /* Auto-add */ if(!temparea) { if(tempcnode) temparea=AddArea(mm->Area,&mm->PktOrig,&mm->PktDest,tempcnode->Flags & NODE_AUTOADD,FALSE); else temparea=AddArea(mm->Area,&mm->PktOrig,&mm->PktDest,FALSE,FALSE); if(!temparea) return(FALSE); if(temparea->Flags & AREA_UNCONFIRMED) LogWrite(3,TOSSINGERR,"Unknown area %s",mm->Area); else LogWrite(3,TOSSINGINFO,"Unknown area %s -- auto-adding",mm->Area); } /* Don't toss in auto-added areas */ if(temparea->Flags & AREA_UNCONFIRMED) { toss_bad++; if(!WriteBad(mm,"Unknown area (auto-added but not confirmed)")) return(FALSE); return(TRUE); } /* Check if the node receives this area */ if(!(mm->Flags & MMFLAG_EXPORTED) && !(mm->Flags & MMFLAG_NOSECURITY)) { for(temptnode=(struct TossNode *)temparea->TossNodes.First;temptnode;temptnode=temptnode->Next) if(Compare4D(&temptnode->ConfigNode->Node,&mm->PktOrig)==0) break; if(!temptnode) { LogWrite(1,TOSSINGERR,"%u:%u/%u.%u doesn't receive %s", mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->Area); toss_bad++; if(!WriteBad(mm,"Node does not receive this area")) return(FALSE); return(TRUE); } if(temptnode->Flags & TOSSNODE_READONLY) { LogWrite(1,TOSSINGERR,"%u:%u/%u.%u is not allowed to write in %s", mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->Area); toss_bad++; if(!WriteBad(mm,"Node is not allowed to write in this area")) return(FALSE); return(TRUE); } } /* Remove all seen-by:s if the message comes from an other zone */ if(temparea->Aka->Node.Zone != mm->PktOrig.Zone && mm->SeenBy.First) jbFreeList(&mm->SeenBy); /* Check if a node already is in seen-by */ if((config.cfg_Flags & CFG_CHECKSEENBY) && !(temparea->Flags & AREA_IGNORESEENBY)) { for(temptnode=(struct TossNode *)temparea->TossNodes.First;temptnode;temptnode=temptnode->Next) { temptnode->ConfigNode->IsInSeenBy=FALSE; if(temptnode->ConfigNode->Node.Zone == temparea->Aka->Node.Zone) if(temptnode->ConfigNode->Node.Point==0) if(FindNodes2D(&mm->SeenBy,&temptnode->ConfigNode->Node)) temptnode->ConfigNode->IsInSeenBy=TRUE; } } /* Add nodes to seen-by */ for(temptnode=(struct TossNode *)temparea->TossNodes.First;temptnode;temptnode=temptnode->Next) if(temptnode->ConfigNode->Node.Point == 0 && temparea->Aka->Node.Zone == temptnode->ConfigNode->Node.Zone) if(!(temptnode->ConfigNode->Flags & NODE_PASSIVE)) if(!(temptnode->Flags & TOSSNODE_WRITEONLY)) { if(!mmAddNodes2DList(&mm->SeenBy,temptnode->ConfigNode->Node.Net,temptnode->ConfigNode->Node.Node)) return(FALSE); } /* Remove nodes specified in config from seen-by and path */ for(tmpremnode=(struct RemNode *)temparea->Aka->RemList.First;tmpremnode;tmpremnode=tmpremnode->Next) mmRemNodes2DListPat(&mm->SeenBy,&tmpremnode->NodePat); /* Add nodes specified in config to seen-by */ for(tmpaddnode=(struct AddNode *)temparea->Aka->AddList.First;tmpaddnode;tmpaddnode=tmpaddnode->Next) mmAddNodes2DList(&mm->SeenBy,tmpaddnode->Node.Net,tmpaddnode->Node.Node); /* Add own node to seen-by */ if(temparea->Aka->Node.Point == 0) { if(!mmAddNodes2DList(&mm->SeenBy,temparea->Aka->Node.Net,temparea->Aka->Node.Node)) return(FALSE); } /* Add own node to path */ if(temparea->Aka->Node.Point == 0 || (config.cfg_Flags & CFG_PATH3D)) { if(!AddNodePath(&mm->Path,&temparea->Aka->Node)) return(FALSE); } /* Dupecheck */ if(config.cfg_DupeMode!=DUPE_IGNORE && (mm->Flags & MMFLAG_TOSSED) && !(temparea->Flags & AREA_IGNOREDUPES)) { if(CheckDupe(mm)) { LogWrite(4,TOSSINGERR,"Duplicate message in %s",mm->Area); toss_dupes++; temparea->NewDupes++; if(tempcnode) tempcnode->Dupes++; if(config.cfg_DupeMode == DUPE_BAD) { if(!WriteBad(mm,"Duplicate message")) return(FALSE); } return(TRUE); } } if(!mmSortNodes2D(&mm->SeenBy)) return(FALSE); /* Filter */ if(!Filter(mm)) return(FALSE); if(mm->Flags & MMFLAG_KILL) return(TRUE); temparea->NewTexts++; /* Write to all nodes */ if(!(mm->Flags & MMFLAG_RESCANNED)) { /* not rescanned */ for(temptnode=(struct TossNode *)temparea->TossNodes.First;temptnode;temptnode=temptnode->Next) /* is not sender of packet */ if(Compare4D(&mm->PktOrig,&temptnode->ConfigNode->Node)!=0) /* is not passive */ if(!(temptnode->ConfigNode->Flags & NODE_PASSIVE)) /* is not write-only */ if(!(temptnode->Flags & TOSSNODE_WRITEONLY)) /* is not already in seen-by */ if(!(temptnode->ConfigNode->IsInSeenBy == TRUE && (config.cfg_Flags & CFG_CHECKSEENBY))) { if(!WriteEchoMail(mm,temptnode->ConfigNode,temparea->Aka)) return(FALSE); } } if(mm->Flags & MMFLAG_TWIT) return(TRUE); if(!(mm->Flags & MMFLAG_EXPORTED) && temparea->Messagebase) { toss_import++; if(config.cfg_Flags & CFG_STRIPRE) strcpy(mm->Subject,StripRe(mm->Subject)); /* Remove LOCAL flag if set and set SENT flag */ mm->Attr |= FLAG_SENT; mm->Attr &= ~(FLAG_LOCAL); if(!(*temparea->Messagebase->importfunc)(mm,temparea)) return(FALSE); } return(TRUE); } /******************************* netmail **********************************/ /* For loop-mail checking */ bool CheckFoundAka(char *str) { struct Node4D via4d; struct Aka *aka; if(!(strstr(str,":") && strstr(str,"/"))) return(FALSE); if(!Parse4D(str,&via4d)) return(FALSE); if(via4d.Zone==0 || via4d.Net==0) return(FALSE); for(aka=(struct Aka *)config.AkaList.First;aka;aka=aka->Next) if(Compare4D(&aka->Node,&via4d)==0) return(TRUE); return(FALSE); } bool IsLoopMail(struct MemMessage *mm) { struct TextChunk *tmp; uint16_t q; uint32_t c,d; for(tmp=(struct TextChunk *)mm->TextChunks.First;tmp;tmp=tmp->Next) { c=0; while(cLength) { for(d=c;dLength && tmp->Data[d]!=13;d++); if(tmp->Data[d]==13) d++; if(strncmp(&tmp->Data[c],"\x01Via",4)==0) { /* Is ^aVia line */ char via[200]; if(d-c<150) q=d-c; else q=150; strncpy(via,&tmp->Data[c],q); via[q]=0; if(strstr(via,"CrashMail")) { /* Is created by CrashMail */ char destbuf[20]; uint16_t u,v; v=0; for(u=0;via[u]!=0;u++) { if(via[u]==':' || via[u]=='/' || via[u]=='.' || (via[u]>='0' && via[u]<='9')) { if(v<19) destbuf[v++]=via[u]; } else { if(v!=0) { destbuf[v]=0; if(CheckFoundAka(destbuf)) return(TRUE); } v=0; } } if(v!=0) { destbuf[v]=0; if(CheckFoundAka(destbuf)) return(TRUE); } } } c=d; } } return(FALSE); } /* Bouncing and receipts */ bool Bounce(struct MemMessage *mm,char *reason,bool headeronly) { char buf[400],*tmpbuf; uint32_t c; struct Route *tmproute; struct MemMessage *tmpmm; struct TextChunk *chunk; struct Node4D n4d; if(mm->Flags & MMFLAG_AUTOGEN) { LogWrite(1,TOSSINGERR,"No bounce messages sent for messages created by CrashMail"); return(TRUE); } if(mm->Area[0] == 0) Copy4D(&n4d,&mm->OrigNode); else Copy4D(&n4d,&mm->Origin4D); for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&n4d)==0) break; if(!tmproute) { Print4D(&n4d,buf); LogWrite(1,TOSSINGERR,"Can't bounce message, no routing for %s",buf); return(TRUE); } if(!(tmpmm=mmAlloc())) return(FALSE); Copy4D(&tmpmm->DestNode,&n4d); Copy4D(&tmpmm->OrigNode,&tmproute->Aka->Node); strcpy(tmpmm->To,mm->From); strcpy(tmpmm->From,config.cfg_Sysop); strcpy(tmpmm->Subject,"Bounced message"); tmpmm->Attr=FLAG_PVT; MakeFidoDate(time(NULL),tmpmm->DateTime); tmpmm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(tmpmm); if(config.cfg_Flags & CFG_ADDTID) AddTID(tmpmm); strncpy(buf,reason,390); strcat(buf,"\x0d\x0d"); mmAddLine(tmpmm,buf); if(headeronly) mmAddLine(tmpmm,"This is the header of the message that was bounced:\x0d\x0d"); else mmAddLine(tmpmm,"This is the message that was bounced:\x0d\x0d"); if(mm->Area[0]) { sprintf(buf,"Area: %.80s\x0d",mm->Area); mmAddLine(tmpmm,buf); sprintf(buf,"From: %.40s (%u:%u/%u.%u)\x0d",mm->From, n4d.Zone, n4d.Net, n4d.Node, n4d.Point); mmAddLine(tmpmm,buf); sprintf(buf," To: %.40s\x0d",mm->To); mmAddLine(tmpmm,buf); } else { sprintf(buf,"From: %.40s (%u:%u/%u.%u)\x0d",mm->From, n4d.Zone, n4d.Net, n4d.Node, n4d.Point); mmAddLine(tmpmm,buf); sprintf(buf," To: %.40s (%u:%u/%u.%u)\x0d", mm->To,mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point); mmAddLine(tmpmm,buf); } sprintf(buf,"Subj: %s\x0d",mm->Subject); mmAddLine(tmpmm,buf); sprintf(buf,"Date: %s\x0d\x0d",mm->DateTime); mmAddLine(tmpmm,buf); if(!headeronly) { for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) { if(chunk->Length) { if(!(tmpbuf=(char *)osAlloc(chunk->Length))) { nomem=TRUE; mmFree(tmpmm); return(FALSE); } for(c=0;cLength;c++) { if(chunk->Data[c]==1) tmpbuf[c]='@'; else tmpbuf[c]=chunk->Data[c]; } mmAddBuf(&tmpmm->TextChunks,tmpbuf,chunk->Length); osFree(tmpbuf); } } } if(!HandleMessage(tmpmm)) { mmFree(tmpmm); return(FALSE); } mmFree(tmpmm); return(TRUE); } bool AnswerReceipt(struct MemMessage *mm) { char buf[400]; struct Route *tmproute; struct MemMessage *tmpmm; LogWrite(4,TOSSINGINFO,"Answering to a receipt request"); for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&mm->OrigNode)==0) break; if(!tmproute) { Print4D(&mm->OrigNode,buf); LogWrite(1,TOSSINGERR,"Can't send receipt, no routing for %s",buf); return(TRUE); } if(!(tmpmm=mmAlloc())) return(FALSE); Copy4D(&tmpmm->DestNode,&mm->OrigNode); Copy4D(&tmpmm->OrigNode,&tmproute->Aka->Node); strcpy(tmpmm->To,mm->From); strcpy(tmpmm->From,config.cfg_Sysop); strcpy(tmpmm->Subject,"Receipt"); tmpmm->Attr=FLAG_PVT | FLAG_IRRR; MakeFidoDate(time(NULL),tmpmm->DateTime); mm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(tmpmm); if(config.cfg_Flags & CFG_ADDTID) AddTID(tmpmm); sprintf(buf,"Your message to %s dated %s with the subject \"%s\" has reached its final " "destination. This message doesn't mean that the message has been read, it " "just tells you that it has arrived at this system.\x0d\x0d", mm->To,mm->DateTime,mm->Subject); mmAddLine(tmpmm,buf); if(!HandleMessage(tmpmm)) { mmFree(tmpmm); return(FALSE); } mmFree(tmpmm); return(TRUE); } bool AnswerAudit(struct MemMessage *mm) { char buf[200],auditbuf[500]; struct Route *tmproute,*destroute; struct MemMessage *tmpmm; struct Node4D n4d; LogWrite(4,TOSSINGINFO,"Answering to an audit request"); for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&mm->OrigNode)==0) break; if(!tmproute) { Print4D(&mm->OrigNode,buf); LogWrite(1,TOSSINGERR,"Can't send receipt, no routing for %s",buf); return(TRUE); } if(!(tmpmm=mmAlloc())) return(FALSE); Copy4D(&tmpmm->DestNode,&mm->OrigNode); Copy4D(&tmpmm->OrigNode,&tmproute->Aka->Node); strcpy(tmpmm->To,mm->From); strcpy(tmpmm->From,config.cfg_Sysop); strcpy(tmpmm->Subject,"Audit"); tmpmm->Attr=FLAG_PVT; MakeFidoDate(time(NULL),tmpmm->DateTime); tmpmm->Flags |= MMFLAG_AUTOGEN; MakeNetmailKludges(tmpmm); if(config.cfg_Flags & CFG_ADDTID) AddTID(tmpmm); for(destroute=(struct Route *)config.RouteList.First;destroute;destroute=destroute->Next) if(Compare4DPat(&destroute->Pattern,&mm->DestNode)==0) break; if(destroute) { ExpandNodePat(&destroute->DestPat,&mm->DestNode,&n4d); sprintf(auditbuf,"Your message to %s dated %s with the subject \"%s\" has just been " "routed to %u:%u/%u.%u by this system.\x0d\x0d", mm->To,mm->DateTime,mm->Subject, n4d.Zone,n4d.Net,n4d.Node,n4d.Point); } else { Copy4D(&n4d,&mm->DestNode); sprintf(auditbuf,"Your message to %s dated %s with the subject \"%s\" could not be " "routed since no routing for %u:%u/%u.%u was configured at this " "system. Message lost! \x0d\x0d", mm->To,mm->DateTime,mm->Subject, n4d.Zone,n4d.Net,n4d.Node,n4d.Point); } mmAddLine(tmpmm,auditbuf); if(!HandleMessage(tmpmm)) { mmFree(tmpmm); return(FALSE); } mmFree(tmpmm); return(TRUE); } /* Main netmail handling */ bool HandleNetmail(struct MemMessage *mm) { struct Area *tmparea; struct Aka *aka; struct ConfigNode *cnode,*pktcnode; struct ImportNode *inode; struct Route *tmproute; struct Node4D n4d,Dest4D; struct PatternNode *patternnode; struct AreaFixName *areafixname; struct TextChunk *tmpchunk,*chunk; bool istext; char buf[400],buf2[200],buf3[200],subjtemp[80]; uint32_t c,d,jbcpos; time_t t; struct tm *tp; uint32_t size; uint8_t oldtype; bool headeronly; /* Find orignode */ for(pktcnode=(struct ConfigNode *)config.CNodeList.First;pktcnode;pktcnode=pktcnode->Next) if(Compare4D(&mm->PktOrig,&pktcnode->Node)==0) break; /* Calculate size */ size=0; for(tmpchunk=(struct TextChunk *)mm->TextChunks.First;tmpchunk;tmpchunk=tmpchunk->Next) size+=tmpchunk->Length; /* Statistics */ if((mm->Flags & MMFLAG_TOSSED) && pktcnode) { pktcnode->GotNetmails++; pktcnode->GotNetmailBytes+=size; } /* Set zones if they are zero */ if(mm->DestNode.Zone == 0) mm->DestNode.Zone = mm->PktDest.Zone; if(mm->OrigNode.Zone == 0) mm->OrigNode.Zone = mm->PktOrig.Zone; /* Add CR if last line doesn't end with CR */ chunk=(struct TextChunk *)mm->TextChunks.First; if(chunk && chunk->Length!=0) { if(chunk->Data[chunk->Length-1] != 13 && chunk->Data[chunk->Length-1]) mmAddBuf(&mm->TextChunks,"\x0d",1); } /* Filter */ if(!Filter(mm)) return(FALSE); if(mm->Flags & MMFLAG_KILL) return(TRUE); /* Check if it is to me */ for(aka=(struct Aka *)config.AkaList.First;aka;aka=aka->Next) if(Compare4D(&mm->DestNode,&aka->Node)==0) break; if(aka) { /* AreaFix */ if(!(mm->Flags & MMFLAG_AUTOGEN)) { for(areafixname=(struct AreaFixName *)config.AreaFixList.First;areafixname;areafixname=areafixname->Next) if(stricmp(areafixname->Name,mm->To)==0) break; if(areafixname) { if(!AreaFix(mm)) return(FALSE); if(!(config.cfg_Flags & CFG_IMPORTAREAFIX)) return(TRUE); } } } /* Find correct area */ for(tmparea=(struct Area *)config.AreaList.First;tmparea;tmparea=tmparea->Next) if(tmparea->AreaType == AREATYPE_NETMAIL) { if(Compare4D(&tmparea->Aka->Node,&mm->DestNode)==0) break; for(inode=(struct ImportNode *)tmparea->TossNodes.First;inode;inode=inode->Next) if(Compare4D(&inode->Node,&mm->DestNode)==0) break; if(inode) break; } /* If no area was found but it is to one of the akas, take first netmail area */ /* Same if NOROUTE was specified in config */ if(!tmparea) { for(aka=(struct Aka *)config.AkaList.First;aka;aka=aka->Next) if(Compare4D(&mm->DestNode,&aka->Node)==0) break; if(aka || (config.cfg_Flags & CFG_NOROUTE)) { for(tmparea=(struct Area *)config.AreaList.First;tmparea;tmparea=tmparea->Next) if(tmparea->AreaType == AREATYPE_NETMAIL) break; } } if(tmparea) { /* Import netmail */ if(mm->Flags & MMFLAG_TWIT) return(TRUE); if(config.cfg_Flags & CFG_STRIPRE) strcpy(mm->Subject,StripRe(mm->Subject)); /* Import empty netmail? */ istext=TRUE; if(!(config.cfg_Flags & CFG_IMPORTEMPTYNETMAIL)) { istext=FALSE; for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk && !istext;chunk=chunk->Next) for(c=0;cLength && !istext;) { if(chunk->Data[c]!=1) istext=TRUE; while(chunk->Data[c]!=13 && cLength) c++; if(chunk->Data[c]==13) c++; } } if(istext) { if(!(mm->Flags & MMFLAG_TOSSED)) LogWrite(3,TOSSINGINFO,"Importing message"); tmparea->NewTexts++; if(tmparea->Messagebase) { toss_import++; if(config.cfg_Flags & CFG_STRIPRE) strcpy(mm->Subject,StripRe(mm->Subject)); /* Remove LOCAL flag if set and set SENT flag */ mm->Attr |= FLAG_SENT; mm->Attr &= ~(FLAG_LOCAL); if(!(*tmparea->Messagebase->importfunc)(mm,tmparea)) return(FALSE); } } else { Print4D(&mm->OrigNode,buf); LogWrite(4,TOSSINGINFO,"Killed empty netmail from %s at %s",mm->From,buf); } if((mm->Attr & FLAG_RREQ) && (config.cfg_Flags & CFG_ANSWERRECEIPT)) { if(!AnswerReceipt(mm)) return(FALSE); } } else { /* Clear flags */ mm->Attr&=(FLAG_PVT|FLAG_CRASH|FLAG_FILEATTACH|FLAG_HOLD|FLAG_RREQ|FLAG_IRRR|FLAG_AUDIT); if(mm->Flags & MMFLAG_TOSSED) mm->Attr&=~(FLAG_CRASH|FLAG_HOLD); mm->Type = PKTS_NORMAL; if(mm->Attr & FLAG_CRASH) mm->Type=PKTS_CRASH; if(mm->Attr & FLAG_HOLD) mm->Type=PKTS_HOLD; /* File-attach? */ if((mm->Attr & FLAG_FILEATTACH) && !(config.cfg_Flags & CFG_NODIRECTATTACH)) { if(mm->Type == PKTS_NORMAL) mm->Type=PKTS_DIRECT; } /* Find route statement */ for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,&mm->DestNode)==0) break; if(!tmproute) { LogWrite(1,TOSSINGERR,"No routing configured for %u:%u/%u.%u - message lost", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point); toss_bad++; if(!WriteBad(mm,"No routing for destination node")) return(FALSE); return(TRUE); } /* Set destination */ if(mm->Type == PKTS_NORMAL) { char buf1[50],buf2[50],buf3[50]; Print4DPat(&tmproute->Pattern,buf1); Print4DPat(&tmproute->DestPat,buf2); Print4D(&tmproute->Aka->Node,buf3); LogWrite(6,DEBUG,"Uses this route statement: ROUTE \"%s\" \"%s\" \"%s\"",buf1,buf2,buf3); ExpandNodePat(&tmproute->DestPat,&mm->DestNode,&Dest4D); } else { Copy4D(&Dest4D,&mm->DestNode); } /* Change */ oldtype=mm->Type; mm->Type=ChangeType(&Dest4D,mm->Type); if(mm->Type != oldtype) { LogWrite(4,TOSSINGINFO,"Changed priority for netmail to %u:%u/%u.%u from %s to %s", Dest4D.Zone,Dest4D.Net,Dest4D.Node,Dest4D.Point, prinames[oldtype],prinames[mm->Type]); } if(Dest4D.Point != 0) { if(mm->Type == PKTS_DIRECT || mm->Type == PKTS_CRASH) { Dest4D.Point=0; LogWrite(4,TOSSINGINFO,"Cannot send %s to a point, sending to %u:%u/%u.%u instead", prinames[mm->Type],Dest4D.Zone,Dest4D.Net,Dest4D.Node,Dest4D.Point); } } /* Check for loopmail */ if(config.cfg_LoopMode != LOOP_IGNORE && !(mm->Flags & MMFLAG_EXPORTED)) { if(IsLoopMail(mm)) { LogWrite(1,TOSSINGERR,"Possible loopmail detected: Received from %u:%u/%u.%u, to %u:%u/%u.%u", mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point); if(config.cfg_LoopMode == LOOP_LOGBAD) { struct MemMessage *tmpmm; struct TextChunk *tmp; /* Make a copy of the message with only kludge lines */ if(!(tmpmm=mmAlloc())) return(FALSE); Copy4D(&tmpmm->PktOrig,&mm->PktOrig); Copy4D(&tmpmm->PktDest,&mm->PktDest); Copy4D(&tmpmm->OrigNode,&mm->OrigNode); Copy4D(&tmpmm->DestNode,&mm->DestNode); strcpy(tmpmm->Area,mm->Area); strcpy(tmpmm->To,mm->To); strcpy(tmpmm->From,mm->From); strcpy(tmpmm->Subject,mm->Subject); strcpy(tmpmm->DateTime,mm->DateTime); strcpy(tmpmm->MSGID,mm->MSGID); strcpy(tmpmm->REPLY,mm->REPLY); tmpmm->Attr=mm->Attr; tmpmm->Cost=mm->Cost; tmpmm->Type=mm->Type; tmpmm->Flags=mm->Flags; for(tmp=(struct TextChunk *)mm->TextChunks.First;tmp;tmp=tmp->Next) { c=0; while(cLength) { for(d=c;dLength && tmp->Data[d]!=13;d++); if(tmp->Data[d]==13) d++; if(tmp->Data[c]==1) { tmp->Data[c]='@'; mmAddBuf(&tmpmm->TextChunks,&tmp->Data[c],d-c); tmp->Data[c]=1; } c=d; } } toss_bad++; if(!WriteBad(tmpmm,"Possible loopmail")) return(FALSE); mmFree(tmpmm); } } } /* Bounce if not in nodelist */ if(config.cfg_NodelistType) { for(patternnode=(struct PatternNode *)config.BounceList.First;patternnode;patternnode=patternnode->Next) if(Compare4DPat(&patternnode->Pattern,&mm->DestNode)==0) break; if(patternnode) { struct Node4D node; Copy4D(&node,&mm->DestNode); node.Point=0; if(!(*config.cfg_NodelistType->nlCheckNode)(&node)) { LogWrite(3,TOSSINGERR,"Bounced message from %u:%u/%u.%u to %u:%u/%u.%u -- not in nodelist", mm->OrigNode.Zone, mm->OrigNode.Net, mm->OrigNode.Node, mm->OrigNode.Point, mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point); sprintf(buf, "Warning! Your message has been bounced because the node %u:%u/%u doesn't exist in the nodelist.", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node); headeronly=FALSE; if(config.cfg_Flags & CFG_BOUNCEHEADERONLY) headeronly=TRUE; if(!Bounce(mm,buf,headeronly)) return(FALSE); return(TRUE); } } } /* Bounce if unconfigured point */ if(config.cfg_Flags & CFG_BOUNCEPOINTS) { Copy4D(&n4d,&mm->DestNode); n4d.Point=0; for(aka=(struct Aka *)config.AkaList.First;aka;aka=aka->Next) { if(Compare4D(&aka->Node,&n4d)==0 && aka->Node.Point==0) { for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,&mm->DestNode)==0) break; if(!cnode) { LogWrite(3,TOSSINGERR,"Bounced message from %u:%u/%u.%u to %u:%u/%u.%u -- unknown point", mm->OrigNode.Zone, mm->OrigNode.Net, mm->OrigNode.Node, mm->OrigNode.Point, mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point); sprintf(buf,"Warning! Your message has been bounced because the point %u:%u/%u.%u doesn't exist.", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point); headeronly=FALSE; if(config.cfg_Flags & CFG_BOUNCEHEADERONLY) headeronly=TRUE; if(!Bounce(mm,buf,headeronly)) return(FALSE); return(TRUE); } } } } /* Handle file-attach */ if(mm->Attr & FLAG_FILEATTACH) { LogWrite(6,DEBUG,"Netmail is fileattach"); if(!(mm->Flags & MMFLAG_EXPORTED)) { for(patternnode=(struct PatternNode *)config.FileAttachList.First;patternnode;patternnode=patternnode->Next) if(Compare4DPat(&patternnode->Pattern,&mm->DestNode)==0) break; if(!patternnode) { LogWrite(3,TOSSINGERR,"Refused to route file-attach from %u:%u/%u.%u to %u:%u/%u.%u",mm->OrigNode.Zone,mm->OrigNode.Net,mm->OrigNode.Node,mm->OrigNode.Point, mm->DestNode.Zone,mm->DestNode.Net,mm->DestNode.Node,mm->DestNode.Point); sprintf(buf,"Warning! Your message has been bounced because because routing of file-attaches to %u:%u/%u.%u is not allowed.", mm->DestNode.Zone,mm->DestNode.Net,mm->DestNode.Node,mm->DestNode.Point); headeronly=FALSE; if(config.cfg_Flags & CFG_BOUNCEHEADERONLY) headeronly=TRUE; if(!Bounce(mm,buf,headeronly)) return(FALSE); return(TRUE); } } strcpy(subjtemp,mm->Subject); mm->Subject[0]=0; for(c=0;subjtemp[c];c++) { if(subjtemp[c]==',') subjtemp[c]=' '; if(subjtemp[c]=='\\') subjtemp[c]='/'; } jbcpos=0; while(jbstrcpy(buf,subjtemp,80,&jbcpos)) { if(mm->Subject[0] != 0) strcat(mm->Subject," "); strcat(mm->Subject,GetFilePart(buf)); LogWrite(4,TOSSINGINFO,"Routing file %s to %u:%u/%u.%u",GetFilePart(buf),Dest4D.Zone,Dest4D.Net,Dest4D.Node,Dest4D.Point); if((mm->Flags & MMFLAG_EXPORTED)) { if(osExists(buf)) { MakeFullPath(config.cfg_PacketDir,GetFilePart(buf),buf2,200); copyfile(buf,buf2); if(nomem || ioerror) return(FALSE); AddFlow(buf2,&Dest4D,mm->Type,FLOW_DELETE); } else { AddFlow(buf,&Dest4D,mm->Type,FLOW_NONE); } } else { MakeFullPath(config.cfg_Inbound,GetFilePart(buf),buf2,200); MakeFullPath(config.cfg_PacketDir,GetFilePart(buf),buf3,200); if(movefile(buf2,buf3)) { AddFlow(buf3,&Dest4D,mm->Type,FLOW_DELETE); } else { AddFlow(buf2,&Dest4D,mm->Type,FLOW_DELETE); } } } } time(&t); tp = localtime(&t); Print4D(&tmproute->Aka->Node,buf2); sprintf(buf,"\x01Via %s @%04u%02u%02u.%02u%02u%02u CrashMail II/" OS_PLATFORM_NAME " " VERSION "\x0d", buf2, tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); mmAddLine(mm,buf); if(mm->Type == PKTS_NORMAL) { LogWrite(5,TOSSINGINFO,"Routing message to %u:%u/%u.%u via %u:%u/%u.%u", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point, Dest4D.Zone, Dest4D.Net, Dest4D.Node, Dest4D.Point); } else { LogWrite(5,TOSSINGINFO,"Sending message directly to %u:%u/%u.%u", Dest4D.Zone, Dest4D.Net, Dest4D.Node, Dest4D.Point); } if(!WriteNetMail(mm,&Dest4D,tmproute->Aka)) return(FALSE); if((mm->Attr & FLAG_AUDIT) && (config.cfg_Flags & CFG_ANSWERAUDIT) && (mm->Flags & MMFLAG_TOSSED)) { if(!AnswerAudit(mm)) return(FALSE); } toss_route++; } return(TRUE); } /******************************* end netmail **********************************/ /********************************** Rescan *******************************/ bool HandleRescan(struct MemMessage *mm) { struct Area *temparea; rescan_total++; /* Find area */ for(temparea=(struct Area *)config.AreaList.First;temparea;temparea=temparea->Next) if(stricmp(temparea->Tagname,mm->Area)==0) break; /* Add own node to seen-by to be on the safe side */ if(temparea->Aka->Node.Point == 0) { if(!mmAddNodes2DList(&mm->SeenBy,temparea->Aka->Node.Net,temparea->Aka->Node.Node)) return(FALSE); } /* Add destination node to seen-by to be on the safe side */ if(RescanNode->Node.Point == 0) { if(!mmAddNodes2DList(&mm->SeenBy,RescanNode->Node.Net,RescanNode->Node.Node)) return(FALSE); } /* Add own node to path */ if(temparea->Aka->Node.Point == 0 || (config.cfg_Flags & CFG_PATH3D)) { if(!AddNodePath(&mm->Path,&temparea->Aka->Node)) return(FALSE); } if(!mmSortNodes2D(&mm->SeenBy)) return(FALSE); if(!WriteEchoMail(mm,RescanNode,temparea->Aka)) return(FALSE); return(TRUE); } crashmail-1.5/src/crashmail/handle.h000066400000000000000000000006121230617144500174620ustar00rootroot00000000000000bool HandleMessage(struct MemMessage *mm); bool HandleRescan(struct MemMessage *mm); struct Area *AddArea(char *name,struct Node4D *node,struct Node4D *mynode,uint32_t active,uint32_t forcepassthru); bool AddTossNode(struct Area *area,struct ConfigNode *cnode,uint16_t flags); bool WriteBad(struct MemMessage *mm,char *reason); bool Bounce(struct MemMessage *mm,char *reason,bool headeronly); crashmail-1.5/src/crashmail/logwrite.c000066400000000000000000000044471230617144500200700ustar00rootroot00000000000000#include "crashmail.h" #ifdef OS_HAS_SYSLOG #include bool usesyslog; int syslogpri[] = { LOG_INFO, /* SYSTEMINFO */ LOG_ERR, /* SYSTEMERR */ LOG_INFO, /* TOSSINGINFO */ LOG_ERR, /* TOSSINGERR */ LOG_INFO, /* MISCINFO */ LOG_DEBUG, /* DEBUG */ LOG_INFO, /* AREAFIX */ LOG_INFO, /* ACTIONINFO */ LOG_ERR /* USERERR */ }; #endif osFile logfh; bool OpenLogfile(char *logfile) { #ifdef OS_HAS_SYSLOG if(stricmp(logfile,"syslog")==0) { usesyslog=TRUE; openlog("CrashMail",0,LOG_USER); return(TRUE); } #endif if(!(logfh=osOpen(logfile,MODE_READWRITE))) { uint32_t err=osError(); printf("Failed to open logfile %s\n",config.cfg_LogFile); printf("Error: %s\n",osErrorMsg(err)); return(FALSE); } osSeek(logfh,0,OFFSET_END); return(TRUE); } void CloseLogfile(void) { #ifdef OS_HAS_SYSLOG if(usesyslog) { closelog(); usesyslog=FALSE; return; } #endif osClose(logfh); } char *categoryletters="-%=!/D+^?"; void LogWrite(uint32_t level,uint32_t category,char *fmt,...) { va_list args; time_t t; struct tm *tp; char *monthnames[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; char buf[500]; int i; if(level > config.cfg_LogLevel) return; if(level == 0) LogWrite(6,DEBUG,"*** Warning: Loglevel is 0!!! ***"); if(fmt[0]==0) { printf("\n"); return; } if(handle_nesting > 1 && handle_nesting + strlen(fmt) < 499) { buf[0]=0; for(i=1;itm_mday, monthnames[tp->tm_mon], tp->tm_year%100, tp->tm_hour, tp->tm_min, tp->tm_sec); va_start(args, fmt); osVFPrintf(logfh,fmt,args); osFPrintf(logfh,"\n"); va_end(args); } crashmail-1.5/src/crashmail/logwrite.h000066400000000000000000000005451230617144500200700ustar00rootroot00000000000000#include #define SYSTEMINFO 0 #define SYSTEMERR 1 #define TOSSINGINFO 2 #define TOSSINGERR 3 #define MISCINFO 4 #define DEBUG 5 #define AREAFIX 6 #define ACTIONINFO 7 #define USERERR 8 bool OpenLogfile(char *filename); void CloseLogfile(void); void LogWrite(uint32_t level,uint32_t category,char *fmt,...); crashmail-1.5/src/crashmail/mb.c000066400000000000000000000012521230617144500166210ustar00rootroot00000000000000#include "crashmail.h" #ifdef MSGBASE_MSG #include "mb_msg.h" #endif #ifdef MSGBASE_JAM #include "mb_jam.h" #endif struct Messagebase AvailMessagebases[] = { #ifdef MSGBASE_MSG { "MSG", "Standard *.msg messagebase as specified in FTS-1", 0, msg_beforefunc, msg_afterfunc, msg_importfunc, msg_exportfunc, msg_rescanfunc }, #endif #ifdef MSGBASE_JAM { "JAM", "JAM Messagebase", 0, jam_beforefunc, jam_afterfunc, jam_importfunc, jam_exportfunc, jam_rescanfunc }, #endif { NULL, /* NULL here marks the end of the array */ NULL, 0, NULL, NULL, NULL, NULL, NULL } }; crashmail-1.5/src/crashmail/mb.h000066400000000000000000000007461230617144500166350ustar00rootroot00000000000000#ifndef MB_H #define MB_H #include "shared/types.h" #include "memmessage.h" #include "config.h" struct Messagebase { char *name; char *desc; bool active; bool (*beforefunc)(void); bool (*afterfunc)(bool success); bool (*importfunc)(struct MemMessage *mm,struct Area *area); bool (*exportfunc)(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)); bool (*rescanfunc)(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)); }; #endif crashmail-1.5/src/crashmail/mb_jam.c000066400000000000000000000772031230617144500174610ustar00rootroot00000000000000#include #include "crashmail.h" #define MIN(a,b) ((a)<(b)? (a):(b)) struct openbase { uint32_t lastuse; s_JamBase* Base_PS; struct jam_Area *area; }; struct jam_Area { struct jam_Area *Next; struct Area *area; s_JamBase *Base_PS; uint32_t BaseNum; uint32_t OldNum; uint32_t OldHighWater; uint32_t HighWater; bool newmsg; }; struct jbList jam_AreaList; struct openbase *jam_openbases; uint32_t jam_lastnum; long jam_utcoffset; int jam_linkmb(struct Area *area,uint32_t oldnum); s_JamBase *jam_openbase(struct jam_Area *area) { int c; struct openbase *thisbase; /* See if that area already is open */ for(c=0;clastuse) thisbase=&jam_openbases[c]; thisbase->lastuse=0; JAM_CloseMB(thisbase->Base_PS); free(thisbase->Base_PS); } /* Open area */ if(JAM_OpenMB(area->area->Path,&thisbase->Base_PS)) { if(thisbase->Base_PS) free(thisbase->Base_PS); LogWrite(2,SYSTEMINFO,"Creating JAM messagebase \"%s\"",area->area->Path); if(JAM_CreateMB(area->area->Path,1,&thisbase->Base_PS)) { if(thisbase->Base_PS) free(thisbase->Base_PS); LogWrite(1,SYSTEMERR,"Failed to create JAM messagebase \"%s\"",area->area->Path); return(NULL); } } /* Set the rest */ thisbase->lastuse=jam_lastnum++; thisbase->area=area; return(thisbase->Base_PS); } struct jam_Area *jam_getarea(struct Area *area) { struct jam_Area *ja; uint32_t num; s_JamBaseHeader Header_S; /* Check if area already exists */ for(ja=(struct jam_Area *)jam_AreaList.First;ja;ja=ja->Next) if(ja->area == area) { if(!(ja->Base_PS=jam_openbase(ja))) return(NULL); return(ja); } /* This is the first time we use this area */ if(!(ja=osAllocCleared(sizeof(struct jam_Area)))) { nomem=TRUE; return(FALSE); } jbAddNode(&jam_AreaList,(struct jbNode *)ja); ja->area=area; if(!(ja->Base_PS=jam_openbase(ja))) return(NULL); if(JAM_GetMBSize(ja->Base_PS,&num)) { LogWrite(1,TOSSINGERR,"Failed to get size of JAM area \"%s\"",area->Path); return(NULL); } ja->OldNum=num; if(JAM_ReadMBHeader(ja->Base_PS,&Header_S)) { LogWrite(1,TOSSINGERR,"Failed to read header of JAM area \"%s\"",area->Path); return(NULL); } ja->BaseNum=Header_S.BaseMsgNum; ja->OldHighWater=0; ja->HighWater=0; return(ja); } void jam_gethighwater(struct jam_Area *ja) { char buf[200]; osFile fh; uint32_t num; strcpy(buf,ja->area->Path); strcat(buf,".cmhw"); if((fh=osOpen(buf,MODE_OLDFILE))) { if(osRead(fh,&num,sizeof(uint32_t))) { ja->HighWater=num; ja->OldHighWater=num; } osClose(fh); } } void jam_writehighwater(struct jam_Area *ja) { char buf[200]; osFile fh; uint32_t num; strcpy(buf,ja->area->Path); strcat(buf,".cmhw"); num=ja->HighWater; if((fh=osOpen(buf,MODE_NEWFILE))) { osWrite(fh,&num,sizeof(uint32_t)); osClose(fh); } } bool jam_beforefunc(void) { time_t t1,t2; struct tm *tp; jbNewList(&jam_AreaList); if(config.cfg_jam_MaxOpen == 0) config.cfg_jam_MaxOpen = 5; if(!(jam_openbases=osAllocCleared(config.cfg_jam_MaxOpen * sizeof(struct openbase)))) { nomem=TRUE; return(FALSE); } /* Some timezone tricks */ t1=time(NULL); tp=gmtime(&t1); tp->tm_isdst=-1; t2=mktime(tp); jam_utcoffset=t2-t1; jam_lastnum=1; return(TRUE); } bool jam_afterfunc(bool success) { int c; struct jam_Area *ja; if(success && (config.cfg_jam_Flags & CFG_JAM_HIGHWATER)) for(ja=(struct jam_Area *)jam_AreaList.First;ja;ja=ja->Next) if(ja->HighWater != ja->OldHighWater) jam_writehighwater(ja); if(success && (config.cfg_jam_Flags & CFG_JAM_LINK)) for(ja=(struct jam_Area *)jam_AreaList.First;ja;ja=ja->Next) if(ja->newmsg) jam_linkmb(ja->area,ja->OldNum); for(c=0;cnewmsg=TRUE; JAM_ClearMsgHeader(&Header_S); if(!(SubPacket_PS = JAM_NewSubPacket())) { nomem=TRUE; return(FALSE); } /* Allocate memory to store message text in */ msgpos=0; msgsize=0; for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) msgsize+=chunk->Length; if(msgsize != 0) { if(!(msgtext=osAlloc(msgsize))) { LogWrite(1,SYSTEMERR,"Out of memory"); JAM_DelSubPacket(SubPacket_PS); return(FALSE); } } /* Do header */ Header_S.DateProcessed = time(NULL); Header_S.DateWritten = FidoToTime(mm->DateTime); /* Damned time zones... dates should be in local time in JAM */ Header_S.DateProcessed -= jam_utcoffset; Header_S.DateWritten -= jam_utcoffset; Header_S.Cost=mm->Cost; Header_S.MsgIdCRC=JAM_Crc32(mm->MSGID,strlen(mm->MSGID)); Header_S.ReplyCRC=JAM_Crc32(mm->REPLY,strlen(mm->REPLY)); /* Add header fields */ if(mm->From[0]) jam_addfield(SubPacket_PS,JAMSFLD_SENDERNAME,mm->From); if(mm->To[0]) jam_addfield(SubPacket_PS,JAMSFLD_RECVRNAME,mm->To); if(mm->Subject[0]) jam_addfield(SubPacket_PS,JAMSFLD_SUBJECT,mm->Subject); if(mm->Area[0] == 0) { /* Addresses in netmail */ Print4D(&mm->OrigNode,buf); jam_addfield(SubPacket_PS,JAMSFLD_OADDRESS,buf); Print4D(&mm->DestNode,buf); jam_addfield(SubPacket_PS,JAMSFLD_DADDRESS,buf); } else { /* Addresses in echomail */ Print4D(&mm->Origin4D,buf); jam_addfield(SubPacket_PS,JAMSFLD_OADDRESS,buf); } /* Header attributes */ for(c=0;jam_flagarray[c].name;c++) if(mm->Attr & jam_flagarray[c].fidoflagbit) Header_S.Attribute |= jam_flagarray[c].jamflagbit; if(mm->Attr & FLAG_FILEATTACH) { Header_S.Attribute |= MSG_FILEATTACH; c=0; while(mm->Subject[c]!=0) { f=0; while(mm->Subject[c]!=0 && mm->Subject[c]!=32 && mm->Subject[c]!=',' && f<80) buf[f++]=mm->Subject[c++]; buf[f]=0; while(mm->Subject[c]==32 || mm->Subject[c]==',') c++; if(buf[0]!=0) jam_addfield(SubPacket_PS,JAMSFLD_ENCLFILE,buf); } } if(mm->Attr & FLAG_FILEREQ) { Header_S.Attribute |= MSG_FILEREQUEST; c=0; while(mm->Subject[c]!=0) { f=0; while(mm->Subject[c]!=0 && mm->Subject[c]!=32 && mm->Subject[c]!=',' && f<80) buf[f++]=mm->Subject[c++]; buf[f]=0; while(mm->Subject[c]==32 || mm->Subject[c]==',') c++; if(buf[0]!=0) jam_addfield(SubPacket_PS,JAMSFLD_ENCLFREQ,buf); } } /* Echomail/netmail attribute */ if(mm->Area[0]==0) Header_S.Attribute |= MSG_TYPENET; else Header_S.Attribute |= MSG_TYPEECHO; /* Separate kludges from text */ for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) for(c=0;cLength;) { linebegin=msgpos; while(chunk->Data[c]!=13 && cLength) { if(chunk->Data[c]!=10) msgtext[msgpos++]=chunk->Data[c]; c++; } if(chunk->Data[c]==13 && cLength) msgtext[msgpos++]=chunk->Data[c++]; linelen=msgpos-linebegin; if(linelen!=0) { if(linelen>=5 && strncmp(&msgtext[linebegin],"\x01""PID:",5)==0) { mystrncpy(buf,&msgtext[linebegin+5],MIN(100,linelen-5)); stripleadtrail(buf); jam_addfield(SubPacket_PS,JAMSFLD_PID,buf); msgpos=linebegin; } else if(linelen>=7 && strncmp(&msgtext[linebegin],"\x01""MSGID:",7)==0) { mystrncpy(buf,&msgtext[linebegin+7],MIN(100,linelen-7)); stripleadtrail(buf); jam_addfield(SubPacket_PS,JAMSFLD_MSGID,buf); msgpos=linebegin; } else if(linelen>=7 && strncmp(&msgtext[linebegin],"\x01""REPLY:",7)==0) { mystrncpy(buf,&msgtext[linebegin+7],MIN(100,linelen-7)); stripleadtrail(buf); jam_addfield(SubPacket_PS,JAMSFLD_REPLYID,buf); msgpos=linebegin; } else if(linelen>=7 && strncmp(&msgtext[linebegin],"\x01""FLAGS:",7)==0) { mystrncpy(buf,&msgtext[linebegin+7],MIN(100,linelen-7)); stripleadtrail(buf); jbcpos=0; newflags[0]=0; while(jbstrcpy(flag,buf,10,&jbcpos)) { uint32_t flagbit; if((flagbit=jam_findflag(flag))) { Header_S.Attribute |= flagbit; } else { strcat(newflags,flag); strcat(newflags," "); } } stripleadtrail(newflags); if(newflags[0]!=0) jam_addfield(SubPacket_PS,JAMSFLD_FLAGS,newflags); msgpos=linebegin; } else if(linelen>=5 && strncmp(&msgtext[linebegin],"\x01""INTL",5)==0) { /* Remove this kludge */ msgpos=linebegin; } else if(linelen>=5 && strncmp(&msgtext[linebegin],"\x01""TOPT",5)==0) { /* Remove this kludge */ msgpos=linebegin; } else if(linelen>=5 && strncmp(&msgtext[linebegin],"\x01""FMPT",5)==0) { /* Remove this kludge */ msgpos=linebegin; } else if(msgtext[linebegin]==1) { mystrncpy(buf,&msgtext[linebegin+1],MIN(100,linelen-1)); stripleadtrail(buf); jam_addfield(SubPacket_PS,JAMSFLD_FTSKLUDGE,buf); msgpos=linebegin; } } } /* Seen-by */ if(config.cfg_Flags & CFG_IMPORTSEENBY) { char *buf; uint32_t c,d; if((buf=mmMakeSeenByBuf(&mm->SeenBy))) { c=0; while(buf[c]!=0) { d=c; while(buf[d]!=0 && buf[d]!=13) d++; if(buf[d]==13) { buf[d++]=0; jam_addfield(SubPacket_PS,JAMSFLD_SEENBY2D,&buf[c+9]); } c=d; } } osFree(buf); } /* Path */ for(pathnode=(struct Path *)mm->Path.First;pathnode;pathnode=pathnode->Next) for(c=0;cPaths;c++) jam_addfield(SubPacket_PS,JAMSFLD_PATH2D,pathnode->Path[c]); if(jam_nomem) { LogWrite(1,SYSTEMERR,"Out of memory"); JAM_DelSubPacket(SubPacket_PS); if(msgsize) osFree(msgtext); return(FALSE); } /* Write message */ if(JAM_LockMB(ja->Base_PS,10)) { LogWrite(1,SYSTEMERR,"Timeout when trying to lock JAM messagebase \"%s\"",area->Path); JAM_DelSubPacket(SubPacket_PS); if(msgsize) osFree(msgtext); return(FALSE); } if(msgsize == 0) { msgtext=""; msgpos=1; } res=JAM_AddMessage(ja->Base_PS,&Header_S,SubPacket_PS,msgtext,msgpos); JAM_UnlockMB(ja->Base_PS); JAM_DelSubPacket(SubPacket_PS); if(msgsize) osFree(msgtext); if(res) { LogWrite(1,SYSTEMERR,"Failed to write message to JAM messagebase \"%s\"",area->Path); return(FALSE); } return(TRUE); } void jam_makekludge(struct MemMessage *mm,char *pre,char *data,uint32_t len) { char *buf; if(!(buf=osAlloc(strlen(pre)+len+10))) /* A few bytes extra */ return; strcpy(buf,pre); if(len && data) mystrncpy(&buf[strlen(buf)],data,len+1); strcat(buf,"\x0d"); mmAddLine(mm,buf); osFree(buf); } bool jam_ExportJAMNum(struct Area *area,uint32_t num,bool (*handlefunc)(struct MemMessage *mm),bool isrescanning) { struct MemMessage *mm; struct jam_Area *ja; char *msgtext; char buf[200],domain[20]; int res,c; s_JamSubPacket* SubPacket_PS; s_JamMsgHeader Header_S; s_JamSubfield* Field_PS; struct Node4D n4d; bool hasaddr; char flagsbuf[200],filesubject[200]; uint16_t oldattr; /* Open the area */ if(!(ja=jam_getarea(area))) return(FALSE); /* Read message header */ res=JAM_ReadMsgHeader(ja->Base_PS,num-ja->BaseNum,&Header_S,&SubPacket_PS); if(res) { if(res == JAM_NO_MESSAGE) { return(TRUE); /* Message no longer exists */ } else { LogWrite(1,TOSSINGERR,"Failed to read message #%u in JAM messagebase \"%s\"",num,area->Path); return(TRUE); } } /* Check if deleted */ if(Header_S.Attribute & MSG_DELETED) { /* Message deleted */ JAM_DelSubPacket(SubPacket_PS); return(TRUE); } /* Check if already sent */ if(!isrescanning) { if((Header_S.Attribute & MSG_SENT) || !(Header_S.Attribute & MSG_LOCAL)) { /* Don't touch if the message is sent or not local */ JAM_DelSubPacket(SubPacket_PS); return(TRUE); } } /* Read message text */ msgtext=NULL; if(Header_S.TxtLen) { if(!(msgtext=osAlloc(Header_S.TxtLen+1))) /* One extra byte for the terminating zero */ { nomem=TRUE; JAM_DelSubPacket(SubPacket_PS); return(FALSE); } res=JAM_ReadMsgText(ja->Base_PS,Header_S.TxtOffset,Header_S.TxtLen,msgtext); if(res) { LogWrite(1,TOSSINGERR,"Failed to read message #%u in JAM messagebase \"%s\"",num,area->Path); JAM_DelSubPacket(SubPacket_PS); return(FALSE); } msgtext[Header_S.TxtLen]=0; } /* Allocate message structure */ if(!(mm=mmAlloc())) { JAM_DelSubPacket(SubPacket_PS); if(msgtext) osFree(msgtext); return(FALSE); } if(area->AreaType == AREATYPE_NETMAIL) strcpy(mm->Area,""); else strcpy(mm->Area,area->Tagname); mm->msgnum=num; /* Subfields */ flagsbuf[0]=0; filesubject[0]=0; hasaddr=FALSE; for(Field_PS=JAM_GetSubfield(SubPacket_PS);Field_PS;Field_PS=JAM_GetSubfield(NULL)) { switch(Field_PS->LoID) { case JAMSFLD_OADDRESS: mystrncpy(buf,Field_PS->Buffer,Field_PS->DatLen+1); if(Parse5D(buf,&n4d,domain)) { mm->OrigNode.Zone=n4d.Zone; mm->OrigNode.Net=n4d.Net; mm->OrigNode.Node=n4d.Node; mm->OrigNode.Point=n4d.Point; } break; case JAMSFLD_DADDRESS: mystrncpy(buf,Field_PS->Buffer,Field_PS->DatLen+1); if(hasaddr) { LogWrite(1,TOSSINGERR,"Warning: Multiple DADDRESS not supported by CrashMail"); } else { hasaddr=TRUE; if(Parse5D(buf,&n4d,domain)) { mm->DestNode.Zone=n4d.Zone; mm->DestNode.Net=n4d.Net; mm->DestNode.Node=n4d.Node; mm->DestNode.Point=n4d.Point; } } break; case JAMSFLD_SENDERNAME: mystrncpy(buf,Field_PS->Buffer,Field_PS->DatLen+1); mystrncpy(mm->From,buf,36); break; case JAMSFLD_RECVRNAME: mystrncpy(buf,Field_PS->Buffer,Field_PS->DatLen+1); mystrncpy(mm->To,buf,36); break; case JAMSFLD_MSGID: jam_makekludge(mm,"\x01" "MSGID: ",Field_PS->Buffer,Field_PS->DatLen); break; case JAMSFLD_REPLYID: jam_makekludge(mm,"\x01" "REPLY: ",Field_PS->Buffer,Field_PS->DatLen); break; case JAMSFLD_SUBJECT: mystrncpy(buf,Field_PS->Buffer,Field_PS->DatLen+1); mystrncpy(mm->Subject,buf,72); break; case JAMSFLD_PID: jam_makekludge(mm,"\x01" "PID: ",Field_PS->Buffer,Field_PS->DatLen); break; case JAMSFLD_ENCLFILE: if(filesubject[0]) LogWrite(1,TOSSINGERR,"Warning: Multiple ENCLOSEDFILE not supported by CrashMail"); else mystrncpy(filesubject,Field_PS->Buffer,Field_PS->DatLen+1); break; case JAMSFLD_ENCLFREQ: LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDFREQ not supported by CrashMail"); break; case JAMSFLD_ENCLFWALIAS: LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDFILEWALIAS not supported by CrashMail"); break; case JAMSFLD_ENCLFILEWC: LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDFILEWCARD with wildcards not supported by CrashMail"); break; case JAMSFLD_ENCLINDFILE: LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDINDIRECTFILE not supported by CrashMail"); break; case JAMSFLD_FTSKLUDGE: jam_makekludge(mm,"\x01",Field_PS->Buffer,Field_PS->DatLen); break; case JAMSFLD_SEENBY2D: jam_makekludge(mm,"SEEN-BY: ",Field_PS->Buffer,Field_PS->DatLen); break; case JAMSFLD_PATH2D: jam_makekludge(mm,"\01" "PATH: ",Field_PS->Buffer,Field_PS->DatLen); break; case JAMSFLD_FLAGS: strcpy(flagsbuf,"\x01" "FLAGS: "); mystrncpy(&flagsbuf[8],Field_PS->Buffer,Field_PS->DatLen+1); /* Don't add until attributes from header has been added */ break; } } if(filesubject[0]) { mm->Attr|=FLAG_FILEATTACH; mystrncpy(mm->Subject,filesubject,72); } /* Message header */ MakeFidoDate(Header_S.DateWritten+jam_utcoffset,mm->DateTime); mm->Cost=Header_S.Cost; for(c=0;jam_flagarray[c].name;c++) if(Header_S.Attribute & jam_flagarray[c].jamflagbit) { if(jam_flagarray[c].fidoflagbit) { mm->Attr |= jam_flagarray[c].fidoflagbit; } else if(jam_flagarray[c].name[0] && strlen(flagsbuf)<90) { if(flagsbuf[0]==0) strcpy(flagsbuf,"\x01" "FLAGS: "); else strcat(flagsbuf," "); strcat(flagsbuf,jam_flagarray[c].name); } } if(flagsbuf[0]) { strcat(flagsbuf,"\x0d"); mmAddLine(mm,buf); } oldattr = mm->Attr; mm->Attr = mm->Attr & (FLAG_PVT|FLAG_CRASH|FLAG_FILEATTACH|FLAG_FILEREQ|FLAG_RREQ|FLAG_IRRR|FLAG_AUDIT|FLAG_HOLD); /* Add own kludges */ if(area->AreaType == AREATYPE_NETMAIL) { if(mm->OrigNode.Zone != mm->DestNode.Zone || (config.cfg_Flags & CFG_FORCEINTL)) { sprintf(buf,"\x01" "INTL %u:%u/%u %u:%u/%u\x0d", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->OrigNode.Zone, mm->OrigNode.Net, mm->OrigNode.Node); mmAddLine(mm,buf); } if(mm->OrigNode.Point) { sprintf(buf,"\x01" "FMPT %u\x0d",mm->OrigNode.Point); mmAddLine(mm,buf); } if(mm->DestNode.Point) { sprintf(buf,"\x01" "TOPT %u\x0d",mm->DestNode.Point); mmAddLine(mm,buf); } } if((config.cfg_Flags & CFG_ADDTID) && !isrescanning) AddTID(mm); if(isrescanning) { sprintf(buf,"\x01RESCANNED %u:%u/%u.%u\x0d",area->Aka->Node.Zone, area->Aka->Node.Net, area->Aka->Node.Node, area->Aka->Node.Point); mmAddLine(mm,buf); } /* Message text */ if(msgtext) { /* Extract origin address */ if(mm->Area[0]) { uint32_t textpos,d; char originbuf[200]; struct Node4D n4d; textpos=0; while(msgtext[textpos]) { d=textpos; while(msgtext[d] != 13 && msgtext[d] != 0) d++; if(msgtext[d] == 13) d++; if(d-textpos > 11 && strncmp(&msgtext[textpos]," * Origin: ",11)==0) { mystrncpy(originbuf,&msgtext[textpos],MIN(d-textpos,200)); if(ExtractAddress(originbuf,&n4d)) Copy4D(&mm->Origin4D,&n4d); } textpos=d; } } if(!(mmAddBuf(&mm->TextChunks,msgtext,Header_S.TxtLen))) { JAM_DelSubPacket(SubPacket_PS); osFree(msgtext); mmFree(mm); return(FALSE); } } /* Free JAM message */ if(msgtext) osFree(msgtext); JAM_DelSubPacket(SubPacket_PS); /* Message reading done */ if(isrescanning) mm->Flags |= MMFLAG_RESCANNED; else mm->Flags |= MMFLAG_EXPORTED; if(!(*handlefunc)(mm)) { mmFree(mm); return(FALSE); } if(!isrescanning) { scan_total++; /* Update message header */ if(config.cfg_Flags & CFG_ALLOWKILLSENT) { if((oldattr & FLAG_KILLSENT) && (area->AreaType == AREATYPE_NETMAIL)) { /* Delete message with KILLSENT flag */ LogWrite(2,TOSSINGINFO,"Deleting message with KILLSENT flag"); Header_S.Attribute |= MSG_DELETED; } } Header_S.Attribute |= MSG_SENT; Header_S.DateProcessed = time(NULL); Header_S.DateProcessed -= jam_utcoffset; /* jam_openbases might have been changed in handlefunc */ if(!(ja=jam_getarea(area))) { mmFree(mm); return(FALSE); } if(JAM_LockMB(ja->Base_PS,10)) { LogWrite(1,SYSTEMERR,"Timeout when trying to lock JAM messagebase \"%s\"",area->Path); mmFree(mm); return(FALSE); } if(JAM_ChangeMsgHeader(ja->Base_PS,num-ja->BaseNum,&Header_S)) LogWrite(1,TOSSINGERR,"Failed to update header of message #%u in JAM messagebase \"%s\"",num,area->Path); JAM_UnlockMB(ja->Base_PS); } mmFree(mm); return(TRUE); } bool jam_exportfunc(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)) { uint32_t start,end; struct jam_Area *ja; /* Open the area */ if(!(ja=jam_getarea(area))) { if(nomem) return(FALSE); return(TRUE); /* Area did not exist and could not be created. Go on anyway. */ } if(config.cfg_jam_Flags & CFG_JAM_HIGHWATER) jam_gethighwater(ja); if(ja->HighWater) start=ja->HighWater+1; else start=ja->BaseNum; if(start < ja->BaseNum) start=ja->BaseNum; end = ja->BaseNum + ja->OldNum; while(start < end && !ctrlc) { if(!jam_ExportJAMNum(area,start,handlefunc,FALSE)) return(FALSE); start++; } if(ctrlc) return(FALSE); ja->HighWater=end-1; return(TRUE); } bool jam_rescanfunc(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)) { uint32_t start; struct jam_Area *ja; /* Open the area */ if(!(ja=jam_getarea(area))) return(FALSE); start=ja->BaseNum; if(max !=0 && ja->OldNum > max) start=ja->BaseNum+ja->OldNum-max; while(start < ja->BaseNum + ja->OldNum && !ctrlc) { if(!jam_ExportJAMNum(area,start,handlefunc,TRUE)) return(FALSE); start++; } if(ctrlc) return(FALSE); return(TRUE); } /************************** Linking ***********************/ struct Msg { unsigned long MsgIdCRC; unsigned long ReplyCRC; unsigned long ReplyTo; unsigned long Reply1st; unsigned long ReplyNext; unsigned long OldReplyTo; unsigned long OldReply1st; unsigned long OldReplyNext; }; int jam_CompareMsgIdReply(s_JamBase *Base_PS,struct Msg *msgs,uint32_t msgidmsg,uint32_t replymsg) { int Status_I; s_JamMsgHeader MsgIdHeader_S; s_JamMsgHeader ReplyHeader_S; s_JamSubPacket* MsgIdSubPacket_PS; s_JamSubPacket* ReplySubPacket_PS; s_JamSubfield* MsgIdField_PS = NULL; s_JamSubfield* ReplyField_PS = NULL; if(msgs[msgidmsg].MsgIdCRC != msgs[replymsg].ReplyCRC) return(FALSE); if(config.cfg_jam_Flags & CFG_JAM_QUICKLINK) return(TRUE); Status_I = JAM_ReadMsgHeader(Base_PS,msgidmsg,&MsgIdHeader_S,&MsgIdSubPacket_PS ); if(Status_I) return(FALSE); Status_I = JAM_ReadMsgHeader(Base_PS,replymsg,&ReplyHeader_S,&ReplySubPacket_PS ); if(Status_I) { JAM_DelSubPacket(MsgIdSubPacket_PS); return(FALSE); } for ( MsgIdField_PS = JAM_GetSubfield( MsgIdSubPacket_PS ); MsgIdField_PS; MsgIdField_PS = JAM_GetSubfield( NULL ) ) if(MsgIdField_PS->LoID == JAMSFLD_MSGID) break; for ( ReplyField_PS = JAM_GetSubfield( ReplySubPacket_PS ); ReplyField_PS; ReplyField_PS = JAM_GetSubfield( NULL ) ) if(ReplyField_PS->LoID == JAMSFLD_REPLYID) break; if(!ReplyField_PS || !MsgIdField_PS) { JAM_DelSubPacket(MsgIdSubPacket_PS); JAM_DelSubPacket(ReplySubPacket_PS); return(FALSE); } if(ReplyField_PS->DatLen != MsgIdField_PS->DatLen) { JAM_DelSubPacket(MsgIdSubPacket_PS); JAM_DelSubPacket(ReplySubPacket_PS); return(FALSE); } if(strncmp(ReplyField_PS->Buffer,MsgIdField_PS->Buffer,ReplyField_PS->DatLen) != 0) { JAM_DelSubPacket(MsgIdSubPacket_PS); JAM_DelSubPacket(ReplySubPacket_PS); return(FALSE); } JAM_DelSubPacket(MsgIdSubPacket_PS); JAM_DelSubPacket(ReplySubPacket_PS); return(TRUE); } /* dest is a reply to num */ void jam_setreply(struct Msg *msgs,uint32_t nummsgs,uint32_t base,uint32_t num,uint32_t dest) { int n,times; if(msgs[dest].ReplyTo) return; /* Already linked */ msgs[dest].ReplyTo=num+base; if(msgs[num].Reply1st == 0) { msgs[num].Reply1st=dest+base; } else { n=msgs[num].Reply1st-base; if(n == dest) return; if (n < 0 || n >= nummsgs) { /* Oops! Base seems to be b0rken */ printf("Warning: message #%d is linked to something outside the base\n", num + base); return; } times=0; while(msgs[n].ReplyNext) { times++; if(times > 1000) /* Something appears to have gone wrong */ { printf("Warning: >1000 replies to message %d or circular reply links\n",num+base); return; } n=msgs[n].ReplyNext-base; if(n == dest) return; if (n < 0 || n >= nummsgs) { /* Oops! Base seems to be b0rken */ printf("Warning: message #%d is linked to something outside the base\n", num + base); return; } } msgs[n].ReplyNext=dest+base; } } int jam_linkmb(struct Area *area,uint32_t oldnum) { struct jam_Area *ja; uint32_t nummsgs,res,c,d; struct Msg *msgs; printf("Linking JAM area %s \n",area->Tagname); fflush(stdout); if(!(ja=jam_getarea(area))) return(FALSE); if(JAM_GetMBSize(ja->Base_PS,&nummsgs)) { LogWrite(1,TOSSINGERR,"Failed to get size of JAM area \"%s\"",area->Path); return(FALSE); } if(nummsgs == 0) return(TRUE); /* Nothing to do */ /* Read msgid/reply */ if(!(msgs=osAlloc(nummsgs*sizeof(struct Msg)))) { LogWrite(1,SYSTEMERR,"Out of memory, cannot link JAM area %s",area->Tagname); return(FALSE); } for(c=0;cBase_PS, c, &Header_S, NULL); msgs[c].MsgIdCRC=-1; msgs[c].ReplyCRC=-1; msgs[c].ReplyTo=0; msgs[c].Reply1st=0; msgs[c].ReplyNext=0; msgs[c].OldReplyTo=0; msgs[c].OldReply1st=0; msgs[c].OldReplyNext=0; if(!res) { msgs[c].MsgIdCRC=Header_S.MsgIdCRC; msgs[c].ReplyCRC=Header_S.ReplyCRC; msgs[c].ReplyTo=Header_S.ReplyTo; msgs[c].Reply1st=Header_S.Reply1st; msgs[c].ReplyNext=Header_S.ReplyNext; msgs[c].OldReplyTo=Header_S.ReplyTo; msgs[c].OldReply1st=Header_S.Reply1st; msgs[c].OldReplyNext=Header_S.ReplyNext; } } for(c=oldnum;cBase_PS,msgs,d,c)) jam_setreply(msgs,nummsgs,ja->BaseNum,d,c); } if(msgs[c].MsgIdCRC != -1) { /* See if there are any replies to this message */ for(d=0;dBase_PS,msgs,c,d)) jam_setreply(msgs,nummsgs,ja->BaseNum,c,d); } } /* Update links */ for(c=0;cBase_PS,10)) { LogWrite(1,SYSTEMERR,"Timeout when trying to lock JAM messagebase \"%s\"",area->Path); osFree(msgs); return(FALSE); } res = JAM_ReadMsgHeader( ja->Base_PS, c, &Header_S, NULL); if(!res) { Header_S.ReplyTo=msgs[c].ReplyTo; Header_S.Reply1st=msgs[c].Reply1st; Header_S.ReplyNext=msgs[c].ReplyNext; JAM_ChangeMsgHeader(ja->Base_PS,c,&Header_S); JAM_UnlockMB(ja->Base_PS); } } osFree(msgs); return(TRUE); } crashmail-1.5/src/crashmail/mb_jam.h000066400000000000000000000004541230617144500174600ustar00rootroot00000000000000bool jam_beforefunc(void); bool jam_afterfunc(bool success); bool jam_importfunc(struct MemMessage *mm,struct Area *area); bool jam_exportfunc(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)); bool jam_rescanfunc(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)); crashmail-1.5/src/crashmail/mb_msg.c000066400000000000000000000312661230617144500174770ustar00rootroot00000000000000#include "crashmail.h" struct msg_Area { struct msg_Area *Next; struct Area *area; uint32_t LowMsg; uint32_t HighMsg; uint32_t OldHighWater; uint32_t HighWater; }; bool msg_GetHighLowMsg(struct msg_Area *area); bool msg_WriteHighWater(struct msg_Area *area); bool msg_WriteMSG(struct MemMessage *mm,char *file); uint32_t msg_ReadCR(char *buf, uint32_t maxlen, osFile fh); bool msg_ExportMSGNum(struct Area *area,uint32_t num,bool (*handlefunc)(struct MemMessage *mm),bool isrescanning); struct jbList msg_AreaList; bool msg_messageend; bool msg_shortread; struct msg_Area *msg_getarea(struct Area *area) { struct msg_Area *ma; /* Check if area already exists */ for(ma=(struct msg_Area *)msg_AreaList.First;ma;ma=ma->Next) if(ma->area == area) return(ma); /* This is the first time we use this area */ if(!(ma=osAllocCleared(sizeof(struct msg_Area)))) { nomem=TRUE; return(FALSE); } jbAddNode(&msg_AreaList,(struct jbNode *)ma); ma->area=area; if(!msg_GetHighLowMsg(ma)) return(FALSE); return(ma); } bool msg_beforefunc(void) { jbNewList(&msg_AreaList); return(TRUE); } bool msg_afterfunc(bool success) { struct msg_Area *ma; if(success && (config.cfg_msg_Flags & CFG_MSG_HIGHWATER)) for(ma=(struct msg_Area *)msg_AreaList.First;ma;ma=ma->Next) if(ma->HighWater != ma->OldHighWater) msg_WriteHighWater(ma); return(TRUE); } bool msg_importfunc(struct MemMessage *mm,struct Area *area) { char buf[200],buf2[20]; struct msg_Area *ma; if(!(ma=msg_getarea(area))) return(FALSE); ma->HighMsg++; sprintf(buf2,"%u.msg",ma->HighMsg); MakeFullPath(ma->area->Path,buf2,buf,200); while(osExists(buf)) { ma->HighMsg++; sprintf(buf2,"%u.msg",ma->HighMsg); MakeFullPath(ma->area->Path,buf2,buf,200); } return msg_WriteMSG(mm,buf); } bool msg_rescanfunc(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)) { uint32_t start; struct msg_Area *ma; if(!(ma=msg_getarea(area))) return(FALSE); start=ma->LowMsg; if(max !=0 && ma->HighMsg-start+1 > max) start=ma->HighMsg-max+1; while(start <= ma->HighMsg && !ctrlc) { if(!msg_ExportMSGNum(area,start,handlefunc,TRUE)) return(FALSE); start++; } if(ctrlc) return(FALSE); return(TRUE); } bool msg_exportfunc(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)) { uint32_t start; char buf[200]; struct StoredMsg Msg; osFile fh; struct msg_Area *ma; if(!(ma=msg_getarea(area))) return(FALSE); if(config.cfg_msg_Flags & CFG_MSG_HIGHWATER) { if(ma->HighWater == 0) { MakeFullPath(area->Path,"1.msg",buf,200); if((fh=osOpen(buf,MODE_OLDFILE))) { if((osRead(fh,&Msg,sizeof(struct StoredMsg))==sizeof(struct StoredMsg))) { ma->HighWater = Msg.ReplyTo; ma->OldHighWater = Msg.ReplyTo; } osClose(fh); } } } if(ma->HighWater) start=ma->HighWater+1; else start=ma->LowMsg; if(startLowMsg) start=ma->LowMsg; while(start <= ma->HighMsg && !ctrlc) { if(!msg_ExportMSGNum(area,start,handlefunc,FALSE)) return(FALSE); start++; } if(ctrlc) return(FALSE); return(TRUE); } bool msg_ExportMSGNum(struct Area *area,uint32_t num,bool (*handlefunc)(struct MemMessage *mm),bool isrescanning) { char buf[200],buf2[50]; bool kludgeadd; osFile fh; struct StoredMsg Msg; struct MemMessage *mm; uint16_t oldattr; struct msg_Area *ma; if(!(ma=msg_getarea(area))) return(FALSE); if(!(mm=mmAlloc())) return(FALSE); sprintf(buf2,"%u.msg",num); MakeFullPath(area->Path,buf2,buf,200); if(!(fh=osOpen(buf,MODE_OLDFILE))) { /* Message doesn't exist */ return(TRUE); } if(osRead(fh,&Msg,sizeof(struct StoredMsg))!=sizeof(struct StoredMsg)) { LogWrite(1,TOSSINGERR,"Unexpected EOF while reading %s, message ignored",buf); osClose(fh); return(TRUE); } if(!isrescanning) { if((Msg.Attr & FLAG_SENT) || !(Msg.Attr & FLAG_LOCAL)) { /* Don't touch if the message is sent or not local */ osClose(fh); return(TRUE); } } mm->OrigNode.Net=Msg.OrigNet; mm->OrigNode.Node=Msg.OrigNode; mm->DestNode.Net=Msg.DestNet; mm->DestNode.Node=Msg.DestNode; if(area->AreaType == AREATYPE_NETMAIL) strcpy(mm->Area,""); else strcpy(mm->Area,area->Tagname); mystrncpy(mm->To,Msg.To,36); mystrncpy(mm->From,Msg.From,36); mystrncpy(mm->Subject,Msg.Subject,72); mystrncpy(mm->DateTime,Msg.DateTime,20); oldattr = Msg.Attr; mm->Attr = Msg.Attr & (FLAG_PVT|FLAG_CRASH|FLAG_FILEATTACH|FLAG_FILEREQ|FLAG_RREQ|FLAG_IRRR|FLAG_AUDIT|FLAG_HOLD); mm->Cost = Msg.Cost; kludgeadd=FALSE; msg_messageend=FALSE; msg_shortread=FALSE; do { msg_ReadCR(buf,200,fh); if(buf[0]!=1 && buf[0]!=10 && !kludgeadd) { kludgeadd=TRUE; if((config.cfg_Flags & CFG_ADDTID) && !isrescanning) AddTID(mm); if(isrescanning) { sprintf(buf2,"\x01RESCANNED %u:%u/%u.%u\x0d",area->Aka->Node.Zone, area->Aka->Node.Net, area->Aka->Node.Node, area->Aka->Node.Point); mmAddLine(mm,buf2); } if(mm->Area[0]==0) { if(mm->DestNode.Zone == 0 || mm->OrigNode.Zone == 0) { /* No INTL line and no zone in header */ mm->DestNode.Zone=area->Aka->Node.Zone; mm->OrigNode.Zone=area->Aka->Node.Zone; Msg.DestZone=area->Aka->Node.Zone; Msg.OrigZone=area->Aka->Node.Zone; if(config.cfg_Flags & CFG_FORCEINTL) { sprintf(buf2,"\x01INTL %u:%u/%u %u:%u/%u\x0d",Msg.DestZone,Msg.DestNet,Msg.DestNode, Msg.OrigZone,Msg.OrigNet,Msg.OrigNode); mmAddLine(mm,buf2); } } } } if(buf[0]) { if(!mmAddLine(mm,buf)) { osClose(fh); mmFree(mm); return(FALSE); } } } while(!msg_messageend && !msg_shortread); osClose(fh); mm->msgnum=num; if(isrescanning) mm->Flags |= MMFLAG_RESCANNED; else mm->Flags |= MMFLAG_EXPORTED; if(!(*handlefunc)(mm)) { mmFree(mm); return(FALSE); } if(!isrescanning) { scan_total++; sprintf(buf2,"%u.msg",num); MakeFullPath(area->Path,buf2,buf,200); if((config.cfg_Flags & CFG_ALLOWKILLSENT) && (oldattr & FLAG_KILLSENT) && (area->AreaType == AREATYPE_NETMAIL)) { /* Delete message with KILLSENT flag */ LogWrite(2,TOSSINGINFO,"Deleting message with KILLSENT flag"); osDelete(buf); } else { ma->HighWater=num; Msg.Attr|=FLAG_SENT; if(config.cfg_msg_Flags & CFG_MSG_WRITEBACK) { mm->Attr=Msg.Attr; msg_WriteMSG(mm,buf); } else { if((fh=osOpen(buf,MODE_READWRITE))) { osWrite(fh,&Msg,sizeof(struct StoredMsg)); osClose(fh); } } } } mmFree(mm); return(TRUE); } uint32_t msg_templowmsg; uint32_t msg_temphighmsg; void msg_scandirfunc(char *file) { if(strlen(file) > 4) { if(stricmp(&file[strlen(file)-4],".msg")==0) { if(atol(file) > msg_temphighmsg) msg_temphighmsg = atol(file); if(atol(file) < msg_templowmsg || msg_templowmsg==0 ||msg_templowmsg==1) if(atol(file) >= 2 ) msg_templowmsg=atol(file); } } } bool msg_GetHighLowMsg(struct msg_Area *area) { if(!osExists(area->area->Path)) { LogWrite(2,SYSTEMINFO,"Creating directory \"%s\"",area->area->Path); if(!osMkDir(area->area->Path)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Unable to create directory"); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } } msg_templowmsg=0; msg_temphighmsg=0; if(!osScanDir(area->area->Path,msg_scandirfunc)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to scan directory %s",area->area->Path); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } area->HighMsg=msg_temphighmsg; area->LowMsg=msg_templowmsg; if(area->HighMsg==0) area->HighMsg=1; if(area->LowMsg==0 || area->LowMsg==1) area->LowMsg=2; return(TRUE); } bool msg_WriteHighWater(struct msg_Area *area) { osFile fh; char buf[200]; struct StoredMsg Msg; if(area->HighWater > 65535) { LogWrite(1,TOSSINGERR,"Warning: Highwater mark in %s exceeds 65535, cannot store in 1.msg", area->area->Tagname); return(TRUE); } strcpy(Msg.From,"CrashMail II"); strcpy(Msg.To,"All"); strcpy(Msg.Subject,"HighWater mark"); MakeFidoDate(time(NULL),Msg.DateTime); Msg.TimesRead=0; Msg.DestNode=0; Msg.OrigNode=0; Msg.Cost=0; Msg.OrigNet=0; Msg.DestNet=0; Msg.DestZone=0; Msg.OrigZone=0; Msg.OrigPoint=0; Msg.DestPoint=0; Msg.ReplyTo=area->HighWater; Msg.Attr=FLAG_SENT | FLAG_PVT; Msg.NextReply=0; MakeFullPath(area->area->Path,"1.msg",buf,200); if(!(fh=osOpen(buf,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to write Highwater mark to %s",buf); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } if(!osWrite(fh,&Msg,sizeof(struct StoredMsg))) { ioerror=TRUE; ioerrornum=osError(); } if(!osWrite(fh,"",1)) { ioerror=TRUE; ioerrornum=osError(); } osClose(fh); if(ioerror) return(FALSE); return(TRUE); } bool msg_WriteMSG(struct MemMessage *mm,char *file) { struct StoredMsg Msg; struct TextChunk *chunk; struct Path *path; osFile fh; int c; strcpy(Msg.From,mm->From); strcpy(Msg.To,mm->To); strcpy(Msg.Subject,mm->Subject); strcpy(Msg.DateTime,mm->DateTime); Msg.TimesRead=0; Msg.ReplyTo=0; Msg.NextReply=0; Msg.Cost= mm->Cost; Msg.Attr= mm->Attr; if(mm->Area[0]==0) { Msg.DestZone = mm->DestNode.Zone; Msg.DestNet = mm->DestNode.Net; Msg.DestNode = mm->DestNode.Node; Msg.DestPoint = mm->DestNode.Point; Msg.OrigZone = mm->OrigNode.Zone; Msg.OrigNet = mm->OrigNode.Net; Msg.OrigNode = mm->OrigNode.Node; Msg.OrigPoint = mm->OrigNode.Point; } else { Msg.DestZone = 0; Msg.DestNet = 0; Msg.DestNode = 0; Msg.DestPoint = 0; Msg.OrigZone = 0; Msg.OrigNet = 0; Msg.OrigNode = 0; Msg.OrigPoint = 0; } if(!(fh=osOpen(file,MODE_NEWFILE))) { printf("Failed to write to %s\n",file); return(FALSE); } /* Write header */ if(!osWrite(fh,&Msg,sizeof(struct StoredMsg))) { ioerror=TRUE; ioerrornum=osError(); } /* Write text */ for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) { if(!osWrite(fh,chunk->Data,chunk->Length)) { ioerror=TRUE; ioerrornum=osError(); } } /* Write seen-by */ if((config.cfg_Flags & CFG_IMPORTSEENBY) && mm->Area[0]!=0) { char *sbbuf; if(!(sbbuf=mmMakeSeenByBuf(&mm->SeenBy))) { osClose(fh); return(FALSE); } if(sbbuf[0]) { if(!osWrite(fh,sbbuf,(uint32_t)strlen(sbbuf))) { ioerror=TRUE; ioerrornum=osError(); } } osFree(sbbuf); } /* Write path */ for(path=(struct Path *)mm->Path.First;path;path=path->Next) for(c=0;cPaths;c++) if(path->Path[c][0]!=0) { if(!osWrite(fh,"\x01PATH: ",7)) { ioerror=TRUE; ioerrornum=osError(); } if(!osWrite(fh,path->Path[c],(uint32_t)strlen(path->Path[c]))) { ioerror=TRUE; ioerrornum=osError(); } if(!osWrite(fh,"\x0d",1)) { ioerror=TRUE; ioerrornum=osError(); } } if(!osPutChar(fh,0)) { ioerror=TRUE; ioerrornum=osError(); } osClose(fh); if(ioerror) return(FALSE); return(TRUE); } uint32_t msg_ReadCR(char *buf, uint32_t maxlen, osFile fh) { /* Reads from fh until buffer full or CR */ short ch,c=0; ch=osGetChar(fh); while(ch!=-1 && ch!=0 && ch!=10 && ch !=13 && c!=maxlen-2) { buf[c++]=ch; if(c!=maxlen-2) ch=osGetChar(fh); } if(ch==13 || ch==10) buf[c++]=ch; buf[c]=0; if(ch==0) msg_messageend=TRUE; if(ch==-1) msg_shortread=TRUE; return(c); } crashmail-1.5/src/crashmail/mb_msg.h000066400000000000000000000004541230617144500174770ustar00rootroot00000000000000bool msg_beforefunc(void); bool msg_afterfunc(bool success); bool msg_importfunc(struct MemMessage *mm,struct Area *area); bool msg_exportfunc(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)); bool msg_rescanfunc(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)); crashmail-1.5/src/crashmail/memmessage.c000066400000000000000000000256341230617144500203600ustar00rootroot00000000000000#include "crashmail.h" bool mmAddNodes2DList(struct jbList *list,uint16_t net,uint16_t node) { /* Add a node to SEEN-BY list */ struct Nodes2D *tmplist; uint16_t num; /* Check if it already exists */ for(tmplist=(struct Nodes2D *)list->First;tmplist;tmplist=tmplist->Next) { for(num=0;numNodes;num++) if(tmplist->Net[num]==net && tmplist->Node[num]==node) return(TRUE); } tmplist=(struct Nodes2D *)list->Last; if(tmplist && tmplist->Nodes == PKT_NUM2D) tmplist=NULL; if(!tmplist) { if(!(tmplist=(struct Nodes2D *)osAlloc(sizeof(struct Nodes2D)))) { nomem=TRUE; return(FALSE); } jbAddNode(list,(struct jbNode *)tmplist); tmplist->Nodes=0; tmplist->Next=NULL; } tmplist->Net[tmplist->Nodes]=net; tmplist->Node[tmplist->Nodes]=node; tmplist->Nodes++; return(TRUE); } void mmRemNodes2DList(struct jbList *list,uint16_t net,uint16_t node) { /* Rem a node from SEEN-BY list */ struct Nodes2D *tmplist; uint16_t num; for(tmplist=(struct Nodes2D *)list->First;tmplist;tmplist=tmplist->Next) for(num=0;numNodes;num++) if(tmplist->Net[num]==net && tmplist->Node[num]==num) { tmplist->Net[num]=0; tmplist->Node[num]=0; } } void mmRemNodes2DListPat(struct jbList *list,struct Node2DPat *pat) { struct Nodes2D *tmplist; uint16_t num; for(tmplist=(struct Nodes2D *)list->First;tmplist;tmplist=tmplist->Next) for(num=0;numNodes;num++) { if(Compare2DPat(pat,tmplist->Net[num],tmplist->Node[num])==0) { tmplist->Net[num]=0; tmplist->Node[num]=0; } } } bool mmAddPath(char *str,struct jbList *list) { /* Add a node string to PATH list */ struct Path *path; if(str[0]==0) return(TRUE); path=(struct Path *)list->Last; if(path && path->Paths == PKT_NUMPATH) path=NULL; if(!path) { if(!(path=(struct Path *)osAlloc(sizeof(struct Path)))) { nomem=TRUE; return(FALSE); } jbAddNode(list,(struct jbNode *)path); path->Paths=0; path->Next=NULL; } mystrncpy(path->Path[path->Paths],str,100); striptrail(path->Path[path->Paths]); path->Paths++; return(TRUE); } bool mmAddBuf(struct jbList *chunklist,char *buf,uint32_t len) { struct TextChunk *chunk,*oldchunk; uint32_t copylen,d; if(len == 0) return(TRUE); /* Find last chunk */ chunk=(struct TextChunk *)chunklist->Last; /* Will it fit in this chunk?. If yes, copy and exit. */ if(chunk && chunk->Length+len <= PKT_CHUNKLEN) { memcpy(&chunk->Data[chunk->Length],buf,(size_t)len); chunk->Length+=len; return(TRUE); } /* We will need a new chunk */ while(len) { oldchunk=chunk; if(!(chunk=(struct TextChunk *)osAlloc(sizeof(struct TextChunk)))) { nomem=TRUE; return(FALSE); } chunk->Length=0; jbAddNode(chunklist,(struct jbNode *)chunk); /* Copy over last line from old chunk if not complete */ if(oldchunk && oldchunk->Length > 0 && oldchunk->Data[oldchunk->Length-1] != 13) { for(d=oldchunk->Length-1;d>0;d--) if(oldchunk->Data[d] == 13) break; if(d != 0) { memcpy(chunk->Data,&oldchunk->Data[d+1],oldchunk->Length-d-1); chunk->Length=oldchunk->Length-d-1; oldchunk->Length=d+1; } } copylen=len; if(copylen > PKT_CHUNKLEN-chunk->Length) copylen=PKT_CHUNKLEN-chunk->Length; memcpy(&chunk->Data[chunk->Length],buf,(size_t)copylen); chunk->Length+=copylen; buf=&buf[copylen]; len-=copylen; } return(TRUE); } struct MemMessage *mmAlloc(void) { struct MemMessage *mm; mm=osAllocCleared(sizeof(struct MemMessage)); if(!mm) { osFree(mm); nomem=TRUE; return(NULL); } jbNewList(&mm->TextChunks); jbNewList(&mm->SeenBy); jbNewList(&mm->Path); return(mm); } void mmClear(struct MemMessage *mm) { struct Node4D null4d = { 0,0,0,0 }; Copy4D(&mm->OrigNode,&null4d); Copy4D(&mm->DestNode,&null4d); Copy4D(&mm->PktOrig,&null4d); Copy4D(&mm->PktDest,&null4d); Copy4D(&mm->Origin4D,&null4d); mm->Area[0]=0; mm->To[0]=0; mm->From[0]=0; mm->Subject[0]=0; mm->DateTime[0]=0; mm->MSGID[0]=0; mm->REPLY[0]=0; mm->Attr=0; mm->Cost=0; mm->Type=0; mm->Flags=0; jbFreeList(&mm->TextChunks); jbFreeList(&mm->SeenBy); jbFreeList(&mm->Path); } void mmFree(struct MemMessage *mm) { jbFreeList(&mm->TextChunks); jbFreeList(&mm->SeenBy); jbFreeList(&mm->Path); osFree(mm); } struct TempSort { uint16_t Net; uint16_t Node; }; int CompareSort(const void *t1,const void *t2) { if(((struct TempSort *)t1)->Net > ((struct TempSort *)t2)->Net) return(1); if(((struct TempSort *)t1)->Net < ((struct TempSort *)t2)->Net) return(-1); if(((struct TempSort *)t1)->Node > ((struct TempSort *)t2)->Node) return(1); if(((struct TempSort *)t1)->Node < ((struct TempSort *)t2)->Node) return(-1); return(0); } bool mmSortNodes2D(struct jbList *list) { struct Nodes2D *tmp; struct TempSort *sorttemp; uint32_t nodes=0; uint32_t c,d; for(tmp=(struct Nodes2D *)list->First;tmp;tmp=tmp->Next) nodes+=tmp->Nodes; if(nodes==0) return(TRUE); if(!(sorttemp=(struct TempSort *)osAlloc(sizeof(struct TempSort)*nodes))) { nomem=TRUE; return(FALSE); } d=0; for(tmp=(struct Nodes2D *)list->First;tmp;tmp=tmp->Next) for(c=0;cNodes;c++) { sorttemp[d].Net=tmp->Net[c]; sorttemp[d].Node=tmp->Node[c]; d++; } qsort(sorttemp,(size_t)nodes,sizeof(struct TempSort),CompareSort); tmp=(struct Nodes2D *)list->First; tmp->Nodes=0; for(c=0;cNodes == PKT_NUM2D) { tmp=tmp->Next; tmp->Nodes=0; } tmp->Net[tmp->Nodes]=sorttemp[c].Net; tmp->Node[tmp->Nodes]=sorttemp[c].Node; tmp->Nodes++; } osFree(sorttemp); return(TRUE); } bool AddSeenby(char *str,struct jbList *list) { /* Add a node string to SEEN-BY list */ uint32_t c,d; char buf[60]; uint16_t lastnet,num; c=0; lastnet=0; while(str[c]!=13 && str[c]!=0) { d=0; while(str[c]!=0 && str[c]!=13 && str[c]!=32 && d<59) buf[d++]=str[c++]; buf[d]=0; while(str[c]==32) c++; if(buf[0]) { num=0; d=0; while(buf[d]>='0' && buf[d]<='9') { num*=10; num+=buf[d]-'0'; d++; } if(buf[d]=='/') { lastnet=num; num=atoi(&buf[d+1]); if(!mmAddNodes2DList(list,lastnet,num)) return(FALSE); } else if(buf[d]==0) { if(!mmAddNodes2DList(list,lastnet,num)) return(FALSE); } } } return(TRUE); } void ProcessKludge(struct MemMessage *mm,char *kludge) { struct Node4D node; char buf[60]; uint32_t c,d; if(strncmp(kludge,"\x01RESCANNED",10)==0) { mm->Flags |= MMFLAG_RESCANNED; } if(strncmp(kludge,"\x01MSGID:",7)==0) { for(d=0,c=8;d<79 && kludge[c]!=13 && kludge[c]!=0;c++,d++) mm->MSGID[d]=kludge[c]; mm->MSGID[d]=0; } if(strncmp(kludge,"\x01REPLY:",7)==0) { for(d=0,c=8;d<79 && kludge[c]!=13 && kludge[c]!=0;c++,d++) mm->REPLY[d]=kludge[c]; mm->REPLY[d]=0; } if(mm->Area[0]==0) { if(strncmp(kludge,"\x01" "FMPT",5)==0) mm->OrigNode.Point=atoi(&kludge[6]); if(strncmp(kludge,"\x01TOPT",5)==0) mm->DestNode.Point=atoi(&kludge[6]); if(strncmp(kludge,"\x01INTL",5)==0) { if(kludge[5]==':') c=7; else c=6; for(d=0;d<59 && kludge[c]!=32 && kludge[c]!=0;c++,d++) buf[d]=kludge[c]; buf[d]=0; if(Parse4D(buf,&node)) { mm->DestNode.Zone = node.Zone; mm->DestNode.Net = node.Net; mm->DestNode.Node = node.Node; } if(kludge[c]==32) c++; for(d=0;d<59 && kludge[c]!=32 && kludge[c]!=0 && kludge[c]!=13;c++,d++) buf[d]=kludge[c]; buf[d]=0; if(Parse4D(buf,&node)) { mm->OrigNode.Zone = node.Zone; mm->OrigNode.Net = node.Net; mm->OrigNode.Node = node.Node; } } } } bool mmAddLine(struct MemMessage *mm,char *buf) { if(mm->Area[0] && strncmp(buf,"SEEN-BY:",8)==0) return AddSeenby(&buf[9],&mm->SeenBy); else if(mm->Area[0] && strncmp(buf,"\x01PATH:",6)==0) return mmAddPath(&buf[7],&mm->Path); else if(mm->Area[0] && strncmp(buf," * Origin: ",11)==0) { struct Node4D n4d; if(ExtractAddress(buf,&n4d)) { if(n4d.Zone == 0) n4d.Zone=mm->PktOrig.Zone; Copy4D(&mm->Origin4D,&n4d); } } else if(buf[0] == 1) ProcessKludge(mm,buf); return mmAddBuf(&mm->TextChunks,buf,(uint32_t)strlen(buf)); } char *mmMakeSeenByBuf(struct jbList *list) { char *text; uint32_t seenbys,size; struct Nodes2D *nodes; uint32_t c; uint16_t lastnet; char buf[100],buf2[50],buf3[20]; /* Count seenbys */ seenbys=0; for(nodes=(struct Nodes2D *)list->First;nodes;nodes=nodes->Next) seenbys+=nodes->Nodes; /* We allocate generously. Maximum length per seenby: 12345/12345: 12 characters 79 characters - "SEEN-BY:" = 71 characters which makes 71/12 seenbys/line = 5 Fortunately even with this generous calculation will not result in huge memory areas, 500 seenbys (which is a lot) would need 8000 bytes... */ size=(seenbys/5+1)*80; /* Allocate our memory block */ if(!(text=osAlloc(size))) return(NULL); text[0]=0; strcpy(buf,"SEEN-BY:"); lastnet=0; for(nodes=(struct Nodes2D *)list->First;nodes;nodes=nodes->Next) { for(c=0;cNodes;c++) { if(nodes->Net[c]!=0 || nodes->Node[c]!=0) { strcpy(buf2," "); if(nodes->Net[c]!=lastnet) { sprintf(buf3,"%u/",nodes->Net[c]); strcat(buf2,buf3); } sprintf(buf3,"%u",nodes->Node[c]); strcat(buf2,buf3); lastnet=nodes->Net[c]; if(strlen(buf)+strlen(buf2) > 77) { strcat(text,buf); strcat(text,"\x0d"); strcpy(buf,"SEEN-BY:"); sprintf(buf2," %u/%u",nodes->Net[c],nodes->Node[c]); lastnet=nodes->Net[c]; strcat(buf,buf2); } else { strcat(buf,buf2); } } } } if(strlen(buf)>8) { strcat(text,buf); strcat(text,"\x0d"); } return(text); } crashmail-1.5/src/crashmail/memmessage.h000066400000000000000000000032161230617144500203550ustar00rootroot00000000000000#ifndef MEMMESSAGE_H #define MEMMESSAGE_H #define PKT_CHUNKLEN 10000 #define PKT_NUM2D 50 #define PKT_NUMPATH 10 struct TextChunk { struct TextChunk *Next; uint32_t Length; char Data[PKT_CHUNKLEN]; }; struct Nodes2D { struct Nodes2D *Next; uint16_t Nodes; uint16_t Net[PKT_NUM2D]; uint16_t Node[PKT_NUM2D]; }; struct Path { struct Path *Next; uint16_t Paths; char Path[PKT_NUMPATH][100]; }; #define MMFLAG_RESCANNED 1 #define MMFLAG_EXPORTED 2 #define MMFLAG_TOSSED 4 #define MMFLAG_NOSECURITY 8 #define MMFLAG_AUTOGEN 16 #define MMFLAG_TWIT 32 #define MMFLAG_KILL 64 struct MemMessage { uint32_t msgnum; struct Node4D OrigNode; struct Node4D DestNode; struct Node4D PktOrig; struct Node4D PktDest; struct Node4D Origin4D; char Area[80]; char To[36]; char From[36]; char Subject[72]; char DateTime[20]; char MSGID[80]; char REPLY[80]; uint16_t Attr; uint16_t Cost; uint8_t Type; uint16_t Flags; struct jbList TextChunks; struct jbList SeenBy; struct jbList Path; }; bool mmAddNodes2DList(struct jbList *list,uint16_t net,uint16_t node); void mmRemNodes2DList(struct jbList *list,uint16_t net,uint16_t node); void mmRemNodes2DListPat(struct jbList *list,struct Node2DPat *pat); bool mmAddPath(char *str,struct jbList *list); bool mmAddBuf(struct jbList *chunklist,char *buf,uint32_t len); bool mmAddLine(struct MemMessage *mm,char *buf); struct MemMessage *mmAlloc(void); void mmClear(struct MemMessage *mm); void mmFree(struct MemMessage *mm); bool mmSortNodes2D(struct jbList *list); char *mmMakeSeenByBuf(struct jbList *list); #endif crashmail-1.5/src/crashmail/misc.c000066400000000000000000000463661230617144500171750ustar00rootroot00000000000000#include "crashmail.h" void ExpandPacker(char *cmd,char *dest,uint32_t destsize,char *arc,char *file) { uint32_t c,d; d=0; for(c=0;cDate > (*(struct osFileEntry **)f2)->Date) return(1); if((*(struct osFileEntry **)f1)->Date < (*(struct osFileEntry **)f2)->Date) return(-1); return stricmp((*(struct osFileEntry **)f1)->Name,(*(struct osFileEntry **)f2)->Name); /* Compares by filenames to get packet files with the same date in the right order */ } bool SortFEList(struct jbList *list) { struct osFileEntry *ftt,**buf,**work; uint32_t nodecount = 0; for(ftt=(struct osFileEntry *)list->First;ftt;ftt=ftt->Next) nodecount++; if(!nodecount) return(TRUE); if(!(buf=(struct osFileEntry **)osAlloc(nodecount * sizeof(struct osFileEntry *)))) { nomem=TRUE; return(FALSE); } work=buf; for(ftt=(struct osFileEntry *)list->First;ftt;ftt=ftt->Next) *work++=ftt; qsort(buf,(size_t)nodecount,(size_t)sizeof(struct osFileEntry *),DateCompareFE); jbNewList(list); for(work=buf;nodecount--;) jbAddNode(list,(struct jbNode *)*work++); osFree(buf); return(TRUE); } bool IsArc(char *file) { int c; char ext[4]; if(strlen(file)!=12) return(FALSE); if(file[8]!='.') return(FALSE); for(c=0;c<8;c++) if((file[c]<'0' || file[c]>'9') && ((tolower(file[c]) < 'a') || (tolower(file[c]) > 'f'))) return(FALSE); strncpy(ext,&file[9],2); ext[2]=0; if(stricmp(ext,"MO")==0) return(TRUE); if(stricmp(ext,"TU")==0) return(TRUE); if(stricmp(ext,"WE")==0) return(TRUE); if(stricmp(ext,"TH")==0) return(TRUE); if(stricmp(ext,"FR")==0) return(TRUE); if(stricmp(ext,"SA")==0) return(TRUE); if(stricmp(ext,"SU")==0) return(TRUE); return(FALSE); } bool IsPkt(char *file) { if(strlen(file)!=12) return(FALSE); if(file[8]!='.') return(FALSE); if(stricmp(&file[9],"pkt")!=0) return(FALSE); return(TRUE); } bool IsNewPkt(char *file) { if(strlen(file) < 7) return(FALSE); if(stricmp(&file[strlen(file)-7],".newpkt")!=0) return(FALSE); return(TRUE); } bool IsPktTmp(char *file) { if(strlen(file) < 7) return(FALSE); if(stricmp(&file[strlen(file)-7],".pkttmp")!=0) return(FALSE); return(TRUE); } bool IsOrphan(char *file) { if(strlen(file) < 7) return(FALSE); if(stricmp(&file[strlen(file)-7],".orphan")!=0) return(FALSE); return(TRUE); } bool IsBad(char *file) { if(strlen(file)>4 && stricmp(&file[strlen(file)-4],".bad")==0) return(TRUE); return(FALSE); } void striptrail(char *str) { int c; for(c=strlen(str)-1;str[c] < 33 && c>=0;c--) str[c]=0; } void striplead(char *str) { int c; c=0; while(str[c]==' ') c++; strcpy(str,&str[c]); } void stripleadtrail(char *str) { striplead(str); striptrail(str); } void BadFile(char *filename,char *comment) { char destname[100],numbuf[10]; uint32_t num; LogWrite(3,TOSSINGERR,"Renaming %s to .bad",filename); num=0; do { MakeFullPath(config.cfg_Inbound,GetFilePart(filename),destname,90); strcat(destname,".bad"); if(num != 0) { sprintf(numbuf,",%d",num); strcat(destname,numbuf); } num++; } while(osExists(destname)); if(!movefile(filename,destname)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to move %s to %s",filename,destname); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return; } osSetComment(destname,comment); } bool MatchFlags(char group,char *node) { uint8_t c; for(c=0;ctm_mday, monthnames[tp->tm_mon], tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec); } #define COPYBUFSIZE 5000 bool copyfile(char *file,char *newfile) { osFile ifh,ofh; uint32_t len; char *copybuf; if(!(copybuf=(char *)malloc(COPYBUFSIZE))) { nomem=TRUE; return(FALSE); } if(!(ifh=osOpen(file,MODE_OLDFILE))) { free(copybuf); return(FALSE); } if(!(ofh=osOpen(newfile,MODE_NEWFILE))) { free(copybuf); osClose(ifh); return(FALSE); } while((len=osRead(ifh,copybuf,COPYBUFSIZE)) && !ioerror) { if(!osWrite(ofh,copybuf,len)) { ioerror=TRUE; ioerrornum=osError(); } } free(copybuf); osClose(ofh); osClose(ifh); if(ioerror) { osDelete(newfile); return(FALSE); } return(TRUE); } bool movefile(char *file,char *newfile) { if(osRename(file,newfile)) return(TRUE); /* rename was enough */ if(!copyfile(file,newfile)) return(FALSE); osDelete(file); return(TRUE); } char ChangeType(struct Node4D *dest,char pri) { struct Change *change; char newpri; bool ispattern; newpri=pri; for(change=(struct Change *)config.ChangeList.First;change;change=change->Next) { if(Compare4DPat(&change->Pattern,dest)==0) { ispattern=FALSE; if(pri == PKTS_ECHOMAIL && change->ChangeNormal == TRUE) ispattern=TRUE; if(pri == PKTS_NORMAL && change->ChangeNormal == TRUE) ispattern=TRUE; if(pri == PKTS_CRASH && change->ChangeCrash == TRUE) ispattern=TRUE; if(pri == PKTS_DIRECT && change->ChangeDirect == TRUE) ispattern=TRUE; if(pri == PKTS_HOLD && change->ChangeHold == TRUE) ispattern=TRUE; if(ispattern) { if(pri == PKTS_ECHOMAIL && change->DestPri == PKTS_NORMAL) newpri=pri; else newpri=change->DestPri; } } } return(newpri); } bool MakeNetmailKludges(struct MemMessage *mm) { char buf[100]; if(mm->OrigNode.Point) { sprintf(buf,"\x01" "FMPT %u\x0d",mm->OrigNode.Point); mmAddBuf(&mm->TextChunks,buf,strlen(buf)); } if(mm->DestNode.Point) { sprintf(buf,"\x01TOPT %u\x0d",mm->DestNode.Point); mmAddBuf(&mm->TextChunks,buf,strlen(buf)); } if(mm->OrigNode.Zone != mm->DestNode.Zone || (config.cfg_Flags & CFG_FORCEINTL)) { sprintf(buf,"\x01INTL %u:%u/%u %u:%u/%u\x0d", mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->OrigNode.Zone, mm->OrigNode.Net, mm->OrigNode.Node); mmAddBuf(&mm->TextChunks,buf,strlen(buf)); } return(TRUE); } /*************************************/ /* */ /* Fido DateTime conversion routines */ /* */ /*************************************/ time_t FidoToTime(char *date) { uint32_t month; struct tm tm; time_t t; static char *mo[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if(date[2]==' ' && date[6]==' ') { /* Standard Fido "01 Jan 91 11:22:33" */ /* Get month */ for(month=0;month<12;month++) if(strnicmp(mo[month],&date[3],3)==0) break; if(month==12) return time(NULL); /* return current time */ tm.tm_sec=atoi(&date[17]); tm.tm_min=atoi(&date[14]); tm.tm_hour=atoi(&date[11]); tm.tm_mday=atoi(date); tm.tm_mon=month; tm.tm_year=atoi(&date[7]); tm.tm_wday=0; tm.tm_yday=0; tm.tm_isdst=-1; if(tm.tm_year < 80) /* Y2K fix */ tm.tm_year+=100; } else { /* SEAdog "Wed 13 Jan 86 02:34" */ /* SEAdog "Wed 3 Jan 86 02:34" */ /* Get month */ for(month=0;month<12;month++) if(strnicmp(mo[month],&date[7],3)==0) break; if(month==12) return time(NULL); /* return current time */ tm.tm_sec=0; tm.tm_min=atoi(&date[17]); tm.tm_hour=atoi(&date[14]); tm.tm_mday=atoi(&date[4]); tm.tm_mon=month; tm.tm_year=atoi(&date[11]); tm.tm_wday=0; tm.tm_yday=0; tm.tm_isdst=-1; if(tm.tm_year < 80) /* Y2K fix */ tm.tm_year+=100; } t=mktime(&tm); if(t == -1) t=time(NULL); return(t); } bool Parse5D(char *buf, struct Node4D *n4d, char *domain) { uint32_t c=0; char buf2[100]; domain[0]=0; mystrncpy(buf2,buf,100); for(c=0;c0 && origin[pos]!='(') pos--; /* Find address */ if(origin[pos]!='(') return(FALSE); pos++; while(origin[pos]!=13 && (origin[pos]<'0' || origin[pos]>'9')) pos++; /* Skip (FidoNet ... ) */ e=0; while(origin[pos]!=')' && e<49) addr[e++]=origin[pos++]; addr[e]=0; return Parse5D(addr,n4d,domain); } unsigned long hextodec(char *hex) { char *hextab="0123456789abcdef"; int c=0,c2=0; unsigned long result=0; for(;;) { for(c2=0;c2<16;c2++) if(tolower(hex[c]) == hextab[c2]) break; if(c2 == 16) return(result); /* End of correct hex number */ result *= 16; result += c2; c++; } } /* WriteRFC and WriteMSG */ void MakeRFCAddr(char *dest,char *nam,struct Node4D *node,char *dom) { char domain[50],name[50]; int j; /* Prepare domain */ strcpy(domain,dom); for(j=0;domain[j];j++) domain[j]=tolower(domain[j]); if(stricmp(domain,"fidonet")==0) strcpy(domain,"fidonet.org"); /* Prepare name */ strcpy(name,nam); for(j=0;name[j];j++) if(name[j] == ' ') name[j]='_'; /* Make addr */ if(node->Point != 0) sprintf(dest,"%s@p%u.f%u.n%u.z%u.%s", name, node->Point, node->Node, node->Net, node->Zone, domain); else sprintf(dest,"%s@f%u.n%u.z%u.%s", name, node->Node, node->Net, node->Zone, domain); } bool WriteRFC(struct MemMessage *mm,char *name,bool rfcaddr) { osFile fh; char *domain; struct Aka *aka; struct TextChunk *tmp; uint32_t c,d,lastspace; char buffer[100],fromaddr[100],toaddr[100]; for(aka=(struct Aka *)config.AkaList.First;aka;aka=aka->Next) if(Compare4D(&mm->DestNode,&aka->Node)==0) break; domain="FidoNet"; if(aka && aka->Domain[0]!=0) domain=aka->Domain; if(rfcaddr) { MakeRFCAddr(fromaddr,mm->From,&mm->OrigNode,domain); MakeRFCAddr(toaddr,mm->To,&mm->DestNode,domain); } else { sprintf(fromaddr,"%u:%u/%u.%u@%s",mm->OrigNode.Zone, mm->OrigNode.Net, mm->OrigNode.Node, mm->OrigNode.Point, domain); sprintf(toaddr,"%u:%u/%u.%u@%s",mm->DestNode.Zone, mm->DestNode.Net, mm->DestNode.Node, mm->DestNode.Point, domain); } if(!(fh=osOpen(name,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Unable to write RFC-message to %s",name); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } /* Write header */ if(!osFPrintf(fh,"From: %s (%s)\n",fromaddr,mm->From)) { ioerror=TRUE; ioerrornum=osError(); } if(!osFPrintf(fh,"To: %s (%s)\n",toaddr,mm->To)) { ioerror=TRUE; ioerrornum=osError(); } if(!osFPrintf(fh,"Subject: %s\n",mm->Subject)) { ioerror=TRUE; ioerrornum=osError(); } if(!osFPrintf(fh,"Date: %s\n",mm->DateTime)) { ioerror=TRUE; ioerrornum=osError(); } if(mm->MSGID[0]!=0) { if(!osFPrintf(fh,"Message-ID: <%s>\n",mm->MSGID)) { ioerror=TRUE; ioerrornum=osError(); } } if(mm->REPLY[0]!=0) { if(!osFPrintf(fh,"References: <%s>\n",mm->REPLY)) { ioerror=TRUE; ioerrornum=osError(); } } /* Write kludges */ for(tmp=(struct TextChunk *)mm->TextChunks.First;tmp;tmp=tmp->Next) { c=0; while(cLength) { for(d=c;dLength && tmp->Data[d]!=13 && tmp->Data[d]!=10;d++); if(tmp->Data[d]==13 || tmp->Data[d]==10) d++; if(tmp->Data[c]==1 && d-c-2!=0) { if(!osPuts(fh,"X-Fido-")) { ioerror=TRUE; ioerrornum=osError(); } if(!osWrite(fh,&tmp->Data[c+1],d-c-2)) { ioerror=TRUE; ioerrornum=osError(); } if(!osPuts(fh,"\n")) { ioerror=TRUE; ioerrornum=osError(); } } c=d; } } /* Write end-of-header */ if(!osPuts(fh,"\n")) { ioerror=TRUE; ioerrornum=osError(); } /* Write message text */ for(tmp=(struct TextChunk *)mm->TextChunks.First;tmp;tmp=tmp->Next) { d=0; while(dLength) { lastspace=0; c=0; while(tmp->Data[d+c]==10) d++; while(c<78 && d+cLength && tmp->Data[d+c]!=13) { if(tmp->Data[d+c]==32) lastspace=c; c++; } if(c==78 && lastspace) { strncpy(buffer,&tmp->Data[d],lastspace); buffer[lastspace]=0; d+=lastspace+1; } else { strncpy(buffer,&tmp->Data[d],c); buffer[c]=0; if(tmp->Data[d+c]==13) c++; d+=c; } if(buffer[0]!=1) { if(!osPuts(fh,buffer)) { ioerror=TRUE; ioerrornum=osError(); } if(!osPutChar(fh,'\n')) { ioerror=TRUE; ioerrornum=osError(); } } } } osClose(fh); if(ioerror) return(FALSE); return(TRUE); } bool WriteMSG(struct MemMessage *mm,char *file) { struct StoredMsg Msg; struct TextChunk *chunk; struct Path *path; osFile fh; int c; strcpy(Msg.From,mm->From); strcpy(Msg.To,mm->To); strcpy(Msg.Subject,mm->Subject); strcpy(Msg.DateTime,mm->DateTime); Msg.TimesRead=0; Msg.ReplyTo=0; Msg.NextReply=0; Msg.Cost= mm->Cost; Msg.Attr= mm->Attr | FLAG_SENT; if(mm->Area[0]==0) { Msg.DestZone = mm->DestNode.Zone; Msg.DestNet = mm->DestNode.Net; Msg.DestNode = mm->DestNode.Node; Msg.DestPoint = mm->DestNode.Point; Msg.OrigZone = mm->OrigNode.Zone; Msg.OrigNet = mm->OrigNode.Net; Msg.OrigNode = mm->OrigNode.Node; Msg.OrigPoint = mm->OrigNode.Point; } else { Msg.DestZone = 0; Msg.DestNet = 0; Msg.DestNode = 0; Msg.DestPoint = 0; Msg.OrigZone = 0; Msg.OrigNet = 0; Msg.OrigNode = 0; Msg.OrigPoint = 0; } if(!(fh=osOpen(file,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Unable to write message to %s",file); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } /* Write header */ if(!osWrite(fh,&Msg,sizeof(struct StoredMsg))) { ioerror=TRUE; ioerrornum=osError(); } /* Write text */ for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) { if(!osWrite(fh,chunk->Data,chunk->Length)) { ioerror=TRUE; ioerrornum=osError(); } } /* Write seen-by */ if((config.cfg_Flags & CFG_IMPORTSEENBY) && mm->Area[0]!=0) { char *sbbuf; if(!(sbbuf=mmMakeSeenByBuf(&mm->SeenBy))) { osClose(fh); return(FALSE); } if(sbbuf[0]) { if(!osWrite(fh,sbbuf,(uint32_t)strlen(sbbuf))) { ioerror=TRUE; ioerrornum=osError(); } } osFree(sbbuf); } /* Write path */ for(path=(struct Path *)mm->Path.First;path;path=path->Next) for(c=0;cPaths;c++) if(path->Path[c][0]!=0) { if(!osWrite(fh,"\x01PATH: ",7)) { ioerror=TRUE; ioerrornum=osError(); } if(!osWrite(fh,path->Path[c],(uint32_t)strlen(path->Path[c]))) { ioerror=TRUE; ioerrornum=osError(); } if(!osWrite(fh,"\x0d",1)) { ioerror=TRUE; ioerrornum=osError(); } } if(!osPutChar(fh,0)) { ioerror=TRUE; ioerrornum=osError(); } osClose(fh); if(ioerror) return(FALSE); return(TRUE); } crashmail-1.5/src/crashmail/misc.h000066400000000000000000000023071230617144500171650ustar00rootroot00000000000000void ExpandPacker(char *cmd,char *dest,uint32_t destsize,char *archive,char *file); bool SortFEList(struct jbList *list); void BadFile(char *filename,char *comment); bool IsArc(char *file); bool IsPkt(char *file); bool IsNewPkt(char *file); bool IsPktTmp(char *file); bool IsOrphan(char *file); bool IsBad(char *file); void striptrail(char *str); void striplead(char *str); void stripleadtrail(char *str); bool MatchFlags(char group,char *node); void ExpandFilter(char *cmd,char *dest,uint32_t destsize, char *rfc1, char *rfc2, char *msg, char *area, char *subj, char *time, char *from, char *to, char *orignode, char *destnode); void MakeFidoDate(time_t tim,char *dest); bool AddTID(struct MemMessage *mm); bool movefile(char *file,char *newfile); bool copyfile(char *file,char *newfile); char ChangeType(struct Node4D *dest,char pri); bool MakeNetmailKludges(struct MemMessage *mm); time_t FidoToTime(char *date); bool Parse5D(char *buf, struct Node4D *n4d, char *domain); bool ExtractAddress(char *origin, struct Node4D *n4d); unsigned long hextodec(char *hex); bool WriteMSG(struct MemMessage *mm,char *file); bool WriteRFC(struct MemMessage *mm,char *name,bool rfcaddr); crashmail-1.5/src/crashmail/nl.c000066400000000000000000000011621230617144500166340ustar00rootroot00000000000000#include "crashmail.h" #ifdef NODELIST_CMNL #include "nl_cmnl.h" #endif #ifdef NODELIST_V7P #include "nl_v7p.h" #endif struct Nodelist AvailNodelists[] = { #ifdef NODELIST_CMNL { "CMNL", "CrashMail nodelist index", cmnl_nlStart, cmnl_nlEnd, cmnl_nlCheckNode, cmnl_nlGetHub, cmnl_nlGetRegion }, #endif #ifdef NODELIST_V7P { "V7+", "Version 7+ format", v7p_nlStart, v7p_nlEnd, v7p_nlCheckNode, v7p_nlGetHub, v7p_nlGetRegion }, #endif { NULL, /* NULL here marks the end of the array */ NULL, NULL, NULL, NULL, NULL, NULL } }; crashmail-1.5/src/crashmail/nl.h000066400000000000000000000004601230617144500166410ustar00rootroot00000000000000#ifndef NL_H #define NL_H #include struct Nodelist { char *name; char *desc; bool (*nlStart)(char *errbuf); void (*nlEnd)(void); bool (*nlCheckNode)(struct Node4D *node); long (*nlGetHub)(struct Node4D *node); long (*nlGetRegion)(struct Node4D *node); }; #endif crashmail-1.5/src/crashmail/nl_cmnl.c000066400000000000000000000020401230617144500176410ustar00rootroot00000000000000#include "crashmail.h" #include "cmnllib/cmnllib.h" osFile nlfh; bool cmnl_nlStart(char *errbuf) { if(!(nlfh=cmnlOpenNL(config.cfg_Nodelist))) { strcpy(errbuf,cmnlLastError()); return(FALSE); } return(TRUE); } void cmnl_nlEnd(void) { cmnlCloseNL(nlfh); } bool cmnl_nlCheckNode(struct Node4D *node) { struct cmnlIdx idx; idx.zone=node->Zone; idx.net=node->Net; idx.node=node->Node; idx.point=node->Point; if(!cmnlFindNL(nlfh,config.cfg_Nodelist,&idx,NULL,0)) return(FALSE); return(TRUE); } long cmnl_nlGetHub(struct Node4D *node) { struct cmnlIdx idx; idx.zone=node->Zone; idx.net=node->Net; idx.node=node->Node; idx.point=node->Point; if(!cmnlFindNL(nlfh,config.cfg_Nodelist,&idx,NULL,0)) return(-1); return(idx.hub); } long cmnl_nlGetRegion(struct Node4D *node) { struct cmnlIdx idx; idx.zone=node->Zone; idx.net=node->Net; idx.node=node->Node; idx.point=node->Point; if(!cmnlFindNL(nlfh,config.cfg_Nodelist,&idx,NULL,0)) return(-1); return(idx.region); } crashmail-1.5/src/crashmail/nl_cmnl.h000066400000000000000000000002711230617144500176520ustar00rootroot00000000000000bool cmnl_nlStart(char *errbuf); void cmnl_nlEnd(void); bool cmnl_nlCheckNode(struct Node4D *node); long cmnl_nlGetHub(struct Node4D *node); long cmnl_nlGetRegion(struct Node4D *node); crashmail-1.5/src/crashmail/nl_v7p.c000066400000000000000000000214531230617144500174350ustar00rootroot00000000000000#include "crashmail.h" /* Data records for V7+ nodelist. As far as I can understand, these should be stored on disk in native format. /JB */ #define V7P_NDXFILENAME "NODEX.ndx" #define V7P_DATFILENAME "NODEX.dat" #define V7P_DTPFILENAME "NODEX.dtp" /* The Linux version of FastLst seems to use lower case for suffixes and upper case for the rest */ #define V7P_NDXBUFSIZE 512 struct v7p_ndxcontrol { long indexstart; long rootstart; long lastblock; long firstleaf; long lastleaf; long freelist; uint16_t levels; uint16_t xor; }; struct v7p_ndxindex { long rectype; long prev; long next; short keycount; uint16_t keystart; }; struct v7p_ndxindexkey { uint16_t offset; uint16_t len; long value; long lower; }; struct v7p_ndxleaf { long rectype; long prev; long next; short keycount; uint16_t keystart; }; struct v7p_ndxleafkey { uint16_t offset; uint16_t len; long value; }; struct v7p_datheader { short Zone,Net,Node,HubNode; short CallCost,MsgFee,NodeFlags; char ModemType; char Phone_len; char Password_len; char Bname_len; char Sname_len; char Cname_len; char pack_len; char BaudRate; }; osFile v7p_ndxfh; osFile v7p_datfh; osFile v7p_dtpfh; uint16_t v7p_ndxrecsize; struct v7p_ndxcontrol v7p_ndxcontrol; struct v7p_datheader v7p_datheader; char v7p_ndxbuf[V7P_NDXBUFSIZE]; int v7p_ndxcompare(char *d1,char *d2,int len) { struct Node4D n1,n2; /* char b1[100],b2[100]; */ if(len != 8 && len != 6) return(-1); /* Weird, but definitely not a match */ memcpy(&n1,d1,len); memcpy(&n2,d2,sizeof(struct Node4D)); if(len == 6) n1.Point=0; /* Print4D(&n1,b1); Print4D(&n2,b2); printf("compare %s and %s, %d\n",b1,b2,Compare4D(&n1,&n2)); */ return Compare4D(&n1,&n2); } bool v7p_findoffset(struct Node4D *node,uint32_t *offset) { int i,res; long recnum; struct v7p_ndxindex *v7p_ndxindex; struct v7p_ndxindexkey *v7p_ndxindexkey; struct v7p_ndxleaf *v7p_ndxleaf; struct v7p_ndxleafkey *v7p_ndxleafkey; v7p_ndxindex=(struct v7p_ndxindex *)v7p_ndxbuf; v7p_ndxleaf=(struct v7p_ndxleaf *)v7p_ndxbuf; recnum=v7p_ndxcontrol.indexstart; osSeek(v7p_ndxfh,recnum*v7p_ndxrecsize,OFFSET_BEGINNING); if(osRead(v7p_ndxfh,v7p_ndxbuf,v7p_ndxrecsize) != v7p_ndxrecsize) return(FALSE); while(v7p_ndxindex->rectype != -1) { if(v7p_ndxindex->keycount < 0) return(FALSE); for(i=0;i < v7p_ndxindex->keycount;i++) { v7p_ndxindexkey=(struct v7p_ndxindexkey *)(v7p_ndxbuf+sizeof(struct v7p_ndxindex)+i*sizeof(struct v7p_ndxindexkey)); if(v7p_ndxcompare(v7p_ndxbuf+v7p_ndxindexkey->offset,(char *)node,v7p_ndxindexkey->len) > 0) break; } if(i==0) { recnum=v7p_ndxindex->rectype; } else { v7p_ndxindexkey=(struct v7p_ndxindexkey *)(v7p_ndxbuf+sizeof(struct v7p_ndxindex)+(i-1)*sizeof(struct v7p_ndxindexkey)); recnum=v7p_ndxindexkey->lower; } osSeek(v7p_ndxfh,recnum*v7p_ndxrecsize,OFFSET_BEGINNING); if(osRead(v7p_ndxfh,v7p_ndxbuf,v7p_ndxrecsize) != v7p_ndxrecsize) return(FALSE); } if(v7p_ndxleaf->keycount <= 0) return(FALSE); for(i=0;i < v7p_ndxleaf->keycount;i++) { v7p_ndxleafkey=(struct v7p_ndxleafkey *)(v7p_ndxbuf+sizeof(struct v7p_ndxleaf)+i*sizeof(struct v7p_ndxleafkey)); res=v7p_ndxcompare(v7p_ndxbuf+v7p_ndxleafkey->offset,(char *)node,v7p_ndxleafkey->len); if(res > 0) { return(FALSE); } else if(res == 0) { *offset=v7p_ndxleafkey->value; break; } } if(i == v7p_ndxleaf->keycount) return(FALSE); return(TRUE); } uint32_t v7p_unpack(char *dest,char *pack,uint32_t size) { uint32_t c,d; uint16_t w; char *table=" EANROSTILCHBDMUGPKYWFVJXZQ-'0123456789"; d=0; for(c=0;c sz) sz=v7p_datheader.Password_len; if(v7p_datheader.pack_len > sz) sz=v7p_datheader.pack_len; if(!(junk=osAlloc(sz))) { return(FALSE); } if(!(unpacked=osAlloc(sz*3/2+3))) { osFree(junk); return(FALSE); } if(osRead(v7p_datfh,junk,v7p_datheader.Phone_len) != v7p_datheader.Phone_len) { osFree(junk); osFree(unpacked); return(FALSE); } if(osRead(v7p_datfh,junk,v7p_datheader.Password_len) != v7p_datheader.Password_len) { osFree(junk); osFree(unpacked); return(FALSE); } if(osRead(v7p_datfh,junk,v7p_datheader.pack_len) != v7p_datheader.pack_len) { osFree(junk); osFree(unpacked); return(FALSE); } d=v7p_unpack(unpacked,junk,v7p_datheader.pack_len); unpacked[d]=0; sum = v7p_datheader.Bname_len + v7p_datheader.Sname_len + v7p_datheader.Cname_len; if(d-sum < 8) { /* not v7+ */ osFree(junk); osFree(unpacked); return(FALSE); } for(c=0;c<8;c++) if(!isxdigit(unpacked[sum+c])) { /* not v7+ */ osFree(junk); osFree(unpacked); return(FALSE); } unpacked[sum+8]=0; dtpoffset=hextodec(&unpacked[sum]); osSeek(v7p_dtpfh,dtpoffset,OFFSET_BEGINNING); if(osRead(v7p_dtpfh,junk,4) != 4) { osFree(junk); osFree(unpacked); return(FALSE); } *region = junk[0]+junk[1]*256; /* Platform-independent */ *hub = junk[2]+junk[3]*256; osFree(junk); osFree(unpacked); return(TRUE); } bool v7p_nlStart(char *errbuf) { char ndxname[120],datname[120],dtpname[120]; MakeFullPath(config.cfg_Nodelist,V7P_NDXFILENAME,ndxname,120); MakeFullPath(config.cfg_Nodelist,V7P_DATFILENAME,datname,120); MakeFullPath(config.cfg_Nodelist,V7P_DTPFILENAME,dtpname,120); if(!(v7p_ndxfh=osOpen(ndxname,MODE_OLDFILE))) { sprintf(errbuf,"Failed to open V7+ index file \"%s\"",ndxname); return(FALSE); } if(!(v7p_datfh=osOpen(datname,MODE_OLDFILE))) { sprintf(errbuf,"Failed to open V7+ data file \"%s\"",datname); osClose(v7p_ndxfh); return(FALSE); } if(!(v7p_dtpfh=osOpen(dtpname,MODE_OLDFILE))) { sprintf(errbuf,"Failed to open V7+ dtp file \"%s\" (not a V7+ nodelist?)",dtpname); osClose(v7p_ndxfh); osClose(v7p_datfh); return(FALSE); } if(osRead(v7p_ndxfh,&v7p_ndxrecsize,sizeof(uint16_t))!=sizeof(uint16_t)) { sprintf(errbuf,"V7+ nodelist \"%s\" appears to be corrupt",config.cfg_Nodelist); osClose(v7p_ndxfh); osClose(v7p_datfh); osClose(v7p_dtpfh); return(FALSE); } if(v7p_ndxrecsize > V7P_NDXBUFSIZE) { sprintf(errbuf,"Record size of V7+ nodelist is too big (%d chars, max is %d chars)",v7p_ndxrecsize,V7P_NDXBUFSIZE); osClose(v7p_ndxfh); osClose(v7p_datfh); osClose(v7p_dtpfh); return(FALSE); } if(osRead(v7p_ndxfh,&v7p_ndxcontrol,sizeof(struct v7p_ndxcontrol))!=sizeof(struct v7p_ndxcontrol)) { sprintf(errbuf,"V7+ nodelist \"%s\" appears to be corrupt",config.cfg_Nodelist); osClose(v7p_ndxfh); osClose(v7p_datfh); osClose(v7p_dtpfh); return(FALSE); } return(TRUE); } void v7p_nlEnd(void) { osClose(v7p_ndxfh); osClose(v7p_datfh); osClose(v7p_dtpfh); } bool v7p_nlCheckNode(struct Node4D *node) { uint32_t junk; if(v7p_findoffset(node,&junk)) return(TRUE); return(FALSE); } long v7p_nlGetHub(struct Node4D *node) { struct Node4D t4d; uint32_t hub,region,datoffset; Copy4D(&t4d,node); t4d.Point=0; if(!v7p_findoffset(&t4d,&datoffset)) return(-1); if(!v7p_gethubregion(datoffset,&hub,®ion)) return(-1); return(hub); } long v7p_nlGetRegion(struct Node4D *node) { struct Node4D t4d; uint32_t hub,region,datoffset; Copy4D(&t4d,node); t4d.Point=0; if(!v7p_findoffset(&t4d,&datoffset)) return(-1); if(!v7p_gethubregion(datoffset,&hub,®ion)) return(-1); return(region); } /* for testing int main(int argc, char **argv) { char err[200]; struct Node4D n; nlname=argv[1]; if(!v7p_nlStart(err)) { printf("err: %s\n",err); exit(10); } Parse4D(argv[2],&n); printf(" check: %d\n",v7p_nlCheckNode(&n)); printf(" hub: %d\n",v7p_nlGetHub(&n)); printf("region: %d\n",v7p_nlGetRegion(&n)); v7p_nlEnd(); exit(0); } */ crashmail-1.5/src/crashmail/nl_v7p.h000066400000000000000000000002641230617144500174370ustar00rootroot00000000000000bool v7p_nlStart(char *errbuf); void v7p_nlEnd(void); bool v7p_nlCheckNode(struct Node4D *node); long v7p_nlGetHub(struct Node4D *node); long v7p_nlGetRegion(struct Node4D *node); crashmail-1.5/src/crashmail/node4dpat.c000066400000000000000000000222441230617144500201110ustar00rootroot00000000000000#include "crashmail.h" bool rawParse4DPat(char *buf, struct Node4DPat *node) { uint32_t c=0,tempc=0; char temp[10]; bool GotZone=FALSE,GotNet=FALSE,GotNode=FALSE; strcpy(node->Zone,"*"); strcpy(node->Net,"*"); strcpy(node->Node,"*"); strcpy(node->Point,"*"); if(strcmp(buf,"*")==0) return(TRUE); for(c=0;cZone,temp); GotZone=TRUE; tempc=0; } else if(buf[c]=='/') { if(GotNet || GotNode) return(FALSE); strcpy(node->Net,temp); GotNet=TRUE; tempc=0; } else if(buf[c]=='.') { if(GotNode) return(FALSE); strcpy(node->Node,temp); node->Point[0]=0; GotNode=TRUE; tempc=0; } else if((buf[c]>='0' && buf[c]<='9') || buf[c]=='*' || buf[c]=='?') { if(tempc<9) { temp[tempc++]=buf[c]; temp[tempc]=0; } } else return(FALSE); } if(GotZone && !GotNet) { strcpy(node->Net,temp); } else if(GotNode) { strcpy(node->Point,temp); } else { strcpy(node->Node,temp); strcpy(node->Point,"0"); } return(TRUE); } bool Parse4DPat(char *buf, struct Node4DPat *node) { bool res; node->Zone[0]=0; node->Net[0]=0; node->Node[0]=0; node->Point[0]=0; if(strnicmp(buf,"ZONE ",5)==0) { node->Type=PAT_ZONE; res=rawParse4DPat(&buf[5],node); strcpy(node->Zone,node->Node); node->Net[0]=0; node->Node[0]=0; node->Point[0]=0; } else if(strnicmp(buf,"REGION ",7)==0) { node->Type=PAT_REGION; res=rawParse4DPat(&buf[7],node); node->Node[0]=0; node->Point[0]=0; } else if(strnicmp(buf,"NET ",4)==0) { node->Type=PAT_NET; res=rawParse4DPat(&buf[4],node); node->Node[0]=0; node->Point[0]=0; } else if(strnicmp(buf,"HUB ",4)==0) { node->Type=PAT_HUB; res=rawParse4DPat(&buf[4],node); node->Point[0]=0; } else if(strnicmp(buf,"NODE ",5)==0) { node->Type=PAT_NODE; res=rawParse4DPat(&buf[5],node); node->Point[0]=0; } else { node->Type=PAT_PATTERN; res=rawParse4DPat(buf,node); } return(res); } bool Parse4DDestPat(char *buf, struct Node4DPat *node) { bool res; node->Zone[0]=0; node->Net[0]=0; node->Node[0]=0; node->Point[0]=0; res=TRUE; if(stricmp(buf,"ZONE")==0) { node->Type=PAT_ZONE; } else if(stricmp(buf,"REGION")==0) { node->Type=PAT_REGION; } else if(stricmp(buf,"NET")==0) { node->Type=PAT_NET; } else if(stricmp(buf,"HUB")==0) { node->Type=PAT_HUB; } else if(stricmp(buf,"NODE")==0) { node->Type=PAT_NODE; } else { node->Type=PAT_PATTERN; res=rawParse4DPat(buf,node); } return(res); } int NodeCompare(char *pat,uint16_t num) { char buf[10]; uint8_t c; sprintf(buf,"%u",num); if(pat[0]==0) return(0); for(c=0;cType) { case PAT_PATTERN: if(node->Zone!=0) if(NodeCompare(nodepat->Zone, node->Zone )!=0) return(1); if(NodeCompare(nodepat->Net, node->Net )!=0) return(1); if(NodeCompare(nodepat->Node, node->Node )!=0) return(1); if(NodeCompare(nodepat->Point,node->Point)!=0) return(1); return(0); case PAT_ZONE: if(NodeCompare(nodepat->Zone,node->Zone)!=0) return(1); return(0); case PAT_REGION: if(!nodelistopen) return(1); Copy4D(&n4d,node); n4d.Point=0; num=(*config.cfg_NodelistType->nlGetRegion)(&n4d); if(num == -1) return(1); if(node->Zone!=0) if(NodeCompare(nodepat->Zone,node->Zone)!=0) return(1); if(NodeCompare(nodepat->Net,num)!=0) return(1); return(0); case PAT_NET: if(node->Zone!=0) if(NodeCompare(nodepat->Zone,node->Zone)!=0) return(1); if(NodeCompare(nodepat->Net,node->Net)!=0) return(1); return(0); case PAT_HUB: if(!nodelistopen) return(1); Copy4D(&n4d,node); n4d.Point=0; num=(*config.cfg_NodelistType->nlGetHub)(&n4d); if(num == -1) return(1); if(node->Zone!=0) if(NodeCompare(nodepat->Zone,node->Zone)!=0) return(1); if(NodeCompare(nodepat->Net,node->Net)!=0) return(1); if(NodeCompare(nodepat->Node,num)!=0) return(1); return(0); case PAT_NODE: if(node->Zone!=0) if(NodeCompare(nodepat->Zone, node->Zone )!=0) return(1); if(NodeCompare(nodepat->Net, node->Net )!=0) return(1); if(NodeCompare(nodepat->Node, node->Node )!=0) return(1); return(0); } return(1); } void Print4DPat(struct Node4DPat *pat,char *dest) { switch(pat->Type) { case PAT_PATTERN: sprintf(dest,"%s:%s/%s.%s",pat->Zone,pat->Net,pat->Node,pat->Point); break; case PAT_ZONE: sprintf(dest,"ZONE %s",pat->Zone); break; case PAT_REGION: sprintf(dest,"REGION %s:%s",pat->Zone,pat->Net); break; case PAT_NET: sprintf(dest,"NET %s:%s",pat->Zone,pat->Net); break; case PAT_HUB: sprintf(dest,"HUB %s:%s/%s",pat->Zone,pat->Net,pat->Node); break; case PAT_NODE: sprintf(dest,"NODE %s:%s/%s",pat->Zone,pat->Net,pat->Node); break; } } void Print4DDestPat(struct Node4DPat *pat,char *dest) { switch(pat->Type) { case PAT_PATTERN: sprintf(dest,"%s:%s/%s.%s",pat->Zone,pat->Net,pat->Node,pat->Point); break; case PAT_ZONE: sprintf(dest,"ZONE"); break; case PAT_REGION: sprintf(dest,"REGION"); break; case PAT_NET: sprintf(dest,"NET"); break; case PAT_HUB: sprintf(dest,"HUB"); break; case PAT_NODE: sprintf(dest,"NODE"); break; } } bool Check4DPatNodelist(struct Node4DPat *pat) { if(pat->Type == PAT_HUB || pat->Type == PAT_REGION) return(FALSE); return(TRUE); } bool Parse2DPat(char *buf, struct Node2DPat *node) { uint32_t c=0,tempc=0; char temp[10]; bool GotNet=FALSE; strcpy(node->Net,"*"); strcpy(node->Node,"*"); if(strcmp(buf,"*")==0) return(TRUE); for(c=0;cNet,temp); GotNet=TRUE; tempc=0; } else if((buf[c]>='0' && buf[c]<='9') || buf[c]=='*' || buf[c]=='?') { if(tempc<9) { temp[tempc++]=buf[c]; temp[tempc]=0; } } else return(FALSE); } strcpy(node->Node,temp); return(TRUE); } int Compare2DPat(struct Node2DPat *nodepat,uint16_t net,uint16_t node) { if(NodeCompare(nodepat->Net, net )!=0) return(1); if(NodeCompare(nodepat->Node, node)!=0) return(1); return(0); } /* Expand destpat */ bool iswildcard(char *str) { int c; for(c=0;cType == PAT_REGION) region=(*config.cfg_NodelistType->nlGetRegion)(&n4d); if(temproute->Type == PAT_HUB) hub=(*config.cfg_NodelistType->nlGetHub)(&n4d); if(region == -1) region=0; if(hub == -1) hub=0; } if(region == 0) region=dest->Net; switch(temproute->Type) { case PAT_PATTERN: sendto->Zone = iswildcard(temproute->Zone) ? dest->Zone : atoi(temproute->Zone); sendto->Net = iswildcard(temproute->Net) ? dest->Net : atoi(temproute->Net); sendto->Node = iswildcard(temproute->Node) ? dest->Node : atoi(temproute->Node); sendto->Point = iswildcard(temproute->Point) ? dest->Point : atoi(temproute->Point); break; case PAT_ZONE: sendto->Zone = dest->Zone; sendto->Net = dest->Zone; sendto->Node = 0; sendto->Point = 0; break; case PAT_REGION: sendto->Zone = dest->Zone; sendto->Net = region; sendto->Node = 0; sendto->Point = 0; break; case PAT_NET: sendto->Zone = dest->Zone; sendto->Net = dest->Net; sendto->Node = 0; sendto->Point = 0; break; case PAT_HUB: sendto->Zone = dest->Zone; sendto->Net = dest->Net; sendto->Node = hub; sendto->Point = 0; break; case PAT_NODE: sendto->Zone = dest->Zone; sendto->Net = dest->Net; sendto->Node = dest->Node; sendto->Point = 0; break; } } crashmail-1.5/src/crashmail/node4dpat.h000066400000000000000000000016101230617144500201100ustar00rootroot00000000000000#ifndef NODE4DPAT_H #define NODE4DPAT_H #define PAT_PATTERN 0 #define PAT_ZONE 1 #define PAT_REGION 2 #define PAT_NET 3 #define PAT_HUB 4 #define PAT_NODE 5 struct Node4DPat { char Type; char Zone[10]; char Point[10]; char Net[10]; char Node[10]; }; struct Node2DPat { char Net[10]; char Node[10]; }; bool Parse4DPat(char *buf, struct Node4DPat *node); bool Parse4DDestPat(char *buf, struct Node4DPat *node); int Compare4DPat(struct Node4DPat *nodepat,struct Node4D *node); void Print4DPat(struct Node4DPat *pat,char *dest); void Print4DDestPat(struct Node4DPat *pat,char *dest); bool Check4DPatNodelist(struct Node4DPat *pat); bool Parse2DPat(char *buf, struct Node2DPat *node); int Compare2DPat(struct Node2DPat *nodepat,uint16_t net,uint16_t node); void ExpandNodePat(struct Node4DPat *temproute,struct Node4D *dest,struct Node4D *sendto); #endif crashmail-1.5/src/crashmail/outbound.c000066400000000000000000000507421230617144500200720ustar00rootroot00000000000000#include "crashmail.h" struct jbList ArcList; bool doAddFlow(char *filename,char *basename,char type,long mode); bool LockBasename(char *basename) { char buf[200]; osFile fp; strcpy(buf,basename); strcat(buf,".bsy"); if(osExists(buf)) return(FALSE); if(!(fp=osOpen(buf,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to create busy file %s\n",buf); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } osClose(fp); return(TRUE); } void UnlockBasename(char *basename) { char buf[200]; strcpy(buf,basename); strcat(buf,".bsy"); osDelete(buf); } void MakeBaseName(struct Node4D *n4d,char *basename) { struct Aka *firstaka; struct Route *tmproute; bool samedomain; char *ospathchars; uint32_t num,c; char buf[50]; ospathchars=OS_PATH_CHARS; for(tmproute=(struct Route *)config.RouteList.First;tmproute;tmproute=tmproute->Next) if(Compare4DPat(&tmproute->Pattern,n4d)==0) break; firstaka=(struct Aka *)config.AkaList.First; samedomain=FALSE; if(!tmproute) samedomain=TRUE; else if(tmproute->Aka->Domain[0]==0 || firstaka->Domain[0]==0 || stricmp(tmproute->Aka->Domain,firstaka->Domain)==0) samedomain=TRUE; if(samedomain) { /* Main domain */ strcpy(basename,config.cfg_Outbound); if(basename[0]) { if(strchr(ospathchars,basename[strlen(basename)-1])) basename[strlen(basename)-1]=0; /* Strip / */ } if(n4d->Zone != firstaka->Node.Zone) { /* Not in main zone */ num=n4d->Zone; if(!(config.cfg_Flags & CFG_NOMAXOUTBOUNDZONE)) { if(num > 0xfff) num=0xfff; } sprintf(buf,".%03x",num); strcat(basename,buf); } } else { /* Other domain */ strcpy(basename,config.cfg_Outbound); if(basename[0]) { if(strchr(ospathchars,basename[strlen(basename)-1])) basename[strlen(basename)-1]=0; /* Strip / */ } *GetFilePart(basename)=0; /* Use domain as last component in path */ strcat(basename,tmproute->Aka->Domain); num=n4d->Zone; if(!(config.cfg_Flags & CFG_NOMAXOUTBOUNDZONE)) { if(num > 0xfff) num=0xfff; } sprintf(buf,".%03x",num); strcat(basename,buf); } if(!osExists(basename)) osMkDir(basename); /* Add slash */ c=strlen(basename); basename[c++]=ospathchars[0]; basename[c++]=0; /* Add net/node */ sprintf(buf,"%04x%04x",n4d->Net,n4d->Node); strcat(basename,buf); if(n4d->Point) { strcat(basename,".pnt"); if(!osExists(basename)) osMkDir(basename); /* Add slash */ c=strlen(basename); basename[c++]=ospathchars[0]; basename[c++]=0; /* Add point */ sprintf(buf,"%08x",n4d->Point); strcat(basename,buf); } } void WriteIndex(void) { osFile fh; char buf[200]; struct ConfigNode *cnode; MakeFullPath(config.cfg_PacketDir,"cmindex",buf,200); /* Get basenum */ if(!(fh=osOpen(buf,MODE_NEWFILE))) return; for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(cnode->LastArcName[0]) { Print4D(&cnode->Node,buf); osFPrintf(fh,"%s %s\n",buf,cnode->LastArcName); } osClose(fh); } void ReadIndex(void) { osFile fh; char buf[200],buf2[200]; uint32_t jbcpos; struct ConfigNode *cnode,*c1,*c2; struct Node4D n4d; MakeFullPath(config.cfg_PacketDir,"cmindex",buf,200); /* Get basenum */ if(!(fh=osOpen(buf,MODE_OLDFILE))) return; while(osFGets(fh,buf,200)) { striptrail(buf); jbcpos=0; jbstrcpy(buf2,buf,200,&jbcpos); if(Parse4D(buf2,&n4d)) { jbstrcpy(buf2,buf,200,&jbcpos); for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,&n4d)==0) mystrncpy(cnode->LastArcName,buf2,13); } } osClose(fh); /* Check for duplicates */ for(c1=(struct ConfigNode *)config.CNodeList.First;c1;c1=c1->Next) for(c2=c1->Next;c2;c2=c2->Next) if(c1->LastArcName[0] && hextodec(c1->LastArcName) == hextodec(c2->LastArcName)) { LogWrite(1,TOSSINGINFO,"Warning: The same bundle name is used for %u:%u/%u.%u and %u:%u/%u.%u", c1->Node.Zone, c1->Node.Net, c1->Node.Node, c1->Node.Point, c2->Node.Zone, c2->Node.Net, c2->Node.Node, c2->Node.Point); LogWrite(1,TOSSINGINFO,"Cleared bundle name for %u:%u/%u.%u", c2->Node.Zone, c2->Node.Net, c2->Node.Node, c2->Node.Point); c2->LastArcName[0]=0; WriteIndex(); } } bool ExistsBasenum(uint32_t num) { char name[20]; struct osFileEntry *fe; struct ConfigNode *cnode; sprintf(name,"%08x.",num); for(fe=(struct osFileEntry *)ArcList.First;fe;fe=fe->Next) if(IsArc(fe->Name) && hextodec(fe->Name) == num) return(TRUE); for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(cnode->LastArcName[0] && hextodec(cnode->LastArcName) == num) return(TRUE); return(FALSE); } bool ExistsBundle(uint32_t basenum,uint32_t num) { char name[20]; struct osFileEntry *fe; char *daynames[]={"su","mo","tu","we","th","fr","sa"}; sprintf(name,"%08x.%s%d",basenum,daynames[num/10],num%10); for(fe=(struct osFileEntry *)ArcList.First;fe;fe=fe->Next) if(stricmp(fe->Name,name)==0) return(TRUE); return(FALSE); } void MakeArcName(struct ConfigNode *cnode,char *dest) { struct osFileEntry *fe,*foundfe; char ext[10]; uint32_t basenum; uint32_t suffix,newsuffix,day,i; char *daynames[]={"su","mo","tu","we","th","fr","sa"}; time_t t; struct tm *tp; time(&t); tp=localtime(&t); day=tp->tm_wday; /* Get basenum and suffix of latest bundle */ suffix=-1; if(!cnode->LastArcName[0]) { basenum=time(NULL); while(ExistsBasenum(basenum)) basenum++; } else { basenum=hextodec(cnode->LastArcName); strncpy(ext,&cnode->LastArcName[strlen(cnode->LastArcName)-3],3); ext[2]=0; for(i=0;i<7;i++) { if(stricmp(ext,daynames[i])==0) { suffix=i*10; suffix+=cnode->LastArcName[strlen(cnode->LastArcName)-1]-'0'; } } } /* Does LastArcName still exist in directory? */ foundfe=NULL; if(cnode->LastArcName[0]) { for(fe=(struct osFileEntry *)ArcList.First;fe;fe=fe->Next) if(stricmp(cnode->LastArcName,fe->Name)==0) foundfe=fe; } if(suffix == -1) { if((config.cfg_Flags & CFG_WEEKDAYNAMING)) newsuffix=day*10; else newsuffix=0; } else { newsuffix=suffix; if(!foundfe) { newsuffix=-1; } else { if(foundfe->Size == 0) newsuffix=-1; if(foundfe->Size > config.cfg_MaxBundleSize) newsuffix=-1; } if((config.cfg_Flags & CFG_WEEKDAYNAMING) && suffix/10 != day) newsuffix=-1; if(newsuffix == -1) { newsuffix=suffix+1; if(newsuffix == 70) newsuffix=0; if((config.cfg_Flags & CFG_WEEKDAYNAMING) && newsuffix/10 != day) newsuffix=day*10; if(ExistsBundle(basenum,newsuffix)) newsuffix=suffix; } } sprintf(dest,"%08x.%s%d",basenum,daynames[newsuffix/10],newsuffix%10); if(stricmp(cnode->LastArcName,dest)!=0) { mystrncpy(cnode->LastArcName,dest,13); WriteIndex(); } } void DeleteZero(char *dir,struct jbList *arclist) { struct osFileEntry *fe,*fe2; char buf[200]; /* Delete zero length bundles for this node */ fe=(struct osFileEntry *)arclist->First; while(fe) { fe2=fe->Next; if(fe->Size == 0) { MakeFullPath(dir,fe->Name,buf,200); LogWrite(2,TOSSINGINFO,"Deleting zero length bundle %s",buf); osDelete(buf); jbFreeNode(&ArcList,(struct jbNode *)fe); } fe=fe2; } } void HandleOrphan(char *name) { osFile fh; char buf[200],buf2[200]; char type; bool mode; uint32_t jbcpos; struct Node4D n4d; char basename[200]; if(!(fh=osOpen(name,MODE_OLDFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open orphan file \"%s\"",name); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return; } if(!osFGets(fh,buf,100)) { LogWrite(1,SYSTEMERR,"Orphan file \"%s\" contains no information",name); osClose(fh); return; } osClose(fh); jbcpos=0; jbstrcpy(buf2,buf,100,&jbcpos); if(stricmp(buf2,"Normal")==0) type=PKTS_NORMAL; else if(stricmp(buf2,"Hold")==0) type=PKTS_HOLD; else if(stricmp(buf2,"Direct")==0) type=PKTS_DIRECT; else if(stricmp(buf2,"Crash")==0) type=PKTS_CRASH; else { LogWrite(1,SYSTEMERR,"Unknown flavour \"%s\" in \"%s\"",buf2,name); return; } jbstrcpy(buf2,buf,100,&jbcpos); if(!Parse4D(buf2,&n4d)) { LogWrite(1,SYSTEMERR,"Invalid node \"%s\" in \"%s\"",buf2,name); return; } mode=FLOW_NONE; jbstrcpy(buf2,buf,100,&jbcpos); if(stricmp(buf2,"Truncate")==0) mode=FLOW_TRUNC; if(stricmp(buf2,"Delete")==0) mode=FLOW_DELETE; mystrncpy(buf,name,200); buf[strlen(buf)-7]=0; /* Remove .orphan */ MakeBaseName(&n4d,basename); if(!LockBasename(basename)) { printf("Cannot add to %s, node is busy...\n",GetFilePart(basename)); return; } if(doAddFlow(buf,basename,type,mode)) osDelete(name); /* Orphan file no longer needed */ UnlockBasename(basename); } void MakeOrphan(char *file,struct Node4D *n4d,char type,long mode) { char buf[200]; osFile fh; strcpy(buf,file); strcat(buf,".orphan"); if(!(fh=osOpen(buf,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open \"%s\", cannot make .orphan file",buf); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return; } sprintf(buf,"%s %d:%d/%d.%d",prinames[(int)type],n4d->Zone,n4d->Net,n4d->Node,n4d->Point); if(mode==FLOW_TRUNC) strcat(buf," Truncate"); else if(mode==FLOW_DELETE) strcat(buf," Delete"); strcat(buf,"\n"); osPuts(fh,buf); osClose(fh); } /* Only call if file is already locked */ /* MakeOrphan() should be called if necessary */ bool doAddFlow(char *filename,char *basename,char type,long mode) { char buf[200],letter,*prefix; osFile fh; switch(type) { case PKTS_NORMAL: case PKTS_ECHOMAIL: letter='f'; break; case PKTS_HOLD: letter='h'; break; case PKTS_DIRECT: letter='d'; break; case PKTS_CRASH: letter='c'; break; default: letter='f'; } sprintf(buf,"%s.%clo",basename,letter); if(!(fh=osOpen(buf,MODE_READWRITE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open \"%s\"",buf); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } while(osFGets(fh,buf,200)) { striptrail(buf); if(buf[0]=='#') strcpy(buf,&buf[1]); if(buf[0]=='~') strcpy(buf,&buf[1]); if(buf[0]=='^') strcpy(buf,&buf[1]); if(buf[0]=='-') strcpy(buf,&buf[1]); if(stricmp(buf,filename)==0) { osClose(fh); return(TRUE); /* Was already in flow file */ } } osSeek(fh,0,OFFSET_END); prefix=""; if(mode == FLOW_TRUNC) prefix="#"; if(mode == FLOW_DELETE) prefix="^"; if(config.cfg_Flags & CFG_FLOWCRLF) osFPrintf(fh,"%s%s\r\n",prefix,filename); else osFPrintf(fh,"%s%s\n",prefix,filename); osClose(fh); return(TRUE); } /* Handles locking and MakeOrphan() */ bool AddFlow(char *filename,struct Node4D *n4d,char type,long mode) { char basename[200]; MakeBaseName(n4d,basename); if(!LockBasename(basename)) { printf("Cannot add to %s, node is busy...\n",GetFilePart(basename)); MakeOrphan(filename,n4d,type,mode); return(FALSE); } if(!doAddFlow(filename,basename,type,mode)) MakeOrphan(filename,n4d,type,mode); UnlockBasename(basename); return(TRUE); } bool MakePktTmp(char *name) { char buf[200]; MakeFullPath(config.cfg_PacketDir,GetFilePart(name),buf,200); strcpy(&buf[strlen(buf)-6],"pkttmp"); /* Change suffix */ if(!movefile(name,buf)) { LogWrite(1,SYSTEMERR,"Failed to move file \"%s\" to \"%s\"",name,buf); return(FALSE); } return(TRUE); } void UpdateFile(char *name) { struct osFileEntry *newfe,*fe; if(!(newfe=osGetFileEntry(name))) return; for(fe=(struct osFileEntry *)ArcList.First;fe;fe=fe->Next) if(stricmp(fe->Name,name)==0) break; if(fe) { fe->Date=newfe->Date; fe->Size=newfe->Size; osFree(newfe); } else { jbAddNode(&ArcList,(struct jbNode *)newfe); } } #define COPYBUFSIZE 5000 bool PackFile(char *file) { char basename[200],arcname[200],pktname[200],buf[200],buf2[200],*copybuf; uint32_t jbcpos,readlen; int c,res; struct Node4D n4d; char type; char letter; osFile ifh,ofh; /* Parse filename */ mystrncpy(buf,GetFilePart(file),200); for(c=0;buf[c];c++) if(buf[c]=='_') buf[c]=' '; jbcpos=0; jbstrcpy(buf2,buf,100,&jbcpos); jbstrcpy(buf2,buf,100,&jbcpos); if(stricmp(buf2,"Normal")==0) type=PKTS_NORMAL; else if(stricmp(buf2,"Hold")==0) type=PKTS_HOLD; else if(stricmp(buf2,"Direct")==0) type=PKTS_DIRECT; else if(stricmp(buf2,"Crash")==0) type=PKTS_CRASH; else if(stricmp(buf2,"Echomail")==0) type=PKTS_ECHOMAIL; else { LogWrite(1,TOSSINGERR,"Unknown flavour \"%s\" for \"%s\"",buf2,file); return(FALSE); } jbstrcpy(buf2,buf,100,&jbcpos); n4d.Zone=atol(buf2); jbstrcpy(buf2,buf,100,&jbcpos); n4d.Net=atol(buf2); jbstrcpy(buf2,buf,100,&jbcpos); n4d.Node=atol(buf2); jbstrcpy(buf2,buf,100,&jbcpos); n4d.Point=atol(buf2); /* Make basename for this node */ MakeBaseName(&n4d,basename); if(!LockBasename(basename)) { LogWrite(1,TOSSINGINFO,"Cannot add \"%s\" to outbound, node is busy...",GetFilePart(file)); return(FALSE); } /* Handle echomail packet */ if(type == PKTS_ECHOMAIL) { struct ConfigNode *cnode; for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,&n4d)==0) break; if(cnode && cnode->Packer) { /* Pack echomail */ MakeArcName(cnode,buf); MakeFullPath(config.cfg_PacketDir,buf,arcname,200); mystrncpy(pktname,file,200); GetFilePart(pktname)[8]=0; strcat(pktname,".pkt"); LogWrite(4,TOSSINGINFO,"Packing %s for %d:%d/%d.%d with %s", GetFilePart(pktname), cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point, cnode->Packer->Name); osRename(file,pktname); if(config.cfg_BeforePack[0]) { ExpandPacker(config.cfg_BeforePack,buf,200,arcname,pktname); res=osExecute(buf); if(res != 0) { osRename(pktname,file); LogWrite(1,SYSTEMERR,"BEFOREPACK command failed: %u",res); UnlockBasename(basename); return(FALSE); } } ExpandPacker(cnode->Packer->Packer,buf,200,arcname,pktname); res=osExecute(buf); if(res == 0) { UpdateFile(arcname); osDelete(pktname); if(!doAddFlow(arcname,basename,cnode->EchomailPri,FLOW_DELETE)) MakeOrphan(arcname,&n4d,cnode->EchomailPri,FLOW_DELETE); } else { osRename(pktname,file); LogWrite(1,SYSTEMERR,"Packer failed: %u",res); UnlockBasename(basename); return(FALSE); } } else { /* Send unpacked echomail */ MakeFullPath(config.cfg_PacketDir,GetFilePart(file),pktname,200); GetFilePart(pktname)[8]=0; strcat(pktname,".pkt"); LogWrite(4,TOSSINGINFO,"Sending %s unpacked to %d:%d/%d.%d", GetFilePart(pktname), cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); if(!movefile(file,pktname)) { LogWrite(1,SYSTEMERR,"Failed to move file \"%s\" to \"%s\"",file,pktname); UnlockBasename(basename); return(FALSE); } else { if(!doAddFlow(pktname,basename,cnode->EchomailPri,FLOW_DELETE)) MakeOrphan(pktname,&n4d,cnode->EchomailPri,FLOW_DELETE); } } } else { /* Netmail */ switch(type) { case PKTS_NORMAL: case PKTS_ECHOMAIL: letter='o'; break; case PKTS_HOLD: letter='h'; break; case PKTS_DIRECT: letter='d'; break; case PKTS_CRASH: letter='c'; break; default: letter='f'; } sprintf(buf2,".%cut",letter); strcpy(buf,basename); strcat(buf,buf2); LogWrite(4,TOSSINGINFO,"Sending unpacked netmail to %d:%d/%d.%d (%s)", n4d.Zone, n4d.Net, n4d.Node, n4d.Point, prinames[(int)type]); if(!(copybuf=(char *)osAlloc(COPYBUFSIZE))) { nomem=TRUE; UnlockBasename(basename); return(FALSE); } if(!(ifh=osOpen(file,MODE_OLDFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open \"%s\"",file); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); osFree(copybuf); UnlockBasename(basename); return(FALSE); } if(osExists(buf)) { if(!(ofh=osOpen(buf,MODE_READWRITE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open \"%s\"",file); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); osClose(ifh); osFree(copybuf); UnlockBasename(basename); return(FALSE); } osSeek(ifh,SIZE_PKTHEADER,OFFSET_BEGINNING); osSeek(ofh,-2,OFFSET_END); } else { if(!(ofh=osOpen(buf,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to open \"%s\"",file); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); osClose(ifh); osFree(copybuf); UnlockBasename(basename); return(FALSE); } } while((readlen=osRead(ifh,copybuf,COPYBUFSIZE))) { if(!osWrite(ofh,copybuf,readlen)) { ioerror=TRUE; ioerrornum=osError(); } } osClose(ifh); osClose(ofh); osFree(copybuf); osDelete(file); } UnlockBasename(basename); if(ioerror) return(FALSE); return(TRUE); } bool ArchiveOutbound(void) { struct jbList PktList; struct osFileEntry *fe; char buf[200]; /* Orphan files */ LogWrite(3,ACTIONINFO,"Scanning for orphan files"); if(!(osReadDir(config.cfg_PacketDir,&ArcList,IsOrphan))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",config.cfg_PacketDir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } SortFEList(&ArcList); for(fe=(struct osFileEntry *)ArcList.First;fe && !ctrlc;fe=fe->Next) { LogWrite(1,SYSTEMINFO,"Found orphan file \"%s\", retrying...",fe->Name); MakeFullPath(config.cfg_PacketDir,fe->Name,buf,200); HandleOrphan(buf); } jbFreeList(&ArcList); /* Read ArcList */ if(!(osReadDir(config.cfg_PacketDir,&ArcList,IsArc))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",config.cfg_PacketDir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } /* Delete old zero-length files */ DeleteZero(config.cfg_PacketDir,&ArcList); /* Read index */ ReadIndex(); /* Old packets */ LogWrite(3,ACTIONINFO,"Scanning for old packets"); if(!(osReadDir(config.cfg_PacketDir,&PktList,IsPktTmp))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",config.cfg_PacketDir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); jbFreeList(&ArcList); return(FALSE); } SortFEList(&PktList); for(fe=(struct osFileEntry *)PktList.First;fe;fe=fe->Next) { LogWrite(1,SYSTEMINFO,"Found old packet file \"%s\", retrying...",fe->Name); MakeFullPath(config.cfg_PacketDir,fe->Name,buf,200); PackFile(buf); } jbFreeList(&PktList); /* New packets */ LogWrite(3,ACTIONINFO,"Scanning for new files to pack"); if(!(osReadDir(config.cfg_PacketCreate,&PktList,IsNewPkt))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",config.cfg_PacketCreate); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); jbFreeList(&ArcList); return(FALSE); } SortFEList(&PktList); for(fe=(struct osFileEntry *)PktList.First;fe;fe=fe->Next) { MakeFullPath(config.cfg_PacketCreate,fe->Name,buf,200); if(!PackFile(buf)) if(!MakePktTmp(buf)) { jbFreeList(&PktList); jbFreeList(&ArcList); return(FALSE); } } jbFreeList(&PktList); jbFreeList(&ArcList); return(TRUE); } crashmail-1.5/src/crashmail/outbound.h000066400000000000000000000003271230617144500200710ustar00rootroot00000000000000#define FLOW_NONE 0 #define FLOW_TRUNC 1 #define FLOW_DELETE 2 bool ArchiveOutbound(void); void MakeBaseName(struct Node4D *n4d,char *basename); bool AddFlow(char *filename,struct Node4D *n4d,char type,long mode); crashmail-1.5/src/crashmail/pkt.c000066400000000000000000000575401230617144500170340ustar00rootroot00000000000000#include "crashmail.h" /*************************************************************************/ /* */ /* Read Pkt */ /* */ /*************************************************************************/ #define PKT_MINREADLEN 200 bool messageend; bool shortread,longread; uint16_t getuword(uint8_t *buf,uint32_t offset) { return (uint16_t)(buf[offset]+256*buf[offset+1]); } void putuword(uint8_t *buf,uint32_t offset,uint16_t num) { buf[offset]=num%256; buf[offset+1]=num/256; } uint32_t ReadNull(char *buf, uint32_t maxlen, osFile fh) { /* Reads from fh until buffer full or NULL */ short ch,c=0; if(shortread) return(0); ch=osGetChar(fh); while(ch!=-1 && ch!=0 && c!=maxlen-1) { buf[c++]=ch; ch=osGetChar(fh); } buf[c]=0; if(ch==-1) shortread=TRUE; if(ch!=0 && c==maxlen-1) longread=TRUE; return(c); } uint32_t ReadCR(char *buf, uint32_t maxlen, osFile fh) { /* Reads from fh until buffer full or CR */ short ch,c=0; ch=osGetChar(fh); while(ch!=-1 && ch!=0 && ch!=10 && ch !=13 && c!=maxlen-2) { buf[c++]=ch; if(c!=maxlen-2) ch=osGetChar(fh); } if(ch==13 || ch==10) buf[c++]=ch; buf[c]=0; if(ch==0) messageend=TRUE; if(ch==-1) shortread=TRUE; return(c); } bool ReadPkt(char *pkt,struct osFileEntry *fe,bool bundled,bool (*handlefunc)(struct MemMessage *mm)) { osFile fh; struct Aka *tmpaka; struct ConfigNode *tmpcnode; uint32_t msgnum,msgoffset; char buf[200]; uint8_t PktHeader[SIZE_PKTHEADER]; uint8_t PktMsgHeader[SIZE_PKTMSGHEADER]; struct Node4D PktOrig,PktDest; char *monthnames[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; struct MemMessage *mm; struct TextChunk *chunk; bool pkt_pw,pkt_4d,pkt_5d; int res; if(config.cfg_BeforeToss[0]) { ExpandPacker(config.cfg_BeforeToss,buf,200,"",pkt); res=osExecute(buf); if(res != 0) { LogWrite(1,SYSTEMERR,"BEFORETOSS command failed for %s: %u",pkt,res); return(FALSE); } } shortread=FALSE; longread=FALSE; pkt_pw=FALSE; pkt_4d=FALSE; pkt_5d=FALSE; PktOrig.Zone=0; PktOrig.Net=0; PktOrig.Node=0; PktOrig.Point=0; PktDest.Zone=0; PktDest.Net=0; PktDest.Node=0; PktDest.Point=0; if(!(mm=mmAlloc())) return(FALSE); if(!(fh=osOpen(pkt,MODE_OLDFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Unable to open %s",pkt); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); mmFree(mm); return(TRUE); } if(osRead(fh,PktHeader,SIZE_PKTHEADER)!=SIZE_PKTHEADER) { LogWrite(1,TOSSINGERR,"Packet header in %s is too short",pkt); osClose(fh); mmFree(mm); BadFile(pkt,"Packet header is too short"); return(TRUE); } if(getuword(PktHeader,PKTHEADER_PKTTYPE)!=0x0002) { LogWrite(1,TOSSINGERR,"%s is not a Type-2 packet",pkt); osClose(fh); mmFree(mm); BadFile(pkt,"Not a Type-2 packet"); return(TRUE); } if(getuword(PktHeader,PKTHEADER_BAUD) == 2) { /* PktOrig och PktDest */ PktOrig.Zone = getuword(PktHeader,PKTHEADER_ORIGZONE); PktOrig.Net = getuword(PktHeader,PKTHEADER_ORIGNET); PktOrig.Node = getuword(PktHeader,PKTHEADER_ORIGNODE); PktOrig.Point = getuword(PktHeader,PKTHEADER_ORIGPOINT); PktDest.Zone = getuword(PktHeader,PKTHEADER_DESTZONE); PktDest.Net = getuword(PktHeader,PKTHEADER_DESTNET); PktDest.Node = getuword(PktHeader,PKTHEADER_DESTNODE); PktDest.Point = getuword(PktHeader,PKTHEADER_DESTPOINT); pkt_5d=TRUE; } else { /* PktOrig och PktDest */ if(getuword(PktHeader,PKTHEADER_ORIGZONE)) { PktOrig.Zone = getuword(PktHeader,PKTHEADER_ORIGZONE); PktDest.Zone = getuword(PktHeader,PKTHEADER_DESTZONE); } else if(getuword(PktHeader,PKTHEADER_QORIGZONE)) { PktOrig.Zone= getuword(PktHeader,PKTHEADER_QORIGZONE); PktDest.Zone= getuword(PktHeader,PKTHEADER_QDESTZONE); } else { PktOrig.Zone=0; PktDest.Zone=0; } PktOrig.Net = getuword(PktHeader,PKTHEADER_ORIGNET); PktOrig.Node = getuword(PktHeader,PKTHEADER_ORIGNODE); PktDest.Net = getuword(PktHeader,PKTHEADER_DESTNET); PktDest.Node = getuword(PktHeader,PKTHEADER_DESTNODE); if(PktHeader[PKTHEADER_CWVALIDCOPY] == PktHeader[PKTHEADER_CAPABILWORD+1] && PktHeader[PKTHEADER_CWVALIDCOPY+1] == PktHeader[PKTHEADER_CAPABILWORD]) { pkt_4d=TRUE; if(getuword(PktHeader,PKTHEADER_ORIGPOINT)!=0 && getuword(PktHeader,PKTHEADER_ORIGNET)==0xffff) PktOrig.Net = getuword(PktHeader,PKTHEADER_AUXNET); PktOrig.Point = getuword(PktHeader,PKTHEADER_ORIGPOINT); PktDest.Point = getuword(PktHeader,PKTHEADER_DESTPOINT); } } /* Check packet destination */ if((config.cfg_Flags & CFG_CHECKPKTDEST) && !no_security) { for(tmpaka=(struct Aka *)config.AkaList.First;tmpaka;tmpaka=tmpaka->Next) if(Compare4D(&tmpaka->Node,&PktDest) == 0) break; if(!tmpaka) { LogWrite(1,TOSSINGERR,"Addressed to %u:%u/%u.%u, not to me",PktDest.Zone,PktDest.Net,PktDest.Node,PktDest.Point); osClose(fh); mmFree(mm); sprintf(buf,"Addressed to %u:%u/%u.%u, not to me",PktDest.Zone,PktDest.Net,PktDest.Node,PktDest.Point); BadFile(pkt,buf); return(TRUE); } } /* Fixa zone */ if(PktOrig.Zone == 0) for(tmpcnode=(struct ConfigNode *)config.CNodeList.First;tmpcnode;tmpcnode=tmpcnode->Next) { if(Compare4D(&PktOrig,&tmpcnode->Node)==0) { PktOrig.Zone=tmpcnode->Node.Zone; break; } } if(PktDest.Zone == 0) for(tmpaka=(struct Aka *)config.AkaList.First;tmpaka;tmpaka=tmpaka->Next) { if(Compare4D(&PktDest,&tmpaka->Node)==0) { PktDest.Zone=tmpaka->Node.Zone; break; } } if(PktOrig.Zone == 0) PktOrig.Zone=config.cfg_DefaultZone; if(PktDest.Zone == 0) PktDest.Zone=config.cfg_DefaultZone; for(tmpcnode=(struct ConfigNode *)config.CNodeList.First;tmpcnode;tmpcnode=tmpcnode->Next) if(Compare4D(&PktOrig,&tmpcnode->Node)==0) break; if(tmpcnode) { if(tmpcnode->PacketPW[0]!=0 && PktHeader[PKTHEADER_PASSWORD]!=0) pkt_pw=TRUE; } buf[0]=0; if(pkt_pw) strcat(buf,", pw"); if(pkt_4d) strcat(buf,", 4d"); if(pkt_5d) strcat(buf,", 5d"); if(buf[0] != 0) buf[0]='/'; if(pkt_5d) { char domain[10]; mystrncpy(domain,(char *)&PktHeader[PKTHEADER45_ORIGDOMAIN],9); LogWrite(1,ACTIONINFO,"Tossing %s (%uK) from %d:%d/%d.%d@%s %s", fe->Name, (fe->Size+512)/1024, PktOrig.Zone, PktOrig.Net, PktOrig.Node, PktOrig.Point, domain, buf); } else { int month; month=getuword(PktHeader,PKTHEADER_MONTH); if(month > 11) month=12; LogWrite(1,ACTIONINFO,"Tossing %s (%uK) from %d:%d/%d.%d (%02d-%s-%02d %02d:%02d:%02d) %s", fe->Name, (fe->Size+512)/1024, PktOrig.Zone, PktOrig.Net, PktOrig.Node, PktOrig.Point, getuword(PktHeader,PKTHEADER_DAY), monthnames[month], getuword(PktHeader,PKTHEADER_YEAR) % 100, getuword(PktHeader,PKTHEADER_HOUR), getuword(PktHeader,PKTHEADER_MINUTE), getuword(PktHeader,PKTHEADER_SECOND), buf); } if(tmpcnode) { strncpy(buf,(char *)&PktHeader[PKTHEADER_PASSWORD],8); buf[8]=0; if(tmpcnode->PacketPW[0]!=0 && stricmp(buf,tmpcnode->PacketPW)!=0 && !no_security) { LogWrite(1,TOSSINGERR,"Wrong password"); osClose(fh); mmFree(mm); BadFile(pkt,"Wrong password"); return(TRUE); } } msgoffset=osFTell(fh); if(osRead(fh,PktMsgHeader,SIZE_PKTMSGHEADER) < 2) { LogWrite(1,TOSSINGERR,"Message header for msg #1 (offset %d) is too short",msgoffset); osClose(fh); mmFree(mm); sprintf(buf,"Message header for msg #1 (offset %d) is too short",msgoffset); BadFile(pkt,buf); return(TRUE); } msgnum=0; while(getuword(PktMsgHeader,PKTMSGHEADER_PKTTYPE) == 2 && !ctrlc) { msgnum++; printf("Message %u \x0d",msgnum); fflush(stdout); /* Init variables */ mmClear(mm); mm->OrigNode.Net = getuword(PktMsgHeader,PKTMSGHEADER_ORIGNET); mm->OrigNode.Node = getuword(PktMsgHeader,PKTMSGHEADER_ORIGNODE); mm->DestNode.Net = getuword(PktMsgHeader,PKTMSGHEADER_DESTNET); mm->DestNode.Node = getuword(PktMsgHeader,PKTMSGHEADER_DESTNODE); mm->DestNode.Zone=PktDest.Zone; mm->DestNode.Zone=PktOrig.Zone; mm->Attr=getuword(PktMsgHeader,PKTMSGHEADER_ATTR); mm->Cost=getuword(PktMsgHeader,PKTMSGHEADER_COST); Copy4D(&mm->PktOrig,&PktOrig); Copy4D(&mm->PktDest,&PktDest); if(no_security) mm->Flags |= MMFLAG_NOSECURITY; /* Get header strings */ ReadNull(mm->DateTime,20,fh); ReadNull(mm->To,36,fh); ReadNull(mm->From,36,fh); ReadNull(mm->Subject,72,fh); /* Corrupt packet? */ if(shortread) { LogWrite(1,TOSSINGERR,"Message header for msg #%u (offset %d) is short",msgnum,msgoffset); sprintf(buf,"Message header for msg #%u (offset %d) is short",msgnum,msgoffset); osClose(fh); mmFree(mm); BadFile(pkt,buf); return(TRUE); } if(longread) { LogWrite(1,TOSSINGERR,"Header strings too long in msg #%u (offset %d)",msgnum,msgoffset); sprintf(buf,"Header strings too long in msg #%u (offset %d)",msgnum,msgoffset); osClose(fh); mmFree(mm); BadFile(pkt,buf); return(TRUE); } /* Start reading message text */ messageend=FALSE; ReadCR(buf,200,fh); /* Echomail or netmail? */ if(strncmp(buf,"AREA:",5)==0) { mystrncpy(mm->Area,&buf[5],80); stripleadtrail(mm->Area); /* Strip spaces from area name */ } else { if(!mmAddLine(mm,buf)) { osClose(fh); mmFree(mm); return(FALSE); } } /* Get rest of text */ while(!messageend) { ReadCR(buf,200,fh); if(shortread) { osClose(fh); mmFree(mm); LogWrite(1,TOSSINGERR,"Got unexpected EOF when reading msg #%u (offset %d)",msgnum,msgoffset); sprintf(buf,"Got unexpected EOF when reading msg #%u (offset %d)",msgnum,msgoffset); BadFile(pkt,buf); return(TRUE); } if(buf[0]) { if(!mmAddLine(mm,buf)) { osClose(fh); mmFree(mm); return(FALSE); } } } /* Stats */ for(tmpcnode=(struct ConfigNode *)config.CNodeList.First;tmpcnode;tmpcnode=tmpcnode->Next) if(Compare4D(&tmpcnode->Node,&mm->PktOrig)==0) break; if(tmpcnode) { uint32_t size; size=0; for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) size+=chunk->Length; if(mm->Area[0]) { tmpcnode->GotEchomails++; tmpcnode->GotEchomailBytes+=size; } else { tmpcnode->GotNetmails++; tmpcnode->GotNetmailBytes+=size; } } toss_read++; mm->Flags |= MMFLAG_TOSSED; if(!(*handlefunc)(mm)) { osClose(fh); mmFree(mm); return(FALSE); } msgoffset=osFTell(fh); if(osRead(fh,PktMsgHeader,SIZE_PKTMSGHEADER) < 2) { LogWrite(1,TOSSINGERR,"Packet header too short for msg #%u (offset %d)",msgnum+1,msgoffset); osClose(fh); mmFree(mm); sprintf(buf,"Packet header too short for msg #%u (offset %d)",msgnum+1,msgoffset); BadFile(pkt,buf); return(TRUE); } } if(getuword(PktMsgHeader,PKTMSGHEADER_PKTTYPE)!=0) { osClose(fh); mmFree(mm); LogWrite(1,TOSSINGERR,"Unknown message type %u for message #%u (offset %d)",getuword(PktMsgHeader,PKTMSGHEADER_PKTTYPE),msgnum+1,msgoffset); sprintf(buf,"Unknown message type %u for message #%u (offset %d)",getuword(PktMsgHeader,PKTMSGHEADER_PKTTYPE),msgnum+1,msgoffset); BadFile(pkt,buf); return(TRUE); } printf(" \x0d"); fflush(stdout); osClose(fh); return(TRUE); } /*************************************************************************/ /* */ /* Write Pkt */ /* */ /*************************************************************************/ struct Pkt { struct Pkt *Next; osFile fh; uint32_t hexnum; uint32_t Len; uint16_t Type; struct Node4D Dest; struct Node4D Orig; }; void pktWrite(struct Pkt *pkt,uint8_t *buf,uint32_t len) { if(!osWrite(pkt->fh,buf,len)) { ioerror=TRUE; ioerrornum=osError(); } pkt->Len+=len; } void WriteNull(struct Pkt *pkt,char *str) { pktWrite(pkt,(uint8_t *)str,(uint32_t)(strlen(str)+1)); } struct Pkt *FindPkt(struct Node4D *node,struct Node4D *mynode,uint16_t type) { struct Pkt *pkt; for(pkt=(struct Pkt *)PktList.First;pkt;pkt=pkt->Next) if(Compare4D(node,&pkt->Dest)==0 && Compare4D(mynode,&pkt->Orig)==0 && type==pkt->Type) return(pkt); return(NULL); } time_t lastt; uint32_t serial; struct Pkt *CreatePkt(struct Node4D *dest,struct ConfigNode *node,struct Node4D *orig,uint16_t type) { char buf[100],buf2[100]; struct Pkt *pkt; uint32_t num,c; time_t t; struct tm *tp; uint8_t PktHeader[SIZE_PKTHEADER]; do { t=time(NULL); if(t == lastt) serial++; else serial=0; if(serial == 256) serial=0; lastt=t; num = (t<<8) + serial; sprintf(buf2,"%08x.newpkt",num); MakeFullPath(config.cfg_PacketCreate,buf2,buf,100); } while(osExists(buf)); if(!(pkt=(struct Pkt *)osAllocCleared(sizeof(struct Pkt)))) { nomem=TRUE; return(NULL); } if(!(pkt->fh=osOpen(buf,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Unable to create packet %s",buf); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); osFree(pkt); return(NULL); } pkt->hexnum=num; pkt->Type=type; Copy4D(&pkt->Dest,dest); Copy4D(&pkt->Orig,orig); putuword(PktHeader,PKTHEADER_ORIGNODE,orig->Node); putuword(PktHeader,PKTHEADER_DESTNODE,dest->Node); time(&t); tp=localtime(&t); putuword(PktHeader,PKTHEADER_DAY,tp->tm_mday); putuword(PktHeader,PKTHEADER_MONTH,tp->tm_mon); putuword(PktHeader,PKTHEADER_YEAR,tp->tm_year+1900); putuword(PktHeader,PKTHEADER_HOUR,tp->tm_hour); putuword(PktHeader,PKTHEADER_MINUTE,tp->tm_min); putuword(PktHeader,PKTHEADER_SECOND,tp->tm_sec); putuword(PktHeader,PKTHEADER_BAUD,0); putuword(PktHeader,PKTHEADER_PKTTYPE,2); putuword(PktHeader,PKTHEADER_ORIGNET,orig->Net); putuword(PktHeader,PKTHEADER_DESTNET,dest->Net); PktHeader[PKTHEADER_PRODCODELOW]=0xfe; PktHeader[PKTHEADER_REVMAJOR]=VERSION_MAJOR; putuword(PktHeader,PKTHEADER_QORIGZONE,orig->Zone); putuword(PktHeader,PKTHEADER_QDESTZONE,dest->Zone); putuword(PktHeader,PKTHEADER_AUXNET,0); putuword(PktHeader,PKTHEADER_CWVALIDCOPY,0x0100); PktHeader[PKTHEADER_PRODCODEHIGH]=0; PktHeader[PKTHEADER_REVMINOR]=VERSION_MINOR; putuword(PktHeader,PKTHEADER_CAPABILWORD,0x0001); putuword(PktHeader,PKTHEADER_ORIGZONE,orig->Zone); putuword(PktHeader,PKTHEADER_DESTZONE,dest->Zone); putuword(PktHeader,PKTHEADER_ORIGPOINT,orig->Point); putuword(PktHeader,PKTHEADER_DESTPOINT,dest->Point); PktHeader[PKTHEADER_PRODDATA]=0; PktHeader[PKTHEADER_PRODDATA+1]=0; PktHeader[PKTHEADER_PRODDATA+2]=0; PktHeader[PKTHEADER_PRODDATA+3]=0; for(c=0;c<8;c++) PktHeader[PKTHEADER_PASSWORD+c]=0; if(node) strncpy((char *)&PktHeader[PKTHEADER_PASSWORD],node->PacketPW,8); pktWrite(pkt,PktHeader,SIZE_PKTHEADER); if(ioerror) { osClose(pkt->fh); osFree(pkt); return(NULL); } return(pkt); } void FinishPacket(struct Pkt *pkt) { pktWrite(pkt,(uint8_t *)"",1); pktWrite(pkt,(uint8_t *)"",1); osClose(pkt->fh); if(pkt->hexnum) { char oldname[200],newname[200],buf1[100],buf2[100],*typestr; /* Create packet name */ switch(pkt->Type) { case PKTS_HOLD: typestr="Hold"; break; case PKTS_NORMAL: typestr="Normal"; break; case PKTS_DIRECT: typestr="Direct"; break; case PKTS_CRASH: typestr="Crash"; break; case PKTS_ECHOMAIL: typestr="Echomail"; break; } sprintf(buf1,"%08x.newpkt",pkt->hexnum); sprintf(buf2,"%08x_%s_%d_%d_%d_%d.newpkt", pkt->hexnum, typestr, pkt->Dest.Zone, pkt->Dest.Net, pkt->Dest.Node, pkt->Dest.Point); MakeFullPath(config.cfg_PacketCreate,buf1,oldname,200); MakeFullPath(config.cfg_PacketCreate,buf2,newname,200); if(!osRename(oldname,newname)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to rename %s to %s",oldname,newname); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); } } jbFreeNode(&PktList,(struct jbNode *)pkt); } void ClosePackets(void) { struct Pkt *pkt,*pkt2; pkt=(struct Pkt *)PktList.First; while(pkt) { pkt2=pkt->Next; FinishPacket(pkt); pkt=pkt2; } } bool WriteMsgHeader(struct Pkt *pkt,struct MemMessage *mm) { uint8_t PktMsgHeader[SIZE_PKTMSGHEADER]; putuword(PktMsgHeader,PKTMSGHEADER_PKTTYPE,0x0002); putuword(PktMsgHeader,PKTMSGHEADER_ORIGNODE,mm->OrigNode.Node); putuword(PktMsgHeader,PKTMSGHEADER_DESTNODE,mm->DestNode.Node); putuword(PktMsgHeader,PKTMSGHEADER_ORIGNET,mm->OrigNode.Net); putuword(PktMsgHeader,PKTMSGHEADER_DESTNET,mm->DestNode.Net); putuword(PktMsgHeader,PKTMSGHEADER_ATTR,mm->Attr); putuword(PktMsgHeader,PKTMSGHEADER_COST,mm->Cost); pktWrite(pkt,PktMsgHeader,SIZE_PKTMSGHEADER); WriteNull(pkt,mm->DateTime); WriteNull(pkt,mm->To); WriteNull(pkt,mm->From); WriteNull(pkt,mm->Subject); if(ioerror) return(FALSE); return(TRUE); } bool WritePath(struct Pkt *pkt,struct jbList *list) { uint16_t c; struct Path *path; for(path=(struct Path *)list->First;path;path=path->Next) for(c=0;cPaths;c++) if(path->Path[c][0]!=0) { pktWrite(pkt,(uint8_t *)"\x01PATH: ",7); pktWrite(pkt,(uint8_t *)path->Path[c],(uint32_t)strlen(path->Path[c])); pktWrite(pkt,(uint8_t *)"\x0d",1); } if(ioerror) return(FALSE); return(TRUE); } bool WriteSeenBy(struct Pkt *pkt,struct jbList *list) { char *buf; if(!(buf=mmMakeSeenByBuf(list))) return(FALSE); pktWrite(pkt,(uint8_t *)buf,(uint32_t)strlen(buf)); osFree(buf); return(TRUE); } bool WriteEchoMail(struct MemMessage *mm,struct ConfigNode *node, struct Aka *aka) { char buf[100]; struct Node4D *From4D; struct Pkt *pkt; struct TextChunk *chunk; uint32_t size; From4D=&aka->Node; if(node->Flags & NODE_PKTFROM) From4D=&node->PktFrom; toss_written++; mm->Type=PKTS_ECHOMAIL; size=0; for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) size+=chunk->Length; node->SentEchomails++; node->SentEchomailBytes+=size; pkt=FindPkt(&node->Node,From4D,mm->Type); if(!pkt || (pkt && config.cfg_MaxPktSize!=0 && pkt->Len > config.cfg_MaxPktSize)) { if(pkt) FinishPacket(pkt); if(!(pkt=CreatePkt(&node->Node,node,From4D,mm->Type))) { return(FALSE); } jbAddNode(&PktList,(struct jbNode *)pkt); } Copy4D(&mm->DestNode,&node->Node); Copy4D(&mm->OrigNode,From4D); if(!WriteMsgHeader(pkt,mm)) return(FALSE); sprintf(buf,"AREA:%s\x0d",mm->Area); pktWrite(pkt,(uint8_t *)buf,(uint32_t)strlen(buf)); if(ioerror) return(FALSE); for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) { pktWrite(pkt,(uint8_t *)chunk->Data,chunk->Length); if(ioerror) return(FALSE); } if(node->Node.Zone != aka->Node.Zone || (node->Flags & NODE_TINYSEENBY)) { struct jbList seenbylist; struct Nodes2D *seenby; if(!(seenby=(struct Nodes2D *)osAlloc(sizeof(struct Nodes2D)))) { nomem=TRUE; return(FALSE); } seenby->Nodes=0; seenby->Next=NULL; jbNewList(&seenbylist); jbAddNode(&seenbylist,(struct jbNode *)seenby); if(node->Node.Point == 0) if(!mmAddNodes2DList(&seenbylist,node->Node.Net,node->Node.Node)) { jbFreeList(&seenbylist); return(FALSE); } if(aka->Node.Point == 0) if(!mmAddNodes2DList(&seenbylist,aka->Node.Net,aka->Node.Node)) { jbFreeList(&seenbylist); return(FALSE); } if(!mmSortNodes2D(&seenbylist)) { jbFreeList(&seenbylist); return(FALSE); } if(!WriteSeenBy(pkt,&seenbylist)) { jbFreeList(&seenbylist); return(FALSE); } jbFreeList(&seenbylist); } else if(!(node->Flags & NODE_NOSEENBY)) { if(!mmSortNodes2D(&mm->SeenBy)) return(FALSE); if(!WriteSeenBy(pkt,&mm->SeenBy)) return(FALSE); } if(!WritePath(pkt,&mm->Path)) return(FALSE); pktWrite(pkt,(uint8_t *)"",1); return(TRUE); } bool WriteNetMail(struct MemMessage *mm,struct Node4D *dest,struct Aka *aka) { struct Pkt *pkt; struct ConfigNode *cnode; struct TextChunk *chunk; struct Node4D *From4D; uint32_t size; toss_written++; From4D=&aka->Node; for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) if(Compare4D(&cnode->Node,dest)==0) break; if(cnode) { /* Calculate size */ size=0; for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) size+=chunk->Length; cnode->SentNetmails++; cnode->SentNetmailBytes+=size; if(cnode->Flags & NODE_PACKNETMAIL) if(mm->Type == PKTS_NORMAL) mm->Type=PKTS_ECHOMAIL; if(cnode->Flags & NODE_PKTFROM) From4D=&cnode->PktFrom; } pkt=FindPkt(dest,From4D,mm->Type); if(!pkt || (pkt && config.cfg_MaxPktSize!=0 && pkt->Len > config.cfg_MaxPktSize)) { if(pkt) FinishPacket(pkt); if(!(pkt=CreatePkt(dest,cnode,From4D,mm->Type))) { return(FALSE); } jbAddNode(&PktList,(struct jbNode *)pkt); } if(!WriteMsgHeader(pkt,mm)) return(FALSE); for(chunk=(struct TextChunk *)mm->TextChunks.First;chunk;chunk=chunk->Next) { pktWrite(pkt,(uint8_t *)chunk->Data,chunk->Length); if(ioerror) return(FALSE); } pktWrite(pkt,(uint8_t *)"",1); return(TRUE); } crashmail-1.5/src/crashmail/pkt.h000066400000000000000000000004451230617144500170310ustar00rootroot00000000000000bool ReadPkt(char *pkt,struct osFileEntry *fe,bool bundled,bool (*handlefunc)(struct MemMessage *mm)); bool WriteEchoMail(struct MemMessage *mm,struct ConfigNode *cnode, struct Aka *aka); bool WriteNetMail(struct MemMessage *mm,struct Node4D *dest,struct Aka *aka); void ClosePackets(void); crashmail-1.5/src/crashmail/safedel.c000066400000000000000000000006701230617144500176310ustar00rootroot00000000000000#include "crashmail.h" bool SafeDelete(char *file) { struct osFileEntry *fe; if(!(fe=osAllocCleared(sizeof(struct osFileEntry)))) return(FALSE); mystrncpy(fe->Name,file,100); jbAddNode(&DeleteList,(struct jbNode *)fe); return(TRUE); } void ProcessSafeDelete(void) { struct osFileEntry *fe; for(fe=(struct osFileEntry *)DeleteList.First;fe;fe=fe->Next) osDelete(fe->Name); jbFreeList(&DeleteList); } crashmail-1.5/src/crashmail/safedel.h000066400000000000000000000000731230617144500176330ustar00rootroot00000000000000bool SafeDelete(char *file); void ProcessSafeDelete(void); crashmail-1.5/src/crashmail/scan.c000066400000000000000000000160141230617144500171510ustar00rootroot00000000000000#include "crashmail.h" void LogScanResults(void) { printf("\n"); if(scan_total==0) LogWrite(2,TOSSINGINFO,"No messages exported"); else if(scan_total==1) LogWrite(2,TOSSINGINFO,"1 message exported"); else { LogWrite(2,TOSSINGINFO,"%u messages exported",scan_total); } } bool ScanHandle(struct MemMessage *mm) { char buf[50]; if(mm->Area[0]==0) { Print4D(&mm->DestNode,buf); LogWrite(4,TOSSINGINFO,"Exporting message #%u from \"%s\" to \"%s\" at %s", mm->msgnum, mm->From, mm->To, buf); } else { LogWrite(4,TOSSINGINFO,"Exporting message #%u from \"%s\" to \"%s\" in %s", mm->msgnum, mm->From, mm->To, mm->Area); } return HandleMessage(mm); } bool Scan(void) { struct Area *area; LogWrite(2,ACTIONINFO,"Scanning all areas for messages to export"); if(!BeforeScanToss()) return(FALSE); for(area=(struct Area *)config.AreaList.First;area && !ctrlc;area=area->Next) if(area->Messagebase && (area->AreaType == AREATYPE_ECHOMAIL || area->AreaType == AREATYPE_NETMAIL)) { if(area->Messagebase->exportfunc) { if(area->AreaType == AREATYPE_NETMAIL && (config.cfg_Flags & CFG_NOEXPORTNETMAIL)) { printf("Skipping area %s (NOEXPORTNETMAIL is set)\n", area->Tagname); } else { printf("Scanning area %s\n",area->Tagname); if(!(*area->Messagebase->exportfunc)(area,ScanHandle)) { AfterScanToss(FALSE); return(FALSE); } } } } if(ctrlc) { AfterScanToss(FALSE); return(FALSE); } LogScanResults(); AfterScanToss(TRUE); return(TRUE); } bool ScanList(char *file) { osFile fh; char buf[100]; struct Area *area; int res; LogWrite(2,ACTIONINFO,"Scanning areas in %s for messages to export",file); if(!(fh=osOpen(file,MODE_OLDFILE))) { LogWrite(1,USERERR,"Unable to open %s",file); return(FALSE); } if(!BeforeScanToss()) return(FALSE); while(osFGets(fh,buf,100) && !ctrlc) { striptrail(buf); if(buf[0] != 0) { striptrail(buf); for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(stricmp(area->Tagname,buf)==0) break; if(!area) { LogWrite(1,USERERR,"Skipping area %s, area is unknown",buf); } else if(!area->scanned) { area->scanned=TRUE; if(area->AreaType != AREATYPE_ECHOMAIL && area->AreaType != AREATYPE_NETMAIL) { LogWrite(1,USERERR,"Skipping area %s, not an echomail or netmail area",area->Tagname); } else if(area->AreaType == AREATYPE_NETMAIL && (config.cfg_Flags & CFG_NOEXPORTNETMAIL)) { LogWrite(1,USERERR,"Skipping area %s (NOEXPORTNETMAIL is set)", area->Tagname); } else if(!area->Messagebase) { LogWrite(1,USERERR,"Skipping area %s, area is pass-through",area->Tagname); } else if(!area->Messagebase->exportfunc) { LogWrite(1,USERERR,"Skipping area %s, scanning is not supported for this type of messagebase",area->Tagname); } else { printf("Scanning area %s\n",area->Tagname); res=(*area->Messagebase->exportfunc)(area,ScanHandle); if(!res) { AfterScanToss(FALSE); osClose(fh); return(FALSE); } } } } } osClose(fh); if(ctrlc) { AfterScanToss(FALSE); return(FALSE); } LogScanResults(); AfterScanToss(TRUE); return(TRUE); } bool ScanDotJam(char *file) { osFile fh; char buf[100]; struct Area *area; int res; LogWrite(2,ACTIONINFO,"Scanning areas in %s for messages to export",file); if(!(fh=osOpen(file,MODE_OLDFILE))) { LogWrite(1,USERERR,"Unable to open %s",file); return(FALSE); } if(!BeforeScanToss()) return(FALSE); while(osFGets(fh,buf,100) && !ctrlc) { striptrail(buf); if(buf[0] != 0) { striptrail(buf); if(strchr(buf,' ')) *strchr(buf,' ')=0; for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(stricmp(area->Path,buf)==0) break; if(!area) { LogWrite(1,USERERR,"No area with path %s",buf); } else if(!area->scanned) { area->scanned=TRUE; if(area->AreaType != AREATYPE_ECHOMAIL && area->AreaType != AREATYPE_NETMAIL) { LogWrite(1,USERERR,"Skipping area %s, not an echomail or netmail area",area->Tagname); } else if(area->AreaType == AREATYPE_NETMAIL && (config.cfg_Flags & CFG_NOEXPORTNETMAIL)) { LogWrite(1,USERERR,"Skipping area %s (NOEXPORTNETMAIL is set)", area->Tagname); } else if(!area->Messagebase) { LogWrite(1,USERERR,"Skipping area %s, area is pass-through",area->Tagname); } else if(!area->Messagebase->exportfunc) { LogWrite(1,USERERR,"Skipping area %s, scanning is not supported for this type of messagebase",area->Tagname); } else { printf("Scanning area %s\n",area->Tagname); res=(*area->Messagebase->exportfunc)(area,ScanHandle); if(!res) { AfterScanToss(FALSE); osClose(fh); return(FALSE); } } } } } osClose(fh); if(ctrlc) { AfterScanToss(FALSE); return(FALSE); } LogScanResults(); AfterScanToss(TRUE); return(TRUE); } bool ScanArea(char *tagname) { struct Area *area; int res; for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(stricmp(area->Tagname,tagname)==0) break; if(!area) { LogWrite(1,USERERR,"Unknown area %s",tagname); return(FALSE); } if(area->AreaType != AREATYPE_ECHOMAIL && area->AreaType != AREATYPE_NETMAIL) { LogWrite(1,USERERR,"You cannot scan area %s, not an echomail or netmail area",area->Tagname); return(FALSE); } else if(!area->Messagebase) { LogWrite(1,USERERR,"You cannot scan area %s, area is pass-through",area->Tagname); return(FALSE); } else if(!area->Messagebase->exportfunc) { LogWrite(1,USERERR,"You cannot scan area %s, scanning is not supported for this type of messagebase",area->Tagname); return(FALSE); } if(!BeforeScanToss()) return(FALSE); printf("Scanning area %s\n",area->Tagname); res=(*area->Messagebase->exportfunc)(area,ScanHandle); if(!res) { AfterScanToss(FALSE); return(FALSE); } LogScanResults(); AfterScanToss(TRUE); return(TRUE); } crashmail-1.5/src/crashmail/scan.h000066400000000000000000000001471230617144500171560ustar00rootroot00000000000000bool Scan(void); bool ScanList(char *file); bool ScanDotJam(char *file); bool ScanArea(char *tagname); crashmail-1.5/src/crashmail/stats.c000066400000000000000000000117111230617144500173620ustar00rootroot00000000000000#include "crashmail.h" #define STATS_IDENTIFIER "CST3" struct DiskAreaStats { char Tagname[80]; struct Node4D Aka; char Group; char fill_to_make_even; /* Just ignore this one */ uint32_t TotalTexts; uint16_t Last8Days[8]; uint32_t Dupes; time_t FirstTime; time_t LastTime; }; struct DiskNodeStats { struct Node4D Node; uint32_t GotNetmails; uint32_t GotNetmailBytes; uint32_t SentNetmails; uint32_t SentNetmailBytes; uint32_t GotEchomails; uint32_t GotEchomailBytes; uint32_t SentEchomails; uint32_t SentEchomailBytes; uint32_t Dupes; time_t FirstTime; }; bool WriteStats(char *file) { struct Area *area; struct ConfigNode *cnode; struct DiskAreaStats dastat; struct DiskNodeStats dnstat; osFile fh; uint32_t areas,nodes; if(!(fh=osOpen(file,MODE_NEWFILE))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Unable to open %s for writing",file); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } areas=0; nodes=0; for(area=(struct Area *)config.AreaList.First;area;area=area->Next) if(area->AreaType == AREATYPE_BAD || area->AreaType == AREATYPE_ECHOMAIL || area->AreaType == AREATYPE_NETMAIL) { if(!(area->Flags & AREA_UNCONFIRMED)) areas++; } for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) nodes++; if(DayStatsWritten == 0) DayStatsWritten = time(NULL) / (24*60*60); osWrite(fh,STATS_IDENTIFIER,4); osWrite(fh,&DayStatsWritten,sizeof(uint32_t)); osWrite(fh,&areas,sizeof(uint32_t)); for(area=(struct Area *)config.AreaList.First;area;area=area->Next) { if(area->AreaType == AREATYPE_BAD || area->AreaType == AREATYPE_ECHOMAIL || area->AreaType == AREATYPE_NETMAIL) { if(!(area->Flags & AREA_UNCONFIRMED)) { strcpy(dastat.Tagname,area->Tagname); dastat.TotalTexts=area->Texts; dastat.Dupes=area->Dupes; dastat.LastTime=area->LastTime; dastat.FirstTime=area->FirstTime; memcpy(&dastat.Last8Days[0],&area->Last8Days[0],sizeof(uint16_t)*8); Copy4D(&dastat.Aka,&area->Aka->Node); dastat.Group=area->Group; osWrite(fh,&dastat,sizeof(struct DiskAreaStats)); } } } osWrite(fh,&nodes,sizeof(uint32_t)); for(cnode=(struct ConfigNode *)config.CNodeList.First;cnode;cnode=cnode->Next) { Copy4D(&dnstat.Node,&cnode->Node); dnstat.GotEchomails=cnode->GotEchomails; dnstat.GotEchomailBytes=cnode->GotEchomailBytes; dnstat.SentEchomails=cnode->SentEchomails; dnstat.SentEchomailBytes=cnode->SentEchomailBytes; dnstat.GotNetmails=cnode->GotNetmails; dnstat.GotNetmailBytes=cnode->GotNetmailBytes; dnstat.SentNetmails=cnode->SentNetmails; dnstat.SentNetmailBytes=cnode->SentNetmailBytes; dnstat.Dupes=cnode->Dupes; dnstat.FirstTime=cnode->FirstTime; osWrite(fh,&dnstat,sizeof(struct DiskNodeStats)); } osClose(fh); return(TRUE); } bool ReadStats(char *file) { struct Area *area; struct ConfigNode *cnode; struct DiskAreaStats dastat; struct DiskNodeStats dnstat; uint32_t c,num; osFile fh; char buf[5]; if(!(fh=osOpen(file,MODE_OLDFILE))) return(TRUE); /* No reason for exiting */ osRead(fh,buf,4); buf[4]=0; if(strcmp(buf,STATS_IDENTIFIER)!=0) { LogWrite(1,SYSTEMERR,"Unknown format of stats file %s, exiting...",file); osClose(fh); return(FALSE); } osRead(fh,&DayStatsWritten,sizeof(uint32_t)); osRead(fh,&num,sizeof(uint32_t)); c=0; while(cNext) if(stricmp(area->Tagname,dastat.Tagname)==0) break; if(area) { area->Texts=dastat.TotalTexts; area->Dupes=dastat.Dupes; area->FirstTime=dastat.FirstTime; area->LastTime=dastat.LastTime; memcpy(&area->Last8Days[0],&dastat.Last8Days[0],sizeof(uint16_t)*8); } c++; } osRead(fh,&num,sizeof(uint32_t)); c=0; while(cNext) if(Compare4D(&dnstat.Node,&cnode->Node)==0) break; if(cnode) { cnode->GotEchomails=dnstat.GotEchomails; cnode->GotEchomailBytes=dnstat.GotEchomailBytes; cnode->SentEchomails=dnstat.SentEchomails; cnode->SentEchomailBytes=dnstat.SentEchomailBytes; cnode->GotNetmails=dnstat.GotNetmails; cnode->GotNetmailBytes=dnstat.GotNetmailBytes; cnode->SentNetmails=dnstat.SentNetmails; cnode->SentNetmailBytes=dnstat.SentNetmailBytes; cnode->Dupes=dnstat.Dupes; cnode->FirstTime=dnstat.FirstTime; } c++; } osClose(fh); return(TRUE); } crashmail-1.5/src/crashmail/stats.h000066400000000000000000000000711230617144500173640ustar00rootroot00000000000000bool ReadStats(char *file); bool WriteStats(char *file); crashmail-1.5/src/crashmail/toss.c000066400000000000000000000156731230617144500172270ustar00rootroot00000000000000#include "crashmail.h" bool Compare(char *str,char *recog) { uint32_t c,d; char comp; char buf[5]; d=0; for(c=0;dNext) if(Compare(buf,tmppacker->Recog)) return(tmppacker); return(NULL); } void LogTossResults(void) { struct Area *area; printf("\n"); for(area=(struct Area *)config.AreaList.First;area;area=area->Next) { if(ctrlc) return; if(area->NewDupes) LogWrite(3,TOSSINGINFO,"Area %s -- %u messages (%u dupes)",area->Tagname,area->NewTexts,area->NewDupes); else if(area->NewTexts) LogWrite(3,TOSSINGINFO,"Area %s -- %u messages",area->Tagname,area->NewTexts); } printf("\n"); LogWrite(1,TOSSINGINFO," Total -> Read messages: %6lu Written messages: %6lu",toss_read,toss_written); LogWrite(1,TOSSINGINFO,"Imported -> Imported messages: %6lu Routed netmails: %6lu",toss_import,toss_route); LogWrite(1,TOSSINGINFO," Bad -> Bad messages: %6lu Duplicate messages: %6lu",toss_bad,toss_dupes); printf("\n"); } bool TossBundle(char *file,struct osFileEntry *fe) { struct Packer *tmppacker; char buf[200],buf2[100]; struct jbList FEList; struct osFileEntry *pktfe,*safedel; int arcres; if(fe->Size == 0) { LogWrite(1,TOSSINGINFO,"Deleting zero length bundle %s",file); osDelete(file); return(TRUE); } if(!(tmppacker=(struct Packer *)DetectPacker(file))) { LogWrite(1,TOSSINGINFO,"Failed to recognize archive type of %s",file); BadFile(file,"Unknown packer"); return(TRUE); } printf("\n"); LogWrite(2,TOSSINGINFO,"Unarchiving %s using %s",file,tmppacker->Name); printf("\n"); ExpandPacker(tmppacker->Unpacker,buf2,100,file,""); arcres=osChDirExecute(config.cfg_TempDir,buf2); printf("\n"); if(arcres == -1) { LogWrite(1,SYSTEMERR,"Unable to find temp directory \"%s\"",config.cfg_TempDir); return(FALSE); } if(arcres!=0) { LogWrite(1,SYSTEMERR,"Unarchiving failed: %u",arcres); sprintf(buf2,"Unarchiving with %s failed: %u",tmppacker->Name,arcres); BadFile(file,buf2); } if(!osReadDir(config.cfg_TempDir,&FEList,IsPkt)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",config.cfg_TempDir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } if(!SortFEList(&FEList)) { jbFreeList(&FEList); return(FALSE); } for(pktfe=(struct osFileEntry *)FEList.First;pktfe && !ctrlc;pktfe=pktfe->Next) { MakeFullPath(config.cfg_TempDir,pktfe->Name,buf,200); /* If you have your tempdir in Inbound, this might be an unpacked mailpacket that has been processed already */ for(safedel=(struct osFileEntry *)DeleteList.First;safedel;safedel=safedel->Next) if(strcmp(safedel->Name,buf)==0) break; if(!safedel) { if(!ReadPkt(buf,pktfe,TRUE,HandleMessage)) { jbFreeList(&FEList); return(FALSE); } osDelete(buf); } } jbFreeList(&FEList); if(ctrlc) return(FALSE); return(TRUE); } bool TossDir(char *dir) { struct osFileEntry *fe; struct jbList PktFEList; struct jbList ArcFEList; char buf[200]; LogWrite(3,ACTIONINFO,"Tossing files in %s...",dir); if(!BeforeScanToss()) return(FALSE); /* Notify about old bad files */ if(!osReadDir(dir,&PktFEList,IsBad)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",dir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); AfterScanToss(FALSE); return(FALSE); } for(fe=(struct osFileEntry *)PktFEList.First;fe;fe=fe->Next) LogWrite(1,TOSSINGINFO,"Old bad file found in inbound: %s",fe->Name); jbFreeList(&PktFEList); /* Search for pkt files */ if(!osReadDir(dir,&PktFEList,IsPkt)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",dir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); AfterScanToss(FALSE); return(FALSE); } /* Search for bundles */ if(!osReadDir(dir,&ArcFEList,IsArc)) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read directory \"%s\"",dir); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); jbFreeList(&PktFEList); AfterScanToss(FALSE); return(FALSE); } SortFEList(&PktFEList); SortFEList(&ArcFEList); if(nomem) { jbFreeList(&PktFEList); jbFreeList(&ArcFEList); AfterScanToss(FALSE); return(FALSE); } /* Process pkt files */ for(fe=(struct osFileEntry *)PktFEList.First;fe && !ctrlc;fe=fe->Next) { MakeFullPath(dir,fe->Name,buf,200); if(!ReadPkt(buf,fe,FALSE,HandleMessage)) { jbFreeList(&PktFEList); jbFreeList(&ArcFEList); AfterScanToss(FALSE); return(FALSE); } SafeDelete(buf); } for(fe=(struct osFileEntry *)ArcFEList.First;fe && !ctrlc;fe=fe->Next) { MakeFullPath(dir,fe->Name,buf,200); if(!TossBundle(buf,fe)) { jbFreeList(&PktFEList); jbFreeList(&ArcFEList); AfterScanToss(FALSE); return(FALSE); } SafeDelete(buf); } jbFreeList(&PktFEList); jbFreeList(&ArcFEList); if(ctrlc) { AfterScanToss(FALSE); return(FALSE); } LogTossResults(); AfterScanToss(TRUE); return(TRUE); } bool TossFile(char *file) { struct osFileEntry *fe; LogWrite(3,ACTIONINFO,"Tossing file %s...",file); if(!(fe=osGetFileEntry(file))) { uint32_t err=osError(); LogWrite(1,SYSTEMERR,"Failed to read file \"%s\"",file); LogWrite(1,SYSTEMERR,"Error: %s",osErrorMsg(err)); return(FALSE); } if(!BeforeScanToss()) { osFree(fe); return(FALSE); } if(IsPkt(fe->Name)) { if(!ReadPkt(file,fe,FALSE,HandleMessage)) { osFree(fe); AfterScanToss(FALSE); return(FALSE); } SafeDelete(file); } else { if(!TossBundle(file,fe)) { osFree(fe); AfterScanToss(FALSE); return(FALSE); } SafeDelete(file); } LogTossResults(); AfterScanToss(TRUE); return(TRUE); } crashmail-1.5/src/crashmail/toss.h000066400000000000000000000000651230617144500172210ustar00rootroot00000000000000bool TossFile(char *file); bool TossDir(char *dir); crashmail-1.5/src/crashmail/version.h000066400000000000000000000002701230617144500177140ustar00rootroot00000000000000#ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 1 #define VERSION_MINOR 5 #define VERSION_PATCH 0 #define VERSION "1.5" #define TID_VERSION "1.5" #endif /* VERSION_H */ crashmail-1.5/src/jamlib/000077500000000000000000000000001230617144500153525ustar00rootroot00000000000000crashmail-1.5/src/jamlib/LICENSE000066400000000000000000000634761230617144500163770ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! crashmail-1.5/src/jamlib/Makefile.linux000066400000000000000000000011101230617144500201410ustar00rootroot00000000000000CC = gcc $(CPPFLAGS) $(CFLAGS) -D__LINUX__ $(LDFLAGS) -Wall AR = ar -ru RM = rm -f OBJS = crc32.o mbase.o message.o lastread.o subpack.o structrw.o jamlib.a : $(OBJS) $(AR) jamlib.a $(OBJS) crc32.o: crc32.c jam.h $(CC) -c crc32.c -o crc32.o mbase.o: mbase.c jam.h $(CC) -c mbase.c -o mbase.o message.o: message.c jam.h $(CC) -c message.c -o message.o lastread.o: lastread.c jam.h $(CC) -c lastread.c -o lastread.o subpack.o: subpack.c jam.h $(CC) -c subpack.c -o subpack.o structrw.o: structrw.c jam.h $(CC) -c structrw.c -o structrw.o clean: $(RM) *.o *.a crashmail-1.5/src/jamlib/Makefile.os2000066400000000000000000000010651230617144500175160ustar00rootroot00000000000000CC = gcc -Wall -D__OS2__ -DINCL_DOSPROCESS AR = ar -ru RM = del OBJS = crc32.o mbase.o message.o lastread.o subpack.o structrw.o jamlib.a: $(OBJS) $(AR) jamlib.a $(OBJS) crc32.o: crc32.c jam.h $(CC) -c crc32.c -o crc32.o mbase.o: mbase.c jam.h $(CC) -c mbase.c -o mbase.o message.o: message.c jam.h $(CC) -c message.c -o message.o lastread.o: lastread.c jam.h $(CC) -c lastread.c -o lastread.o subpack.o: subpack.c jam.h $(CC) -c subpack.c -o subpack.o structrw.o: structrw.c jam.h $(CC) -c structrw.c -o structrw.o clean: $(RM) *.o *.a crashmail-1.5/src/jamlib/Makefile.win32000066400000000000000000000010311230617144500177460ustar00rootroot00000000000000CC = gcc -Wall AR = ar -ru RM = del OBJS = crc32.o mbase.o message.o lastread.o subpack.o structrw.o jamlib.a : $(OBJS) $(AR) jamlib.a $(OBJS) crc32.o: crc32.c jam.h $(CC) -c crc32.c -o crc32.o mbase.o: mbase.c jam.h $(CC) -c mbase.c -o mbase.o message.o: message.c jam.h $(CC) -c message.c -o message.o lastread.o: lastread.c jam.h $(CC) -c lastread.c -o lastread.o subpack.o: subpack.c jam.h $(CC) -c subpack.c -o subpack.o structrw.o: structrw.c jam.h $(CC) -c structrw.c -o structrw.o clean: $(RM) *.o *.a crashmail-1.5/src/jamlib/crc32.c000066400000000000000000000106511230617144500164350ustar00rootroot00000000000000/* Crc32 - CRC32-calculation for JAM Copyright (C) 2000 Johan Billing This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2003-10-22 - Fixed comparison between signed and unsigned variable in JAM_Crc32() */ #include "jam.h" uint32_t crc32tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; /*********************************************************************** ** ** JAM_Crc32 - Calculates CRC-32 ** ***********************************************************************/ uint32_t JAM_Crc32(char *Buffer_PC, uint32_t Length_I) { uint32_t Crc_I; uint32_t c; Crc_I=0xffffffff; for(c=0;c < Length_I; c++) Crc_I=(Crc_I>>8) ^ crc32tab[(char)Crc_I ^ tolower(Buffer_PC[c])]; return(Crc_I); } crashmail-1.5/src/jamlib/jam.h000066400000000000000000000253021230617144500162740ustar00rootroot00000000000000/* JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2000-04-16: - Added #defines for JAM_NO_MESSAGE and JAM_CORRUPT_MSG - Added #ifndef linux around typedefs for uint16_t and uint32_t - Added prototype for JAM_AddEmptyMessage() Backported changes from JAMLIB 1.4.7 made by Johan Billing 2003-10-26 - Added prototype for JAM_DeleteMessage() */ /*********************************************************************** ** ** JAM Definitions ** ***********************************************************************/ #ifndef __JAM_H__ #define __JAM_H__ #include #include #include #include /* ** Error codes */ #define JAM_BAD_PARAM 1 /* one or more parameters are wrong */ #define JAM_IO_ERROR 2 /* i/o error. check JAM_Errno() for details */ #define JAM_LOCK_FAILED 3 /* lock could not be set */ #define JAM_NOT_LOCKED 4 /* the message base was not locked before writing */ #define JAM_NO_MEMORY 5 /* out of memory! */ #define JAM_NO_USER 6 /* user not found */ #define JAM_NO_MESSAGE 7 /* message has been deleted */ #define JAM_CORRUPT_MSG 8 /* message header is corrupt */ /* ** CRC definitions */ #define JAM_NO_CRC 0xffffffff /* ** File extensions */ #define EXT_HDRFILE ".jhr" #define EXT_TXTFILE ".jdt" #define EXT_IDXFILE ".jdx" #define EXT_LRDFILE ".jlr" /* ** Revision level and header signature */ #define CURRENTREVLEV 1 #define HEADERSIGNATURE "JAM\0" /* ** Header file information block, stored first in all .JHR files */ typedef struct { char Signature[4]; /* followed by */ uint32_t DateCreated; /* Creation date */ uint32_t ModCounter; /* Last processed counter */ uint32_t ActiveMsgs; /* Number of active (not deleted) msgs */ uint32_t PasswordCRC; /* CRC-32 of password to access */ uint32_t BaseMsgNum; /* Lowest message number in index file */ char RSRVD[1000]; /* Reserved space */ } s_JamBaseHeader; /* ** Message status bits */ #define MSG_LOCAL 0x00000001L /* Msg created locally */ #define MSG_INTRANSIT 0x00000002L /* Msg is in-transit */ #define MSG_PRIVATE 0x00000004L /* Private */ #define MSG_READ 0x00000008L /* Read by addressee */ #define MSG_SENT 0x00000010L /* Sent to remote */ #define MSG_KILLSENT 0x00000020L /* Kill when sent */ #define MSG_ARCHIVESENT 0x00000040L /* Archive when sent */ #define MSG_HOLD 0x00000080L /* Hold for pick-up */ #define MSG_CRASH 0x00000100L /* Crash */ #define MSG_IMMEDIATE 0x00000200L /* Send Msg now, ignore restrictions */ #define MSG_DIRECT 0x00000400L /* Send directly to destination */ #define MSG_GATE 0x00000800L /* Send via gateway */ #define MSG_FILEREQUEST 0x00001000L /* File request */ #define MSG_FILEATTACH 0x00002000L /* File(s) attached to Msg */ #define MSG_TRUNCFILE 0x00004000L /* Truncate file(s) when sent */ #define MSG_KILLFILE 0x00008000L /* Delete file(s) when sent */ #define MSG_RECEIPTREQ 0x00010000L /* Return receipt requested */ #define MSG_CONFIRMREQ 0x00020000L /* Confirmation receipt requested */ #define MSG_ORPHAN 0x00040000L /* Unknown destination */ #define MSG_ENCRYPT 0x00080000L /* Msg text is encrypted */ #define MSG_COMPRESS 0x00100000L /* Msg text is compressed */ #define MSG_ESCAPED 0x00200000L /* Msg text is seven bit ASCII */ #define MSG_FPU 0x00400000L /* Force pickup */ #define MSG_TYPELOCAL 0x00800000L /* Msg is for local use only (no export) */ #define MSG_TYPEECHO 0x01000000L /* Msg is for conference distribution */ #define MSG_TYPENET 0x02000000L /* Msg is direct network mail */ #define MSG_NODISP 0x20000000L /* Msg may not be displayed to user */ #define MSG_LOCKED 0x40000000L /* Msg is locked, no editing possible */ #define MSG_DELETED 0x80000000L /* Msg is deleted */ /* ** Message header */ typedef struct { char Signature[4]; /* followed by */ uint16_t Revision; /* CURRENTREVLEV */ uint16_t ReservedWord; /* Reserved */ uint32_t SubfieldLen; /* Length of Subfields */ uint32_t TimesRead; /* Number of times message read */ uint32_t MsgIdCRC; /* CRC-32 of MSGID line */ uint32_t ReplyCRC; /* CRC-32 of REPLY line */ uint32_t ReplyTo; /* This msg is a reply to.. */ uint32_t Reply1st; /* First reply to this msg */ uint32_t ReplyNext; /* Next msg in reply chain */ uint32_t DateWritten; /* When msg was written */ uint32_t DateReceived; /* When msg was received/read */ uint32_t DateProcessed; /* When msg was processed by packer */ uint32_t MsgNum; /* Message number (1-based) */ uint32_t Attribute; /* Msg attribute, see "Status bits" */ uint32_t Attribute2; /* Reserved for future use */ uint32_t TxtOffset; /* Offset of text in text file */ uint32_t TxtLen; /* Length of message text */ uint32_t PasswordCRC; /* CRC-32 of password to access msg */ uint32_t Cost; /* Cost of message */ } s_JamMsgHeader; /* ** Message header Subfield types */ #define JAMSFLD_OADDRESS 0 #define JAMSFLD_DADDRESS 1 #define JAMSFLD_SENDERNAME 2 #define JAMSFLD_RECVRNAME 3 #define JAMSFLD_MSGID 4 #define JAMSFLD_REPLYID 5 #define JAMSFLD_SUBJECT 6 #define JAMSFLD_PID 7 #define JAMSFLD_TRACE 8 #define JAMSFLD_ENCLFILE 9 #define JAMSFLD_ENCLFWALIAS 10 #define JAMSFLD_ENCLFREQ 11 #define JAMSFLD_ENCLFILEWC 12 #define JAMSFLD_ENCLINDFILE 13 #define JAMSFLD_EMBINDAT 1000 #define JAMSFLD_FTSKLUDGE 2000 #define JAMSFLD_SEENBY2D 2001 #define JAMSFLD_PATH2D 2002 #define JAMSFLD_FLAGS 2003 #define JAMSFLD_TZUTCINFO 2004 #define JAMSFLD_UNKNOWN 0xffff /* ** Message header Subfield */ typedef struct { uint16_t LoID; /* Field ID, 0 - 0xffff */ uint16_t HiID; /* Reserved for future use */ uint32_t DatLen; /* Length of buffer that follows */ char* Buffer; /* DatLen bytes of data */ } s_JamSubfield; typedef struct { uint16_t LoID; /* Field ID, 0 - 0xffff */ uint16_t HiID; /* Reserved for future use */ uint32_t DatLen; /* Length of buffer that follows */ } s_JamSaveSubfield; /* ** Message index record */ typedef struct { uint32_t UserCRC; /* CRC-32 of destination username */ uint32_t HdrOffset; /* Offset of header in .JHR file */ } s_JamIndex; /* ** Lastread structure, one per user */ typedef struct { uint32_t UserCRC; /* CRC-32 of user name (lowercase) */ uint32_t UserID; /* Unique UserID */ uint32_t LastReadMsg; /* Last read message number */ uint32_t HighReadMsg; /* Highest read message number */ } s_JamLastRead; /* ** JAMLIB message base handle */ typedef struct { FILE* HdrFile_PS; /* File handle for .JHR file */ FILE* TxtFile_PS; /* File handle for .JDT file */ FILE* IdxFile_PS; /* File handle for .JDX file */ FILE* LrdFile_PS; /* File handle for .JLR file */ int Errno_I; /* last i/o error */ int Locked_I; /* is area locked? */ uint32_t LastUserPos_I; /* last position of lastread record */ uint32_t LastUserId_I; /* userid for the last read lastread record */ } s_JamBase; /* ** JAMLIB subfield packet */ typedef struct { s_JamSubfield** Fields; uint32_t NumFields; uint32_t NumAlloc; } s_JamSubPacket; /* ** JAMLIB function declarations */ /* mbase.c */ int JAM_OpenMB ( char* Basename_PC, s_JamBase** NewArea_PPS ); int JAM_CloseMB ( s_JamBase* Area_PS ); int JAM_CreateMB ( char* Basename_PC, uint32_t BaseMsg_I, s_JamBase** NewArea_PPS ); int JAM_RemoveMB ( s_JamBase* Area_PS, char* Basename_PC ); int JAM_LockMB ( s_JamBase* Area_PS, int Timeout_I ); int JAM_UnlockMB ( s_JamBase* Area_PS ); int JAM_ReadMBHeader ( s_JamBase* Area_PS, s_JamBaseHeader* Header_PS ); int JAM_WriteMBHeader ( s_JamBase* Area_PS, s_JamBaseHeader* Header_PS ); int JAM_FindUser ( s_JamBase* Area_PS, uint32_t UserCrc_I, uint32_t StartMsg_I, uint32_t* MsgNo_PI ); int JAM_GetMBSize ( s_JamBase* Area_PS, uint32_t* Messages_PI ); /* message.c */ int JAM_ReadMsgHeader ( s_JamBase* Area_PS, uint32_t MsgNo_I, s_JamMsgHeader* Header_PS, s_JamSubPacket** SubfieldPack_PPS ); int JAM_ReadMsgText ( s_JamBase* Area_PS, uint32_t Offset_I, uint32_t Length_I, char* Buffer_PC ); int JAM_AddMessage ( s_JamBase* Area_PS, s_JamMsgHeader* Header_PS, s_JamSubPacket* SubPack_PS, char* Text_PC, uint32_t TextLen_I ); int JAM_AddEmptyMessage ( s_JamBase* Area_PS ); int JAM_DeleteMessage ( s_JamBase* Base_PS, uint32_t MsgNo_I ); int JAM_ChangeMsgHeader ( s_JamBase* Area_PS, uint32_t MsgNo_I, s_JamMsgHeader* Header_PS ); int JAM_ClearMsgHeader ( s_JamMsgHeader* Header_PS ); int JAM_Errno ( s_JamBase* Area_PS ); /* lastread.c */ int JAM_ReadLastRead ( s_JamBase* Area_PS, uint32_t User_I, s_JamLastRead* Record_PS ); int JAM_WriteLastRead ( s_JamBase* Area_PS, uint32_t User_I, s_JamLastRead* Record_PS ); /* subpacket.c */ s_JamSubPacket* JAM_NewSubPacket ( void ); int JAM_DelSubPacket ( s_JamSubPacket* SubPack_PS ); s_JamSubfield* JAM_GetSubfield ( s_JamSubPacket* SubPack_PS ); s_JamSubfield* JAM_GetSubfield_R ( s_JamSubPacket* SubPack_PS , uint32_t* Count_PI); int JAM_PutSubfield ( s_JamSubPacket* SubPack_PS, s_JamSubfield* Field_PS ); /* crc32.c */ uint32_t JAM_Crc32 ( char* Buffer_PC, uint32_t Length_I ); #endif crashmail-1.5/src/jamlib/jamlib.doc000066400000000000000000001270471230617144500173120ustar00rootroot00000000000000 JAMLIB A JAM subroutine library by Björn Stenberg modifications by Johan Billing version 1.3.2 2004-07-10 GENERAL ======= History ------- JAMLIB 1.0 was originally released by Björn Stenberg 1996-03-06. Since the original license did not permit modification of the library, Johan Billing contacted Björn Stenberg and asked him to change the license. Björn Stenberg agreed to change the license to the GNU Lesser General Public License 1999-12-21 (see the accompanying file LICENSE). After that, some minor additions and bug fixes were made by Johan Billing and JAMLIB 1.1 was released under the new license. Changes in 1.3.2: * Updated the Win32-specific parts of the code to make it compatible with newer versions of MinGW (tested with 3.1.0-1). Changes in 1.3.1: * Backported the following bugfixes and improvements from JAMLIB 1.4.7 while retaining the platform-independence and high portability of the early versions of JAMLIB. - JAMLIB now uses calloc() instead of malloc() followed by memset() - JAM_OpenMB() and JAM_CreateMB() will set (*NewArea_PPS) to NULL if calloc() failed - JAM_CreateMB() no longer attempts indefinitely to lock the newly created messagebase. If the first attempt fails, it will return an error. - jam_Lock() now sets Base_PS->Errno under Linux - JAM_NewSubPacket() and JAM_PutSubField() would give memory leaks under conditions of low memory conditions. Fixed. - JAM_ReadMsgHeader() would give memory leaks on failure. Fixed. - Added JAM_DeleteMessage() Big thanks to Sir Raorn (and others?) for finding and fixing these bugs! * JAM_CreateMB() would never unlock or close the newly created messagebase upon failure. Fixed. * Improved handling of ActiveMsgs counter. JAM_AddMessage() now only increases ActiveMsgs if the added message does not have MSG_DELETED set. JAM_ChangeMsgHeader() decreases ActiveMsgs if MSG_DELETED is set and the message wasn't already deleted. JAM_DeleteMessage() now only decreases ActiveMsgs if the message wasn't already deleted. * Updated the documentation to reflect the need to free() memory after JAM_CloseMB() and failed calls to JAM_OpenMB() and JAM_CreateMB(). * Eliminated compiler warnings Changes in 1.3: * JAM_AddMessage() would fail when trying to add an empty message to the messagebase under Linux. Fixed. Changes in 1.2: * Since JAM_GetSubField() is not reentrant and cannot be used in multi-threaded applications, JAM_GetSubField_R() was added as a replacement for cases where a reentrant function is needed. Changes in 1.1: * Added support for Win32 and Linux * Added JAM_AddEmptyMessage() * Rewrote the Makefiles * Rewrote the CRC32 routine * Fixed broken JAM_FindUser() * Fixed broken JAM_GetSubfield() * Changed JAM_OpenMB so that files are opened in binary mode. This is necessary to use JAMLIB under Windows. * Improved JAM_ReadMsgHeader() to give the error JAM_NO_MESSAGE if the message no longer exists in the messagebase and JAM_CORRUPT_MSG if the subfields of the message have been corrupted. * Improved portability by changing JAMLIB so that it no longer reads and writes stuctures directly using fread() and fwrite(). * Improved ANSI-C compatibilty by no longer including the non-ANSI header file memory.h and using feof() to check for EOF instead of errno == EPASTEOF. * Added an #ifdef so that ushort and ulong are no longer defined in jam.h when compiling under Linux. These are normally already defined in the standard header files. License ------- JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Description ----------- These are a collection of subroutines that encapsulate much of the format-specific and tedious details of the JAM message base format. The idea is that application programmers by using these routines can concentrate on the more high-level issues of their programs instead of worrying about their JAM routines. I [Björn Stenberg] wrote these routines primarily because I needed them myself. I was trying to implement JAM support in my FrexxLink BBS system and was frustrated by the poor level of documentation supplied in the JAMAPI archive distributed by the JAM authors. Finally, I dove into the JAMAPI source code in a desperate attempt at finding out how to use it. To my despair, I discovered that the JAMAPI is targeted at a very low level. I would need to implement a lot of JAM-handling code into my own program. This library is an attempt to do two things: Firstly, provide an, at least sparingly, _documented_ API, allowing application programmers to easily implement JAM into their programs. Secondly, raise the level of functionality above that of the original JAMAPI package, so that the application programmer does not have to learn and understand all the internals of the JAM message base format to implement support for it. I have not succeded completely on any of the two points, of course. Documentation can never be too good, and there are still a few things about JAM you must know in order to use it. But I think I have made it somewhat easier than perhaps was the case before. References ---------- If you are extra curious about the JAM message format, I suggest you get a hold of an archive called JAMAPI.ARJ. That archive contains a file called JAM.DOC which is the file I have used as reference for the development of these routines. Credits ------- All original code except for the CRC32 routine was written by Björn Stenberg. The CRC32 code was rewritten by Johan Billing for JAMLIB 1.1 to replace the original CRC32 code whose origin and copyright was unclear. The jam.h header file is a compilation of the best from the various header files in the JAMAPI package with some of additions by Björn Stenberg as well. Additions and modifications by Johan Billing. The JAM message base proposal is: JAM(mbp) - Copyright 1993 Joaquim Homrighausen, Andrew Milner, Mats Birch, Mats Wallin. ALL RIGHTS RESERVED Contact Information ------------------- For questions about JAMLIB, please contact: Johan Billing E-mail: billing@df.lth.se If you wish to contact Björn Stenberg, his current e-mail address (as of 1999-12-21) is bjorn@haxx.nu. THE LIBRARY =========== The Source Code --------------- I made a point of making this library as system independant as I could. Only one function needs to be altered when porting this to another system: The file locking. ANSI C does not include file locking so there is not much I can do about it. The choice of C over C++ is a part of this philosophy aswell. More systems have C compilers than C++ compilers, and more people know C than C++. Also, converting this library to a C++ class should be fairly simple. If you do, send me a copy. I use some naming conventions throughout the code and in the examples. These are invented by myself as a reaction to the stunningly ugly and hard-to-read Hungarian Notation promoted by some people. The rules of my notation are simple: * All library-global identifiers are prefixed with 'JAM_'. All file-global identifiers are prefixed with 'jam_'. Local identifiers do not have prefixes. * All variables have a suffix describing their basic type. Suffixes used in this library are: _I - integer (int Example_I) _C - character (char Example_C) _S - struct (struct Example_S) _P - pointer (void* Example_P) _A - array Suffixes are then combined, to show the correct type: _PI - pointer to integer (int* Example_PI) _PC - pointer to char (char* Example_PC) _AC - array of char (char Example_AC[x]) _PPS - pointer to pointer to struct (struct** Example_PPS) * Functions do not have suffixes The whole idea is that it is quicker to read and comprehend a variable called 'Text_PC' than one called 'pszText'. We read from left to right, and thus the most important information - the name - should be the leftmost data in the word. The variable type is additional information and is therefore added to the end where it does not disturb the reader. The Functions ------------- The library is divided into five groups: * Message base functions * Message functions * Subfield functions * LastRead functions * Miscellanous functions -------------------------------------------------------------------------- Message base functions ---------------------- These functions handle JAM message bases, by opening, locking, scanning etc the contents of a message base. These are fairly straight-forward and simple routines that you should have little, if any, trouble with. A message base is identified by a message base handle, which is obtained from either JAM_OpenMB() och JAM_CreateMB(). All functions that read or write from the message base take this handle as parameter, to know which message base to use. ================================ JAM_OpenMB - Open a message base ================================ Syntax int JAM_OpenMB( uchar* Basename_PC, t_JamBase** NewBase_PPS ); Description Opens a message base. Only one message base can be open at a time. Parameters Basename_PC The path and base filename of the message base. "Base filename" means the filename without the JAM-specific extension. NewBase_PPS A pointer to a message base handle where the new message base handle will be written. On error you must free() this memory if (*NewBase_PPS) not NULL. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_BAD_PARAM if NewBas_PPS is NULL Example { int Result_I; Result_I = JAM_OpenMB( "c:\\jam\\mybase", &Base_PS ); if ( Result_I ) printf("JAM_OpenMB returned %d.\n", Result_I ); } ================================ JAM_CloseMB - Close message base ================================ Syntax int JAM_CloseMB( Base_PS ); Description Unlocks (if locked) and closes the currently open message base. Parameters Base_PS The message base to close. Note, that you must free() this memory by yourself. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_LOCK_FAILED if the message base could not be unlocked Example { int Result_I; Result_I = JAM_CloseMB( Base_PS ); if ( Result_I ) printf("JAM_CloseMB returned %d.\n", Result_I ); } ======================================== JAM_CreateMB - Create a new message base ======================================== Syntax int JAM_CreateMB( uchar* Basename_PC, ulong BaseMsg_I, s_JamBase** NewBase_PPS ); Description Creates the necessary files for a new message base and writes a new message base header. If the message base already exists, its contents are destroyed. Parameters Basename_PC The path and base filename of the new message base. BaseMsg_I The base message number (first message #) for the new message base. This number is used when calculating new messages' unique message number. It should not be set to 0. NewBase_PPS A pointer to a message base handle where the new message base handle will be written. On error you must free() this memory if (*NewBase_PPS) not NULL. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_BAD_PARAM if BaseMsg_I is 0 or NewBase_PPS is NULL Example { int Result_I; Result_I = JAM_CreateMB( "c:\\jam\\mybase", 1, &Base_PS ); if ( Result_I ) printf("JAM_CreateMB returned %d.\n", Result_I ); } ==================================== JAM_RemoveMB - Remove a message base ==================================== Syntax int JAM_RemoveMB( ErrorBase_PS, uchar* Basename_PC ); Description Deletes all files associated with a message base. No checking is done as to whether the message base is currently open or not. Parameters ErrorBase_PS The message base in which to store the I/O error, if any. This parameter does *NOT* specify the message to be removed, it is only used for error tracking purposes. If an i/o error occurs when removing the message base files, this message base handler will simply hold the error code. Basename_PC The path and base filename of the message base to remove. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_BAD_PARAM if ErrorBase_PS is NULL Example { int Result_I; Result_I = JAM_RemoveMB( Base_PS, "c:\\jam\\mybase" ); if ( Result_I ) { printf("JAM_RemoveMB returned %d.\n", Result_I ); if ( Result_I == JAM_IO_ERROR ) printf( "i/o error %d\n", JAM_Errno( ErrorBase_PS ) ); } } =================================================== JAM_LockMB - Lock message base for exclusive access =================================================== Syntax int JAM_LockMB( t_JamBase* Base_PS ); Description Locks the currently open message base so that no other programs may modify it. The message base should be locked for only small periods of time, or the performance of tossers and other software may be affected. Parameters Base_PS The message base to lock Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_LOCK_FAILED if the message base is currently locked by another process JAM_BAD_PARAM if Base_PS is NULL Example { int Result_I; while ( 1 ) { Result_I = JAM_LockMB( Base_PS ); if ( Result_I ) { if ( Result_I == JAM_LOCK_FAILED ) /* base locked by someone else, wait for unlock */ sleep( 1 ); else { /* error */ printf("JAM_LockMB returned %d.\n", Result_I ); return -1; } } } } ================================== JAM_UnlockMB - Unlock message base ================================== Syntax int JAM_UnlockMB( s_JamBase* Base_PS ); Description Unlocks message base, allowing other programs to modify it. Parameters Base_PS The message base to unlock Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_BAD_PARAM if Base_PS is NULL Example { int Result_I; Result_I = JAM_UnlockMB( Base_PS ); if ( Result_I ) printf("JAM_UnlockMB returned %d.\n", Result_I ); } =========================================== JAM_ReadMBHeader - Read message base header =========================================== Syntax int JAM_ReadMBHeader( s_JamBase* Base_PS, s_JamBaseHeader* Header_PS ); Description Reads the message base header from the start of the JAM header file. Parameters Base_PS The message base to use Header_PS A pointer to a base header structure where the base header will be stored. Returns 0 if successful JAM_BAD_PARAM if Base_PS or Header_PS is NULL JAM_IO_ERROR if an I/O error occured. see JAM_Errno() Example { s_JamBaseHeader BaseHeader_S; int Result_I; Result_I = JAM_ReadMBHeader( Base_PS, &BaseHeader_S ); if ( Result_I ) printf("JAM_ReadMBHeader returned %d.\n", Result_I ); } ============================================= JAM_WriteMBHeader - Write message base header ============================================= Syntax int JAM_WriteMBHeader( s_JamBase* Base_PS, s_JamBaseHeader* Header_PS ); Description Increases the ModCounter field by one, resets the header signature and writes the message base header to the start of the JAM header file. Parameters Base_PS The message base to use Header_PS A pointer to the base header to be stored Returns 0 if successful JAM_BAD_PARAM if Base_PS or Header_PS is NULL JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NOT_LOCKED if the message base is not locked Example { s_JamBaseHeader BaseHeader_S; int Result_I; /* modify header here */ Result_I = JAM_WriteMBHeader( &BaseHeader_S ); if ( Result_I ) printf("JAM_WriteMBHeader returned %d.\n", Result_I ); } ===================================== JAM_FindUser - Find message to a user ===================================== Syntax int JAM_FindUser( s_JamBase* Base_PS, ulong UserCrc_I, ulong StartMsg_I, ulong* MsgNo_PI ); Description Scans the message base looking for a message written to a specific user. Parameters Base_PS The message base to use UserCrc_I The CRC32 value for the searched name StartMsg_I The first message number to look at. This value is not the message's unique number, but rather the absolute position of the message in the message base. Message 0 therefore means the first message. MsgNo_PI A pointer to a variable where the message number for the found message will be stored. This number is the absolute message position in the message base. Message 0 means the first message. Returns 0 if a message was found JAM_NO_USER if no message was found JAM_IO_ERROR if an I/O error occured. see JAM_Errno() Example { uchar Name_AC[32]; int Result_I; ulong Crc_I; ulong Msg_I; strcpy( Name_AC, "Bjorn Stenberg" ); Crc_I = JAM_Crc32( Name_AC, strlen( Name_AC ) ); Result_I = JAM_FindUser( Base_PS, Crc_I, 0, &Msg_I ); switch ( Result_I ) { case JAM_NO_USER: printf("No message for me.\n"); break; case JAM_IO_ERROR: printf("IO error %d\n", JAM_Errno() ); break; } } ========================================================== JAM_GetMBSize - Get the number of messages in message base ========================================================== Syntax int JAM_GetMBSize( s_JamBase* Base_PS, ulong* Messages_PI ); Description Finds out the number of messages (deleted and undeleted) in the message base. Parameters Base_PS The message base to use Messages_PI A pointer to a variable where the number of messages will be stored. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() Example { int Result_I; ulong Size_I; Result_I = JAM_GetMBSize( Base_PS, &Size_I ); if ( Result_I ) printf("JAM_GetMBSize returned %d.\n", Result_I ); } -------------------------------------------------------------------------- Message functions ----------------- These functions handle individual JAM messages. A JAM message contains of three parts: * Message Header * Message Header Subfields * Message Text The message header is a simple C structure and the message text is a simple text buffer. The subfields, however, are a bit more tricky. These contain everything that is not covered by the header, including the TO, FROM, SUBJECT fields, origin and destination network adresses etc. There can be an unlimited number of subfields to a message. In this routine library the subfields are encapsulated by a 'subfield packet', which is handled by its own set of routines. See a later section of this document for an explanation of those. ============================================================= JAM_ReadMsgHeader - Read a message's header and its subfields ============================================================= Syntax int JAM_ReadMsgHeader( s_JamBase* Base_PS, ulong MsgNo_I, s_JamMsgHeader* Header_PS, s_JamSubPacket** Subfields_PPS ); Description Reads a message header and (optionally) the message header subfields. Parameters Base_PS The message base to use MsgNo_I The message number, i.e. the absolute position of the message in the message base. Message 0 is the first message. Header_PS A pointer to a message header structure where the message header will be stored. Subfields_PPS A pointer to a subpacket pointer, where the subfield packet handle will be stored. If this parameter is NULL, no subfields are read. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NO_MEMORY if a memory allocation failed JAM_NO_MESSAGE if message has been removed JAM_CORRUPT_MSG if message subfields are corrupted Example { s_JamMsgHeader Header_S; s_JamSubPacket* SubPack_PS int Result_I; Result_I = JAM_ReadMsgHeader( 0, &Header_S, &SubPack_PS ); if ( Result_I ) printf("JAM_ReadMsgHeader returned %d.\n", Result_I ); } ======================================= JAM_ReadMsgText - Read a message's text ======================================= Syntax int JAM_ReadMsgText( s_JamBase* Base_PS, ulong Offset_I, ulong Length_I, uchar* Buffer_PC ); Description Reads the body text associated with a message. Parameters Base_PS The message base to use Offset_I The text position in the text file. This information is stored in the message header. Length_I The text length. This information is stored in the message header. Buffer_PC A pointer to where the text should be stored. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() Example { s_JamMsgHeader Header_S; uchar* Buffer_PC; int Result_I; /* read msg header */ Result_I = JAM_ReadMsgHeader( Base_PS, 0, &Header_S, &SubPack_PS ); if ( Result_I ) { printf("JAM_ReadMsgHeader returned %d.\n", Result_I ); return; } /* allocate buffer text */ Buffer_PC = (uchar*) malloc( Header_S.TxtLen ); if ( !Buffer_PC ) { printf("malloc failed.\n"); return; } /* read text */ Result_I = JAM_ReadMsgText( Base_PS, Header_S.TxtOffset, Header_S.TxtLen, Buffer_PC ); if ( Result_I ) printf("JAM_ReadMsgText returned %d.\n", Result_I ); free( Buffer_PC ); } ============================================== JAM_AddMessage - Add a message to message base ============================================== Syntax int JAM_AddMessage( s_JamBase* Base_PS, s_JamMsgHeader* Header_PS, s_JamSubPacket* SubPack_PS, uchar* Text_PC, ulong TextLen_I ); Description Adds a message to the message base. Fully automatic. Parameters Base_PS The message base to use Header_PS A pointer to the message header struct. The function will set the following header fields: Signature, Revision, TxtOffset, TxtLen, SubfieldLen and MsgNum. Whatever you set these fields to will be overwritten. SubPack_PS A subfield packet handler, containing all subfields for the message. Text_PC A pointer to the first byte of the message text. TextLen_I The length of the message text, excluding any zero termination characters. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NOT_LOCKED if the message base is not locked Example { s_JamSubPacket* SubPacket_PS; s_JamSubfield Subfield_S; s_JamMsgHeader Header_S; uchar Text_AC[64]; uchar Field_AC[64]; /* ** Fix message header */ JAM_ClearMsgHeader( &Header_S ); Header_S.DateWritten = time(NULL); /* ** Create subfield packet */ SubPacket_PS = JAM_NewSubPacket(); if ( !SubPacket_PS ) { printf("JAM_NewSubPacket returned NULL.\n" ); return; } /* set up subfield 1 */ strcpy( Field_AC, "This is field #1" ); Subfield_S.LoID = JAMSFLD_SENDERNAME; Subfield_S.HiID = 0; Subfield_S.DatLen = strlen( Field_AC ); Subfield_S.Buffer = Field_AC; JAM_PutSubfield( SubPacket_PS, &Subfield_S ); /* set up subfield 2 */ strcpy( Field_AC, "This is field #2" ); Subfield_S.LoID = JAMSFLD_RECVRNAME; Subfield_S.HiID = 0; Subfield_S.DatLen = strlen( Field_AC ); Subfield_S.Buffer = Field_AC; JAM_PutSubfield( SubPacket_PS, &Subfield_S ); /* ** Add message */ strcpy( Text_AC, "Hello world!\nThis is a test."); /* [lock the message base] */ Result_I = JAM_AddMessage( Base_PS, &Header_S, SubPacket_PS, Text_AC, strlen( Text_AC ) ); if ( Result_I ) { printf("JAM_AddMessage returned %d.\n", Result_I ); return; } /* [unlock the message base] */ JAM_DelSubPacket( SubPacket_PS ); } ================================================================= JAM_AddEmptyMessage - Add a empty message entry to a message base ================================================================= Syntax int JAM_AddEmptyMessage( s_JamBase* Base_PS); Description Adds an empty message header to the message base. Useful when writing a messagebase maintenance utility. Parameters Base_PS The message base to use Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NOT_LOCKED if the message base is not locked Example none =============================================== JAM_ChangeMsgHeader - Change a message's header =============================================== Syntax int JAM_ChangeMsgHeader( s_JamBase* Base_PS, ulong MsgNo_I, s_JamMsgHeader* Header_PS ); Description Writes over an old message header with a new one. Only the header - not the subfields - can be changed due to the subfields' dynamic size. NOTE! Use this function with caution. It is easy to corrupt a message by giving it an incorrect header. Parameters Base_PS The message base to use MsgNo_I The absolute message number. Message #0 is the first in the message base. Header_PS A pointer to the header structure to write. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NOT_LOCKED if the message base is not locked Example { s_JamMsgHeader Header_S; int Result_I; /* [lock the message base] */ Result_I = JAM_ReadMsgHeader( Base_PS, 0, &Header_S, NULL ); if ( Result_I ) printf("JAM_ReadMsgHeader returned %d.\n", Result_I ); Header_S.TimesRead++; Result_I = JAM_ChangeMsgHeader( Base_PS, 0, &Header_S ); if ( Result_I ) printf("JAM_ChangeMsgHeader returned %d.\n", Result_I ); /* [unlock the message base] */ } ===================================================== JAM_ClearMsgHeader - Clear a message header structure ===================================================== Syntax int JAM_ClearMsgHeader( s_JamMsgHeader* Header_PS ); Description Clears a message header structure and prepares it for use. This includes setting the Signature field and the Revision field to their correct values, and setting the CRC fields to JAM_NO_CRC. Parameters Header_PS A pointer to the structure to prepare. Returns 0 if successful JAM_BAD_PARAM if Header_PS is NULL =================================================== JAM_DeleteMessage - Delete message from messagebase =================================================== Syntax int JAM_DeleteMessage( s_JamBase* Base_PS, ulong MsgNo_I ); Description Deletes message from messagebase by setting HdrOffset and UserCRC in index to 0xFFFFFFFF. ActiveMsgs in base header also updated. Parameters Base_PS The message base to use MsgNo_I The absolute message number. Message #0 is the first in the message base. Returns 0 if successful JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NOT_LOCKED if the message base is not locked Example none -------------------------------------------------------------------------- Subfield packet functions ------------------------- As described earlier, a subfield is a part of the message header. Due to the complexity of the different network types in use, it is not feasible to try and cram all data into one header struct. Therefore, JAM uses a fairly small header struct and instead marks all additional data fields as 'subfields'. In order to make life a little more easy, I have used the concept of a container for all subfields. I call it a 'Subfield Packet'. It is identified by a struct pointer, and should be looked upon as a file or a list that you manipulate via the following four functions: =============================================== JAM_NewSubPacket - Create a new subfield packet =============================================== Syntax s_JamSubPacket* JAM_NewSubPacket( void ); Description Creates a new, empty, subfield packet. Parameters None Returns The subpacket handle, if successful, or NULL if a memory allocation failed. Example { s_JamSubPacket* SubPacket_PS; SubPacket_PS = JAM_NewSubPacket(); if ( !SubPacket_PS ) { printf("JAM_NewSubPacket returned NULL.\n" ); return; } } =========================================== JAM_DelSubPacket - Delete a subfield packet =========================================== Syntax int JAM_DelSubPacket( s_JamSubPacket* SubPack_PS ); Description Frees all memory used by a subfield packet. All subfields in the packet will be lost and the packet handle will not be valid any more. Parameters SubPack_PS The subfield packet to delete Returns 0 if successful JAM_BAD_PARAM if SubPack_PS is NULL. Example { s_JamSubPacket* SubPacket_PS; SubPacket_PS = JAM_NewSubPacket(); if ( !SubPacket_PS ) { printf("JAM_NewSubPacket returned NULL.\n" ); return; } SubPacket_PS = JAM_DelSubPacket(); if ( !SubPacket_PS ) { printf("JAM_DelSubPacket returned NULL.\n" ); return; } } ======================================================================= JAM_GetSubfield - Get a subfield from a subfield packet (not reentrant) ======================================================================= Syntax s_JamSubfield* JAM_GetSubfield( s_JamSubPacket* SubPack_PS ); Description Returns a pointer to the first/next subfield struct in the subfield packet. WARNING: This function is not reentrant and should not be used in multi-threaded applications unless you know what you are doing. Use JAM_GetSubfield_R instead when a reentrant function is needed. Parameter SubPack_PS The subfield packet to use. If this parameter is NULL, the next subfield from the subfield packet previously scanned will be returned. Returns A pointer to a subfield, if successful, or NULL if there are no more subfields in the packet. Example { s_JamSubPacket* SubPack_PS; s_JamSubfield* Subfield_PS; s_JamMsgHeader Header_S; int Result_I; Result_I = JAM_ReadMsgHeader( 0, &Header_S, &SubPack_PS ); if ( Result_I ) printf("JAM_ReadMsgHeader returned %d.\n", Result_I ); for ( Subfield_PS = JAM_GetSubfield( SubPack_PS ); Subfield_PS; Subfield_PS = JAM_GetSubfield( NULL ) ) printf("Subfield id %d\n", Subfield_PS->LoID ); JAM_DelSubPacket( SubPack_PS ); } ===================================================================== JAM_GetSubfield_R - Get a subfield from a subfield packet (reentrant) ===================================================================== Syntax s_JamSubfield* JAM_GetSubfield( s_JamSubPacket* SubPack_PS, ulong* Count_PI ); Description Returns a pointer to the first/next subfield struct in the subfield packet. This function is a reentrant replacement for JAM_GetSubfield. Parameter SubPack_PS The subfield packet to use. Count_PI Pointer to a variable that contains the number of the subfield to retrieve. The variable should be set to zero the first time the function is called and is then automatically increased by the function for any subsequent calls. Returns A pointer to a subfield, if successful, or NULL if there are no more subfields in the packet. Example { s_JamSubPacket* SubPack_PS; s_JamSubfield* Subfield_PS; s_JamMsgHeader Header_S; ulong Count_I; int Result_I; Result_I = JAM_ReadMsgHeader( 0, &Header_S, &SubPack_PS ); if ( Result_I ) printf("JAM_ReadMsgHeader returned %d.\n", Result_I ); Count_I = 0; while( ( Subfield_PS = JAM_GetSubfield_R( SubPack_PS , &Count_I ) ) ) printf("Subfield id %d\n", Subfield_PS->LoID ); JAM_DelSubPacket( SubPack_PS ); } ======================================================= JAM_PutSubfield - Put a subfield into a subfield packet ======================================================= Syntax int JAM_PutSubfield( s_JamSubPacket* SubPack_PS, s_JamSubfield* Subfield_PS ); Description Puts a subfield into a subfield packet. The subfield is copied before being put into the subfield packet. Parameters SubPack_PS The subfield packet to add to Subfield_PS The subfield to put in the packet Returns 0 if successful JAM_NO_MEMORY if a memory allocation failed Example { s_JamSubPacket* SubPacket_PS; s_JamSubfield Subfield_S; uchar Field_AC[64]; SubPacket_PS = JAM_NewSubPacket(); if ( !SubPacket_PS ) { printf("JAM_NewSubPacket returned NULL.\n" ); return; } /* set up subfield 1 */ strcpy( Field_AC, "This is field #1" ); Subfield_S.LoID = JAMSFLD_SENDERNAME; Subfield_S.HiID = 0; Subfield_S.DatLen = strlen( Field_AC ); Subfield_S.Buffer = Field_AC; JAM_PutSubfield( SubPacket_PS, &Subfield_S ); /* set up subfield 2 */ strcpy( Field_AC, "This is field #2" ); Subfield_S.LoID = JAMSFLD_RECVRNAME; Subfield_S.HiID = 0; Subfield_S.DatLen = strlen( Field_AC ); Subfield_S.Buffer = Field_AC; JAM_PutSubfield( SubPacket_PS, &Subfield_S ); JAM_DelSubPacket( SubPacket_PS ); } -------------------------------------------------------------------------- LastRead functions ------------------ JAM implements the often-used concept of high water marking for remembering which user read how many messages in each area. Personally I think this concept stinks, since it does not store *which* messages a user has read, only the number of the highest message he has read. But since it's a part of JAM and it's fairly straightforward and easy, I've implemented two support functions for it. I would, however, strongly recommend all BBS programmers to use proper message mapping systems instead, so your users can read their messages in whatever order they wish. ========================================= JAM_ReadLastRead - Read a lastread record ========================================= Syntax int JAM_ReadLastRead( s_JamBase* Base_PS, ulong User_I, s_JamLastRead* Record_PS ); Description Reads a lastread record from the lastread file. Parameter Base_PS The message base to use User_I A system-unique user number. Record_PS A pointer to the lastread struct where the record will be stored. Returns 0 if successful JAM_BAD_PARAM if Record_PS is NULL JAM_IO_ERROR if an I/O error occured. see JAM_Errno() JAM_NO_USER if the user number was not found Example { int Result_I; s_JamLastRead LastRead_S; Result_I = JAM_ReadLastRead( Base_PS, 4711, &LastRead_S ); if ( Result_I ) printf("JAM_ReadLastRead returned %d\n", Result_I ); } =========================================== JAM_WriteLastRead - Write a lastread record =========================================== Syntax int JAM_WriteLastRead( s_JamBase* Base_PS, ulong User_I, s_JamLastRead* Record_PS ); Description Writes a lastread record to the lastread file. If the user number could not be found, the record will be appended to the end of the file. Parameter Base_PS The message base to use User_I A system-unique user number Record_PS A pointer to the lastread struct to be written Returns 0 if successful JAM_BAD_PARAM if Record_PS is NULL JAM_IO_ERROR if an I/O error occured. see JAM_Errno() Example { int Result_I; s_JamLastRead LastRead_S; Result_I = JAM_WriteLastRead( Base_PS, 4711, &LastRead_S ); if ( Result_I ) printf("JAM_WriteLastRead returned %d\n", Result_I ); } -------------------------------------------------------------------------- Miscellanous functions ---------------------- ============================================== JAM_Crc32 - Calculate CRC32 on a block of data ============================================== Syntax ulong JAM_Crc32( uchar* Buffer_PC, ulong Length_I ); Description Calculates the Crc32 value for a block of data. All ASCII characters are converted to lowercase before calculating the CRC (the input data is unchanged). Parameters Buffer_PC A pointer to the first byte of the data block Length_I The number of bytes in the data block Returns The Crc32 value Example { ulong Crc_I; uchar Text_AC[32]; strcpy( Text_AC, "Hello world!\n"); Crc_I = JAM_Crc32( Text_AC, strlen( Text_AC ) ); } ============================= JAM_Errno - Specify I/O error ============================= Syntax int JAM_Errno( s_JamBase* Base_PS ); Description When any of these library routines return JAM_IO_ERROR, you can call this function to find out exactly what went wrong. Parameters Base_PS The message base to use Returns Standard 'errno' values, as the C compiler generated them, or if the I/O error was system specific, the return code is (10000 + system status code). Examples { int Result_I; uchar Text_AC[10]; /* generate an I/O error */ Result_I = JAM_ReadMsgText( 0xffffffff, 10, Text_AC ); if ( Result_I ) { errno = JAM_Errno( Base_PS ); perror("JAM i/o error"); } } crashmail-1.5/src/jamlib/lastread.c000066400000000000000000000107621230617144500173230ustar00rootroot00000000000000/* JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2000-04-16: - Changed source to use feof() instead of errno == EPASTEOF - Changed source to use structrw to read and write structures */ /*********************************************************************** ** ** LASTREAD.C -- Lastread pointer handling ** ** Author: Bj”rn Stenberg (bjorn.stenberg@sth.frontec.se) ** ***********************************************************************/ #include #include #include "jam.h" #include "structrw.h" /*********************************************************************** ** ** File global variables ** ***********************************************************************/ /*********************************************************************** ** ** JAM_ReadLastRead - Read LastRead record ** ***********************************************************************/ int JAM_ReadLastRead( s_JamBase* Base_PS, uint32_t User_I, s_JamLastRead* Record_PS ) { s_JamLastRead Record_S; int Pos_I; if (!Record_PS) return JAM_BAD_PARAM; if ( fseek( Base_PS->LrdFile_PS, 0, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } for ( Pos_I = 0; ; Pos_I++ ) { if ( 1 > freadjamlastread(Base_PS->LrdFile_PS,&Record_S) ) { if ( feof(Base_PS->LrdFile_PS) ) return JAM_NO_USER; Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( Record_S.UserID == User_I ) { Base_PS->LastUserPos_I = Pos_I; Base_PS->LastUserId_I = User_I; *Record_PS = Record_S; return 0; } } return 0; } /*********************************************************************** ** ** JAM_WriteLastRead - Write LastRead record ** ***********************************************************************/ int JAM_WriteLastRead( s_JamBase* Base_PS, uint32_t User_I, s_JamLastRead* Record_PS ) { s_JamLastRead Record_S; int Pos_I; if (!Record_PS) return JAM_BAD_PARAM; /* if the last read is stored */ if ( User_I == Base_PS->LastUserId_I ) { Pos_I = Base_PS->LastUserPos_I * sizeof( s_JamLastRead ); if ( fseek( Base_PS->LrdFile_PS, Pos_I, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* be safe, check it */ if ( 1 > freadjamlastread(Base_PS->LrdFile_PS,&Record_S) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* is it where we expected it to be? */ if ( User_I == Record_S.UserID ) { if ( fseek( Base_PS->LrdFile_PS, Pos_I, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( 1 > fwritejamlastread(Base_PS->LrdFile_PS,Record_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } fflush(Base_PS -> LrdFile_PS); return 0; } } /* no last position, or position incorrect */ if ( fseek( Base_PS->LrdFile_PS, 0, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } for ( Pos_I = 0; ; Pos_I++ ) { if ( 1 > freadjamlastread(Base_PS->LrdFile_PS,&Record_S) ) { if ( feof(Base_PS->LrdFile_PS) ) { /* user not in file, append a new record */ if ( fseek( Base_PS->LrdFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } break; } Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* found the user? */ if ( Record_S.UserID == User_I ) { if ( fseek( Base_PS->LrdFile_PS, Pos_I * sizeof(s_JamLastRead), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } break; } } if ( 1 > fwritejamlastread(Base_PS->LrdFile_PS,Record_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } fflush( Base_PS->LrdFile_PS ); return 0; } crashmail-1.5/src/jamlib/mbase.c000066400000000000000000000355631230617144500166210ustar00rootroot00000000000000/* JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2000-04-16: - Added support for Win32 and Linux - Changed JAM_OpenMB to open files in binary mode - Changed source to use feof() instead of errno == EPASTEOF - Changed source to use structrw to read and write structures - Fixed broken JAM_FindUser() - #includes string.h and stdlib.h instead of memory.h Backported changes from JAMLIB 1.4.7 made by Johan Billing 2003-10-26 - Now uses calloc instead of malloc/memset - (*NewArea_PPS) will be set to zero even if calloc() failed in JAM_OpenMB() and JAM_CreateMB() - JAM_CreateMB() no longer attempts to forever to lock the newly created messagebase. If the first attempt fails, it will return an error. - jam_Lock() now sets Base_PS->Errno under Linux Other changes made by Johan Billing 2003-10-26 - Fixed comparison between signed and unsigned variable in JAM_GetMBSize() - JAM_CreateMB() would not unlock and close the newly created messagebase upon failure. Changes made by Johan Billing 2004-07-10 - Updated the Win32-specific parts of the code to make it compatible with newer versions of MinGW (tested with 3.1.0-1): * Now uses Sleep() instead of sleep() * Changed _LK_UNLOCK to _LK_UNLCK in jam_Lock() */ /*********************************************************************** ** ** MBASE.C -- Message base handling ** ** Author: Bj”rn Stenberg (bjorn.stenberg@sth.frontec.se) ** ***********************************************************************/ #include #include #include #include #include #include "jam.h" #include "structrw.h" #if defined( __OS2__ ) #include /* ANSI C does not include file locking :-( */ #endif #if defined( __WIN32__ ) #include #include #include #if !defined( _LK_UNLCK ) && defined ( _LK_UNLOCK ) #define _LK_UNLCK _LK_UNLOCK /* For backwards compatibility */ #endif #endif #if defined( __LINUX__ ) #include #include #endif #define OS_ERROR_OFFSET 10000 #if defined( __OS2__ ) #define JAM_Sleep( _x_ ) DosSleep( _x_*1000 ) #endif #if defined( __WIN32__ ) #define JAM_Sleep(x) Sleep(x*1000) #endif #if defined( __LINUX__ ) #define JAM_Sleep(x) sleep(x) #endif /*************************************<********************************** ** ** File-global functions ** ***********************************************************************/ int jam_Open( s_JamBase* Base_PS, char* Filename_PC, char* Mode_PC ); int jam_Lock( s_JamBase* Base_PS, int DoLock_I ); /*********************************************************************** ** ** JAM_OpenMB - Open message base ** ***********************************************************************/ int JAM_OpenMB( char* Basename_PC, s_JamBase** NewArea_PPS ) { s_JamBase* Base_PS; int Status_I; if ( !NewArea_PPS ) return JAM_BAD_PARAM; *NewArea_PPS = NULL; Base_PS = (s_JamBase*) calloc( 1, sizeof( s_JamBase ) ); if (!Base_PS) return JAM_NO_MEMORY; *NewArea_PPS = Base_PS; Status_I = jam_Open( Base_PS, Basename_PC, "r+b" ); if ( Status_I ) { return Status_I; } return 0; } /*********************************************************************** ** ** JAM_CreateMB - Create a new message base ** ***********************************************************************/ int JAM_CreateMB( char* Basename_PC, uint32_t BaseMsg_I, s_JamBase** NewArea_PPS ) { s_JamBaseHeader Base_S; int Status_I; s_JamBase* Base_PS; if ( !NewArea_PPS || !BaseMsg_I ) return JAM_BAD_PARAM; *NewArea_PPS = NULL; Base_PS = (s_JamBase*) calloc( 1, sizeof( s_JamBase ) ); if (!Base_PS) return JAM_NO_MEMORY; *NewArea_PPS = Base_PS; Status_I = jam_Open( Base_PS, Basename_PC, "w+b" ); if ( Status_I ) return Status_I; Base_S.DateCreated = time(NULL); Base_S.ModCounter = 0; Base_S.ActiveMsgs = 0; Base_S.PasswordCRC = 0xffffffff; Base_S.BaseMsgNum = BaseMsg_I; memset( &Base_S.RSRVD, 0, sizeof( Base_S.RSRVD ) ); Status_I = JAM_LockMB( Base_PS, 0 ); /* If the new base cannot be locked directly, something is seriously wrong */ if ( Status_I ) { JAM_CloseMB(Base_PS); return Status_I; } Status_I = JAM_WriteMBHeader( Base_PS, &Base_S ); if ( Status_I ) { JAM_UnlockMB( Base_PS ); JAM_CloseMB(Base_PS); return Status_I; } JAM_UnlockMB( Base_PS ); return 0; } /*********************************************************************** ** ** JAM_CloseMB - Close message base ** ***********************************************************************/ int JAM_CloseMB( s_JamBase* Base_PS ) { if ( Base_PS->Locked_I ) { int Status_I = JAM_UnlockMB( Base_PS ); if ( Status_I ) return Status_I; } if ( Base_PS->HdrFile_PS ) { fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; fclose( Base_PS->TxtFile_PS ); Base_PS->TxtFile_PS = NULL; fclose( Base_PS->IdxFile_PS ); Base_PS->IdxFile_PS = NULL; fclose( Base_PS->LrdFile_PS ); Base_PS->LrdFile_PS = NULL; } Base_PS->Locked_I = 0; return 0; } /*********************************************************************** ** ** JAM_RemoveMB - Remove a message base ** ***********************************************************************/ int JAM_RemoveMB( s_JamBase* Base_PS, char* Basename_PC ) { char Filename_AC[250]; int Status_AI[4]; /* .JHR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_HDRFILE ); Status_AI[0] = remove( Filename_AC ); if ( Status_AI[0] ) Base_PS->Errno_I = errno; /* .JDT file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_TXTFILE ); Status_AI[1] = remove( Filename_AC ); if ( Status_AI[1] ) Base_PS->Errno_I = errno; /* .JDX file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_IDXFILE ); Status_AI[2] = remove( Filename_AC ); if ( Status_AI[2] ) Base_PS->Errno_I = errno; /* .JLR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_LRDFILE ); Status_AI[3] = remove( Filename_AC ); if ( Status_AI[3] ) Base_PS->Errno_I = errno; if (Status_AI[0] || Status_AI[1] || Status_AI[2] || Status_AI[3]) return JAM_IO_ERROR; return 0; } /*********************************************************************** ** ** JAM_GetMBSize - Get the number of messages in message base ** ***********************************************************************/ int JAM_GetMBSize( s_JamBase* Base_PS, uint32_t* Messages_PI ) { long Offset_I; /* go to end of index file */ if ( fseek( Base_PS->IdxFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Offset_I = ftell( Base_PS->IdxFile_PS ); if ( Offset_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } *Messages_PI = Offset_I / sizeof( s_JamIndex ); return 0; } /*********************************************************************** ** ** JAM_LockMB - Lock message base ** ***********************************************************************/ int JAM_LockMB( s_JamBase* Base_PS, int Timeout_I ) { if ( Base_PS->Locked_I ) return 0; switch ( Timeout_I ) { /* unlimited timeout */ case -1: while ( jam_Lock( Base_PS, 1 ) == JAM_LOCK_FAILED ) JAM_Sleep( 1 ); return 0; /* no timeout */ case 0: return jam_Lock( Base_PS, 1 ); /* X seconds timeout */ default: { time_t Time_I = time(NULL) + Timeout_I; while ( time(NULL) < Time_I ) { int Result_I; Result_I = jam_Lock( Base_PS, 1 ); if ( Result_I == JAM_LOCK_FAILED ) JAM_Sleep( 1 ); else return Result_I; } return JAM_LOCK_FAILED; } } } /*********************************************************************** ** ** JAM_UnlockMB - Flush all writes and unlock message base ** ***********************************************************************/ int JAM_UnlockMB( s_JamBase* Base_PS ) { fflush( Base_PS->HdrFile_PS ); fflush( Base_PS->TxtFile_PS ); fflush( Base_PS->IdxFile_PS ); fflush( Base_PS->LrdFile_PS ); return jam_Lock( Base_PS, 0 ); } /*********************************************************************** ** ** JAM_ReadMBHeader - Read message base header ** ***********************************************************************/ int JAM_ReadMBHeader( s_JamBase* Base_PS, s_JamBaseHeader* Header_PS ) { if ( !Header_PS || !Base_PS ) return JAM_BAD_PARAM; if ( fseek( Base_PS->HdrFile_PS, 0, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( 1 > freadjambaseheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } return 0; } /*********************************************************************** ** ** JAM_WriteMBHeader - Write message base header ** ***********************************************************************/ int JAM_WriteMBHeader( s_JamBase* Base_PS, s_JamBaseHeader* Header_PS ) { if ( !Header_PS || !Base_PS ) return JAM_BAD_PARAM; if ( !Base_PS->Locked_I ) return JAM_NOT_LOCKED; if ( fseek( Base_PS->HdrFile_PS, 0, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* ensure header looks right */ memcpy( Header_PS->Signature, HEADERSIGNATURE, 4 ); Header_PS->ModCounter++; if ( 1 > fwritejambaseheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } fflush( Base_PS->HdrFile_PS ); return 0; } /*********************************************************************** ** ** JAM_FindUser - Scan scan file for messages to a user ** ***********************************************************************/ int JAM_FindUser( s_JamBase* Base_PS, uint32_t UserCrc_I, uint32_t StartMsg_I, uint32_t* MsgNo_PI ) { uint32_t MsgNo_I; /* go to start message */ if ( fseek( Base_PS->IdxFile_PS, StartMsg_I * sizeof( s_JamIndex ), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* scan file */ for ( MsgNo_I = StartMsg_I; ; MsgNo_I++ ) { s_JamIndex Index_S; if ( 1 > freadjamindex(Base_PS->IdxFile_PS,&Index_S) ) { if ( feof(Base_PS->IdxFile_PS) ) return JAM_NO_USER; Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( Index_S.UserCRC == UserCrc_I ) break; } *MsgNo_PI = MsgNo_I; return 0; } /*********************************************************************** ** ** jam_Lock - Lock/unlock a message base ** ***********************************************************************/ int jam_Lock( s_JamBase* Base_PS, int DoLock_I ) { #if defined(__OS2__) FILELOCK Area_S; APIRET Status_I; ULONG Timeout_I = 0; int Handle_I; Handle_I = fileno( Base_PS->HdrFile_PS ); if ( Handle_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Area_S.lOffset = 0; Area_S.lRange = 1; if ( DoLock_I ) Status_I = DosSetFileLocks( Handle_I, NULL, &Area_S, Timeout_I, 0 ); else Status_I = DosSetFileLocks( Handle_I, &Area_S, NULL, Timeout_I, 0 ); if ( Status_I ) { if ( 232 == Status_I ) return JAM_LOCK_FAILED; Base_PS->Errno_I = Status_I + OS_ERROR_OFFSET; return JAM_IO_ERROR; } if ( DoLock_I ) Base_PS->Locked_I = 1; else Base_PS->Locked_I = 0; return 0; #elif defined(__WIN32__) int Handle_I,Status_I; fseek(Base_PS->HdrFile_PS,0,SEEK_SET); /* Lock from start of file */ Handle_I = fileno( Base_PS->HdrFile_PS ); if ( Handle_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( DoLock_I ) Status_I = _locking(Handle_I,_LK_NBLCK,1); else Status_I = _locking(Handle_I,_LK_UNLCK,1); if ( Status_I ) return JAM_LOCK_FAILED; if ( DoLock_I ) Base_PS->Locked_I = 1; else Base_PS->Locked_I = 0; return 0; #elif defined(__LINUX__) int Handle_I,Status_I; struct flock fl; fseek(Base_PS->HdrFile_PS,0,SEEK_SET); /* Lock from start of file */ Handle_I = fileno( Base_PS->HdrFile_PS ); if ( Handle_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if(DoLock_I) fl.l_type=F_WRLCK; else fl.l_type=F_UNLCK; fl.l_whence=SEEK_SET; fl.l_start=0; fl.l_len=1; fl.l_pid=getpid(); Status_I=fcntl(Handle_I,F_SETLK,&fl); if ( Status_I ) { Base_PS->Errno_I = errno; return JAM_LOCK_FAILED; } if ( DoLock_I ) Base_PS->Locked_I = 1; else Base_PS->Locked_I = 0; return 0; #else #error Unsupported platform #endif } /*********************************************************************** ** ** jam_Open - Open/create message base files ** ***********************************************************************/ int jam_Open( s_JamBase* Base_PS, char* Basename_PC, char* Mode_PC ) { char Filename_AC[250]; /* .JHR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_HDRFILE ); Base_PS->HdrFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->HdrFile_PS) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* .JDT file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_TXTFILE ); Base_PS->TxtFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->TxtFile_PS) { Base_PS->Errno_I = errno; fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; return JAM_IO_ERROR; } /* .JDX file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_IDXFILE ); Base_PS->IdxFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->IdxFile_PS) { Base_PS->Errno_I = errno; fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; fclose( Base_PS->TxtFile_PS ); Base_PS->TxtFile_PS = NULL; return JAM_IO_ERROR; } /* .JLR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_LRDFILE ); Base_PS->LrdFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->LrdFile_PS) { Base_PS->Errno_I = errno; fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; fclose( Base_PS->TxtFile_PS ); Base_PS->TxtFile_PS = NULL; fclose( Base_PS->IdxFile_PS ); Base_PS->IdxFile_PS = NULL; return JAM_IO_ERROR; } return 0; } crashmail-1.5/src/jamlib/message.c000066400000000000000000000401461230617144500171470ustar00rootroot00000000000000/* JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2000-04-16: - Changed source to use feof() instead of errno == EPASTEOF - Changed source to use structrw to read and write structures - Added JAM_AddEmptyMessage() - Added error messages JAM_NO_MESSAGE and JAM_CORRUPT_MSG for JAM_ReadMsgHeader() - #includes stdlib.h instead of malloc.h and memory.h - Fixed a bug that caused JAM_AddMessage() to fail when trying to add an empty message to the messagebase under Linux. Backported changes from JAMLIB 1.4.7 made by Johan Billing 2003-10-26 - Fixed memory leaks that would occur if JAM_ReadMsgHeader() failed - Added JAM_DeleteMessage() Other changes made by Johan Billing 2003-10-26 - Fixed comparison between signed and unsigned variable in JAM_AddMessage() - Improved handling of ActiveMsgs counter. JAM_AddMessage() now only increases ActiveMsgs if the added message does not have MSG_DELETED set. JAM_ChangeMsgHeader() decreases ActiveMsgs if MSG_DELETED is set and the message wasn't already deleted. JAM_DeleteMessage() now only decreases ActiveMsgs if the message wasn't already deleted. */ /*********************************************************************** ** ** Message.C -- Message handling ** ** Author: Bj”rn Stenberg (bjorn.stenberg@sth.frontec.se) ** ***********************************************************************/ #include #include #include #include #include "jam.h" #include "structrw.h" /*********************************************************************** ** ** JAM_ReadMsgHeader - Read message header ** ***********************************************************************/ int JAM_ReadMsgHeader( s_JamBase* Base_PS, uint32_t MsgNo_I, s_JamMsgHeader* Header_PS, s_JamSubPacket** SubfieldPack_PPS ) { s_JamIndex Index_S; if ( !Base_PS || !Header_PS ) return JAM_BAD_PARAM; /* find index record */ if ( fseek( Base_PS->IdxFile_PS, MsgNo_I * sizeof( s_JamIndex ), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* read index record */ if ( 1 > freadjamindex(Base_PS->IdxFile_PS,&Index_S) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* message is not there */ if(Index_S.HdrOffset == 0xffffffff && Index_S.UserCRC == 0xffffffff) { return JAM_NO_MESSAGE; } /* find header */ if ( fseek( Base_PS->HdrFile_PS, Index_S.HdrOffset, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* read header */ if ( 1 > freadjammsgheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* are Subfields requested? */ if ( SubfieldPack_PPS && Header_PS->SubfieldLen ) { s_JamSubPacket* SubPacket_PS; s_JamSubfield Subfield_S; char* Buf_PC; char* Ptr_PC; char* Roof_PC; int BufSize_I = Header_PS->SubfieldLen; Buf_PC = (void*) malloc( BufSize_I ); if ( !Buf_PC ) return JAM_NO_MEMORY; /* read all subfields */ if ( 1 > fread( Buf_PC, BufSize_I, 1, Base_PS->HdrFile_PS ) ) { Base_PS->Errno_I = errno; free (Buf_PC); return JAM_IO_ERROR; } SubPacket_PS = JAM_NewSubPacket(); if ( !SubPacket_PS ) { free (Buf_PC); return JAM_NO_MEMORY; } Roof_PC = Buf_PC + BufSize_I; /* cut out the subfields */ for ( Ptr_PC = Buf_PC; Ptr_PC < Roof_PC; Ptr_PC += Subfield_S.DatLen + SIZE_JAMSAVESUBFIELD ) { int Status_I; getjamsubfield(Ptr_PC,&Subfield_S); if((char *)Subfield_S.Buffer + Subfield_S.DatLen > Roof_PC) { JAM_DelSubPacket( SubPacket_PS ); free (Buf_PC); return JAM_CORRUPT_MSG; } Status_I = JAM_PutSubfield( SubPacket_PS, &Subfield_S ); if ( Status_I ) { JAM_DelSubPacket( SubPacket_PS ); free (Buf_PC); return Status_I; } } free( Buf_PC ); *SubfieldPack_PPS = SubPacket_PS; } else if ( SubfieldPack_PPS ) /* fields requested but none found */ /* return an empty packet */ *SubfieldPack_PPS = JAM_NewSubPacket(); return 0; } /*********************************************************************** ** ** JAM_ReadMsgText - Read message text ** ***********************************************************************/ int JAM_ReadMsgText( s_JamBase* Base_PS, uint32_t Offset_I, uint32_t Length_I, char* Buffer_PC ) { if ( !Base_PS || !Buffer_PC ) return JAM_BAD_PARAM; if ( !Length_I ) return 0; if ( fseek( Base_PS->TxtFile_PS, Offset_I, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( 1 > fread( Buffer_PC, Length_I, 1, Base_PS->TxtFile_PS ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } return 0; } /*********************************************************************** ** ** JAM_ChangeMsgHeader - Change a message header ** ***********************************************************************/ int JAM_ChangeMsgHeader( s_JamBase* Base_PS, uint32_t MsgNo_I, s_JamMsgHeader* Header_PS ) { s_JamBaseHeader BaseHeader_S; s_JamMsgHeader OldHeader_S; s_JamIndex Index_S; int Status_I; if ( !Base_PS ) return JAM_BAD_PARAM; if ( !Base_PS->Locked_I ) return JAM_NOT_LOCKED; /* read message base header */ Status_I = JAM_ReadMBHeader( Base_PS, &BaseHeader_S ); if ( Status_I ) return Status_I; /* find index record */ if ( fseek( Base_PS->IdxFile_PS, MsgNo_I * sizeof( s_JamIndex ), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* read index record */ if ( 1 > freadjamindex(Base_PS->IdxFile_PS,&Index_S) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* find header */ if ( fseek( Base_PS->HdrFile_PS, Index_S.HdrOffset, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* read old message header */ if ( 1 > freadjammsgheader( Base_PS->HdrFile_PS, &OldHeader_S ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* find header */ if ( fseek( Base_PS->HdrFile_PS, Index_S.HdrOffset, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* write header */ if ( 1 > fwritejammsgheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if( ( Header_PS->Attribute & MSG_DELETED ) && !(OldHeader_S.Attribute & MSG_DELETED) ) { /* message is deleted now but wasn't before */ BaseHeader_S.ActiveMsgs--; } Status_I = JAM_WriteMBHeader( Base_PS, &BaseHeader_S ); if ( Status_I ) return Status_I; return 0; } /*********************************************************************** ** ** JAM_AddMessage - Add a message to a message base ** ***********************************************************************/ int JAM_AddMessage( s_JamBase* Base_PS, s_JamMsgHeader* Header_PS, s_JamSubPacket* SubPack_PS, char* Text_PC, uint32_t TextLen_I ) { s_JamBaseHeader BaseHeader_S; s_JamIndex Index_S; long Offset_I; int Status_I; uint32_t TotLen_I; if ( !Base_PS ) return JAM_BAD_PARAM; if ( !Base_PS->Locked_I ) return JAM_NOT_LOCKED; /* read message base header */ Status_I = JAM_ReadMBHeader( Base_PS, &BaseHeader_S ); if ( Status_I ) return Status_I; /* ** Add text if any */ Header_PS->TxtOffset = 0; Header_PS->TxtLen = 0; if(Text_PC && TextLen_I!=0) { /* go to end of text file */ if ( fseek( Base_PS->TxtFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* store text offset (for header) */ Offset_I = ftell( Base_PS->TxtFile_PS ); if ( Offset_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Header_PS->TxtOffset = Offset_I; Header_PS->TxtLen = TextLen_I; /* write text */ if ( 1 > fwrite( Text_PC, TextLen_I, 1, Base_PS->TxtFile_PS ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } } /* ** Add header */ /* go to end of header file */ if ( fseek( Base_PS->HdrFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* calculate the size of all Subfields */ TotLen_I = 0; if ( SubPack_PS ) { s_JamSubfield* Subfield_PS; for ( Subfield_PS = JAM_GetSubfield( SubPack_PS ); Subfield_PS; Subfield_PS = JAM_GetSubfield( NULL ) ) TotLen_I += sizeof( s_JamSaveSubfield ) + Subfield_PS->DatLen; } Header_PS->SubfieldLen = TotLen_I; /* go to end of index file */ if ( fseek( Base_PS->IdxFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* find out new message number (for message header) */ Offset_I = ftell( Base_PS->IdxFile_PS ); if ( Offset_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* update header */ Header_PS->MsgNum = Offset_I / sizeof( s_JamIndex ) + BaseHeader_S.BaseMsgNum; memcpy( Header_PS->Signature, HEADERSIGNATURE, 4 ); Header_PS->Revision = CURRENTREVLEV; /* go to end of header file */ if ( fseek( Base_PS->HdrFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* find out new header offset (for index record) */ Offset_I = ftell( Base_PS->HdrFile_PS ); if ( Offset_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Index_S.HdrOffset = Offset_I; /* write new header */ if ( 1 > fwritejammsgheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* write Subfields */ if ( SubPack_PS ) { s_JamSubfield* Subfield_PS; char User_AC[101]; /* clear username */ User_AC[0] = 0; for ( Subfield_PS = JAM_GetSubfield( SubPack_PS ); Subfield_PS; Subfield_PS = JAM_GetSubfield( NULL ) ) { /* first, save Subfield header */ if ( 1 > fwritejamsavesubfield(Base_PS->HdrFile_PS,(s_JamSaveSubfield *)Subfield_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* then, save Subfield data if any*/ if(Subfield_PS->DatLen) { if ( 1 > fwrite( Subfield_PS->Buffer, Subfield_PS->DatLen, 1, Base_PS->HdrFile_PS ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } } /* store username for index file */ if ( Subfield_PS->LoID == JAMSFLD_RECVRNAME ) { memcpy( User_AC, Subfield_PS->Buffer, Subfield_PS->DatLen ); User_AC[ Subfield_PS->DatLen ] = 0; } } /* update index record */ if ( User_AC[0] ) Index_S.UserCRC = JAM_Crc32( User_AC, strlen( User_AC ) ); else Index_S.UserCRC = JAM_NO_CRC; } else /* update index record */ Index_S.UserCRC = JAM_NO_CRC; /* ** Add index */ /* write index record */ if ( 1 > fwritejamindex(Base_PS->IdxFile_PS,&Index_S) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if(!(Header_PS->Attribute & MSG_DELETED)) BaseHeader_S.ActiveMsgs++; /* Only increase ActiveMsgs if MSG_DELETED not set */ /* write message base header */ Status_I = JAM_WriteMBHeader( Base_PS, &BaseHeader_S ); if ( Status_I ) return Status_I; return 0; } /*********************************************************************** ** ** JAM_AddEmptyMessage - Add a empty message entry to a message base ** ***********************************************************************/ int JAM_AddEmptyMessage( s_JamBase* Base_PS) { s_JamIndex Index_S; if ( !Base_PS ) return JAM_BAD_PARAM; if ( !Base_PS->Locked_I ) return JAM_NOT_LOCKED; /* go to end of index file */ if ( fseek( Base_PS->IdxFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* ** Add index */ Index_S.HdrOffset = 0xffffffff; Index_S.UserCRC = 0xffffffff; /* write index record */ if ( 1 > fwritejamindex(Base_PS->IdxFile_PS,&Index_S) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } return 0; } /*********************************************************************** ** ** JAM_DeleteMessage - Delete message from messagebase ** ***********************************************************************/ int JAM_DeleteMessage( s_JamBase* Base_PS, uint32_t MsgNo_I ) { s_JamBaseHeader BaseHeader_S; s_JamMsgHeader Header_S; s_JamIndex Index_S; int Status_I; uint32_t OldAttribute_I; if ( !Base_PS ) return JAM_BAD_PARAM; if ( !Base_PS->Locked_I ) return JAM_NOT_LOCKED; /* read message base header */ Status_I = JAM_ReadMBHeader( Base_PS, &BaseHeader_S ); if ( Status_I ) return Status_I; /* find index record */ if ( fseek( Base_PS->IdxFile_PS, MsgNo_I * sizeof( s_JamIndex ), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* read index record */ if ( 1 > freadjamindex( Base_PS->IdxFile_PS, &Index_S ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* message is not there */ if(Index_S.HdrOffset == 0xffffffff && Index_S.UserCRC == 0xffffffff) { return JAM_NO_MESSAGE; } /* find header */ if ( fseek( Base_PS->HdrFile_PS, Index_S.HdrOffset, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* read header */ if ( 1 > freadjammsgheader( Base_PS->HdrFile_PS, &Header_S ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } OldAttribute_I = Header_S.Attribute; Header_S.Attribute |= MSG_DELETED; /* find header */ if ( fseek( Base_PS->HdrFile_PS, Index_S.HdrOffset, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* write header */ if ( 1 > fwritejammsgheader( Base_PS->HdrFile_PS, &Header_S ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* find index record */ if ( fseek( Base_PS->IdxFile_PS, MsgNo_I * sizeof( s_JamIndex ), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Index_S.HdrOffset = 0xffffffff; Index_S.UserCRC = 0xffffffff; /* write index record */ if ( 1 > fwritejamindex(Base_PS->IdxFile_PS,&Index_S) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if(!(OldAttribute_I & MSG_DELETED)) BaseHeader_S.ActiveMsgs--; /* decrease ActiveMsgs if the message wasn't already deleted */ /* write message base header */ Status_I = JAM_WriteMBHeader( Base_PS, &BaseHeader_S ); if ( Status_I ) return Status_I; return 0; } /*********************************************************************** ** ** JAM_Errno - Report the latest C library error code ** ***********************************************************************/ int JAM_Errno( s_JamBase* Base_PS ) { return Base_PS->Errno_I; } /*********************************************************************** ** ** JAM_ClearMsgHeader - Clear a message header ** ***********************************************************************/ int JAM_ClearMsgHeader( s_JamMsgHeader* Header_PS ) { if (!Header_PS) return JAM_BAD_PARAM; memset( Header_PS, 0, sizeof( s_JamMsgHeader ) ); memcpy( Header_PS->Signature, HEADERSIGNATURE, 4 ); Header_PS->Revision = CURRENTREVLEV; Header_PS->MsgIdCRC = JAM_NO_CRC; Header_PS->ReplyCRC = JAM_NO_CRC; Header_PS->PasswordCRC = JAM_NO_CRC; return 0; } crashmail-1.5/src/jamlib/structrw.c000066400000000000000000000211761230617144500174220ustar00rootroot00000000000000/* structrw - Platform-independent reading and writing of JAM structs Copyright (C) 1999 Johan Billing This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2003-10-22 - Added #include */ #include #include #include "jam.h" #include "structrw.h" uint16_t jamgetuword(char *buf,uint32_t offset) { return (uint16_t) buf[offset]+ buf[offset+1]*256; } void jamputuword(char *buf,uint32_t offset,uint16_t num) { buf[offset]=num%256; buf[offset+1]=num/256; } void jamputuint32_t(char *buf,uint32_t offset,uint32_t num) { buf[offset]=num%256; buf[offset+1]=(num / 256) % 256; buf[offset+2]=(num / 256 / 256) % 256; buf[offset+3]=(num / 256 / 256 / 256) % 256; } uint32_t jamgetuint32_t(char *buf,uint32_t offset) { return (uint32_t) buf[offset]+ buf[offset+1]*256+ buf[offset+2]*256*256+ buf[offset+3]*256*256*256; } int freadjambaseheader(FILE *fp,s_JamBaseHeader *s_JamBaseHeader) { char buf[SIZE_JAMBASEHEADER]; if(fread(buf,SIZE_JAMBASEHEADER,1,fp) != 1) return 0; memcpy(s_JamBaseHeader->Signature,&buf[JAMBASEHEADER_SIGNATURE],4); s_JamBaseHeader->DateCreated = jamgetuint32_t(buf,JAMBASEHEADER_DATECREATED); s_JamBaseHeader->ModCounter = jamgetuint32_t(buf,JAMBASEHEADER_MODCOUNTER); s_JamBaseHeader->ActiveMsgs = jamgetuint32_t(buf,JAMBASEHEADER_ACTIVEMSGS); s_JamBaseHeader->PasswordCRC = jamgetuint32_t(buf,JAMBASEHEADER_PASSWORDCRC); s_JamBaseHeader->BaseMsgNum = jamgetuint32_t(buf,JAMBASEHEADER_BASEMSGNUM); memcpy(s_JamBaseHeader->RSRVD,&buf[JAMBASEHEADER_RSRVD],1000); return 1; } int fwritejambaseheader(FILE *fp,s_JamBaseHeader *s_JamBaseHeader) { char buf[SIZE_JAMBASEHEADER]; memcpy(&buf[JAMBASEHEADER_SIGNATURE],s_JamBaseHeader->Signature,4); jamputuint32_t(buf,JAMBASEHEADER_DATECREATED, s_JamBaseHeader->DateCreated); jamputuint32_t(buf,JAMBASEHEADER_MODCOUNTER, s_JamBaseHeader->ModCounter); jamputuint32_t(buf,JAMBASEHEADER_ACTIVEMSGS, s_JamBaseHeader->ActiveMsgs); jamputuint32_t(buf,JAMBASEHEADER_PASSWORDCRC, s_JamBaseHeader->PasswordCRC ); jamputuint32_t(buf,JAMBASEHEADER_BASEMSGNUM, s_JamBaseHeader->BaseMsgNum); memcpy(&buf[JAMBASEHEADER_RSRVD],s_JamBaseHeader->RSRVD,1000); if(fwrite(buf,SIZE_JAMBASEHEADER,1,fp) != 1) return 0; return 1; } int freadjammsgheader(FILE *fp,s_JamMsgHeader *s_JamMsgHeader) { char buf[SIZE_JAMMSGHEADER]; if(fread(buf,SIZE_JAMMSGHEADER,1,fp) != 1) return 0; memcpy(s_JamMsgHeader->Signature,&buf[JAMMSGHEADER_SIGNATURE],4); s_JamMsgHeader->Revision = jamgetuword(buf,JAMMSGHEADER_REVISION); s_JamMsgHeader->ReservedWord = jamgetuword(buf,JAMMSGHEADER_RESERVEDWORD); s_JamMsgHeader->SubfieldLen = jamgetuint32_t(buf,JAMMSGHEADER_SUBFIELDLEN); s_JamMsgHeader->TimesRead = jamgetuint32_t(buf,JAMMSGHEADER_TIMESREAD); s_JamMsgHeader->MsgIdCRC = jamgetuint32_t(buf,JAMMSGHEADER_MSGIDCRC); s_JamMsgHeader->ReplyCRC = jamgetuint32_t(buf,JAMMSGHEADER_REPLYCRC); s_JamMsgHeader->ReplyTo = jamgetuint32_t(buf,JAMMSGHEADER_REPLYTO); s_JamMsgHeader->Reply1st = jamgetuint32_t(buf,JAMMSGHEADER_REPLY1ST); s_JamMsgHeader->ReplyNext = jamgetuint32_t(buf,JAMMSGHEADER_REPLYNEXT); s_JamMsgHeader->DateWritten = jamgetuint32_t(buf,JAMMSGHEADER_DATEWRITTEN); s_JamMsgHeader->DateReceived = jamgetuint32_t(buf,JAMMSGHEADER_DATERECEIVED); s_JamMsgHeader->DateProcessed = jamgetuint32_t(buf,JAMMSGHEADER_DATEPROCESSED); s_JamMsgHeader->MsgNum = jamgetuint32_t(buf,JAMMSGHEADER_MSGNUM); s_JamMsgHeader->Attribute = jamgetuint32_t(buf,JAMMSGHEADER_ATTRIBUTE); s_JamMsgHeader->Attribute2 = jamgetuint32_t(buf,JAMMSGHEADER_ATTRIBUTE2); s_JamMsgHeader->TxtOffset = jamgetuint32_t(buf,JAMMSGHEADER_TXTOFFSET); s_JamMsgHeader->TxtLen = jamgetuint32_t(buf,JAMMSGHEADER_TXTLEN); s_JamMsgHeader->PasswordCRC = jamgetuint32_t(buf,JAMMSGHEADER_PASSWORDCRC); s_JamMsgHeader->Cost = jamgetuint32_t(buf,JAMMSGHEADER_COST); return 1; } int fwritejammsgheader(FILE *fp,s_JamMsgHeader *s_JamMsgHeader) { char buf[SIZE_JAMMSGHEADER]; memcpy(&buf[JAMMSGHEADER_SIGNATURE],s_JamMsgHeader->Signature,4); jamputuword(buf,JAMMSGHEADER_REVISION, s_JamMsgHeader->Revision); jamputuword(buf,JAMMSGHEADER_RESERVEDWORD, s_JamMsgHeader->ReservedWord); jamputuint32_t(buf,JAMMSGHEADER_SUBFIELDLEN, s_JamMsgHeader->SubfieldLen); jamputuint32_t(buf,JAMMSGHEADER_TIMESREAD, s_JamMsgHeader->TimesRead); jamputuint32_t(buf,JAMMSGHEADER_MSGIDCRC, s_JamMsgHeader->MsgIdCRC); jamputuint32_t(buf,JAMMSGHEADER_REPLYCRC, s_JamMsgHeader->ReplyCRC ); jamputuint32_t(buf,JAMMSGHEADER_REPLYTO, s_JamMsgHeader->ReplyTo); jamputuint32_t(buf,JAMMSGHEADER_REPLY1ST, s_JamMsgHeader->Reply1st); jamputuint32_t(buf,JAMMSGHEADER_REPLYNEXT, s_JamMsgHeader->ReplyNext); jamputuint32_t(buf,JAMMSGHEADER_DATEWRITTEN, s_JamMsgHeader->DateWritten); jamputuint32_t(buf,JAMMSGHEADER_DATERECEIVED, s_JamMsgHeader->DateReceived ); jamputuint32_t(buf,JAMMSGHEADER_DATEPROCESSED, s_JamMsgHeader->DateProcessed); jamputuint32_t(buf,JAMMSGHEADER_MSGNUM, s_JamMsgHeader->MsgNum); jamputuint32_t(buf,JAMMSGHEADER_ATTRIBUTE, s_JamMsgHeader->Attribute); jamputuint32_t(buf,JAMMSGHEADER_ATTRIBUTE2, s_JamMsgHeader->Attribute2); jamputuint32_t(buf,JAMMSGHEADER_TXTOFFSET, s_JamMsgHeader->TxtOffset); jamputuint32_t(buf,JAMMSGHEADER_TXTLEN, s_JamMsgHeader->TxtLen); jamputuint32_t(buf,JAMMSGHEADER_PASSWORDCRC, s_JamMsgHeader->PasswordCRC); jamputuint32_t(buf,JAMMSGHEADER_COST, s_JamMsgHeader->Cost); if(fwrite(buf,SIZE_JAMMSGHEADER,1,fp) != 1) return 0; return 1; } int freadjamindex(FILE *fp,s_JamIndex *s_JamIndex) { char buf[SIZE_JAMINDEX]; if(fread(buf,SIZE_JAMINDEX,1,fp) != 1) return 0; s_JamIndex->UserCRC = jamgetuint32_t(buf,JAMINDEX_USERCRC); s_JamIndex->HdrOffset = jamgetuint32_t(buf,JAMINDEX_HDROFFSET); return 1; } int fwritejamindex(FILE *fp,s_JamIndex *s_JamIndex) { char buf[SIZE_JAMINDEX]; jamputuint32_t(buf,JAMINDEX_USERCRC, s_JamIndex->UserCRC); jamputuint32_t(buf,JAMINDEX_HDROFFSET, s_JamIndex->HdrOffset); if(fwrite(buf,SIZE_JAMINDEX,1,fp) != 1) return 0; return 1; } int freadjamlastread(FILE *fp,s_JamLastRead *s_JamLastRead) { char buf[SIZE_JAMLASTREAD]; if(fread(buf,SIZE_JAMLASTREAD,1,fp) != 1) return 0; s_JamLastRead->UserCRC = jamgetuint32_t(buf,JAMLASTREAD_USERCRC); s_JamLastRead->UserID = jamgetuint32_t(buf,JAMLASTREAD_USERID); s_JamLastRead->LastReadMsg = jamgetuint32_t(buf,JAMLASTREAD_LASTREADMSG); s_JamLastRead->HighReadMsg = jamgetuint32_t(buf,JAMLASTREAD_HIGHREADMSG); return 1; } int fwritejamlastread(FILE *fp,s_JamLastRead *s_JamLastRead) { char buf[SIZE_JAMLASTREAD]; jamputuint32_t(buf,JAMLASTREAD_USERCRC,s_JamLastRead->UserCRC); jamputuint32_t(buf,JAMLASTREAD_USERID,s_JamLastRead->UserID); jamputuint32_t(buf,JAMLASTREAD_LASTREADMSG,s_JamLastRead->LastReadMsg); jamputuint32_t(buf,JAMLASTREAD_HIGHREADMSG,s_JamLastRead->HighReadMsg); if(fwrite(buf,SIZE_JAMLASTREAD,1,fp) != 1) return 0; return 1; } int fwritejamsavesubfield(FILE *fp,s_JamSaveSubfield *s_JamSaveSubfield) { char buf[SIZE_JAMLASTREAD]; jamputuword(buf,JAMSAVESUBFIELD_LOID, s_JamSaveSubfield->LoID); jamputuword(buf,JAMSAVESUBFIELD_HIID, s_JamSaveSubfield->HiID); jamputuint32_t(buf,JAMSAVESUBFIELD_DATLEN, s_JamSaveSubfield->DatLen); if(fwrite(buf,SIZE_JAMSAVESUBFIELD,1,fp) != 1) return 0; return 1; } void getjamsubfield(char *buf,s_JamSubfield *Subfield_S) { Subfield_S->LoID = jamgetuword(buf,JAMSAVESUBFIELD_LOID); Subfield_S->HiID = jamgetuword(buf,JAMSAVESUBFIELD_HIID); Subfield_S->DatLen = jamgetuint32_t(buf,JAMSAVESUBFIELD_DATLEN); Subfield_S->Buffer = (char *) buf + SIZE_JAMSAVESUBFIELD; } crashmail-1.5/src/jamlib/structrw.h000066400000000000000000000057021230617144500174240ustar00rootroot00000000000000/* structrw - Platform-independent reading and writing of JAM structs Copyright (C) 1999 Johan Billing This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define JAMBASEHEADER_SIGNATURE 0 #define JAMBASEHEADER_DATECREATED 4 #define JAMBASEHEADER_MODCOUNTER 8 #define JAMBASEHEADER_ACTIVEMSGS 12 #define JAMBASEHEADER_PASSWORDCRC 16 #define JAMBASEHEADER_BASEMSGNUM 20 #define JAMBASEHEADER_RSRVD 24 #define SIZE_JAMBASEHEADER 1024 #define JAMMSGHEADER_SIGNATURE 0 #define JAMMSGHEADER_REVISION 4 #define JAMMSGHEADER_RESERVEDWORD 6 #define JAMMSGHEADER_SUBFIELDLEN 8 #define JAMMSGHEADER_TIMESREAD 12 #define JAMMSGHEADER_MSGIDCRC 16 #define JAMMSGHEADER_REPLYCRC 20 #define JAMMSGHEADER_REPLYTO 24 #define JAMMSGHEADER_REPLY1ST 28 #define JAMMSGHEADER_REPLYNEXT 32 #define JAMMSGHEADER_DATEWRITTEN 36 #define JAMMSGHEADER_DATERECEIVED 40 #define JAMMSGHEADER_DATEPROCESSED 44 #define JAMMSGHEADER_MSGNUM 48 #define JAMMSGHEADER_ATTRIBUTE 52 #define JAMMSGHEADER_ATTRIBUTE2 56 #define JAMMSGHEADER_TXTOFFSET 60 #define JAMMSGHEADER_TXTLEN 64 #define JAMMSGHEADER_PASSWORDCRC 68 #define JAMMSGHEADER_COST 72 #define SIZE_JAMMSGHEADER 76 #define JAMINDEX_USERCRC 0 #define JAMINDEX_HDROFFSET 4 #define SIZE_JAMINDEX 8 #define JAMLASTREAD_USERCRC 0 #define JAMLASTREAD_USERID 4 #define JAMLASTREAD_LASTREADMSG 8 #define JAMLASTREAD_HIGHREADMSG 12 #define SIZE_JAMLASTREAD 16 #define JAMSAVESUBFIELD_LOID 0 #define JAMSAVESUBFIELD_HIID 2 #define JAMSAVESUBFIELD_DATLEN 4 #define SIZE_JAMSAVESUBFIELD 8 int freadjambaseheader(FILE *fp,s_JamBaseHeader *s_JamBaseHeader); int fwritejambaseheader(FILE *fp,s_JamBaseHeader *s_JamBaseHeader); int freadjammsgheader(FILE *fp,s_JamMsgHeader *s_JamMsgHeader); int fwritejammsgheader(FILE *fp,s_JamMsgHeader *s_JamMsgHeader); int freadjamindex(FILE *fp,s_JamIndex *s_JamIndex); int fwritejamindex(FILE *fp,s_JamIndex *s_JamIndex); int freadjamlastread(FILE *fp,s_JamLastRead *s_JamLastRead); int fwritejamlastread(FILE *fp,s_JamLastRead *s_JamLastRead); int fwritejamsavesubfield(FILE *fp,s_JamSaveSubfield *s_JamSaveSubfield); void getjamsubfield(char *buf,s_JamSubfield *Subfield_S); crashmail-1.5/src/jamlib/subpack.c000066400000000000000000000130661230617144500171540ustar00rootroot00000000000000/* JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2000-04-16: - Fixed broken JAM_GetSubfield() - #includes stdlib.h instead of malloc.h and memory.h Changes made by Johan Billing 2000-09-17: - Added JAM_GetSubfield_R() Backported changes from JAMLIB 1.4.7 made by Johan Billing 2003-10-22 - JAM_NewSubPacket() and JAM_PutSubField() would give memory leaks under low memory conditions. Fixed. Other changes made by Johan Billing 2003-10-22 - Fixed comparison between signed and unsigned variable in JAM_DelSubPacket() and JAM_GetSubField() */ /*********************************************************************** ** ** SUBPACKET.C -- Subfield packet handling ** ** Author: Bj”rn Stenberg (bjorn.stenberg@sth.frontec.se) ** ***********************************************************************/ #include #include #include #include #include "jam.h" /*********************************************************************** ** ** JAM_NewSubPacket - Create a new subfield packet ** ***********************************************************************/ s_JamSubPacket* JAM_NewSubPacket( void ) { s_JamSubPacket* Sub_PS; /* allocate packet struct */ Sub_PS = (s_JamSubPacket*) malloc( sizeof( s_JamSubPacket ) ); if ( !Sub_PS ) return NULL; Sub_PS->NumAlloc = 20; Sub_PS->NumFields = 0; /* allocate pointer array */ Sub_PS->Fields = (s_JamSubfield**) calloc( Sub_PS->NumAlloc, sizeof( s_JamSubfield* ) ); if ( !Sub_PS->Fields ) { free (Sub_PS); return NULL; } return Sub_PS; } /*********************************************************************** ** ** JAM_DelSubPacket - Free the data associated with a subfield packet ** ***********************************************************************/ int JAM_DelSubPacket( s_JamSubPacket* SubPack_PS ) { uint32_t i; if (!SubPack_PS) return JAM_BAD_PARAM; for ( i=0; i < SubPack_PS->NumFields; i++ ) { s_JamSubfield* Field_PS = SubPack_PS->Fields[i]; if ( Field_PS->Buffer ) free( Field_PS->Buffer ); free( Field_PS ); } free( SubPack_PS->Fields ); free( SubPack_PS ); return 0; } /*********************************************************************** ** ** JAM_GetSubfield -- Get first/next subfield from a subfield packet ** (not reentrant) ** ***********************************************************************/ s_JamSubfield* JAM_GetSubfield( s_JamSubPacket* SubPack_PS ) { static s_JamSubPacket* LastPack_PS = NULL; static uint32_t NextIndex_I = 0; if ( SubPack_PS ) { LastPack_PS = SubPack_PS; NextIndex_I = 0; } if ( NextIndex_I < LastPack_PS->NumFields ) return LastPack_PS->Fields[ NextIndex_I++ ]; return NULL; } /*********************************************************************** ** ** JAM_GetSubfield_R -- Get first/next subfield from a subfield packet ** (reentrant) ** ***********************************************************************/ s_JamSubfield* JAM_GetSubfield_R( s_JamSubPacket* SubPack_PS , uint32_t* Count_PI) { if ( *Count_PI < SubPack_PS->NumFields ) return SubPack_PS->Fields[ (*Count_PI)++ ]; return NULL; } /*********************************************************************** ** ** JAM_PutSubfield -- Add a subfield to a subfield packet ** ***********************************************************************/ int JAM_PutSubfield( s_JamSubPacket* SubPack_PS, s_JamSubfield* Field_PS ) { s_JamSubfield* NewField_PS; char* NewBuf_PC; /* do we have to expand the array? */ if ( SubPack_PS->NumFields == SubPack_PS->NumAlloc ) { s_JamSubfield** Fields_PPS; SubPack_PS->NumAlloc *= 2; Fields_PPS = (s_JamSubfield**) realloc( SubPack_PS->Fields, SubPack_PS->NumAlloc * sizeof( s_JamSubfield* ) ); if ( !Fields_PPS ) return JAM_NO_MEMORY; SubPack_PS->Fields=Fields_PPS; } /* ** Copy the passed subfield */ /* allocate a new subfield */ NewField_PS = (s_JamSubfield*) malloc( sizeof( s_JamSubfield ) ); if ( !NewField_PS ) return JAM_NO_MEMORY; /* allocate a new buffer */ if ( Field_PS->DatLen ) { NewBuf_PC = (char*) malloc( Field_PS->DatLen ); if ( !NewBuf_PC ) { free (NewField_PS); return JAM_NO_MEMORY; } memcpy( NewBuf_PC, Field_PS->Buffer, Field_PS->DatLen ); } else NewBuf_PC = NULL; /* copy field struct */ NewField_PS->LoID = Field_PS->LoID; NewField_PS->HiID = Field_PS->HiID; NewField_PS->DatLen = Field_PS->DatLen; NewField_PS->Buffer = NewBuf_PC; /* ** Update subfield packet */ SubPack_PS->Fields[ SubPack_PS->NumFields ] = NewField_PS; SubPack_PS->NumFields++; return 0; } crashmail-1.5/src/oslib/000077500000000000000000000000001230617144500152245ustar00rootroot00000000000000crashmail-1.5/src/oslib/os.h000066400000000000000000000005201230617144500160130ustar00rootroot00000000000000#ifndef OS_OS_H #define OS_OS_H #include bool osInit(void); void osEnd(void); #if defined(PLATFORM_WIN32) #include #elif defined(PLATFORM_LINUX) #include #elif defined(PLATFORM_OS2) #include #else #error Unsupported platform #endif #endif crashmail-1.5/src/oslib/osdir.h000066400000000000000000000006021230617144500165130ustar00rootroot00000000000000#ifndef OS_OSDIR_H #define OS_OSDIR_H #include "shared/types.h" #include struct osFileEntry { struct osFileEntry *Next; char Name[100]; time_t Date; uint32_t Size; }; bool osReadDir(char *dir,struct jbList *filelist,bool (*acceptfunc)(char *filename)); bool osScanDir(char *dir,void (*func)(char *file)); struct osFileEntry *osGetFileEntry(char *file); #endif crashmail-1.5/src/oslib/osfile.h000066400000000000000000000015411230617144500166570ustar00rootroot00000000000000#ifndef OS_OSFILE_H #define OS_OSFILE_H #include #include "shared/types.h" typedef void *osFile; #define MODE_OLDFILE 1005 /* Corresponds to "rb" with fopen */ #define MODE_NEWFILE 1006 /* Corresponds to "wb" with fopen */ #define MODE_READWRITE 1004 /* Corresponds to "w+b" with fopen */ #define OFFSET_BEGINNING -1 #define OFFSET_END 1 osFile osOpen(char *name,uint32_t mode); void osClose(osFile os); int osGetChar(osFile os); uint32_t osRead(osFile os,void *buf,uint32_t bytes); uint32_t osFGets(osFile os,char *str,uint32_t max); off_t osFTell(osFile os); bool osPutChar(osFile os, char ch); bool osWrite(osFile os,const void *buf, uint32_t bytes); bool osPuts(osFile os,char *str); bool osFPrintf(osFile os,char *fmt,...); bool osVFPrintf(osFile os,char *fmt,va_list args); void osSeek(osFile os,off_t offset,short mode); #endif crashmail-1.5/src/oslib/osmem.h000066400000000000000000000002451230617144500165160ustar00rootroot00000000000000#ifndef OS_OSMEM_H #define OS_OSMEM_H #include "shared/types.h" void *osAlloc(uint32_t size); void *osAllocCleared(uint32_t size); void osFree(void *buf); #endif crashmail-1.5/src/oslib/osmisc.h000066400000000000000000000006161230617144500166750ustar00rootroot00000000000000#ifndef OS_OSMISC_H #define OS_OSMISC_H #include "shared/types.h" void osSetComment(char *file,char *comment); bool osExists(char *file); int osChDirExecute(char *dir,char *cmd); int osExecute(char *cmd); bool osRename(char *oldfile,char *newfile); bool osDelete(char *file); bool osMkDir(char *dir); void osSleep(int secs); uint32_t osError(void); char *osErrorMsg(uint32_t errnum); #endif crashmail-1.5/src/oslib/ospattern.h000066400000000000000000000003051230617144500174120ustar00rootroot00000000000000#ifndef OS_OSPATTERN_H #define OS_OSPATTERN_H #include "shared/types.h" bool osCheckPattern(char *pattern); bool osMatchPattern(char *pattern,char *str); bool osIsPattern(char *pattern); #endif crashmail-1.5/src/oslib_linux/000077500000000000000000000000001230617144500164435ustar00rootroot00000000000000crashmail-1.5/src/oslib_linux/Makefile000066400000000000000000000010461230617144500201040ustar00rootroot00000000000000INCDIR = ../ CC = gcc $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -I $(INCDIR) -DPLATFORM_LINUX AR = ar -ru RM = rm -f OBJS = osfile.o osdir.o osmisc.o osmem.o ospattern.o os.o oslib.a : $(OBJS) $(AR) oslib.a $(OBJS) # os osfile.o : osfile.c $(CC) -c osfile.c -o osfile.o osmisc.o : osmisc.c $(CC) -c osmisc.c -o osmisc.o osdir.o : osdir.c $(CC) -c osdir.c -o osdir.o osmem.o : osmem.c $(CC) -c osmem.c -o osmem.o ospattern.o : ospattern.c $(CC) -c ospattern.c -o ospattern.o os.o : os.c $(CC) -c os.c -o os.o clean : $(RM) *.o *.a crashmail-1.5/src/oslib_linux/os.c000066400000000000000000000001511230617144500172250ustar00rootroot00000000000000#include #include bool osInit(void) { return(TRUE); } void osEnd(void) { } crashmail-1.5/src/oslib_linux/os_linux.h000066400000000000000000000013041230617144500204520ustar00rootroot00000000000000#include #include typedef uint16_t UINT16; /* Unsigned 16-bit integer */ #define OS_EXIT_ERROR 10 #define OS_EXIT_OK 0 #define OS_PLATFORM_NAME "Linux" #define OS_PATH_CHARS "/" #define OS_CURRENT_DIR "." #define OS_CONFIG_NAME "crashmail.prefs" #define OS_CONFIG_VAR "CMCONFIGFILE" #define OS_HAS_SYSLOG /* OS_PATH_CHARS is used by MakeFullPath. If path doesn't end with one of these characters, the first character will be appended to it. Example: OS_PATH_CHARS = "/:" "inbound" + "file" --> "inbound/file" "inbound/" + "file" --> "inbound/file" "inbound:" + "file" --> "inbound/file" */ #define stricmp strcasecmp #define strnicmp strncasecmp crashmail-1.5/src/oslib_linux/osdir.c000066400000000000000000000036151230617144500177340ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include bool osReadDir(char *dirname,struct jbList *filelist,bool (*acceptfunc)(char *filename)) { DIR *dir; struct dirent *dirent; struct osFileEntry *tmp; char buf[200]; jbNewList(filelist); if(!(dir=opendir(dirname))) return(FALSE); while((dirent=readdir(dir))) { bool add; if(!acceptfunc) add=TRUE; else add=(*acceptfunc)(dirent->d_name); if(add) { struct stat st; MakeFullPath(dirname,dirent->d_name,buf,200); if(stat(buf,&st) == 0) { if(!(tmp=(struct osFileEntry *)osAllocCleared(sizeof(struct osFileEntry)))) { jbFreeList(filelist); closedir(dir); return(FALSE); } mystrncpy(tmp->Name,dirent->d_name,100); tmp->Size=st.st_size; tmp->Date=st.st_mtime; jbAddNode(filelist,(struct jbNode *)tmp); } } } closedir(dir); return(TRUE); } bool osScanDir(char *dirname,void (*func)(char *file)) { DIR *dir; struct dirent *dirent; if(!(dir=opendir(dirname))) return(FALSE); while((dirent=readdir(dir))) (*func)(dirent->d_name); closedir(dir); return(TRUE); } struct osFileEntry *osGetFileEntry(char *file) { struct stat st; struct osFileEntry *tmp; if(stat(file,&st) != 0) return(FALSE); if(!(tmp=(struct osFileEntry *)osAllocCleared(sizeof(struct osFileEntry)))) return(FALSE); mystrncpy(tmp->Name,GetFilePart(file),100); tmp->Size=st.st_size; tmp->Date=st.st_mtime; return(tmp); } crashmail-1.5/src/oslib_linux/osfile.c000066400000000000000000000036631230617144500201000ustar00rootroot00000000000000#include #include #include #include #include #include #include osFile osOpen(char *name,uint32_t mode) { FILE *fh; if(mode == MODE_NEWFILE) { fh=fopen(name,"wb"); } else if(mode == MODE_OLDFILE) { fh=fopen(name,"rb"); } else { if(!(fh=fopen(name,"r+b"))) fh=fopen(name,"w+b"); } return (osFile) fh; } void osClose(osFile os) { fclose((FILE *)os); } int osGetChar(osFile os) { int c; c=fgetc((FILE *)os); if(c==EOF) c=-1; return(c); } uint32_t osRead(osFile os,void *buf,uint32_t bytes) { return fread(buf,1,bytes,(FILE *)os); } bool osPutChar(osFile os, char ch) { if(fputc(ch,(FILE *)os)==EOF) return(FALSE); return(TRUE); } bool osWrite(osFile os,const void *buf,uint32_t bytes) { if(fwrite(buf,1,bytes,(FILE *)os)!=bytes) return(FALSE); return(TRUE); } bool osPuts(osFile os,char *str) { if(fputs(str,(FILE *)os)==EOF) return(FALSE); return(TRUE); } uint32_t osFGets(osFile os,char *str,uint32_t max) { char *s; s=fgets(str,max,(FILE *)os); if(s) { if(strlen(s)>=2 && s[strlen(s)-1]==10 && s[strlen(s)-2]==13) { /* CRLF -> LF */ s[strlen(s)-2]=10; s[strlen(s)-1]=0; } return (uint32_t)strlen(s); } return(0); } off_t osFTell(osFile os) { return ftello((FILE *)os); } bool osFPrintf(osFile os,char *fmt,...) { va_list args; int res; va_start(args, fmt); res=vfprintf(os,fmt,args); va_end(args); if(!res) return(FALSE); return(TRUE); } bool osVFPrintf(osFile os,char *fmt,va_list args) { int res; res=vfprintf(os,fmt,args); if(!res) return(FALSE); return(TRUE); } void osSeek(osFile fh,off_t offset,short mode) { int md; if(mode == OFFSET_BEGINNING) md=SEEK_SET; if(mode == OFFSET_END) md=SEEK_END; fseeko((FILE *)fh,offset,md); } crashmail-1.5/src/oslib_linux/osmem.c000066400000000000000000000003711230617144500177300ustar00rootroot00000000000000#include #include #include void *osAlloc(uint32_t size) { return malloc((size_t)size); } void *osAllocCleared(uint32_t size) { return calloc((size_t)size,1); } void osFree(void *buf) { free(buf); } crashmail-1.5/src/oslib_linux/osmisc.c000066400000000000000000000024531230617144500201100ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include void osSetComment(char *file,char *comment) { /* Does not exist in this os */ } /* Returns -1 if dir was not found and errorlevel otherwise */ int osChDirExecute(char *dir,char *cmd) { char olddir[300]; int res; if(!getcwd(olddir,300)) return(-1); if(chdir(dir) != 0) return(-1); res=osExecute(cmd); chdir(olddir); return(res); } int osExecute(char *cmd) { int res; res=system(cmd); return WEXITSTATUS(res); } bool osExists(char *file) { struct stat st; if(stat(file,&st) == 0) return(TRUE); return(FALSE); } bool osMkDir(char *dir) { if(mkdir(dir,0777) != 0) return(FALSE); return(TRUE); } bool osRename(char *oldfile,char *newfile) { if(rename(oldfile,newfile) == 0) return(TRUE); return(FALSE); } bool osDelete(char *file) { if(remove(file) == 0) return(TRUE); return(FALSE); } void osSleep(int secs) { sleep(secs); } char *osErrorMsg(uint32_t errnum) { return (char *)strerror(errnum); } uint32_t osError(void) { return (uint32_t)errno; } crashmail-1.5/src/oslib_linux/ospattern.c000066400000000000000000000011321230617144500206230ustar00rootroot00000000000000#include #include #include #include bool osCheckPattern(char *pattern) { return(TRUE); } bool osMatchPattern(char *pattern,char *str) { int c; for(c=0;pattern[c];c++) { if(pattern[c]=='*') return(TRUE); if(str[c] == 0) return(FALSE); if(pattern[c]!='?' && tolower(pattern[c])!=tolower(str[c])) return(FALSE); } if(str[c]!=0) return(FALSE); return(TRUE); } bool osIsPattern(char *pat) { if(strchr(pat,'?') || strchr(pat,'*')) return(TRUE); return(FALSE); } crashmail-1.5/src/oslib_os2/000077500000000000000000000000001230617144500160075ustar00rootroot00000000000000crashmail-1.5/src/oslib_os2/Makefile000066400000000000000000000007751230617144500174600ustar00rootroot00000000000000INCDIR = ../ CC = gcc -I $(INCDIR) -DPLATFORM_OS2 -Wall AR = ar -ru RM = del OBJS = osfile.o osdir.o osmisc.o osmem.o ospattern.o os.o oslib.a : $(OBJS) $(AR) oslib.a $(OBJS) # os osfile.o: osfile.c $(CC) -c osfile.c -o osfile.o osmisc.o : osmisc.c $(CC) -c osmisc.c -o osmisc.o osdir.o : osdir.c $(CC) -c osdir.c -o osdir.o osmem.o : osmem.c $(CC) -c osmem.c -o osmem.o ospattern.o : ospattern.c $(CC) -c ospattern.c -o ospattern.o os.o: os.c $(CC) -c os.c -o os.o clean: $(RM) *.o *.a crashmail-1.5/src/oslib_os2/os.c000066400000000000000000000001511230617144500165710ustar00rootroot00000000000000#include #include bool osInit(void) { return(TRUE); } void osEnd(void) { } crashmail-1.5/src/oslib_os2/os_os2.h000066400000000000000000000012461230617144500173670ustar00rootroot00000000000000#include #include #include bool osInit(void); void osEnd(void); typedef unsigned short UINT16; /* Unsigned 16-bit integer */ #define OS_EXIT_ERROR 10 #define OS_EXIT_OK 0 #define OS_PLATFORM_NAME "2" #define OS_CURRENT_DIR "." #define OS_PATH_CHARS "\\" #define OS_CONFIG_NAME "crashmail.prefs" #define OS_CONFIG_VAR "CMCONFIGFILE" /* Used by MakeFullPath. If path doesn't end with one of these characters, the first character will be appended to it. Example: OS_PATH_CHARS = "/:" "inbound" + "file" --> "inbound/file" "inbound/" + "file" --> "inbound/file" "inbound:" + "file" --> "inbound/file" */ crashmail-1.5/src/oslib_os2/osdir.c000066400000000000000000000036331230617144500173000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include bool osReadDir(char *dirname,struct jbList *filelist,bool (*acceptfunc)(char *filename)) { DIR *dir; struct dirent *dirent; struct osFileEntry *tmp; char buf[200]; jbNewList(filelist); if(!(dir=opendir(dirname))) return(FALSE); while((dirent=readdir(dir))) { bool add; if(!acceptfunc) add=TRUE; else add=(*acceptfunc)(dirent->d_name); if(add) { struct stat st; MakeFullPath(dirname,dirent->d_name,buf,200); if(stat(buf,&st) == 0) { if(!(tmp=(struct osFileEntry *)osAllocCleared(sizeof(struct osFileEntry)))) { jbFreeList(filelist); closedir(dir); return(FALSE); } mystrncpy(tmp->Name,dirent->d_name,100); tmp->Size=st.st_size; tmp->Date=st.st_mtime; jbAddNode(filelist,(struct jbNode *)tmp); } } } closedir(dir); return(TRUE); } bool osScanDir(char *dirname,void (*func)(char *file)) { DIR *dir; struct dirent *dirent; if(!(dir=opendir(dirname))) return(FALSE); while((dirent=readdir(dir))) (*func)(dirent->d_name); closedir(dir); return(TRUE); } struct osFileEntry *osGetFileEntry(char *file) { struct stat st; struct osFileEntry *tmp; if(stat(file,&st) != 0) return(FALSE); if(!(tmp=(struct osFileEntry *)osAllocCleared(sizeof(struct osFileEntry)))) return(FALSE); mystrncpy(tmp->Name,GetFilePart(file),100); tmp->Size=st.st_size; tmp->Date=st.st_mtime; return(tmp); } crashmail-1.5/src/oslib_os2/osfile.c000066400000000000000000000036101230617144500174340ustar00rootroot00000000000000#include #include #include #include #include #include osFile osOpen(char *name,ulong mode) { FILE *fh; if(mode == MODE_NEWFILE) { fh=fopen(name,"wb"); } else if(mode == MODE_OLDFILE) { fh=fopen(name,"rb"); } else { if(!(fh=fopen(name,"r+b"))) fh=fopen(name,"w+b"); } return (osFile) fh; } void osClose(osFile os) { fclose((FILE *)os); } int osGetChar(osFile os) { int c; c=fgetc((FILE *)os); if(c==EOF) c=-1; return(c); } ulong osRead(osFile os,void *buf,ulong bytes) { return fread(buf,1,bytes,(FILE *)os); } bool osPutChar(osFile os, char ch) { if(fputc(ch,(FILE *)os)==EOF) return(FALSE); return(TRUE); } bool osWrite(osFile os,const void *buf,ulong bytes) { if(fwrite(buf,1,bytes,(FILE *)os)!=bytes) return(FALSE); return(TRUE); } bool osPuts(osFile os,char *str) { if(fputs(str,(FILE *)os)==EOF) return(FALSE); return(TRUE); } ulong osFGets(osFile os,char *str,ulong max) { char *s; s=fgets(str,max,(FILE *)os); if(s) { if(strlen(s)>=2 && s[strlen(s)-1]==10 && s[strlen(s)-2]==13) { /* CRLF -> LF */ s[strlen(s)-2]=10; s[strlen(s)-1]=0; } return (ulong)strlen(s); } return(0); } ulong osFTell(osFile os) { return ftell((FILE *)os); } bool osFPrintf(osFile os,char *fmt,...) { va_list args; int res; va_start(args, fmt); res=vfprintf(os,fmt,args); va_end(args); if(!res) return(FALSE); return(TRUE); } bool osVFPrintf(osFile os,char *fmt,va_list args) { int res; res=vfprintf(os,fmt,args); if(!res) return(FALSE); return(TRUE); } void osSeek(osFile fh,ulong offset,short mode) { int md; if(mode == OFFSET_BEGINNING) md=SEEK_SET; if(mode == OFFSET_END) md=SEEK_END; fseek((FILE *)fh,offset,md); } crashmail-1.5/src/oslib_os2/osmem.c000066400000000000000000000003371230617144500172760ustar00rootroot00000000000000#include #include void *osAlloc(ulong size) { return malloc((size_t)size); } void *osAllocCleared(ulong size) { return calloc((size_t)size,1); } void osFree(void *buf) { free(buf); } crashmail-1.5/src/oslib_os2/osmisc.c000066400000000000000000000035361230617144500174570ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include /*#include */ #include #include #include #include void osSetComment(char *file,char *comment) { /* Modeled after * eatool.c (emx+gcc) -- Copyright (c) 1992-1995 by Eberhard Mattes * by Peter Karlsson 1999 */ char *buf; _ead ead; int size; ead = _ead_create(); if (!ead) return; size = strlen(comment); buf = malloc(size + 4); if (buf != NULL) { ((USHORT *)buf)[0] = EAT_ASCII; ((USHORT *)buf)[1] = size; memcpy(buf+4, comment, size); if (_ead_add(ead, ".SUBJECT", 0, buf, size + 4) >= 0) { _ead_write(ead, file, 0, _EAD_MERGE); } } free(buf); _ead_destroy(ead); } /* Returns -1 if dir was not found and errorlevel otherwise */ int osChDirExecute(char *dir,char *cmd) { char olddir[300]; int res; if(!getcwd(olddir,300)) return(-1); if(chdir(dir) != 0) return(-1); res=system(cmd); chdir(olddir); return(res); } int osExecute(char *cmd) { return system(cmd); } bool osExists(char *file) { struct stat st; if(stat(file,&st) == 0) return(TRUE); return(FALSE); } bool osMkDir(char *dir) { if(mkdir(dir, 0) != 0) return(FALSE); return(TRUE); } bool osRename(char *oldfile,char *newfile) { if(rename(oldfile,newfile) == 0) return(TRUE); return(FALSE); } bool osDelete(char *file) { if(remove(file) == 0) return(TRUE); return(FALSE); } void osSleep(int secs) { sleep(secs); } char *osErrorMsg(ulong errnum) { return (char *)strerror(errnum); } ulong osError(void) { return (ulong)errno; } crashmail-1.5/src/oslib_os2/ospattern.c000066400000000000000000000011351230617144500201720ustar00rootroot00000000000000#include #include #include #include bool osCheckPattern(char *pattern) { return(TRUE); } bool osMatchPattern(char *pattern,char *str) { int c; for(c=0;pattern[c];c++) { if(pattern[c]=='*') return(TRUE); if(str[c] == 0) return(FALSE); if(pattern[c]!='?' && tolower(pattern[c])!=tolower(str[c])) return(FALSE); } if(str[c]!=0) return(FALSE); return(TRUE); } bool osIsPattern(char *pat) { if(strchr(pat,'?') || strchr(pat,'*')) return(TRUE); return(FALSE); } crashmail-1.5/src/oslib_win32/000077500000000000000000000000001230617144500162465ustar00rootroot00000000000000crashmail-1.5/src/oslib_win32/Makefile000066400000000000000000000010011230617144500176760ustar00rootroot00000000000000INCDIR = ../ CC = gcc -I $(INCDIR) -DPLATFORM_WIN32 -Wall AR = ar -ru RM = del OBJS = osfile.o osdir.o osmisc.o osmem.o ospattern.o os.o oslib.a : $(OBJS) $(AR) oslib.a $(OBJS) # os osfile.o : osfile.c $(CC) -c osfile.c -o osfile.o osmisc.o : osmisc.c $(CC) -c osmisc.c -o osmisc.o osdir.o : osdir.c $(CC) -c osdir.c -o osdir.o osmem.o : osmem.c $(CC) -c osmem.c -o osmem.o ospattern.o : ospattern.c $(CC) -c ospattern.c -o ospattern.o os.o : os.c $(CC) -c os.c -o os.o clean : $(RM) *.o *.a crashmail-1.5/src/oslib_win32/os.c000066400000000000000000000001511230617144500170300ustar00rootroot00000000000000#include #include bool osInit(void) { return(TRUE); } void osEnd(void) { } crashmail-1.5/src/oslib_win32/os_win32.h000066400000000000000000000013211230617144500200570ustar00rootroot00000000000000#include #include bool osInit(void); void osEnd(void); typedef unsigned short UINT16; /* Unsigned 16-bit integer */ #define OS_EXIT_ERROR 10 #define OS_EXIT_OK 0 #define OS_PLATFORM_NAME "Win32" #define OS_CURRENT_DIR "." #define OS_PATH_CHARS "\\" #define OS_CONFIG_NAME "crashmail.prefs" #define OS_CONFIG_VAR "CMCONFIGFILE" /* Used by MakeFullPath. If path doesn't end with one of these characters, the first character will be appended to it. Example: OS_PATH_CHARS = "/:" "inbound" + "file" --> "inbound/file" "inbound/" + "file" --> "inbound/file" "inbound:" + "file" --> "inbound/file" */ #define stricmp strcasecmp #define strnicmp strncasecmp crashmail-1.5/src/oslib_win32/osdir.c000066400000000000000000000057661230617144500175500ustar00rootroot00000000000000#include #include #include #include #include #include #include void win32finddata_to_fileentry(WIN32_FIND_DATA *w32,struct osFileEntry *fe) { FILETIME local; SYSTEMTIME systime; struct tm tp; mystrncpy(fe->Name,w32->cFileName,100); fe->Size=w32->nFileSizeLow; FileTimeToLocalFileTime(&w32->ftLastWriteTime,&local); FileTimeToSystemTime(&local,&systime); tp.tm_sec=systime.wSecond; tp.tm_min=systime.wMinute; tp.tm_hour=systime.wHour; tp.tm_mday=systime.wDay; tp.tm_mon=systime.wMonth-1; tp.tm_year=systime.wYear-1900; tp.tm_wday=0; tp.tm_yday=0; tp.tm_isdst=-1; fe->Date=mktime(&tp); if(fe->Date == -1) /* Just in case */ time(&fe->Date); } bool win32readdiraddfile(WIN32_FIND_DATA *FindFileData,struct jbList *filelist,bool (*acceptfunc)(char *filename)) { bool add; struct osFileEntry *tmp; if(!acceptfunc) add=TRUE; else add=(*acceptfunc)(FindFileData->cFileName); if(add) { if(!(tmp=(struct osFileEntry *)osAllocCleared(sizeof(struct osFileEntry)))) { jbFreeList(filelist); return(FALSE); } win32finddata_to_fileentry(FindFileData,tmp); jbAddNode(filelist,(struct jbNode *)tmp); } return(TRUE); } bool osReadDir(char *dirname,struct jbList *filelist,bool (*acceptfunc)(char *filename)) { WIN32_FIND_DATA FindFileData; HANDLE hFind; char buf[200]; jbNewList(filelist); mystrncpy(buf,dirname,190); if(buf[strlen(buf)-1] != '\\') strcat(buf,"\\"); strcat(buf,"*"); hFind = FindFirstFile(buf,&FindFileData); if (hFind == INVALID_HANDLE_VALUE) return(FALSE); if(!win32readdiraddfile(&FindFileData,filelist,acceptfunc)) { FindClose(hFind); return(FALSE); } while(FindNextFile(hFind,&FindFileData)) { if(!win32readdiraddfile(&FindFileData,filelist,acceptfunc)) { FindClose(hFind); return(FALSE); } } FindClose(hFind); return(TRUE); } bool osScanDir(char *dirname,void (*func)(char *file)) { WIN32_FIND_DATA FindFileData; HANDLE hFind; char buf[200]; mystrncpy(buf,dirname,190); if(buf[strlen(buf)-1] != '\\') strcat(buf,"\\"); strcat(buf,"*"); hFind = FindFirstFile(buf,&FindFileData); if (hFind == INVALID_HANDLE_VALUE) return(FALSE); (*func)(FindFileData.cFileName); while(FindNextFile(hFind,&FindFileData)) (*func)(FindFileData.cFileName); FindClose(hFind); return(TRUE); } struct osFileEntry *osGetFileEntry(char *file) { WIN32_FIND_DATA FindFileData; HANDLE hFind; struct osFileEntry *tmp; hFind = FindFirstFile(file,&FindFileData); FindClose(hFind); if (hFind == INVALID_HANDLE_VALUE) return(NULL); if(!(tmp=(struct osFileEntry *)osAllocCleared(sizeof(struct osFileEntry)))) return(NULL); win32finddata_to_fileentry(&FindFileData,tmp); return(tmp); } crashmail-1.5/src/oslib_win32/osfile.c000066400000000000000000000151671230617144500177050ustar00rootroot00000000000000#include #include /* Only for vsprintf() */ #include #include #include #define MIN(a,b) ((a)<(b)? (a):(b)) #define OSFILE_BUFSIZE 5000 #define LASTACCESS_READ 1 #define LASTACCESS_WRITE 2 struct osfile { HANDLE h; char *buf; int lastaccessmode; ulong bufmax; ulong buflen; ulong bufpos; ulong filepos; }; bool win32writebuffer(struct osfile *osf) { DWORD numberofbyteswritten; if(osf->bufpos == 0) return(TRUE); /* Nothing to do */ if(!WriteFile(osf->h,osf->buf,osf->bufpos,&numberofbyteswritten,NULL)) return(FALSE); if(numberofbyteswritten != osf->bufpos) return(FALSE); osf->bufpos=0; osf->buflen=0; return(TRUE); } bool win32readbuffer(struct osfile *osf) { DWORD numberofbytesread; if(!ReadFile(osf->h,osf->buf,osf->bufmax,&numberofbytesread,NULL)) return(FALSE); if(numberofbytesread == 0) return(FALSE); osf->buflen=numberofbytesread; osf->bufpos=0; return(TRUE); } bool win32setmoderead(struct osfile *osf) { if(osf->lastaccessmode == LASTACCESS_WRITE) { /* Flush write buffer */ if(!win32writebuffer(osf)) return(FALSE); osf->buflen=0; osf->bufpos=0; } osf->lastaccessmode = LASTACCESS_READ; return(TRUE); } bool win32setmodewrite(struct osfile *osf) { if(osf->lastaccessmode == LASTACCESS_READ) { /* File pointer needs to be adjusted */ SetFilePointer(osf->h,osf->filepos,NULL,FILE_END); osf->buflen=0; osf->bufpos=0; } osf->lastaccessmode = LASTACCESS_WRITE; return(TRUE); } osFile osOpen(char *name,ulong mode) { DWORD desiredaccess; DWORD sharemode; DWORD creationdisposition; struct osfile *os; if(!(os=osAllocCleared(sizeof(struct osfile)))) return(NULL); if(!(os->buf=osAlloc(OSFILE_BUFSIZE))) { osFree(os); return(NULL); } if(mode == MODE_NEWFILE) { desiredaccess=GENERIC_WRITE; sharemode=0; creationdisposition=CREATE_ALWAYS; } else if(mode == MODE_OLDFILE) { desiredaccess=GENERIC_READ; sharemode=FILE_SHARE_READ; creationdisposition=OPEN_EXISTING; } else { desiredaccess=GENERIC_READ | GENERIC_WRITE; sharemode=FILE_SHARE_READ | FILE_SHARE_WRITE; creationdisposition=OPEN_ALWAYS; } os->h=CreateFile(name,desiredaccess,sharemode,NULL, creationdisposition,FILE_ATTRIBUTE_NORMAL,NULL); if(os->h == INVALID_HANDLE_VALUE) { osFree(os->buf); osFree(os); return(NULL); } os->bufmax=OSFILE_BUFSIZE; return (osFile)os; } void osClose(osFile os) { struct osfile *osf=(struct osfile *)os; if(osf->lastaccessmode == LASTACCESS_WRITE) win32writebuffer(osf); CloseHandle(osf->h); osFree(osf->buf); osFree(osf); } ulong osRead(osFile os,void *buf,ulong bytes) { struct osfile *osf=(struct osfile *)os; ulong read,n; win32setmoderead(osf); read=0; if(osf->buflen-osf->bufpos != 0) { /* Copy what's left in the buffer */ n=MIN(osf->buflen-osf->bufpos,bytes); memcpy(buf,&osf->buf[osf->bufpos],n); osf->bufpos+=n; osf->filepos+=n; buf+=n; bytes-=n; read+=n; } if(bytes > osf->bufmax) { /* Larger than buffer, read directly */ DWORD numberofbytesread; if(!ReadFile(osf->h,buf,bytes,&numberofbytesread,NULL)) return(read); read+=numberofbytesread; osf->filepos+=numberofbytesread; } else if(bytes > 0) { /* Get rest from buffer */ if(!win32readbuffer(osf)) return(read); n=MIN(osf->buflen-osf->bufpos,bytes); memcpy(buf,&osf->buf[osf->bufpos],n); osf->bufpos+=n; osf->filepos+=n; buf+=n; bytes-=n; read+=n; } return(read); } int osGetChar(osFile os) { struct osfile *osf=(struct osfile *)os; win32setmoderead(osf); if(osf->buflen-osf->bufpos == 0) { /* Buffer needs to be refilled */ if(!win32readbuffer(osf)) return(-1); } osf->filepos++; return(osf->buf[osf->bufpos++]); } ulong osFGets(osFile os,char *str,ulong max) { ulong d; int ch; d=0; if(max==0) return(0); for(;;) { ch=osGetChar(os); if(ch == -1) /* End of file */ { str[d]=0; return(d); } if(ch == 10) /* End of line */ { str[d++]=ch; str[d]=0; return(d); } if(ch != 13) /* CRs are skipped */ str[d++]=ch; if(d == max-1) /* Buffer full */ { str[d]=0; return(d); } } } bool osWrite(osFile os,const void *buf,ulong bytes) { struct osfile *osf=(struct osfile *)os; DWORD numberofbyteswritten; win32setmodewrite(osf); if(bytes > osf->bufmax-osf->bufpos) { /* Will not fit in buffer */ if(!win32writebuffer(osf)) return(FALSE); } if(bytes > osf->bufmax) { /* Larger than buffer, write directly */ if(!WriteFile(osf->h,buf,bytes,&numberofbyteswritten,NULL)) return(FALSE); if(numberofbyteswritten != bytes) return(FALSE); osf->filepos+=bytes; } else { /* Put in buffer */ memcpy(&osf->buf[osf->bufpos],buf,bytes); osf->bufpos+=bytes; osf->filepos+=bytes; } return(TRUE); } bool osPutChar(osFile os, char ch) { struct osfile *osf=(struct osfile *)os; win32setmodewrite(osf); if(osf->bufpos == osf->bufmax) { if(!win32writebuffer(osf)) return(FALSE); } osf->buf[osf->bufpos++]=ch; osf->filepos++; return(TRUE); } bool osPuts(osFile os,char *str) { if(osWrite(os,str,strlen(str))!=strlen(str)) return(FALSE); return(TRUE); } bool osFPrintf(osFile os,char *fmt,...) { va_list args; char buf[1000]; va_start(args, fmt); vsprintf(buf,fmt,args); va_end(args); return osPuts(os,buf); } bool osVFPrintf(osFile os,char *fmt,va_list args) { char buf[1000]; vsprintf(buf,fmt,args); return osPuts(os,buf); } void osSeek(osFile os,ulong offset,short mode) { struct osfile *osf=(struct osfile *)os; DWORD movemethod; /* Flush buffer if needed */ if(osf->lastaccessmode == LASTACCESS_WRITE) win32writebuffer(osf); /* Empty buffer */ osf->buflen=0; osf->bufpos=0; osf->lastaccessmode=0; if(mode == OFFSET_BEGINNING) movemethod=FILE_BEGIN; if(mode == OFFSET_END) movemethod=FILE_END; osf->filepos=SetFilePointer(osf->h,offset,NULL,movemethod); } ulong osFTell(osFile os) { struct osfile *osf=(struct osfile *)os; return(osf->filepos); } crashmail-1.5/src/oslib_win32/osmem.c000066400000000000000000000003371230617144500175350ustar00rootroot00000000000000#include #include void *osAlloc(ulong size) { return malloc((size_t)size); } void *osAllocCleared(ulong size) { return calloc((size_t)size,1); } void osFree(void *buf) { free(buf); } crashmail-1.5/src/oslib_win32/osmisc.c000066400000000000000000000030311230617144500177040ustar00rootroot00000000000000#include #include /* For system() */ #include #include #include #include void osSetComment(char *file,char *comment) { /* Does not exist in this os */ } /* Returns -1 if dir was not found and errorlevel otherwise */ int osChDirExecute(char *dir,char *cmd) { char olddir[300]; int res; if(!GetCurrentDirectory(300,olddir)) return(-1); if(!SetCurrentDirectory(dir)) return(-1); res=osExecute(cmd); SetCurrentDirectory(olddir); return(res); } int osExecute(char *cmd) { return system(cmd); } bool osExists(char *file) { HANDLE hFind; WIN32_FIND_DATA FindFileData; hFind = FindFirstFile(file,&FindFileData); FindClose(hFind); if (hFind == INVALID_HANDLE_VALUE) return(FALSE); return(TRUE); } bool osMkDir(char *dir) { if(CreateDirectory(dir,NULL)) return(TRUE); return(FALSE); } bool osRename(char *oldfile,char *newfile) { if(MoveFile(oldfile,newfile)) return(TRUE); return(FALSE); } bool osDelete(char *file) { if(DeleteFile(file)) return(TRUE); return(FALSE); } void osSleep(int secs) { Sleep(secs*1000); } char *osErrorMsg(ulong errnum) { char charbuf[1000]; static char oembuf[1000]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL,(DWORD)errnum,0,charbuf,1000,NULL); CharToOem(charbuf,oembuf); return (char *)oembuf; } ulong osError(void) { return (ulong)GetLastError(); } crashmail-1.5/src/oslib_win32/ospattern.c000066400000000000000000000011351230617144500204310ustar00rootroot00000000000000#include #include #include #include bool osCheckPattern(char *pattern) { return(TRUE); } bool osMatchPattern(char *pattern,char *str) { int c; for(c=0;pattern[c];c++) { if(pattern[c]=='*') return(TRUE); if(str[c] == 0) return(FALSE); if(pattern[c]!='?' && tolower(pattern[c])!=tolower(str[c])) return(FALSE); } if(str[c]!=0) return(FALSE); return(TRUE); } bool osIsPattern(char *pat) { if(strchr(pat,'?') || strchr(pat,'*')) return(TRUE); return(FALSE); } crashmail-1.5/src/shared/000077500000000000000000000000001230617144500153625ustar00rootroot00000000000000crashmail-1.5/src/shared/expr.c000066400000000000000000000155111230617144500165070ustar00rootroot00000000000000#include #include #include "shared/types.h" #include "shared/expr.h" #include #include struct expr *expr_get(struct expr *expr,int *errpos,char **errstr) { int c,d,len,start; bool hasnot,hasquote,stop; struct expr *res; hasnot=FALSE; c=expr->parsepos; /* Skip leading whitespace */ while(expr->buf[c]==' ') c++; /* Check for NOT */ if(strnicmp(&expr->buf[c],"NOT ",4)==0) { c+=4; hasnot=TRUE; } /* Check if parentheses */ if(expr->buf[c]=='(') { int nump; nump=1; d=c+1; while(nump!=0 && expr->buf[d]!=0) { if(expr->buf[d]=='(') nump++; if(expr->buf[d]==')') nump--; d++; } if(nump == 0) { start=c+1; len=d-c-2; if(!(res=osAlloc(sizeof(struct expr)))) { *errpos=expr->offset; *errstr="Out of memory"; return(NULL); } if(!(res->buf=osAlloc(len+10))) { osFree(res); *errpos=expr->offset; *errstr="Out of memory"; return(NULL); } res->parsepos=0; res->offset=expr->offset+start; res->buf[0]=0; if(hasnot) strcat(res->buf,"NOT "); strncat(res->buf,&expr->buf[start],len); expr->parsepos=d; return(res); } else { *errpos=expr->offset+c; *errstr="No matching parenthesis found"; return(NULL); } } d=c; stop=FALSE; hasquote=FALSE; while(!stop) { if(expr->buf[d] == 0) stop=TRUE; if(expr->buf[d] == ' ' && !hasquote) stop=TRUE; if(expr->buf[d] == '"' && hasquote) stop=TRUE; if(expr->buf[d] == '"') hasquote=TRUE; if(!stop) d++; } if(expr->buf[d]=='"') d++; start=c; len=d-c; if(!(res=osAlloc(sizeof(struct expr)))) { *errpos=expr->offset; *errstr="Out of memory"; return(NULL); } if(!(res->buf=osAlloc(len+10))) { osFree(res); *errpos=expr->offset; *errstr="Out of memory"; return(NULL); } res->parsepos=0; res->offset=expr->offset+start; res->buf[0]=0; if(hasnot) strcat(res->buf,"NOT "); strncat(res->buf,&expr->buf[start],len); expr->parsepos=d; return(res); } struct expr *expr_getrest(struct expr *expr,int *errpos,char **errstr) { int c,d,start,len; struct expr *res; c=expr->parsepos; /* Skip leading whitespace */ while(expr->buf[c]==' ') c++; d=c; while(expr->buf[d]!=0) d++; start=c; len=d-c; if(!(res=osAlloc(sizeof(struct expr)))) { *errpos=expr->offset; *errstr="Out of memory"; return(NULL); } if(!(res->buf=osAlloc(len+10))) { osFree(res); *errpos=expr->offset; *errstr="Out of memory"; return(NULL); } res->parsepos=0; res->offset=expr->offset+start; res->buf[0]=0; strncat(res->buf,&expr->buf[start],len); expr->parsepos=d; return(res); } struct expr *expr_makeexpr(char *str) { struct expr *expr; if(!(expr=osAllocCleared(sizeof(struct expr)))) { return(NULL); } if(!(expr->buf=osAlloc(strlen(str)+1))) { osFree(expr); return(NULL); } strcpy(expr->buf,str); return(expr); } void expr_free(struct expr *expr) { osFree(expr->buf); osFree(expr); } int expr_eval(struct expr *expr,int (*eval)(char *str,int *errpos,char **errstr),int *errpos,char **errstr) { struct expr *e1,*e2,*e3; e1=expr_get(expr,errpos,errstr); if(!e1) { return(-1); } e2=expr_get(expr,errpos,errstr); if(!e2) { expr_free(e1); return(-1); } e3=expr_getrest(expr,errpos,errstr); if(!e3) { expr_free(e1); expr_free(e2); return(-1); } if(e2->buf[0] == 0) { /* Only one expression */ if(strcmp(e1->buf,expr->buf)==0) { /* No more simplification possible */ if(strnicmp(e1->buf,"NOT ",4)==0) { int res; strcpy(e1->buf,&e1->buf[4]); e1->parsepos=0; res=expr_eval(e1,eval,errpos,errstr); if(res == 1) res=0; else if(res == 0) res=1; /* printf("NOT |%s|: %d\n",e1->buf,res); */ expr_free(e1); expr_free(e2); expr_free(e3); return(res); } else { int res,tmperrpos; res=(*eval)(e1->buf,&tmperrpos,errstr); if(res == -1) *errpos=e1->offset+tmperrpos; /* printf("EVAL: |%s|: %d\n",e1->buf,res); */ expr_free(e1); expr_free(e2); expr_free(e3); return(res); } } else { int res; res=expr_eval(e1,eval,errpos,errstr); /* printf("|%s|: %d\n",e1->buf,res); */ expr_free(e1); expr_free(e2); expr_free(e3); return (res); } } if(stricmp(e2->buf,"AND")==0) { int r1,r2,res; if(e3->buf[0] == 0) { *errstr="Expected second expression after AND"; *errpos=e3->offset; expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } r1=expr_eval(e1,eval,errpos,errstr); if(r1 == -1) { expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } r2=expr_eval(e3,eval,errpos,errstr); if(r2 == -1) { expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } res=0; if(r1 && r2) res=1; /* printf("|%s| AND |%s|: %d\n",e1->buf,e3->buf,res); */ expr_free(e1); expr_free(e2); expr_free(e3); return(res); } else if(stricmp(e2->buf,"OR")==0) { int r1,r2,res; if(e3->buf[0] == 0) { *errstr="Expected second expression after OR"; *errpos=e3->offset; expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } r1=expr_eval(e1,eval,errpos,errstr); if(r1 == -1) { expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } r2=expr_eval(e3,eval,errpos,errstr); if(r2 == -1) { expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } res=0; if(r1 || r2) res=1; /* printf("|%s| OR |%s|: %d\n",e1->buf,e3->buf,res); */ expr_free(e1); expr_free(e2); expr_free(e3); return(res); } else { *errstr="Expected AND or OR"; *errpos=e2->offset; expr_free(e1); expr_free(e2); expr_free(e3); return(-1); } } crashmail-1.5/src/shared/expr.h000066400000000000000000000003701230617144500165110ustar00rootroot00000000000000struct expr { char *buf; int parsepos; int offset; }; int expr_eval(struct expr *expr,int (*eval)(char *str,int *errpos,char **errstr),int *errpos,char **errstr); struct expr *expr_makeexpr(char *str); void expr_free(struct expr *expr); crashmail-1.5/src/shared/fidonet.h000066400000000000000000000050531230617144500171660ustar00rootroot00000000000000/* PktHeader */ #define PKTHEADER_ORIGNODE 0 #define PKTHEADER_DESTNODE 2 #define PKTHEADER_YEAR 4 #define PKTHEADER_MONTH 6 #define PKTHEADER_DAY 8 #define PKTHEADER_HOUR 10 #define PKTHEADER_MINUTE 12 #define PKTHEADER_SECOND 14 #define PKTHEADER_BAUD 16 #define PKTHEADER_PKTTYPE 18 #define PKTHEADER_ORIGNET 20 #define PKTHEADER_DESTNET 22 #define PKTHEADER_PRODCODELOW 24 #define PKTHEADER_REVMAJOR 25 #define PKTHEADER_PASSWORD 26 #define PKTHEADER_QORIGZONE 34 #define PKTHEADER_QDESTZONE 36 #define PKTHEADER_AUXNET 38 #define PKTHEADER_CWVALIDCOPY 40 #define PKTHEADER_PRODCODEHIGH 42 #define PKTHEADER_REVMINOR 43 #define PKTHEADER_CAPABILWORD 44 #define PKTHEADER_ORIGZONE 46 #define PKTHEADER_DESTZONE 48 #define PKTHEADER_ORIGPOINT 50 #define PKTHEADER_DESTPOINT 52 #define PKTHEADER_PRODDATA 54 #define SIZE_PKTHEADER 58 /* PktHeader FSC-0045 */ #define PKTHEADER45_ORIGNODE 0 #define PKTHEADER45_DESTNODE 2 #define PKTHEADER45_ORIGPOINT 4 #define PKTHEADER45_DESTPOINT 6 #define PKTHEADER45_RESERVED 8 #define PKTHEADER45_SUBVERSION 16 #define PKTHEADER45_VERSION 18 #define PKTHEADER45_ORIGNET 20 #define PKTHEADER45_DESTNET 22 #define PKTHEADER45_PRODCODE 24 #define PKTHEADER45_REVISION 25 #define PKTHEADER45_PASSWORD 26 #define PKTHEADER45_ORIGZONE 34 #define PKTHEADER45_DESTZONE 36 #define PKTHEADER45_ORIGDOMAIN 38 #define PKTHEADER45_DESTDOMAIN 46 #define PKTHEADER45_PRODDATA 54 #define SIZE_PKTHEADER45 58 /* PktMsgHeader */ #define PKTMSGHEADER_PKTTYPE 0 #define PKTMSGHEADER_ORIGNODE 2 #define PKTMSGHEADER_DESTNODE 4 #define PKTMSGHEADER_ORIGNET 6 #define PKTMSGHEADER_DESTNET 8 #define PKTMSGHEADER_ATTR 10 #define PKTMSGHEADER_COST 12 /* plus header strings */ #define SIZE_PKTMSGHEADER 14 /* Header flags */ #define FLAG_PVT 1 #define FLAG_CRASH 2 #define FLAG_RECD 4 #define FLAG_SENT 8 #define FLAG_FILEATTACH 16 #define FLAG_INTRANSIT 32 #define FLAG_ORPHAN 64 #define FLAG_KILLSENT 128 #define FLAG_LOCAL 256 #define FLAG_HOLD 512 /* FLAG_UNUSED 1024 */ #define FLAG_FILEREQ 2048 #define FLAG_RREQ 4096 #define FLAG_IRRR 8192 #define FLAG_AUDIT 16384 #define FLAG_UPDATEREQ 32768 crashmail-1.5/src/shared/jblist.c000066400000000000000000000026371230617144500170250ustar00rootroot00000000000000#include #include #include #include "oslib/osmem.h" #include "shared/jblist.h" void jbNewList(struct jbList *list) { list->First=NULL; list->Last=NULL; } void jbAddNode(struct jbList *list, struct jbNode *node) { if(!list->First) list->First=node; else list->Last->Next=node; list->Last=node; node->Next=NULL; } void jbFreeList(struct jbList *list) { struct jbNode *tmp,*tmp2; for(tmp=list->First;tmp;) { tmp2=tmp->Next; osFree(tmp); tmp=tmp2; } list->First=NULL; list->Last=NULL; } void jbFreeNum(struct jbList *list,uint32_t num) { struct jbNode *old=NULL,*tmp; uint32_t c; tmp=list->First; for(c=0;cNext; } if(c==num) { if(old) old->Next=tmp->Next; if(num==0) list->First=tmp->Next; if(tmp->Next == NULL) list->Last=old; osFree(tmp); } } void jbFreeNode(struct jbList *list,struct jbNode *node) { struct jbNode *old=NULL,*tmp; for(tmp=list->First;tmp;tmp=tmp->Next) { if(tmp->Next == node) old=tmp; if(tmp == node) break; } if(tmp == node) { if(old) old->Next=tmp->Next; if(node == list->First) list->First=tmp->Next; if(tmp->Next == NULL) list->Last=old; osFree(tmp); } } crashmail-1.5/src/shared/jblist.h000066400000000000000000000006331230617144500170240ustar00rootroot00000000000000#ifndef SHARED_JBLIST_H #define SHARED_JBLIST_H struct jbList { struct jbNode *First; struct jbNode *Last; }; struct jbNode { struct jbNode *Next; }; void jbAddNode(struct jbList *list, struct jbNode *node); void jbNewList(struct jbList *list); void jbFreeList(struct jbList *list); void jbFreeNum(struct jbList *list,uint32_t num); void jbFreeNode(struct jbList *list,struct jbNode *node); #endif crashmail-1.5/src/shared/jbstrcpy.c000066400000000000000000000023431230617144500173700ustar00rootroot00000000000000#include #include #include "shared/types.h" bool jbstrcpy(char *dest,char *src,uint32_t maxlen,uint32_t *jbc) { uint32_t d=0; uint32_t stopchar1,stopchar2; uint32_t jbcpos; jbcpos= *jbc; while(src[jbcpos]==32 || src[jbcpos]==9) jbcpos++; if(src[jbcpos]=='"') { jbcpos++; stopchar1='"'; stopchar2=0; } else { stopchar1=' '; stopchar2=9; } while(src[jbcpos]!=stopchar1 && src[jbcpos]!=stopchar2 && src[jbcpos]!=10 && src[jbcpos]!=0) { if(src[jbcpos]=='\\' && src[jbcpos+1]!=0 && src[jbcpos+1]!=10) jbcpos++; if(d #include #include #include "shared/types.h" void mystrncpy(char *dest,char *src,uint32_t len) { strncpy(dest,src,(size_t)len-1); dest[len-1]=0; } crashmail-1.5/src/shared/mystrncpy.h000066400000000000000000000002151230617144500176010ustar00rootroot00000000000000#ifndef SHARED_MYSTRNCPY_H #define SHARED_MYSTRNCPY_H #include "shared/types.h" void mystrncpy(char *dest,char *src,uint32_t len); #endif crashmail-1.5/src/shared/node4d.c000066400000000000000000000044351230617144500167110ustar00rootroot00000000000000#include #include #include #include #include "node4d.h" bool Parse4DTemplate(char *buf, struct Node4D *node,struct Node4D *tpl) { uint32_t c,val; bool GotZone,GotNet,GotNode,GotVal; GotZone=FALSE; GotNet=FALSE; GotNode=FALSE; GotVal=FALSE; Copy4D(node,tpl); for(c=0;cZone=val; GotZone=TRUE; GotVal=FALSE; } else if(buf[c]=='/') { if(GotNet || GotNode) return(FALSE); if(GotVal) node->Net=val; GotNet=TRUE; GotVal=FALSE; } else if(buf[c]=='.') { if(GotNode) return(FALSE); if(GotVal) node->Node=val; GotNode=TRUE; GotVal=FALSE; } else if(buf[c]>='0' && buf[c]<='9') { if(!GotVal) { val=0; GotVal=TRUE; } val*=10; val+=buf[c]-'0'; } else return(FALSE); } if(GotVal) { if(GotZone && !GotNet) node->Net=val; else if(GotNode) node->Point=val; else node->Node=val; } return(TRUE); } bool Parse4D(char *buf, struct Node4D *node) { struct Node4D tpl4d = { 0,0,0,0 }; return Parse4DTemplate(buf,node,&tpl4d); } void Copy4D(struct Node4D *node1,struct Node4D *node2) { node1->Zone=node2->Zone; node1->Net=node2->Net; node1->Node=node2->Node; node1->Point=node2->Point; } int Compare4D(struct Node4D *node1,struct Node4D *node2) { if(node1->Zone!=0 && node2->Zone!=0) { if(node1->Zone > node2->Zone) return(1); if(node1->Zone < node2->Zone) return(-1); } if(node1->Net > node2->Net) return(1); if(node1->Net < node2->Net) return(-1); if(node1->Node > node2->Node) return(1); if(node1->Node < node2->Node) return(-1); if(node1->Point > node2->Point) return(1); if(node1->Point < node2->Point) return(-1); return(0); } void Print4D(struct Node4D *n4d,char *dest) { if(n4d->Point) sprintf(dest,"%u:%u/%u.%u",n4d->Zone, n4d->Net, n4d->Node, n4d->Point); else sprintf(dest,"%u:%u/%u",n4d->Zone, n4d->Net, n4d->Node); } crashmail-1.5/src/shared/node4d.h000066400000000000000000000006211230617144500167070ustar00rootroot00000000000000#ifndef NODE4D_H #define NODE4D_H #include struct Node4D { uint16_t Zone,Net,Node,Point; }; bool Parse4D(char *buf, struct Node4D *node); bool Parse4DTemplate(char *buf, struct Node4D *node,struct Node4D *tpl); void Copy4D(struct Node4D *node1,struct Node4D *node2); int Compare4D(struct Node4D *node1,struct Node4D *node2); void Print4D(struct Node4D *n4d,char *dest); #endif crashmail-1.5/src/shared/parseargs.c000066400000000000000000000043501230617144500175170ustar00rootroot00000000000000#include #include #include #include #include "shared/parseargs.h" bool parseargs(struct argument *arg,int argc, char **argv) { int i,j; for(i=1;i%s\n",buf1,buf2); break; case ARGTYPE_BOOL: printf("%s\n",arg[j].name); break; } } printf("\n"); printf("[Mandatory] means that the argument has to be specified. Parentheses around\n"); printf("the keyword mean that the keyword itself does not have to be specified.\n\n"); } crashmail-1.5/src/shared/parseargs.h000066400000000000000000000007501230617144500175240ustar00rootroot00000000000000#ifndef SHARED_PARSEARGS_H #define SHARED_PARSEARGS_H #include "shared/types.h" #define ARGTYPE_END 0 #define ARGTYPE_STRING 1 #define ARGTYPE_BOOL 2 #define ARGFLAG_AUTO 1 /* Keyword does not have to be specified */ #define ARGFLAG_MANDATORY 2 /* Argument cannot be left out */ struct argument { uint16_t type; char *name; uint16_t flags; void *data; }; bool parseargs(struct argument *arg,int argc, char **argv); void printargs(struct argument *arg); #endif crashmail-1.5/src/shared/path.c000066400000000000000000000013551230617144500164660ustar00rootroot00000000000000#include #include #include #include #include #include void MakeFullPath(char *path,char *file,char *dest,uint32_t destsize) { int d; char *chr; chr=OS_PATH_CHARS; mystrncpy(dest,path,destsize); d=strlen(dest); if(d != 0) { if(!strchr(chr,dest[d-1])) if(d+1 < destsize) { dest[d++]=(char)chr[0]; dest[d]=0; } } if(destsize-d-1 > 0) mystrncpy(&dest[d],file,destsize-d-1); } char *GetFilePart(char *str) { int d; char *chr,*ret; chr=OS_PATH_CHARS; ret=str; for(d=0;str[d];d++) if(strchr(chr,str[d])) ret = &str[d+1]; return(ret); } crashmail-1.5/src/shared/path.h000066400000000000000000000001501230617144500164630ustar00rootroot00000000000000 void MakeFullPath(char *path,char *file,char *dest,uint32_t destsize); char *GetFilePart(char *str); crashmail-1.5/src/shared/storedmsg.h000066400000000000000000000005561230617144500175500ustar00rootroot00000000000000struct StoredMsg { char From[36]; char To[36]; char Subject[72]; char DateTime[20]; UINT16 TimesRead; UINT16 DestNode; UINT16 OrigNode; UINT16 Cost; UINT16 OrigNet; UINT16 DestNet; UINT16 DestZone; UINT16 OrigZone; UINT16 DestPoint; UINT16 OrigPoint; UINT16 ReplyTo; UINT16 Attr; UINT16 NextReply; /* Text */ }; crashmail-1.5/src/shared/types.h000066400000000000000000000002101230617144500166700ustar00rootroot00000000000000#ifndef SHARED_TYPES_H #define SHARED_TYPES_H #ifndef NO_TYPEDEF_BOOL typedef int bool; #endif #define FALSE 0 #define TRUE 1 #endif crashmail-1.5/src/tools/000077500000000000000000000000001230617144500152545ustar00rootroot00000000000000crashmail-1.5/src/tools/crashexport.c000066400000000000000000000206311230617144500177640ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PLATFORM_AMIGA char *ver="$VER: CrashExport " VERSION " " __AMIGADATE__; #endif bool diskfull; char cfgbuf[4000]; #define AREATYPE_NETMAIL 1 #define AREATYPE_ECHOMAIL 2 #define AREATYPE_DEFAULT 3 #define AREATYPE_BAD 4 #define AREATYPE_LOCAL 5 char tagname[100],desc[100],msgbase[10],path[100],export[1000],aka[50]; char group,areatype; bool unconfirmed; #define ARG_PREFSFILE 0 #define ARG_OUTFILE 1 #define ARG_FORMAT 2 #define ARG_GROUP 3 struct argument args[] = { { ARGTYPE_STRING, "PREFSFILE", ARGFLAG_AUTO | ARGFLAG_MANDATORY, NULL }, { ARGTYPE_STRING, "OUTFILE", ARGFLAG_AUTO | ARGFLAG_MANDATORY, NULL }, { ARGTYPE_STRING, "FORMAT", ARGFLAG_AUTO | ARGFLAG_MANDATORY, NULL }, { ARGTYPE_STRING, "GROUP", 0, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; #define FORMAT_AREASBBS 0 #define FORMAT_FORWARD 1 #define FORMAT_FORWARDNODESC 2 #define FORMAT_GOLDED 3 #define FORMAT_TIMED 4 int format; bool CheckFlags(char group,char *node) { int c; for(c=0;c 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } if(stricmp((char *)args[ARG_FORMAT].data,"areasbbs")==0) { format=FORMAT_AREASBBS; } else if(stricmp((char *)args[ARG_FORMAT].data,"forward")==0) { format=FORMAT_FORWARD; } else if(stricmp((char *)args[ARG_FORMAT].data,"forwardnodesc")==0) { format=FORMAT_FORWARDNODESC; } else if(stricmp((char *)args[ARG_FORMAT].data,"golded")==0) { format=FORMAT_GOLDED; } else if(stricmp((char *)args[ARG_FORMAT].data,"timed")==0) { format=FORMAT_TIMED; } else { printf("Unknown format \"%s\"\n",(char *)args[ARG_FORMAT].data); osEnd(); exit(OS_EXIT_ERROR); } if(!(ifh=osOpen(args[ARG_PREFSFILE].data,MODE_OLDFILE))) { uint32_t err=osError(); printf("Failed to open %s for reading\n",(char *)args[ARG_PREFSFILE].data); printf("Error: %s",osErrorMsg(err)); osEnd(); exit(OS_EXIT_ERROR); } if(!(ofh=osOpen(args[ARG_OUTFILE].data,MODE_NEWFILE))) { uint32_t err=osError(); printf("Failed to open %s for writing\n",(char *)args[ARG_OUTFILE].data); printf("Error: %s",osErrorMsg(err)); osClose(ifh); osEnd(); exit(OS_EXIT_ERROR); } time(&t); osFPrintf(ofh,"; Generated by CrashExport %s\n; %s\n",VERSION,ctime(&t)); if(format == FORMAT_AREASBBS) { /* Get default origin and sysop name for areas.bbs */ strcpy(sysopname,"Sysop"); while(osFGets(ifh,cfgbuf,4000)) { jbcpos=0; jbstrcpy(cfgword,cfgbuf,30,&jbcpos); if(stricmp(cfgword,"SYSOP")==0) jbstrcpy(sysopname,cfgbuf,100,&jbcpos); } osFPrintf(ofh,"%s ! %s\n","Default origin",sysopname); osSeek(ifh,0,OFFSET_BEGINNING); } while(osFGets(ifh,cfgbuf,4000)) { jbcpos=0; jbstrcpy(cfgword,cfgbuf,30,&jbcpos); if(stricmp(cfgword,"AREA")==0 || stricmp(cfgword,"NETMAIL")==0 || stricmp(cfgword,"LOCALAREA")==0) { if(tagname[0]) writearea(ofh); group=0; unconfirmed=FALSE; export[0]=0; desc[0]=0; jbstrcpy(tagname,cfgbuf,100,&jbcpos); jbstrcpy(aka,cfgbuf,50,&jbcpos); jbstrcpy(msgbase,cfgbuf,10,&jbcpos); jbstrcpy(path,cfgbuf,100,&jbcpos); if(stricmp(cfgword,"NETMAIL")==0) areatype=AREATYPE_NETMAIL; else if(stricmp(cfgword,"LOCALAREA")==0) areatype=AREATYPE_LOCAL; else if(stricmp(tagname,"BAD")==0) areatype=AREATYPE_BAD; else if(stricmp(tagname,"DEFAULT")==0 || strnicmp(tagname,"DEFAULT_",8)==0) areatype=AREATYPE_DEFAULT; else areatype=AREATYPE_ECHOMAIL; } if(stricmp(cfgword,"EXPORT")==0) { struct Node4D tpl4d,tmp4d; tpl4d.Zone=0; tpl4d.Net=0; tpl4d.Node=0; tpl4d.Point=0; while(jbstrcpy(buf,cfgbuf,100,&jbcpos)) { if(buf[0]=='!' || buf[0]=='%' || buf[0]=='@') strcpy(buf,&buf[1]); if(Parse4DTemplate(buf,&tmp4d,&tpl4d)) { Copy4D(&tpl4d,&tmp4d); tpl4d.Point=0; Print4D(&tmp4d,buf); if(strlen(export) < sizeof(export)-20) { if(export[0]) strcat(export," "); strcat(export,buf); } } } } if(stricmp(cfgword,"UNCONFIRMED")==0) { unconfirmed=TRUE; } if(stricmp(cfgword,"DESCRIPTION")==0) { jbstrcpy(desc,cfgbuf,100,&jbcpos); } if(stricmp(cfgword,"GROUP")==0) { if(jbstrcpy(buf,cfgbuf,100,&jbcpos)) group=buf[0]; } } if(tagname[0]) writearea(ofh); osClose(ofh); osClose(ifh); osEnd(); exit(OS_EXIT_OK); } crashmail-1.5/src/tools/crashgetnode.c000066400000000000000000000055031230617144500200710ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #ifdef PLATFORM_AMIGA char *ver="$VER: CrashCompileNL " VERSION " " __AMIGADATE__; #endif #define ARG_NODE 0 #define ARG_DIRECTORY 1 struct argument args[] = { { ARGTYPE_STRING, "NODE", ARGFLAG_AUTO | ARGFLAG_MANDATORY, NULL }, { ARGTYPE_STRING, "DIRECTORY", ARGFLAG_AUTO, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; bool nomem,diskfull; void strip(char *str) { int c; for(c=strlen(str)-1;str[c] < 33 && c>=0;c--) str[c]=0; } int main(int argc, char **argv) { osFile fh; char line[200],*dir,*buf; struct Node4D n4d; struct cmnlIdx idx; if(!osInit()) exit(OS_EXIT_ERROR); if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } if(args[ARG_DIRECTORY].data) dir=(char *)args[ARG_DIRECTORY].data; else dir=getenv("CMNODELISTDIR"); if(!dir) { printf("No directory specified and CMNODELISTDIR not set\n"); osEnd(); exit(OS_EXIT_ERROR); } if(!Parse4D((char *)args[ARG_NODE].data,&n4d)) { printf("Invalid node %s\n",(char *)args[ARG_NODE].data); exit(OS_EXIT_ERROR); } if(!(fh=cmnlOpenNL(dir))) { printf("%s\n",cmnlLastError()); exit(OS_EXIT_ERROR); } idx.zone=n4d.Zone; idx.net=n4d.Net; idx.node=n4d.Node; idx.point=n4d.Point; if(!cmnlFindNL(fh,dir,&idx,line,200)) { cmnlCloseNL(fh); printf("%s\n",cmnlLastError()); exit(OS_EXIT_ERROR); } cmnlCloseNL(fh); printf("Node %d:%d/%d.%d\n",idx.zone,idx.net,idx.node,idx.point); printf("Region %d, Hub %d\n",idx.region,idx.hub); strip(line); if(line[0]==',') { buf="Node"; strtok(line,","); /* Skip number */ } else { buf=strtok(line,","); if(!buf) buf=""; strtok(NULL,","); /* Skip number */ } printf("Node is listed as a %s\n",buf); if((buf=strtok(NULL,","))) { printf("Name: %s\n",buf); } if((buf=strtok(NULL,","))) { printf("Location: %s\n",buf); } if((buf=strtok(NULL,","))) { printf("Sysop: %s\n",buf); } if((buf=strtok(NULL,","))) { printf("Phone: %s\n",buf); } if((buf=strtok(NULL,","))) { printf("Baud: %s\n",buf); } if((buf=strtok(NULL,""))) { printf("Flags: %s\n",buf); } osEnd(); exit(OS_EXIT_OK); } crashmail-1.5/src/tools/crashlist.c000066400000000000000000000140001230617144500174070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PLATFORM_AMIGA char *ver="$VER: CrashList " VERSION " " __AMIGADATE__; #endif #define ARG_DIRECTORY 0 struct argument args[] = { { ARGTYPE_STRING, "DIRECTORY", ARGFLAG_AUTO, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; struct idx { uint16_t zone,net,node,point,region,hub; uint32_t offset; }; bool nomem,diskfull; void putuword(char *buf,uint32_t offset,uint16_t num) { buf[offset]=num%256; buf[offset+1]=num/256; } void putulong(char *buf,uint32_t offset,uint32_t num) { buf[offset]=num%256; buf[offset+1]=(num / 256) % 256; buf[offset+2]=(num / 256 / 256) % 256; buf[offset+3]=(num / 256 / 256 / 256) % 256; } void WriteIdx(osFile fh,struct idx *idx) { char binbuf[16]; putuword(binbuf,0,idx->zone); putuword(binbuf,2,idx->net); putuword(binbuf,4,idx->node); putuword(binbuf,6,idx->point); putuword(binbuf,8,idx->region); putuword(binbuf,10,idx->hub); putulong(binbuf,12,idx->offset); osWrite(fh,binbuf,sizeof(binbuf)); } char nlname[100]; char *findfile,*finddir; time_t newest; bool isnodelistending(char *name) { if(strlen(name)<4) return(FALSE); if(name[strlen(name)-4]!='.') return(FALSE); if(!isdigit(name[strlen(name)-3])) return(FALSE); if(!isdigit(name[strlen(name)-2])) return(FALSE); if(!isdigit(name[strlen(name)-1])) return(FALSE); return(TRUE); } void scandirfunc(char *file) { char buf[500]; struct osFileEntry *fe; if(isnodelistending(file)) { if(strnicmp(file,findfile,strlen(file)-4)==0) { MakeFullPath(finddir,file,buf,500); if((fe=osGetFileEntry(buf))) { if(nlname[0]==0 || newest < fe->Date) { mystrncpy(nlname,fe->Name,100); newest=fe->Date; } osFree(fe); } } } } bool FindList(char *dir,char *file,char *dest) { MakeFullPath(dir,file,dest,500); if(osExists(dest)) return(TRUE); nlname[0]=0; newest=0; findfile=file; finddir=dir; if(!osScanDir(dir,scandirfunc)) { uint32_t err=osError(); printf("Failed to scan directory %s\n",dir); printf("Error: %s\n",osErrorMsg(err)); return(FALSE); } if(nlname[0]==0) { printf("Found no nodelist matching %s in %s\n",file,dir); return(FALSE); } MakeFullPath(dir,nlname,dest,500); return(TRUE); } void ProcessList(char *dir,char *file,osFile ifh,uint16_t defzone) { struct idx idx; char buf[500]; osFile nfh; if(!FindList(dir,file,buf)) return; if(!(nfh=osOpen(buf,MODE_OLDFILE))) { uint32_t err=osError(); printf("Failed to read %s\n",buf); printf("Error: %s\n",osErrorMsg(err)); return; } strcpy(buf,(char *)GetFilePart(buf)); printf("Processing nodelist %s...\n",buf); osWrite(ifh,buf,100); idx.zone=defzone; idx.net=0; idx.node=0; idx.point=0; idx.region=0; idx.hub=0; idx.offset=0; idx.offset=osFTell(nfh); while(osFGets(nfh,buf,500)) { if(strnicmp(buf,"Zone,",5)==0) { idx.zone=atoi(&buf[5]); idx.region=0; idx.net=idx.zone; idx.hub=0; idx.node=0; idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,"Region,",7)==0) { idx.region=atoi(&buf[7]); idx.net=idx.region; idx.hub=0; idx.node=0; idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,"Host,",5)==0) { idx.net=atoi(&buf[5]); idx.hub=0; idx.node=0; idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,"Hub,",4)==0) { idx.hub=atoi(&buf[4]); idx.node=idx.hub; idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,"Pvt,",4)==0) { idx.node=atoi(&buf[4]); idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,"Hold,",5)==0) { idx.node=atoi(&buf[5]); idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,",",1)==0) { idx.node=atoi(&buf[1]); idx.point=0; WriteIdx(ifh,&idx); } if(strnicmp(buf,"Point,",6)==0) { idx.point=atoi(&buf[6]); WriteIdx(ifh,&idx); } idx.offset=osFTell(nfh); } idx.zone=0; idx.region=0; idx.net=0; idx.hub=0; idx.node=0; idx.point=0; idx.offset=0xffffffff; WriteIdx(ifh,&idx); } int main(int argc, char **argv) { osFile lfh,ifh; char *dir,buf[200],cfgbuf[200],file[100]; uint32_t jbcpos,zone; if(!osInit()) exit(OS_EXIT_ERROR); if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } dir=OS_CURRENT_DIR; if(args[ARG_DIRECTORY].data) dir=(char *)args[ARG_DIRECTORY].data; MakeFullPath(dir,"cmnodelist.prefs",buf,200); if(!(lfh=osOpen(buf,MODE_OLDFILE))) { uint32_t err=osError(); printf("Failed to open %s for reading\n",buf); printf("Error: %s\n",osErrorMsg(err)); osEnd(); exit(OS_EXIT_ERROR); } MakeFullPath(dir,"cmnodelist.index",buf,200); if(!(ifh=osOpen(buf,MODE_NEWFILE))) { uint32_t err=osError(); printf("Failed to open %s for writing (nodelist in use?)\n",buf); printf("Error: %s\n",osErrorMsg(err)); osClose(lfh); osEnd(); exit(OS_EXIT_ERROR); } osWrite(ifh,"CNL1",4); while(osFGets(lfh,cfgbuf,200)) { if(cfgbuf[0]!=';') { jbcpos=0; if(jbstrcpy(file,cfgbuf,100,&jbcpos)) { zone=0; if(jbstrcpy(buf,cfgbuf,10,&jbcpos)) zone=atoi(buf); ProcessList(dir,file,ifh,zone); } } } osClose(lfh); osClose(ifh); osEnd(); exit(OS_EXIT_OK); } /* Hitta rätt nodelista */ crashmail-1.5/src/tools/crashlistout.c000066400000000000000000000342361230617144500201540ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NET(x) (x >> 16) #define NODE(x) (x & 0xFFFF) #define TYPE_CRASH 0 #define TYPE_DIRECT 1 #define TYPE_NORMAL 2 #define TYPE_HOLD 3 #define TYPE_REQUEST 4 char *type_names[] = { "Crash", "Direct", "Normal", "Hold", "Request" }; uint32_t TotalFiles=0; uint32_t TotalBytes=0; uint32_t TotalRequests=0; struct fileentry { struct fileentry *Next; struct Node4D Node; char file[100]; char dir[100]; uint32_t size; time_t date; uint32_t type; bool flow; }; struct Node4DPat { char Zone[10]; char Net[10]; char Node[10]; char Point[10]; }; char *cfg_Dir; uint32_t cfg_Zone; struct Node4DPat cfg_Pattern; bool cfg_Verbose; #define ARG_DIRECTORY 0 #define ARG_ZONE 1 #define ARG_PATTERN 2 #define ARG_VERBOSE 3 struct argument args[] = { { ARGTYPE_STRING, "DIRECTORY", ARGFLAG_AUTO, NULL }, { ARGTYPE_STRING, "ZONE", ARGFLAG_AUTO, NULL }, { ARGTYPE_STRING, "PATTERN", ARGFLAG_AUTO, NULL }, { ARGTYPE_BOOL, "VERBOSE", 0, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; struct jbList list; /* Some stuff for node pattern (taken from CrashMail) */ bool Parse4DPat(char *buf, struct Node4DPat *node) { uint32_t c=0,tempc=0; char temp[10]; bool GotZone=FALSE,GotNet=FALSE,GotNode=FALSE; strcpy(node->Zone,"*"); strcpy(node->Net,"*"); strcpy(node->Node,"*"); strcpy(node->Point,"*"); if(strcmp(buf,"*")==0) return(TRUE); for(c=0;cZone,temp); GotZone=TRUE; tempc=0; } else if(buf[c]=='/') { if(GotNet || GotNode) return(FALSE); strcpy(node->Net,temp); GotNet=TRUE; tempc=0; } else if(buf[c]=='.') { if(GotNode) return(FALSE); strcpy(node->Node,temp); node->Point[0]=0; GotNode=TRUE; tempc=0; } else if((buf[c]>='0' && buf[c]<='9') || buf[c]=='*' || buf[c]=='?') { if(tempc<9) { temp[tempc++]=buf[c]; temp[tempc]=0; } } else return(FALSE); } if(GotZone && !GotNet) { strcpy(node->Net,temp); } else if(GotNode) { strcpy(node->Point,temp); } else { strcpy(node->Node,temp); strcpy(node->Point,"0"); } return(TRUE); } int NodeCompare(char *pat,uint16_t num) { char buf[10]; uint8_t c; sprintf(buf,"%u",num); if(pat[0]==0) return(0); for(c=0;cZone!=0) if(NodeCompare(nodepat->Zone, node->Zone )!=0) return(1); if(NodeCompare(nodepat->Net, node->Net )!=0) return(1); if(NodeCompare(nodepat->Node, node->Node )!=0) return(1); if(NodeCompare(nodepat->Point,node->Point)!=0) return(1); return(0); } /* Some other functions from CrashMail */ char *unit(uint32_t i) { static char buf[20]; if ((i>10000000)||(i<-10000000)) sprintf(buf,"%uM",i/(1024*1024)); else if ((i>10000)||(i<-10000)) sprintf(buf,"%uK",i/1024); else sprintf(buf,"%u",i); return buf; } unsigned long hextodec(char *hex) { char *hextab="0123456789abcdef"; int c=0,c2=0; unsigned long result=0; for(;;) { for(c2=0;c2<16;c2++) if(tolower(hex[c]) == hextab[c2]) break; if(c2 == 16) return(result); /* End of correct hex number */ result *= 16; result += c2; c++; } } void strip(char *str) { int c; for(c=strlen(str)-1;str[c] < 33 && c>=0;c--) str[c]=0; } /* Display entries in the list */ char *PrintNode(struct Node4D *node) { static char buf[50]; Print4D(node,buf); return(buf); } char *PrintDate(time_t date) { static char buf[50]; struct tm *tp; char *monthnames[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; tp=localtime(&date); sprintf(buf,"%02d-%s-%02d %02d:%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100, tp->tm_hour, tp->tm_min); return(buf); } char *PrintFlowSize(struct fileentry *fe) { static char buf[50]; char fullfile[200],line[200]; osFile os; struct osFileEntry *osfe; uint32_t files,bytes; files=0; bytes=0; MakeFullPath(cfg_Dir,fe->file,fullfile,200); if(!(os=osOpen(fullfile,MODE_OLDFILE))) { sprintf(buf,"?/?"); return(buf); } while(osFGets(os,line,200)) { strip(line); if(line[0]) { if(line[0] == '#' || line[0] == '^' || line[0] == '-') strcpy(line,&line[1]); if(stricmp(GetFilePart(line),line) == 0) { /* No path specified */ MakeFullPath(fe->dir,line,fullfile,200); osfe=osGetFileEntry(fullfile); } else { osfe=osGetFileEntry(line); } if(osfe) { files++; bytes+=osfe->Size; TotalFiles++; TotalBytes+=osfe->Size; osFree(osfe); } } } osClose(os); sprintf(buf,"%s/%u",unit(bytes),files); return(buf); } void DisplayFlowContents(struct fileentry *fe) { char size[40],*todo; char fullfile[200],line[200]; osFile os; struct osFileEntry *osfe; MakeFullPath(cfg_Dir,fe->file,fullfile,200); if(!(os=osOpen(fullfile,MODE_OLDFILE))) { printf("Failed to open file\n"); } else { while(osFGets(os,line,200)) { strip(line); if(line[0]) { todo=""; if(line[0] == '#' || line[0] == '^') todo="(To be truncated)"; if(line[0] == '-') todo="(To be deleted)"; if(line[0] == '#' || line[0] == '^' || line[0] == '-') strcpy(line,&line[1]); if(stricmp(GetFilePart(line),line) == 0) { /* No path specified */ MakeFullPath(fe->dir,line,fullfile,200); osfe=osGetFileEntry(fullfile); } else { osfe=osGetFileEntry(line); } strcpy(size,"Not found"); if(osfe) { sprintf(size, "%s", unit(osfe->Size)); osFree(osfe); } printf(" %-39.39s %10s %s\n",line,size,todo); } } osClose(os); } printf("\n"); } char *PrintReqNums(struct fileentry *fe) { static char buf[50]; char fullfile[200],line[200]; osFile os; uint32_t reqs; reqs=0; MakeFullPath(cfg_Dir,fe->file,fullfile,200); if(!(os=osOpen(fullfile,MODE_OLDFILE))) { sprintf(buf,"?/?"); return(buf); } while(osFGets(os,line,200)) { strip(line); if(line[0]) { reqs++; TotalRequests++; } } sprintf(buf,"-/%u",reqs); return(buf); } void DisplayReqContents(struct fileentry *fe) { char fullfile[200],line[200]; osFile os; MakeFullPath(cfg_Dir,fe->file,fullfile,200); if(!(os=osOpen(fullfile,MODE_OLDFILE))) { printf("Failed to open file\n"); } else { while(osFGets(os,line,200)) { strip(line); if(line[0]) printf(" %s\n",line); } osClose(os); } printf("\n"); } void DisplayFlow(struct fileentry *fe) { printf("%-8.8s %-17.17s %-16.16s %7.7s %s\n", type_names[fe->type],PrintNode(&fe->Node),PrintDate(fe->date),PrintFlowSize(fe),fe->file); if(cfg_Verbose) DisplayFlowContents(fe); } void DisplayPkt(struct fileentry *fe) { char buf[100]; sprintf(buf,"%s/1",unit(fe->size)); printf("%-8.8s %-17.17s %-16.16s %7.7s %s\n", type_names[fe->type],PrintNode(&fe->Node),PrintDate(fe->date),buf,fe->file); if(cfg_Verbose) printf("\n"); } void DisplayReq(struct fileentry *fe) { printf("%-8.8s %-17.17s %-16.16s %7.7s %s\n", type_names[fe->type],PrintNode(&fe->Node),PrintDate(fe->date),PrintReqNums(fe),fe->file); if(cfg_Verbose) DisplayReqContents(fe); } int sortcompare(const void *f1, const void *f2) { struct fileentry *e1,*e2; e1=*(struct fileentry **)f1; e2=*(struct fileentry **)f2; return Compare4D(&e1->Node,&e2->Node); } void sortlist(struct jbList *list) { struct jbNode *jb,**buf,**work; uint32_t count=0; for(jb=list->First;jb;jb=jb->Next) count++; if(count < 2) return; if(!(buf=(struct jbNode **)osAlloc(count * sizeof(struct jbNode *)))) return; work=buf; for(jb=list->First;jb;jb=jb->Next) *work++=jb; qsort(buf,(size_t)count,(size_t)sizeof(struct jbNode *),sortcompare); jbNewList(list); for(work=buf;count--;) jbAddNode(list,*work++); osFree(buf); } void addentry(char *dir,char *file,uint32_t type,struct Node4D *boss,bool flow) { struct osFileEntry *fe; struct fileentry *entry; struct Node4D n4d; char buf[200]; char buf2[200]; uint32_t hex; hex=hextodec(file); if(boss) { Copy4D(&n4d,boss); n4d.Point = hex; } else { n4d.Zone = cfg_Zone; n4d.Net = NET(hex); n4d.Node = NODE(hex); n4d.Point = 0; } if(Compare4DPat(&cfg_Pattern,&n4d)!=0) return; if(dir) MakeFullPath(dir,file,buf,200); else mystrncpy(buf,file,200); MakeFullPath(cfg_Dir,buf,buf2,200); if(!(fe=osGetFileEntry(buf2))) { return; } if(!(entry=osAlloc(sizeof(struct fileentry)))) { osFree(fe); return; } Copy4D(&entry->Node,&n4d); if(dir) { MakeFullPath(dir,file,entry->file,100); MakeFullPath(cfg_Dir,dir,entry->dir,100); } else { mystrncpy(entry->file,file,100); mystrncpy(entry->dir,cfg_Dir,100); } mystrncpy(entry->file,buf,100); entry->size=fe->Size; entry->date=fe->Date; entry->type=type; entry->flow=flow; jbAddNode(&list,(struct jbNode *)entry); osFree(fe); } struct Node4D *scandir_boss; char *scandir_dir; void scandirfunc(char *file) { char *extptr; uint32_t hex; if(strlen(file) != 12) return; extptr=&file[strlen(file)-4]; if(stricmp(extptr,".PNT")==0 && !scandir_boss) { char buf[200]; struct Node4D n4d; hex=hextodec(file); n4d.Zone = cfg_Zone; n4d.Net = NET(hex); n4d.Node = NODE(hex); n4d.Point = 0; scandir_dir = file; scandir_boss = &n4d; MakeFullPath(cfg_Dir,file,buf,200); osScanDir(buf,scandirfunc); scandir_dir = NULL; scandir_boss = NULL; } if(!stricmp(extptr,".REQ")) addentry(scandir_dir,file,TYPE_REQUEST,scandir_boss,TRUE); if(!stricmp(extptr,".CLO")) addentry(scandir_dir,file,TYPE_CRASH,scandir_boss,TRUE); if(!stricmp(extptr,".DLO")) addentry(scandir_dir,file,TYPE_DIRECT,scandir_boss,TRUE); if(!stricmp(extptr,".FLO")) addentry(scandir_dir,file,TYPE_NORMAL,scandir_boss,TRUE); if(!stricmp(extptr,".HLO")) addentry(scandir_dir,file,TYPE_HOLD,scandir_boss,TRUE); if(!stricmp(extptr,".CUT")) addentry(scandir_dir,file,TYPE_CRASH,scandir_boss,FALSE); if(!stricmp(extptr,".DUT")) addentry(scandir_dir,file,TYPE_DIRECT,scandir_boss,FALSE); if(!stricmp(extptr,".OUT")) addentry(scandir_dir,file,TYPE_NORMAL,scandir_boss,FALSE); if(!stricmp(extptr,".HUT")) addentry(scandir_dir,file,TYPE_HOLD,scandir_boss,FALSE); } int main(int argc, char **argv) { char *var; struct fileentry *fe; if(!osInit()) exit(OS_EXIT_ERROR); if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } jbNewList(&list); /* get outbound dir */ if((var=getenv("CMOUTBOUND"))) cfg_Dir=var; else cfg_Dir=OS_CURRENT_DIR; if(args[ARG_DIRECTORY].data) cfg_Dir=(char *)args[ARG_DIRECTORY].data; /* Get zone */ if((var=getenv("CMOUTBOUNDZONE"))) cfg_Zone=atoi(var); else cfg_Zone=2; if(args[ARG_ZONE].data) cfg_Zone=atoi((char *)args[ARG_ZONE].data); /* Get pattern */ strcpy(cfg_Pattern.Zone,"*"); strcpy(cfg_Pattern.Net,"*"); strcpy(cfg_Pattern.Node,"*"); strcpy(cfg_Pattern.Point,"*"); if(args[ARG_PATTERN].data) { if(!Parse4DPat((char *)args[ARG_PATTERN].data,&cfg_Pattern)) { printf("Invalid node pattern \"%s\"\n",(char *)args[ARG_PATTERN].data); osEnd(); exit(OS_EXIT_ERROR); } } /* Get verbose flag */ cfg_Verbose=FALSE; if(args[ARG_VERBOSE].data) cfg_Verbose=TRUE; /* Real program starts here */ printf("CrashListOut " VERSION "\n\n"); scandir_dir = NULL; scandir_boss = NULL; if(!osScanDir(cfg_Dir,scandirfunc)) { uint32_t err=osError(); printf("Failed to scan directory %s\n",cfg_Dir); printf("Error: %s",osErrorMsg(err)); return(FALSE); } sortlist(&list); printf("%-8.8s %-17.17s %-16.16s %7.7s %s\n\n", "Type","Node","Last Change","B/F","File"); if(list.First) { for(fe=(struct fileentry *)list.First;fe;fe=fe->Next) { if(fe->type == TYPE_REQUEST) DisplayReq(fe); else if(fe->flow) DisplayFlow(fe); else DisplayPkt(fe); } if(!cfg_Verbose) printf("\n"); printf("Totally %s bytes in %u files to send, %u requests.\n",unit(TotalBytes),TotalFiles,TotalRequests); } else { printf("Outbound directory is empty.\n"); } jbFreeList(&list); exit(OS_EXIT_OK); } crashmail-1.5/src/tools/crashmaint.c000066400000000000000000000523001230617144500175510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PLATFORM_AMIGA char *ver="$VER: CrashMaint "VERSION" ("__COMMODORE_DATE__")"; #endif static size_t ptrsize = sizeof(void *); struct Area { struct Area *Next; char Tagname[80]; char Path[80]; char Messagebase[20]; uint32_t KeepNum,KeepDays; }; struct jbList AreaList; struct Messagebase { char *Name; bool (*processfunc)(struct Area *area,bool maint,bool pack,bool verbose); }; #ifdef MSGBASE_MSG bool ProcessAreaMSG(struct Area *area,bool maint, bool pack, bool verbose); #endif #ifdef MSGBASE_JAM bool ProcessAreaJAM(struct Area *area,bool maint, bool pack, bool verbose); #endif struct Messagebase Messagebases[] = { #ifdef MSGBASE_JAM { "JAM", ProcessAreaJAM }, #endif #ifdef MSGBASE_MSG { "MSG", ProcessAreaMSG }, #endif { NULL, NULL } }; #define ARG_MAINT 0 #define ARG_PACK 1 #define ARG_VERBOSE 2 #define ARG_SETTINGS 3 #define ARG_PATTERN 4 struct argument args[] = { { ARGTYPE_BOOL, "MAINT", 0, NULL }, { ARGTYPE_BOOL, "PACK", 0, NULL }, { ARGTYPE_BOOL, "VERBOSE", 0, NULL }, { ARGTYPE_STRING, "SETTINGS", 0, NULL }, { ARGTYPE_STRING, "PATTERN", 0, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; bool ctrlc; void breakfunc(int x) { ctrlc=TRUE; } /******************** *.msg *********************/ #ifdef MSGBASE_MSG struct Msg { struct Msg *Next; uint32_t Num,NewNum,Day; }; struct jbList MsgList; int Compare(const void *a1,const void *a2) { struct Msg **m1,**m2; m1=(struct Msg **)a1; m2=(struct Msg **)a2; if((*m1)->Num > (*m2)->Num) return(1); if((*m1)->Num < (*m2)->Num) return(-1); return(0); } bool Sort(struct jbList *list) { struct Msg *msg,**buf,**work; uint32_t nc; nc=0; for(msg=(struct Msg *)list->First;msg;msg=msg->Next) nc++; if(nc == 0) return(TRUE); if(!(buf=(struct Msg **)osAlloc(nc*sizeof(struct StatsNode *)))) return(FALSE); work=buf; for(msg=(struct Msg *)list->First;msg;msg=msg->Next) *work++=msg; qsort(buf,nc,ptrsize,Compare); jbNewList(list); for(work=buf;nc--;) jbAddNode(list,(struct jbNode *)*work++); osFree(buf); return(TRUE); } void MakeFidoDate(time_t tim,char *dest) { struct tm *tp; time_t t; char *monthnames[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; t=tim; tp=localtime(&t); sprintf(dest,"%02d %s %02d %02d:%02d:%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec); } char *scanfuncarea; bool nomem; void scanfunc(char *str) { char buf[200]; struct osFileEntry *fe; uint32_t num,day; struct Msg *msg; if(strlen(str) < 5) return; if(stricmp(&str[strlen(str)-4],".msg")!=0) return; if(atol(str) < 2) return; MakeFullPath(scanfuncarea,str,buf,200); if(!(fe=osGetFileEntry(buf))) return; num=atol(str); day=fe->Date / (24*60*60); osFree(fe); if(!(msg=(struct Msg *)osAlloc(sizeof(struct Msg)))) { nomem=TRUE; return; } jbAddNode(&MsgList,(struct jbNode *)msg); msg->Num=num; msg->Day=day; } bool ProcessAreaMSG(struct Area *area,bool maint, bool pack, bool verbose) { uint32_t today,num,del,highwater,oldhighwater; struct Msg *msg; char buf[200],newbuf[200],buf2[100]; struct StoredMsg StoredMsg; osFile fh; highwater=0; oldhighwater=0; jbNewList(&MsgList); printf("Processing %s...\n",area->Tagname); scanfuncarea=area->Path; if(!(osScanDir(area->Path,scanfunc))) { uint32_t err=osError(); printf(" Error: Couldn't scan directory %s\n",area->Path); printf(" Error: %s\n",osErrorMsg(err)); jbFreeList(&MsgList); return(TRUE); } if(nomem) { printf("Out of memory\n"); jbFreeList(&MsgList); return(FALSE); } if(!Sort(&MsgList)) { printf("Out of memory\n"); jbFreeList(&MsgList); return(FALSE); } if(!MsgList.First) { printf(" Area is empty\n"); return(TRUE); } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } MakeFullPath(area->Path,"1.msg",buf,200); if((fh=osOpen(buf,MODE_OLDFILE))) { if(osRead(fh,&StoredMsg,sizeof(struct StoredMsg))==sizeof(struct StoredMsg)) { highwater=StoredMsg.ReplyTo; oldhighwater=StoredMsg.ReplyTo; } osClose(fh); } if(maint && area->KeepNum!=0) { num=0; for(msg=(struct Msg *)MsgList.First;msg;msg=msg->Next) num++; msg=(struct Msg *)MsgList.First; del=0; while(num>area->KeepNum && !ctrlc) { while(msg->Num==0) msg=msg->Next; sprintf(buf2,"%u.msg",msg->Num); MakeFullPath(area->Path,buf2,buf,200); if(msg->Num == highwater) highwater=0; if(verbose) printf(" Deleting message #%u by number\n",msg->Num); osDelete(buf); msg->Num=0; num--; del++; } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } printf(" %u messages deleted by number, %u messages left\n",del,num); } if(maint && area->KeepDays!=0) { del=0; num=0; today=time(NULL) / (24*60*60); for(msg=(struct Msg *)MsgList.First;msg && !ctrlc;msg=msg->Next) { if(today - msg->Day > area->KeepDays && msg->Num!=0) { sprintf(buf2,"%u.msg",msg->Num); MakeFullPath(area->Path,buf2,buf,200); if(msg->Num == highwater) highwater=0; if(verbose) printf(" Deleting message #%u by date\n",msg->Num); osDelete(buf); msg->Num=0; del++; } else { num++; } } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } printf(" %u messages deleted by date, %u messages left\n",del,num); } if(pack) { num=2; msg=(struct Msg *)MsgList.First; while(msg && !ctrlc) { while(msg && msg->Num==0) msg=msg->Next; if(msg) { msg->NewNum=num++; msg=msg->Next; } } for(msg=(struct Msg *)MsgList.First;msg && !ctrlc;msg=msg->Next) if(msg->Num!=0 && msg->Num!=msg->NewNum) { sprintf(buf2,"%u.msg",msg->Num); MakeFullPath(area->Path,buf2,buf,200); sprintf(buf2,"%u.msg",msg->NewNum); MakeFullPath(area->Path,buf2,newbuf,200); if(highwater == msg->Num) highwater=msg->NewNum; if(verbose) printf(" Renaming message %u to %u\n",msg->Num,msg->NewNum); osRename(buf,newbuf); } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } printf(" Area renumbered\n"); } jbFreeList(&MsgList); if(highwater!=oldhighwater) { strcpy(StoredMsg.From,"CrashMail II"); strcpy(StoredMsg.To,"All"); strcpy(StoredMsg.Subject,"HighWater mark"); MakeFidoDate(time(NULL),StoredMsg.DateTime); StoredMsg.TimesRead=0; StoredMsg.DestNode=0; StoredMsg.OrigNode=0; StoredMsg.Cost=0; StoredMsg.OrigNet=0; StoredMsg.DestNet=0; StoredMsg.DestZone=0; StoredMsg.OrigZone=0; StoredMsg.OrigPoint=0; StoredMsg.DestPoint=0; StoredMsg.ReplyTo=highwater; StoredMsg.Attr=FLAG_SENT | FLAG_PVT; StoredMsg.NextReply=0; MakeFullPath(area->Path,"1.msg",buf,200); if((fh=osOpen(buf,MODE_NEWFILE))) { osWrite(fh,&StoredMsg,sizeof(struct StoredMsg)); osWrite(fh,"",1); osClose(fh); } } return(TRUE); } #endif /*************************** JAM ************************/ #ifdef MSGBASE_JAM long jam_utcoffset = 0xbaadf00d; bool ProcessAreaJAM(struct Area *area,bool maint, bool pack, bool verbose) { uint32_t today,active,basenum,total,del,num,day; s_JamBase *Base_PS,*NewBase_PS; s_JamBaseHeader BaseHeader_S; s_JamMsgHeader Header_S; s_JamSubPacket* SubPacket_PS; int res,res1,res2; char buf[200],oldname[200],tmpname[200]; bool firstwritten; char *msgtext; /* Some timezone tricks */ if(jam_utcoffset == 0xbaadf00d) { time_t t1,t2; struct tm *tp; t1=time(NULL); tp=gmtime(&t1); tp->tm_isdst=-1; t2=mktime(tp); jam_utcoffset=t2-t1; } printf("Processing %s...\n",area->Tagname); if(JAM_OpenMB(area->Path,&Base_PS)) { printf(" Failed to open messagebase \"%s\"\n",area->Path); return(TRUE); } if(JAM_LockMB(Base_PS,10)) { printf(" Timeout when trying to lock messagebase \"%s\"\n",area->Path); JAM_CloseMB(Base_PS); return(TRUE); } if(JAM_ReadMBHeader(Base_PS,&BaseHeader_S)) { printf(" Failed to read header of messagebase \"%s\"\n",area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } if(JAM_GetMBSize(Base_PS,&total)) { printf(" Failed to get size of messagebase \"%s\"\n",area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } basenum=BaseHeader_S.BaseMsgNum; active=BaseHeader_S.ActiveMsgs; if(total == 0) { printf(" Area is empty\n"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } if(maint && area->KeepNum!=0) { num=0; del=0; while(num < total && active > area->KeepNum && !ctrlc) { res=JAM_ReadMsgHeader(Base_PS,num,&Header_S,NULL); if(res == 0) { /* Read success */ if(!(Header_S.Attribute & MSG_DELETED)) { /* Not already deleted */ if(verbose) printf(" Deleting message #%u by number\n",basenum+num); Header_S.Attribute |= MSG_DELETED; JAM_ChangeMsgHeader(Base_PS,num,&Header_S); BaseHeader_S.ActiveMsgs--; JAM_WriteMBHeader(Base_PS,&BaseHeader_S); active--; del++; } } num++; } if(ctrlc) { JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } printf(" %u messages deleted by number, %u messages left\n",del,active); } if(maint && area->KeepDays!=0) { del=0; num=0; today=(time(NULL)-jam_utcoffset) / (24*60*60); while(num < total && !ctrlc) { res=JAM_ReadMsgHeader(Base_PS,num,&Header_S,NULL); if(res == 0) { /* Read success */ day=Header_S.DateReceived / (24*60*60); if(day == 0) day=Header_S.DateProcessed / (24*60*60); if(day == 0) day=Header_S.DateWritten / (24*60*60); if(today-day > area->KeepDays && !(Header_S.Attribute & MSG_DELETED)) { /* Not already deleted and too old*/ if(verbose) printf(" Deleting message #%u by date\n",basenum+num); Header_S.Attribute |= MSG_DELETED; JAM_ChangeMsgHeader(Base_PS,num,&Header_S); BaseHeader_S.ActiveMsgs--; JAM_WriteMBHeader(Base_PS,&BaseHeader_S); del++; active--; } } num++; } if(ctrlc) { JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } printf(" %u messages deleted by date, %u messages left\n",del,active); } if(pack) { strcpy(buf,area->Path); strcat(buf,".cmtemp"); if(JAM_CreateMB(buf,1,&NewBase_PS)) { printf(" Failed to create new messagebase \"%s\"\n",buf); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } if(JAM_LockMB(NewBase_PS,10)) { printf(" Timeout when trying to lock messagebase \"%s\"\n",buf); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } /* Copy messages */ del=0; num=0; firstwritten=FALSE; BaseHeader_S.ActiveMsgs=0; while(num < total && !ctrlc) { res=JAM_ReadMsgHeader(Base_PS,num,&Header_S,NULL); if(res) { if(res == JAM_NO_MESSAGE) { if(firstwritten) { JAM_AddEmptyMessage(NewBase_PS); } else { BaseHeader_S.BaseMsgNum++; del++; } } else { printf(" Failed to read message %d, cannot pack messagebase\n",num+basenum); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } } else { if(Header_S.Attribute & MSG_DELETED) { if(firstwritten) { JAM_AddEmptyMessage(NewBase_PS); } else { BaseHeader_S.BaseMsgNum++; del++; } } else { if(!firstwritten) { /* Set basenum */ res=JAM_WriteMBHeader(NewBase_PS,&BaseHeader_S); if(res) { printf(" Failed to write messagebase header, cannot pack messagebase\n"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } firstwritten=TRUE; } /* Read header with all subpackets*/ res=JAM_ReadMsgHeader(Base_PS,num,&Header_S,&SubPacket_PS); if(res) { printf(" Failed to read message %d, cannot pack messagebase\n",num+basenum); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } /* Read message text */ msgtext=NULL; if(Header_S.TxtLen) { if(!(msgtext=osAlloc(Header_S.TxtLen))) { printf("Out of memory\n"); JAM_DelSubPacket(SubPacket_PS); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(FALSE); } res=JAM_ReadMsgText(Base_PS,Header_S.TxtOffset,Header_S.TxtLen,msgtext); if(res) { printf(" Failed to read message %d, cannot pack messagebase\n",num+basenum); JAM_DelSubPacket(SubPacket_PS); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } } /* Write new message */ res=JAM_AddMessage(NewBase_PS,&Header_S,SubPacket_PS,msgtext,Header_S.TxtLen); if(msgtext) osFree(msgtext); JAM_DelSubPacket(SubPacket_PS); BaseHeader_S.ActiveMsgs++; if(res) { printf(" Failed to copy message %d (disk full?), cannot pack messagebase\n",num+basenum); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } } } num++; } /* Write back header */ BaseHeader_S.ModCounter++; res=JAM_WriteMBHeader(NewBase_PS,&BaseHeader_S); if(res) { printf(" Failed to write messagebase header, cannot pack messagebase\n"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); if(ctrlc) { JAM_RemoveMB(NewBase_PS,buf); return(TRUE); } /* This could not be done with JAMLIB... */ sprintf(oldname,"%s%s",area->Path,EXT_HDRFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_HDRFILE); res1=osDelete(oldname); res2=osRename(tmpname,oldname); if(res1 && res2) { sprintf(oldname,"%s%s",area->Path,EXT_TXTFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_TXTFILE); res1=osDelete(oldname); res2=osRename(tmpname,oldname); } if(res1 && res2) { sprintf(oldname,"%s%s",area->Path,EXT_IDXFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_IDXFILE); res1=osDelete(oldname); res2=osRename(tmpname,oldname); } if(res1 && res2) { sprintf(oldname,"%s%s",area->Path,EXT_LRDFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_LRDFILE); /* Keep lastread file */ res2=osDelete(tmpname); } if(!res1 || !res2) { printf(" Failed to update area. The area might be in use by another program.\n"); return(FALSE); } printf(" %d deleted messages removed from messagebase\n",del); } else { JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); } return(TRUE); } #endif /************************** end of messagebases *******************/ char cfgbuf[4000]; bool ReadConfig(char *file) { osFile fh; char cfgword[20]; char tag[80],aka[80],path[80],mb[20]; struct Area *tmparea,*LastArea; uint32_t jbcpos; if(!(fh=osOpen(file,MODE_OLDFILE))) { uint32_t err=osError(); printf("Failed to open file %s for reading\n",file); printf("Error: %s\n",osErrorMsg(err)); return(FALSE); } LastArea=NULL; while(osFGets(fh,cfgbuf,4000)) { jbcpos=0; jbstrcpy(cfgword,cfgbuf,20,&jbcpos); if(stricmp(cfgword,"KEEPDAYS")==0 && LastArea) { if(jbstrcpy(tag,cfgbuf,80,&jbcpos)) LastArea->KeepDays=atol(tag); } if(stricmp(cfgword,"KEEPNUM")==0 && LastArea) { if(jbstrcpy(tag,cfgbuf,80,&jbcpos)) LastArea->KeepNum=atol(tag); } if(stricmp(cfgword,"AREA")==0 || stricmp(cfgword,"NETMAIL")==0 || stricmp(cfgword,"LOCALAREA")==0) { jbstrcpy(tag,cfgbuf,80,&jbcpos); jbstrcpy(aka,cfgbuf,80,&jbcpos); if(stricmp(tag,"DEFAULT")!=0 && strnicmp(tag,"DEFAULT_",8)!=0) { if(jbstrcpy(mb,cfgbuf,20,&jbcpos)) { jbstrcpy(path,cfgbuf,80,&jbcpos); if(!(tmparea=(struct Area *)osAllocCleared(sizeof(struct Area)))) { printf("Out of memory\n"); osClose(fh); return(FALSE); } jbAddNode(&AreaList,(struct jbNode *)tmparea); LastArea=tmparea; strcpy(tmparea->Tagname,tag); strcpy(tmparea->Messagebase,mb); strcpy(tmparea->Path,path); } } } } osClose(fh); return(TRUE); } int main(int argc, char **argv) { struct Area *area; char *cfg; bool maint,pack,verbose; int i; signal(SIGINT,breakfunc); if(!osInit()) exit(OS_EXIT_ERROR); if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } jbNewList(&AreaList); maint=FALSE; pack=FALSE; verbose=FALSE; if(args[ARG_MAINT].data) maint=TRUE; if(args[ARG_PACK].data) pack=TRUE; if(args[ARG_VERBOSE].data) verbose=TRUE; if(!maint && !pack) { printf("Nothing to do.\n"); osEnd(); exit(OS_EXIT_OK); } if(args[ARG_PATTERN].data) { if(!(osCheckPattern((char *)args[ARG_PATTERN].data))) { printf("Invalid pattern \"%s\"\n",(char *)args[ARG_PATTERN].data); osEnd(); exit(OS_EXIT_ERROR); } } cfg=getenv(OS_CONFIG_VAR); if(!cfg) cfg=OS_CONFIG_NAME; if(args[ARG_SETTINGS].data) cfg=(char *)args[ARG_SETTINGS].data; if(!(ReadConfig(cfg))) { jbFreeList(&AreaList); osEnd(); exit(OS_EXIT_ERROR); } for(area=(struct Area *)AreaList.First;area && !ctrlc;area=area->Next) { bool match; match=FALSE; if(!args[ARG_PATTERN].data) match=TRUE; else if(osMatchPattern((char *)args[ARG_PATTERN].data,area->Tagname)) match=TRUE; if(match) { for(i=0;Messagebases[i].Name;i++) if(stricmp(Messagebases[i].Name,area->Messagebase)==0) break; if(Messagebases[i].processfunc) { if(!Messagebases[i].processfunc(area,maint,pack,verbose)) exit(OS_EXIT_ERROR); } else { printf("Cannot process area %s, messagebase %s not supported by CrashMaint\n", area->Tagname, area->Messagebase); } } } if(ctrlc) printf("*** User Break ***\n"); jbFreeList(&AreaList); osEnd(); exit(OS_EXIT_OK); } crashmail-1.5/src/tools/crashstats.c000066400000000000000000000413561230617144500176100ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define COPYRIGHT "1998,2013" #ifdef PLATFORM_AMIGA char *ver="$VER: CrashStats "VERSION" ("__COMMODORE_DATE__")"; #endif #define STATS_IDENTIFIER "CST3" static size_t ptrsize = sizeof(void *); struct DiskAreaStats { char Tagname[80]; struct Node4D Aka; char Group; char fill_to_make_even; /* Just ignore this one */ uint32_t TotalTexts; uint16_t Last8Days[8]; uint32_t Dupes; time_t FirstTime; time_t LastTime; }; struct DiskNodeStats { struct Node4D Node; uint32_t GotNetmails; uint32_t GotNetmailBytes; uint32_t SentNetmails; uint32_t SentNetmailBytes; uint32_t GotEchomails; uint32_t GotEchomailBytes; uint32_t SentEchomails; uint32_t SentEchomailBytes; uint32_t Dupes; time_t FirstTime; }; struct StatsNode { struct StatsNode *Next; char Tagname[80]; uint32_t Average; uint32_t Total; uint32_t Dupes; time_t FirstTime; time_t LastTime; uint16_t Last8Days[8]; }; struct NodeStatsNode { struct NodeStatsNode *Next; struct Node4D Node; uint32_t GotNetmails; uint32_t GotNetmailBytes; uint32_t SentNetmails; uint32_t SentNetmailBytes; uint32_t GotEchomails; uint32_t GotEchomailBytes; uint32_t SentEchomails; uint32_t SentEchomailBytes; uint32_t Dupes; uint32_t Days; time_t FirstTime; }; #define ARG_FILE 0 #define ARG_SORT 1 #define ARG_LAST7 2 #define ARG_NOAREAS 3 #define ARG_NONODES 4 #define ARG_GROUP 5 struct argument args[] = { { ARGTYPE_STRING, "FILE", ARGFLAG_AUTO | ARGFLAG_MANDATORY, NULL }, { ARGTYPE_STRING, "SORT", 0, NULL }, { ARGTYPE_BOOL, "LAST7", 0, NULL }, { ARGTYPE_BOOL, "NOAREAS", 0, NULL }, { ARGTYPE_BOOL, "NONODES", 0, NULL }, { ARGTYPE_STRING, "GROUP", 0, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; bool diskfull; int CompareAlpha(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; return(stricmp((*s1)->Tagname,(*s2)->Tagname)); } int CompareTotal(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; if((*s1)->Total < (*s2)->Total) return(1); if((*s1)->Total > (*s2)->Total) return(-1); return(0); } int CompareDupes(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; if((*s1)->Dupes < (*s2)->Dupes) return(1); if((*s1)->Dupes > (*s2)->Dupes) return(-1); return(0); } int CompareMsgsDay(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; if((*s1)->Average < (*s2)->Average) return(1); if((*s1)->Average > (*s2)->Average) return(-1); return(0); } int CompareFirstTime(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; if((*s1)->FirstTime < (*s2)->FirstTime) return(1); if((*s1)->FirstTime > (*s2)->FirstTime) return(-1); return(0); } int CompareLastTime(const void *a1,const void *a2) { struct StatsNode **s1,**s2; s1=(struct StatsNode **)a1; s2=(struct StatsNode **)a2; if((*s1)->LastTime < (*s2)->LastTime) return(1); if((*s1)->LastTime > (*s2)->LastTime) return(-1); return(0); } bool Sort(struct jbList *list,char sortmode) { uint32_t nc; struct StatsNode *sn,**buf,**work; nc=0; for(sn=(struct StatsNode *)list->First;sn;sn=sn->Next) nc++; if(nc==0) return(TRUE); /* Nothing to sort */ if(!(buf=(struct StatsNode **)osAlloc(nc*sizeof(struct StatsNode *)))) return(FALSE); work=buf; for(sn=(struct StatsNode *)list->First;sn;sn=sn->Next) *work++=sn; switch(sortmode) { case 'a': qsort(buf,nc,ptrsize,CompareAlpha); break; case 't': qsort(buf,nc,ptrsize,CompareTotal); break; case 'm': qsort(buf,nc,ptrsize,CompareMsgsDay); break; case 'd': qsort(buf,nc,ptrsize,CompareFirstTime); break; case 'l': qsort(buf,nc,ptrsize,CompareLastTime); break; case 'u': qsort(buf,nc,ptrsize,CompareDupes); break; } jbNewList(list); for(work=buf;nc--;) jbAddNode(list,(struct jbNode *)*work++); osFree(buf); return(TRUE); } int CompareNodes(const void *a1,const void *a2) { struct NodeStatsNode **s1,**s2; s1=(struct NodeStatsNode **)a1; s2=(struct NodeStatsNode **)a2; return(Compare4D(&(*s1)->Node,&(*s2)->Node)); } bool SortNodes(struct jbList *list) { struct NodeStatsNode *sn,**buf,**work; uint32_t nc; nc=0; for(sn=(struct NodeStatsNode *)list->First;sn;sn=sn->Next) nc++; if(nc==0) return(TRUE); /* Nothing to sort */ if(!(buf=(struct NodeStatsNode **)osAlloc(nc*sizeof(struct NodeStatsNode *)))) return(FALSE); work=buf; for(sn=(struct NodeStatsNode *)list->First;sn;sn=sn->Next) *work++=sn; qsort(buf,nc,ptrsize,CompareNodes); jbNewList(list); for(work=buf;nc--;) jbAddNode(list,(struct jbNode *)*work++); osFree(buf); return(TRUE); } char *unit(uint32_t i) { static char buf[40]; if ((i>10000000)||(i<-10000000)) sprintf(buf,"%d MB",i/(1024*1024)); else if ((i>10000)||(i<-10000)) sprintf(buf,"%d KB",i/1024); else sprintf(buf,"%d bytes",i); return buf; } bool CheckFlags(char group,char *node) { int c; for(c=0;c 7) days=7; sum=0; for(c=1;c 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } sortmode='a'; if(args[ARG_SORT].data) sortmode=tolower(((char *)args[ARG_SORT].data)[0]); if(!strchr("amtdlu",sortmode)) { printf("Unknown sort mode %c\n",sortmode); osEnd(); exit(OS_EXIT_ERROR); } if(args[ARG_NOAREAS].data && args[ARG_NONODES].data) { printf("Nothing to do\n"); osEnd(); exit(OS_EXIT_ERROR); } printf("CrashStats "VERSION" © " COPYRIGHT " Johan Billing & Robert James Clay\n"); if(!(fh=osOpen(args[ARG_FILE].data,MODE_OLDFILE))) { uint32_t err=osError(); printf("Error opening %s\n",(char *)args[ARG_FILE].data); printf("Error: %s\n",osErrorMsg(err)); osEnd(); exit(OS_EXIT_ERROR); } osRead(fh,buf,4); buf[4]=0; if(strcmp(buf,STATS_IDENTIFIER)!=0) { printf("Unknown format of stats file\n"); osClose(fh); osEnd(); exit(OS_EXIT_ERROR); } osRead(fh,&DayStatsWritten,sizeof(uint32_t)); total=0; totaldupes=0; firsttime=0; areas=0; for(c=0;c<8;c++) total8days[c]=0; jbNewList(&StatsList); jbNewList(&NodesList); osRead(fh,&num,sizeof(uint32_t)); c=0; if(!args[ARG_NOAREAS].data) { while(cTagname,dastat.Tagname); sn->Dupes=dastat.Dupes; sn->Total=dastat.TotalTexts; sn->FirstTime=dastat.FirstTime; sn->LastTime=dastat.LastTime; memcpy(&sn->Last8Days[0],&dastat.Last8Days[0],8*sizeof(uint16_t)); sn->Average=CalculateAverage(&dastat.Last8Days[0],dastat.TotalTexts,DayStatsWritten,sn->FirstTime / (24*60*60)); } if(dastat.FirstTime!=0) if(firsttime==0 || firsttime > dastat.FirstTime) firsttime=dastat.FirstTime; c++; } } else { while(cNode,&dnstat.Node); nsn->GotNetmails=dnstat.GotNetmails; nsn->GotNetmailBytes=dnstat.GotNetmailBytes; nsn->SentNetmails=dnstat.SentNetmails; nsn->SentNetmailBytes=dnstat.SentNetmailBytes; nsn->GotEchomails=dnstat.GotEchomails; nsn->GotEchomailBytes=dnstat.GotEchomailBytes; nsn->SentEchomails=dnstat.SentEchomails; nsn->SentEchomailBytes=dnstat.SentEchomailBytes; nsn->Dupes=dnstat.Dupes; nsn->Days=DayStatsWritten-dnstat.FirstTime % (24*60*60); if(nsn->Days==0) nsn->Days=1; nsn->FirstTime=dnstat.FirstTime; if(dnstat.FirstTime!=0) if(firsttime==0 || firsttime > dnstat.FirstTime) firsttime=dnstat.FirstTime; c++; } } else { while(ctm_mday,monthnames[tp->tm_mon],tp->tm_year%100); tp=localtime(&t); sprintf(date2,"%02d-%s-%02d",tp->tm_mday,monthnames[tp->tm_mon],tp->tm_year%100); printf("\nStatistics from %s to %s\n",date,date2); if(!ctrlc && !args[ARG_NOAREAS].data) { Sort(&StatsList,'a'); Sort(&StatsList,sortmode); printf("\n"); if(args[ARG_LAST7].data) { printf("Area "); for(c=1;c<8;c++) { t=(DayStatsWritten-c)*24*60*60; tp=localtime(&t); printf(" %02d",tp->tm_mday); } printf(" Total\n============================================================================\n"); if(!ctrlc) { for(sn=(struct StatsNode *)StatsList.First;sn && !ctrlc;sn=sn->Next) { tot=0; for(c=1;c<8;c++) tot+=sn->Last8Days[c]; printf("%-33.33s %4d %4d %4d %4d %4d %4d %4d : %5d\n", sn->Tagname, sn->Last8Days[1], sn->Last8Days[2], sn->Last8Days[3], sn->Last8Days[4], sn->Last8Days[5], sn->Last8Days[6], sn->Last8Days[7], tot); for(c=1;c<8;c++) total8days[c]+=sn->Last8Days[c]; areas++; } if(!ctrlc) { tot=0; for(c=1;c<8;c++) tot+=total8days[c]; printf("=============================================================================\n"); sprintf(buf,"Totally in all %u areas",areas); printf("%-33.33s %4d %4d %4d %4d %4d %4d %4d : %5d\n", buf, total8days[1], total8days[2], total8days[3], total8days[4], total8days[5], total8days[6], total8days[7], tot); } } } else { printf("Area First Last Msgs Msgs/day Dupes\n"); printf("============================================================================\n"); if(!ctrlc) { for(sn=(struct StatsNode *)StatsList.First;sn && !ctrlc;sn=sn->Next) { if(sn->LastTime==0) { strcpy(date2,""); } else { tp=localtime(&sn->LastTime); sprintf(date2,"%02d-%s-%02d",tp->tm_mday,monthnames[tp->tm_mon],tp->tm_year%100); } if(sn->FirstTime==0) { strcpy(date,""); } else { tp=localtime(&sn->FirstTime); sprintf(date,"%02d-%s-%02d",tp->tm_mday,monthnames[tp->tm_mon],tp->tm_year%100); } for(c=0;c<8;c++) total8days[c]+=sn->Last8Days[c]; total+=sn->Total; totaldupes+=sn->Dupes; areas++; printf("%-29.30s %-9.9s %-9.9s %7d %7d %7d\n",sn->Tagname,date,date2,sn->Total,sn->Average,sn->Dupes); } } if(!ctrlc) { printf("============================================================================\n"); sprintf(buf,"Totally in all %u areas",areas); printf("%-42s %7d %7d %7d\n", buf, total, CalculateAverage(&total8days[0],total,DayStatsWritten,firsttime / (24*60*60)), totaldupes); } } } if(!ctrlc && !args[ARG_NONODES].data) { SortNodes(&NodesList); printf("\n"); printf("Nodes statistics\n"); printf("================\n"); for(nsn=(struct NodeStatsNode *)NodesList.First;nsn && !ctrlc;nsn=nsn->Next) { if(nsn->FirstTime==0) { strcpy(date,""); } else { tp=localtime(&nsn->FirstTime); sprintf(date,"%0d-%s-%0d",tp->tm_mday,monthnames[tp->tm_mon],tp->tm_year%100); } sprintf(buf,"%u:%u/%u.%u",nsn->Node.Zone,nsn->Node.Net,nsn->Node.Node,nsn->Node.Point); printf("%-30.40s Statistics since: %s\n\n",buf,date); printf(" Sent netmails: %u/%s\n",nsn->SentNetmails,unit(nsn->SentNetmailBytes)); printf(" Received netmails: %u/%s\n",nsn->GotNetmails,unit(nsn->GotNetmailBytes)); printf(" Sent echomails: %u/%s\n",nsn->SentEchomails,unit(nsn->SentEchomailBytes)); printf(" Received echomails: %u/%s\n",nsn->GotEchomails,unit(nsn->GotEchomailBytes)); printf(" Dupes: %u\n",nsn->Dupes); printf("\n"); } } if(ctrlc) { printf("*** Break\n"); } else { printf("\n"); } jbFreeList(&StatsList); jbFreeList(&NodesList); osEnd(); exit(OS_EXIT_OK); } crashmail-1.5/src/tools/crashwrite.c000066400000000000000000000231751230617144500176030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PLATFORM_AMIGA char *ver="$VER: CrashWrite "VERSION" ("__COMMODORE_DATE__")"; #endif #define DEFAULT_TONAME "All" #define DEFAULT_FROMNAME "CrashWrite" #define DEFAULT_SUBJECT "Information" #define DEFAULT_ORIGIN "Another user of CrashMail II" #define ARG_FROMNAME 0 #define ARG_FROMADDR 1 #define ARG_TONAME 2 #define ARG_TOADDR 3 #define ARG_SUBJECT 4 #define ARG_AREA 5 #define ARG_ORIGIN 6 #define ARG_DIR 7 #define ARG_TEXT 8 #define ARG_NOMSGID 9 #define ARG_FILEATTACH 10 #define ARG_PKTFROMADDR 11 #define ARG_PKTTOADDR 12 #define ARG_PASSWORD 13 struct argument args[] = { { ARGTYPE_STRING, "FROMNAME", 0, NULL }, { ARGTYPE_STRING, "FROMADDR", 0, NULL }, { ARGTYPE_STRING, "TONAME", 0, NULL }, { ARGTYPE_STRING, "TOADDR", 0, NULL }, { ARGTYPE_STRING, "SUBJECT", 0, NULL }, { ARGTYPE_STRING, "AREA", 0, NULL }, { ARGTYPE_STRING, "ORIGIN", 0, NULL }, { ARGTYPE_STRING, "DIR", ARGFLAG_MANDATORY, NULL }, { ARGTYPE_STRING, "TEXT", 0, NULL }, { ARGTYPE_BOOL, "NOMSGID", 0, NULL }, { ARGTYPE_BOOL, "FILEATTACH", 0, NULL }, { ARGTYPE_STRING, "PKTFROMADDR", 0, NULL }, { ARGTYPE_STRING, "PKTTOADDR", 0, NULL }, { ARGTYPE_STRING, "PASSWORD", 0, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; char PktMsgHeader[SIZE_PKTMSGHEADER]; char PktHeader[SIZE_PKTHEADER]; bool nomem,diskfull; uint16_t getuword(char *buf,uint32_t offset) { return (uint16_t)(buf[offset]+256*buf[offset+1]); } void putuword(char *buf,uint32_t offset,uint16_t num) { buf[offset]=num%256; buf[offset+1]=num/256; } void MakeFidoDate(time_t tim,char *dest) { struct tm *tp; time_t t; char *monthnames[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; t=tim; tp=localtime(&t); sprintf(dest,"%02d %s %02d %02d:%02d:%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec); } void WriteNull(osFile ofh,char *str) { osWrite(ofh,str,(uint32_t)(strlen(str)+1)); } int main(int argc, char **argv) { struct Node4D from4d,to4d,pktfrom4d,pktto4d; osFile ifh,ofh; time_t t; struct tm *tp; uint32_t pktnum,c,serial; uint16_t attr; char fromname[36],toname[36],subject[72],datetime[20],origin[80]; char pktname[30],fullname[200],readbuf[100]; int i; from4d.Zone=0; from4d.Net=0; from4d.Node=0; from4d.Point=0; to4d.Zone=0; to4d.Net=0; to4d.Node=0; to4d.Point=0; if(!osInit()) exit(OS_EXIT_ERROR); if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } if(args[ARG_FROMADDR].data) { if(!(Parse4D((char *)args[ARG_FROMADDR].data,&from4d))) { printf("Invalid address \"%s\"\n",(char *)args[ARG_FROMADDR].data); osEnd(); exit(OS_EXIT_ERROR); } } if(args[ARG_TOADDR].data) { if(!(Parse4D((char *)args[ARG_TOADDR].data,&to4d))) { printf("Invalid address \"%s\"\n",(char *)args[ARG_TOADDR].data); osEnd(); exit(OS_EXIT_ERROR); } } Copy4D(&pktfrom4d,&from4d); Copy4D(&pktto4d,&to4d); if(args[ARG_PKTFROMADDR].data) { if(!(Parse4D((char *)args[ARG_PKTFROMADDR].data,&pktfrom4d))) { printf("Invalid address \"%s\"\n",(char *)args[ARG_PKTFROMADDR].data); osEnd(); exit(OS_EXIT_ERROR); } } if(args[ARG_PKTTOADDR].data) { if(!(Parse4D((char *)args[ARG_PKTTOADDR].data,&pktto4d))) { printf("Invalid address \"%s\"\n",(char *)args[ARG_PKTTOADDR].data); osEnd(); exit(OS_EXIT_ERROR); } } time(&t); tp=localtime(&t); /* Create packet header */ putuword(PktHeader,PKTHEADER_ORIGNODE,pktfrom4d.Node); putuword(PktHeader,PKTHEADER_DESTNODE,pktto4d.Node); putuword(PktHeader,PKTHEADER_DAY,tp->tm_mday); putuword(PktHeader,PKTHEADER_MONTH,tp->tm_mon); putuword(PktHeader,PKTHEADER_YEAR,tp->tm_year+1900); putuword(PktHeader,PKTHEADER_HOUR,tp->tm_hour); putuword(PktHeader,PKTHEADER_MINUTE,tp->tm_min); putuword(PktHeader,PKTHEADER_SECOND,tp->tm_sec); putuword(PktHeader,PKTHEADER_BAUD,0); putuword(PktHeader,PKTHEADER_PKTTYPE,2); putuword(PktHeader,PKTHEADER_ORIGNET,pktfrom4d.Net); putuword(PktHeader,PKTHEADER_DESTNET,pktto4d.Net); PktHeader[PKTHEADER_PRODCODELOW]=0xfe; PktHeader[PKTHEADER_REVMAJOR]=VERSION_MAJOR; putuword(PktHeader,PKTHEADER_QORIGZONE,pktfrom4d.Zone); putuword(PktHeader,PKTHEADER_QDESTZONE,pktto4d.Zone); putuword(PktHeader,PKTHEADER_AUXNET,0); putuword(PktHeader,PKTHEADER_CWVALIDCOPY,0x0100); PktHeader[PKTHEADER_PRODCODEHIGH]=0; PktHeader[PKTHEADER_REVMINOR]=VERSION_MINOR; putuword(PktHeader,PKTHEADER_CAPABILWORD,0x0001); putuword(PktHeader,PKTHEADER_ORIGZONE,pktfrom4d.Zone); putuword(PktHeader,PKTHEADER_DESTZONE,pktto4d.Zone); putuword(PktHeader,PKTHEADER_ORIGPOINT,pktfrom4d.Point); putuword(PktHeader,PKTHEADER_DESTPOINT,pktto4d.Point); PktHeader[PKTHEADER_PRODDATA]=0; PktHeader[PKTHEADER_PRODDATA+1]=0; PktHeader[PKTHEADER_PRODDATA+2]=0; PktHeader[PKTHEADER_PRODDATA+3]=0; for(c=0;c<8;c++) PktHeader[PKTHEADER_PASSWORD+c]=0; if(args[ARG_PASSWORD].data) strncpy(&PktHeader[PKTHEADER_PASSWORD],args[ARG_PASSWORD].data,8); /* Create message header */ attr=0; if(!args[ARG_AREA].data) attr|=FLAG_PVT; if(args[ARG_FILEATTACH].data) attr|=FLAG_FILEATTACH; putuword(PktMsgHeader,PKTMSGHEADER_PKTTYPE,0x0002); putuword(PktMsgHeader,PKTMSGHEADER_ORIGNODE,from4d.Node); putuword(PktMsgHeader,PKTMSGHEADER_DESTNODE,to4d.Node); putuword(PktMsgHeader,PKTMSGHEADER_ORIGNET,from4d.Net); putuword(PktMsgHeader,PKTMSGHEADER_DESTNET,to4d.Net); putuword(PktMsgHeader,PKTMSGHEADER_ATTR,attr); putuword(PktMsgHeader,PKTMSGHEADER_COST,0); mystrncpy(fromname,DEFAULT_FROMNAME,36); mystrncpy(toname,DEFAULT_TONAME,36); mystrncpy(subject,DEFAULT_SUBJECT,72); mystrncpy(origin,DEFAULT_ORIGIN,80); if(args[ARG_FROMNAME].data) mystrncpy(fromname,(char *)args[ARG_FROMNAME].data,36); if(args[ARG_TONAME].data) mystrncpy(toname,(char *)args[ARG_TONAME].data,36); if(args[ARG_SUBJECT].data) mystrncpy(subject,(char *)args[ARG_SUBJECT].data,72); if(args[ARG_ORIGIN].data) mystrncpy(origin,(char *)args[ARG_ORIGIN].data,80); MakeFidoDate(t,datetime); /* Create pkt file */ serial=0; do { t=time(NULL); pktnum = (t<<8) + serial; serial++; sprintf(pktname,"%08x.pkt",pktnum); MakeFullPath(args[ARG_DIR].data,pktname,fullname,200); } while(osExists(fullname)); if(!(ofh=osOpen(fullname,MODE_NEWFILE))) { uint32_t err=osError(); printf("Unable to create packet %s\n",fullname); printf("Error: %s\n",osErrorMsg(err)); osEnd(); exit(OS_EXIT_ERROR); } printf("Writing...\n"); printf(" From: %-36s (%u:%u/%u.%u)\n",fromname,from4d.Zone,from4d.Net,from4d.Node,from4d.Point); printf(" To: %-36s (%u:%u/%u.%u)\n",toname,to4d.Zone,to4d.Net,to4d.Node,to4d.Point); printf(" Subj: %s\n",subject); printf(" Date: %s\n",datetime); osWrite(ofh,PktHeader,SIZE_PKTHEADER); osWrite(ofh,PktMsgHeader,SIZE_PKTMSGHEADER); WriteNull(ofh,datetime); WriteNull(ofh,toname); WriteNull(ofh,fromname); WriteNull(ofh,subject); if(args[ARG_AREA].data) { osFPrintf(ofh,"AREA:%s\x0d",args[ARG_AREA].data); } else { if(from4d.Point) osFPrintf(ofh,"\x01" "FMPT %d\x0d",from4d.Point); if(to4d.Point) osFPrintf(ofh,"\x01" "TOPT %d\x0d",to4d.Point); osFPrintf(ofh,"\x01" "INTL %u:%u/%u %u:%u/%u\x0d",to4d.Zone,to4d.Net,to4d.Node, from4d.Zone,from4d.Net,from4d.Node); } if(!args[ARG_NOMSGID].data) { osFPrintf(ofh,"\x01" "MSGID: %u:%u/%u.%u %08x\x0d", from4d.Zone,from4d.Net,from4d.Node,from4d.Point,pktnum); } if(args[ARG_TEXT].data) { printf("Appending %s...\n",(char *)args[ARG_TEXT].data); if(!(ifh=osOpen((char *)args[ARG_TEXT].data,MODE_OLDFILE))) { uint32_t err=osError(); printf("Unable to open \"%s\" for reading\n",(char *)args[ARG_TEXT].data); printf("Error: %s\n",osErrorMsg(err)); osClose(ofh); osDelete(fullname); exit(OS_EXIT_ERROR); } while(osFGets(ifh,readbuf,100)) { for(i=0;readbuf[i];i++) if(readbuf[i] == '\n') readbuf[i]=0x0d; osFPrintf(ofh,"%s",readbuf); } osClose(ifh); } if(args[ARG_AREA].data) { osFPrintf(ofh,"--- CrashWrite II/" OS_PLATFORM_NAME " " VERSION "\x0d"); osFPrintf(ofh," * Origin: %s (%u:%u/%u.%u)\x0d",origin,from4d.Zone,from4d.Net,from4d.Node,from4d.Point); } osWrite(ofh,"",1); osWrite(ofh,"",1); osWrite(ofh,"",1); osClose(ofh); osEnd(); exit(OS_EXIT_OK); } crashmail-1.5/tests/000077500000000000000000000000001230617144500144675ustar00rootroot00000000000000crashmail-1.5/tests/01-nodelist-test.sh000066400000000000000000000011271230617144500200400ustar00rootroot00000000000000#!/bin/sh describe "nodelist" . ./testcommon.sh before () { setup_crashmail_env } after () { clean_crashmail_env } it_generates_nodelist_index () { $__tools__/crashlist nodelist && test -f nodelist/cmnodelist.index } it_finds_a_node () { $__tools__/crashlist nodelist $__tools__/crashgetnode 99:99/99 nodelist > crashgetnode.output diff crashgetnode.output $__topdir__/crashgetnode-node.expected } it_finds_a_point () { $__tools__/crashlist nodelist $__tools__/crashgetnode 99:99/1.1 nodelist > crashgetnode.output diff crashgetnode.output $__topdir__/crashgetnode-point.expected } crashmail-1.5/tests/02-crashwrite-test.sh000066400000000000000000000006461230617144500204000ustar00rootroot00000000000000#!/bin/sh describe "crashwrite" . ./testcommon.sh before () { setup_crashmail_env setup_tmpfile } after () { clean_crashmail_env clean_tmpfile } it_generates_a_packet () { $__tools__/crashwrite DIR spool/temp \ FROMNAME "Test Sysop" \ FROMADDR "99:99/1" \ TONAME "Test User" \ TOADDR "99:99/99" \ SUBJECT "Test Message" | tee $tmpfile pkt=$(awk '/^Writing:/ {print $2}' $tmpfile) test -f $pkt } crashmail-1.5/tests/03-crashmail-test.sh000066400000000000000000000056551230617144500201760ustar00rootroot00000000000000#!/bin/sh describe "crashmail" . ./testcommon.sh before () { setup_crashmail_env setup_tmpfile } after () { clean_crashmail_env clean_tmpfile } # Verify that crashmail runs without error. it_runs_successfully () { $__crashmail__ settings crashmail.prefs | tee $tmpfile grep '^CrashMail II .* started successfully' $tmpfile grep '^CrashMail end' $tmpfile } # Verify that inbound mail is tossed into the # netmail area. it_tosses_netmail_successfully () { echo This is a test netmail message. | $__tools__/crashwrite dir spool/inbound \ fromname "Test User" fromaddr 99:99/999 \ toname "Test Sysop" toaddr 99:99/1 \ subject "Test netmail message" \ text /dev/stdin $__crashmail__ settings crashmail.prefs toss test -f areas/netmail/2.msg } # Verify that inbound echomail is tossed into # the appropriate area. it_tosses_echos_successfully () { echo This is a test netmail message. | $__tools__/crashwrite dir spool/inbound \ fromname "Test User" fromaddr 99:99/999 \ toname "Test Sysop" toaddr 99:99/1 \ subject "Test netmail message" \ area testarea \ text /dev/stdin $__crashmail__ settings crashmail.prefs toss test -f areas/testarea/2.msg } # Verify that echomail from unlinked nodes is # tossed into the BAD area. it_handles_bad_packets_successfully () { echo This is a test netmail message. | $__tools__/crashwrite dir spool/inbound \ fromname "Test User" fromaddr 99:99/77 \ toname "Test Sysop" toaddr 99:99/1 \ subject "Test netmail message" \ area testarea \ text /dev/stdin $__crashmail__ settings crashmail.prefs toss test -f areas/bad/2.msg } # Verify that crashmail detects and filters # out duplicate messages. it_detects_dupes_successfully () { mkdir spool/temp/newpacket echo 'This is a test.' | $__tools__/crashwrite dir spool/temp/newpacket \ fromname "Test User" fromaddr 99:99/99 \ toname "Test Sysop" toaddr 99:99/1 \ subject "Test netmail message" \ area testarea \ text /dev/stdin pkt=$(find spool/temp/newpacket -type f) cp $pkt spool/inbound $__crashmail__ toss cp $pkt spool/inbound $__crashmail__ toss | tee $tmpfile grep 'Duplicate message in testarea' $tmpfile } ### FIXME - 969 instead of 971 on 64 bit system. Needs investigation. ## This catches a problem in which appending new messages to a packet ## resulted in an invalid packet size. #it_merges_packets () { # $__crashmail__ sendinfo 99:99/99 # $__crashmail__ sendlist 99:99/99 # # size=$(wc -c < spool/outbound/00630063.out) # [ "$size" -eq 971 ] #} # This checks that crashmail can HUB route packets correctly. # The node 88:99/99 matches the following ROUTE line in the # test configuration: # # ROUTE "88:*/*" HUB 99:99/1 # # Mail to 88:99/1 should go to the network HOST (88:99/0) # but mail to 88:99/99 should go to the HUB, 88:99/2. it_routes_packets () { $__crashmail__ sendinfo 88:99/1 test -f spool/outbound.058/00630000.out $__crashmail__ sendinfo 88:99/99 test -f spool/outbound.058/00630002.out } crashmail-1.5/tests/04-crashstats-test.sh000066400000000000000000000017331230617144500204040ustar00rootroot00000000000000#!/bin/sh describe "crashstats" . ./testcommon.sh . ./03-crashmail-test.sh before () { setup_crashmail_env # Uses tests from 03-crashmail-tests.sh to generate # crashmail.stats before each test. it_tosses_netmail_successfully it_tosses_echos_successfully } after () { clean_crashmail_env } it_generates_alpha_sorted_stats () { test -f crashmail.stats $__tools__/crashstats crashmail.stats sort a } it_generates_message_sorted_stats () { test -f crashmail.stats $__tools__/crashstats crashmail.stats sort t } it_generates_byday_sorted_stats () { test -f crashmail.stats $__tools__/crashstats crashmail.stats sort m } it_generates_byfirstimport_sorted_stats () { test -f crashmail.stats $__tools__/crashstats crashmail.stats sort d } it_generates_bylastimport_sorted_stats () { test -f crashmail.stats $__tools__/crashstats crashmail.stats sort l } it_generates_bydupes_sorted_stats () { test -f crashmail.stats $__tools__/crashstats crashmail.stats sort u } crashmail-1.5/tests/05-areafix-test.sh000066400000000000000000000017071230617144500176460ustar00rootroot00000000000000#!/bin/sh describe "areafix" . ./testcommon.sh before () { setup_crashmail_env setup_tmpfile } after () { clean_crashmail_env clean_tmpfile } it_generates_area_list () { echo %LIST | $__tools__/crashwrite dir spool/inbound \ fromname "Test User" fromaddr 99:99/88 \ toname "Areafix" toaddr 99:99/1 \ subject "TESTPASS" \ text /dev/stdin $__crashmail__ settings crashmail.prefs toss | tee $tmpfile grep 'AreaFix: Sending list of areas to 99:99/88.0' $tmpfile test -f spool/outbound/00630058.out } it_subscribes_node () { echo TESTAREA | $__tools__/crashwrite dir spool/inbound \ fromname "Test User" fromaddr 99:99/88 \ toname "Areafix" toaddr 99:99/1 \ subject "TESTPASS" \ text /dev/stdin $__crashmail__ settings crashmail.prefs toss | tee $tmpfile grep 'AreaFix: Request from 99:99/88' $tmpfile grep 'AreaFix: Attached to TESTAREA' $tmpfile test -f spool/outbound/00630058.out grep '^EXPORT .* 99:99/88.0' crashmail.prefs } crashmail-1.5/tests/15bba400.pkt000066400000000000000000000004351230617144500163270ustar00rootroot00000000000000cÝ;0ccþccccccc08 Feb 13 21:59:48Test SysopTest UserDupe checkAREA:testarea MSGID: 99:99/99.0 15bba400 This is for checking dupe handling --- CrashWrite II/Linux 1.1 * Origin: Another user of CrashMail II (99:99/99.0) crashmail-1.5/tests/Makefile000066400000000000000000000000671230617144500161320ustar00rootroot00000000000000# tests/Makefile ROUNDUP=./roundup all: $(ROUNDUP) crashmail-1.5/tests/cmnodelist.prefs000066400000000000000000000000351230617144500176670ustar00rootroot00000000000000 nodelist.txt pointlist.txt crashmail-1.5/tests/crashgetnode-node.expected000066400000000000000000000002541230617144500216040ustar00rootroot00000000000000Node 99:99/99.0 Region 0, Hub 0 Node is listed as a Node Name: Test_Host_1 Location: Test_Locale Sysop: Test_Sysop Phone: 0-000-000-0000 Baud: 300 Flags: INA:localhost,IBN crashmail-1.5/tests/crashgetnode-point.expected000066400000000000000000000002551230617144500220110ustar00rootroot00000000000000Node 99:99/1.1 Region 0, Hub 0 Node is listed as a Point Name: Test_Point_1 Location: Test_Locale Sysop: Test_Sysop Phone: 0-000-000-0000 Baud: 300 Flags: INA:localhost,IBN crashmail-1.5/tests/crashmail.prefs000066400000000000000000000022251230617144500174740ustar00rootroot00000000000000SYSOP "Test Sysop" LOGFILE "crashmail.log" LOGLEVEL 6 DUPEFILE "dupes" 200 DUPEMODE BAD LOOPMODE LOG+BAD DEFAULTZONE 1 NODELIST nodelist CMNL INBOUND "spool/inbound" OUTBOUND "spool/outbound" TEMPDIR "spool/temp" CREATEPKTDIR "spool/temp" PACKETDIR "spool/packets" STATSFILE "crashmail.stats" GROUPNAME A "TestGroup" PACKER "ZIP" "zip -j %a %f" "unzip -j -o %a" "PK" AREAFIXNAME "areafix" AREAFIXHELP "../doc/AreafixHelp.txt" AKA 99:99/1 DOMAIN "testdomain" NODE 99:99/99 "ZIP" "" DEFAULTGROUP A AREAFIXINFO "TESTPASS" "A" "" "" NODE 99:99/88 "ZIP" "" DEFAULTGROUP A AREAFIXINFO "TESTPASS" "A" "" "" NODE 99:99/999 "ZIP" "" DEFAULTGROUP A AREAFIXINFO "TESTPASS" "A" "" "" NODE 88:99/1 "ZIP" "" DEFAULTGROUP A AREAFIXINFO "TESTPASS" "A" "" "" NODE 88:99/99 "ZIP" "" DEFAULTGROUP A AREAFIXINFO "TESTPASS" "A" "" "" ROUTE "88:*/*" HUB 99:99/1 ROUTE "*:*/*" NODE 99:99/1 BOUNCE "1:*/*.*" "2:*/*.*" "3:*/*.*" "4:*/*.*" "5:*/*.*" "6:*/*.*" MSG_HIGHWATER JAM_HIGHWATER JAM_LINK JAM_QUICKLINK NETMAIL "NETMAIL" 99:99/1 MSG "areas/netmail" AREA "BAD" 99:99/1 MSG "areas/bad" AREA "TESTAREA" 99:99/1.0 MSG "areas/testarea" EXPORT 99:99/99.0 99:99/999.0 88:99/99 GROUP A crashmail-1.5/tests/crashmail.stats000066400000000000000000000010301230617144500175040ustar00rootroot00000000000000CST3‚=NETMAILÐ3ßSÿ)[¶“ÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿccÿÿ ÿ6TQÕQBADAILÐ3ßSÿ)[¶“ÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿccÿÿÿ6TQÕQTESTAREA3ßSÿ)[¶“ÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿccAÿÿ ÿ6TQÕQccc&R )× ÿ6TQccXL^94ÿ7TQcrashmail-1.5/tests/nodelist.txt000066400000000000000000000014051230617144500170510ustar00rootroot00000000000000Zone,99,Test_Zone_99,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN Host,99,Test_Net,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN ,1,Test_Host_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN ,99,Test_Host_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN ,999,Test_Host_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN Zone,88,Test_Zone_88,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN Host,99,Test_Net,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN ,1,Test_Host_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN Hub,2,Test_Host_2,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN ,99,Test_Host_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN crashmail-1.5/tests/pointlist.txt000066400000000000000000000004671230617144500172640ustar00rootroot00000000000000Zone,99,Test_Zone,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN Host,99,Test_Net,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN ,1,Test_Host_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN Point,1,Test_Point_1,Test_Locale,Test_Sysop,0-000-000-0000,300,INA:localhost,IBN crashmail-1.5/tests/roundup000077500000000000000000000317661230617144500161260ustar00rootroot00000000000000#!/bin/bash # [r5]: roundup.5.html # [r1t]: roundup-1-test.sh.html # [r5t]: roundup-5-test.sh.html # # _(c) 2010 Blake Mizerany - MIT License_ # # Spray **roundup** on your shells to eliminate weeds and bugs. If your shells # survive **roundup**'s deathly toxic properties, they are considered # roundup-ready. # # **roundup** reads shell scripts to form test plans. Each # test plan is sourced into a sandbox where each test is executed. # # See [roundup-1-test.sh.html][r1t] or [roundup-5-test.sh.html][r5t] for example # test plans. # # __Install__ # # git clone http://github.com/bmizerany/roundup.git # cd roundup # make # sudo make install # # Alternatively, copy `roundup` wherever you like. # # __NOTE__: Because test plans are sourced into roundup, roundup prefixes its # variable and function names with `roundup_` to avoid name collisions. See # "Sandbox Test Runs" below for more insight. # Usage and Prerequisites # ----------------------- # Exit if any following command exits with a non-zero status. set -e # The current version is set during `make version`. Do not modify this line in # anyway unless you know what you're doing. ROUNDUP_VERSION="0.0.5" export ROUNDUP_VERSION # Usage is defined in a specific comment syntax. It is `grep`ed out of this file # when needed (i.e. The Tomayko Method). See # [shocco](http://rtomayko.heroku.com/shocco) for more detail. #/ usage: roundup [--help|-h] [--version|-v] [plan ...] roundup_usage() { grep '^#/' <"$0" | cut -c4- } while test "$#" -gt 0 do case "$1" in --help|-h) roundup_usage exit 0 ;; --version|-v) echo "roundup version $ROUNDUP_VERSION" exit 0 ;; --color) color=always shift ;; -) echo >&2 "roundup: unknown switch $1" exit 1 ;; *) break ;; esac done # Consider all scripts with names matching `*-test.sh` the plans to run unless # otherwise specified as arguments. if [ "$#" -gt "0" ] then roundup_plans="$@" else roundup_plans="$(ls *-test.sh)" fi : ${color:="auto"} # Create a temporary storage place for test output to be retrieved for display # after failing tests. roundup_tmp=$(mktemp -d -t .roundup.XXX) trap "rm -rf \"$roundup_tmp\"" EXIT INT # __Tracing failures__ roundup_trace() { # Delete the first two lines that represent roundups execution of the # test function. They are useless to the user. sed '1d' | # Delete the last line which is the "set +x" of the error trap sed '$d' | # Replace the rc=$? of the error trap with an verbose string appended # to the failing command trace line. sed '$s/.*rc=/exit code /' | # Trim the two left most `+` signs. They represent the depth at which # roundup executed the function. They also, are useless and confusing. sed 's/^++//' | # Indent the output by 4 spaces to align under the test name in the # summary. sed 's/^/ /' | # Highlight the last line in front of the exit code to bring notice to # where the error occurred. # # The sed magic puts every line into the hold buffer first, then # substitutes in the previous hold buffer content, prints that and starts # with the next cycle. At the end the last line (in the hold buffer) # is printed without substitution. sed -n "x;1!{ \$s/\(.*\)/$mag\1$clr/; };1!p;\$x;\$p" } # __Other helpers__ # Track the test stats while outputting a real-time report. This takes input on # **stdin**. Each input line must come in the format of: # # # The plan description to be displayed # d # # # A passing test # p # # # A failed test # f roundup_summarize() { set -e # __Colors for output__ # Use colors if we are writing to a tty device. if (test -t 1) || (test $color = always) then red=$(printf "\033[31m") grn=$(printf "\033[32m") mag=$(printf "\033[35m") ylw=$(printf "\033[33m") clr=$(printf "\033[m") cols=$(tput cols) fi # Make these available to `roundup_trace`. export red grn mag clr ylw ntests=0 passed=0 skipped=0 failed=0 : ${cols:=10} while read status name do case $status in p) ntests=$(expr $ntests + 1) passed=$(expr $passed + 1) printf " %-48s " "$name:" printf "$grn[PASS]$clr\n" ;; s) ntests=$(expr $ntests + 1) skipped=$(expr $skipped + 1) printf " %-48s " "$name:" printf "$ylw[SKIP]$clr\n" ;; f) ntests=$(expr $ntests + 1) failed=$(expr $failed + 1) printf " %-48s " "$name:" printf "$red[FAIL]$clr\n" roundup_trace < "$roundup_tmp/$name" ;; d) printf "%s\n" "$name" ;; esac done # __Test Summary__ # # Display the summary now that all tests are finished. yes = | head -n 57 | tr -d '\n' printf "\n" printf "Tests: %3d | " $ntests printf "Passed: %3d | " $passed printf "Skipped: %3d | " $skipped printf "Failed: %3d" $failed printf "\n" # Exit with an error if any tests failed test $failed -eq 0 || exit 2 } run_with_tracing() { local func="$1" local tracefile="${2:-$roundup_tmp/$func}" # Output `$func` trace to temporary file. { # redirect tracing output of `$func` into file. { set -xe $func } &>"$tracefile" # disable tracing again. Its trace output goes to /dev/null. set +x } &>/dev/null } run() { local func="$1" # Any number of things are possible in `$func`. # Drop into an subshell to contain operations that may throw # off `$func`; such as `cd`. # Momentarily turn off auto-fail to give us access to the exit status # in `$?` for capturing. set +e ( # exit subshell with return code of last failing command. This # is needed to see the return code 253 on failed assumptions. # But, only do this if the error handling is activated. set -E trap 'rc=$?; set +x; set -o | grep "errexit.*on" >/dev/null && exit $rc' ERR run_with_tracing "$func" ) # copy roundup_result from subshell above roundup_result=$? # Check if `$func` was successful, otherwise emit fail signal if [ "$roundup_result" != 0 ]; then # `$func` failed printf "f $func\n"; continue fi } # Sandbox Test Runs # ----------------- # The above checks guarantee we have at least one test. We can now move through # each specified test plan, determine its test plan, and administer each test # listed in a isolated sandbox. for roundup_p in $roundup_plans do # Create a sandbox, source the test plan, run the tests, then leave # without a trace. ( # Consider the description to be the `basename` of the plan minus the # tailing -test.sh. roundup_desc=$(basename "$roundup_p" -test.sh) # Define functions for # [roundup(5)][r5] # A custom description is recommended, but optional. Use `describe` to # set the description to something more meaningful. # TODO: reimplement this. describe() { roundup_desc="$*" } # Helper to express an assumption for a given testcase. Example: # it_runs_fine() { # assume it_builds_fine # assume test -f foo # ./binary # } assume() { if (echo "$1" | grep "^it_.*" >/dev/null) then if [ "$(eval echo \${passed_$1})" == 1 ] then return 0 else return 253 fi else if eval "$@" then return 0 else return 253 fi fi } # Provide default `before` and `after` functions that run only `:`, a # no-op. They may or may not be redefined by the test plan. init() { :; } before() { :; } after() { :; } cleanup() { :; } # Seek test methods and aggregate their names, forming a test plan. # This is done before populating the sandbox with tests to avoid odd # conflicts. # TODO: I want to do this with sed only. Please send a patch if you # know a cleaner way. roundup_plan=$( grep "^it_.*()" $roundup_p | sed "s/\(it_[a-zA-Z0-9_]*\).*$/\1/g" ) # We have the test plan and are in our sandbox with [roundup(5)][r5] # defined. Now we source the plan to bring its tests into scope. . ./$roundup_p # Output the description signal printf "d %s" "$roundup_desc" | tr "\n" " " printf "\n" # Run `init` function of the current plan. THis will be done before any of # the tests in the plan are executed. # If `init` wasn't redefined, then this is `:`. run "init" for roundup_test_name in $roundup_plan do # Any number of things are possible in `before`, `after`, and the # test. Drop into an subshell to contain operations that may throw # off roundup; such as `cd`. # Momentarily turn off auto-fail to give us access to the tests # exit status in `$?` for capturing. set +e ( # exit subshell with return code of last failing command. This # is needed to see the return code 253 on failed assumptions. # But, only do this if the error handling is activated. set -E trap 'rc=$?; set +x; set -o | grep "errexit.*on" >/dev/null && exit $rc' ERR trap "cleanup" INT # If `before` wasn't redefined, then this is `:`. run_with_tracing before "$roundup_tmp/$roundup_test_name" # Momentarily turn off auto-fail to give us access to the tests # exit status in `$?` for capturing. set +e ( # Define a helper to log stdout to the file stdout and stderr to the # file stderr. This can be used like this: # capture ls asdf # grep "error" stderr capture () { { "$@" 2>&1 1>&3 | tee -- $roundup_tmp/stderr | awk "{ print \"\033[31m\"\$0\"\033[m\"; }" 1>&2 return ${PIPESTATUS[0]}; } 3>&1 | tee -- $roundup_tmp/stdout return ${PIPESTATUS[0]} } stdout () { echo -n "$roundup_tmp/stdout"; } stderr () { echo -n "$roundup_tmp/stderr"; } # Define a negating operator which triggers the error trap of the shell. The # builtin ! will not. function expectfail () { ! "$@"; } # Set `-xe` before the test in the subshell. We want the # test to fail fast to allow for more accurate output of # where things went wrong but not in _our_ process because a # failed test should not immediately fail roundup. Each # tests trace output is saved in temporary storage. set -xe $roundup_test_name ) >"$roundup_tmp/$roundup_test_name" 2>&1 # We need to capture the exit status before returning the `set # -e` mode. Returning with `set -e` before we capture the exit # status will result in `$?` being set with `set`'s status # instead. roundup_result=$? # If `after` wasn't redefined, then this runs `:`. after # Pass roundup return code outside of the subshell exit $roundup_result ) # copy roundup_result from subshell above roundup_result=$? # It's safe to return to normal operation. set -e # This is the final step of a test. Print its pass/fail signal # and name. if [ "$roundup_result" == 0 ] then printf "p"; eval export passed_$roundup_test_name=1 elif [ "$roundup_result" == 253 ] then printf "s" else printf "f" fi printf " $roundup_test_name\n" done # Run `cleanup` function of the current plan. # This function is guaranteed to run after the last test case. # If `cleanup` wasn't redefined, then this is `:`. run "cleanup" ) done | # All signals are piped to this for summary. roundup_summarize crashmail-1.5/tests/test.txt000066400000000000000000000000551230617144500162070ustar00rootroot00000000000000Hi! This is a test, that is only a test... crashmail-1.5/tests/testcommon.sh000066400000000000000000000012501230617144500172110ustar00rootroot00000000000000#!/bin/sh setup_sandbox () { __topdir__=$PWD __testdir__=$(mktemp -d testXXXXXX) __tools__=$PWD/../bin __crashmail__=$PWD/../bin/crashmail cd $__testdir__ } clean_sandbox () { cd $__topdir__ rm -rf $__testdir__ } setup_crashmail_env () { setup_sandbox mkdir -p spool/{inbound,outbound,temp,packets} mkdir -p areas/netmail areas/testarea areas/bad mkdir -p nodelist cp $__topdir__/crashmail.prefs crashmail.prefs cp $__topdir__/{nodelist.txt,pointlist.txt,cmnodelist.prefs} nodelist/ $__topdir__/../bin/crashlist nodelist } setup_tmpfile () { tmpfile=$(mktemp outputXXXXXX) } clean_tmpfile () { rm -f $tmpfile } clean_crashmail_env () { clean_sandbox }