Introduction to Icecap protocol
Here's an example how connecting to IRC gateway looks like (wrapped for readability):
C: 4;presence connect;network=ircnet;presence=mynick S: *;gateway_connect_failed;id=8;time=1115157174;network=ircnet;presence=mynick ;ip=2001:123:0:2::1;port=6667;error=Network is unreachable S: *;gateway_connecting;id=9;time=1115157174;network=ircnet;presence=mynick ;ip=127.0.0.1;port=6667 S: 4;+
A full example to say "hello world" in #irssi2 is here.
The protocol is line based and parameters are separated by ';' character. If ';' is wanted to be used, it must be escaped as "\." string. '\' must be escaped as "\\". "\r" and "\n" can also be used for CR and LF characters. NUL character isn't allowed anywhere.
Note that the ';' separator really is escaped as "\." and not "\;". This way it's easier to split the parameters because ';' is always guaranteed to be a separator. Also some special clients which don't really care about escaping can ignore it when they're looking for some specific parameters.
The protocol by design is asynchronous. That means that a command may be sent to server and before it's finished server may have sent replies to previous commands or sent events unilaterally.
The command that client sends starts with a "tag" (similar to IMAP's tags). The tag can be pretty much any string except "*". It's not required to be unique, but clients typically would want to do that. After the command is finished, server sends back the tag and indicator of whether the command finished successfully or not.
The next parameter in command line is the actual command name. If the command has some kind of hierarchy, the words are typically separated by spaces (just a convention I like).
The rest of the parameters are command-specific parameters.
Command replies start with the tag that the command was started with.
If the second field is "+", it means the command finished successfully. The tag won't be sent anymore again so client can reuse it if it wants to. After "+" parameter may come more command reply-specific parameters.
If the second field is "-", it means the command failed. The third field contains unique error identifier. After that there may be more optional fields, such as human readable error message (not decided yet).
If the second field is ">", it means the command execution has been successful so far, but more lines are coming before this command reply is finished.
If the second field is anything else, it means a protocol error which should never happen.
Events start with a "*" field. The second field is the event name. Typically it contains underscores instead of spaces, mostly because the events may be sent outside Icecap to many different services and languages and spaces might cause problems. Rest of the fields are parameters.
Parameters can be either static or dynamic. Static parameters never change so they can be safely cached by clients if they so wish, but dynamic parameters might be added, changed or removed at any time. Dynamic parameters begin with a $ character. They can be created either dynamically on-the-fly by the Icecap server, or clients can also set them for old events. Typical dynamic events would be "$user" and "$highlight". Currently they aren't implemented.
All events have "id" parameter which contains unique 64bit numeric ID for the event. The ID is always ascending. Other Icecap servers can also be connected to one Icecap instance. This means that their IDs will conflict. There will probably be "ID namespace" parameter to separate them from each others. Luckily clients should normally never care about event IDs.
All events also have "time" parameter containing timestamp when the event was created. Note that by requesting old events the timestamp contains the original timestamp, not the time when the event was requested.
Other parameters are event-specific.
Networks and presences
See irssi2-intro.txt if you don't understand what they mean.
Each gateway connection is uniquely identified by network/presence pair, so many commands and events contain them. Sometimes it's possible to give just the network. In that case Icecap server automatically decides what presence to use if there are multiple.
Channels can be created without joining them. This is useful for example if you want to join some channel once in a while and you want to have some settings for the channel (eg. charset). If channel has autojoin-parameter set, it's joined after its gateway is connected. Autojoin-parameter is automatically updated whenever channel is manually joined, and removed when channel is manually left or you get kicked out.
Channels can be removed, but normally this probably shouldn't be done. Having a list of all channels ever joined makes it easy to browse their old logs. Without such a list it would be difficult to find out what channels have exited.
Channels and users could be added to groups. Each group as a numeric ID, name and parent group (for showing them hierarchically). Groups can be added, removed and renamed. Each channel and user can belong to multiple groups. The group is specified with group=1,2,5 parameter.
Groups will be synchronized with chat protocols' own group lists. If groups are added by remote server, they're automatically picked up. If they're removed or changed a lot, user should probably be asked if he actually wants those changes to reflect Icecap's group list.
Groups aren't currently implemented.
Clients can see also the raw IRC events that gateways receive. These should not be used unless you're really wanting to deal with some IRC-specific things. Typically these would only be used for rawlog-like feature for debugging.
The event names are "irc_event" and "irc_event_error". irc_event_error is sent for 4xx and 5xx numeric replies which are typically reserved for errors.
First send information about file name and size:
C: 1;file send;resume;name=foo.txt;size=1234567 S: 1;+;handle=0;start=0;end=65536
Server requested you to send next 65536 bytes. Do it:
C: +65536;2;file send;handle=0 C: <send 65536 bytes here> S: 2;+;handle=0;start=65536;end=65536
Continue this, until server replies with:
The file transfer is complete now.
To actually support resuming existing files, you have to handle checksum parameter. Currently the checksum is calculated using SHA1. If the checksum doesn't match in given range, send it like above. If it does match, just don't send the bytes:
S: 1;+;handle=0;start=0;end=65536;checksum=sha1:123456.. C: 1;file send;handle=0
Requesting old events
C: 5;event get;end=*;limit=100;filter=&(channel=#icecap)(mypresence=myname)(network=Freenode)(time>1)
Requests last 100 events for #icecap channel, made after unixtime 1 The filter is similar to LDAP, ie. you can do &(cond1)(cond2)(cond3) etc. to have require all 3 conditions to match. Similarly you can do |(cond1)(cond2) to require either cond1 or cond2 to match. These can be combined like &(cond1)(|(cond2a)(cond2b)). You have to specify a start time, or this will fail.
You can also specify start/end ID ranges where to look for the events, but this probably will change at least some because of ID namespaces to support multiple servers (see events section above).
Server-side windows are mostly intended for clients to synchronize their "window activity" displaying. Windows have an event filter string which uniquely identifies them. For identifying windows in shorter form, they also have ID numbers which server generates. Windows also have a last_seen_event_id property, which is used to track the window activity.
- Whenever a window is switched in client, "window set" command should be sent to server with last_seen_event_id parameter set to the ID of the last printed event.
- This should be done for both the old and the new window
- This shouldn't be done if last_seen_event_id is already greater or equal than the last printed event ID.
- Whenever client receives "window changed" event with last_seen_event_id parameter set, the client should check if the window's activity status needs to be changed. If last_seen_event_id is greater or equal than the ID of the event that was last printed in the window, the window's activity status is now "seen". Otherwise it contains unseen messages.
- Clients often show activity in different colors. The color could be different depending on if it has new messages, or eg. joins or parts. Client can figure out these by itself by looking at the events that have occurred since last_seen_event_id.
- At startup the windows are created with "window add" command. As a reply to it, last_seen_event_id parameter is returned. The window activity should again be updated just as if "window changed" event had been received. Note that the window's scrollback needs to be read before this can be done.
Some protocols require user to reply to some question. Such as "foobar wants to add you to his buddy list. Do you allow that?". Besides sending these as raw events for clients that understand such and display them in some really pretty way, this could also be handled in a generic way by making server ask this from clients in a generic way.
Question is sent to all connected clients. The server accepts the first reply it gets as the answer. The other clients are then sent an dialog_abort event so they can remove the dialog from screen. Dialogs have a per-client ascending ID identifying it.
Besides using dialogs for questions, the server can also use them for notifying user of some things, such as "You're running out of disk space" with just an "OK" button.
The actual protocol for doing this isn't yet implemented/defined.
- connect to Icecap server
- see if it has already connected to gateways ("presence list")
- create windows for all existing channels ("channel list")
- show nicklist for the channels ("channel names")
- show a screenful of latest channel events for each channel
- all done, wait for user input
- see if any networks have already been defined in server ("network list")
no --> start some configuration dialog for network configuration
- show a list of networks and let user connect to one or more of them
- if presence isn't found for the network, create it automatically
- using some default nick
- note that user should be able to create multiple presences
- (connections) for a single network
- if presence isn't found for the network, create it automatically
- all done, wait for user input
Remote authentication using TLS+SASL, something like:
S: *;capa;stream=tls;stream=zlib;auth=digest-md5 C: 1;starttls S: 1;+ <tls negotiation> C: 2;auth;plain;abcdefghijklmn S: 2;+
Some kind of filtering for what server sends us:
C: 3;match;filter=&(channel=#icecap)(network=ircnet)(event=join,part,msg);send S: 3;+
Highlighing / adding whatever extra parameters to events:
C: 4;match permanent;filter=&(event=msg)(data~/irssi[^i]/i);add=highlight S: 4;+ S: *;msg;channel=#icecap;from=someone;highlight;msg=i've a highlight parameter