2007-03-01

QPS Protocol

The following describes the format of the QPS 3.5 protocol. Earlier versions of QPS are very similar, with only minor packet changes. All communication between the client and the QPS server is done via Messages.

Message from Client to Server.
0000 0002 Session ID
0002 0002 Code
0004 0004 Version (0x003f003f)
0008 0002 Sequence Number
000a 0004 Sub-Data Length
000e 0004 Packet Length
0012 **** Data
**** **** Sub-Data


Just like the Quark file format, everything is stored in Network Byte Order. Packet length is the length of Data + length of Sub-Data. Sequence numbers start at 1 and are incremented for each message sent by the client. Session ID starts at 0, and is initialized by the server upon successful login.

Message from Server to Client
0000 0004 Version (0x003f003f)
0004 0002 Code
0006 0002 Status
0008 0002 Sequence Number
000a 0004 Flags
000e 0004 Length
0012 **** Data


All server communication is a response to a message from the client. The sequence number can be used to identify which request from the client prompted this response.


Signon Packet (0x05)
The Signon packet is the first packet sent from the client to the server. It contains the username and password of the client.
0000 0012 Message Header (Code: 0x05)
0012 0001 Username Length
0013 **** Username
**** 0001 Password Length
**** **** Password
**** 0004 0x100


0000 0012 Response Header
0012 0004 Session ID


If the Code is 0, then login was successful. Otherwise, the login failed. The Session ID returned should be used in all future packets sent by the client.


GetHeaders (0x4a)
This packet retrieves a list of headers from the server.
0000 0012 Message Header (Code: 0x4a)


0000 0012 Response Header
0012 0004 Unknown
0016 0004 Number of Header Items
001a 0034 Header Item 0
004e 0034 Header Item 1...


Header Item
0000 0004 Header ID
0004 0004 Data Type
0008 0004 Reserved
000c 0001 Name Length
000d 0027 Name


The Data Types supported are:
0001 String
0002 Time Stamp
0004 Integer
0005 Float
0006 Checkbox
0007 Dropdown


GetPublications (0x66)
This packet asks the server for the list of Publications hosted by the server.
0000 0012 Message Header (Code: 0x66)
0012 0014 All zeroes


0000 0012 Response Header
0012 0008 Unknown
001a 0004 Number of Publications
001e 0001 Publication 0 Name Length
001f **** Publication 0 Name
**** 0004 Publication 0 ID
**** 0001 Publication 1 Name Length
**** **** Publication 1 Name
**** 0004 Publication 1 ID...


If Publication Name Length is even, then there's a 1 byte pad at the end of the
Publication Name.

GetStatuses (0x2b)
This call gets all the possible Statuses from the server. Statuses may differ
across publications, which is why the publication is sent with the request.
0000 0012 Message Header (Code: 0x2b)
0012 0001 Publication Length
0013 0043 Publication Name


Why we don't send the ID instead of the Name is a mystery.

0000 0012 Response Header
0012 0008 Unknown
001a 0004 Number of Statuses
001e **** Status 0
**** **** Status 1...


Status
0000 0004 Unknown
0004 0004 Status ID
0008 000e Unknown
0016 0001 Status Length
0017 **** Status Name
**** 0002 Unknown


If Status Length is even, then Status Name has a 1 byte pad at the end of it.

GetRepositories (0x40)
This call returns the repository information (the file share where the story files are actually kept).
0000 0012 Message Header (Code: 0x40)


0000 0012 Response Header
0012 0004 Number of Repositories
0016 **** Repository 0
**** **** Repository 1...


Repository
0000 0004 Length of Repository packet
0004 0032 Unknown
0036 0001 Repository Name Length
0037 **** Repository Name
**** 009a Unknown
**** 0002 Key 0
**** 0002 Value 0 Length
**** **** Value 0
**** 0002 Key 1
**** 0002 Value 1 Length
**** **** Value 1... (until Key=0xffff)


If Value Length is odd, then the Value has a 1 byte pad at the end of it.

The Keys are below.

0x09 Mount Info
0x12 Pathname
0x13 Mount Point


For Mount Point and Pathname, the Value is pretty simple, it's simply a string.
Mount Info is more complicated.

0000 0002 Unknown
0002 0004 Mount Type
0006 **** Mount Data


Mount Data depends n the Mount Type. There are two common mount types. "cifs" for Samba, and "afpm" for Apple File Share.

cifs
0000 000a Unknown
000a **** CIFS Share


afpm
0000 000c Unknown
000c 0002 Offset to IP Length (from beginning of Mount Data)
000e 0002 Offset to Share Length (from beginning of Mount Data)
**** 0001 IP Length
**** **** AFP IP
**** 0001 Share Length
**** **** AFP Share


Search (0x14)
The client sends the search criteria to the server to create a new "search session".
0000 0012 Message Header (Code: 0x14)
0012 0004 0x8


This is the first packet that contains sub-data. This is defined below.
0000 0004 Sub Data Length
0004 0004 0x00060006
0008 0004 0xb46dd345
000c 0020 All zeroes
002c 0004 0x2
0030 0004 0xaf7fffff
0034 0004 0x48
0038 0004 Length of Publications
003c 0004 Offset of Publication 0 ID
0040 0004 Queries Length
0044 0004 Offset of Queries
**** 0004 Publication 0 ID
**** 0004 0x0
**** 0004 Publication 1 ID
**** 0004 0x0...
**** **** Queries


Query
0000 0004 Query Length
0004 0004 Header Key
0008 0004 Header Type
000c **** Query Value


Query Value depends on the Header Type

Header Type 6 (Checkbox):
0000 0004 0x2
0004 0004 0x10000
0008 0004 0x1
000c 0004 0x18
0010 0004 0x1
0014 0004 0x1
0018 0004 0x1
001c 0004 Checkbox Value (0 or 1)
0020 0004 0x0


Header Type 7 (Dropdown):
0000 0004 0x0
0004 0004 0x10000
0008 0004 0x1
000c 0004 0x18
0010 0004 0x1
0014 0004 0x1
0018 0004 0x1
001c 0004 Dropdown ID
0020 0004 0x0


The server will return your search ID.

0000 0012 Response Header
0012 0002 Search ID


This Search ID will be used to fetch the results.

SearchCount (0x42)
This message requests the total # of results from the search.
0000 0012 Message Header (Code: 0x42)
0012 0002 Search ID
0014 000e 0
0022 0001 1
0023 0001 0


There is a Sub-Data component attached to this message.
0000 0004 1


The server will respond with the number of stories that match your query.
0000 0012 Response Header
0012 0004 Number of Stories


GetSearchResults (0x43)
This message gets a bunch of search results.
0000 0012 Message Header (Code: 0x43)


The server will respond with the results. If Code is not zero, then the server is
finished answering. If Code is zero, the response contains a list of matching stories.
You should continue to call GetSearchResults until the server is done.
0000 0012 Response Header
0012 0002 Entry 0 Length
0014 **** Entry 0
**** 0002 Entry 1 Length
**** **** Entry 1...


Entry
0000 0010 Unknown
0010 0001 File Status
0011 0025 Unknown
0036 0001 Filename Length
0037 **** Filename


If File Status has high-bit set, then the story is checked out by someone.

SearchUpdate

After doing a search.. the client should listen for updates from the server. The
server will send new updates whenever stories get added or removed from
the search results. Updates can be identified because they have a Status of 5.
The message will have its Flags set to identify whether or not the story is being added or removed from the search results. If the Flags & 0x100 then the story is being added, otherwise it is being removed.
0000 0012 Response Header
0012 0008 Unknown
001a 0001 Filename Length
001b **** Filename


OpenHeader (0x08)
This message will open the header of a story for reading and editing.
0000 0012 Message Header (Code: 0x08)
0012 0008 0
001a 0001 Filename Length
001b 0100 Filename
011b 0001 2 (for writing)


0000 0012 Response Header
0012 0002 Header ID


ReadHeader (0x09)
This message will fetch the current story header information.
0000 0012 Message Header (Code: 0x09)
0012 0002 Header ID


0000 0012 Response Header
0012 0120 Unknown
0132 0004 Number of Header Items
0136 0014 Header Item 0
014a 0014 Header Item 1...


Header Item
0000 0004 ID
0004 0004 Type
0008 0004 Reserved
000c 0004 Flags
0010 0004 Value


For some Types, Value may reference data in the packet. This data is located at 0x118+Value from the beginning of the current Header Item.

If Flags&0x1000000 then the value is blank. This is important, because the flags may say a field is blank, but the Value might point to data.

SaveHeader (0x0a)
0000 0012 Message Header (Code: 0x0a)
0012 0002 Header ID


The raw Header should be put into the SubData part of the message.

0000 0012 Response Header


CloseHeader (0x0e)
This message tells the server that we're done with the header.
0000 0012 Message Header (Code: 0x0e)
0012 0002 Header ID


If you made changes to the header, you MUST open the file on disk, and make the same changes to the end of the file. Otherwise, if the QPS server reboots, all changes you made will be undone.

SignOff (0x16)
This message tells the server that we want to sign off.
0000 0012 Message Header (Code: 0x16)
0012 0002 Session ID


0000 0012 Response Header


Close (0x06)
After we've signed off, we need to close the socket or sign back on. If we close the socket, we send this packet before doing so.
0000 0012 Message Header (Code: 0x06)

Quark file format

This is the file format used by Quark Copydesk.

The file is broken into chunks. Each chunk is exactly 256 bytes long. Chunks are identified by their ID number. Chunk 1 starts at position 0, Chunk 2 starts at position 512, and so on. The first chunk in the file contains the header followed by the TOC.

Quark Header:
The first thing in this file is a 26 byte Quark Header.

0000 0004 File Format Version (0x001b001b)
0004 0008 Identifier ("SPIFSPOC")
000c 0004 QPS Header Offset
0010 000a Reserved
The QPS Header Offset points to the QPS Header attached to the end of the file.

Table of Contents:
Immediately after the header is a table of contents. This table lists the positions in the file for all the interesting sections of the file. Each entry is the Chunk ID of the start of the section.

0000 0004 QPS Column
0004 0004 QPS History
0008 0004 Unknown
000c 0004 Unknown
0010 0004 Backup Color
0014 0004 Backup Font
0018 0004 Color
001c 0004 Font
0020 0004 Component
0024 0004 Style
0028 0004 Unknown
002c 0004 Unknown
0030 0004 Component Data


Some of the sections are unknown. The main sections we are interested in are Font, Component, Style, and Component Data.

General Section Format:
Sections are made up of one or more chunks. Each Chunk is formatted as follows.

0000 00fc Chunk Data
00fc 0004 Next Chunk ID


Next Chunk ID will be 0 if this is the last chunk in the section. Otherwise it points to the next chunk that makes up the current section's data.

If the Next Chunk ID is negative, then the chunk it references is a special chunk called a Contiguous Chunk. Negate the Contiguous Chunk's ID to obtain the actual Chunk ID. A Contiguous Chunk looks different than a regular chunk.

0000 0002 Number of Contiguous Chunks
0002 **** Chunk Data
**** 0004 Next Chunk ID


Chunk Data is (Number of Contiguous Chunks)*256-6 bytes long. That means that if there is only 1 Contiguous Chunk, then Chunk Data is 250 bytes long, and Next Chunk ID is located at the end of the chunk.

This system means that all sections will be padded to fit into their respective chunks.

When describing section formats below, we will ignore the chunk system, and simply focus on the format of the data that is stuffed into those chunks.

Font Section:
The Font section contains a list of all the fonts used in this document. The fonts listed in this section are references in the rest of the file by their ID number.

0000 0004 Length
0004 0002 Number of Fonts
0006 **** Font Record 0
**** **** Font Record 1
**** **** Font Record 2 ...


Each Font Record looks like so.
0000 0002 Font ID
0002 0002 Padding
0004 0001 Font Name Length
0005 **** Font name
**** 0001 Short Name Length
**** **** Short Name


Style Section:
The Style section contains a list of all the Paragraph and Character styles used in the document. The overall section is structured as follows.

0000 0004 Length of Character Styles
0004 009a Character Style 0
009e 009a Character Style 1
0138 009a Character Style 2...
**** 0004 Length of Paragraph Styles
**** 00c6 Paragraph Style 0
**** 00c6 Paragraph Style 1
**** 00c6 Paragraph Style 2...


Each Character Style is 154 bytes long and is structured like so.

0000 0001 Length of Style Name
0001 0055 Style Name
0056 0002 Font ID
0058 0002 Font Flags
005a 0004 Font Size (16.16 fixed text size)
005e 001f Unknown
0079 0001 Hide Flag (notes have this flag set)
007a 0020 Unknown


Character Style Flags:
0001 Bold
0002 Italic
0004 Underline
0008 Outline
0010 Dropshadow
0020 Superscript
0040 Subscript
0100 Superior
0200 Strikeout
0400 Allcaps
0800 Smallcaps


Each Paragraph Style is 198 bytes long and looks like so.

0000 0001 Style Name Length
0001 003f Style Name
0040 0002 Style ID
0042 0016 Reserved
0058 0002 Justification
005a 006c Reserved



Component Section:
The Component section lists the name and type of all the components in the file.
0000 0004 Unknown
0004 0004 Component Section Length
0008 0008 Reserved
0010 0002 Number of Components
0012 001e Reserved
0030 008e Component 0
00be 008e Component 1
014c 008e Component 2...


Each Component is 142 bytes long and is structured like so.

0000 0018 Unknown
0018 0004 Component ID
001c 0004 Unknown
0020 0001 Type Length
0021 001f Component Type
0040 0001 Name Length
0041 001f Component Name
0060 002e Reserved


Component Data Section:
The Component Data section contains the raw data for each component.

0000 0002 Number of Components
0002 001c Component Index 1
001e 001c Component Index 2
003a 001c Component Index 3...


Some components have some extra information at the end of them... so you cannot rely on the offsets of any component until you have checked the component prior. Each component index is fairly straightforward.

0000 0004 Unknown
0004 0004 Flags
0008 0008 Reserved
0010 0004 Chunk ID
0014 0004 Component ID
0018 0004 Reserved
001c 0004 Extra Length
0020 **** Extra Data


If Flags is nonzero, then Extra Length and Extra Data are present. Extra Data contains information such as the filename that the component was imported from.

Each Component Index points to a Chunk that contains pointers to the raw text and styles of the component.

0000 0004 Length of text in this component
0004 0004 Number of Paragraphs * 8
0008 0004 Paragraph 0 Chunk ID
000c 0004 Paragraph 0 Length
0010 0004 Paragraph 1 Chunk ID
0014 0004 Paragraph 1 Length...
**** **** Character Styles
**** **** Paragraph Styles


The Paragraph Chunks are special Chunks. They don't follow the standard Chunk format at all. Paragraph 0 starts at the referenced Chunk ID, however, the data of that block just continues from the beginning for length bytes. There is no chunk metadata at all.

The Character Styles are shown below.

0000 0004 Number of Character Styles * 8
0004 0004 Style 0 ID
0008 0004 Style 0 Length
000c 0004 Style 1 ID
0010 0004 Style 1 Length...


The way the styles work is pretty simple. Style ID references a specific Style in the Style Section. This style applies for Length bytes of text. Then the next style referenced takes over and applies to the next Length bytes of text, and so on.

Paragraph Styles work in the exact same way.

QPS Header:
This header is identical the format used in the QPS Protocol (more on that later).