I implemented a simple script for storing instant messages when the addressed user is offline. I believe I pulled the code from the wiredux project, but I’m not 100% sure, I will check that soon to give proper credits. Later, I made my own extension in order to forward the messages to the user’s email address (if enabled in his/her profile settings).
Sorry, the code looks horrible and I used some existing PHP module for accessing the opensim database, just because I was lazy 🙂 But it works ok so far.
In the root directory of my Apache web server, I made the directory “im”. There I placed the following script, named “offline.php”
<?php
/**************************************************************
* Configuration settings
**************************************************************/
define("HOST","localhost");
define("OPENSIM","opensim");
define("OSPROFILE","osprofile");
define("USER","opensim");
define("PASSWORD","mypassword");
define("OFFLINE_IM", "offline_msgs");
define("USERSETTINGS", "usersettings");
define("USERACCOUNTS", "UserAccounts");
include("mysql.php");
$DbLink = new DB(HOST,OPENSIM,USER,PASSWORD);
$method = $_SERVER["PATH_INFO"];
/**************************************************************
* save message
**************************************************************/
if ($method == "/SaveMessage/")
{
$msg = $HTTP_RAW_POST_DATA;
$start = strpos($msg, "?>");
if ($start < 0) {
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><boolean>false</boolean>";
return;
}
$start += 2;
$fullmsg = substr($msg, $start);
try {
$xml = new SimpleXMLElement($msg);
$message = $xml->message;
$toAgentID = $xml->toAgentID;
$fromAgentName = $xml->fromAgentName;
$fromAgentID = $xml->fromAgentID;
$time = date('H:i',$xml->timestamp+0);
$DbLink->query("insert into ".OFFLINE_IM." (uuid, message) values ('" .
mysql_escape_string($toAgentID) . "', '" .
mysql_escape_string($fullmsg) . "')");
send_email($toAgentID,$fromAgentName,$time,$message);
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><boolean>true</boolean>";
}
catch(Exception $e) {
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><boolean>false</boolean>";
}
$DbLink->close();
exit;
}
/**************************************************************
* retrieve messages
**************************************************************/
if ($method == "/RetrieveMessages/")
{
$parms = $HTTP_RAW_POST_DATA;
$parts = split("[<>]", $parms);
$agent_id = $parts[6];
$DbLink->query("select message from ".OFFLINE_IM." where uuid='" .
mysql_escape_string($agent_id) . "'");
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><ArrayOfGridInstantMessage xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">";
while(list($message) = $DbLink->next_record()) {
echo $message;
}
echo "</ArrayOfGridInstantMessage>";
$DbLink->query("delete from ".OFFLINE_IM." where uuid='" .
mysql_escape_string($agent_id) . "'");
exit;
}
/**************************************************************
* email forwarding
**************************************************************/
function send_email($toID,$fromName,$time,$msg)
{
$Db = new DB(HOST,OSPROFILE,USER,PASSWORD);
$Db->query("select imviaemail from " . USERSETTINGS .
" where useruuid='" . mysql_escape_string($toID) . "'");
$nrows = $Db->num_rows();
if( $nrows < 1 ) { $Db->close(); return; }
$row = $Db->next_record();
$imviaemail = $row['imviaemail'];
if( !$imviaemail ) { $Db->close(); return; }
$email = get_email($toID);
if( strpos($email,'@') === false ) { $Db->close(); return; }
$body = "You received the following message on My Opensim Server:\n\n" .
"[" . $time . "] " . $fromName . ": ". $msg . "\n\n" .
"*** do not reply to this message by email! ***\n";
$subject = "[My Opensim Server] Message from " . $fromName;
$headers =
'From: ' . $fromName . " <no-reply@server.example.com>\r\n" .
'Reply-To: no-reply@server.example.com' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
mail($email, $subject, $body, $headers);
$Db->close();
}
/**************************************************************
* get email address
**************************************************************/
function get_email($uuid)
{
$Db = new DB(HOST,OPENSIM,USER,PASSWORD);
$Db->query("select Email from " . USERACCOUNTS .
" where PrincipalID='" . mysql_escape_string($uuid) . "'");
$nrows = $Db->num_rows();
if( $nrows < 1 ) { $Db->close(); return(""); }
$row = $Db->next_record();
$email = $row['Email'];
$Db->close();
return($email);
}
?>
Warning: the parsing of the XML message from opensimulator is very primitive and there are some hard-coded constants, like
$agent_id = $parts[6];
Such constants might have to be changed when using a different version of opensimulator. Actually, I should change that into some proper XML parsing, but no time for that yet and for the moment it works…
This script uses an extra table in the opensimulator database. I created this table as follows in the mysql command:
CREATE TABLE IF NOT EXISTS `offline_msgs` (
`uuid` varchar(36) NOT NULL,
`message` text NOT NULL,
KEY `uuid` (`uuid`)
) ENGINE=MyISAM
The script also uses the profile database as created by the osprofile project (I believe it was osprofile, I will check that later), to get the preference of the user to receive offline IM’s by email or not. The profile database also includes the user’s email address, but this one doesn’t seem to updated reliably, so I pull the email address from the main opensim database instead.
The PHP module mysql.php:
<?php
/*
* Copyright (c) 2007, 2008 Contributors, http://opensimulator.org/
* See CONTRIBUTORS for a full list of copyright holders.
*
* See LICENSE for the full licensing terms of this file.
*
*/
//This looks like its lifted from http://www.weberdev.com/get_example-4372.html I'd contact the original developer for licensing info, but his website is broken.
class DB
{
var $Host; // Hostname of our MySQL server
var $Database; // Logical database name on that server
var $User; // Database user
var $Password; // Database user's password
var $Link_ID = 0; // Result of mysql_connect()
var $Query_ID = 0; // Result of most recent mysql_query()
var $Record = array(); // Current mysql_fetch_array()-result
var $Row; // Current row number
var $Errno = 0; // Error state of query
var $Error = "";
function __construct($host,$db,$user,$passwd)
{
$this->Host = $host;
$this->Database = $db;
$this->User = $user;
$this->Password = $passwd;
}
function halt($msg)
{
echo("</TD></TR></TABLE><B>Database error:</B> $msg<BR>\n");
echo("<B>MySQL error</B>: $this->Errno ($this->Error)<BR>\n");
die("Session halted.");
}
function connect()
{
if($this->Link_ID == 0)
{
$this->Link_ID = mysql_connect($this->Host, $this->User, $this->Password);
if (!$this->Link_ID)
{
$this->halt("Link_ID == false, connect failed");
}
$SelectResult = mysql_select_db($this->Database, $this->Link_ID);
if(!$SelectResult)
{
$this->Errno = mysql_errno($this->Link_ID);
$this->Error = mysql_error($this->Link_ID);
$this->halt("cannot select database <I>".$this->Database."</I>");
}
}
}
function escape($String)
{
return mysql_escape_string($String);
}
function query($Query_String)
{
$this->connect();
$this->Query_ID = mysql_query($Query_String,$this->Link_ID);
$this->Row = 0;
$this->Errno = mysql_errno();
$this->Error = mysql_error();
if (!$this->Query_ID)
{
$this->halt("Invalid SQL: ".$Query_String);
}
return $this->Query_ID;
}
function next_record()
{
$this->Record = @mysql_fetch_array($this->Query_ID);
$this->Row += 1;
$this->Errno = mysql_errno();
$this->Error = mysql_error();
$stat = is_array($this->Record);
if (!$stat)
{
@mysql_free_result($this->Query_ID);
$this->Query_ID = 0;
}
return $this->Record;
}
function num_rows()
{
return mysql_num_rows($this->Query_ID);
}
function affected_rows()
{
return mysql_affected_rows($this->Link_ID);
}
function optimize($tbl_name)
{
$this->connect();
$this->Query_ID = @mysql_query("OPTIMIZE TABLE $tbl_name",$this->Link_ID);
}
function clean_results()
{
if($this->Query_ID != 0) mysql_freeresult($this->Query_ID);
}
function close()
{
if($this->Link_ID != 0) mysql_close($this->Link_ID);
}
}
?>
Finally, I have the following in my OpenSim.ini (actually, in config-include/MyWorld.ini, since I use the Diva distribution):
[Messaging]
; Control which region module is used for instant messaging.
; Default is InstantMessageModule (this is the name of the core IM module a$
InstantMessageModule = InstantMessageModule
; MessageTransferModule = MessageTransferModule
OfflineMessageModule = OfflineMessageModule
OfflineMessageURL = http://server.example.com/im/offline.php
MuteListModule = MuteListModule
MuteListURL = http://server.example.com/im/mute.php
; Control whether group messages are forwarded to offline users. Default i$
; ForwardOfflineGroupMessages = true
The “mute.php” script doesn’t exist, but this config line still seems to be neccessary.