diff --git a/Makefile.freebsd b/Makefile.freebsd index 091f60e..a927c4d 100644 --- a/Makefile.freebsd +++ b/Makefile.freebsd @@ -5,7 +5,7 @@ JAMLIB = deps/jamlib/jamlib.a ZMODEM = deps/Xmodem/libzmodem.a LUA = deps/lua/liblua.a -all: magicka +all: magicka magimail ${LUA}: cd deps/lua && $(MAKE) -f Makefile freebsd MAKEFLAGS= @@ -23,6 +23,9 @@ OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o magicka: $(OBJ) ${LUA} ${JAMLIB} ${ZMODEM} $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) $(LUA) -lutil -lm -lssl -lcrypto -lssh +magimail: $(JAMLIB) + cd utils/magimail && $(MAKE) freebsd + .PHONY: clean clean: @@ -30,3 +33,4 @@ clean: cd deps/lua && $(MAKE) clean cd deps/jamlib && $(MAKE) -f Makefile.linux clean cd deps/Xmodem && $(MAKE) clean + cd utils/magimail && $(MAKE) cleanfreebsd diff --git a/Makefile.freebsd.WWW b/Makefile.freebsd.WWW index 2920a62..3831e74 100644 --- a/Makefile.freebsd.WWW +++ b/Makefile.freebsd.WWW @@ -7,7 +7,7 @@ LUA = deps/lua/liblua.a B64 = deps/libb64-1.2/src/libb64.a MICROHTTPD=-lmicrohttpd -all: magicka +all: magicka magimail ${LUA}: cd deps/lua && $(MAKE) -f Makefile freebsd MAKEFLAGS= @@ -28,6 +28,9 @@ OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o magicka: $(OBJ) ${LUA} ${ZMODEM} ${JAMLIB} ${B64} $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) $(LUA) $(B64) -lutil -lm -lssl -lcrypto -lssh $(MICROHTTPD) +magimail: $(JAMLIB) + cd utils/magimail && $(MAKE) freebsd + .PHONY: clean clean: @@ -36,3 +39,4 @@ clean: cd deps/jamlib && $(MAKE) -f Makefile.linux clean cd deps/Xmodem && $(MAKE) clean cd deps/libb64-1.2 && $(MAKE) clean + cd utils/magimail && $(MAKE) cleanfreebsd diff --git a/Makefile.linux b/Makefile.linux index 3158efc..a96ea59 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -6,7 +6,7 @@ ZMODEM = deps/Xmodem/libzmodem.a LUA = deps/lua/liblua.a -all: magicka +all: magicka magimail ${LUA}: cd deps/lua && $(MAKE) -f Makefile linux MAKEFLAGS= @@ -24,6 +24,9 @@ OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o magicka: $(OBJ) ${LUA} ${ZMODEM} ${JAMLIB} $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) $(LUA) -lutil -lm -ldl -lssl -lcrypto -lssh +magimail: $(JAMLIB) + cd utils/magimail && $(MAKE) linux + .PHONY: clean clean: @@ -31,3 +34,4 @@ clean: cd deps/lua && $(MAKE) clean cd deps/jamlib && $(MAKE) -f Makefile.linux clean cd deps/Xmodem && $(MAKE) clean + cd utils/magimail && $(MAKE) cleanlinux diff --git a/Makefile.linux.WWW b/Makefile.linux.WWW index a296746..6a42ba2 100644 --- a/Makefile.linux.WWW +++ b/Makefile.linux.WWW @@ -7,7 +7,7 @@ LUA = deps/lua/liblua.a B64 = deps/libb64-1.2/src/libb64.a MICROHTTPD=-lmicrohttpd -all: magicka +all: magicka magimail ${LUA}: cd deps/lua && $(MAKE) -f Makefile linux MAKEFLAGS= @@ -29,6 +29,9 @@ OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o magicka: $(OBJ) ${LUA} ${JAMLIB} ${ZMODEM} ${B64} $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) $(LUA) $(B64) -lutil -lm -ldl -lssl -lcrypto -lssh $(MICROHTTPD) +magimail: $(JAMLIB) + cd utils/magimail && $(MAKE) linux + .PHONY: clean clean: @@ -37,4 +40,4 @@ clean: cd deps/jamlib && $(MAKE) -f Makefile.linux clean cd deps/Xmodem && $(MAKE) clean cd deps/libb64-1.2 && $(MAKE) clean - + cd utils/magimail && $(MAKE) cleanlinux diff --git a/Makefile.osx b/Makefile.osx index 9c85c38..68bee62 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -7,7 +7,7 @@ LUA = deps/lua/liblua.a OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o files.o settings.o lua_glue.o strings.o bluewave.o hashmap/hashmap.o -all: magicka +all: magicka magimail ${LUA}: cd deps/lua && $(MAKE) -f Makefile macosx MAKEFLAGS= @@ -24,6 +24,9 @@ ${ZMODEM}: magicka: $(OBJ) ${LUA} ${ZMODEM} ${JAMLIB} $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/opt/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) $(LUA) -lutil -lm -ldl -lssl -lcrypto -lssh +magimail: $(JAMLIB) + cd utils/magimail && $(MAKE) linux + .PHONY: clean clean: @@ -31,3 +34,4 @@ clean: cd deps/lua && $(MAKE) clean cd deps/jamlib && $(MAKE) -f Makefile.linux clean cd deps/Xmodem && $(MAKE) clean + cd utils/magimail && $(MAKE) cleanlinux diff --git a/Makefile.osx.WWW b/Makefile.osx.WWW index 96d6ea7..eff0b26 100644 --- a/Makefile.osx.WWW +++ b/Makefile.osx.WWW @@ -8,7 +8,7 @@ B64 = deps/libb64-1.2/src/libb64.a MICROHTTPD=-lmicrohttpd -all: magicka +all: magicka magimail ${LUA}: cd deps/lua && $(MAKE) -f Makefile macosx MAKEFLAGS= @@ -29,6 +29,9 @@ OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o magicka: $(OBJ) ${LUA} ${ZMODEM} ${JAMLIB} ${B64} $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/opt/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) $(LUA) $(B64) -lutil -lm -ldl -lssl -lcrypto -lssh $(MICROHTTPD) +magimail: $(JAMLIB) + cd utils/magimail && $(MAKE) linux + .PHONY: clean clean: @@ -36,3 +39,4 @@ clean: cd deps/lua && $(MAKE) clean cd deps/jamlib && $(MAKE) -f Makefile.linux clean cd deps/Xmodem && $(MAKE) clean + cd utils/magimail && $(MAKE) cleanlinux diff --git a/utils/magimail/Makefile b/utils/magimail/Makefile new file mode 100644 index 0000000..b4c97b3 --- /dev/null +++ b/utils/magimail/Makefile @@ -0,0 +1,25 @@ +# type either "make linux", "make win32", or "make os2" to compile + +help: + @echo You can use this Makefile in the following ways: + @echo gmake freebsd ......... Make FreeBSD binaries + @echo make linux ............ Make Linux binaries + @echo gmake cleanfreebsd .... Remove object files under FreeBSD + @echo make cleanlinux ....... Remove object files under Linux + +freebsd : + mkdir -p bin + gmake -C src -f Makefile freebsd + +linux : + mkdir -p bin + make -C src -f Makefile linux + +cleanfreebsd : + rm -rf bin + gmake -C src -f Makefile cleanfreebsd + +cleanlinux : + rm -rf bin + make -C src -f Makefile cleanlinux + diff --git a/utils/magimail/README b/utils/magimail/README new file mode 100644 index 0000000..cbda1fa --- /dev/null +++ b/utils/magimail/README @@ -0,0 +1,75 @@ +magimail is derived from... + + 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. + diff --git a/utils/magimail/doc/AreafixHelp.txt b/utils/magimail/doc/AreafixHelp.txt new file mode 100644 index 0000000..dae2c4c --- /dev/null +++ b/utils/magimail/doc/AreafixHelp.txt @@ -0,0 +1,114 @@ + + 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. diff --git a/utils/magimail/doc/ReadMe.txt b/utils/magimail/doc/ReadMe.txt new file mode 100644 index 0000000..f0c15b3 --- /dev/null +++ b/utils/magimail/doc/ReadMe.txt @@ -0,0 +1,505 @@ + + 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 + + diff --git a/utils/magimail/doc/example.prefs b/utils/magimail/doc/example.prefs new file mode 100644 index 0000000..fd6e289 --- /dev/null +++ b/utils/magimail/doc/example.prefs @@ -0,0 +1,714 @@ +; 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 + diff --git a/utils/magimail/doc/filter.txt b/utils/magimail/doc/filter.txt new file mode 100644 index 0000000..9d975ae --- /dev/null +++ b/utils/magimail/doc/filter.txt @@ -0,0 +1,296 @@ + 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 + + diff --git a/utils/magimail/src/Makefile b/utils/magimail/src/Makefile new file mode 100644 index 0000000..ac93790 --- /dev/null +++ b/utils/magimail/src/Makefile @@ -0,0 +1,29 @@ +# 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 cleanlinux ....... Remove object files under Linux + +freebsd: + mkdir -p obj + gmake -C cmnllib -f Makefile.linux + gmake -C oslib_linux + gmake -f Makefile.linux + +linux : + mkdir -p obj + make -C cmnllib -f Makefile.linux + make -C oslib_linux + make -f Makefile.linux + +cleanfreebsd : + gmake -C cmnllib -f Makefile.linux clean + gmake -C oslib_linux clean + gmake -f Makefile.linux clean + +cleanlinux : + make -C cmnllib -f Makefile.linux clean + make -C oslib_linux clean + make -f Makefile.linux clean + diff --git a/utils/magimail/src/Makefile.linux b/utils/magimail/src/Makefile.linux new file mode 100644 index 0000000..1f798c7 --- /dev/null +++ b/utils/magimail/src/Makefile.linux @@ -0,0 +1,219 @@ +# Makefile for Linux + +# General + +PLATFORMDEF = -DPLATFORM_LINUX +EXESUFFIX = + +BINDIR = ../bin +OBJDIR = obj +INCDIR = ./ + +OSLIB = oslib_linux/oslib.a +JAMLIB = ../../../deps/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) + +MBDEFS_SQ3 = -DMSGBASE_SQ3 +MBOBJS_SQ3 = $(OBJDIR)/mb_sq3.o + +# Sum them up. Only include the messagebases you want to use +MBDEFS = $(MBDEFS_MSG) $(MBDEFS_JAM) $(MBDEFS_SQ3) +MBOBJS = $(MBOBJS_MSG) $(MBOBJS_JAM) $(MBOBJS_SQ3) + +DEFS = $(PLATFORMDEF) $(MBDEFS) $(NLDEFS) + +# Commands + +CC = gcc $(CPPFLAGS) $(CFLAGS) $(DEFS) $(LDFLAGS) -I $(INCDIR) -I ../../../deps/ -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)/magimail.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)/magimail$(EXESUFFIX) \ + $(BINDIR)/magistats$(EXESUFFIX) \ + $(BINDIR)/magilist$(EXESUFFIX) \ + $(BINDIR)/magigetnode$(EXESUFFIX) \ + $(BINDIR)/magimaint$(EXESUFFIX) \ + $(BINDIR)/magiwrite$(EXESUFFIX) \ + $(BINDIR)/magiexport$(EXESUFFIX) \ + $(BINDIR)/magilistout$(EXESUFFIX) + +$(BINDIR)/magimail$(EXESUFFIX) : $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) + $(CC) -o $(BINDIR)/magimail$(EXESUFFIX) $(CMOBJS) $(SHOBJS) $(NLOBJS) $(MBOBJS) $(OSLIB) -lsqlite3 + $(STRIP) $(BINDIR)/magimail$(EXESUFFIX) + +$(BINDIR)/magistats$(EXESUFFIX) : tools/magistats.c $(SHOBJS) $(OSLIB) + $(CC) -o $(BINDIR)/magistats$(EXESUFFIX) tools/magistats.c $(SHOBJS) $(OSLIB) + $(STRIP) $(BINDIR)/magistats$(EXESUFFIX) + +$(BINDIR)/magilist$(EXESUFFIX) : tools/magilist.c $(SHOBJS) $(OSLIB) + $(CC) -o $(BINDIR)/magilist$(EXESUFFIX) tools/magilist.c $(SHOBJS) $(OSLIB) + $(STRIP) $(BINDIR)/magilist$(EXESUFFIX) + +$(BINDIR)/magigetnode$(EXESUFFIX) : tools/magigetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) + $(CC) -o $(BINDIR)/magigetnode$(EXESUFFIX) tools/magigetnode.c $(SHOBJS) $(CMNLLIB) $(OSLIB) + $(STRIP) $(BINDIR)/magigetnode$(EXESUFFIX) + +$(BINDIR)/magimaint$(EXESUFFIX) : tools/magimaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) + $(CC) -o $(BINDIR)/magimaint$(EXESUFFIX) tools/magimaint.c $(SHOBJS) $(OSLIB) $(JAMLIB) + $(STRIP) $(BINDIR)/magimaint$(EXESUFFIX) + +$(BINDIR)/magiwrite$(EXESUFFIX) : tools/magiwrite.c $(SHOBJS) $(OSLIB) + $(CC) -o $(BINDIR)/magiwrite$(EXESUFFIX) tools/magiwrite.c $(SHOBJS) $(OSLIB) + $(STRIP) $(BINDIR)/magiwrite$(EXESUFFIX) + +$(BINDIR)/magiexport$(EXESUFFIX) : tools/magiexport.c $(SHOBJS) $(OSLIB) + $(CC) -o $(BINDIR)/magiexport$(EXESUFFIX) tools/magiexport.c $(SHOBJS) $(OSLIB) + $(STRIP) $(BINDIR)/magiexport$(EXESUFFIX) + +$(BINDIR)/magilistout$(EXESUFFIX) : tools/magilistout.c $(SHOBJS) $(OSLIB) + $(CC) -o $(BINDIR)/magilistout$(EXESUFFIX) tools/magilistout.c $(SHOBJS) $(OSLIB) + $(STRIP) $(BINDIR)/magilistout$(EXESUFFIX) + +# magimail + +$(OBJDIR)/magimail.o : magimail/magimail.c + $(CC) -c magimail/magimail.c -o $(OBJDIR)/magimail.o + +$(OBJDIR)/logwrite.o : magimail/logwrite.c + $(CC) -c magimail/logwrite.c -o $(OBJDIR)/logwrite.o + +$(OBJDIR)/dupe.o : magimail/dupe.c + $(CC) -c magimail/dupe.c -o $(OBJDIR)/dupe.o + +$(OBJDIR)/stats.o : magimail/stats.c + $(CC) -c magimail/stats.c -o $(OBJDIR)/stats.o + +$(OBJDIR)/misc.o : magimail/misc.c + $(CC) -c magimail/misc.c -o $(OBJDIR)/misc.o + +$(OBJDIR)/safedel.o : magimail/safedel.c + $(CC) -c magimail/safedel.c -o $(OBJDIR)/safedel.o + +$(OBJDIR)/toss.o : magimail/toss.c + $(CC) -c magimail/toss.c -o $(OBJDIR)/toss.o + +$(OBJDIR)/scan.o : magimail/scan.c + $(CC) -c magimail/scan.c -o $(OBJDIR)/scan.o + +$(OBJDIR)/pkt.o : magimail/pkt.c + $(CC) -c magimail/pkt.c -o $(OBJDIR)/pkt.o + +$(OBJDIR)/memmessage.o : magimail/memmessage.c + $(CC) -c magimail/memmessage.c -o $(OBJDIR)/memmessage.o + +$(OBJDIR)/handle.o : magimail/handle.c + $(CC) -c magimail/handle.c -o $(OBJDIR)/handle.o + +$(OBJDIR)/node4dpat.o : magimail/node4dpat.c + $(CC) -c magimail/node4dpat.c -o $(OBJDIR)/node4dpat.o + +$(OBJDIR)/config.o : magimail/config.c + $(CC) -c magimail/config.c -o $(OBJDIR)/config.o + +$(OBJDIR)/outbound.o : magimail/outbound.c + $(CC) -c magimail/outbound.c -o $(OBJDIR)/outbound.o + +$(OBJDIR)/areafix.o : magimail/areafix.c + $(CC) -c magimail/areafix.c -o $(OBJDIR)/areafix.o + +$(OBJDIR)/filter.o : magimail/filter.c + $(CC) -c magimail/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 : magimail/mb.c + $(CC) -c magimail/mb.c -o $(OBJDIR)/mb.o + +$(OBJDIR)/mb_msg.o : magimail/mb_msg.c + $(CC) -c magimail/mb_msg.c -o $(OBJDIR)/mb_msg.o + +$(OBJDIR)/mb_jam.o : magimail/mb_jam.c + $(CC) -c magimail/mb_jam.c -o $(OBJDIR)/mb_jam.o + +$(OBJDIR)/mb_sq3.o : magimail/mb_sq3.c + $(CC) -c magimail/mb_sq3.c -o $(OBJDIR)/mb_sq3.o + +# nl + +$(OBJDIR)/nl.o : magimail/nl.c + $(CC) -c magimail/nl.c -o $(OBJDIR)/nl.o + +$(OBJDIR)/nl_cmnl.o : magimail/nl_cmnl.c + $(CC) -c magimail/nl_cmnl.c -o $(OBJDIR)/nl_cmnl.o + +$(OBJDIR)/nl_v7p.o : magimail/nl_v7p.c + $(CC) -c magimail/nl_v7p.c -o $(OBJDIR)/nl_v7p.o + + +# Clean + +clean : + $(RM) $(OBJDIR)/*.o + diff --git a/utils/magimail/src/cmnllib/Makefile.linux b/utils/magimail/src/cmnllib/Makefile.linux new file mode 100644 index 0000000..446813d --- /dev/null +++ b/utils/magimail/src/cmnllib/Makefile.linux @@ -0,0 +1,17 @@ +INCDIR = ../ + +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 + diff --git a/utils/magimail/src/cmnllib/cmnllib.c b/utils/magimail/src/cmnllib/cmnllib.c new file mode 100644 index 0000000..4fd74da --- /dev/null +++ b/utils/magimail/src/cmnllib/cmnllib.c @@ -0,0 +1,149 @@ +#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]; +} diff --git a/utils/magimail/src/cmnllib/cmnllib.h b/utils/magimail/src/cmnllib/cmnllib.h new file mode 100644 index 0000000..4ba7ded --- /dev/null +++ b/utils/magimail/src/cmnllib/cmnllib.h @@ -0,0 +1,19 @@ +#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; diff --git a/utils/magimail/src/magimail/areafix.c b/utils/magimail/src/magimail/areafix.c new file mode 100644 index 0000000..e278721 --- /dev/null +++ b/utils/magimail/src/magimail/areafix.c @@ -0,0 +1,1703 @@ +#include "magimail.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; + char *tmp; + 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; + tmp = strdup(areaname); + strcpy(areaname,&tmp[1]); + free(tmp); + } + else if(areaname[0]=='=') + { + command=COMMAND_UPDATE; + tmp = strdup(areaname); + strcpy(areaname,&tmp[1]); + free(tmp); + } + else + { + command=COMMAND_ADD; + + if(areaname[0]=='+') { + tmp = strdup(areaname); + strcpy(areaname,&tmp[1]); + free(tmp); + } + } + + 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 MagiMail "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 MagiMail "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; + } +} + diff --git a/utils/magimail/src/magimail/areafix.h b/utils/magimail/src/magimail/areafix.h new file mode 100644 index 0000000..ce58669 --- /dev/null +++ b/utils/magimail/src/magimail/areafix.h @@ -0,0 +1,14 @@ + +#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); + + diff --git a/utils/magimail/src/magimail/config.c b/utils/magimail/src/magimail/config.c new file mode 100644 index 0000000..f70e4b5 --- /dev/null +++ b/utils/magimail/src/magimail/config.c @@ -0,0 +1,2373 @@ +#include "magimail.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; + char *tmp; + + 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; + tmp = strdup(buf2); + strcpy(buf2,&tmp[1]); + free(tmp); + } + + if(buf2[0]=='@') + { + flags=TOSSNODE_WRITEONLY; + tmp = strdup(buf2); + strcpy(buf2,&tmp[1]); + free(tmp); + } + + if(buf2[0]=='%') + { + flags=TOSSNODE_FEED; + tmp = strdup(buf2); + strcpy(buf2,&tmp[1]); + free(tmp); + } + + 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); +} diff --git a/utils/magimail/src/magimail/config.h b/utils/magimail/src/magimail/config.h new file mode 100644 index 0000000..bc76b4f --- /dev/null +++ b/utils/magimail/src/magimail/config.h @@ -0,0 +1,377 @@ +#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 + + diff --git a/utils/magimail/src/magimail/dupe.c b/utils/magimail/src/magimail/dupe.c new file mode 100644 index 0000000..c21b032 --- /dev/null +++ b/utils/magimail/src/magimail/dupe.c @@ -0,0 +1,314 @@ +#include "magimail.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,"MagiMail")) + { + /* Is created by MagiMail */ + + 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 MagiMail"); + 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 MagiMail/" 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); +} + diff --git a/utils/magimail/src/magimail/handle.h b/utils/magimail/src/magimail/handle.h new file mode 100644 index 0000000..53d34f4 --- /dev/null +++ b/utils/magimail/src/magimail/handle.h @@ -0,0 +1,7 @@ +bool 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); + diff --git a/utils/magimail/src/magimail/logwrite.c b/utils/magimail/src/magimail/logwrite.c new file mode 100644 index 0000000..02319f3 --- /dev/null +++ b/utils/magimail/src/magimail/logwrite.c @@ -0,0 +1,134 @@ +#include "magimail.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("MagiMail",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); +} + diff --git a/utils/magimail/src/magimail/logwrite.h b/utils/magimail/src/magimail/logwrite.h new file mode 100644 index 0000000..6f01f8f --- /dev/null +++ b/utils/magimail/src/magimail/logwrite.h @@ -0,0 +1,15 @@ +#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,...); diff --git a/utils/magimail/src/magimail/magimail.c b/utils/magimail/src/magimail/magimail.c new file mode 100644 index 0000000..42124fc --- /dev/null +++ b/utils/magimail/src/magimail/magimail.c @@ -0,0 +1,720 @@ +#include "magimail.h" + +#ifdef PLATFORM_AMIGA +const char ver[]="\0$VER: MagiMail/" 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 MagiMail 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,"MagiMail 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("MagiMail 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,"MagiMail %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); +} diff --git a/utils/magimail/src/magimail/magimail.h b/utils/magimail/src/magimail/magimail.h new file mode 100644 index 0000000..c936984 --- /dev/null +++ b/utils/magimail/src/magimail/magimail.h @@ -0,0 +1,89 @@ +#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[]; + + diff --git a/utils/magimail/src/magimail/mb.c b/utils/magimail/src/magimail/mb.c new file mode 100644 index 0000000..dce593a --- /dev/null +++ b/utils/magimail/src/magimail/mb.c @@ -0,0 +1,56 @@ +#include "magimail.h" + +#ifdef MSGBASE_MSG +#include "mb_msg.h" +#endif + +#ifdef MSGBASE_JAM +#include "mb_jam.h" +#endif + +#ifdef MSGBASE_SQ3 +#include "mb_sq3.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 +#ifdef MSGBASE_SQ3 + { "SQ3", + "SQlite3 Messagebase", + 0, + sq3_beforefunc, + sq3_afterfunc, + sq3_importfunc, + sq3_exportfunc, + sq3_rescanfunc }, +#endif + { NULL, /* NULL here marks the end of the array */ + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL + } +}; diff --git a/utils/magimail/src/magimail/mb.h b/utils/magimail/src/magimail/mb.h new file mode 100644 index 0000000..a818a57 --- /dev/null +++ b/utils/magimail/src/magimail/mb.h @@ -0,0 +1,20 @@ +#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 diff --git a/utils/magimail/src/magimail/mb_jam.c b/utils/magimail/src/magimail/mb_jam.c new file mode 100644 index 0000000..c1439dc --- /dev/null +++ b/utils/magimail/src/magimail/mb_jam.c @@ -0,0 +1,1339 @@ +#include + +#include "magimail.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 |= JAM_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 |= JAM_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 |= JAM_MSG_TYPENET; + + else + Header_S.Attribute |= JAM_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 & JAM_MSG_DELETED) + { + /* Message deleted */ + JAM_DelSubPacket(SubPacket_PS); + return(TRUE); + } + + /* Check if already sent */ + + if(!isrescanning) + { + if((Header_S.Attribute & JAM_MSG_SENT) || !(Header_S.Attribute & JAM_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 MagiMail"); } + 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 MagiMail"); + else mystrncpy(filesubject,Field_PS->Buffer,Field_PS->DatLen+1); + break; + + case JAMSFLD_ENCLFREQ: + LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDFREQ not supported by MagiMail"); + break; + + case JAMSFLD_ENCLFWALIAS: + LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDFILEWALIAS not supported by MagiMail"); + break; + + case JAMSFLD_ENCLFILEWC: + LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDFILEWCARD with wildcards not supported by MagiMail"); + break; + + case JAMSFLD_ENCLINDFILE: + LogWrite(1,TOSSINGERR,"Warning: ENCLOSEDINDIRECTFILE not supported by MagiMail"); + 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 |= JAM_MSG_DELETED; + } + } + + Header_S.Attribute |= JAM_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); +} diff --git a/utils/magimail/src/magimail/mb_jam.h b/utils/magimail/src/magimail/mb_jam.h new file mode 100644 index 0000000..e9119ae --- /dev/null +++ b/utils/magimail/src/magimail/mb_jam.h @@ -0,0 +1,5 @@ +bool 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)); diff --git a/utils/magimail/src/magimail/mb_msg.c b/utils/magimail/src/magimail/mb_msg.c new file mode 100644 index 0000000..39ee3c1 --- /dev/null +++ b/utils/magimail/src/magimail/mb_msg.c @@ -0,0 +1,593 @@ +#include "magimail.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,"MagiMail"); + 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); +} diff --git a/utils/magimail/src/magimail/mb_msg.h b/utils/magimail/src/magimail/mb_msg.h new file mode 100644 index 0000000..5e88218 --- /dev/null +++ b/utils/magimail/src/magimail/mb_msg.h @@ -0,0 +1,5 @@ +bool 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)); diff --git a/utils/magimail/src/magimail/mb_sq3.c b/utils/magimail/src/magimail/mb_sq3.c new file mode 100644 index 0000000..56fcac7 --- /dev/null +++ b/utils/magimail/src/magimail/mb_sq3.c @@ -0,0 +1,628 @@ +#include +#include "magimail.h" +long sq3_utcoffset; + +#define MIN(a,b) ((a)<(b)? (a):(b)) + +void sq3_init_msg_db(char *dbpath) { + char *sql1 = "CREATE TABLE IF NOT EXISTS msgs (" + "id INTEGER PRIMARY KEY," + "sender TEXT COLLATE NOCASE," + "recipient TEXT COLLATE NOCASE," + "subject TEXT," + "oaddress TEXT," + "daddress TEXT," + "msgid TEXT," + "replyid TEXT," + "local INTEGER," + "sent INTEGER," + "private INTEGER," + "datewritten INTEGER," + "body TEXT);"; + char *sql2 = "CREATE TABLE IF NOT EXISTS msgptrs (" + "id INTEGER PRIMARY KEY," + "msgid INTEGER," + "userid INTEGER);"; + + sqlite3 *db; + int rc; + char *errmsg; + rc = sqlite3_open(dbpath, &db); + + sqlite3_busy_timeout(db, 10000); + + if (rc != SQLITE_OK) { + sqlite3_close(db); + return; + } + + rc = sqlite3_exec(db, sql1, 0, 0, &errmsg); + if (rc != SQLITE_OK) { + sqlite3_free(errmsg); + sqlite3_close(db); + return; + } + rc = sqlite3_exec(db, sql2, 0, 0, &errmsg); + if (rc != SQLITE_OK) { + sqlite3_free(errmsg); + sqlite3_close(db); + return; + } + +} + +bool sq3_beforefunc(void) { + time_t t1,t2; + struct tm *tp; + t1=time(NULL); + tp=gmtime(&t1); + tp->tm_isdst=-1; + t2=mktime(tp); + sq3_utcoffset=t2-t1; + return TRUE; +} + +bool sq3_afterfunc(bool success) { + return TRUE; +} + +bool sq3_importfunc(struct MemMessage *mm,struct Area *area) { + char buffer[1024]; + sqlite3 *db; + sqlite3_stmt *res; + int rc; + char *sql = "INSERT INTO msgs (sender, recipient, subject, oaddress, daddress, msgid, replyid, local, sent, private, datewritten, body) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + char *sender; + char *recipient; + char *subject; + char *oaddress; + char *daddress; + char *msgid; + char *replyid; + int local; + int sent; + int private; + time_t datewritten; + struct TextChunk *chunk; + uint32_t c,linebegin,linelen; + char *msgtext; + char *body; + + uint32_t msgsize,msgpos; + if (mm->From[0]) { + sender = strdup(mm->From); + } else { + sender = NULL; + } + if (mm->To[0]) { + recipient = strdup(mm->To); + } else { + recipient = NULL; + } + if (mm->Subject[0]) { + subject = strdup(mm->Subject); + } else { + subject = NULL; + } + if(mm->Area[0] == 0) { + oaddress = (char *)osAlloc(100); + Print4D(&mm->OrigNode, oaddress); + daddress = (char *)osAlloc(100); + Print4D(&mm->DestNode, daddress); + private = 1; + } else { + oaddress = (char *)osAlloc(100); + Print4D(&mm->Origin4D, oaddress); + daddress = NULL; + private = 0; + } + datewritten = FidoToTime(mm->DateTime); + datewritten -= sq3_utcoffset; + local = 0; + sent = 0; + + msgpos=0; + msgsize=0; + + replyid = NULL; + msgid = NULL; + + 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"); + return(FALSE); + } + } + + /* 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>=7 && strncmp(&msgtext[linebegin],"\x01""MSGID:",7)==0) + { + mystrncpy(buffer,&msgtext[linebegin+7],MIN(100,linelen-7)); + stripleadtrail(buffer); + msgid = strdup(buffer); + msgpos=linebegin; + } + else if(linelen>=7 && strncmp(&msgtext[linebegin],"\x01""REPLY:",7)==0) + { + mystrncpy(buffer,&msgtext[linebegin+7],MIN(100,linelen-7)); + stripleadtrail(buffer); + replyid = strdup(buffer); + msgpos=linebegin; + } + else if(linelen>=7 && strncmp(&msgtext[linebegin],"\x01""FLAGS:",7)==0) + { + 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) + { + msgpos=linebegin; + } + } + } + + if (msgsize == 0) { + msgtext = ""; + msgpos = 1; + } + + + body = (char *)osAlloc(msgpos + 1); + + memset(body, 0, msgpos+1); + memcpy(body, msgtext, msgpos); + + if (msgsize) { + osFree(msgtext); + } + + snprintf(buffer, 1024, "%s.sq3", area->Path); + sq3_init_msg_db(buffer); + + rc = sqlite3_open(buffer, &db); + sqlite3_busy_timeout(db, 10000); + if (rc != SQLITE_OK) { + sqlite3_close(db); + return FALSE; + } + + rc = sqlite3_prepare_v2(db, sql, -1, &res, 0); + if (rc != SQLITE_OK) { + sqlite3_close(db); + return FALSE; + } + + sqlite3_bind_text(res, 1, sender, -1, 0); + sqlite3_bind_text(res, 2, recipient, -1, 0); + sqlite3_bind_text(res, 3, subject, -1, 0); + sqlite3_bind_text(res, 4, oaddress, -1, 0); + sqlite3_bind_text(res, 5, daddress, -1, 0); + sqlite3_bind_text(res, 6, msgid, -1, 0); + sqlite3_bind_text(res, 7, replyid, -1, 0); + sqlite3_bind_int(res, 8, local); + sqlite3_bind_int(res, 9, sent); + sqlite3_bind_int(res, 10, private); + sqlite3_bind_int(res, 11, datewritten); + sqlite3_bind_text(res, 12, body, -1, 0); + + sqlite3_step(res); + sqlite3_finalize(res); + sqlite3_close(db); + + if (sender != NULL) { + osFree(sender); + } + if (recipient != NULL) { + osFree(recipient); + } + if (subject != NULL) { + osFree(subject); + } + if (oaddress != NULL) { + osFree(oaddress); + } + if (daddress != NULL) { + osFree(daddress); + } + + if (msgid != NULL) { + osFree(msgid); + } + if (replyid != NULL) { + osFree(replyid); + } + osFree(body); + return TRUE; +} + +void sq3_makekludge(struct MemMessage *mm,char *pre,char *data) +{ + char *buf; + uint32_t len = strlen(data); + + 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 sq3_rescanfunc(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)) { + sqlite3 *db; + sqlite3_stmt *res; + int rc; + char *sql = "SELECT id, sender, recipient, subject, oaddress, daddress, msgid, replyid, local, sent, private, datewritten, body FROM msgs ORDER BY datewritten DESC LIMIT %d"; + char buffer[1024]; + struct MemMessage *mm; + char domain[20]; + struct Node4D n4d; + char *msgtext; + snprintf(buffer, 1024, "%s.sq3", area->Path); + + sq3_init_msg_db(buffer); + + rc = sqlite3_open(buffer, &db); + sqlite3_busy_timeout(db, 10000); + if (rc != SQLITE_OK) { + sqlite3_close(db); + return FALSE; + } + sprintf(buffer, sql, max); + rc = sqlite3_prepare_v2(db, buffer, -1, &res, 0); + if (rc != SQLITE_OK) { + sqlite3_close(db); + return FALSE; + } + + while (sqlite3_step(res) == SQLITE_ROW && !ctrlc) { + mm = mmAlloc(); + if (area->AreaType == AREATYPE_NETMAIL) { + strcpy(mm->Area, ""); + mm->Attr = FLAG_CRASH | FLAG_LOCAL | FLAG_PVT; + } else { + strcpy(mm->Area, area->Tagname); + mm->Attr = FLAG_CRASH | FLAG_LOCAL; + } + mm->msgnum = sqlite3_column_int(res, 0); + if (sqlite3_column_text(res, 1) != NULL) { + mystrncpy(mm->From, (char *)sqlite3_column_text(res, 1), 36); + } + if (sqlite3_column_text(res, 2) != NULL) { + mystrncpy(mm->To, (char *)sqlite3_column_text(res, 2), 36); + } + + if (sqlite3_column_text(res, 3) != NULL) { + mystrncpy(mm->Subject, (char *)sqlite3_column_text(res, 3), 72); + } + if (sqlite3_column_text(res, 4) != NULL) { + if (Parse5D((char *)sqlite3_column_text(res, 4), &n4d, domain)) { + mm->OrigNode.Zone=n4d.Zone; + mm->OrigNode.Net=n4d.Net; + mm->OrigNode.Node=n4d.Node; + mm->OrigNode.Point=n4d.Point; + } + } + if (sqlite3_column_text(res, 5) != NULL) { + if (Parse5D((char *)sqlite3_column_text(res, 5), &n4d, domain)) { + mm->DestNode.Zone=n4d.Zone; + mm->DestNode.Net=n4d.Net; + mm->DestNode.Node=n4d.Node; + mm->DestNode.Point=n4d.Point; + } + } + if (sqlite3_column_text(res, 6) != NULL) { + sq3_makekludge(mm,"\x01" "MSGID: ", (char *)sqlite3_column_text(res, 6)); + } + if (sqlite3_column_text(res, 7) != NULL) { + sq3_makekludge(mm, "\x01" "REPLY: ", (char *)sqlite3_column_text(res, 7)); + } + MakeFidoDate(sqlite3_column_int(res, 11)+sq3_utcoffset,mm->DateTime); + + msgtext = strdup((char *)sqlite3_column_text(res, 12)); + + mm->Cost = 0; + + if(area->AreaType == AREATYPE_NETMAIL) + { + if(mm->OrigNode.Zone != mm->DestNode.Zone || (config.cfg_Flags & CFG_FORCEINTL)) + { + sprintf(buffer,"\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,buffer); + } + + if(mm->OrigNode.Point) + { + sprintf(buffer,"\x01" "FMPT %u\x0d",mm->OrigNode.Point); + mmAddLine(mm,buffer); + } + + if(mm->DestNode.Point) + { + sprintf(buffer,"\x01" "TOPT %u\x0d",mm->DestNode.Point); + mmAddLine(mm,buffer); + } + sprintf(buffer,"\x01RESCANNED %u:%u/%u.%u\x0d",area->Aka->Node.Zone, + area->Aka->Node.Net, + area->Aka->Node.Node, + area->Aka->Node.Point); + mmAddLine(mm,buffer); + } + + 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,strlen(msgtext)))) + { + + osFree(msgtext); + mmFree(mm); + continue; + } + } + osFree(msgtext); + mm->Flags |= MMFLAG_RESCANNED; + if(!(*handlefunc)(mm)) { + mmFree(mm); + continue; + } + + mmFree(mm); + } + sqlite3_close(db); + return TRUE; +} + +bool sq3_exportfunc(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)) { + sqlite3 *db; + sqlite3_stmt *res; + sqlite3_stmt *res2; + int rc; + char *sql = "SELECT id, sender, recipient, subject, oaddress, daddress, msgid, replyid, local, sent, private, datewritten, body FROM msgs WHERE local=1 and sent=0"; + char *sql2 = "UPDATE msgs SET sent=1 WHERE id=?"; + char buffer[1024]; + struct MemMessage *mm; + char domain[20]; + struct Node4D n4d; + char *msgtext; + snprintf(buffer, 1024, "%s.sq3", area->Path); + + sq3_init_msg_db(buffer); + + rc = sqlite3_open(buffer, &db); + sqlite3_busy_timeout(db, 10000); + if (rc != SQLITE_OK) { + sqlite3_close(db); + return FALSE; + } + + rc = sqlite3_prepare_v2(db, sql, -1, &res, 0); + if (rc != SQLITE_OK) { + sqlite3_close(db); + return FALSE; + } + + while (sqlite3_step(res) == SQLITE_ROW && !ctrlc) { + mm = mmAlloc(); + if (area->AreaType == AREATYPE_NETMAIL) { + strcpy(mm->Area, ""); + mm->Attr = FLAG_LOCAL | FLAG_PVT; + } else { + strcpy(mm->Area, area->Tagname); + mm->Attr = FLAG_CRASH | FLAG_LOCAL; + } + mm->msgnum = sqlite3_column_int(res, 0); + if (sqlite3_column_text(res, 1) != NULL) { + mystrncpy(mm->From, (char *)sqlite3_column_text(res, 1), 36); + } + if (sqlite3_column_text(res, 2) != NULL) { + mystrncpy(mm->To, (char *)sqlite3_column_text(res, 2), 36); + } + + if (sqlite3_column_text(res, 3) != NULL) { + mystrncpy(mm->Subject, (char *)sqlite3_column_text(res, 3), 72); + } + if (sqlite3_column_text(res, 4) != NULL) { + if (Parse5D((char *)sqlite3_column_text(res, 4), &n4d, domain)) { + mm->OrigNode.Zone=n4d.Zone; + mm->OrigNode.Net=n4d.Net; + mm->OrigNode.Node=n4d.Node; + mm->OrigNode.Point=n4d.Point; + } + } + if (sqlite3_column_text(res, 5) != NULL) { + if (Parse5D((char *)sqlite3_column_text(res, 5), &n4d, domain)) { + mm->DestNode.Zone=n4d.Zone; + mm->DestNode.Net=n4d.Net; + mm->DestNode.Node=n4d.Node; + mm->DestNode.Point=n4d.Point; + } + } + if (sqlite3_column_text(res, 6) != NULL) { + sq3_makekludge(mm,"\x01" "MSGID: ",(char *)sqlite3_column_text(res, 6)); + } + if (sqlite3_column_text(res, 7) != NULL) { + sq3_makekludge(mm, "\x01" "REPLY: ", (char *)sqlite3_column_text(res, 7)); + } + MakeFidoDate(sqlite3_column_int(res, 11)+sq3_utcoffset,mm->DateTime); + + msgtext = strdup((char *)sqlite3_column_text(res, 12)); + + mm->Cost = 0; + + if(area->AreaType == AREATYPE_NETMAIL) + { + if(mm->OrigNode.Zone != mm->DestNode.Zone || (config.cfg_Flags & CFG_FORCEINTL)) + { + sprintf(buffer,"\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,buffer); + } + + if(mm->OrigNode.Point) + { + sprintf(buffer,"\x01" "FMPT %u\x0d",mm->OrigNode.Point); + mmAddLine(mm,buffer); + } + + if(mm->DestNode.Point) + { + sprintf(buffer,"\x01" "TOPT %u\x0d",mm->DestNode.Point); + mmAddLine(mm,buffer); + } + } + + if(config.cfg_Flags & CFG_ADDTID) + AddTID(mm); + + 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,strlen(msgtext)))) + { + + osFree(msgtext); + mmFree(mm); + continue; + } + } + osFree(msgtext); + mm->Flags |= MMFLAG_EXPORTED; + if(!(*handlefunc)(mm)) { + mmFree(mm); + continue; + } + + scan_total++; + rc = sqlite3_prepare_v2(db, sql2, -1, &res2, 0); + if (rc != SQLITE_OK) { + mmFree(mm); + continue; + } + + sqlite3_bind_int(res2, 1, mm->msgnum); + sqlite3_step(res2); + sqlite3_finalize(res2); + mmFree(mm); + } + sqlite3_close(db); + return TRUE; +} diff --git a/utils/magimail/src/magimail/mb_sq3.h b/utils/magimail/src/magimail/mb_sq3.h new file mode 100644 index 0000000..7b5fab3 --- /dev/null +++ b/utils/magimail/src/magimail/mb_sq3.h @@ -0,0 +1,5 @@ +bool sq3_beforefunc(void); +bool sq3_afterfunc(bool success); +bool sq3_importfunc(struct MemMessage *mm,struct Area *area); +bool sq3_exportfunc(struct Area *area,bool (*handlefunc)(struct MemMessage *mm)); +bool sq3_rescanfunc(struct Area *area,uint32_t max,bool (*handlefunc)(struct MemMessage *mm)); diff --git a/utils/magimail/src/magimail/memmessage.c b/utils/magimail/src/magimail/memmessage.c new file mode 100644 index 0000000..83e7a34 --- /dev/null +++ b/utils/magimail/src/magimail/memmessage.c @@ -0,0 +1,539 @@ +#include "magimail.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); +} diff --git a/utils/magimail/src/magimail/memmessage.h b/utils/magimail/src/magimail/memmessage.h new file mode 100644 index 0000000..118bb5c --- /dev/null +++ b/utils/magimail/src/magimail/memmessage.h @@ -0,0 +1,83 @@ +#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 + diff --git a/utils/magimail/src/magimail/misc.c b/utils/magimail/src/magimail/misc.c new file mode 100644 index 0000000..2f0b041 --- /dev/null +++ b/utils/magimail/src/magimail/misc.c @@ -0,0 +1,938 @@ +#include "magimail.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; + char *tmp; + c=0; + + while(str[c]==' ') + c++; + + tmp = strdup(str); + strcpy(str,&tmp[c]); + free(tmp); +} + +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); +} diff --git a/utils/magimail/src/magimail/misc.h b/utils/magimail/src/magimail/misc.h new file mode 100644 index 0000000..28fc611 --- /dev/null +++ b/utils/magimail/src/magimail/misc.h @@ -0,0 +1,53 @@ +void 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); + + + + + + diff --git a/utils/magimail/src/magimail/nl.c b/utils/magimail/src/magimail/nl.c new file mode 100644 index 0000000..a76bfe1 --- /dev/null +++ b/utils/magimail/src/magimail/nl.c @@ -0,0 +1,39 @@ +#include "magimail.h" + +#ifdef NODELIST_CMNL +#include "nl_cmnl.h" +#endif + +#ifdef NODELIST_V7P +#include "nl_v7p.h" +#endif + +struct Nodelist AvailNodelists[] = +{ +#ifdef NODELIST_CMNL + { "CMNL", + "MagiMail 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 } +}; + diff --git a/utils/magimail/src/magimail/nl.h b/utils/magimail/src/magimail/nl.h new file mode 100644 index 0000000..3c03769 --- /dev/null +++ b/utils/magimail/src/magimail/nl.h @@ -0,0 +1,17 @@ +#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 diff --git a/utils/magimail/src/magimail/nl_cmnl.c b/utils/magimail/src/magimail/nl_cmnl.c new file mode 100644 index 0000000..a35a1af --- /dev/null +++ b/utils/magimail/src/magimail/nl_cmnl.c @@ -0,0 +1,65 @@ +#include "magimail.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); +} diff --git a/utils/magimail/src/magimail/nl_cmnl.h b/utils/magimail/src/magimail/nl_cmnl.h new file mode 100644 index 0000000..75d53f3 --- /dev/null +++ b/utils/magimail/src/magimail/nl_cmnl.h @@ -0,0 +1,5 @@ +bool 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); diff --git a/utils/magimail/src/magimail/nl_v7p.c b/utils/magimail/src/magimail/nl_v7p.c new file mode 100644 index 0000000..974d127 --- /dev/null +++ b/utils/magimail/src/magimail/nl_v7p.c @@ -0,0 +1,437 @@ +#include "magimail.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); +} +*/ diff --git a/utils/magimail/src/magimail/nl_v7p.h b/utils/magimail/src/magimail/nl_v7p.h new file mode 100644 index 0000000..6f462f5 --- /dev/null +++ b/utils/magimail/src/magimail/nl_v7p.h @@ -0,0 +1,5 @@ +bool 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); diff --git a/utils/magimail/src/magimail/outbound.c b/utils/magimail/src/magimail/outbound.c new file mode 100644 index 0000000..2de7c19 --- /dev/null +++ b/utils/magimail/src/magimail/outbound.c @@ -0,0 +1,999 @@ +#include "magimail.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; + char *buff2; + + 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); + buff2 = buf; + + if(buf[0]=='#') buff2 = &buf[1]; + if(buf[0]=='~') buff2 = &buf[1]; + if(buf[0]=='^') buff2 = &buf[1]; + if(buf[0]=='-') buff2 = &buf[1]; + if(stricmp(buff2,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); +} + diff --git a/utils/magimail/src/magimail/outbound.h b/utils/magimail/src/magimail/outbound.h new file mode 100644 index 0000000..5877b25 --- /dev/null +++ b/utils/magimail/src/magimail/outbound.h @@ -0,0 +1,7 @@ +#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); diff --git a/utils/magimail/src/magimail/pkt.c b/utils/magimail/src/magimail/pkt.c new file mode 100644 index 0000000..72d101f --- /dev/null +++ b/utils/magimail/src/magimail/pkt.c @@ -0,0 +1,960 @@ +#include "magimail.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); +} + diff --git a/utils/magimail/src/magimail/pkt.h b/utils/magimail/src/magimail/pkt.h new file mode 100644 index 0000000..35d403b --- /dev/null +++ b/utils/magimail/src/magimail/pkt.h @@ -0,0 +1,6 @@ +bool 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); + + diff --git a/utils/magimail/src/magimail/safedel.c b/utils/magimail/src/magimail/safedel.c new file mode 100644 index 0000000..946341b --- /dev/null +++ b/utils/magimail/src/magimail/safedel.c @@ -0,0 +1,24 @@ +#include "magimail.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); +} diff --git a/utils/magimail/src/magimail/safedel.h b/utils/magimail/src/magimail/safedel.h new file mode 100644 index 0000000..dc1d62b --- /dev/null +++ b/utils/magimail/src/magimail/safedel.h @@ -0,0 +1,2 @@ +bool SafeDelete(char *file); +void ProcessSafeDelete(void); diff --git a/utils/magimail/src/magimail/scan.c b/utils/magimail/src/magimail/scan.c new file mode 100644 index 0000000..e67f3d4 --- /dev/null +++ b/utils/magimail/src/magimail/scan.c @@ -0,0 +1,306 @@ +#include "magimail.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); +} + diff --git a/utils/magimail/src/magimail/scan.h b/utils/magimail/src/magimail/scan.h new file mode 100644 index 0000000..8dc11e1 --- /dev/null +++ b/utils/magimail/src/magimail/scan.h @@ -0,0 +1,4 @@ +bool Scan(void); +bool ScanList(char *file); +bool ScanDotJam(char *file); +bool ScanArea(char *tagname); diff --git a/utils/magimail/src/magimail/stats.c b/utils/magimail/src/magimail/stats.c new file mode 100644 index 0000000..b38f1ff --- /dev/null +++ b/utils/magimail/src/magimail/stats.c @@ -0,0 +1,191 @@ +#include "magimail.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); +} diff --git a/utils/magimail/src/magimail/stats.h b/utils/magimail/src/magimail/stats.h new file mode 100644 index 0000000..d51459a --- /dev/null +++ b/utils/magimail/src/magimail/stats.h @@ -0,0 +1,2 @@ +bool ReadStats(char *file); +bool WriteStats(char *file); diff --git a/utils/magimail/src/magimail/toss.c b/utils/magimail/src/magimail/toss.c new file mode 100644 index 0000000..d8fc48f --- /dev/null +++ b/utils/magimail/src/magimail/toss.c @@ -0,0 +1,326 @@ +#include "magimail.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); +} + diff --git a/utils/magimail/src/magimail/toss.h b/utils/magimail/src/magimail/toss.h new file mode 100644 index 0000000..b42ff15 --- /dev/null +++ b/utils/magimail/src/magimail/toss.h @@ -0,0 +1,3 @@ +bool TossFile(char *file); +bool TossDir(char *dir); + diff --git a/utils/magimail/src/magimail/version.h b/utils/magimail/src/magimail/version.h new file mode 100644 index 0000000..904dd6c --- /dev/null +++ b/utils/magimail/src/magimail/version.h @@ -0,0 +1,13 @@ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 0 +#define VERSION_PATCH 0 + +#define VERSION "1.0" + +#define TID_VERSION "1.0" + +#endif /* VERSION_H */ + diff --git a/utils/magimail/src/oslib/os.h b/utils/magimail/src/oslib/os.h new file mode 100644 index 0000000..9f941c7 --- /dev/null +++ b/utils/magimail/src/oslib/os.h @@ -0,0 +1,20 @@ +#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 + diff --git a/utils/magimail/src/oslib/osdir.h b/utils/magimail/src/oslib/osdir.h new file mode 100644 index 0000000..ba6bccb --- /dev/null +++ b/utils/magimail/src/oslib/osdir.h @@ -0,0 +1,20 @@ +#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 diff --git a/utils/magimail/src/oslib/osfile.h b/utils/magimail/src/oslib/osfile.h new file mode 100644 index 0000000..8b55d20 --- /dev/null +++ b/utils/magimail/src/oslib/osfile.h @@ -0,0 +1,30 @@ +#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 diff --git a/utils/magimail/src/oslib/osmem.h b/utils/magimail/src/oslib/osmem.h new file mode 100644 index 0000000..c9b3cef --- /dev/null +++ b/utils/magimail/src/oslib/osmem.h @@ -0,0 +1,10 @@ +#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 diff --git a/utils/magimail/src/oslib/osmisc.h b/utils/magimail/src/oslib/osmisc.h new file mode 100644 index 0000000..204ea4f --- /dev/null +++ b/utils/magimail/src/oslib/osmisc.h @@ -0,0 +1,21 @@ +#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 diff --git a/utils/magimail/src/oslib/ospattern.h b/utils/magimail/src/oslib/ospattern.h new file mode 100644 index 0000000..a56a4e9 --- /dev/null +++ b/utils/magimail/src/oslib/ospattern.h @@ -0,0 +1,10 @@ +#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 diff --git a/utils/magimail/src/oslib_linux/Makefile b/utils/magimail/src/oslib_linux/Makefile new file mode 100644 index 0000000..5ee9b75 --- /dev/null +++ b/utils/magimail/src/oslib_linux/Makefile @@ -0,0 +1,34 @@ +INCDIR = ../ + +CC = gcc $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -I $(INCDIR) -DPLATFORM_LINUX -g +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 + diff --git a/utils/magimail/src/oslib_linux/os.c b/utils/magimail/src/oslib_linux/os.c new file mode 100644 index 0000000..96de05c --- /dev/null +++ b/utils/magimail/src/oslib_linux/os.c @@ -0,0 +1,13 @@ +#include + +#include + +bool osInit(void) +{ + return(TRUE); +} + +void osEnd(void) +{ +} + diff --git a/utils/magimail/src/oslib_linux/os_linux.h b/utils/magimail/src/oslib_linux/os_linux.h new file mode 100644 index 0000000..d9dfef6 --- /dev/null +++ b/utils/magimail/src/oslib_linux/os_linux.h @@ -0,0 +1,33 @@ +#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 + diff --git a/utils/magimail/src/oslib_linux/osdir.c b/utils/magimail/src/oslib_linux/osdir.c new file mode 100644 index 0000000..2358e3b --- /dev/null +++ b/utils/magimail/src/oslib_linux/osdir.c @@ -0,0 +1,100 @@ +#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); +} diff --git a/utils/magimail/src/oslib_linux/osfile.c b/utils/magimail/src/oslib_linux/osfile.c new file mode 100644 index 0000000..98ee747 --- /dev/null +++ b/utils/magimail/src/oslib_linux/osfile.c @@ -0,0 +1,143 @@ +#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); +} diff --git a/utils/magimail/src/oslib_linux/osmem.c b/utils/magimail/src/oslib_linux/osmem.c new file mode 100644 index 0000000..dbbdae9 --- /dev/null +++ b/utils/magimail/src/oslib_linux/osmem.c @@ -0,0 +1,19 @@ +#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); +} diff --git a/utils/magimail/src/oslib_linux/osmisc.c b/utils/magimail/src/oslib_linux/osmisc.c new file mode 100644 index 0000000..15b0989 --- /dev/null +++ b/utils/magimail/src/oslib_linux/osmisc.c @@ -0,0 +1,99 @@ +#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; +} diff --git a/utils/magimail/src/oslib_linux/ospattern.c b/utils/magimail/src/oslib_linux/ospattern.c new file mode 100644 index 0000000..bb95842 --- /dev/null +++ b/utils/magimail/src/oslib_linux/ospattern.c @@ -0,0 +1,40 @@ +#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); +} diff --git a/utils/magimail/src/shared/expr.c b/utils/magimail/src/shared/expr.c new file mode 100644 index 0000000..d010367 --- /dev/null +++ b/utils/magimail/src/shared/expr.c @@ -0,0 +1,423 @@ +#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); + } +} + diff --git a/utils/magimail/src/shared/expr.h b/utils/magimail/src/shared/expr.h new file mode 100644 index 0000000..03a2de7 --- /dev/null +++ b/utils/magimail/src/shared/expr.h @@ -0,0 +1,11 @@ +struct 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); + diff --git a/utils/magimail/src/shared/fidonet.h b/utils/magimail/src/shared/fidonet.h new file mode 100644 index 0000000..dff3c33 --- /dev/null +++ b/utils/magimail/src/shared/fidonet.h @@ -0,0 +1,92 @@ +/* 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 diff --git a/utils/magimail/src/shared/jblist.c b/utils/magimail/src/shared/jblist.c new file mode 100644 index 0000000..c1ad164 --- /dev/null +++ b/utils/magimail/src/shared/jblist.c @@ -0,0 +1,96 @@ +#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); + } +} diff --git a/utils/magimail/src/shared/jblist.h b/utils/magimail/src/shared/jblist.h new file mode 100644 index 0000000..b36c292 --- /dev/null +++ b/utils/magimail/src/shared/jblist.h @@ -0,0 +1,21 @@ +#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 diff --git a/utils/magimail/src/shared/jbstrcpy.c b/utils/magimail/src/shared/jbstrcpy.c new file mode 100644 index 0000000..f1eb8e8 --- /dev/null +++ b/utils/magimail/src/shared/jbstrcpy.c @@ -0,0 +1,78 @@ +#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; +} + diff --git a/utils/magimail/src/shared/mystrncpy.h b/utils/magimail/src/shared/mystrncpy.h new file mode 100644 index 0000000..ecfe12e --- /dev/null +++ b/utils/magimail/src/shared/mystrncpy.h @@ -0,0 +1,8 @@ +#ifndef SHARED_MYSTRNCPY_H +#define SHARED_MYSTRNCPY_H + +#include "shared/types.h" + +void mystrncpy(char *dest,char *src,uint32_t len); + +#endif diff --git a/utils/magimail/src/shared/parseargs.c b/utils/magimail/src/shared/parseargs.c new file mode 100644 index 0000000..b05b7cf --- /dev/null +++ b/utils/magimail/src/shared/parseargs.c @@ -0,0 +1,100 @@ +#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"); +} + diff --git a/utils/magimail/src/shared/parseargs.h b/utils/magimail/src/shared/parseargs.h new file mode 100644 index 0000000..b07e8fa --- /dev/null +++ b/utils/magimail/src/shared/parseargs.h @@ -0,0 +1,24 @@ +#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 diff --git a/utils/magimail/src/shared/path.c b/utils/magimail/src/shared/path.c new file mode 100644 index 0000000..9f60284 --- /dev/null +++ b/utils/magimail/src/shared/path.c @@ -0,0 +1,57 @@ +#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); +} + + + + + + + + + + + + diff --git a/utils/magimail/src/shared/path.h b/utils/magimail/src/shared/path.h new file mode 100644 index 0000000..ea0ae6f --- /dev/null +++ b/utils/magimail/src/shared/path.h @@ -0,0 +1,5 @@ + +void MakeFullPath(char *path,char *file,char *dest,uint32_t destsize); +char *GetFilePart(char *str); + + diff --git a/utils/magimail/src/shared/storedmsg.h b/utils/magimail/src/shared/storedmsg.h new file mode 100644 index 0000000..adc552a --- /dev/null +++ b/utils/magimail/src/shared/storedmsg.h @@ -0,0 +1,21 @@ +struct 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 */ +}; diff --git a/utils/magimail/src/shared/types.h b/utils/magimail/src/shared/types.h new file mode 100644 index 0000000..1184d9d --- /dev/null +++ b/utils/magimail/src/shared/types.h @@ -0,0 +1,11 @@ +#ifndef SHARED_TYPES_H +#define SHARED_TYPES_H + +#ifndef NO_TYPEDEF_BOOL +typedef int bool; +#endif + +#define FALSE 0 +#define TRUE 1 + +#endif diff --git a/utils/magimail/src/tools/magiexport.c b/utils/magimail/src/tools/magiexport.c new file mode 100644 index 0000000..17b7c40 --- /dev/null +++ b/utils/magimail/src/tools/magiexport.c @@ -0,0 +1,358 @@ +#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); +} + diff --git a/utils/magimail/src/tools/magigetnode.c b/utils/magimail/src/tools/magigetnode.c new file mode 100644 index 0000000..e5e89d2 --- /dev/null +++ b/utils/magimail/src/tools/magigetnode.c @@ -0,0 +1,156 @@ +#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); +} + diff --git a/utils/magimail/src/tools/magilist.c b/utils/magimail/src/tools/magilist.c new file mode 100644 index 0000000..61f4d17 --- /dev/null +++ b/utils/magimail/src/tools/magilist.c @@ -0,0 +1,341 @@ +#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 */ + diff --git a/utils/magimail/src/tools/magilistout.c b/utils/magimail/src/tools/magilistout.c new file mode 100644 index 0000000..afab58b --- /dev/null +++ b/utils/magimail/src/tools/magilistout.c @@ -0,0 +1,726 @@ +#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 MagiMail) */ + +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 MagiMail */ + +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); +} + + diff --git a/utils/magimail/src/tools/magimaint.c b/utils/magimail/src/tools/magimaint.c new file mode 100644 index 0000000..480c5e4 --- /dev/null +++ b/utils/magimail/src/tools/magimaint.c @@ -0,0 +1,1042 @@ +#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,"MagiMail"); + 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 & JAM_MSG_DELETED)) + { + /* Not already deleted */ + + if(verbose) + printf(" Deleting message #%u by number\n",basenum+num); + + Header_S.Attribute |= JAM_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 & JAM_MSG_DELETED)) + { + /* Not already deleted and too old*/ + + if(verbose) + printf(" Deleting message #%u by date\n",basenum+num); + + Header_S.Attribute |= JAM_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 & JAM_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); +} diff --git a/utils/magimail/src/tools/magistats.c b/utils/magimail/src/tools/magistats.c new file mode 100644 index 0000000..ffc953c --- /dev/null +++ b/utils/magimail/src/tools/magistats.c @@ -0,0 +1,707 @@ +#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); +} + + + + + + diff --git a/utils/magimail/src/tools/magiwrite.c b/utils/magimail/src/tools/magiwrite.c new file mode 100644 index 0000000..316cb5e --- /dev/null +++ b/utils/magimail/src/tools/magiwrite.c @@ -0,0 +1,359 @@ +#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 MagiMail" + +#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); +}