Filename....: JAM-001 Rev.........: 001 Dated.......: 93-07-01 Status .....: Released Subject.....: JAM message base proposal Author......: Joaquim Homrighausen Co-Authors..: Andrew Milner, Mats Birch, Mats Wallin --------------------------------------------------------------------- JAM(mbp) The Joaquim-Andrew-Mats Message Base Proposal --------------------------------------------------------------------- Copyright 1993 Joaquim Homrighausen, Andrew Milner, Mats Birch, Mats Wallin. ALL RIGHTS RESERVED. --------------------------------------------------------------------- ===================================================================== Restrictions --------------------------------------------------------------------- JAM may be used by any developer as long as these specifications are followed exactly. JAM may be used free-of-charge by any developer for any purpose, commercially or otherwise. This document may be freely copied and distributed, but must NEVER be distributed in a modified form. If you have an enhancement request, please contact the author of this document; do not change it yourself. All applications that support JAM must include one of the following notices in their documentation and somewhere in the product's credit section: "JAM(mbp) - Copyright 1993 Joaquim Homrighausen, Andrew Milner, Mats Birch, Mats Wallin. ALL RIGHTS RESERVED." or "This product uses the JAM(mbp) API - Copyright 1993 Joaquim Homrighausen, Andrew Milner, Mats Birch, Mats Wallin. ALL RIGHTS RESERVED." No organization, company, person, entity, or other being may impose any fees for any reason for providing this document or the accompanying API. This document and the accompanying API may not be sold or otherwise transferred for personal or company gain under any circumstances. ===================================================================== Definitions and general notes --------------------------------------------------------------------- CURRENTREV 1 JAM The JAM message base format. CRC Cyclic Redundancy Check. All CRC values calculated on strings must assume that the data within the string has been converted to lowercase (A-Z = a-z). CRC-32 32-bit CRC (as used in the Zmodem file transfer protocol) value. The polynom for a CRC-32 is edb88320H and the CRC-32 seed is -1L (ffffffffH). uchar Unsigned 8-bit value ushort Unsigned 16-bit value ulong Unsigned 32-bit value UNIX date An ulong representing the number of seconds since midnight, January 1, 1970. UNIX-style dates is the only form of time stamps used in JAM (1). Message # The physical record number within the index file is used as a message number. The lowest message number is one (1) and the highest message number is 4294967295 (ffffffffH). FTN FidoNet Technology Network FTS FidoNet Technical Standard (1) All timestamps created locally (i.e. those not imported from other systems) are stored in local time. ===================================================================== Files --------------------------------------------------------------------- Each conference is made up from four files. How and where these files are stored and named is implementation dependant. The only file with a fixed minimum size is the .JHR (header data) file. It has a 1024- byte block used to hold information about a specific message area as described later. filename.JHR - Message header data filename.JDT - Message text data filename.JDX - Message index filename.JLR - Lastread information A future revision of JAM may also include a file that holds the following three items: - The highest assigned user number - The last generated message ID - A global conference list with the conference name, description, and physical location of the message base. ===================================================================== .JHR file header --------------------------------------------------------------------- Below is the format of the 1024-byte record at the beginning of all .JHR files. The first actual message header starts at offset 1024 in the .JHR file. FixedHeaderInfoStruct: ulong Signature; // <J><A><M> followed by <NUL> ulong datecreated; // Creation date ulong modcounter; // Update counter ulong activemsgs; // Number of active (not deleted) msgs ulong passwordcrc; // CRC-32 of password to access ulong basemsgnum; // Lowest message number in index file uchar RESERVED[1000]; // Reserved space end; MODCOUNTER must be incremented and updated on disk each time an application modifies the contents of the message base. When it reaches ffffffffH, it wraps to zero. --------------------------------------------------------------------- BaseMsgNum Lowest message number in index file --------------------------------------------------------------------- This field determines the lowest message number in the index file. The value for this field is one (1) when a message area is first created. By using this field, a message area can be packed (deleted messages are removed) without renumbering it. If BaseMsgNum contains 500, the first index record points to message number 500. BaseMsgNum has to be taken into account when an application calculates the next available message number (for creating new messages) as well as the highest and lowest message number in a message area. --------------------------------------------------------------------- ????????.JHR Message headers --------------------------------------------------------------------- The .JHR file contains none or more Header records. Each record define one message and contains information about the message and its text (if any). The Header record is of variable length. The layout of the Header record follows. MessageHeader: MessageFixedHeader: ulong Signature; // <J><A><M> followed by <NUL> ushort Revision; // Revision level of header (1) ushort ReservedWord; // Reserved for future use ulong SubfieldLen; // Length of subfields (2) ulong TimesRead; // Number of times message read ulong MSGIDcrc; // CRC-32 of MSGID line (3) ulong REPLYcrc; // CRC-32 of REPLY line (3) ulong ReplyTo; // This msg is a reply to.. ulong Reply1st; // First reply to this msg ulong Replynext; // Next msg in reply chain ulong DateWritten; // When msg was written ulong DateReceived; // When msg was read by recipient ulong DateProcessed;// When msg was processed by tosser/ // scanner ulong MessageNumber;// Message number (1-based) ulong Attribute; // Msg attribute, see "Msg Attributes" ulong Attribute2; // Reserved for future use ulong Offset; // Offset of text in ????????.JDT file ulong TxtLen; // Length of message text ulong PasswordCRC; // CRC-32 of password to access message ulong Cost; // Cost of message end; SubField1 // Extra fields as defined below . . SubFieldXX end; (1) This field is intended for future revisions of the specifications to allow the use of a different fixed-length binary message header. The current revision level is one (1). (2) The SubfieldLen field is set to zero (0) if the header does not have any subfield data. I.e. the length of the binary header is not included in this field. (3) When calculating the CRC-32 of the MSGID and REPLY lines, the text ^aMSGID: and ^aREPLY: should be removed as well as all leading and trailing white space characters. The SubField structure is made up of an ID, a length specifier, and a block of data. Zero or more subfields may follow the fixed-length binary header. SubFields are not stored in any specific order and are not terminated by any specific character unless otherwise specified. SubField: ushort LoID; // Field ID, 0-65535 ushort HiID; // Reserved for future use ulong datlen; // Length of buffer that follows uchar Buffer[datlen]; // DATLEN bytes of data end; --------------------------------------------------------------------- Defined LoID codes --------------------------------------------------------------------- ID=0, Name=OADDRESS A network address. This is used to specify the originating address. More than one OADDRESS field may exist. DATLEN must not exceed 100 characters. For a FidoNet-style address, this field must follow the ZONE:NET/NODE.POINT@DOMAIN format where .POINT is excluded if zero and @DOMAIN is excluded if unknown. ID=1, Name=DADDRESS A network address. This is used to specify the destination address. More than one DADDRESS field may exist (e.g. carbon copies). DATLEN must not exceed 100 characters. For a FidoNet-style address, this field must follow the ZONE:NET/NODE.POINT@DOMAIN format where .POINT is excluded if zero and @DOMAIN is excluded if unknown. ID=2, Name=SENDERNAME The sender (author) of the message. DATLEN must not exceed 100 characters. ID=3, Name=RECEIVERNAME The recipient of the message. DATLEN must not exceed 100 characters. ID=4, Name=MSGID Used to store the message identification data. All data not relevant to the actual ID string, including leading and trailing white space characters should be removed. DATLEN must not exceed 100 characters. ID=5, Name=REPLYID Used to store the message reply data. All data not relevant to the actual reply string, including leading and trailing white space characters should be removed. DATLEN must not exceed 100 characters. ID=6, Name=SUBJECT The subject of the message. DATLEN must not exceed 100 characters. Note that this field may not be used for FidoNet-style file attaches or file requests. ID=7, Name=PID Used to store the FTN PID kludge line. Only the actual PID data is stored and ^aPID: is stripped along with any leading and trailing white space characters. DATLEN must not exceed 40 characters. ID=8, Name=TRACE This is also referred to as ^aVia information in FTNs. It contains information about a system which the message has travelled through. The format of the field is where: YYYY is the year (1992-9999) MM is the month (01-12) DD is the day (01-31) HH is the hour (00-23) MM is the minute (00-59) SS is the second (00-59) The timestamp is stored in ASCII (0-9) characters. The network address is the address of the system. It is expressed in ASCII notation in the native format of the forwarding system. ID=9, Name=ENCLOSEDFILE A file attached to the message. Only one filename may be specified per subfield. No wildcard characters are allowed. If this subfield is present in a message header, the ATTRIBUTE must include the MSG_FILEATTACH bit. ID=10, Name=ENCLOSEDFILEWALIAS Identical to ENCLOSEDFILE with the exception that the filename is followed by a (00H) and an alias filename to be transmited to the remote system in place of the local name of the file. ID=11, Name=ENCLOSEDFREQ A request for one or more files. Only one filemask may be specified per subfield. If the filemask contains a complete path, it is to be regarded as an update file request. If this subfield is present in a message header, the ATTRIBUTE must include the MSG_FILEREQUEST bit. To indicate that a password is to be transmitted along with the request, a (00H) character followed by the password is appended. E.g. SECRET*.*MYPASSWORD. ID=12, Name=ENCLOSEDFILEWCARD One or more files attached to the message. Only one filename may be specified per subfield. Wildcard characters are allowed. If this subfield is present in a message header, the ATTRIBUTE must include the MSG_FILEATTACH bit. ID=13, Name=ENCLOSEDINDIRECTFILE One or more files attached to the message. The filename points to an ASCII file with one filename entry per line. If alias filenames are to be used, they are specified after the actual filename and separated by a (00H) character, e.g. C:\MYFILE.LZHNEWS. Wildcard characters are not allowed. ID=1000, Name=EMBINDAT Reserved for future use. ID=2000, Name=FTSKLUDGE An FTS-compliant "kludge" line not otherwise represented here. All data not relevant to the actual kludge line, including leading and trailing white space and ^A (01H) characters should be removed. DATLEN must not exceed 255 characters. The FTS kludges INTL, TOPT, and FMPT must never be stored as separate SubFields. Their data must be extracted and used for the address SubFields. ID=2001, Name=SEENBY2D Used to store two-dimensional (net/node) SEEN-BY information often used in FTN conference environments. Only the actual SEEN-BY data is stored and ^aSEEN-BY: or SEEN-BY: is stripped along with any leading and trailing white space characters. ID=2002, Name=PATH2D Used to store two-dimensional (net/node) PATH information often used in FTN conference environments. Only the actual PATH data is stored and ^aPATH: is stripped along with any leading and trailing white space characters. ID=2003, Name=FLAGS Used to store the FTN FLAGS kludge information. Note that all FLAG options that have binary representation in the JAM message header must be removed from the FLAGS string prior to storing it. Only the actual flags option string is stored and ^aFLAGS is stripped along with any leading and trailing white space characters. ID=2004, Name=TZUTCINFO Time zone information. This subfield consists of four mandatory bytes and one optional. The first character may be a plus (+) or a minus (-) character to indicate a location east (plus) or west (minus) of UTC 0000. The plus character is implied unless the first character is a minus character. The following four bytes must be digits in the range zero through nine and indicates the offset in hours and minutes. E.g. 0100 indicates an offset of one hour east of UTC. --------------------------------------------------------------------- Message attributes --------------------------------------------------------------------- MSG_LOCAL (0x00000001L) // Msg created locally MSG_INTRANSIT (0x00000002L) // Msg is in-transit MSG_PRIVATE (0x00000004L) // Private MSG_READ (0x00000008L) // Read by addressee MSG_SENT (0x00000010L) // Sent to remote MSG_KILLSENT (0x00000020L) // Kill when sent MSG_ARCHIVESENT (0x00000040L) // Archive when sent MSG_HOLD (0x00000080L) // Hold for pick-up MSG_CRASH (0x00000100L) // Crash MSG_IMMEDIATE (0x00000200L) // Send Msg now, ignore restrictions MSG_DIRECT (0x00000400L) // Send directly to destination MSG_GATE (0x00000800L) // Send via gateway MSG_FILEREQUEST (0x00001000L) // File request MSG_FILEATTACH (0x00002000L) // File(s) attached to Msg MSG_TRUNCFILE (0x00004000L) // Truncate file(s) when sent MSG_KILLFILE (0x00008000L) // Delete file(s) when sent MSG_RECEIPTREQ (0x00010000L) // Return receipt requested MSG_CONFIRMREQ (0x00020000L) // Confirmation receipt requested MSG_ORPHAN (0x00040000L) // Unknown destination MSG_ENCRYPT (0x00080000L) // Msg text is encrypted (1) MSG_COMPRESS (0x00100000L) // Msg text is compressed (1) MSG_ESCAPED (0x00200000L) // Msg text is seven bit ASCII (1) MSG_FPU (0x00400000L) // Force pickup MSG_TYPELOCAL (0x00800000L) // Msg is for local use only MSG_TYPEECHO (0x01000000L) // Msg is for conference distribution MSG_TYPENET (0x02000000L) // Msg is direct network mail MSG_NODISP (0x20000000L) // Msg may not be displayed to user MSG_LOCKED (0x40000000L) // Msg is locked, no editing possible MSG_DELETED (0x80000000L) // Msg is deleted (1) This revision of JAM does not include compression, encryption, or escaping. The bits are reserved for future use. ===================================================================== ????????.JDT Message text --------------------------------------------------------------------- The .JDT file contains the text of messages. The text is stored as an stream of seven or eight bit ASCII data. Allowed characters in the text are 00H through ffH unless the header ATTRIBUTE field has the MSG_ESCAPED bit enabled, in which case the legal range of data is 20H through 7eH. An escaped character is stored as \ where is the two digit hexadecimal ASCII value of the character. A single \ is stored as \\ or \5C. The case of the hexadecimal ASCII value is irrelevant, i.e. 5c is treated as 5C. ===================================================================== ????????.JDX Message index --------------------------------------------------------------------- The .JDX file is used to quickly locate messages for any given user name or to locate a message with a specific number. Each record in the file consists of two ulongs. The first ulong holds the CRC-32 of the recipient's name (lowercase), the second ulong holds the physical offset of the message header in the .JHR (header) file. The record number (+BaseMsgNum) within the .JDX file determines a message's number. If both ulongs are -1 (ffffffffH), there is no corresponding message header. ===================================================================== ????????.JLR Lastread storage --------------------------------------------------------------------- The .JLR file is used to maintain a user's position within a message area. The layout of the "lastread" record follows. One record per user is required. LastRead: ulong UserCRC; // CRC-32 of user name (lowercase) (1) ulong UserID; // Unique UserID ulong LastReadMsg; // Last read message number ulong HighReadMsg; // Highest read message number end; (1) The functions to convert a string to lowercase characters that are provided in the API will only convert characters A-Z (into a-z). It is required that this convention is followed by all applications. The UserID field is a unique number for each user. If the "lastread" record is deleted, UserCRC and UserID are both set to -1 (ffffffffH). An application may not depend on any specific order in the .JLR file. A user's "lastread" record may appear anywhere in the file and must be searched for when retrieving it and when storing an updated record. ===================================================================== Updating message headers --------------------------------------------------------------------- If a header record grows after is has been retrieved from the .JHR file, it must be appended to the end of the .JHR file since it would overwrite the following header otherwise. The .JDX file must be properly updated to indicate the new location of the header record. The old header record must be changed to indicate that it has been deleted by setting the MSG_DELETED bit in the Attribute field and the TextLen field to zero (to prevent a maintenance program from removing the message text that is now pointed to by another header). ===================================================================== Message base sharing and locking --------------------------------------------------------------------- To allow several programs to access the message base at any given time, region locking is used to protect the message base from being corrupted during updates. When an application needs to write to any of the message base files, it must first attempt to lock the first byte of the .JHR (header) file. If the lock call fails, the application must either fail or attempt to lock the file again. The message base files may under no circumstances be updated if the application cannot successfully lock the .JHR file. Note that data acquired (read) from the message base may not be used when writing data to the message base, unless the application has maintained a lock of the message base from the time the data was acquired or the MODCOUNTER field is the same as when the data was acquired. The application must open the files in shareable (DENYNONE) read/ write or readonly mode. The only exception to this is an application that requires exclusive access to the message base, such as a message base maintenance utility, it should open the files in non-shareable (DENYALL) read/write mode. ===================================================================== Reply threads and linking --------------------------------------------------------------------- JAM introduces a new reply link pointer, not commonly used today. This section is an attempt to describe how reply threads, reply linking, and this new reply link pointer is implemented in JAM. One of the major differences is that reply threads in JAM are not based on similar or identical subjects of messages since this method does not allow for proper reply threads. The method used in JAM is based on the immediate relation between any given message and direct replies to it. This is supported by many message editors by using the MSGID and REPLY FTS kludge fields. These are common, although expressed differently, in messages not based on FidoNet technology, such as RFC-822. The obvious advantages include allowing a program to easily find the original message to a reply, and to find all replies to any given message. The reply thread information consists of three fields: ReplyTo, Reply1st, and ReplyNext. The reason for three fields, as opposed to just two, is that with two fields, it is only possible to keep track of the original message of a reply (which is sufficient) and one reply to any given message (which is not sufficient). With three fields, it is possible to maintain a thread of any number of replies to any given message. In the description of the different fields below, the following messages and message numbers will be referred to: 1 -> 2 -> 4 -> 5 : : : +--> 8 : +--> 3 -> 7 : +--> 6 Message number two, three, and six are replies to message number one. Message number four and eight are replies to message number two. Message number seven is a reply to message number three. Message number five is a reply to message number four. --------------------------------------------------------------------- ReplyTo --------------------------------------------------------------------- This field holds the number of the message that this message is a reply to. In the example above, the ReplyTo field would contain the following values: Message number one would contain zero; message number two, three, and six, would contain one; message number four and eight would contain two; message number seven would contain three, and message number five would contain four. --------------------------------------------------------------------- Reply1st --------------------------------------------------------------------- This field holds the number of the first message that is a reply to this message. In the example above, the Reply1st field would contain the following values: Message number one would contain two, message number three would contain seven, and message number four would contain five. All other messages would contain zero. --------------------------------------------------------------------- ReplyNext --------------------------------------------------------------------- This field is used to create the actual message thread or chain. In the event that there is more than one reply to any given message, it is necessary to maintain a thread of all the replies; this is due to the fact that the original message can only hold information about the first reply (the Reply1st field) to it. The first reply (which the original message's Reply1st field holds), has its ReplyNext field pointing to the second reply, the second reply's ReplyNext field poinst to the third reply, and so on. In the example above, the ReplyNext field would contain the following values: Message number two would contain three, message number three would contain six, and message number four would contain eight. All other messages would contain zero. ===================================================================== Contacts --------------------------------------------------------------------- Joaquim Homrighausen Andrew Milner Mats Wallin // end of file "jam.doc"