Squish Developers Kit Version 2.00 Created May 23rd, 1994 Documentation produced by Scott Dudley Copyright 1991-1994 by SCI Communications. All rights reserved. Maximus and Squish are trademarks of SCI Communications. TABLE OF CONTENTS INTRODUCTION . . . . . . . . . . . . . . . . . . . . . . . . 1 COPYRIGHT AND DISTRIBUTION RESTRICTIONS . . . . . . . . . . . 2 Distribution Policy . . . . . . . . . . . . . . . . . . 2 No Warranty . . . . . . . . . . . . . . . . . . . . . . 2 Author Contact Information . . . . . . . . . . . . . . . 3 MSGAPI FUNCTION REFERENCE . . . . . . . . . . . . . . . . . . 4 Initialization/Termination Functions . . . . . . . . . . 4 MsgOpenApi . . . . . . . . . . . . . . . . . . . . 4 MsgCloseApi . . . . . . . . . . . . . . . . . . . . 6 Area-Oriented Functions . . . . . . . . . . . . . . . . 6 MsgOpenArea . . . . . . . . . . . . . . . . . . . . 6 MsgCloseArea . . . . . . . . . . . . . . . . . . . 7 MsgValidate . . . . . . . . . . . . . . . . . . . . 8 MsgGetHighWater . . . . . . . . . . . . . . . . . . 8 MsgSetHighWater . . . . . . . . . . . . . . . . . . 9 MsgGetCurMsg . . . . . . . . . . . . . . . . . . . 9 MsgGetNumMsg . . . . . . . . . . . . . . . . . . . 9 MsgGetHighMsg . . . . . . . . . . . . . . . . . . . 9 MsgLock . . . . . . . . . . . . . . . . . . . . . . 10 MsgUnlock . . . . . . . . . . . . . . . . . . . . . 10 MsgInvalidHarea . . . . . . . . . . . . . . . . . . 10 Message-Oriented Functions . . . . . . . . . . . . . . . 11 MsgOpenMsg . . . . . . . . . . . . . . . . . . . . 11 MsgCloseMsg . . . . . . . . . . . . . . . . . . . . 12 MsgReadMsg . . . . . . . . . . . . . . . . . . . . 12 MsgWriteMsg . . . . . . . . . . . . . . . . . . . . 15 MsgKillMsg . . . . . . . . . . . . . . . . . . . . 17 MsgGetCurPos . . . . . . . . . . . . . . . . . . . 18 MsgSetCurPos . . . . . . . . . . . . . . . . . . . 18 MsgGetTextLen . . . . . . . . . . . . . . . . . . . 18 MsgGetCtrlLen . . . . . . . . . . . . . . . . . . . 19 MsgInvalidHmsg . . . . . . . . . . . . . . . . . . 19 UMSGID Translation Functions . . . . . . . . . . . . . . 19 MsgMsgnToUid . . . . . . . . . . . . . . . . . . . 19 MsgUidToMsgn . . . . . . . . . . . . . . . . . . . 20 CtrlInfo Manipulation Functions . . . . . . . . . . . . 20 MsgGetCtrlToken . . . . . . . . . . . . . . . . . . 20 MsgFreeCtrlToken . . . . . . . . . . . . . . . . . 21 MsgRemoveToken . . . . . . . . . . . . . . . . . . 21 MsgGetNumKludges . . . . . . . . . . . . . . . . . 21 MsgCvt4D . . . . . . . . . . . . . . . . . . . . . 22 MsgCreateCtrlBuf . . . . . . . . . . . . . . . . . 23 MsgCvtCtrlToKludge . . . . . . . . . . . . . . . . 24 MsgFreeCtrlBuf . . . . . . . . . . . . . . . . . . 24 Squish-Specific Functions . . . . . . . . . . . . . . . 24 SquishHash . . . . . . . . . . . . . . . . . . . . 24 SquishSetMaxMsg . . . . . . . . . . . . . . . . . . 25 API Changes from MsgAPI Version 0 . . . . . . . . . . . 25 BUILDING MSGAPI APPLICATIONS . . . . . . . . . . . . . . . . 26 Source File Requirements . . . . . . . . . . . . . . . . 26 Compiling Source Files . . . . . . . . . . . . . . . . . 26 Linking Your Application . . . . . . . . . . . . . . . . 27 Building the Sample Applications . . . . . . . . . . . . 28 BUILDING THE MSGAPI SOURCE . . . . . . . . . . . . . . . . . 29 OS/2 DLL DEVELOPMENT . . . . . . . . . . . . . . . . . . . . 30 Sample Feature DLLs . . . . . . . . . . . . . . . . . . 30 KILLRCAT.DLL . . . . . . . . . . . . . . . . . . . 30 MSGTRACK.DLL . . . . . . . . . . . . . . . . . . . 31 Feature DLL Programming Interface . . . . . . . . . . . 31 Building Feature DLLs . . . . . . . . . . . . . . . . . 37 SQUISH FILE FORMAT SPECIFICATION . . . . . . . . . . . . . . 38 Squish Philosophy . . . . . . . . . . . . . . . . . . . 38 Squish Data Types . . . . . . . . . . . . . . . . . . . 39 Data File Format . . . . . . . . . . . . . . . . . . . . 41 Index File Format . . . . . . . . . . . . . . . . . . . 48 Message Links . . . . . . . . . . . . . . . . . . . . . 50 Reading Messages . . . . . . . . . . . . . . . . . . . . 51 Writing Messages . . . . . . . . . . . . . . . . . . . . 52 Deleting Messages . . . . . . . . . . . . . . . . . . . 53 Concurrency Considerations . . . . . . . . . . . . . . . 53 INTRODUCTION This document serves as a reference guide for version 2.0 of the Squish Developers Kit. The primary purpose of this document is to describe the C Application Programming Interface (API) for Squish bases, also known as MsgAPI. Information is included on rebuilding the MsgAPI source, compiling applications that use MsgAPI, and a description of each function within the MsgAPI. The standard MsgAPI distribution supports Turbo C, Borland C, WATCOM C, and Microsoft C. This document does not provide a tutorial on using the MsgAPI, but those with a reasonable amount of programming experience should have little trouble in using MsgAPI. In addition, this document includes information on the "feature DLL" interface that is supported by the OS/2 version of the Squish tosser/scanner program. These hooks allow third-party developers to manipulate messages when they are being processed by Squish. These hooks are called when Squish is tossing and packing messages. Finally, this document also describes the physical layout of the Squish file format. Although use of the MsgAPI is recommended, this information should allow third-party developers to access the Squish base directly, without going through a set of API calls. WARNING! THE SQUISH DEVELOPER'S KIT IS NOT A SUPPORTED PRODUCT. We would be interested in hearing of any problems that you have with this product, but we cannot provide technical support for MsgAPI users. While we have tried to ensure that the material provided in this kit is correct, we also cannot guarantee that the code and documentation will function correctly on all systems. Please read the following section for information on distribution terms and the product warranty. Squish Developers Kit Version 2.0 - Page 1 COPYRIGHT AND DISTRIBUTION RESTRICTIONS All of the source code, header files and documentation within the Squish Developers Kit is copyright 1991-1994 by SCI Communications. All rights reserved. Distribution Policy Although the MsgAPI code is copyrighted, you are granted a limited license to modify or usr MsgAPI in your own applications. 1) You may use the MsgAPI code as part of any type of application, including freeware, shareware, or commercial programs. No royalties or licensing fees are required. 2) This code must not be sold on its own. While it is permissible to sell an application that uses or contains the MsgAPI code, you may not charge any extra fee (above the base cost of the product) for just providing the user with a copy of the Squish Developers kit. Also, you may not charge any extra fee (above the base cost of the product) for providing Squish base support within your program. 3) If you use this code in your application, you must give credit for the MsgAPI code and indicate that "Squish" is a trademark of SCI Communications. 4) If you wish to identify your application as being compatible with the Squish message format, you must ensure that the original MsgAPI code is capable of reading and writing to the message bases that are created by your program. If the original MsgAPI code is unable to read the files created by your program, you may not label your program as Squish- compatible. AS LONG AS THE ABOVE FOUR CONDITIONS ARE FOLLOWED, MSGAPI MAY BE INCORPORATED INTO ANY TYPE OF APPLICATION WITHOUT CHARGE, REGARDLESS OF THE NATURE OF THE APPLICATION (COMMERCIAL, SHAREWARE, OR FREEWARE). No Warranty BECAUSE THE SQUISH DEVELOPERS KIT (SDK) IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO WARRANTY. EXCEPT WHEN OTHERWISE STATED IN WRITING, SCI COMMUNICATIONS AND/OR OTHER PARTIES PROVIDE THE SDK "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF SQUISH, AND THE ACCURACY OF ITS ASSOCIATED DOCUMENTATION, IS WITH Squish Developers Kit Version 2.0 - Page 2 YOU. SHOULD THE SDK OR ITS ASSOCIATED DOCUMENTATION PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT WILL SCI COMMUNICATIONS BE RESPONSIBLE IN ANY WAY FOR THE BEHAVIOUR OF MODIFIED VERSIONS OF THE SDK. IN NO EVENT WILL SCI COMMUNICATIONS AND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE SDK AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THE SDK, EVEN IF SCI COMMUNICATIONS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. Author Contact Information If you make any modifications to this code, and if you feel that the author would be interested in the changes, please forward the source code to the author. You can contact the author at any of the addresses listed below: FidoNet: 1:249/106 Internet: sjd@f106.n249.z1.fidonet.org CompuServe: >INTERNET:sjd@f106.n249.z1.fidonet.org BBS: (613) 634-3058 (V.32bis) Surface mail: 777 Downing St. Kingston, Ont. Canada K7M 5N3 The author can also be reached through the FidoNet EchoMail conferences called MUFFIN (Maximus support) and TUB (Squish support). Sending correspondence via electronic mail is strongly preferred, and the use of surface mail is discouraged. However, if you really have to send paper mail (and expect to receive a reply), please enclose a self-addressed, stamped envelope. (Users outside of Canada should include an international postal reply coupon instead of a stamp.) DO NOT ATTEMPT TO CONTACT THE AUTHOR BY TELEPHONE! VOICE SUPPORT WILL NOT BE PROVIDED FOR THIS PRODUCT! Squish Developers Kit Version 2.0 - Page 3 MSGAPI FUNCTION REFERENCE This section describes all of the functions in the MsgAPI that can be used by application programs. Initialization/Termination Functions MsgOpenApi sword MsgOpenApi(struct _minf *minf); The MsgOpenApi function is used to initialize the MsgAPI. This function must be called before any of the other API functions are called, or else the results are undefined. This function serves to initialize any needed structures, and to prepare the message bases for use. This function accepts one argument: a pointer to a structure containing information about the application. This structure contains the following information: struct _minf { /* The following fields are required for all * MsgAPI clients: */ word req_version; word def_zone; word haveshare; /* The following fields are required when * req_version >= 1: */ void OS2FAR * (MAPIENTRY *palloc)(size_t size); void (MAPIENTRY *pfree)(void OS2FAR *ptr); void OS2FAR * (MAPIENTRY *repalloc)(void OS2FAR *ptr, size_t size); void far * (MAPIENTRY *farpalloc)(size_t size); void (MAPIENTRY *farpfree)(void far *ptr); void far * (MAPIENTRY *farrepalloc)(void far *ptr, size_t size); }; 'req_version' indicates the MsgAPI revision level that the application is requesting. The compile-time revision level can always be accessed using the constant 'MSGAPI_VERSION'. 'def_zone' should contain a default FidoNet zone number. Squish Developers Kit Version 2.0 - Page 4 Certain message systems, such as the FTSC-0001 *.MSG format, do not store zone information with each message. When the API encounters such a message and no zone is present, the specified zone will be used instead. A 'def_zone' of 0 indicates that nothing is to be inferred about the zone number of a message, and in that case, the API functions will return 0 as the zone number for any message with an unknown zone. 'haveshare' is automatically filled in by the internal API routines, and this flag indicates whether or not the DOS "SHARE.EXE" program is currently loaded. Note that SHARE must always be loaded to access Squish-format bases in a multitasking or network environment. This field is not used in the OS/2 version of the MsgAPI. If 'req_version' is more than or equal to 1, the final six fields in the _minf structure must be provided. These fields are memory allocation hooks that MsgAPI will call whenever it needs to allocate memory. (If req_version is 0, or if one of the function pointers in this structure is NULL, then MsgAPI will use its own memory allocation routines.) 'palloc' is called to allocate a block of near memory. This function should behave in the same manner as the ANSI malloc() function. If this field is NULL, the MsgAPI will use its own malloc() function. 'pfree' is called to free a block of near memory. This function should behave in the same manner as the ANSI free() function. If this field is NULL, the MsgAPI will use its own free() function. 'repalloc' is called to reallocate a block of near memory. This function should behave in the same manner as the ANSI realloc() function. If this field is NULL, the MsgAPI will use its own realloc() function. 'farpalloc' is called to allocate a block of far memory. This function should behave in the same manner as the ANSI malloc() function, except that a far pointer should be returned. If this field is NULL, MsgAPI will use its own malloc() function. 'farpfree' is called to free a block of far memory. This function should behave in the same manner as the ANSI free() function, except that a far pointer should be accepted. If this field is NULL, MsgAPI will use its own free() function. 'farrepalloc' is called to reallocate a block of far memory. This function should behave in the same manner as the ANSI Squish Developers Kit Version 2.0 - Page 5 realloc() function, except that a far pointer should be accepted and returned. If this field is NULL, MsgAPI will use its own realloc() function. MsgOpenApi() returns a value of 0 if the initialization was performed successfully, and -1 if a problem was encountered. MsgCloseApi sword MsgCloseApi(void); This function is used to deinitialize the MsgAPI. This function performs any clean-up actions which may be necessary, including the closing of files and releasing allocated memory. This function should be called before the application terminates. MsgCloseApi returns a value of 0 if the API was successfully deinitialized, and -1 otherwise. Area-Oriented Functions MsgOpenArea HAREA MsgOpenArea(byte *name, word mode, word type); This function is used to open or create a message area. This function accepts three parameters: 'name' is the name of the message area to open. The contents of this string are implementation-defined. (See 'type' for more information.) 'mode' is the mode with which the area should be opened. Values for 'mode' are as follows: MSGAREA_NORMAL Open the message area in a normal access mode. If the area does not exist, this function fails. MSGAREA_CRIFNEC Open the message area in a normal access mode. If the area does not exist, the MsgAPI attempts to create the area. If the area cannot be created, this function fails. MSGAREA_CREATE Create the message area. If the area already exists, it is truncated or started anew with no messages. If the area cannot be created, this function Squish Developers Kit Version 2.0 - Page 6 fails. 'type' specifies the type of message area to open. 'type' can have any of the following values: MSGTYPE_SDM Star Dot MSG (SDM). This specifies a FTSC-0001 compatible access mode, and it instructs the MsgAPI to create and read Fido-compatible messages for this area. If MSGTYPE_SDM is specified, 'name' should contain the path to the *.MSG directory. MSGTYPE_SQUISH Squish (*.SQ?) format. This specifies that the proprietary Squish message format is to be used for this area. 'name' should give the path and root name (eight characters) for the message area. In addition, if the mask 'MSGTYPE_ECHO' is bitwise 'OR'ed with the 'MSGTYPE_SDM' value, the area in question will be treated as a FidoNet-style echomail area. This instructs the MsgAPI to keep high-water mark information in the 1.MSG file, and to stop the normal MsgAPI functions from writing to the first message in each area. Other message formats have a cleaner way of storing the high-water mark, so this mask is only required for *.MSG areas. Other values for 'type' are currently reserved. If this function succeeds in opening the message area, an HAREA handle is returned. This handle does not contain any information which can be used directly by the caller; all interaction should be performed through the MsgAPI functions only. If this function fails, NULL is returned, and the global 'msgapierr' variable is set to one of the following values: MERR_NOMEM Not enough memory for requested task MERR_NOENT The area did not exist or could not be created. MERR_BADF The message area is structurally damaged. MsgCloseArea sword MsgCloseArea(HAREA ha); This function serves to "close" a message area. This function performs all clean-up actions necessary, such as Squish Developers Kit Version 2.0 - Page 7 closing files, changing directories, and so on. The function accepts one argument, which must be an HAREA handle returned by the MsgOpenArea function. The MsgCloseArea function should be called for each area opened by MsgOpenArea. If the area was successfully closed, MsgCloseArea returns 0. Otherwise, a value of -1 is returned and msgapierr is set to one of the following: MERR_BADH An invalid handle was passed to the function. MERR_EOPEN Messages are still "open" in this area, so the area could not be closed. MsgValidate sword MsgValidate(word type, byte *name); The MsgValidate function validates a particular message area, determining whether or not the area exists and if it is valid. 'type' is the type of the message area, using the same constants as specified for MsgOpenARea. 'name' is the name of the message area, using the same format as specified for MsgOpenArea. MsgValidate returns the value 1 if the area exists and is valid. Otherwise. MsgValidate returns 0. MsgGetHighWater dword MsgGetHighWater(HAREA ha); The MsgGetHighWater function returns the 'high water marker' for the current area. This number represents the highest message number that was processed by a message export or import utility. 'ha' is a message area handle, as returned by MsgOpenArea. The high water marker is automatically adjusted when messages are killed. The MsgGetHighWater function returns the high water mark number on success, or 0 on error and sets msgapierr to: Squish Developers Kit Version 2.0 - Page 8 MERR_BADH MsgSetHighWater sword MsgSetHighWater(HAREA mh, dword hwm); The MsgSetHighWater function sets the 'high water marker' for the current area. 'ha' is a message area handle, as returned by MsgOpenArea. 'hwm' is the new high water marker to use for the specified area. The MsgGetHighWater function returns 0 on success, or -1 on error and sets msgapierr to: MERR_BADH MsgGetCurMsg dword MsgGetCurMsg(HAREA ha); The MsgGetCurMsg function returns the number of the last message accessed with MsgOpenMsg. 'ha' is a message area handle, as returned by MsgOpenArea. MsgGetCurMsg returns the current message number on success, or 0 if there is no current message. MsgGetNumMsg dword MsgGetNumMsg(HAREA ha); The MsgGetNumMsg function returns the number of messages in the current message area. On error, MsgGetNumMsg returns (dword)-1 and sets msgapierr to: MERR_BADH MsgGetHighMsg dword MsgGetHighMsg(HAREA mh); The MsgGetHighMsg function returns the number of the highest message in the specified area. On error, MsgGetHighMsg returns (dword)-1 and sets msgapierr to: Squish Developers Kit Version 2.0 - Page 9 MERR_BADH MsgLock sword MsgLock(HAREA ha); The MsgLock function 'locks' a message area for exclusive access. This function may enable buffering or otherwise improve performance, so it is advised that MsgLock be called if speed is a concern when accessing the area. All of the MsgAPI functions automatically perform file locking and sharing internally, so this function is only required when high performance is necessary. 'ha' is a message area handle, as returned by the MsgOpenArea function. MsgLock returns 0 on success. On error, MsgLock returns -1 and sets msgapierr to one of the following: MERR_BADH MsgUnlock sword MsgUnlock(HAREA ha); The MsgUnlock function unlocks a previously-locked message area. 'ha' is a message area handle, as returned by MsgOpenMsg. MsgUnlock returns 0 on success, or it returns -1 on error and sets msgapierr to: MERR_BADH MsgInvalidHarea sword MsgInvalidHarea(HAREA ha); The MsgInvalidHarea function tests the given message area handle for validity. If the message area handle is invalid, this function returns TRUE and sets msgapierr to MERR_BADH. Otherwise, a value of FALSE will be returned. returned. Squish Developers Kit Version 2.0 - Page 10 Message-Oriented Functions MsgOpenMsg HMSG MsgOpenMsg(HAREA mh, word mode, dword msgn); This function "opens" a message for access, and it must be used to read from or write to a given message. The function accepts three arguments: 'mh' is a message area handle, as returned by the MsgOpenArea function. 'mode' is an access flag, containing one of the following manifest constants: MOPEN_CREATE Create a new message. This mode should only be used for creating new messages. MOPEN_READ Open an existing message for reading ONLY. MOPEN_WRITE Open an existing message for writing ONLY. MOPEN_RW Open an existing message for reading AND writing. 'msgn' is the specified message number to open. If mode is either MOPEN_READ, MOPEN_WRITE or MOPEN_RW, the message number must currently exist in the specified area. If mode is set to MOPEN_CREATE, a value of 0 for 'msgn' indicates that a new message should be created, and assigned a number one higher than the current highest message. If 'msgn' is non-zero, but MOPEN_CREATE is set to the number of a currently-existing message, the specified message will be truncated and the new message will take its place. For MOPEN_READ or MOPEN_RW, the following constants can also be passed in place of 'msgn': MSGNUM_CUR Open the last message which was accessed by MsgOpenMsg. MSGNUM_PREV Open the message prior to the last message accessed by MsgOpenMsg. MSGNUM_NEXT Open the message after the last message accessed by MsgOpenMsg. The MsgAPI maintains the number of the last message opened by MsgOpenMsg, which is used when processing these constants. (See also MsgGetCurMsg.) Squish Developers Kit Version 2.0 - Page 11 If the message was successfully opened, the MsgOpenMsg function will return a HMSG handle. Otherwise, a value of NULL is returned, and msgapierr will be set to one of the following: MERR_NOENT MERR_NOMEM MERR_BADF MERR_BADA MERR_BADH MsgCloseMsg sword MsgCloseMsg(HMSG hmsg); The MsgCloseMsg function serves to "close" a message which has been previously opened by MsgOpenMsg. All messages should be closed after use, or else data loss may result. This function accepts a single argument, which is the message handle that was returned by MsgOpenMsg. If the message was successfully closed, this function returns 0. Otherwise, a value of -1 is returned, and msgapierr is set to: MERR_BADH MsgReadMsg dword MsgReadMsg(HMSG hmsg, dword ofs, dword bytes, byte *text, dword cbyt, byte *ctxt); The MsgReadMsg function is used to read a message from disk. This function can be used to read all parts of a message, including the message header, message body, and control information. 'hmsg' is a message handle, as returned by the MsgOpenMsg function. The message in question must have been opened with a mode of either MOPEN_READ or MOPEN_RW. 'msg' is a pointer to an XMSG (extended message) structure. The format of this structure is detailed in the "Squish File Format Specification" section, but it contains all of the message information that is found in the message header, including the to/from/subject fields, origination and arrival dates, 4D origination and destination addresses, and so forth. (See the appendices for specific information on the XMSG structure itself.) If the application wishes to Squish Developers Kit Version 2.0 - Page 12 read the header of a given message, this argument should point to an XMSG structure. Otherwise, this argument should be NULL, which informs the API that the message header does not need to be read. 'ofs' is used for reading message text in a multiple-pass environment. This defines the offset in the message body from which the API should start reading. To start reading from the beginning of the message, a value of 0L should be given. Otherwise, the offset into the message (in bytes) should be given for this argument. If the application does not wish to read the message body, this argument should be set to 0L. 'bytes' represents the maximum number of bytes to read from the message. Fewer bytes may be read, but the API will read no more than 'bytes' during this call. (See 'text', and also this function's return value.) If the application does not wish to read the message body, this argument should be set to 0L. 'text' is a pointer to a block of memory, into which the API will place the message body. The message body will be read from the position specified by 'ofs', up to a maximum of 'bytes' bytes. If the application does not wish to read the message body, this argument should be set to NULL. 'cbyt' represents the maximum number of bytes of control information to read from the message. 'ctxt' is a pointer to a block of memory, into which the API will place the message control information. No more than 'cbyt' bytes of control information will be placed into the buffer. NOTE: unlike the message text functions, control information can only be read in one pass. The text read by this function is free-form. The message body may or may not contain control characters, NULs, or any other sequence of characters. Messages are simply treated as a block of bytes, with no interpretation whatsoever. In FidoNet areas, the message body consists of one or more paragraphs of text. Each paragraph is delimited by a hard carriage return, '\r', or ASCII 13. Each paragraph can be of any length, so the text should be wordwrapped onto logical lines before being displayed. If created by older applications, paragraphs may also contain linefeeds ('\n') and soft returns ('\x8d') at the end of each line, but these are optional and should always be ignored. As an example, assume that the following stream of text was returned by MsgReadMsg(): Squish Developers Kit Version 2.0 - Page 13 "Hi!\r\rHow's it going? I got the new MsgAPI kit today!\r\rAnyhow, gotta run!" The "\r" marks are carriage returns, so they indicate the end of a paragraph. Notice that the second paragraph is fairly long, so it might have to be wordwrapped, depending on the screen width. Your application might wordwrap the text to make it look like this, if using a window 40 characters wide: ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Hi! ³ ³ ³ ³ How's it going? I got the new MsgAPI ³ ³ kit today! ³ ³ ³ ³ Anyhow, gotta run! ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Paragraphs should always be wordwrapped by the application, regardless of the screen/window size. When parsing the message text, linefeeds and soft carriage returns should be simply skipped. The 'message control information' has a somewhat more restricted format. The control information is passed to the application in the form of an ASCIIZ string. The control information is a variable-length string of text which contains information not found in the (fixed-size) message header. The format of control information is given by the following regular expression: (group)+ A 'group' consists of a and a control item. is the Start Of Header character, or ASCII 01. All control information strings must begin with an SOH, whether or not control items are present. Following the is a control item. A control item consists of a string which describes the type of control item, or it may consist of nothing. At least one group must be present in each message. If a message has no extra control information, this field should consists of a followed by a single . Although the control items are free-form, the following format is suggested: Squish Developers Kit Version 2.0 - Page 14 tag: value where 'tag' is a descriptive identifier, describing the type of field that the item represents. 'value' is simply free-form text, which continues up until the next SOH or . The character set for the tag and value consists of those characters in the range 2-255, inclusive. As an example, a message might have the following control information: CHARSET: LATIN1REALNAME: Mark Twain The trailing byte must be included in the read count given by 'cbyt'. The return value for this function is the number of bytes read from the message body. If no characters were requested, this function returns 0. On error, the function returns -1 and sets msgapierr to one of the following: MERR_BADH MERR_BADF MERR_NOMEM MsgWriteMsg sword MsgWriteMsg(HMSG hmsg, word fAppend, PXMSG msg, byte *text, dword textlen, dword totlen, dword clen, byte *ctxt); The MsgWriteMsg function is used to write the message header, body, and control information to a message. 'hmsg' is a message handle, as returned by the MsgOpenMsg function. The message must have been opened with a mode of MOPEN_CREATE, MOPEN_WRITE or MOPEN_RW. 'fAppend' is a boolean flag, indicating the state of the message body. If 'append' is zero, then the API will write the message body starting at offset zero. Otherwise, if 'append' is non-zero, the API will continue writing from the offset used by the last MsgWriteMsg call. This flag applies to the message body only; if no text is to be written to the body, this argument should be set to 0. 'msg' is a pointer to an XMSG structure. If this pointer is Squish Developers Kit Version 2.0 - Page 15 non-NULL, then MsgWriteMsg will place the XMSG structure information into the message's physical header. To leave the header unmodified, NULL should be passed for 'msg'. THIS 'msg' PARAMETER MUST BE PASSED THE **FIRST** TIME THAT MSGWRITEMSG() IS USED WITH A JUST-OPENED MESSAGE HANDLE! 'text' points to an array of bytes to be written to the message body. If no text is to be written, this argument should be NULL. 'textlen' indicates the number of bytes to be written to the message body in this pass of the MsgWriteMsg function. The text is free-format, and it can consist of any characters, including NULs and control characters. If the application does not wish to update the message body, a value of 0L should be passed for this argument. 'totlen' indicates the total length of the message to be written. This differs from 'textlen' in that the message may be written a piece at a time (using small 'textlen' values), but the total length of the message will not exceed 'totlen'. This parameter can be somewhat restrictive for the application; however, this value is required for optimal use of some message base types. The 'totlen' value does not have to be the exact length of the message to write; however, space may be wasted if this value is not reasonably close to the actual length of the message. The rationale behind this argument is that it gives the API writer the most flexibility, in terms of supporting future message base formats. If the application can provide this information to the API, then almost any message base format can be supported by simply dropping in a new API module or DLL. To write text by making multiple passes, the FIRST pass should call MsgWriteMsg with 'append' set to 0, with the total length of the message in 'totlen', and the length of 'text' in textlen. Second and subsequent passes should set 'append' to 1, with the length of 'text' in textlen. If the application does not wish to update the message body of an existing message, a value of 0L should be passed for this argument. This argument MUST be specified during the first call to the MsgWriteMsg when using a mode of MOPEN_CREATE, even if the first call is not requesting any text to be written. However, this value will be stored internally, and ignored on the second and later calls. When operating on a preexisting message (opened with MOPEN_WRITE or MOPEN_RW), it is an error to specify a length Squish Developers Kit Version 2.0 - Page 16 in 'totlen' which is greater than the original length of the message. 'clen' specifies the total length of the control information, including the trailing NUL byte. To write no control information, a value of 0L should be passed for this argument. 'ctxt' is a pointer to the control information string. To write no control information, a value of 0L should be passed for this argument. N.B. Several restrictions apply to writing control information: First and foremost, control information can only be written once. If the control information is to be changed, the message must be read and copied to another message. Secondly, control information MUST be written during or before MsgWriteMsg is called with information about the message body. MsgWriteMsg returns a value of 0 on success, or -1 on error. If an error occurred, msgapierr will be set to one of the following values: MERR_BADH MERR_BADF MERR_NOMEM MERR_NODS MsgKillMsg sword MsgKillMsg(HAREA ha, dword msgnum); The MsgKillMsg function is used to delete a message from the specified message area. 'ha' is a message area handle, as returned by MsgOpenArea. 'msgnum' specifies the message number to kill. It is an error to kill a message which is currently open. MsgKillMsg returns a value of 0 if the message was successfully killed, or it returns -1 on error and sets msgapierr to one of the following: MERR_BADH MERR_NOENT Squish Developers Kit Version 2.0 - Page 17 MERR_BADF MERR_NOMEM MsgGetCurPos dword MsgGetCurPos(HMSG hmsg); The MsgGetCurPos function retrieves the 'current position' of a message handle. This position is where the MsgReadMsg would read text from the message body next. 'hmsg' is a message handle, as returned by MsgOpenMsg. MsgGetCurPos returns the offset into the message on success, or (dword)-1 on error and sets msgapierr to: MERR_BADH MsgSetCurPos sword MsgSetCurPos(HMSG hmsg, dword pos); The MsgSetCurPos function sets the 'current position' in a message handle. This position is used by MsgReadMsg to read text from the message body. 'hmsg' is a message handle, as returned by MsgOpenMsg. 'pos' is the number of bytes into the message from which MsgReadMsg should start reading. MsgSetCurPos returns 0 on success, or -1 on error and sets msgapierr to: MERR_BADH MsgGetTextLen dword MsgGetTextLen(HMSG hmsg); The MsgGetTextLen function retrieves the length of the message body for the specified message. 'hmsg' is a message handle, as returned by MsgOpenMsg. MsgGetTextLen returns the length of the body on success. On error, it returns (dword)-1 and sets msgapierr to: MERR_BADH Squish Developers Kit Version 2.0 - Page 18 MsgGetCtrlLen dword MsgGetCtrlLen(HMSG hmsg); The MsgGetCtrlLen function retrieves the length of the control information for the specified message. 'hmsg' is a message handle, as returned by MsgOpenMsg. MsgGetCtrlLen returns the length of the control information on success. On error, it returns (dword)-1 and sets msgapierr to: MERR_BADH MsgInvalidHmsg sword MsgInvalidHmsg(HMSG hmsg); The MsgInvalidHmsg function tests the given message handle for validity. If the message area handle is invalid, this function returns TRUE and sets msgapierr to MERR_BADH. Otherwise, a value of FALSE will be returned. returned. UMSGID Translation Functions MsgMsgnToUid UMSGID MsgMsgnToUid(HAREA ha, dword msgnum); The MsgMsgnToUid function converts a message number to a 'unique message ID', or UMSGID. This function can be used to maintain pointers to an 'absolute' message number, regardless of whether or not the area is renumbered or packed. The MsgMsgnToUid function converts a message number to a UMSGID, and the MsgUidToMsgn function converts that UMSGID back to a message number. 'mh' is the message area handle, as returned by MsgOpenArea. 'msgnum' is the message number to convert. MsgMsgnToUid returns a UMSGID on success; otherwise, it returns 0 and sets msgapierr to: MERR_BADH MERR_BADF MERR_NOENT Squish Developers Kit Version 2.0 - Page 19 MsgUidToMsgn dword MsgUidToMsgn(HAREA ha, UMSGID umsgid, word type); The MsgUidToMsgn function converts a UMSGID to a message number. 'ha' is the message area handle, as returned by MsgOpenArea. 'umsgid' is the UMSGID, as returned by a prior call to MsgMsgnToUid. 'type' is the type of conversion to perform. 'type' can be any of the following values: UID_EXACT Return the message number represented by the UMSGID, or 0 if the message no longer exists. UID_PREV Return the message number represented by the UMSGID. If the message no longer exists, the number of the preceding message will be returned. UID_NEXT Return the message number represented by the UMSGID. If the message no longer exists, the number of the following message will be returned. If no valid message could be found, MsgUidToMsgn returns 0 and sets msgapierr to one of the following: MERR_BADH MERR_NOENT CtrlInfo Manipulation Functions MsgGetCtrlToken byte *MsgGetCtrlToken(char *szCtrlInfo, char *szTag); The MsgGetCtrlToken function finds a specified control information tag in a message's control information buffer. 'szCtrlInfo' is the control information buffer for a message, using the format described for MsgReadMsg. 'szTag' is the name of the tag to be extracted from the control information buffer. (For example, tags such as "FMPT ", "MSGID:" or "REPLY:" can be specified.) Squish Developers Kit Version 2.0 - Page 20 If the tag is found in szCtrlInfo, the function allocates a new block of memory and copies the tag to that new block. The address of the new block is returned to the caller. (The returned address should be freed later with a call to MsgFreeCtrlToken.) If the tag is not found in szCtrlInfo, this function returns NULL. For example, given a szCtrlInfo buffer that initially contains the following text: REALNAME: John DoeFMPT 24TOPT 6 A call to MsgGetCtrlToken(szBuf, "FMPT ") would return the string "FMPT 24". After the call, szCtrlInfo would contain: REALNAME: John DoeTOPT 6 MsgFreeCtrlToken void MsgFreeCtrlToken(byte *szCtrlToken); The MsgFreeCtrlToken function frees a control token that was returned by a call to MsgGetCtrlToken. This function must be called to release memory that is allocated by the MsgGetCtrlToken function. 'szCtrlToken' is the return value of a prior call to MsgGetCtrlToken. MsgRemoveToken void MsgRemoveToken(byte *szCtrlInfo, byte *szTag); The MsgRemoveToken function removes a specified control token from a control information buffer. The existing text in the buffer is shifted over such that no gaps are left in the buffer. 'szCtrlInfo' is a control information buffer, using the format specified in the description for MsgReadMsg. MsgGetNumKludges word MsgGetNumKludges(byte *szCtrlInfo); The MsgGetNumKludges function returns the number of tags contained in the control information buffer. Squish Developers Kit Version 2.0 - Page 21 'szCtrlInfo' is a control information buffer, using the format specified in the description for MsgReadMsg. For example, given a control information buffer of the following format: TAG1: ABCTAG2: DEFTAG3: GHI MsgGetNumKludges would return a value of 3, since three separate tags exist within the control information buffer. MsgCvt4D void MsgCvt4D(byte *szCtrlInfo, NETADDR *orig, NETADDR *dest); The MsgCvt4D function converts FidoNet-style control information (namely, the FMPT, TOPT and INTL tags) into a proper 4D address. Messages are also un-gaterouted, if necessary. After each tag is converted, it is removed from the control information. Most normal applications will not need to use this function. MsgCvt4D is usually only used by tosser/scanner programs. (The MsgAPI automatically handles 4D address conversions when writing to *.MSG areas.) 'szCtrlInfo' specifies the buffer containing the original control information for the message. 'orig' points to a NETADDR structure containing the two- dimensional origination address of the message. (This information is normally extracted from the 2D header of a FTS-0001 packet.) 'dest' points to a NETADDR structure containing the two- dimensional destination address of the message. (This information is normally extracted from the 2D header of a FTS-0001 packet.) On output, the 'orig' and 'dest' parameters are updated with zone and point information from the control information, if necessary. For example, if the following parameters were given as input: orig = {0, 249, 106, 0} /* zone and point fields are 0 */ dest = {0, 254, 1, 0} /* zone and point fields are 0 */ szCtrlInfo = FMPT 4TOPT 6INTL: 2:254/1 Squish Developers Kit Version 2.0 - Page 22 1:249/106REALNAME: John Doe A call to MsgCvt4D would result in the parameters being updated as follows: orig = {1, 249, 106, 4} dest = {2, 254, 1, 6} szCtrlInfo = REALNAME: John Doe MsgCreateCtrlBuf char *MsgCreateCtrlBuf(char *szKludgeText, char **pszEndText, unsigned *puiLength); The MsgCreateCtrlBuf function is used to convert an array of FidoNet-style "kludge lines" (as per FTS-0001) into a control information buffer. Most application programs will not need to use this function. MsgCreateCtrlBuf is normally used by tosser/scanner programs that need to directly manipulate FTS-0001 *.PKT files. Most applications will not need to use this function. 'szKludgeText' points to the buffer containing the beginning of an FTS-0001 style message body. MsgCreateCtrlBuf will extract all of the control information from this buffer, up until the first non-blank and non-kludge line is encountered. 'pszEndText' should be a pointer to a 'char *' variable. When MsgCreateCtrlBuf returns, the given variable will point to the first byte of the text following the kludge information. (In most cases, this will be the beginning of the FTS-0001 style message body.) 'puiLength' is a pointer to a variable that contains the length of the message buffer pointed to by 'szKludgeText'. On return, this variable will be updated to reflect the real length of the message body, not counting the kludge lines at the beginning of the text. This function returns a pointer to the newly-created control information buffer. This buffer should be freed with a call to MsgFreeCtrlBuf. Squish Developers Kit Version 2.0 - Page 23 MsgCvtCtrlToKludge char *MsgCvtCtrlToKludge(char *szCtrlInfo); The MsgCvtCtrlToKludge function converts a body of control information lines to an equivalent set of FTS-0001 kludge lines. MsgCvtCtrlToKludge is normally used by tosser/scanner programs that need to directly manipulate FTS-0001 *.PKT files. Most applications will not need to use this function. 'szCtrlInfo' is a buffer of control information, using the format given in the MsgReadMsg description. This function returns a pointer to a new block of memory containing the FTS-0001 style kludge lines. This buffer should be freed with a call to MsgFreeCtrlBuf. MsgFreeCtrlBuf void MsgFreeCtrlBuf(byte *szCtrlBuf); The MsgFreeCtrlBuf function is used to free the control buffers which are allocated by the MsgCvtCtrlToKludge and MsgCreateCtrlBuf functions. 'szCtrlBuf' is the return value of a prior call to MsgCvtCtrlToKludge or MsgCreateCtrlBuf. Squish-Specific Functions SquishHash dword SquishHash(byte *szTxt); The SquishHash function is used to calculate a hash for a "To:" field in a message. 'szTxt' is a pointer to the To: field of a message. The algorithm used to calculate this hash is described in the "Index File Format" subsection of the Squish File Format Specification (contained later in this document). Squish Developers Kit Version 2.0 - Page 24 SquishSetMaxMsg dword SquishSetMaxMsg(HAREA ha, dword dwMaxMsg, dword dwSkipMsg, dword dwMaxDays); The SquishSetMaxMsg function is used to set the "maximum messages", "skip messages" and "maximum age" parameters in a Squish-format base. 'ha' is a Squish area handle, as returned by MsgOpenMsg. 'ha' must be a handle for a Squish-format message area. 'dwMaxMsg' is the maximum number of messages to allow in this area. 'dwSkipMsg' is the number of messages to skip (at the beginning of the base) before automatically deleting messages. 'dwMaxDays' is the maximum age (in days) of messages to be kept in the message base. API Changes from MsgAPI Version 0 1) Renamed the msg.ftsc_date field to msg.__ftsc_date. Most of the developers whoa re currently using this field should actually be using the binary dates. See the comments for the __ftsc_date field in MSGAPI.H for more information. 2) For compatibility with Windows, the MSG and MSGH names have been changed. The old-style ones will still be there for compatibility, but the API defaults to using HAREA instead of MSG*, and HMSG instead of MSGH*. If you wish to turn off the old definitions because they conflict with your source files or the Windows headers, define MSGAPI_NO_OLD_TYPES before including msgapi.h. 3) Renamed the InvalidMsgh and InvalidMsg functions to MsgInvalidHarea and MsgInvalidHmsg, making them more consistent with the other API names. Also fixed the documented return codes for these functions. 4) A few slight changes were made to the XMSG structure. Notably, now only 9 reply links are supported, and the field that used to hold the tenth reply link is now used for storing a message's UMSGID. Squish Developers Kit Version 2.0 - Page 25 BUILDING MSGAPI APPLICATIONS Source File Requirements Any code which uses the MsgAPI must include the following line at the top of each source file: #include "msgapi.h" If the MsgAPI source is not installed in the current directory, you should add the MsgAPI directory to your compiler's "include path" before trying to compile an application. Programmers who are using MsgAPI with Windows should make sure to define the "MSGAPI_NO_OLD_TYPES" constant before including the msgapi.h header. Older versions of the MsgAPI used typedefs which were incompatible with Windows, and this macro must be defined to stop MsgAPI from using the old typedef names. For information on using the MsgAPI function calls, please see the section entitled "MsgAPI Function Reference". This section contains a complete list of function calls in the API, including formal parameters, notes on operation, and return codes. If you are using TC++ or BC++, make sure that your application is compiled in "C mode". MsgAPI was not designed for use with C++. (In the TC++ IDE, set Options/Compiler/C++ to "CPP extension only". For the command-line compiler, make sure that you are NOT using the "-P" switch.) Also, the distribution TC++/BC++ and WC libraries have been compiled without overlay support. If you wish to use the WATCOM or Borland overlay managers, you will have to recompile the MsgAPI source with overlay support. Compiling Source Files No special requirements are necessary for compiling source files for the DOS version of MsgAPI. However, make sure to select the LARGE memory model when compiling your application. If you wish to create an application using a different memory model, you will have to recompile the MsgAPI code. For compiling OS/2 applications that use MsgAPI, make sure that the "OS_2" macro is defined on the compiler command-line. The MsgAPI uses a different set of calling conventions under OS/2, so all of your code must be compiled with the OS_2 macro defined. Squish Developers Kit Version 2.0 - Page 26 Linking Your Application This package already includes pre-made libraries for a number of compilers, all built using the large memory model. Under DOS, the MsgAPI library is statically linked with each application. To link with the MsgAPI routines, simply specify the required library file when linking. For example: TLINK (Borland C, DOS): tlink \bc\lib\c0l myapp,myapp,nul,bc_dos_l \bc\lib\cl TLINK (Turbo C, DOS): tlink \tc\lib\c0l myapp,myapp,nul,tc_dos_l \tc\lib\cl WLINK (16-bit DOS): wlink file myapp lib wc_dos_l name myapp WLINK (32-bit DOS): wlink file myapp lib wc_dos_f name myapp LINK (DOS): link myapp,myapp,nul,mc_dos_l; If using the TC++ or BC++ IDE, simply declare the required library as part of your project file. If you are using TC++ or BC++ from the command line, you can specify the name of the library after your source module, like this: tcc -ml myapp.c tc_dos_l.lib Under OS/2, the MsgAPI code is distributed as a DLL (dynamic link library). To use this code in your application, simply link with the DLL16.LIB import library (for 16-bit code) or the DLL32.LIB import library (for 32-bit code). 16-bit OS/2 applications should also be compiled in the large memory model. After ensuring that MSGAPI.DLL and/or MSGAPI32.DLL are on your LIBPATH, your application should be ready to run. If you wish to use the default libraries and memory models, you do not need any of the *.c files in the source directory. However, you must still retain all *.h header files, since they are required when compiling your application. Squish Developers Kit Version 2.0 - Page 27 Building the Sample Applications The SAMPLES subdirectory contains a number of sample programs that use the MsgAPI. The commands given below can be used to make the samples using the supported compilers: Compiler OS Command WATCOM C/16 DOS wmake -u -f makefile.wcd WATCOM C/16 OS/2 wmake -u -f makefile.wco WATCOM C/32 DOS wmake -u -f makefile.w3d WATCOM C/32 OS/2 wmake -u -f makefile.w3o Microsoft C DOS nmake -f makefile.mcd Microsoft C OS/2 nmake -f makefile.mco Turbo C DOS make -fmakefile.tcd Borland C DOS make -fmakefile.bcd Squish Developers Kit Version 2.0 - Page 28 BUILDING THE MSGAPI SOURCE The MsgAPI sources have been tested with the following compilers: WATCOM C/16 9.5 (DOS and OS/2) WATCOM C/32 9.5 (DOS and OS/2) Microsoft C 6.0 (DOS and OS/2) Turbo C 2.0 (DOS only) Borland C++ 3.1 (DOS only) In addition, either the Microsoft Macro Assembler or the Borland Turbo Assembler is required to build the MsgAPI source. Other versions of these compilers (or compilers made by different vendors) may also work, but only the compilers above have been tested. The code works with both 16-bit and 32-bit compilers. (However, if you add support for a compiler from a different vendor, you will probably have to modify h\compiler.h.) To recompile the MsgAPI source, simply edit the MAKEFILE within the SRC directory. Sample definitions have been provided for all of the supported compilers. The makefile included in this package should work with almost any make utility. This makefile has been successfully used under Borland's MAKE, Microsoft's NMAKE, WATCOM's WMAKE, and Vadura's DMAKE. Warning! WMAKE users must use the "-u" command-line option when running WMAKE.EXE. By default, WMAKE uses a non-standard line continuation character, so the makefile will not be processed correctly unless the "-u" option is used. By default, the makefile is set up to compile for WATCOM C/16 9.5. To select another compiler, comment out all of the WC/16 definitions and uncomment those that pertain to your compiler. Having made the appropriate modifications, simply type "make" to rebuild the appropriate msgapi*.lib or msgapi*.dll file. Under DOS, the "MODEL=" directive at the beginning of the makefile can also be used to instruct the compiler to build a MsgAPI library for one of the following memory models. MODEL should be set to one of the following values: s Small m Medium c Compact l Large Squish Developers Kit Version 2.0 - Page 29 OS/2 DLL DEVELOPMENT The files in the FEATURES subdirectory contains everything that is required to write third-party extensions for Squish. This developers kit includes the source for two sample feature DLLs. These DLLs are not part of the standard Squish distribution, nor are they supported in any manner. They are simply included to provide examples and to give a demonstration of feature DLL programming. Sample Feature DLLs KILLRCAT.DLL The first sample DLL is "KillrCat", a message filtering program that selectively deletes messages based on a set of search criteria. The following can be added to the top of your SQUISH.CFG for a demonstration of KillrCat's capabilities: ; The Feature and/or Feature32 keywords tell Squish to load ; KILLRCAT and/or KILLRC32. Feature KillrCat Feature32 KillrC32 ; Keyword: ; ; KillrCat ; ; : t = to ; f = from ; s = subject ; b = body ; ; Any combination of the above flags are permitted. ; ; is the tag of the echo area. ; ; is the text to find. KillrCat b sysop249 Node Diff announcement KillrCat s sysop249 Nodediff announcement KillrCat sb users249 Latest Versions'n Stuff KillrCat sb users249 Fidonews! KillrCat b netmail File request generated by AMAX When KillrCat find a message that matches the specified criteria, it diverts the message from its usual area and places it in BAD_MSGS. If you don't want to see these messages at all, fix KILLRCAT.C and use the "delete message" bitmask in the return code. Squish Developers Kit Version 2.0 - Page 30 MSGTRACK.DLL The other sample DLL is a message bouncer. This bouncer requires a version 7 nodelist to work properly. Try adding the following to your SQUISH.CFG for a demonstration: Feature Msgtrack Feature32 Msgtra32 ; Path and name of nodelist Msgtrack Nodelist ; Delete bounced messages ;Msgtrack Kill When packing netmail, this bouncer will automatically return messages to the originating node if the destination is not listed in the nodelist. should be the name of the NODEX.DAT file from your version 7 nodelist. The optional "Msgtrack Kill" line instructs the message bouncer to delete messages after they have been bounced. (By default, Squish just marks a bounced message as "Sent" and "Orphan", but the message is left in the netmail area.) Feature DLL Programming Interface FEATURES.LZH includes a sample TEMPLATE.C file that can be used as a template for writing feature DLLs. A feature DLL must start with the following lines: #define INCL_DOS #include #include "sqfeat.h" os2.h is the standard OS/2 include file, as distributed in the OS/2 toolkit. (For 16-bit feature DLLs, the OS/2 1.x toolkit is required. For 32-bit feature DLLs, an OS/2 2.x toolkit is required.) sqfeat.h is the feature DLL include file. This contains definitions and typedefs necessary to write a Squish feature DLL. Each feature DLL must contain a number of functions. These functions are used as hooks when Squish is performing certain actions. Squish Developers Kit Version 2.0 - Page 31 All of the hook functions must be exported by name, and they all must use the pascal calling convention. The "FEATENTRY" keyword automatically defines all of these attributes.) The following functions must be present in every feature DLL: FeatureInit This function is responsible for performing feature-specific initializations. FeatureInit() is called as soon as the Feature or Feature32 keyword is encountered in SQUISH.CFG. This function is passed a pointer to a _feat_init structure. The structure contains the following fields: struct_len The length of the _feat_init structure szConfigName The name of the DLL. This should be filled in by the FeatureInit() function. This name is be used for adding feature-specific configuration options to SQUISH.CFG. When Squish encounters an unknown configuration line, it first checks to see if the first word matches any of the feature DLLs, as given in the szConfigName field. If a match is found, the configuration line is passed to the DLL's FeatConfig function. pfnLogMsg A pointer to the Squish logging routine. This field is filled in by Squish itself. If the feature DLL needs to add lines to the Squish log, this function pointer can be used to access the system log function. The FeatInit() function should save a copy of the pfnLogMsg field in a global variable, such that the other Feat...() functions can create log entries when necessary. ulFlag This flag contains a bitmask that Squish uses to optimize calls made to the feature DLL. FeatInit() should initialize this field before returning to the caller. Any or all of the following flags can be combined using the bitwise OR operator ("|"): FFLAG_NETSENT Only call FeatNetMsg when packing a message that does not have the SENT bit set. Squish Developers Kit Version 2.0 - Page 32 FFLAG_NETTOUS Only call FeatNetMsg when packing a message which is addressed to one of our addresses, as given in SQUISH.CFG. FFLAG_NETRECD Only call FeatNetMsg when packing a message that does not have the RECEIVED bit set. FFLAG_NETNOTTOUS Only call FeatNetMsg when packing a message that is NOT addressed to one of our addresses, as given in SQUISH.CFG. If this field is given a value of 0, the FeatNetMsg function will be called before packing every netmail message. FeatureConfig This function is called whenever a feature-specific configuration line is encountered. Feature DLLs can use this function to process application-specific configuration information from SQUISH.CFG. This function is passed a pointer to a _feat_config structure. The structure contains the following fields: struct_len This field contains the length of the _feat_config structure. szConfigLine This field contains the line just read from SQUISH.CFG. The first word on this line matches the szConfigName value that was placed in the _feat_init structure by FeatInit. ppszArgs This field contains a standard argv-style variable, pointing to a tokenized version of szConfigLine. The first word on szConfigLine can be accessed as ppszArgs[0]; the second word can be accessed as ppszArgs[1]; and so on. If there are 'n' arguments, ppszArgs[0] through ppszArgs[n-1] will contain pointers to each word on the line, and ppszArgs[n] will be NULL. The FeatureConfig function should extract any relevant Squish Developers Kit Version 2.0 - Page 33 information and store it in a global variable that can be accessed by other functions in the DLL. FeatureNetMsg This function is called whenever a NetMail message is about to be packed. This function is only used on non- ArcmailAttach systems. This function is passed a pointer to the _feat_netmsg structure. The structure contains the following fields: struct_len This field contains the length of the _feat_netmsg structure. ulAction This field controls the action taken by Squish when FeatureNetMsg returns. The contents of this field should be filled out by the feature DLL. This field can be set to a combination of any of the following values, using the bitwise OR operator ("|"): FACT_NONE No action is taken FACT_KILL The message is deleted upon returning to Squish, before the message is packed. FACT_SKIP The message is left alone and is not packed. FACT_HIDE Do not pass this message to other installed features. (By default, all installed features are called with a pointer to the current message. However, if the FACT_HIDE flag is set, other DLLs will not be called with this message during the current run.) FACT_RWMSG Rewrite the current message. This flag should be used if the DLL modifies the message header in pMsg. pszAreaTag This field contains the name of the current Squish Developers Kit Version 2.0 - Page 34 netmail area. ha This is a MsgAPI HAREA handle for the current netmail area. hmsg This is a MsgAPI HMSG handle for the current message. ulMsgNum This contains the message number of the current message. pMsg Pointer to the message header of the current message. pszCtrl Pointer to the control information for the current message. pszMsgTxt Pointer to the message text for the current message. us A NETADDR-type structure giving Squish's primary address. FeatureTossMsg This function is called whenever an EchoMail message is about to be tossed. This function is passed a pointer to the _feat_toss structure. The structure contains the following fields: struct_len Length of the _feat_toss structure. ulTossAction This field specifies the action to be taken when Squish returns from FeatureTossMsg. This field can contain any of the following options, combined using the bitwise OR operator ("|"): FTACT_NONE No action is taken. FTACT_KILL Delete the message without attempting to toss it. FTACT_AREA Toss to the new area tag specified in szArea. FTACT_HIDE Do not pass this message to any other installed features. (See the comments for Squish Developers Kit Version 2.0 - Page 35 FACT_HIDE in FeatureNetMsg for more information.) FTACT_NSCN Do not scan this message to anyone else (if performing a one-pass toss/scan). szArea Area tag for this message. This field can be changed to cause the message to be redirected to another area, but to ensure that Squish recognizes the change, the FTACT_AREA flag should be set (above). szPktName Name of the packet file that contains the current message. pMsg Pointer to the message header for the current message. pszCtrl Control information for the current message. pszMsgTxt Message text of the current message. FeatureScanMsg This function is currently not supported by Squish. However, it must be present for Squish to load the DLL. FeatureTerm This function is called when Squish is about to finish execution. This function should perform feature-specific clean-up operations. Feature16Bit This function is never actually called by Squish, but it must be present in all 16-bit feature DLLs. Feature32Bit This function is never actually called by Squish, but it must be present in all 32-bit feature DLLs. Squish Developers Kit Version 2.0 - Page 36 Building Feature DLLs The following commands can be used to build a sample 16-bit DLL using WATCOM C/16 9.5 or above, where "template" is the name of the source file for the feature: [C:\] wcc /bd /dOS_2 /ml /zu template.c [C:\] wlink sys os2 dll initi debug all name template.dll file template.obj lib os2 opt manyauto, protmode, verb, modname=template The following commands can be used to build a sample 32-bit DLL using WATCOM C/32 9.5 or above: [C:\] wcc386 /bd /dOS_2 /fo=templa32 template.c [C:\] wlink sys os2v2 dll initi debug all name templa32.dll file templa32.obj lib os2386 opt verb, manyauto, modname=templa32 Squish Developers Kit Version 2.0 - Page 37 SQUISH FILE FORMAT SPECIFICATION This section describes the physical file layout of a Squish message base. This is intended as a reference for developers who are writing their own Squish-compatible programs. For an overview of the Squish message base, see the section of SQUISH.PRN entitled "Using Squish-Format Message Areas". While the Squish MsgAPI library provides a standardized interface to Squish and *.MSG bases for C programmers, authors who use other languages may need to access Squish bases directly. This section describes the implementation details of the Squish file format. Squish Philosophy A standard Squish base consists of two files: a message data file and a message index file. Both files have the same prefix, but the data file has an extension of ".sqd", while the index file has an extension of ".sqi". From an overall point of view, the Squish data file consists of messages stored in a doubly-linked list. The Squish data file includes a header that contains pointers to the first and last frames in the area, in addition to other area-specific information. In the data file, a "frame" is used to hold an individual message. A frame consists of a frame header (which contains links to the prior and next messages), followed by the optional message header, control information and message body fields. This "linked list of frames" approach is ideal for a BBS message base. Almost all message base access is sequential, starting from a particular offset, and reading or writing until the end of the message base is reached. Since each frame header contains the offset of the prior and next messages, no disk accesses are required to find the preceding or following messages. The index file is a flat array of Squish Index (SQIDX) records. The index file is used primarily for performing random access look-ups by message number. Unlike other message base formats, the Squish base is only loosely based on the concept of "message numbers". While all messages have a message number, these numbers can change at any time. By definition, the message numbers in a Squish base always range from 1 to the highest message in the area. Consequently, there are no "gaps" in message numbers, so a Squish message area never needs to be renumbered. Squish Developers Kit Version 2.0 - Page 38 While this makes it easy to scan through all of the messages in an area, this also makes it difficult to find one specific message. Consequently, the concept of a "unique message identifier" or (UMSGID) is introduced. When a message is created, it is assigned a 32-bit UMSGID. These identifiers are unique and NEVER CHANGE. Unique message numbers are never "renumbered", so once a UMSGID of a message is obtained, it can always be used to find the current message number of the given message, no matter how many messages have been added or deleted in the interim. Squish Data Types The following integral types are used in the Squish file format definitions: Type Size Description char 1 byte A one-byte unsigned character. word 2 bytes A two-byte unsigned integer. sword 2 bytes A two-byte signed integer. dword 4 bytes A four-byte unsigned integer. FOFS 4 bytes A four-byte unsigned integer. This type is used to store offsets of frames within the Squish data file. UMSGID 4 bytes A four-byte unsigned integer. This type is used to store unique message identifiers for Squish messages. The types above are stored in the standard Intel "backwords" format, with the least significant byte being stored first, and the most significant byte being stored last. A two-byte integer containing 0x1234 would be stored as follows: Offset Value 0 0x34 1 0x12 A four-byte integer containing 0x12345678 would be stored as follows: Offset Value 0 0x78 1 0x56 2 0x34 Squish Developers Kit Version 2.0 - Page 39 3 0x12 The Squish file format also uses a number of abstract data types: The SCOMBO type is used for describing a message date/time stamp. This structure has the following format: Name Type Ofs Description date word 0 DOS bitmapped date value. This field is used to store a message date. The first five bits represent the day of the month. (A value of 1 represents the first of the month.) The next four bits indicate the month of the year. (1=January; 12=December.) The remaining seven bits indicate the year (relative to 1980). time word 2 DOS bitmapped time value. This field used to store a message time. The first five bits indicate the seconds value, divided by two. This implies that all message dates and times get rounded to a multiple of two seconds. (0 seconds = 0; 16 seconds = 8; 58 seconds = 29.) The next six bits represent the minutes value. The remaining five bits represent the hour value, using a 24-hour clock. Total: 4 bytes Squish Developers Kit Version 2.0 - Page 40 The NETADDR type is used for describing a FidoNet network address. This structure has the following format: Name Type Offset Description zone word 0 FidoNet zone number. net word 2 FidoNet net number. node word 4 FidoNet node number. point word 6 FidoNet point number. If the system is not a point, this field should be assigned a value of zero. Total: 8 bytes In addition, to describe an array of a given type, the "type[n]" notation is used. For example, "char[6]" represents an array of six contiguous characters. Likewise, "UMSGID[12]" represents an array of twelve UMSGID types. Data File Format The Squish data file consists of two major sections: 1) A fixed-length area header, stored at the beginning of the file. 2) A variable-length heap that comprises the rest of the file. This part of the file is used for storing message text. The area header stores pointers to the head and tail of two major "chains" of messages; the message chain and the free chain. The message chain is used to find all active messages in an area. The free chain is used for storing the locations of deleted messages, such that space can be reused at a later point in time. The Squish data file always contains a copy of the following _sqbase structure at file offset 0: Name Type Offset Description len word 0 Length of the _sqbase structure. reserved word 2 Reserved for future use. num_msg dword 4 Number of messages in this Squish base. This should always be equal to the value of the high_msg field. Squish Developers Kit Version 2.0 - Page 41 high_msg dword 8 Highest message number in this Squish base. This should always be equal to the value of the num_msg field. skip_msg dword 12 When automatically deleting messages, this field indicates that the first skip_msg messages in the area should not be deleted. (If max_msg=50 and skip_msg=2, this means that the writing program should start deleting from the third message whenever the total message count exceeds 50 messages.) high_water dword 16 The high water marker for this area, stored as a UMSGID. This field is used in EchoMail areas only. This contains the UMSGID of the highest message that was scanned by EchoMail processing software. uid dword 20 This field contains the UMSGID to be assigned to the next message created in this area. base char[80] 24 Name and path of the Squish base, as an ASCIIZ string, not including the extension. This field is optional and will not necessarily be filled out by all applications. (If this field is not supported, it should be initialized to ASCII 0.) begin_frame FOFS 104 Offset of the first frame in the message chain. last_frame FOFS 108 Offset of the last frame in the message chain. Squish Developers Kit Version 2.0 - Page 42 free_frame FOFS 112 Offset of the first frame in the free chain. last_free_frame FOFS 116 Offset of the last message in the free chain. end_frame FOFS 120 Offset of end-of-file. Applications will append messages to the Squish file from this point. max_msg dword 124 Maximum number of messages to store in this area. When writing messages, applications should dynamically delete messages to make sure that no more than max_msgs exist in this area. keep_days word 128 Maximum age (in days) of messages in this area. This field is not normally used by applications. However, it is used by SQPACK when performing a message area pack. sz_sqhdr word 130 Size of the SQHDR structure. For compatibility with future versions of the Squish file format, applications should use this value as the size of the SQHDR structure, instead of using a hardcoded "sizeof(SQHDR)" value. reserved char[124] 132 Reserved for future use. Total: 256 bytes To examine the messages in a Squish base, the application needs to follow the message chain. To do this, start with the begin_frame and/or end_frame fields. These fields contain the offsets of the first and last frames (respectively) in the message base. Squish Developers Kit Version 2.0 - Page 43 A frame in the message chain consists of a Squish Frame Header structure (SQHDR), followed by the XMSG message header, message control information, and message body. A frame in the free chain consists of a SQHDR structure only. A free frame does not necessarily contain a message. A SQHDR structure always has the following format: Name Type Ofs Description id dword 0 The frame identifier signature. This field must always be set to a value of 0xAFAE4453. next_frame FOFS 4 Frame offset of the next frame, or 0 if this is the last frame. prev_frame FOFS 8 Frame offset of the prior frame, or 0 if this is the first frame. frame_length dword 12 Amount of space ALLOCATED for the frame, not including the space used by the SQHDR itself. msg_length dword 16 Amount of space USED in this frame, including the size of the XMSG header, the control information, and the message text. This field does NOT include the size of the SQHDR itself. clen dword 20 Length of the control information field in this frame. frame_type word 24 This field can contain one of the following frame type values: 0 Normal frame. This frame contains an XMSG header, followed by the message control information and the message body. Normal frames should only be encountered when processing the normal message chain. 1 Free frame. This frame has been deleted, but it can be reused. The amount of available space in the frame is given by Squish Developers Kit Version 2.0 - Page 44 the frame_length field. Free frames should only be encountered when processing the free chain. 2 LZSS frame. This frame type is reserved for future use. 3 Frame update. The frame is being updated by another task. This is only a transient frame type; it indicates that the frame should not be manipulated by another task. All other frame types are reserved for future use. reserved word 26 Reserved for future use. Total: 28 bytes For a normal frame type, the XMSG header immediately follows the Squish frame header. The XMSG structure has the following format: Name Type Ofs Description attr dword 0 Message attributes. This is a combination of any of the MSG* attributes. (See below.) from char[36] 4 Name of the user who originated this message. to char[36] 40 Name of the user to whom this message is addressed. subject char[72] 76 Message subject. orig NETADDR 148 Originating network address of this message. dest NETADDR 156 Destination network address of this message. (Used for netmail areas only.) Squish Developers Kit Version 2.0 - Page 45 date_written SCOMBO 164 Date that the message was written. date_arrived SCOMBO 168 Date that the message was placed in this Squish area. utc_ofs sword 172 The message writer's offset from UTC, in minutes. Currently, this field is not used. replyto UMSGID 174 If this message is a reply, this field gives the UMSGID of the original message. Otherwise, this field is given a value of 0. replies UMSGID[9] 178 If any replies for this message are present, this array lists the UMSGIDs of up to nine reply messages. umsgid UMSGID 214 The UMSGID of this message. THIS FIELD IS ONLY VALID IF THE MSGUID BIT IS SET IN THE "ATTR" FIELD. Older Squish programs do not always set this field, so its contents can only be trusted if the MSGUID bit is set. __ftsc_date char[20] 218 FTS-0001 compatible date. Squish applications should not access this field directly. This field is used exclusively by tossers and scanners for preserving the original ASCII message date. Squish applications should use the binary dates in date_written and date_arrived to retrieve the message date. Total: 238 bytes Any of the following bitmasks can be used in the XMSG "attr" field: Attribute Value Description MSGPRIVATE 0x00000001 The message is private. MSGCRASH 0x00000002 The message is given a "crash" flavour when packed. When both MSGCRASH and MSGHOLD are both enabled, the message is given a "direct" flavour. MSGREAD 0x00000004 The message has been read by the addressee. Squish Developers Kit Version 2.0 - Page 46 MSGSENT 0x00000008 The message has been packed and prepared for transmission to a remote system. MSGFILE 0x00000010 The message has a file attached. The filename is given in the "subj" field. MSGFWD 0x00000020 The message is in-transit; it is not addressed to one of our primary addresses. MSGORPHAN 0x00000040 The message is orphaned. The message destination address could not be found in the nodelist. MSGKILL 0x00000080 The message should be deleted from the local message base when it is packed. MSGLOCAL 0x00000100 The message originated on this system. This flag must be present on all locally-generated netmail for Squish to function properly. MSGHOLD 0x00000200 The message should be given a "hold" flavour when packed. When combined with the MSGCRASH flag, the message is given a "direct" flavour. MSGXX2 0x00000400 Reserved for future use. MSGFRQ 0x00000800 The message is a file request. The filename is given in the "subj" field. MSGRRQ 0x00001000 A receipt is requested. (Not supported by Squish.) MSGCPT 0x00002000 This message is a receipt for an earlier MSGRRQ request. MSGARQ 0x00004000 An audit trail is requested. (Not supported by Squish.) MSGURQ 0x00008000 This message is an update request. The filename is given in the "subj" field. MSGSCANNED 0x00010000 This echomail message has been scanned out to other systems. Squish Developers Kit Version 2.0 - Page 47 MSGUID 0x00020000 The "uid" field contains a valid UMSGID for this message. Index File Format The index file provides random access capability for a Squish base. Given a message number, the index file can be used to quickly find the frame offset for that message. Similarly, given a UMSGID, the index file can also be used to find the message number and/or the frame offset for the message. The Squish index file is an array of Squish Index (SQIDX) structures. Each SQIDX structure corresponds to an active message. For a base containing 'n' messages, there are at least 'n' SQIDX structures. (There may also be extra SQIDX frames at the end of the index file, but these will be initialized with invalid values, as described below.) The SQIDX for the first message is stored at offset 0. The SQIDX for the second message is stored at offset 12. The SQIDX for the third message is stored at offset 24. (and so on) The Squish Index structure (SQIDX) has the following format: Name Type Ofs Description ofs FOFS 0 Offset of the frame for this message. A value of 0 is used to indicate an invalid message. umsgid UMSGID 4 Unique message ID for this message. A value of 0xffffffff is used to indicate an invalid message. The umsgid field must always be greater than the umsgid field of the preceding SQIDX structure. UMSGIDs are assigned serially, so this will normally be the case. (A binary search is performed on the index file to translate UMSGIDs, so the umsgid field of the SQIDX headers must always be stored in ascending order.) hash dword 8 The low 31 bits of this field contain a hash the "To:" field for this message. (See below for the hash function.) The high bit is set to 1 if the MSGREAD flag is enabled in the corresponding XMSG Squish Developers Kit Version 2.0 - Page 48 header. Total: 12 bytes The following hash function is used to calculate the "hash" field of the SQIDX structure. All variables are 32-bit unless otherwise noted: Set "hash" to a value of 0 For each 8-bit character "ch" in the To: field, repeat: - Shift "hash" left by four bytes. - Convert "ch" to lowercase - Increment the hash by the ASCII value of "ch" - Set "g" to the value of "hash" - Perform a bitwise AND on "g", using a mask of 0xf0000000. - If "g" is non-zero: - Perform a bitwise OR on "hash" with the value of "g". - Shift "g" right by 24 bits. - Perform a bitwise OR on "hash" with the value of "g". Perform a bitwise AND on "hash" with a value of 0x7fffffff. The following C function can be used to calculate such a hash: #include unsigned long SquishHash(unsigned char *f) { unsigned long hash=0; unsigned long g; char *p; for (p=f; *p; p++) { hash=(hash << 4) + (unsigned long)tolower(*p); if ((g=(hash & 0xf0000000L)) != 0L) { hash |= g >> 24; hash |= g; } } Squish Developers Kit Version 2.0 - Page 49 /* Strip off high bit */ return (hash & 0x7fffffffLu); } The SquishHash function is derived from the hashpjw() function by P.J. Weinberger. The following procedure can be used to find the frame offset or UMSGID of a particular message number: - Read the Squish data header and ensure that the message number is less than or equal to the value given by "high_msg". - Subtract one from the message number. - Multiply the result by 12 (the size of the SQIDX structure). - The product is the required offset in the SQIDX file. Seek to that offset and read the SQIDX header. - The "fofs" field of the SQIDX structure indicates the offset of the message frame. The "umsgid" field indicates the UMSGID for the message in question. To find a message number and/or frame offset of a particular UMSGID, a simple binary search can be performed on the index file. Since the SQIDX structures are (by definition) stored in ascending order by the "umsgid" field. Conventional binary search techniques can be used to find the SQIDX structure for a particular UMSGID value. Having found the SQIDX structure, the offset of the message can be obtained from the SQIDX "fofs" field. In addition, the message number can be determined from the location of the SQIDX within the index file. (The SQIDX at offset 0 is for message 1; the SQIDX at offset 12 is for message 2; and so on.) Message Links Like with any doubly-linked list, inserting a message into a Squish message chain requires more work than just writing a single header. First of all, when writing the SQHDR for the new message, the "prev_frame" and the "next_frame" fields must be set to the frame offsets of the previous and next messages in the chain. In addition, the "next_frame" field of the PREVIOUS message must be set to the offset of the message being inserted. Likewise, Squish Developers Kit Version 2.0 - Page 50 the "prev_frame" field of the NEXT message must be set to the offset of the message being inserted. Similar arguments apply to deleting a message. The prior and next messages must be linked together to remove the current message from the chain. Beyond that, if the message is being inserted or deleted is at the beginning or end of the chain, the begin_frame/last_frame or free_frame/last_free_frame pointers must be updated (for the message and free chains, respectively). Reading Messages Once the offset of a message frame is known, the application can follow these steps to read the message: - Seek to the specified frame offset. - Read the SQHDR for the frame, making sure to read "sz_sqhdr" bytes (as given in the _sqdata base header). - Validate the "id" field. If the contents of this field are not equal to the predefined constant (see "id", above), the Squish base is damaged. - Validate the "frame_type" field. If the type is equal to "normal frame", proceed. If the type is equal to "frame update", another application is processing the base, so the message should be skipped. Any other value is an error. - Read the XMSG header. - Use the "clen" field of the SQHDR to determine the length of the control information. Read this many bytes into an array. - Use the "msg_length" field of the SQHDR to determine the total length of the frame. Subtract the size of the XMSG header and the "clen" value, and use this result as the length of the message text. Read this many bytes into an array. To read the next or prior message in a chain, simply use the "next_frame" or "prev_frame" fields (respectively) to obtain the frame offset (FOFS) of the indicated frame. A FOFS of zero indicates that the end of the chain has been reached. Squish Developers Kit Version 2.0 - Page 51 Writing Messages To write a new message to a Squish base, an application should follow this procedure: - Obtain exclusive access to the Squish base. (See the section entitled "Concurrency" for more information.) - Re-read a copy of the Squish base header. - Scan the messages in the "free_frame" chain to see if any frame is large enough to accommodate the new message. Ensure that the message header has a type of "free frame". If so, unlink the message from the free chain, adjusting the free_frame and last_free_frame pointers if necessary. (See "Message Links", above.) - If no free frame was found, allocate a frame at the end of the file, using the "end_frame" offset from the base header. Increment the "end_frame" value by the amount of space allocated. - Link the new SQHDR frame into the end of the message chain, adjusting the begin_frame and last_frame pointers in the base header (if necessary). - Set the "frame_type" field of the new SQHDR to "frame update" and write at the appropriate offset. - Copy the "uid" field from the base header into the XMSG header of the message to be written. Also set the MSGUID flag in the "attr" field of the XMSG header. Then increment the "uid" field in the base header. - Increment the num_msg and high_msg values in the base header. - Rewrite the Squish base header. - Relinquish exclusive access to the Squish base. - Take the offset of the new SQHDR, and add the "sz_sqhdr" value from the base header. Seek to that offset and write the XMSG header. - Write the message's control information. - Write the message body. - Set the frame type of the new message to "normal". - Seek to the appropriate offset in the Squish index file, and Squish Developers Kit Version 2.0 - Page 52 write a SQIDX header for the new message. Make sure - Check the "high_msg" count of the message area to ensure that it is less than or equal to max_msg. If not, delete messages (while skipping the first skip_msg messages) until the total message count is less than high_msg. Deleting Messages An application should follow this procedure to delete an existing message: - Obtain the frame offset of the message to be deleted. (If given a message number, the frame offset for a message can be found in the n'th record of the index file.) - Read the frame header into memory. - Obtain exclusive access to the message base. - Update the header pointed to by the next_frame link to skip over the message being deleted. - Update the header pointed to by the next_frame link to skip over the message being deleted. - Shift the index file to remove the just-deleted message from the message area. The global pointers in the Squish header (num_msg, high_msg, and so on) should also be updated. - Append the just-deleted header to the chain of "free" messages. The frame should be inserted at the end of the free list. The procedure used for inserting a free frame is similar to that used in "Writing a message", except that the free list is manipulated (instead of the message list). - Relinquish exclusive access to the message base. Concurrency Considerations For Squish applications to operate properly in a multitasking or networked environment, certain precautions must be taken when manipulating a Squish base. While there are few restrictions on reading message information, a certain set of conventions must be followed when writing to a Squish base. The procedure for obtaining exclusive access to the message base (as used in the descriptions above) is as follows: - Attempt to lock the first byte of the Squish data file. If the lock fails, wait for one second and try again. If all Squish Developers Kit Version 2.0 - Page 53 ten locks fail, the base is locked by another process and cannot be modified. - Re-read the Squish base header. Old copies of the header may no longer be current, so this step must always be performed when obtaining exclusive access to the Squish base. An application should not write to the Squish base until performing the steps above. Once exclusive access has been obtained, the application can perform any necessary modifications. When the application no longer needs to write to the message base, the following steps can be performed to relinquish exclusive access: - Write the updated Squish base header. This revised header should include any modifications which were made while exclusive access was in effect. - Unlock the first byte of the Squish data file. As long as all Squish applications follow these conventions for accessing the Squish base, concurrency should not be a problem. ### Squish Developers Kit Version 2.0 - Page 54