Version: TOC 1.0 / TOC 2.0
This document is a reference for the TOC (Talk to OSCAR Client) protocol as implemented by Open OSCAR Server. It covers both TOC 1.0 and TOC 2.0, and contains enough detail to build a fully-functional client from scratch.
TOC is an ASCII-based protocol layered on top of FLAP (SFLAP) framing over TCP. It was originally designed by AOL to service the TiK (Tcl/Tk) and TIC (Java) AIM clients. TOC 2.0 extends the original protocol with server-side buddy lists, typing notifications, encoded messages, and richer buddy status information.
All TOC communication is wrapped in FLAP frames over a single TCP connection. The default port is 9898.
Every frame begins with a 6-byte binary header in network byte order (big-endian):
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Marker | Always 0x2A (ASCII *) |
| 1 | 1 | Frame Type | See below |
| 2 | 2 | Sequence Number | Monotonically increasing per direction |
| 4 | 2 | Data Length | Length of the payload that follows |
The payload immediately follows the header at offset 6.
| Value | Name | Description |
|---|---|---|
| 1 | SIGNON | Connection initialization |
| 2 | DATA | Normal command/response data |
| 3 | ERROR | Not used by TOC |
| 4 | SIGNOFF | Client disconnect |
| 5 | KEEP_ALIVE | Heartbeat (no payload) |
Server → Client (after the client sends FLAPON):
| Offset | Size | Description |
|---|---|---|
| 0 | 4 | FLAP version (always 0x00000001) |
Client → Server (response to the server's signon frame):
| Offset | Size | Description |
|---|---|---|
| 0 | 4 | FLAP version (0x00000001) |
| 4 | 2 | TLV tag (0x0001) |
| 6 | 2 | Screen name length (N) |
| 8 | N | Normalized screen name (NOT null-terminated) |
Each direction maintains an independent sequence counter starting at an arbitrary value (usually 0 for clients). The counter increments by 1 for each frame sent. When it reaches 65535 it wraps to 0.
Client Server
| |
| --- TCP connect (port 9898) ---> |
| --- "FLAPON\r\n\r\n" ---------> |
| <-- FLAP SIGNON frame ---------- |
| --- FLAP SIGNON frame ---------> |
| --- toc_signon/toc2_login -----> |
| <-- SIGN_ON --------------------- |
| <-- CONFIG/CONFIG2 -------------- |
| <-- NICK ------------------------ |
| --- toc_add_buddy (optional) ---> |
| --- toc_add_permit (optional) --> |
| --- toc_init_done --------------> |
| <-- UPDATE_BUDDY (for each --- |
| online buddy) |
Step by step:
FLAPON\r\n\r\n (10 bytes).0x0001 holding the normalized screen name.SIGN_ON, then CONFIG/CONFIG2, then
NICK. On failure, the server sends an ERROR and/or drops the
connection.toc_init_done to go online. This MUST be sent within 30
seconds of the signon command.Passwords are "roasted" before transmission to prevent cleartext on the wire (this is obfuscation, not encryption).
Roasting String: Tic/Toc
Algorithm:
0x.Example: The password password roasts to 0x2408105c23001130.
def roast_password(password):
roast = "Tic/Toc"
result = []
for i, ch in enumerate(password):
xored = ord(ch) ^ ord(roast[i % len(roast)])
result.append(f"{xored:02x}")
return "0x" + "".join(result)
toc_signon <authorizer_host> <authorizer_port> <screen_name> <roasted_password> <language> <version>
| Parameter | Description |
|---|---|
| authorizer_host | Auth server hostname (e.g. login.oscar.aol.com). Ignored by Open OSCAR Server but required. |
| authorizer_port | Auth server port (e.g. 5190). Ignored but required. |
| screen_name | User screen name |
| roasted_password | Password roasted as described above |
| language | Language string (e.g. english) |
| version | Client version string, max 50 characters (e.g. "TIC:MyClient") |
Example:
toc_signon login.oscar.aol.com 5190 toctest1 0x2408105c23001130 english "TIC:MyClient 1.0"
On success, the server replies with:
SIGN_ON:TOC1.0
CONFIG:<config data>
NICK:<formatted screen name>
Same syntax as toc_signon. The server replies with SIGN_ON:TOC2.0 and
CONFIG2 instead of CONFIG. TOC 2 features (server-side buddy list,
UPDATE_BUDDY2, IM_IN2, etc.) are enabled.
toc2_signon login.oscar.aol.com 5190 toctest1 0x2408105c23001130 english "TIC:MyClient 1.0"
Extends toc2_signon with additional trailing parameters and enables encoded
message variants (IM_IN_ENC2, CHAT_IN_ENC).
toc2_login <host> <port> <screen_name> <roasted_pw> <language> <version> 160 US "" "" 3 0 30303 -kentucky -utf8 <code>
| Parameter | Description |
|---|---|
| (first 6) | Same as toc_signon |
| 160 US "" "" 3 0 30303 -kentucky -utf8 | Fixed parameters (send verbatim) |
| code | Login code: 7696 * ascii(screen_name[0]) * ascii(password[0]) |
The version string MUST start with TIC: (e.g. "TIC:MyClient").
Code calculation example:
def login_code(screen_name, password):
return 7696 * ord(screen_name[0]) * ord(password[0])
For screen name toctest1 (first char t = 116) and password testpass1
(first char t = 116): 7696 * 116 * 116 = 103,547,776.
Example:
toc2_login login.oscar.aol.com 5190 toctest1 0x2408105c23001130 english "TIC:MyClient" 160 US "" "" 3 0 30303 -kentucky -utf8 103547776
On success:
SIGN_ON:TOC2.0
NICK:toctest1
CONFIG2:<config data>
All client commands are lowercase, space-separated, and sent as FLAP DATA frames (null-terminated). Arguments containing spaces should be enclosed in quotes. Screen names must be normalized (lowercased, spaces removed). Message content must be escaped (see Escaping Rules).
Tells the server the client is ready to go online. Must be sent within 30
seconds of the signon command, and only after SIGN_ON is received.
toc_init_done
Send an instant message to a user. The message may contain basic HTML.
toc_send_im <destination_user> <message> [auto]
| Parameter | Description |
|---|---|
| destination_user | Normalized screen name of the recipient |
| message | Quoted, escaped message (may contain HTML) |
| auto | Optional literal string auto to mark as auto-response |
Examples:
toc_send_im toctest2 "Hello there!"
toc_send_im toctest2 "I am away right now" auto
toc_send_im toctest2 "Check out \$100 deals"
Add one or more buddies to the buddy list. This subscribes to their presence
updates. Does not modify saved config (TOC 1 only — use toc_set_config to
persist).
toc_add_buddy <buddy1> [<buddy2> ...]
Example:
toc_add_buddy toctest2 joe mike
Remove one or more buddies from the buddy list. Does not modify saved config.
toc_remove_buddy <buddy1> [<buddy2> ...]
Example:
toc_remove_buddy toctest2
Save the buddy list configuration on the server. The config is a line-oriented format where the first character is the item type, followed by a space, then the value. Enclose the entire config in quotes.
toc_set_config <config_string>
Config item types:
| Prefix | Description |
|---|---|
g |
Buddy group name. All buddies until the next g or end of config belong to this group. |
b |
Buddy screen name |
p |
Permit list entry |
d |
Deny list entry |
m |
Permit/Deny mode: 1=Permit All, 2=Deny All, 3=Permit Some, 4=Deny Some |
Example:
toc_set_config "m 1
g Buddies
b toctest2
b joe
g Coworkers
b mike
d spammer123
"
Query the online status of a user. Returns an UPDATE_BUDDY message if
online, or an ERROR if the user is not available.
toc_get_status <screen_name>
Example:
toc_get_status toctest2
Set the user's profile information (HTML).
toc_set_info <info_html>
Example:
toc_set_info "<b>Hello!</b> I love AIM."
Request a user's profile. The server responds with a GOTO_URL containing a
URL to view the profile, or an ERROR.
toc_get_info <screen_name>
Example:
toc_get_info toctest2
Set or clear the away message. If a message is provided, the user's unavailable flag is set. If omitted, the flag is cleared.
toc_set_away [<away_message>]
Examples:
toc_set_away "Gone fishing! <b>BRB</b>"
toc_set_away
Set the idle time in seconds. If 0, the user is marked as active. The server auto-increments the idle counter, so only call this once.
toc_set_idle <seconds>
Examples:
toc_set_idle 300
toc_set_idle 0
Declare the client's capabilities. All supported capabilities must be sent at once. Capabilities are represented as UUIDs (see Capabilities).
toc_set_caps [<uuid1> [<uuid2> ...]]
Example:
toc_set_caps 09461343-4C7F-11D1-8222-444553540000 09461345-4C7F-11D1-8222-444553540000
Warn (evil) another user. You can only warn users who have recently sent you an IM.
toc_evil <screen_name> <norm|anon>
| Type | Effect |
|---|---|
norm |
Normal warning (+20% to target's warning level, your identity revealed) |
anon |
Anonymous warning (+10%, your identity hidden) |
Example:
toc_evil spammer123 anon
Add users to the permit list. If currently in deny mode, switches to permit mode first. With no arguments and in deny mode, switches to permit-none.
toc_add_permit [<user1> [<user2> ...]]
Example:
toc_add_permit toctest2 joe
Add users to the deny (block) list. If currently in permit mode, switches to deny mode first. With no arguments and in permit mode, switches to deny-none.
toc_add_deny [<user1> [<user2> ...]]
Example:
toc_add_deny spammer123
Join a chat room on the specified exchange. Exchange should be 4 for
standard chat rooms. The server responds with CHAT_JOIN on success or
ERROR on failure.
toc_chat_join <exchange> <chat_room_name>
Example:
toc_chat_join 4 "My Chat Room"
Send a message to a chat room. Use the chat room ID received in the
CHAT_JOIN response. Since reflection is always on, you will receive your own
message back as a CHAT_IN.
toc_chat_send <chat_room_id> <message>
Example:
toc_chat_send 0 "Hello everyone!"
Send a private message within a chat room directed at a specific user.
toc_chat_whisper <chat_room_id> <destination_user> <message>
Example:
toc_chat_whisper 0 toctest2 "Can you see this?"
Invite one or more users to a chat room you are currently in.
toc_chat_invite <chat_room_id> <invite_message> <buddy1> [<buddy2> ...]
Example:
toc_chat_invite 0 "Come join us!" toctest2 joe
Accept a chat invitation received via CHAT_INVITE. The server will respond
with CHAT_JOIN.
toc_chat_accept <chat_room_id>
Example:
toc_chat_accept 5
Leave a chat room.
toc_chat_leave <chat_room_id>
Example:
toc_chat_leave 0
Set directory information. Fields are colon-separated within a quoted string.
toc_set_dir <info_string>
Fields (colon-separated): "first_name":"middle_name":"last_name":"maiden_name":"city":"state":"country":"email":"allow_web_search"
Example:
toc_set_dir "John":"":"Doe":"":"New York":"NY":"US":"john@example.com":""
Get a user's directory information. Returns a GOTO_URL or ERROR.
toc_get_dir <screen_name>
Example:
toc_get_dir toctest2
Search the user directory by field. Returns a GOTO_URL or ERROR.
toc_dir_search <search_fields>
Fields (colon-separated): "first_name":"middle_name":"last_name":"maiden_name":"city":"state":"country":"email"
You can search by keyword by placing it in the 11th position:
toc_dir_search "::::::::":"":"search term"
Example:
toc_dir_search "John":"":"Doe":"":"":"":"":""
Reformat the capitalization/spacing of your screen name. Returns
ADMIN_NICK_STATUS and NICK on success, or ERROR on failure.
toc_format_nickname <new_format>
Example:
toc_format_nickname "TocTest1"
Change your password. Returns ADMIN_PASSWD_STATUS on success or ERROR on
failure. Passwords are sent as plaintext (not roasted). Special characters
must be backslash-escaped (see Escaping Rules).
Note: Because passwords are sent in cleartext (unlike the roasted password at login), this command is not secure against network eavesdropping.
toc_change_passwd <old_password> <new_password>
Examples:
toc_change_passwd oldpass newpass
toc_change_passwd "my old pass" "my new pass"
toc_change_passwd oldpa\$\$ newpa\$\$
Accept a rendezvous proposal (e.g. file transfer) from another user.
toc_rvous_accept <screen_name> <cookie> <service_uuid>
| Parameter | Description |
|---|---|
| screen_name | The user who sent the proposal |
| cookie | Base64-encoded cookie from the RVOUS_PROPOSE message |
| service_uuid | UUID of the service being accepted |
Example:
toc_rvous_accept toctest2 fUdgNuQ6AAA= 09461343-4C7F-11D1-8222-444553540000
Cancel/decline a rendezvous proposal from another user.
toc_rvous_cancel <screen_name> <cookie> <service_uuid>
Example:
toc_rvous_cancel toctest2 fUdgNuQ6AAA= 09461343-4C7F-11D1-8222-444553540000
TOC 2 commands use the toc2_ prefix and operate on the server-side buddy
list (feedbag) directly. Changes are automatically saved.
Same syntax as toc_send_im.
toc2_send_im <destination_user> <message> [auto]
Example:
toc2_send_im toctest2 "Hey there!"
Send an encoded instant message. Supports encoding and language parameters.
toc2_send_im_enc <destination_user> "F" <encoding> <language> <message> [auto]
| Parameter | Description |
|---|---|
| destination_user | Normalized screen name |
| "F" | Unknown flag, always F |
| encoding | Character encoding (e.g. A for ASCII) |
| language | Language code (e.g. en) |
| message | Quoted, escaped message |
| auto | Optional auto for auto-response |
Example:
toc2_send_im_enc toctest2 "F" A en "Hello from TOC2!"
Set the permit/deny privacy mode.
toc2_set_pdmode <mode>
| Mode | Description |
|---|---|
| 1 | Allow all (default) |
| 2 | Block all |
| 3 | Allow permit list only |
| 4 | Block deny list only |
| 5 | Allow buddy list only |
Example:
toc2_set_pdmode 1
Create a new buddy list group.
toc2_new_group <group_name>
Example:
toc2_new_group "Work Friends"
Delete a buddy list group and all buddies within it.
toc2_del_group <group_name>
Example:
toc2_del_group "Work Friends"
Add buddies to the server-side buddy list using a config-format string. If the specified group does not exist, it will be created.
toc2_new_buddies <config_format>
Config format: {g:<group>\nb:<buddy>[:<alias>]\nb:<buddy2>\n}
Where \n is a literal linefeed character (ASCII 10, 0x0A).
Extended buddy format with alias and note:
b:<buddy>:<alias>:::::note
Examples:
Add two buddies to the "Friends" group:
toc2_new_buddies "{g:Friends\nb:toctest2\nb:joe\n}"
Add a buddy with an alias:
toc2_new_buddies "{g:Friends\nb:toctest2:Test User 2\n}"
Remove one or more buddies from a group. The last argument is always the group name.
toc2_remove_buddy <screen_name> [<screen_name> ...] <group>
Examples:
toc2_remove_buddy toctest2 "Friends"
toc2_remove_buddy toctest2 joe "Friends"
Add users to the permit list (feedbag-based).
toc2_add_permit <screen_name> [<screen_name> ...]
Example:
toc2_add_permit toctest2 joe
Remove users from the permit list.
toc2_remove_permit <screen_name> [<screen_name> ...]
Example:
toc2_remove_permit toctest2
Add users to the deny (block) list (feedbag-based).
toc2_add_deny <screen_name> [<screen_name> ...]
Example:
toc2_add_deny spammer
Remove users from the deny (block) list.
toc2_remove_deny <screen_name> [<screen_name> ...]
Example:
toc2_remove_deny spammer
Send a typing notification to another user.
toc2_client_event <screen_name> <typing_status>
| Status | Description |
|---|---|
| 0 | Idle (no activity) |
| 1 | Text entered (paused typing) |
| 2 | Currently typing |
Example:
toc2_client_event toctest2 2
Server messages use colons as separators. Messages are NOT null-terminated. When parsing, be aware that message content (IMs, chat messages) may contain colons — split with a maximum field count appropriate to each command.
Sent after a successful login.
SIGN_ON:<protocol_version>
Example:
SIGN_ON:TOC1.0
The user's saved configuration (buddy list, permit/deny). Only sent for TOC 1 logins. Config may be empty if none was previously saved.
CONFIG:<config_data>
Config format uses space-separated item types (same format as toc_set_config):
CONFIG:m 1
g Buddies
b toctest2
b joe
d spammer123
Config may be empty (CONFIG:) if no configuration has been saved.
The user's properly formatted screen name.
NICK:<formatted_screen_name>
Example:
NICK:TocTest1
An incoming instant message.
IM_IN:<source_user>:<auto_response T/F>:<message>
Everything after the third colon is the message (may contain additional colons).
Examples:
IM_IN:TocTest2:F:Hello there!
IM_IN:TocTest2:T:I am away from my computer right now.
IM_IN:TocTest2:F:<b>Bold</b> and <i>italic</i>
Buddy presence update. Handles arrivals, departures, and status changes.
UPDATE_BUDDY:<screen_name>:<online T/F>:<warning_level>:<signon_time>:<idle_minutes>:<user_class>
| Field | Description |
|---|---|
| screen_name | Formatted screen name |
| online | T if online, F if offline |
| warning_level | Warning percentage (0-100) |
| signon_time | Unix epoch timestamp of signon |
| idle_minutes | Minutes idle (0 = active) |
| user_class | 2-3 character class string (see below) |
User class characters:
| Position | Char | Meaning |
|---|---|---|
| uc[0] | ' ' |
Normal |
| uc[0] | 'A' |
On AOL |
| uc[1] | ' ' |
Normal |
| uc[1] | 'A' |
Admin |
| uc[1] | 'U' |
Unconfirmed |
| uc[1] | 'O' |
OSCAR Free (normal AIM user) |
| uc[1] | 'C' |
Mobile/wireless |
| uc[2] | ' ' |
Available |
| uc[2] | 'U' |
Unavailable (away) |
Examples:
UPDATE_BUDDY:TocTest2:T:0:1711036800:0: O
UPDATE_BUDDY:TocTest2:T:0:1711036800:0: U
UPDATE_BUDDY:TocTest2:F:0:0:0:
UPDATE_BUDDY:TocTest2:T:20:1711036800:5: OU
An error response. May include a screen name or sub-error code as a variable argument.
ERROR:<error_code>[:<variable>]
Example:
ERROR:901:toctest2
ERROR:980
See Error Codes for the full list.
The user has been warned.
EVILED:<new_warning_level>:<eviler_screen_name>
If the warning was anonymous, the eviler field is empty.
Examples:
EVILED:20:TocTest2
EVILED:10:
Successfully joined a chat room. The chat room ID is an integer assigned by the server — store it for subsequent chat commands.
CHAT_JOIN:<chat_room_id>:<chat_room_name>
Example:
CHAT_JOIN:0:My Chat Room
A message received in a chat room.
CHAT_IN:<chat_room_id>:<source_user>:<whisper T/F>:<message>
Example:
CHAT_IN:0:TocTest2:F:Hello everyone!
Users have joined or left a chat room. The first message of this type for a room contains the initial user list.
CHAT_UPDATE_BUDDY:<chat_room_id>:<inside T/F>:<user1>[:<user2>...]
| Field | Description |
|---|---|
| inside | T = users arrived, F = users departed |
Examples:
CHAT_UPDATE_BUDDY:0:T:TocTest1:TocTest2
CHAT_UPDATE_BUDDY:0:F:TocTest2
An invitation to join a chat room.
CHAT_INVITE:<chat_room_name>:<chat_room_id>:<sender>:<message>
Example:
CHAT_INVITE:My Chat Room:5:TocTest2:Come join us!
Confirmation that you have left a chat room.
CHAT_LEFT:<chat_room_id>
Example:
CHAT_LEFT:0
Directs the client to open a URL (for profiles, directory info, search results).
GOTO_URL:<window_name>:<url>
Example:
GOTO_URL:profile:info?cookie=abc123&user=toctest2&from=toctest1
Result of a toc_set_dir command.
DIR_STATUS:<return_code>
Return code 0 indicates success.
Result of a toc_format_nickname command.
ADMIN_NICK_STATUS:<return_code>
Return code 0 indicates success.
Example:
ADMIN_NICK_STATUS:0
Result of a toc_change_passwd command.
ADMIN_PASSWD_STATUS:<return_code>
Return code 0 indicates success.
Another user proposes a rendezvous (file transfer, etc.).
RVOUS_PROPOSE:<user>:<uuid>:<cookie>:<seq>:<rvous_ip>:<proposer_ip>:<verified_ip>:<port>[:<tlv_tag>:<tlv_value>...]
| Field | Description |
|---|---|
| user | Screen name of the proposer |
| uuid | Service UUID (e.g. file transfer) |
| cookie | Base64-encoded session cookie |
| seq | Sequence number |
| rvous_ip | Rendezvous IP address |
| proposer_ip | Proposer's IP address |
| verified_ip | Server-verified IP address |
| port | Port number for direct connection |
| tlv pairs | Additional base64-encoded TLV data |
Example:
RVOUS_PROPOSE:TocTest2:09461343-4C7F-11D1-8222-444553540000:fUdgNuQ6AAA=:1:192.168.1.5:192.168.1.5:203.0.113.1:5190:10001:ABIAAgAAAAEA
Server requests the client to pause. A new SIGN_ON will follow when the
server is ready. The buddy list and permit/deny items must be resent,
followed by toc_init_done.
PAUSE
TOC 2 extends the server messages with richer variants. When logged in via
toc2_signon or toc2_login, these messages are sent instead of (or in
addition to) their TOC 1 equivalents.
SIGN_ON:TOC2.0
Server-side buddy list configuration, sent at login. Fields are separated by
linefeeds (\n, ASCII 10). Colons separate sub-fields within each line.
CONFIG2:<config_lines>
Config line types:
| Prefix | Format | Description |
|---|---|---|
g: |
g:<group_name> |
Buddy group |
b: |
b:<screen_name>[:<alias>][:::::note] |
Buddy entry (with optional alias and note) |
d: |
d:<screen_name> |
Deny list entry |
p: |
p:<screen_name> |
Permit list entry |
m: |
m:<mode> |
Privacy mode (1-5, same as toc2_set_pdmode) |
done: |
done: |
End of config |
Example:
CONFIG2:g:Buddies
b:toctest2:Test User
b:joe
d:spammer
m:1
done:
Same as TOC 1:
NICK:<formatted_screen_name>
Incoming instant message (TOC 2 variant). Adds a whisper field.
IM_IN2:<source_user>:<auto_response T/F>:<whisper T/F>:<message>
Example:
IM_IN2:TocTest2:F:F:Hello from TOC2!
Incoming encoded instant message (TOC 2 with toc2_login). Includes user
class and language information.
IM_IN_ENC2:<user>:<auto T/F>:<unknown1>:<unknown2>:<user_class>:<unknown3>:<unknown4>:<language>:<message>
| Field | Description |
|---|---|
| user | Sender's screen name |
| auto | T if auto-response, F otherwise |
| unknown1 | Typically F |
| unknown2 | Typically T |
| user_class | Same as UPDATE_BUDDY user class (e.g. O, OU) |
| unknown3 | Typically F |
| unknown4 | Typically L |
| language | Language code (e.g. en) |
| message | The message content (may contain colons) |
Example:
IM_IN_ENC2:TocTest2:F:F:T: O :F:L:en:Hello from TOC2!
IM_IN_ENC2:TocTest2:T:F:T: OU:F:L:en:I am away right now.
Buddy presence update (TOC 2). Same as UPDATE_BUDDY with a trailing field.
UPDATE_BUDDY2:<screen_name>:<online T/F>:<warning_level>:<signon_time>:<idle_minutes>:<user_class>:<unknown>
The trailing unknown field is typically empty.
Examples:
UPDATE_BUDDY2:TocTest2:T:0:1711036800:0: O :
UPDATE_BUDDY2:TocTest2:F:0:0:0: :
Sent alongside UPDATE_BUDDY2 to describe a buddy's capabilities.
BUDDY_CAPS2:<screen_name>:<cap1>,<cap2>,...
Capabilities are UUIDs (see Capabilities).
Example:
BUDDY_CAPS2:TocTest2:748f2420-6287-11d1-8222-444553540000,09461343-4c7f-11d1-8222-444553540000
Encoded chat message (TOC 2 with toc2_login).
CHAT_IN_ENC:<chat_room_id>:<source_user>:<whisper T/F>:<unknown>:<language>:<message>
The unknown field is typically A.
Example:
CHAT_IN_ENC:0:TocTest2:F:A:en:Hello everyone!
Confirmation after adding a buddy via toc2_new_buddies.
NEW_BUDDY_REPLY2:<screen_name>:<action>
| Action | Description |
|---|---|
added |
Buddy was added successfully |
auth |
ICQ authorization request sent |
Example:
NEW_BUDDY_REPLY2:toctest2:added
Typing notification from another user.
CLIENT_EVENT2:<screen_name>:<typing_status>
| Status | Description |
|---|---|
| 0 | No activity (stopped typing) |
| 1 | Text entered (paused) |
| 2 | Currently typing |
Example:
CLIENT_EVENT2:TocTest2:2
Dynamic buddy list update — items added from another session.
Group added:
INSERTED2:g:<group_name>
Buddy added:
INSERTED2:b:<alias>:<screen_name>:<group>
Deny list entry added:
INSERTED2:d:<screen_name>
Permit list entry added:
INSERTED2:p:<screen_name>
Examples:
INSERTED2:g:New Group
INSERTED2:b:My Friend:toctest2:Buddies
INSERTED2:b::joe:Buddies
INSERTED2:d:spammer
INSERTED2:p:trusteduser
Dynamic buddy list update — items removed from another session.
Group deleted:
DELETED2:g:<group_name>
Buddy deleted:
DELETED2:b:<screen_name>:<group>
Deny list entry removed:
DELETED2:d:<screen_name>
Permit list entry removed:
DELETED2:p:<screen_name>
Examples:
DELETED2:g:Old Group
DELETED2:b:toctest2:Buddies
DELETED2:d:spammer
Buddy icon (Buddy Art) information. Structure is not fully understood.
BART2:<screen_name>:<data>
Errors are sent as ERROR:<code>[:<variable>]. The variable argument (if
present) is typically a screen name or sub-error code.
| Code | Description |
|---|---|
| 901 | $1 not currently available |
| 902 | Warning of $1 not currently available |
| 903 | Message dropped, server speed limit exceeded |
| Code | Description |
|---|---|
| 911 | Error validating input |
| 912 | Invalid account |
| 913 | Error encountered while processing request |
| 914 | Service unavailable |
| Code | Description |
|---|---|
| 950 | Chat in $1 is unavailable |
| Code | Description |
|---|---|
| 960 | Sending messages too fast to $1 |
| 961 | Missed an IM from $1 because it was too big |
| 962 | Missed an IM from $1 because it was sent too fast |
| Code | Description |
|---|---|
| 970 | Failure |
| 971 | Too many matches |
| 972 | Need more qualifiers |
| 973 | Dir service temporarily unavailable |
| 974 | Email lookup restricted |
| 975 | Keyword ignored |
| 976 | No keywords |
| 977 | Language not supported |
| 978 | Country not supported |
| 979 | Failure unknown $1 |
| Code | Description |
|---|---|
| 980 | Incorrect nickname or password |
| 981 | Service temporarily unavailable |
| 982 | Warning level too high to sign on |
| 983 | Connecting/disconnecting too frequently (wait 10 minutes) |
| 989 | Unknown signon error $1 |
Sub-error codes for 989:
| Sub-code | Description |
|---|---|
| 0 | Signed on too soon |
| 7 | Invalid screen name or password |
| 17 | Suspended account |
When sending commands to the server, certain characters must be backslash-escaped whether inside quotes or not:
| Character | Escaped |
|---|---|
\ |
\\ |
$ |
\$ |
" |
\" |
( |
\( |
) |
\) |
{ |
\{ |
} |
\} |
[ |
\[ |
] |
\] |
Example: To send the message He said "hello" (wow) for $5:
toc_send_im toctest2 "He said \"hello\" \(wow\) for \$5"
When sending screen names to the server, normalize them:
Example: Toc Test 1 → toctest1
Server messages are NOT encoded. They use colons as delimiters. When parsing, limit the number of splits to avoid breaking messages that contain colons.
0x00). Include it in the FLAP data length.Capabilities indicate which features a client supports. They are set via
toc_set_caps and reported via BUDDY_CAPS2.
| Name | UUID |
|---|---|
| Voice Chat | 09461341-4C7F-11D1-8222-444553540000 |
| File Send | 09461343-4C7F-11D1-8222-444553540000 |
| Image | 09461345-4C7F-11D1-8222-444553540000 |
| Buddy Icon | 09461346-4C7F-11D1-8222-444553540000 |
| Stocks | 09461347-4C7F-11D1-8222-444553540000 |
| File Get | 09461348-4C7F-11D1-8222-444553540000 |
| Games | 0946134A-4C7F-11D1-8222-444553540000 |
| Send Buddy List | 0946134B-4C7F-11D1-8222-444553540000 |
| AIM/ICQ Interop | 0946134D-4C7F-11D1-8222-444553540000 |
| Chat | 748F2420-6287-11D1-8222-444553540000 |
Some clients send capabilities as short hex codes (1-4 hex digits). These
expand to the full OSCAR capability UUID format:
0946XXYY-4C7F-11D1-8222-444553540000 where XXYY is the short code
zero-padded to 4 hex digits.
Example: Short cap 1343 → 09461343-4C7F-11D1-8222-444553540000 (File Send)
This example shows a complete TOC 1 session: login, add a buddy, send a message, and disconnect.
Wire-level (showing FLAP framing conceptually):
→ TCP connect to server:9898
→ "FLAPON\r\n\r\n"
← [FLAP SIGNON] version=1
→ [FLAP SIGNON] version=1, TLV(1)="toctest1"
→ [FLAP DATA] toc_signon login.oscar.aol.com 5190 toctest1 0x2408105c23001130 english "TIC:Example"
← [FLAP DATA] SIGN_ON:TOC1.0
← [FLAP DATA] CONFIG:m 1\ng Buddies\nb toctest2\n
← [FLAP DATA] NICK:toctest1
→ [FLAP DATA] toc_add_buddy toctest2
→ [FLAP DATA] toc_init_done
← [FLAP DATA] UPDATE_BUDDY:toctest2:T:0:1711036800:0: O
→ [FLAP DATA] toc_send_im toctest2 "Hello!"
→ [FLAP DATA] toc_set_away "Gone fishing"
→ [FLAP SIGNOFF]
→ TCP connect to server:9898
→ "FLAPON\r\n\r\n"
← [FLAP SIGNON] version=1
→ [FLAP SIGNON] version=1, TLV(1)="toctest1"
→ [FLAP DATA] toc2_login login.oscar.aol.com 5190 toctest1 0x2408105c23001130 english "TIC:Example" 160 US "" "" 3 0 30303 -kentucky -utf8 103547776
← [FLAP DATA] SIGN_ON:TOC2.0
← [FLAP DATA] NICK:toctest1
← [FLAP DATA] CONFIG2:g:Buddies\nb:toctest2:Test User\nm:1\ndone:\n
→ [FLAP DATA] toc_init_done
← [FLAP DATA] UPDATE_BUDDY2:toctest2:T:0:1711036800:0: O :
← [FLAP DATA] BUDDY_CAPS2:toctest2:748f2420-6287-11d1-8222-444553540000
→ [FLAP DATA] toc2_new_buddies "{g:Friends\nb:joe\n}"
← [FLAP DATA] NEW_BUDDY_REPLY2:joe:added
→ [FLAP DATA] toc2_send_im_enc toctest2 "F" A en "Hello from TOC2!"
→ [FLAP DATA] toc2_client_event toctest2 2
→ [FLAP DATA] toc2_set_pdmode 1
→ [FLAP SIGNOFF]
→ toc_chat_join 4 "Retro Chat"
← CHAT_JOIN:0:Retro Chat
← CHAT_UPDATE_BUDDY:0:T:toctest1
→ toc_chat_send 0 "Hello room!"
← CHAT_IN:0:toctest1:F:Hello room!
← CHAT_UPDATE_BUDDY:0:T:toctest2
→ toc_chat_invite 0 "Come chat!" joe
→ toc_chat_send 0 "Welcome toctest2!"
← CHAT_IN:0:toctest1:F:Welcome toctest2!
→ toc_chat_leave 0
← CHAT_LEFT:0
A minimal Python client that connects, logs in, sends a message, and reads responses:
import socket
import struct
ROAST = "Tic/Toc"
def roast_password(password):
result = []
for i, ch in enumerate(password):
xored = ord(ch) ^ ord(ROAST[i % len(ROAST)])
result.append(f"{xored:02x}")
return "0x" + "".join(result)
def send_flap(sock, frame_type, seq, payload):
data = payload.encode("ascii") if isinstance(payload, str) else payload
if frame_type == 2:
data += b"\x00" # null-terminate DATA frames
header = struct.pack("!BBHH", 0x2A, frame_type, seq, len(data))
sock.sendall(header + data)
return seq + 1
def recv_flap(sock):
header = b""
while len(header) < 6:
chunk = sock.recv(6 - len(header))
if not chunk:
raise ConnectionError("Connection closed")
header += chunk
marker, frame_type, seq, length = struct.unpack("!BBHH", header)
payload = b""
while len(payload) < length:
chunk = sock.recv(length - len(payload))
if not chunk:
raise ConnectionError("Connection closed")
payload += chunk
return frame_type, seq, payload
def normalize(screen_name):
return screen_name.lower().replace(" ", "")
def escape(text):
for ch in r'\$"(){}[]':
text = text.replace(ch, "\\" + ch)
return text
def main():
HOST = "127.0.0.1"
PORT = 9898
USERNAME = "toctest1"
PASSWORD = "testpass1"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
sock.settimeout(5)
# Step 1: Send FLAPON
sock.sendall(b"FLAPON\r\n\r\n")
# Step 2: Receive server SIGNON frame
frame_type, seq, payload = recv_flap(sock)
assert frame_type == 1, f"Expected SIGNON frame, got {frame_type}"
# Step 3: Send client SIGNON frame
sn = normalize(USERNAME).encode("ascii")
signon_payload = struct.pack("!IHH", 1, 1, len(sn)) + sn
seq_out = send_flap(sock, 1, 0, signon_payload)
# Step 4: Send toc_signon
roasted = roast_password(PASSWORD)
cmd = f'toc_signon login.oscar.aol.com 5190 {normalize(USERNAME)} {roasted} english "TIC:PythonTOC"'
seq_out = send_flap(sock, 2, seq_out, cmd)
# Step 5: Read responses
for _ in range(10):
try:
frame_type, seq, payload = recv_flap(sock)
if frame_type == 2:
print(f"← {payload.decode('ascii', errors='replace')}")
except socket.timeout:
break
# Step 6: Send init_done
seq_out = send_flap(sock, 2, seq_out, "toc_init_done")
# Step 7: Send a message
msg = escape("Hello from Python!")
seq_out = send_flap(sock, 2, seq_out, f'toc_send_im toctest2 "{msg}"')
# Read any remaining responses
for _ in range(10):
try:
frame_type, seq, payload = recv_flap(sock)
if frame_type == 2:
print(f"← {payload.decode('ascii', errors='replace')}")
except socket.timeout:
break
# Disconnect
send_flap(sock, 4, seq_out, b"")
sock.close()
if __name__ == "__main__":
main()