MozillaZine

The IM Project Page Has Vanished

Wednesday April 21st, 1999

The IM Project page has been taken down at the request of Netscape. Netscape was the contributor of the Instant Messaging API document, and they have taken it down pending further review. As far as we can tell, there is nothing more to the story at this point. We'll let you know when the page comes back online.


#13 Re:The IM Project Page Has Vanished

by Strangelove

Wednesday April 21st, 1999 3:29 PM

You are replying to this message

IM-API -- Instant Messaging API

Terry Weissman Travis Bogard Sudharshan Srinivasan

Goals

One of the fundamental strengths of the browser has always been the ability to hide the messy details of diverse network protocols under a simple user interface. The same client can go talk to servers speaking HTTP, FTP, Gopher, etc., and provide a fairly consistent interface to them all.

We would like to make Mozilla be able to do chatting and "instant messaging". However, we don't want to limit ourselves to a single server, or a single protocol. People today are chatting using a variety of different protocols and servers. We would like Mozilla to be able to usefully talk to all of these protocols, and hide most of the differences from the user.

On the other hand, we also don't want to hard-code knowledge of every known chat protocol into the code base. We would like to have a "pluggable" architecture, where it would be possible at any time to write up a new module for a new protocol, and Mozilla would just work with it.

IM-API defines the API for people wanting to make Mozilla speak a new chat protocol.

The big picture

The whole IM/Chat module will be an separate module from the rest of the Mozilla client; it will be possible to build the client without any IM/Chat support at all.

The IM/Chat module will include lots of UI code and stuff. But to actually speak a chat protocol, it will look in the registry for classes that provide the nsIMServer interface. Each of those classes represent a different protocol. When the client wishes to connect to a chat protocol, it will create an instance of the appropriate nsIMServer object, connect() to the service, and start calling getService() to get the Service objects it needs. The client will implement the corresponding Listener interfaces defined below for any Service objects it needs.

So, for a new protocol, you need to define which of the below services make sense for your protocol. You will define an object that implements nsIMServer, and make its GetService() method return objects you define for the services you support. You do not have to define any of the Listener classes; those are generally defined by the client.

As things get further flushed out, and actual UI code gets added, we plan to add stuff so that a particular protocol can add things to the UI to support protocol-specific stuff. The additional UI will probably end up calling additional interfaces provided by the protocol. We do not expect that all calls to the protocol module will go through this IM-API, but we do hope that most of them do.

The API

Random notes about the API:

Any interface whose name ends with "Listener" is generally implemented by the main client code, not by a protocol module. The stuff in IM_APIExt.h is optional. This is the place for features that some implementations of a service will have, and some will not. The user of a service will be able to QueryInterface to determine if a given feature is supported.

Details about this API that I know are wrong and need to be fixed:

I need to #define the NS_IIM* constants properly. Where do these magic numbers come from? Do I need to port this to IDL? I don't talk about how objects get constructed. Factories, mumble, mumble. The unpickle() calls are pretty constructor-ish, though. The names are all currently prefixed with the string "nsIM". I'm not sure if that's a sufficiently unique prefix. There is no RDF in here. I think it likely that much of the pickle/unpickle awkwardness may get replaced with something RDFish. Appropriate experts need to be consulted. Some other questionable things are called out in green italic text. Everywhere we pass HTML text, we might also need to pass some other info (like, maybe, encoding information for proper i18n support)?

************************ IM_APIBase.h ************************

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at <http://www.mozilla.org/MPL/>

Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.

The Original Code is IM-API.

The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1999 Netscape Communications Corporation. All Rights Reserved.

Contributor(s): */

// This file defines the core IM_API functionality: the main services // that a protocol module is likely to want to implement, and the // associated Listener and other interfaces.

// This lists the different availability states a person can be in. // I'm not yet certain if this is the appropriate // generic list.

enum nsIMAvailability { Present, // The person is alive and on-line Gone, // The person is not connected Away, // The person is on-line but is not at his // computer. Unknown, // The protocol is not actively tracking the // presence of this user, and so his state // is not known. };

// The nsIMServer interface represents the main connection to a server. // One instantiation of it will be created for each connection to a // chat server. Thus, if the user wishes to speak to 3 different IRC // servers, there will be three instances of the IRC implementation of // nsIMServer. // // The nsIMServer interface only deals with things having to do with the entire // server connection; basically, whether it is actually connected or not. To // actually do something using the server, you must use the getService() method // to get a service object for the service you want.

class nsIIMServer : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMSERVEROBJECT_IID)

// setServerListener() sets the object this server should use to // report asynchronous events and messages. PRStatus getServerListener(nsIIMServerListener*);

// getServerListener() returns the listener object. nsIIMServerListener* getServerListener();

// getShortDescription() returns a short name for this server, suitable for // use in a list of server names. If you can have multiple instantiations // of the same kind of server (for example, to do multiple IRC hosts, or to // allow people to have multiple AIM screennames for themselves), then the // string for each instantiation ought to be unique. const char* getShortDescription();

// configure() causes this server instantiation to pop up a dialog box // allowing the user to configure all options about the server in general // (hostname, port number, screenname, password, etc.) // It is undefined whether this will return immediately or wait until the // user has finished the operation. PRStatus configure();

// connect() causes a connection to be made to the server. This call // blocks until a connection is firmly established. PRStatus connect();

// disconnect() causes any connection with the server to be dropped. // This call blocks until the connection has been dropped. PRStatus disconnect();

// isConnected() returns whether there is a current connection to the // server. PRBool isConnected()

// GetService() returns a pointer to the object that implements the // given service (which should be on of NS_IIMIMSERVICE_IID, // NS_IIMCHATSERVICE_IID, NS_IIMPRESENCESERVICE_IID, etc.) If the // given service can be found, the pointer is set; if the service // is not supported, an error code is returned and the pointer is // set to NULL.

NS_IMETHOD GetService(REFNSIID iid, void** instancePtr); };

// The nsIIMServerListener interface gets called with asynchronous events that // relate to the whole server (not particular to any service within it).

class nsIIMServerListener : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMSERVERLISTENER_IID)

// If we ever lose the connection to the server, then this function will // be called. "why" is an error message that can be displayed to the user, // explaining what happened. Is passing this as a // string the right I18N thing to do here? PRStatus lostConnection(const char* why); };

// The nsIIMIMService interface (and I really *hate* that name, but // everything else is either too long or too inconsistant with other // names here) provides the instant messaging service.

class nsIIMIMService : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMIMSERVICE_IID)

// setIMListener() sets the object this server should use to // report asynchronous events and messages. PRStatus setIMListener(nsIIMIMListener*);

// getIMListener() returns the listener object. nsIIMIMListener* getIMListener();

// sendMessage() causes an instant message to be delievered to the given // user. This call blocks until the message has been delivered. PRStatus sendMessage(nsIIMIndividual* dest, const char* html); };

// The nsIIMIMListener interface gets called with asynchronous events that // relate to the instant messaging service.

class nsIIMIMListener : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMIMLISTENER_IID)

// receiveMessage() gets called whenever someone out on the network sends // us an instant message. PRStatus receiveMessage(nsIIMIndividual* source, const char* html); };

// The nsIIMChatService interface provides support for the chat service.

class nsIIMChatService : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMCHATSERVICE_IID)

// setChatListener() sets the object this server should use to // report asynchronous events and messages. PRStatus setChatListener(nsIIMChatListener*);

// getChatListener() returns the listener object. nsIIMChatListener* getChatListener();

// listKnownRooms() returns a list of room names that are known to exist // with this chat service. It does not necessarily return every room that // is actually out there, just the ones that are currently known to exist. // This ListOfStrings return type is just a // placeholder; I hope such a class does exist. ListOfStrings* listKnownRooms();

// getRoom() returns the room object corresponding to the given name, or // NULL if no such room exists and can't it can't created. nsIIMChatRoom* getRoom(const char* name); };

// The nsIIMChatListener interface gets called with asynchronous events that // relate to the chat service.

class nsIIMChatListener : public nsISupports { NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMCHATLISTENER_IID)

// roomChanged() gets called whenever a room appears or disappears from // the list of known rooms. It is legal for listKnownRooms() to return // a different list on two successive calls without any intervening calls // to roomChanged() happening; that is, some protocols may only support // polling on the list of rooms and will not provide asynchronous // updates. PRStatus roomChanged(const char* name, PRBool added // True if added, False if removed. );

// receiveInvitation() gets called when the user has received an invitation // to join a room. PRStatus receiveInvitation(nsIIMIndividual* who, // Who sent the invitation const char* roomname, // What room we're // invited to const char* extratext // Any extra text to // display as part of the // invitation. This is // HTML. ); };

// The nsIIMPresenceService intervening provides support for the presence // indication service, the service that lets you track a list of individuals // on the network and report whether they are currently on-line.

class nsIIMPresenceService : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMPRESENCESERVICE_IID)

// setPresenceListener() sets the object this server should use to // report asynchronous events and messages. PRStatus setPresenceListener(nsIIMPresenceListener*);

// getPresenceListener() returns the listener object. nsIIMPresenceListener* getPresenceListener();

// addUser() causes us to start watching for presence of the given user. PRStatus addUser(nsIIMIndividual*);

// removeUser() causes us to stop watching for the given user. PRStatus removeUser(nsIIMIndividual*);

// removeAll() clears the list of all users that we are watching. PRStatus removeAll();

// getWatchList() returns the list of all users that we are watching. // The nsIIMIndividualList class is a placeholder; // I hope we have a standard easy way of doing this. nsIIMIndividualList* getWatchList(); };

// The nsIIMPresenceListener interface gets called with asynchronous events // that relate to the presence service.

class nsIIMPresenceListener : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMPRESENCELISTENER_IID)

// userAvailabilityChange() gets called whenever the given user goes // online, offline, or otherwise changes.

PRStatus userAvailabilityChange(nsIIMIndividual* who); };

// The nsIIMIndividual interface represents a person out on the network.

class nsIIMIndividual : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMINDIVIDUAL_IID)

// getAvailability() returns the current availability information for // the given user. If block is True, then the caller is willing to wait // in order to get the most accurate information; if False, then the // call should return immediately (which may very well lead to a result // of Unknown.) Actually, the caller should always be ready for a result // of Unknown; some protocols may not be able to determine availability // in some circumstances. nsIMAvailability getAvailability(PRBool block);

// getIDString() returns a short identifying string used by the protocol // to identify this person. Note that some protocols may change this // string over time; it is best for callers to remember people by their // pointer, not by their idstring. const char* getIDString(); };

// The nsIIMChatRoom interface represents a chat room. This acts much like // a service, in that it has a corresponding listening class. However, unlike // most services, there can be multiple instantiations of this for a single // protocol, one per chat room that we are connected to. The existance of // a nsIIMChatRoom object implies that the user is connected to it; to // disconnect from a chat room, the client should drop its references to // the nsIIMChatRoom object.

class nsIIMChatRoom : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMCHATROOM_IID)

// setChatRoomListener() sets the object this server should use to // report asynchronous events and messages. PRStatus setChatRoomListener(nsIIMChatRoomListener*);

// getChatRoomListener() returns the listener object. nsIIMChatRoomListener* getChatRoomListener();

// getDisplayName() returns a brief name for this room. const char* getDisplayName();

// getDescription() returns a description or "topic" for this room. const char* getDescription();

// sendMessage() sends a message to the chat room. It is assumed that the // listener's receiveMessage() will get called soon thereafter with this // same message. If that doesn't happen as a side effect of the network // protocol, then the protocol module must call receiveMessage() itself. PRStatus sendMessage(char* html);

// sendInvitation() invites someone to join this chat room. extratext is // extra HTML text to explain the invitation. If the underlying protocol // doesn't support extra text with an invitation, then it should try to // get that text to the receiving user somehow (i.e., send an instant // message with that text just ahead of the invitation). PRStatus sendInvitation(nsIIMIndividual* who, const char* extratext);

// listMembers() lists who is currently connected to this room. nsIIMIndividualList* listMembers(); };

// The nsIIMChatRoomListener interface gets calle with asynchronous events // that relate to a specific chat room.

class nsIIMChatRoomListener : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMCHATROOMLISTENER_IID)

// receiveMessage() gets called whenever a person sends a message to the // chat room.

PRStatus receiveMessage(nsIIMIndividualList* who, const char* html);

// memberChange() gets called whenever someone leaves or enters the room. PRStatus memberChange(nsIIMIndividual* who, PRBool added // True if entered, False if left ); };

************************ IM_APIExt.h ************************

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at <http://www.mozilla.org/MPL/>

Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.

The Original Code is IM-API.

The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1999 Netscape Communications Corporation. All Rights Reserved.

Contributor(s): */

// This file defines strictly optional interfaces for features that // are known to be supported only by some protocols.

// ########## EMOTE SUPPORT Some protocols allow for messages that are // "emoted". ("emoting" refers to the ability to express emotions instead of // just sending text -- the ability to say in a chat room "terry feels sick", // rather than having to do "terry: I feel sick".) If a protocol supports // emoting in instant messages, then the objects supporting the nsIIMIMService // and nsIIMIMListener interfaces need to also support the nsIIMIMEmoteService // and nsIIMIMEmoteListener interfaces, respectively. Similarly, if the // protocol supports emoting in chat messages, then the objects supporting the // nsIIMChatRoom and nsIIMChatRoomListener interfaces need to also support // the nsIIMChatRoomEmote and nsIIMChatRoomEmoteListener interfaces, // respectively. // // This is an annoying number of extra interfaces to support a simple thing // like "emote". However, we don't have a better idea. It does give the // most flexibility, and makes things simple for the simpler protocols that // do not do emote.

class nsIIMIMEmoteService : public nsISupports { NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMIMEMOTESERVICE_IID)

// sendEmote() is just like sendMessage() in nsIIMIMService, but the // message is sent emote-style. PRStatus sendEmote(nsIIMIndividual* dest, const char* html); };

class nsIIMIMEmoteListener : public nsISupports { NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMIMEMOTELISTENER_IID)

// receiveEmote() is just like receiveMessage() in nsIIMIMListener, but // the message is sent emote-style.

PRStatus receiveEmote(nsIIMIMListener* source, const char* html); };

class nsIIMChatRoomEmote : public nsISupports { NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMCHATROOMEMOTE_IID)

// sendEmote() is just like sendMessage() in nsIIMChatRoom, but the // message is sent emote-style. PRStatus sendEmote(const char* html); };

class nsIIMChatRoomEmoteListener : public nsISupports { NS_DEFINE_STATIC_IID_ACCESSOR(NS_IIMCHATROOMEMOTELISTENER_IID)

// receiveEmote() is just like receiveMessage() in nsIIMChatRoomListener, // but the message is sent emote-style.

PRStatus receiveEmote(nsIIMIMListener* who, const char* html); };