OkOb.net | ||||||||
Registration | Projects |
RepRap X2V3 | RepRap X2 | Virtual Display Image Viewer for PDA | XUL Jabber Client | Voice Sensitive Screen Saver |
XUL Jabber Client Development
Author: Denis Bakin
December 5, 2002
CONTENTS
Jabber Client Operation and Execution Flow
XML debug log for signing on with server test.net of new user test3@test.net
XML debug log for signing on with server test.net of existent user test@test.net
XML debug log for adding user den@test.net to the roster of test1@test.net
XML debug log for receiving a subscription request from user denis@test.net
XML debug log for sending message to user den@test.net
XML debug log for receiving a message from user denis@test.net
Jabber Client Directory/File Structure
Application startup code (start_me.html)
Jabber daemon for MS Windows installation instructions
Jabber is an open protocol for the instant message exchange. It offers functionality similar to MSN, ICQ, IRC and many other proprietary instant messaging systems provide. The advantages of the Jabber protocol are:
A detailed explanation of some of these advantages is worth mentioning.
The gateways to the existent instant messaging systems should provide interoperability and smooth transition to Jabber for users of the other similar services. For example, to send a message to an ICQ address trough the jabber.org server, a Jabber user has to have an existent ICQ account, register it with the Jabber ICQ agent, and send the message to the address formed of a destination ICQ ID concatenated with “@icq.jabber.org”. It is simple like that. Another important advantage of the Jabber protocol is its flexibility. The great level of it is reached by using of XML (Extensible Markup Language) namespace. For example, an instant message generated by a client is passed to a server and then further to the destination wrapped into XML (see appendix A):
<message
id=""
to="den@test.net"><subject>SubjectLine</subject><body>MessageBody</body></message>
The only information the server needs to know to relay the message is in the “message” tag. Extra information can be passed along using the extension tag with a unique custom namespace identifier:
<message
id=""
to="den@test.net"><subject>SubjectLine</subject><body>MessageBody</body><x xmlns='jabber:x:say'><phrase>Say
it if you can</phrase></x></message>
The new tag may be recognized and processed on the receiving
end, or it can be ignored in case the receiving client does not support the
functionality required to process the extension.
For more details about the Jabber protocol please refer to
[4].
XUL (XML-based User Interface Language) is a part of Mozilla open source project launched by Netscape. It is a result of an attempt to create a standard platform independent user interface description language. Mozilla itself is an operation environment for the platform independent applications. The standard Mozilla distribution includes a number of the built in multiplatform applications such as an Internet browser, a mail and a chat client. The Mozilla built in multiplatform applications are written and operate using the same means as the ones provided for all other applications. These means are XUL, JavaScript, CSS, XBL and XPCOM. The XUL here is a skeleton of an application, the JavaScript is its brains and the CSS is its decorations. The other components provide some flexibility such as dynamic style changes, custom widgets development and platform dependent code extensions.
A platform independent code running under Mozilla can be either distributed as a package to be installed locally on a user’s workstation or as a network application downloadable on demand. The installable packages have no limits on local resources usage and can be even extended to execute a platform dependent code. The network applications are designed to work in the boundaries of security restrictions. At the same time, the network applications do not require disk space, can be executed on any computer running Mozilla just by referencing a URL and can be upgraded with no end user intervention. The Jabber Client has been developed as a Mozilla network application.
Currently, the latest version of the Mozilla browser is 1.2a Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.2a) Gecko/20020910. This is the Mozilla version the Jabber Client has been developed and tested for. The latest Muse Java API (version 0.8a1) has been used to provide a set of methods to perform conventional Jabber Client functions like signing in with a server and sending/receiving messages. The API hides low level details and takes care of chores like connection management, XML messages wrapping and unwrapping. A nice short Muse API overview with examples is presented in [5].
The applet class ‘jcApplet’ has been created to serve as the Jabber Client application main thread and to allow communication and data exchange between JavaScript UI routines and the Muse API. The applet has been developed and tested to work with the Muse API and the JavaScript routines under the Java Development Kit 1.3.1_05, therefore the JRE (Java Runtime Environment) of version 1.3.1_05 is suggested for executing the developed application. Conceptually the components of the Jabber Client application communicate as described on the fig. 1.
The JRE security does not allow network applications to communicate to servers other than the one the application is downloaded from. This imposes a requirement on the Jabber Client application to be either installed locally or to be downloaded from the Jabber server it is going to connect to, in which case it still fails doing a name resolution. In addition to that, the applet using Muse API has to be provided read access to XML parser properties. The easiest ways to solve the security issue is to either modify the set of permissions for the jcApplet class or sign the applet with a certificate from a trusted certificate authority. The latter is rather expansive, so here is an explanation of how to use the first approach:
// Jabber Client gets permissions to connect
resoveand read SAXParser properties
Note, that these changes affect all users of your system
using the same JRE (Java Runtime Environment). If you’d like to modify
permissions for individual user account only, try to put these lines to the .java.policy file in the user home
directory. It can be quite easily done with help of Java policytool program included in standard set of the JRE executables.
In order to prove the concept placed to the basement of the Jabber Clint application development, the described application structure has been implemented to perform a simple action of signing in with the Jabber server ‘test.net’ and sending a message to the user test@test.net. The application files were placed in a directory shared by MS IIS server and opened with the Mozilla browser by accessing a URL pointing to the start_me.html file. In order for the Mozilla browser to recognize an XUL application, the IIS was configured to supply the application/vnd.mozilla.xul+xml MIME type for XUL files transferred from the server.
1. https://www.okob.net/jabberc/start_me.html
It contains the onload even handler that start the XUL application.
<!-- This will open Jabber Client
window and disappear leaving the client
window intact. --> <HTML> |
2. https://www.okob.net/jabberc/jabber.xul
It contains XUL description of a window with two buttons.
One is to test the LiveConnect operation (“Say Hello”), and another one is to
test the Muse API operation (“Test Muse”).
<?xml version="1.0"?> <script> <hbox> |
3. https://www.okob.net/jabberc/jcApplet.java
This Java code has to be compiled to the jcApplet.class
file.
/*
This is applet providing communication for java script to import java.awt.*; public
class jcApplet extends Applet public void sayHello() public void tryJabberApi() catch (Exception e) return; |
Developing network
applications puts lots of restrictions on the applications and their UI design.
Some of the restrictions encountered during the Jabber Client development were:
In order to avoid
issues related to the mentioned above restrictions the Jabber client was
developed as a one-window application. Tabs were used instead of the menu and
provided functionality similar to the one that can be achieved by having
multiple windows and dialog boxes. Only basic Mozilla features were used. The Jabber client
application provides the following functionality: To perform each of
the mentioned operations the client has a separate tab panel.
Fig. 1
Login/Logout tab The fig.1 shows user
login/logout tab. User has to enter his/her Jabber ID, specify resource if
desired, type in password and either press Enter or click “LogIn” button to log
in. The Roster and Messages tabs will appear, LogIn button will be disabled,
LogOut button will be enabled and status string will change if operation
completed successfully (fig.2). Otherwise an alert box identifying error will
be displayed.
Fig. 2
Login/Logout tab after logging in The fig.3 shows Account
tab. The tab’s outlook changes depending on either a user is
logged in or not. Before user logs in the tab can be used to create a new
account. After logging in it can be used only to change current user password.
The fig.3 shows the tab before and after logging in. The roster provides
Jabber users with functionality similar to an address book. The roster contains
JIDs (Jabber Identifiers) of people user communicates to. The information is
stored on the Jabber server and it is retrieved by the Jabber client after a
successful login. The roster functionality is tightly bound to functionality
called presence. The latter allows a user to subscribe with Jabber server to
receive presence notifications for some JIDs. The result of such subscription
would be exchange of subscriber and subscribed users with their JIDs and
placing them into each other’s roster (after the subscription is granted). The
user subscribed to receive presence notifications of another user receives
messages informing either the subject of the subscription is offline, online,
available …
Fig. 3 Account
tab before and after logging in The fig.4 shows the
Jabber client’s roster tab. The tab displays roster contacts and contacts’
current presence. At the bottom of the window there is an input box allowing
user to enter new contact JID. The two checkboxes below control how the client
responds to subscription requests from other users. If only the topmost one is
checked, only contact JID specified in the input box is allowed to subscribe to
the current user presence, (in the most cases a contact user adds to roster
also subscribes for the user’s presence). By default, the Jabber client allows
everybody to subscribe to the logged in user’s presence. It does not imposes
any serious threat since all contacts subscribed to the user’s presence are
automatically added to the user’s roster and the subscription can be terminated
by simple removing those contacts from roster. If user selects a contact in the
roster list the contact’s JID is placed in the contact JID input textbox on the
roster tab and to the “Send To” field of the messages tab (fig.5).
Fig. 4 Roster tab The messages tab
mostly consists of the message display area (fig.5). The new messages appear in
the area’s topmost line. Each message is preceded by time the message was
received/send and the username part of sender’s JID. The messages the client
sends are also displayed here. The messages tab also features destination
address input box where user can type a JID to send messages to. The JID
selected in roster is automatically placed in the box. Below the “Send To”
input box there is a one line input box for message text. The message entered
is sent to destination when user presses enter or clicks on the “Send” button.
The “Clear Received” button clears the message output window.
Fig. 5 Messages tab In order to start
the Jabber Client application user must open the start_me.html file with the Mozilla browser. The file contains an onload JavaScript handler that opens a
new window and loads the jabber.xul
file telling the Mozilla that the file should be loaded as a standalone network
application. The window with the start_me.html
file is closed after that. The jabber.xul
file can also be loaded directly by referring to its URL, but the application
will run inside a Mozilla browser window in this case. Upon loading the jabber.xul file creates initial user
interface view and waits for the Java applet classes to load. After the Java
applet is loaded it calls setStatusStr() method,
which in turn uses the LiveConnect to call the JavaScript updateStatusStr() function to change the status string to “Done
loading…” (see fig.1 and Appendix B). The application
behavior is the best to be represented as event diagram for each of the
application tabs. Fig.7 explains actions taken for events related to logging in
and out. The initial state is identified by the bold border.
Fig. 7 Log in/out processing The fig.8 describes
behavior of the Account tab. The account tab provides user with a way to create
a new account on a jabber server and to change password of an existent account.
The new account creation is allowed only if used is not logged in. Quite
opposite, the password change operation require user to be logged in and
changes password only for account the user currently logged in for. The
JavaScript function processSignOut(), and Java methods processSignOut() and signIn() are responsible for enabling
and disabling user interface components of both login/out and account tab.
Fig. 8 Accounting
tab operations
The fig.9 shows an
event diagram for the Roster tab. The Roster tab is only available if client is
in the logged in state. All the Roster operations are done through a Jabber
server. For example, adding an item to the roster requires sending a request to
server, and updating the roster displayed to user after the server processes
the add request and sends the client a message containing an updated roster.
The Roster tab is also affected by presence messages. When a presence status
message is received the Jabber client updates the presence status for a roster
contact specified in the message.
Fig. 9 Roster tab operations
The “Contact JID”
and the subscription checkboxes elements of the Roster tab UI are used to
control processing of presence subscription requests. The elements have an onchange handler, which changes
properties of the jcApplet Java class representing state of these elements. The Messages tab is
small but the most significant part of the Jabber client. Jabber is an instant
messaging service and therefore the tab looks like UI of the majority of chat
clients. It has a multi-line read-only textbox where incoming and outgoing
messages are displayed, an input box for outgoing message destination address
and an input box for outgoing message itself. A message is sent to a
destination when user either clicks on the send button or presses Enter in the
outgoing message input box. The fig. 10 shows an event diagram for the Messages
tab related events.
For further details
please refer to the JavaScript, Java and XUL code (Appendix B). For the Jabber
protocol data exchange logs please refer to the Appendix A. The fact that although
simple but functional Jabber client has been developed using the XUL,
JavaScript and Java combination shows the waste possibilities the Mozilla
network application development environment has for both users and developers.
The boundaries between conventional and network applications are becoming
thinner, technologies merge and learn to “understand” each other, but although
very promising the complex multiplatform network application environment like
Mozilla requires lots of work and “fine tuning” to become a reliable,
convenient and easy to use tool. The Jabber is an
emerging standard that may have a great future. Its advantages are easily
noticeable. It is an open standard, it has truly network design, it is
extensible and it already provides gateways for existent proprietary instant
messaging services. Eventually, unless something better is invented, Jabber
should win the majority of the instant messaging fans and become a standard de
facto. XUL Tutorial, https://www.xulplanet.com/tutorials/xultu/ Java Script Reference, https://developer.netscape.com/docs/manuals/communicator/jsref/refix.htm Live Connect, https://wp.netscape.com/eng/mozilla/3.0/handbook/javascript/livecon.htm Jabber Software Foundation, https://www.jabber.org/ Jabber away with instant messaging, https://www.javaworld.com/javaworld/jw-07-2002/jw-0726-im.html Echomine Muse 0.8a1 API, https://www.echomine.org/projects/muse/javadocs/ Exodus – Escape From Proprietary Instant
Messaging, https://exodus.jabberstudio.org/ # Session setup SENT: <stream:stream
to="test.net" xmlns="jabber:client"
xmlns:stream="https://etherx.jabber.org/streams"> # Sending authentication
request SENT: <iq id="jcl_19"
type="get"><query xmlns="jabber:iq:auth"><username>test3</username></query></iq> # Authentication failed, sending new user registration
request SENT: <iq id="jcl_20"
type="set"><query xmlns="jabber:iq:register"><username>test3</username><password>test3</password></query></iq> # Registration succeeded, sending authentication
request SENT: <iq id="jcl_21"
type="get"><query xmlns="jabber:iq:auth"><username>test3</username></query></iq> # Authentication succeeded, sending request for
account data and our presence status update SENT: <iq id="jcl_23"
type="get"><query xmlns="jabber:iq:roster"/></iq> # Session setup SENT: <stream:stream to="test.net"
xmlns="jabber:client"
xmlns:stream="https://etherx.jabber.org/streams"> # Sending authentication
request SENT: <iq
id="jcl_1" type="get"><query xmlns="jabber:iq:auth"><username>test</username></query></iq> # Authentication succeeded, sending request for
account data, updating presence status SENT: <iq
id="jcl_3" type="get"><query xmlns="jabber:iq:roster"/></iq> # Session is already set up, sending request to add
item to roster SENT: <iq
id="jcl_9" type="set"><query xmlns="jabber:iq:roster"><item jid="den@test.net"
name="den"/></query></iq> # Sending request to
subscribe new contact’s presence of the SENT: <presence
to="den@test.net" type="subscribe"/> # Receiving presence
subscription confirmation RECV: <presence
to='test1@test.net' type='subscribed' from='den@test.net'/> # Receiving updated roster RECV: <iq
type='set'><query xmlns='jabber:iq:roster'><item jid='den@test.net'
name='den' subscription='to'/></query></iq><presence
from='den@test.net/Home' to='test1@test.net'><status>available</status><priority>0</priority><x
xmlns='jabber:x:delay' from='den@test.net/Home'
stamp='20020923T00:25:59'/><x xmlns='jabber:x:delay' from='den@test.net/Home' stamp='20020923T00:25:59'/></presence> RECV: <presence to='test1@test.net'
type='subscribe' from='denis@test.net'/> SENT: <message
id=""
to="den@test.net"><subject>SubjectLine</subject><body>MessageBody</body></message> RECV: <message
id='' to='test1@test.net'
from='denis@test.net/Home'><subject>MsgSubj</subject><body>MsgText</body></message> The Jabber client
application is placed under jubberc
directory of test.net web site.
Under the directory there are 6 files. The start_me.html
file is used to start the Jabber client application. The jabber.xul and all .class
files are the core of the jabber client. The jcApplet.java is the source code of the Java class files.
Unfortunately placing the client’s JavaScript code into separate file creates
problems therefore all the JavaScript code resides in the jabber.xul file. The muse
directory under jabberc contains all
Muse API Java classes.
MS Windows
NT, MS Windows 2000 or MS Windows XP Network
connection and valid IP configuration Note: The add-on components for groupchat, the Jabber User
Directory (JUD), and the gateways to other IM networks have NOT yet been ported
to win32 and will NOT run on Windows! Install
Cygwin. Go
to http:/www.cygwin.com and click on the ‘Install now’ icon. In
the setup program choose ‘install from Internet’ option and follow the
setup instructions. When
prompted what packages to install, choose default set of Cygwin packages
plus other packages you would like to have. Test
the Cygwin installation by running ‘bash’ or any of your favorite
applications installed. Install
Jabber daemon. Download
and install Go
to the Jabber installation directory and edit jabber.xml
configuration file. See https://jabberd.jabberstudio.org/howto.html#INSTALL-WINDOWS
for detailed instructions. If
you are behind a firewall make sure your server is reachable from outside
world. The usual setup (see configuration file) is to have jabber server
listening on 5222, 5223 and 5269 TCP ports. Port 5222 is for client TCP
connections. Port 5223 serves SSL encrypted client connections. Port 5269
is present for inter-server communications. Start
the Jabber daemon (jabberd.exe). Using
Jabber client test the server installation. If you experience problems
with user authentication (server returns error 401 for existent users
even if specifying correct password), try to comment out line Enabling
SSL for windows version of jabberd (v 1.4.2). Download
and extract jabberd source code (the jabberd windows binary is not
compiled with SSL support). Run
cygwin setup (see step1) and install openssl
and openssl-dev packages. Look
them up under “Libs” section. Also, install development packages like gcc, binutils, patch e.t.c. Go
to the jubberd source code directory and edit ./cygwin/Makefile.jubberd. Change line (should be one line) Download
and apply patch https://jabberd.jabberstudio.org/downloads/mio_ssl.c.patch Run
./configure –enable-ssl Run
./make Replace
your original jubberd.exe file and all your original DLLs with newly build
ones. If you’ve installed jubberd binary, all your DLLs should be under libs in your jubberd installation
directory. Go
to the https://jabberd.jabberstudio.org/faq.html
and follow instructions under “1.14. How do I get SSL working with
my Jabber server?”
Jabber Client Operation and Execution Flow
Conclusion
References
Appendix A
XML
debug log for signing on with server test.net of new user
test3@test.net
RECV: <?xml
version='1.0'?><stream:stream
xmlns:stream='https://etherx.jabber.org/streams' id='3D9F417F'
xmlns='jabber:client' from='test.net'>
RECV: <iq id='jcl_19'
type='error'><query xmlns='jabber:iq:auth'><username>test3</username></query><error
code='401'>Unauthorized</error></iq>
RECV: <iq
id='jcl_20' type='result'/>
RECV: <iq id='jcl_21'
type='result'><query xmlns='jabber:iq:auth'><username>test3</username><password/><digest/><resource/></query></iq>
SENT: <iq id="jcl_22"
type="set"><query xmlns="jabber:iq:auth"><username>test3</username><resource>Home</resource><digest>cd7deba532cab0e667cc1306a4ed46146d6d9444</digest></query></iq>
RECV: <iq
id='jcl_22' type='result'/>
SENT: <iq id="jcl_24"
type="get"><query xmlns="jabber:iq:private"><bookmarks
xmlns="storage:bookmarks"/></query></iq>
SENT:
<presence><status>available</status><priority>0</priority></presence>
SENT: <iq id="jcl_25"
to="test.net" type="get"><query xmlns="jabber:iq:agents"/></iq>
SENT: <iq id="jcl_26"
type="get"><query xmlns="jabber:iq:private"><storage
xmlns="storage:imprefs"/></query></iq>
RECV: <iq id='jcl_23' type='result'
from='test3@test.net/Home'><query xmlns='jabber:iq:roster'/></iq>
RECV: <iq id='jcl_24' type='result'
from='test3@test.net/Home'><query xmlns='jabber:iq:private'><bookmarks
xmlns='storage:bookmarks'/></query></iq>
RECV: <message from='test.net'
to='test3@test.net'>
<subject>Hello!</subject><body>Welcome
to the Denis's Jabber server at test.net. The server has been
installed for development purposes. For information about how to use Jabber,
visit the Jabber User's Guide at
https://docs.jabber.org/</body><x xmlns='jabber:x:delay'
from='test3@test.net' stamp='20021005T19:46:10'>Offline
Storage</x></message>
RECV: <iq
id='jcl_25' to='test3@test.net/Home' type='result' from='test.net'><query
xmlns='jabber:iq:agents'><agent
jid='users.jabber.org'><name>Jabber User Directory</name><service>jud</service><search/><register/></agent></query></iq><iq
id='jcl_26' type='result' from='test3@test.net/Home'><query
xmlns='jabber:iq:private'><storage xmlns='storage:imprefs'/></query></iq>XML
debug log for signing on with server test.net of existent user
test@test.net
RECV: <?xml version='1.0'?><stream:stream xmlns:stream='https://etherx.jabber.org/streams'
id='3D8E4E26' xmlns='jabber:client' from='test.net'>
RECV: <iq
id='jcl_1' type='result'><query xmlns='jabber:iq:auth'><username>test</username><password/><digest/><resource/></query></iq>
SENT: <iq
id="jcl_2" type="set"><query xmlns="jabber:iq:auth"><username>test</username><resource>Home</resource><digest>0bcfb17160e70bc0f2e7e93354bece206e3d61d1</digest></query></iq>
RECV: <iq id='jcl_2' type='result'/>
SENT: <iq
id="jcl_4" type="get"><query xmlns="jabber:iq:private"><bookmarks
xmlns="storage:bookmarks"/></query></iq>
SENT:
<presence><status>available</status><priority>0</priority></presence>
SENT: <iq
id="jcl_5" to="test.net" type="get"><query
xmlns="jabber:iq:agents"/></iq>
SENT: <iq
id="jcl_6" type="get"><query xmlns="jabber:iq:private"><storage
xmlns="storage:imprefs"/></query></iq>
RECV: <iq
id='jcl_3' type='result' from='test@test.net/Home'><query
xmlns='jabber:iq:roster'><item jid='denis@test.net' name='denis' subscription='none'><group>My Resources</group></item><item jid='den@test.net' name='den'
subscription='none'><group>My
Resources</group></item></query></iq>
RECV: <iq
id='jcl_4' type='result' from='test@test.net/Home'><query xmlns='jabber:iq:private'><bookmarks xmlns='storage:bookmarks'/></query></iq>
RECV: <iq
id='jcl_5' to='test@test.net/Home' type='result' from='test.net'><query
xmlns='jabber:iq:agents'><agent
jid='users.jabber.org'><name>Jabber User Directory</name><service>jud</service><search/><register/></agent></query></iq><iq
id='jcl_6' type='result' from='test@test.net/Home'><query
xmlns='jabber:iq:private'><storage xmlns='storage:imprefs'/></query></iq>XML
debug log for adding user den@test.net to the roster of
test1@test.net
RECV: <iq
type='set'><query xmlns='jabber:iq:roster'><item
jid='den@test.net' name='den' subscription='none'/></query></iq>
RECV: <iq id='jcl_9' type='result' from='test1@test.net/Home'
to='test1@test.net/Home'/>
RECV: <iq
type='set'><query xmlns='jabber:iq:roster'><item
jid='den@test.net' name='den' subscription='none'
ask='subscribe'/></query></iq>XML
debug log for receiving a subscription request from user denis@test.net
SENT: <presence
to="denis@test.net" type="subscribed"/>
RECV: <iq type='set'><query
xmlns='jabber:iq:roster'><item jid='denis@test.net'
subscription='from'/></query></iq>XML
debug log for sending message to user
den@test.net
XML debug log for receiving a message from user
denis@test.net
Appendix B
Jabber Client
Directory/File Structure
Application
startup code (start_me.html)
<!-- This will open Jabber Client window and disappear leaving the client
window intact. -->
<HTML>
<HEAD>
</HEAD>
<BODY onload="window.open('jabber.xul','xulex','chrome,centerscreen');
window.close();
return false;">
<h1>Jabber Client Started</h1>
</BODY>
</HTML>
User interface file (jabber.xul)
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://modern/skin/" type="text/css"?>
<window
title="Jabber Client"
persist="width height"
orient="vertical"
xmlns:html="https://www.w3.org/1999/xhtml"
xmlns="https://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script>
/* Java script functions. Live connect does not seem to work
if they placed in separate file. */
/* Functions */
// Called by applet to set status string
function updateStatusStr(str)
{
// Can't call Java from here
document.getElementById('stBarDesc').value = str.toString();
}
function updateStatusStr2(str)
{
document.getElementById('stBarDesc2').value = str.toString();
}
// Passes parameters to Java sign in method
function signIn()
{
if(document.getElementById('signInButton').disabled)
{
return;
}
usr = document.getElementById('usr').value;
pwd = document.getElementById('pwd').value;
srv = document.getElementById('srv').value;
res = document.getElementById('res').value;
port = document.getElementById('port').value;
if(usr.length == 0)
{
alert("Please specify user name");
return;
}
if(srv.length == 0)
{
alert("Please specify server");
return;
}
document.getElementById('jcApplet').signIn(usr, pwd, srv, res, port);
setChkboxVars();
}
// Checks status and calls signOut Java method
function signOut()
{
if(document.getElementById('signOutButton').disabled)
{
return;
}
document.getElementById('jcApplet').signOut();
}
// Passes parameters to Java register method
function Register()
{
if(document.getElementById('registerButton').disabled)
{
return;
}
usr = document.getElementById('ausr').value;
pwd = document.getElementById('apwd').value;
pwd1 = document.getElementById('apwd1').value;
srv = document.getElementById('asrv').value;
port = document.getElementById('aport').value;
if(usr.length == 0)
{
alert("Please specify user name");
return;
}
if(srv.length == 0)
{
alert("Please specify server");
return;
}
if(pwd != pwd1)
{
alert("Passwords do not match");
return;
}
document.getElementById('jcApplet').register(usr, pwd, srv, port);
document.getElementById('apwd').value='';
document.getElementById('apwd1').value='';
}
// Passes parameters to Java chpass method
function chPass()
{
if(document.getElementById('chpassButton').disabled)
{
return;
}
pwd = document.getElementById('apwd').value;
pwd1 = document.getElementById('apwd1').value;
document.getElementById('jcApplet').chpwd(pwd);
document.getElementById('apwd').value = '';
document.getElementById('apwd1').value = '';
}
// Handles req to add an item to roster
function addToRosterReq()
{
cont = document.getElementById('cont').value;
if(cont.length == 0)
{
alert("Please specify contact JID");
return;
}
document.getElementById('jcApplet').addToRosterReq(cont);
}
// Adds a record to roster view
function addToRoster(jidstr, subs)
{
delFromRoster(jidstr);
var chld = document.getElementById('rtree');
var titem = document.createElement('treeitem');
var trow = document.createElement('treerow');
var jidc = document.createElement('treecell');
var presc = document.createElement('treecell');
titem.setAttribute('id',jidstr);
jidc.setAttribute('label',jidstr);
presc.setAttribute('id', "presence_"+jidstr);
presc.setAttribute('label', subs);
trow.appendChild(jidc);
trow.appendChild(presc);
titem.appendChild(trow);
chld.appendChild(titem);
}
// Puts selected roster JID into textbox
function treeitemIntoHolder()
{
var tree = document.getElementById('roster');
var chld = document.getElementById('rtree');
var tbox = document.getElementById('cont');
var item = chld.childNodes[tree.currentIndex];
var tbox1 = document.getElementById('sndto');
tbox.value = item.id;
tbox1.value = item.id;
setChkboxVars();
}
// Handles req to remove an item from roster
function removeFromRosterReq()
{
cont = document.getElementById('cont').value;
if(cont.length == 0)
{
alert("Please specify contact JID");
return;
}
document.getElementById('jcApplet').removeFromRosterReq(cont);
}
// Removes a record from roster view
function delFromRoster(jidstr)
{
var chld = document.getElementById('rtree');
var rmitem = document.getElementById(jidstr);
if(rmitem != null)
{
chld.removeChild(rmitem);
}
}
// Removes all records from roster view
function clearRoster()
{
var chld = document.getElementById('rtree');
while( chld.lastChild != null )
{
chld.removeChild(chld.firstChild);
}
}
// Sets presence for specified JID in roster view
function setPresence(pjid, prStr)
{
var pitemid = "presence_" + pjid;
var item = document.getElementById(pitemid);
if(item != null)
{
item.setAttribute('label', prStr);
}
}
// Processes signing out
function processSignOut()
{
clearRoster();
document.getElementById('ausr').readonly=0;
document.getElementById('ausr').value='';
document.getElementById('asrv').readonly=0;
document.getElementById('aport').readonly=0;
document.getElementById('signInButton').disabled=0;
document.getElementById('signOutButton').disabled=1;
document.getElementById('registerButton').disabled=0;
document.getElementById('chpassButton').disabled=1;
document.getElementById('rosterTab').collapsed=1;
document.getElementById('messagesTab').collapsed=1;
document.getElementById('pwd').value='';
document.getElementById('rcvmsg').value='';
}
// Process changes to checkboxes
function setChkboxVars()
{
cont = document.getElementById('cont').value;
allow = document.getElementById('subscribe').checked;
allowall = document.getElementById('subscribeall').checked;
document.getElementById('jcApplet').setChkboxVars(allow, cont, allowall);
}
// Adds message to message output window
function showMsg(jid, msg)
{
curmsgs = document.getElementById('rcvmsg').value;
today = new Date();
idx = jid.indexOf('@');
if(idx != -1)
{
jid = jid.substr(0, idx);
}
newmsgs = '['+today.getHours()+':'+today.getMinutes()+':'+today.getSeconds()+
' '+jid+']: '+msg+'\n'+curmsgs;
if(newmsgs.length > 2500)
{
newmsgs = newmsgs.substr(0, 2000);
idx = newmsgs.lastIndexOf('\n');
newmsgs = newmsgs.substr(0, idx)+'...';
}
document.getElementById('rcvmsg').value = newmsgs;
}
// Sends a message
function sendMsg(tojid, msg)
{
var msg = document.getElementById('msg').value;
var tojid = document.getElementById('sndto').value;
if(tojid.length == 0 || tojid.indexOf('@')==-1)
{
alert('Please pick a destination address from roster');
return;
}
var myname = document.getElementById('ausr').value;
ret = document.getElementById('jcApplet').sendMsg(tojid, msg);
if(ret != false)
{
showMsg(myname, msg);
}
}
</script>
<hbox>
<html:applet
code="jcApplet.class"
name="jcApplet"
type="application/x-java-applet;version=1.3.1_05"
id="jcApplet"
archive="muse/aspectjrt.jar, muse/crimson-1.1.3.jar,
muse/jakarta-oro-2.0.4.jar, muse/jaxp-1.1.jar,
muse/jdom-b8.jar, muse/junit-3.7.jar,
muse/log4j-1.2.6.jar, muse/muse-0.8a1.jar"
mayscript="true"
height="0"
width="0">
</html:applet>
</hbox>
<command id="checkboxes" oncommand="setChkboxVars();"/>
<hbox debug="false">
<tabbox>
<tabs>
<tab label="LogIn/Out"/>
<tab label="Account"/>
<tab label="Roster" id="rosterTab" collapsed="true"/>
<tab label="Messages" id="messagesTab" collapsed="true"/>
</tabs>
<tabpanels>
<tabpanel id="tab_sign" debug="false">
<vbox flex="10">
<groupbox>
<grid flex="1">
<columns>
<column flex="1"/>
<column flex="1"/>
</columns>
<rows>
<row>
<label control="jidl" value="UserJID:"/>
<hbox align="start">
<textbox id="usr" flex="0" style="width: 7em;"/><label
value="@" style="width: 1em;"/><textbox id="srv" flex="0"
value="jabber.org" style="width: 11em;"/><label value="/"
style="width: 1em;"/><textbox id="res" flex="0"
style="width: 5em;"/>
</hbox>
</row>
<row>
<label control="pwdl" value="Password:"/>
<hbox align="start">
<textbox id="pwd" type="password" flex="0"
onkeypress="if(event.keyCode==13) signIn();"
style="width: 26em;"/>
</hbox>
</row>
<row>
<hbox/>
<hbox align="start" flex="0">
<label control="portl" value="Port:"/><textbox
id="port" flex="0" value="5222" style="width: 5em;"/>
</hbox>
</row>
<row>
<hbox/>
<hbox align="start">
<checkbox id="invisible" label="Invisible" disabled="1" flex="0"/>
</hbox>
</row>
</rows>
</grid>
<hbox align="end">
<spacer flex="3"/>
<button id="signInButton" label="LogIn" flex="0"
onclick = "signIn();"/>
<spacer flex="1"/>
<button id="signOutButton" label="LogOut" flex="0" disabled="true"
onclick = "signOut(); processSignOut();"/>
<spacer flex="3"/>
</hbox>
</groupbox>
<spacer flex="10"/>
</vbox>
</tabpanel>
<tabpanel id="tab_acct">
<vbox flex="10">
<groupbox>
<grid flex="1">
<columns>
<column flex="1"/>
<column flex="1"/>
</columns>
<rows>
<row>
<label control="jidl" value="User:"/>
<hbox align="start">
<textbox id="ausr" style="width: 9em;"/><label
value="@" style="width: 1em;"/><textbox id="asrv"
value="jabber.org" style="width: 14em;"/>
</hbox>
</row>
<row>
<label control="apwd" value="Password:"/>
<hbox align="start">
<textbox id="apwd" type="password" style="width: 24em;"/>
</hbox>
</row>
<row>
<label control="apwdl" value="Confirm Password:"/>
<hbox align="start">
<textbox id="apwd1" type="password" flex="0" style="width: 24em;"/>
</hbox>
</row>
<row>
<hbox/>
<hbox align="start">
<label control="portl" value="Port:"/><textbox
id="aport" flex="0" value="5222" style="width: 5em;"/>
</hbox>
</row>
</rows>
</grid>
<hbox align="end">
<spacer flex="3"/>
<button id="registerButton" label="Register" flex="0"
onclick = "Register();"/>
<spacer flex="1"/>
<button id="chpassButton" label="Change Password" flex="0" disabled="true"
onclick = "chPass();"/>
<spacer flex="3"/>
</hbox>
</groupbox>
<spacer flex="10"/>
</vbox>
</tabpanel>
<tabpanel id="tab_roster" flex="1">
<vbox flex="10">
<hbox flex="10">
<tree seltype="single" hidecolumnpicker="true" style="height: 10em;"
flex="10" id="roster" onselect="treeitemIntoHolder();">
<treecols>
<treecol id="coljid" label="JID" flex="5"/>
<treecol id="colpres" label="Presence" flex="1"/>
</treecols>
<treechildren id="rtree">
</treechildren>
</tree>
</hbox>
<hbox flex="0">
<label control="jidr" value="Contact JID:"/>
<textbox id="cont" oninput="setChkboxVars()" flex="5"/>
<!--- <spacer flex="1"/>
<label control="grpr" value="Group:"/>
<textbox id="grp" flex="3"/> -->
</hbox>
<hbox>
<checkbox id="subscribe" checked="false" persist="true"
label="Accept subscribe request from the contact JID"
command="checkboxes" flex="0"/>
</hbox>
<hbox>
<checkbox id="subscribeall" checked="true" persist="true"
label="Accept all subscribe requests"
command="checkboxes" flex="0"/>
</hbox>
<hbox align="end">
<spacer flex="3"/>
<button id="addr" label="Add" flex="0"
onclick = "addToRosterReq();"/>
<spacer flex="1"/>
<button id="remover" label="Remove" flex="0"
onclick = "removeFromRosterReq();"/>
<spacer flex="3"/>
</hbox>
<spacer flex="10"/>
</vbox>
</tabpanel>
<tabpanel id="tab_msgs">
<vbox flex="10">
<hbox flex="10">
<textbox id="rcvmsg" multiline="true" readonly="true" wrap="true"
value="No new messages" flex="1" rows="8" cols="80"
style="background-color : white; text-color : black;"/>
</hbox>
<hbox flex="10">
<label control="sndto" value="Send to:"/>
<textbox id="sndto" value="Pick an address from roster" flex="1"/>
</hbox>
<hbox>
<textbox id="msg" value="Type your message here"
onkeypress="if(event.keyCode == 13) sendMsg();"
rows="1" cols="80" flex="1"/>
</hbox>
<hbox align="end">
<spacer flex="3"/>
<button id="send" label="Send" default="1" flex="0"
onclick = "sendMsg();"/>
<spacer flex="1"/>
<button id="clear" label="Clear Received" flex="0"
onclick = "document.getElementById('rcvmsg').value = '';"/>
<spacer flex="3"/>
</hbox>
<spacer flex="10"/>
</vbox>
</tabpanel>
</tabpanels>
</tabbox>
</hbox>
<hbox>
<description id="stBarDesc" value="Loading..."/>
<description value=" "/>
<description id="stBarDesc2" value=""/>
</hbox>
</window>
Java applet source code (jsApplet.java)
/* This is applet provides communication for java script to
Muse jabber client code. */
import java.awt.*;
import java.applet.*;
import java.util.*;
import com.echomine.jabber.*;
import com.echomine.net.*;
import netscape.javascript.*;
public class jcApplet extends Applet
{
/* Public properties */
public String statusStr;
public boolean signedIn;
public boolean allowSubscribe;
public boolean allowAllSubscribe;
public String allowSubscribeJid;
/* Class properties */
JSObject win;
Jabber jabber;
JabberContext jabberContext;
JabberSession jabberSession;
JabberRosterService roster;
/* Handlers */
// Connection monitoring
ConnectionListener cMon = new com.echomine.net.ConnectionListener()
{
public void connectionClosed(ConnectionEvent e)
{
System.out.println("connectionClosed");
// If the flag is set assume connedction terminated unexpectedly
if(signedIn)
{
jsAlert("Connection terminated: " + e);
processSignOut();
}
signedIn = false;
setStatusStr("Not signed in");
return;
}
public void connectionStarting(ConnectionEvent e)
{
System.out.println("connectionStarting");
return;
}
public void connectionEstablished(ConnectionEvent e)
{
System.out.println("connectionEstablished");
return;
}
};
// Message reception handling
JabberMessageListener msgMon = new com.echomine.jabber.JabberMessageListener()
{
public void messageReceived(JabberMessageEvent event)
{
System.out.println("Received msg id="+event.getMessageType());
switch(event.getMessageType())
{
case JabberCode.MSG_CHAT:
System.out.println("Received MSG_CHAT");
JabberChatMessage cmsg = (JabberChatMessage) event.getMessage();
if(cmsg.getType().equals(JabberChatMessage.TYPE_CHAT) ||
cmsg.getType().equals(JabberChatMessage.TYPE_NORMAL))
{
showMessage(cmsg.getFrom(), cmsg.getBody());
}
break;
case JabberCode.MSG_IQ_ROSTER:
System.out.println("Received MSG_IQ_ROSTER");
RosterIQMessage msg = (RosterIQMessage) event.getMessage();
Iterator items = msg.getRosterItems().iterator();
RosterItem item;
while (items.hasNext())
{
item = (RosterItem) items.next();
System.out.println("[Roster Contact] " + item);
if(item.getSubscription().equals("remove"))
{
delFromRoster(item.getJID());
}
else
{
if(item.getSubscription().equals("to") ||
item.getSubscription().equals("both"))
{
addToRoster(item.getJID(), "offline");
}
else
{
addToRoster(item.getJID(), "unknown");
}
}
}
break;
case JabberCode.MSG_PRESENCE:
System.out.println("Received MSG_PRESENCE");
System.out.println("PresMsg: "+event.getMessage());
if(jabberSession == null)
{
break;
}
JabberPresenceMessage pmsg = (JabberPresenceMessage) event.getMessage();
if(pmsg.getType().equals(PresenceCode.TYPE_SUBSCRIBE))
{
try
{
if(allowAllSubscribe ||
(allowSubscribe && allowSubscribeJid != null &&
allowSubscribeJid.equals(pmsg.getFrom())))
{
jabberSession.getPresenceService().acceptSubscribe(pmsg);
setStatusStr2("Accepted subscription: "+pmsg.getFrom());
}
else
{
jabberSession.getPresenceService().denySubscribe(pmsg);
setStatusStr2("Denied subscription: "+pmsg.getFrom());
}
}
catch (Exception e)
{
System.out.println(e);
}
}
else
{
System.out.println("Setting presence to " + pmsg.getType());
setPresence(pmsg.getFrom(), pmsg.getType());
}
break;
default:
return;
}
}
};
/* Major methods */
// Initializes applet
public void init()
{
// Initialize the applet
win = JSObject.getWindow(this);
signedIn = false;
allowSubscribe = false;
allowAllSubscribe = true;
allowSubscribeJid = null;
jabber = new Jabber();
jabberContext = null;
jabberSession = null;
statusStr = "Done loading, not signed in";
// Let user know we are done loading
setStatusStr(statusStr);
System.out.println("Applet initialized.");
return;
}
// Signs a user in
public void signIn(String usr, String pwd, String srv, String res, int port)
{
System.out.println("signIn("+usr+","+pwd+","+srv+","+res+","+port+")");
if(signedIn)
{
jsAlert("You are signed in as " +
jabberContext.getUsername() +
", please sign out first.");
return;
}
// Initialize the Jabber context
if(usr.length() == 0)
{
usr = null;
System.out.println("User not set");
}
if(pwd.length() == 0)
{
pwd = null;
System.out.println("Password not set");
}
if(srv.length() == 0)
{
srv = null;
System.out.println("Server not set");
}
// Create the new session
jabberContext = new JabberContext(usr, pwd, srv);
if(res.length() > 0)
{
jabberContext.setResource(res);
}
jabberSession = jabber.createSession(jabberContext);
try
{
// Connect to the server
jabberSession.connect(srv, port);
// Add connection event listener
jabberSession.getConnection().addConnectionListener(cMon);
// Log in to the Jabber server
jabberSession.getUserService().login();
jabberSession.addMessageListener(msgMon);
roster = jabberSession.getRosterService();
roster.requestRosterList(true);
jabberSession.getPresenceService().setToAvailable(null,null);
}
catch (Exception e)
{
System.out.println(e);
jsAlert(e.toString());
return;
}
allowSubscribe = false;
allowSubscribeJid = null;
signedIn = true;
win.eval("document.getElementById('ausr').readonly=1;"+
"document.getElementById('ausr').value='"+
jabberContext.getUsername()+"';"+
"document.getElementById('asrv').readonly=1;"+
"document.getElementById('asrv').value='"+
jabberContext.getServerName()+"';"+
"document.getElementById('aport').readonly=1;"+
"document.getElementById('aport').value='"+
jabberSession.getConnection().getConnectionModel().getPort()+"';"+
"document.getElementById('signInButton').disabled=1;"+
"document.getElementById('signOutButton').disabled=0;"+
"document.getElementById('registerButton').disabled=1;"+
"document.getElementById('chpassButton').disabled=0;\n"+
"document.getElementById('rosterTab').collapsed=0;"+
"document.getElementById('messagesTab').collapsed=0;\n");
setStatusStr("Signed in as " + jabberContext.getUsername());
return;
}
// Signs a user out
public void signOut()
{
System.out.println("signOut()");
// Disconnect
signedIn = false;
jabberSession.disconnect();
}
// Register a new user
public void register(String usr, String pwd, String srv, int port)
{
System.out.println("register("+usr+","+pwd+","+srv+","+port+")");
if(signedIn)
{
jsAlert("You are signed in as " +
jabberContext.getUsername() +
", please sign out first.");
return;
}
if(usr.length() == 0)
{
usr = null;
System.out.println("User not set");
}
if(pwd.length() == 0)
{
pwd = null;
System.out.println("Password not set");
}
if(srv.length() == 0)
{
srv = null;
System.out.println("Server not set");
}
// Create the new session
jabberContext = new JabberContext(usr, pwd, srv);
jabberSession = jabber.createSession(jabberContext);
try
{
// Connect to the server
jabberSession.connect(srv, port);
// Try to register the new user
HashMap av = new HashMap();
av.put("username", usr);
av.put("password", pwd);
jabberSession.getUserService().register(srv, av);
// Now disconnect
jabberSession.disconnect();
jabberSession = null;
jabberContext = null;
}
catch (Exception e)
{
System.out.println(e);
if(jabberSession != null && jabberSession.getConnection().isConnected())
{
jabberSession.disconnect();
}
jsAlert(e.toString());
return;
}
jsAlert("User "+usr+" has been succefully registerd");
return;
}
// Change user's password
public void chpwd(String pwd)
{
System.out.println("chpwd("+pwd+")");
// Change passwd
try
{
jabberSession.getUserService().changePassword(pwd);
}
catch (Exception e)
{
jsAlert(e.toString());
return;
}
jsAlert("Password has been successfully changed");
}
// Request to add an item to Roster
public void addToRosterReq(String jid)
{
System.out.println("addRosterReq("+jid+")");
if(!signedIn || roster == null)
{
jsAlert("You are not signed in");
return;
}
if(jid.length() == 0)
{
System.out.println("JID not set");
jsAlert("Please specify JID of the contact to add");
return;
}
try
{
roster.addToRoster(jid, jid, null, false);
jabberSession.getPresenceService().subscribe(jid);
}
catch (Exception e)
{
jsAlert(e.toString());
return;
}
return;
}
// Request to remove an item from Roster
public void removeFromRosterReq(String jid)
{
System.out.println("removeFromRosterReq("+jid+")");
if(!signedIn || roster == null)
{
jsAlert("You are not signed in");
return;
}
if(jid.length() == 0)
{
System.out.println("JID not set");
jsAlert("Please specify JID of the contact to remove");
return;
}
try
{
roster.removeFromRoster(jid, false);
}
catch (Exception e)
{
jsAlert(e.toString());
return;
}
return;
}
public void setChkboxVars(boolean allow, String cont, boolean allowall)
{
allowSubscribe = allow;
allowAllSubscribe = allowall;
allowSubscribeJid = cont;
}
public boolean sendMsg(String tojid, String msg)
{
System.out.println("sendMsg("+tojid+","+msg+")");
if(!signedIn || tojid.length() == 0 || msg.length() == 0)
{
jsAlert("Please sign in, specify destination address and a message to send");
return false;
}
try
{
jabberSession.getChatService().sendPrivateMessage(tojid, msg);
}
catch (Exception e)
{
jsAlert(e.toString());
return false;
}
return true;
}
/* Helper methods */
// Calls JS alert
void jsAlert(String str)
{
String arg[] = { str };
win.call("alert", arg);
return;
}
// Sets status string
void setStatusStr(String str)
{
String arg[] = { str };
win.call("updateStatusStr", arg);
return;
}
void setStatusStr2(String str2)
{
String arg[] = { str2 };
win.call("updateStatusStr2", arg);
return;
}
// Add an item to roster
void addToRoster(String jid, String subs)
{
String arg[] = { jid, subs };
win.call("addToRoster", arg);
return;
}
// Remove an item from roster
void delFromRoster(String jid)
{
String arg[] = { jid };
win.call("delFromRoster", arg);
return;
}
// Set presence to an item in roster
void setPresence(String jidstr, String pres)
{
JID jid;
try
{
jid = new JID(jidstr);
}
catch(Exception e)
{
System.out.println("Malformed JID: "+jidstr);
return;
}
String arg[] = { jid.getNode()+"@"+jid.getHost(), pres };
win.call("setPresence", arg);
return;
}
// Removes all items from roster view
void clearRoster()
{
String arg[] = {};
win.call("clearRoster", arg);
return;
}
// Calls XUL sign out processing
void processSignOut()
{
String arg[] = { };
win.call("processSignOut", arg);
return;
}
// Adds message to message output window
void showMessage(String jid, String msg)
{
String arg[] = { jid, msg };
win.call("showMsg", arg);
return;
}
}
Appendix C
Jabber
daemon for MS Windows installation instructions
Requirements
Installation
It will run setup.exe program.
https://jabberd.jabberstudio.org/downloads/JabberD-1.4.2.exe
<mod_auth_0k>./libs/jsm.dll</mod_auth_0k>
in jabber.xml configuration file and restart the daemon.
EXTRALIBS=-lpth /lib/crt0.o $(shell gcc
-print-libgcc-file-name) -lcygwin -lkernel32
to (should be one line)
EXTRALIBS=-L$(shell pth-config --libdir) -lpth
/lib/crt0.o $(shell gcc -print-libgcc-file-name) -llibcrypto -llibssl
-lcygwin -lkernel32