Rewriting pidgin-cyanchat

When I first wrote pidgin-cyanchat over a year ago I had no idea what I was doing. Quite honestly, my knowledge of C was whatever I had picked up reading samples or flying back and forth in IRC conversations. I drew heavily from existing plugins (to the libpurple irc-prpl devs: you are pretty much the only reason this thing works).

I’ve fixed bugs here and there, added a few new features… but I wasn’t really happy with the overall design of the plugin. There were a few inherent issues, and as I was using the IRC plugin as my base, these issues are mainly due to differences between CyanChat and IRC:

  1. CyanChat has only a single room/channel
  2. CyanChat requires that you connect to the server, and then join the room/login… you cannot have PM conversations without joining the chatroom
  3. CyanChat has no user auth, no way to verify that a user is who they claim to be

In the existing implementation, I worked around those issues in a variety of ways, some of which weren’t even fully supported by Pidgin.

  1. I manually add a chatroom entry to the Pidgin buddylist when a CyanChat account is enabled, and manually remove it when the account is disabled. This prevents the user from setting a “friendly name” alias on the chatroom, and prevents it being moved into a group of the user’s choice.
  2. Essentially, the code is just full of checks to see if we are logged into the room before allowing PMs to be sent. It’s quite possible to type a number of lines to a user, and have them not sent due to not being logged in.
  3. I copied from the Bonjour plugin here (which as dynamic buddies across a LAN), and removed the add_buddy function to simply not add a buddy. However, Pidgin still gave me an “Add Buddy” option on all chatroom users. There is an existing bug report for this issue, with an expected resolution in Pidgin 3.0.0.

Wanting to do better, I started this week to rewrite libcyanchat. I split it into multiple files to make editing less of a nightmare, and I actually used comments this time :P
My new implementation is quite different than the existing one. The biggest difference is that one account can connect to multiple servers.
I decided to extend things a bit by making each CyanChat server act as a chatroom in Pidgin. So you create a CyanChat Account, and use the “Join a Chat” menu to specify a server, port, and nickname. A chat opens, which can be added to the buddylist the same way as an IRC channel, or a Jabber MUC. It’s a very nice, clean, easy system… until you remember that CyanChat supports PMs.

Now, a bit of background on the CC protocol… users are sent to the client in the following form
[1 digit user level][nickname],[IP address hash]
Although the server is supposed to prevent users with the same name, the user levels and IP hashes make it relatively simple to identify users. Each user is unique… until you add multiple servers. While there are only two official servers, they use the same IP hashing algorithm, whereas 3rd party servers use a different hashing method. The problem here is that if Bob is logged in on both official servers and I’m having a PM conversation with him, it would be impossible for Pidgin to tell which server I was using to chat with him, because the user ids on both servers would be identical.

So I devised a solution to ensure unique usernames… After the IP hash, add @server:port (thanks Jabber!). That would create a full user id of
[nickname],[IP address hash]@[server name]:[server port]
This works quite nicely for nearly everything. Yes, nearly… because now in a PM conversation with Bob, I get a huge behemoth complicated username sending me chat, rather than simply the nickname. This isn’t a problem in the chatroom, only in PM conversations.
Edit: The user level should not factor in as it would mean messages from ChatServer are spread across two windows due to varying user levels.

So how does on fix that issue? Well, one can’t. Pidgin (libpurple) doesn’t seem to have any way to alias a user who isn’t on the buddylist. So I have no way to simplify the display of Bob’s full id into the simple nick of “Bob”.
I would definitely appreciate feedback on this, especially if someone knows of a workaround for this issue. The biggest problem is that I don’t see any existing protocols which would need to do something like this, and hence I have no examples from which to draw ideas.

Advertisements

CyanChat 1.4 for Pidgin

Haven’t worked on this for a while, and since it’s mostly complete I hadn’t made any large changes. However, there are always the small annoying glitches that you keep promising to fix and never actually do. Hence, we have a new release of pidgin-cyanchat! :D

Major new features include:

  • /rot13 command to “encrypt” chat
  • !version now reports “libpurple” rather than “Pidgin”
  • Fixes for pipe characters in messages
  • Fixes for > and < in chat messages
  • The default port has been changed to the main server (1812)

The full changelog is available at Google Code.

A huge thanks to Chucker for working on an Adium extension using this plugin, and thanks to vaaht for testing and reporting the !version issue.

pidgin-cyanchat: Go forth and download. (Windows installer, and Debian installer for 32 and 64 bit)

pidgin-cyanchat v1.0

Today I’ve finally gotten the plugin to a point where I’m fairly happy with the result. It works well in Pidgin, it also works well over SSH with Finch. Chatting, PM’ing, and logging in and out are all fully supported (as far as I can tell).

The biggest issue in getting it done was the userlist handling. My original concept was “Cho sends us a userlist, we clear the chat users and fill it in again”. It worked well enough in Pidgin. In Finch however, the entire userlist was sent to the chat every time that it was updated. Considering that one person logging on generates two userlist refreshes, it got pretty annoying and made conversation difficult.
My new concept used a diff method, and also tried to guess when users changed their names. CyanChat doesn’t have an actual rename command, so it displays it as a new link in… The headaches and random bugs that came along with guessing at renaming users were quite fun, and quite annoying to debug. With Nadnerb’s help, I finally found a better method of handling renames and implemented it.

So, currently there is a source build, and a build for Ubuntu i386. I’m working on getting a good method to cross compile for Windows and x64.

pidgin-cyanchat homepage

Oh, did I mention that Pidgin 2.5.0 is out? Finally got some good MSN support (albeit somewhat buggy and crash-prone) :P

CyanChat + Pidgin

Ever since I started using Pidgin, I’ve dreamed of having all of my chat protocols working flawlessly in a single program. Recently, I’ve found a Skype for Pidgin plugin, and a Facebook Chat for Pidgin plugin. With those as inspiration, I finally started on my CyanChat plugin for Pidgin.

I have a bit of a history with the CyanChat protocol. I wrote a server and client in C# a year or so ago. Those came to a conclusion when I started using Linux more and couldn’t run the C# programs. LordAndy had started a project to rewrite the popular Magenta client using cross-platform wxWidgets code, and I used and added to wxMagenta. I’ve also written my own enhanced protocol, and started various projects involving that new specification.


CyanChat for Pidgin has support for signing in, joining the chat room, and sending/receiving chat. I tend to commit often (usually so that I have something to fall back upon if I break it), so new features should be fairly easy to access.

Also, if you are a Mac or Window person, and feel like compiling it, please send me an email. I’m fairly sure that it should work on Windows, but I’m curious as to whether it would support Adium. I also have no way of compiling for either of those architectures, so any offers are welcome.