Difference between revisions of "SL Mail V1.2"

From Second Life Wiki
Jump to navigation Jump to search
m (Replaced <source> with <syntaxhighlight>)
 
(62 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{LSL Header}}
{{LSL Header}} __NOTOC__
=SL Mail V1.2=
<div id="box">
[[Image:slmail.info.jpg]]
Updated: 10-2-2011
 
='''Second Life mail client'''=
<div style="padding: 0.5em">


Second Life mail client. Send and receive email from within Second Life from and to any address.
'''Send and receive email from within Second Life from and to any address.'''


Implemented with Address Book function (in a notecard named 'Address Book') to stores addresses by name  and to compile groups of named addresses. Current version is stand-alone (no server functionality) and uses chat commands only.
Implemented with Address Book function (in a notecard named 'Address Book') to stores addresses by name  and to compile groups of named addresses. Current version is stand-alone (no server functionality) and uses chat commands only.


==Release Notes V1.2==
[[Image:BannerSLMail.jpg]]


SL Mail V1.2 Release notes
URL: [http://www.slmail.info slmail.info] <strike>(website coming soon!)</strike> <b>url slmail.info no longer used for SL Mail, and was taken for some other purpose.</b>
------------------------------


(edition 2, 4 sept 2007)
</div>
</div>


==='''LICENCE INFO'''===
<div id="box">


SL Mail V1.2 is released under GNU free software licence
=Release Notes V1.2=
<div style="padding: 0.5em">
SL Mail V1.2 Release notes (edition 2, 4 sept 2007)


==='''PRODUCTION & MAINTANENCE'''===
===IMPORTANT NOTICE===


by Logic Scripted Products and Script Services
Development of SL Mail was abandoned midyear 2008. The domain slmail.info was abandoned and is now taken by another domain holder (ISS) who has nothing to do with SL Mail.


[[Image:Logicbanner1.jpg]]
SL Mail is considered for redevelopment under the same terms, using a version of the ''Serverless Key Exchange'' script. This allows mail clients to exchange their key adresses to other rezzed mail clients in world, and send each other info about the public name they use, and other info that could be usefull. This protocol does not use a central server.


Contact: [[User:Flennan Roffo|Flennan Roffo]]
See: [https://wiki.secondlife.com/wiki/Serverless_Key_Exchange Serverless Key Exchange]


==='''DESCRIPTION'''===
Future annoucements about SL Mail development will be made here.
</div>
</div>


SL Mail V1.2 is a simple tool to send and receive mail from within SL.
<div id="box">
You can either compose a message yourself using chat recording, or choose a notecard from inventory to send.


==='''COMMANDS'''===
=='''LICENCE INFO'''==
<div style="padding: 0.5em">
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.


@Help                          : Show Help about available commands
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.


@Record                      : Starts a chat recording (for owner only).
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
</div>
</div>


@Stop                          : Stops the chat recording without sending
<div id="box">


@Continue                  : Continues a previously stopped chat recording. Lines will be appended to you recording buffer.
=='''PRODUCTION & MAINTANENCE'''==
<div style="padding: 0.5em">
by Logic Scripted Products and Script Services


@Show                          : Shows the content of the recording buffer.
[[Image:Logicbanner1.jpg]]


@Send                          : If recording, stops the recording, and sends the mail (need to supply mail address and subject first!)
URL: [http://www.logicscripts.org logicscripts.org] <strike>(website coming soon!)</strike>


@Send <notecard>      : Sends the content of the notecard, instead of the chat recording.
SLurl:
{{SLurl
|region=Beeratron
|x=57
|y=168
|z=75
|title=Logic Scripted products and Script services
|}}


@Ignore                        : Ignores your current chat recording.
Contact: [[User:Flennan Roffo|Flennan Roffo]]


@Mail <address>         : Define the mail address to send to.
</div>
</div>


@Mail                            : Shows the current mail address to send to.
<div id="box">


@Subject <subject>   : Define the subject of the mail.
=='''DESCRIPTION'''==
<div style="padding: 0.5em">
SL Mail V1.2 is a simple tool to send and receive mail from within SL.
You can either compose a message yourself using chat recording, or choose a notecard from inventory to send. The Address Book functionality allows you to use short names for mail recipients stored in a notecard. The current version does not have a persisent mail address. Watch out for the upcoming next release (V1.3) which will facilitate persisent in-world mail addresses and will be enhanced with many new features (See: Requests For Change (RFC).
</div>
</div>


@Subject                      : Shows the current mail subject.
<div id="box">


@Home <address>       : Set the HOME address of the owner (mail will be forwarder to this address when offline).
=='''COMMANDS'''==
<div style="padding: 0.5em">
{| {{Prettytable}}
|-
|{{Hl2}}| '''Command'''
|{{Hl2}}| '''Description'''
|-
||'''@Help'''
||Show Help about available commands
|-
||'''@Record'''
||Starts a chat recording (for owner only).
|-
||'''@Stop'''
||Stops the chat recording without sending
|-
||'''@Continue'''
||Continues a previously stopped chat recording. Lines will be appended to you recording buffer.
|-
||'''@Show'''
||Shows the content of the recording buffer.
|-
||'''@Send'''
||If recording, stops the recording, and sends the mail (need to supply mail address and subject first!)
|-
||'''@Send <notecard>'''
||Sends the content of the notecard, instead of the chat recording.
|-
||'''@Ignore'''
||Ignores your current chat recording.
|-
||'''@Mail <address>'''
||Define the mail address to send to.
|-
||'''@Mail'''
||Shows the current mail address to send to.
|-
||'''@Subject <subject>'''
||Define the subject of the mail.
|-
||'''@Subject'''
||Shows the current mail subject.
|-
||'''@Home <address>'''
||Set the HOME address of the owner (mail will be forwarder to this address when offline).
|-
||'''@Home'''
||Show the HOME address.
|-
||'''@Info'''
||Shows the mail address the object this script is running in uses (e.g. <object-key>@lsl.secondlife.com).
|-
||'''@Read'''
||Reads the current message from the mail buffer (the mail buffer gets filled with mail you receive)
|-
||'''@Select <msg>'''
||Select a message number from the availabe messages in the mail buffer.
|-
||'''@Next'''
||Make next message the current message.
|-
||'''@Prev'''
||Make previous message the current message.
|-
||'''@First'''
||Make the first message the current message.
|-
||'''@Last'''
||Make the last message the current message.
|-
||'''@Clear'''
||Clear the mail buffer
|-
||'''@Delete'''
||Deletes only the current message
|-
||'''@Reply'''
||Starts a chat recording to reply to the current message. Mail adress and subject are filled in automatically.
|}


@Home                          : Show the HOME address.
Note:


@Info                            : Shows the mail address the object this script is running in uses (e.g. <object-key>@lsl.secondlife.com).
* Commands are case insensitive ("@read" == "@READ"). Arguments might not be case insensitive.
</div>
</div>


@Read                          : Reads the current message from the mail buffer (the mail buffer gets filled with mail you receive)
<div id="box">
=='''ADDRESS BOOK FORMAT'''==
<div style="padding: 0.5em">
A notecard of the name 'Address Book' is read during initialization which may contain Address Book items and Groups.
The notecard is split in two sections, which need to be defined in that particular order.


@Select <msg>            : Select a message number from the availabe messages in the mail buffer.
'''[ADDRESS BOOK]'''


@Next                          : Make next message the current message.
Defines the section in which you can put names for mail addresses,  like this:


@Prev                            : Make previous message the current message.
'''Name1=account@thisdomain.com'''


@First                            : Make the first message the current message.
'''Name2=user@otherdomain.com'''


@Last                            : Make the last message the current message.
etc.


@Clear                          : Clear the mail buffer
'''[GROUP]'''


@Delete                        : Deletes only the current message
Define groups of previously defined names under a new name, like for instance:
 
@Reply                          : Starts a chat recording to reply to the current message. Mail adress and subject are filled in automatically.
 
Note:
 
1) Commands are case insensitive ("@read" == "@READ"). Arguments might not be case insensitive.
 
==='''ADDRESS BOOK FORMAT'''===
 
A notecard of the name 'Address Book' is read during initialization which may contain Address Book items and Groups.
The notecard is split in two sections, which need to be defined in that particular order.
 
===='''[ADDRESS BOOK]'''====
 
Defines the section in which you can put names for mail addresses,  like this:
 
'''Name1=account@thisdomain.com'''
 
'''Name2=user@otherdomain.com'''
 
etc.
 
===='''[GROUP]'''====
 
#Define groups of previously defined names under a new name, like for instance:


'''MyGroup=Name1, Name2'''
'''MyGroup=Name1, Name2'''
Line 124: Line 214:
Notes:
Notes:


2) Names for Addreses and Groups are case insensitive (e.g. "mary"  == "MARY")
* Names for Addreses and Groups are case insensitive (e.g. "mary"  == "MARY")


3) The special address 'Self' can be used in commands where otherwise an address specification can be used. 'Self' is case insensitive (e.g. "self" == "SELF")
* The special address 'Self' can be used in commands where otherwise an address specification can be used. 'Self' is case insensitive (e.g. "self" == "SELF")


==='''LIMITATIONS'''===
</div>
</div>


<div id="box">
=='''LIMITATIONS'''==
<div style="padding: 0.5em">
The current version is limited by the following:
The current version is limited by the following:


- Chat recording lines are limited by the 255 byte limit
* Chat recording lines are limited by the 255 byte limit


- Output of lines from the mail buffer are also limited by this 255 byte limit
* Output of lines from the mail buffer are also limited by this 255 byte limit


- Incoming mail is limited to a 1000 (1024?) byte limit
* Incoming mail is limited to a 1000 (1024?) byte limit


- Outgoing mai is limited by a 4096 byte limit, which includes the standard mail header (region/location info on the first two lines)
* Outgoing mai is limited by a 4096 byte limit, which includes the standard mail header (region/location info on the first two lines)


We are making adjustments to this release to help overcome these limitations. At least the line lenght limit can be overcome by introducing a line continuation character (for example a hyphen '-') that when used at the end of a line appends the next line, so lines longer then 255 bytes are possible then.
We are making adjustments to this release to help overcome these limitations. At least the line lenght limit can be overcome by introducing a line continuation character (for example a hyphen '-') that when used at the end of a line appends the next line, so lines longer then 255 bytes are possible then.
Line 146: Line 240:


These enhancements will be made available in the next release (V1.3) of SL Mail  
These enhancements will be made available in the next release (V1.3) of SL Mail  
</div>
</div>


==='''BUGS'''===
<div id="box">
 
=='''BUGS'''==
<div style="padding: 0.5em">
Report any bugs you find in this release to Logic (IM: Flennan Roffo).
Report any bugs you find in this release to Logic (IM: Flennan Roffo).
</div>
</div>


==='''REQUEST FOR CHANGE (RFC)'''===
<div id="box">
 
=='''REQUEST FOR CHANGE (RFC)'''==
<div style="padding: 0.5em">
Do you have additional wishes for enhancement of this product?
Do you have additional wishes for enhancement of this product?
Contact Flennan Roffo and we will see if they can be implemented in the next release.
Contact Flennan Roffo and we will see if they can be implemented in the next release.
Line 168: Line 268:
* Permanent mail addresses for the SL Mail client (like: <Flennan.Roffo@somedomain.sl>)
* Permanent mail addresses for the SL Mail client (like: <Flennan.Roffo@somedomain.sl>)


Add your RFC!  
Add your RFC!
</div>
</div>


==Scripts==
<div id="box">
=='''SL MAIL SUPPORT & DEVELOPMENT'''==
<div style="padding: 0.5em">


===Script: SL Mail v1.2===
In-world join the group "SL Mail" for bug reports, feature requests, the latest SL Mail release, and notices about development issues.
</div>
</div>
 
<div id="box">
 
=Scripts=
</div>
 
<div id="box">
==Script: SL Mail v1.2==
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">
<pre>
SL Mail core module which scans for incoming mail messages and executes commands used by the owner.
<syntaxhighlight lang="lsl2">
// SL Mail V1.2.lsl
// SL Mail V1.2.lsl
// (c) 2007 Logic Scripted Products and Script Services
// (c) 2007 Logic Scripted Products and Script Services
Line 180: Line 295:


// VERSION
// VERSION
// Version: 1.1
// Version: 1.2


// HISTORY
// HISTORY
Line 224: Line 339:
// 4) Alert with an IM when online upon receiving new mail.                *DONE*
// 4) Alert with an IM when online upon receiving new mail.                *DONE*


string  SL_MAIL_VERSION = "V 1.2";
string  SL_MAIL_VERSION   = "V 1.2";
string  SL_MAIL_DATE   = " 2 sep 2007";
string  SL_MAIL_DATE       = "02 sep 2007";
integer  ErrorFlag         = FALSE;
integer  ErrorFlag         = FALSE;
list    ScriptList       = [ "SL Mail Help V1.2", "SL Mail AddressBook V1.2",  
list    ScriptList         = ["SL Mail Help V1.2", "SL Mail AddressBook V1.2",
                                 "SL Mail OnlineStatus V1.2", "SL Mail FetchNotecard V1.2" ];  
                                 "SL Mail OnlineStatus V1.2", "SL Mail FetchNotecard V1.2" ];
string  LSL_DOMAIN="lsl.secondlife.com";  
string  LSL_DOMAIN         = "lsl.secondlife.com";
integer  StatusOnline=FALSE;
integer  StatusOnline       = FALSE;


// Commands
// Commands
Line 266: Line 381:
integer Recording = FALSE;
integer Recording = FALSE;


list    ChatRecording=[];
list    ChatRecording;
integer NumLines=0;
integer NumLines;
integer Length=0;
integer Length;


list    TimeList=[];
list    TimeList;
list    AddressList=[];
list    AddressList;
list    SubjectList=[];
list    SubjectList;
list    BodyList=[];
list    BodyList;
integer  NumMessages=0;
integer  NumMessages;
integer  CurrentMessage=-1;
integer  CurrentMessage = -1;


float    MAIL_FETCH_INTERVAL=5.0;
float    MAIL_FETCH_INTERVAL = 5.0;


// Link Messages
// Link Messages
Line 305: Line 420:
integer MSG_NOTECARD_FETCHED            = 100100;
integer MSG_NOTECARD_FETCHED            = 100100;


///////////////////////////// Unix Time conversion //////////////////
// Unix Time conversion


integer DAYS_PER_YEAR        = 365;               // Non leap year
integer DAYS_PER_YEAR        = 365;// Non leap year
integer SECONDS_PER_YEAR    = 31536000;         // Non leap year
integer SECONDS_PER_YEAR    = 31536000;// Non leap year
integer SECONDS_PER_DAY      = 86400;
integer SECONDS_PER_DAY      = 86400;
integer SECONDS_PER_HOUR    = 3600;
integer SECONDS_PER_HOUR    = 3600;
integer SECONDS_PER_MINUTE  = 60;
integer SECONDS_PER_MINUTE  = 60;


////////////////////////////// LeapYear() /////////////////////////////
integer isLeapYear(integer year)
 
integer LeapYear(integer year)
{
{
     if (year % 4 == 0)
     if (year % 4 == 0)
Line 322: Line 435:
         {
         {
             if (year % 400 == 0)
             if (year % 400 == 0)
            {
                 return TRUE;
                 return 1;
        //  else
            }
                 return FALSE;
            else
            {
                 return 0;
            }
        }
        else
        {
            return 1;
         }
         }
    //  else
            return TRUE;
     }
     }
    else
//  else
    {
         return FALSE;
         return 0;
    }
}
}


////////////////////////////// DaysPerMonth() ///////////////////////////////////////
               
integer DaysPerMonth(integer year,integer month)
integer DaysPerMonth(integer year,integer month)
{
{
Line 351: Line 454:
             if (month == 2)
             if (month == 2)
             {
             {
                 if (LeapYear(year))
                 if (isLeapYear(year))
                {
                     return 29;
                     return 29;
                }
 
                else
            //  else
                {
                     return 28;
                     return 28;
                }
             }
             }
            else
        //  else
            {
                 return 30;
                 return 30;
            }
         }
         }
        else
    //  else
        {
             return 31;
             return 31;
        }
     }
     }
     else
     else
     {
     {
         if (month % 2 == 0)
         if (month % 2 == 0)
        {
             return 31;
             return 31;
        }
 
        else
    //  else
        {
             return 30;
             return 30;
        }
     }
     }
}
}
/////////////////////////// DaysPerYear() ////////////////////////////////////


integer DaysPerYear(integer year)
integer DaysPerYear(integer year)
{
{
     if (LeapYear(year))
     if (isLeapYear(year))
         return DAYS_PER_YEAR + 1;
         return DAYS_PER_YEAR + 1;
     else
     else
         return DAYS_PER_YEAR;
         return DAYS_PER_YEAR;
}
}
/////////////////////////////// Unix2DataTime() ///////////////////////////////////////


list Unix2DateTime(integer unixtime)
list Unix2DateTime(integer unixtime)
Line 401: Line 490:
     integer year  = 1970;
     integer year  = 1970;
     integer days_per_year = DaysPerYear(year);
     integer days_per_year = DaysPerYear(year);
   
 
     while (day > days_per_year)
     while (day > days_per_year)
     {
     {
Line 411: Line 500:
     integer month = 1;
     integer month = 1;
     integer days_per_month = DaysPerMonth(year,month);
     integer days_per_month = DaysPerMonth(year,month);
   
 
     while (day > days_per_month)
     while (day > days_per_month)
     {
     {
         day -= days_per_month;
         day -= days_per_month;
       
 
         if (++month > 12)
         if (++month > 12)
         {  
         {
             ++year;
             ++year;
             month = 1;
             month = 1;
         }
         }
       
 
         days_per_month = DaysPerMonth(year,month);
         days_per_month = DaysPerMonth(year,month);
     }
     }
Line 430: Line 519:
     integer minute      = second / SECONDS_PER_MINUTE;
     integer minute      = second / SECONDS_PER_MINUTE;
     second              = second % SECONDS_PER_MINUTE;
     second              = second % SECONDS_PER_MINUTE;
   
 
     return [ year, month, day, hour, minute, second ];
     return [ year, month, day, hour, minute, second ];
}
}


///////////////////////////////// MonthName() ////////////////////////////
list MonthNameList = [    "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
 
list MonthNameList = [    "JAN", "FEB", "MAR", "APR", "MAY", "JUN",  
                         "JUL", "AUG", "SEP", "OCT" , "NOV", "DEC" ];
                         "JUL", "AUG", "SEP", "OCT" , "NOV", "DEC" ];


string MonthName(integer month)
string MonthName(integer month)
{
{
     if (month >= 0 && month < 12)
     if (0 <= month < 12)
         return llList2String(MonthNameList, month);
         return llList2String(MonthNameList, month);
    else
//  else
         return "";
         return "";
}
}
///////////////////////////////// DateString() ///////////////////////////


string DateString(list timelist)
string DateString(list timelist)
{
{
     integer year      = llList2Integer(timelist,0);
     integer year      = llList2Integer(timelist, 0);
     integer month      = llList2Integer(timelist,1);
     integer month      = llList2Integer(timelist, 1);
     integer day        = llList2Integer(timelist,2);
     integer day        = llList2Integer(timelist, 2);
   
 
     return (string)day + "-" + MonthName(month - 1) + "-" + (string)year;
     return (string)day + "-" + MonthName(month - 1) + "-" + (string)year;
}
}
///////////////////////////////// TimeString() ////////////////////////////


string TimeString(list timelist)
string TimeString(list timelist)
{
{
     integer hour          = llList2Integer(timelist,3);
     integer hour          = llList2Integer(timelist, 3);
     integer minute        = llList2Integer(timelist,4);
     integer minute        = llList2Integer(timelist, 4);
     integer second        = llList2Integer(timelist,5);
     integer second        = llList2Integer(timelist, 5);
     string  hourstr    = (string)hour;
     string  hourstr    = llGetSubString("0" + (string)hour, -2, -1);
     string  minutestr  = (string)minute;
     string  minutestr  = llGetSubString("0" + (string)minute, -2, -1);
     string  secondstr  = (string)second;
     string  secondstr  = llGetSubString("0" + (string)second, -2, -1);
   
 
    if (hour < 10)        hourstr    = "0" + hourstr;
     return
    if (minute < 10)     minutestr    = "0" + minutestr;
        hourstr + ":" + minutestr + ":" + secondstr;
    if (second < 10)   secondstr    = "0" + secondstr;
     return hourstr + ":" + minutestr + ":" + secondstr;
}
}
/////////////////////////////////// SendMail() /////////////////////////////////////


SendMail(string body)
SendMail(string body)
Line 482: Line 561:
     llMessageLinked(LINK_THIS, MSG_MESSAGE_SEND, body, NULL_KEY);
     llMessageLinked(LINK_THIS, MSG_MESSAGE_SEND, body, NULL_KEY);
}
}
///////////////////////////////// SendHome() ///////////////////////////////////////


SendHome(string subject, string body)
SendHome(string subject, string body)
Line 489: Line 566:
     llMessageLinked(LINK_THIS, MSG_MESSAGE_SENDHOME, body, (key)subject);
     llMessageLinked(LINK_THIS, MSG_MESSAGE_SENDHOME, body, (key)subject);
}
}
///////////////////////////////// Show() ////////////////////////////////////////////


Show()
Show()
{
{
    integer i;
   
     llOwnerSay("Current chat recording is:");
     llOwnerSay("Current chat recording is:");
     llOwnerSay("--------------------------");
     llOwnerSay("--------------------------");


     for (i = 0; i < NumLines; ++i)
     integer i;
    do
     {
     {
         llOwnerSay(llList2String(ChatRecording,i));
         llOwnerSay(llList2String(ChatRecording, i));
     }
     }
    while (++i < NumLines);


     llOwnerSay("--------------------------");
     llOwnerSay("--------------------------");
Line 508: Line 583:
     llOwnerSay("Message length is " + (string)Length);
     llOwnerSay("Message length is " + (string)Length);
}
}
/////////////////////////////// Read() /////////////////////////////////////////


Read()
Read()
Line 527: Line 600:
     list bodylist=llParseStringKeepNulls(Body, [ "\n" ], []);
     list bodylist=llParseStringKeepNulls(Body, [ "\n" ], []);
     integer num=llGetListLength(bodylist);
     integer num=llGetListLength(bodylist);
     integer i;
     integer i;
      
     do
    for (i = 0; i < num; ++i)
     {
     {
         llOwnerSay(llList2String(bodylist,i));
         llOwnerSay(llList2String(bodylist, i));
     }
     }
      
     while (++i < num);
 
     llOwnerSay("-------------------------");
     llOwnerSay("-------------------------");
     llOwnerSay("End of message body.");
     llOwnerSay("End of message body.");
}
}
////////////////////////////////////// List ////////////////////////////////////////////


List()
List()
Line 545: Line 617:


     integer i;
     integer i;
      
     do
    for (i = 0; i < NumMessages; ++i)
     {
     {
         integer time=llList2Integer(TimeList,i);
         integer time=llList2Integer(TimeList,i);
         list timelist=Unix2DateTime(time);
         list timelist=Unix2DateTime(time);
         if (i > 0) llOwnerSay("+-+-+-+-+-+-+-+-+-+-+-+-");
         if (/* 0 < */ i) llOwnerSay("+-+-+-+-+-+-+-+-+-+-+-+-");
         llOwnerSay("Message: " + (string)i);
         llOwnerSay("Message: " + (string)i);
         llOwnerSay("Date  : " + DateString(timelist));
         llOwnerSay("Date  : " + DateString(timelist));
Line 557: Line 628:
         llOwnerSay("Subject: " + llList2String(SubjectList,i));
         llOwnerSay("Subject: " + llList2String(SubjectList,i));
     }
     }
    while (++i < NumMessages);
}
}
//////////////////////////// NotifyNewMail() /////////////////////////


NotifyNewMail(string time, string address, string subject, string body)
NotifyNewMail(string time, string address, string subject, string body)
Line 567: Line 637:
     if (StatusOnline)
     if (StatusOnline)
     {
     {
         llInstantMessage(llGetOwner(), "You received a new mail message on "  
         llInstantMessage(llGetOwner(), "You received a new mail message on "
                                     + DateString(timelist) + " at " + TimeString(timelist)
                                     + DateString(timelist) + " at " + TimeString(timelist)
                                     + " from " + address  
                                     + " from " + address
                                     + " with the subject " + subject);
                                     + " with the subject " + subject);
     }
     }
     else
     else
     {
     {
         SendHome("FW: " + subject, "Received at: " + DateString(timelist) + " " + TimeString(timelist) + "\nFrom: " +  address  
         SendHome("FW: " + subject, "Received at: " + DateString(timelist) + " " + TimeString(timelist) + "\nFrom: " +  address
                     + "\nBegin message body:\n-------------------\n" + body + "\n-------------------\nEnd message body.");
                     + "\nBegin message body:\n-------------------\n" + body + "\n-------------------\nEnd message body.");
     }
     }
}
}
///////////////////////////// ResetOtherScripts() //////////////////////


ResetOtherScripts()
ResetOtherScripts()
{
{
     string    name;
     string name;
     integer num=llGetListLength(ScriptList);
 
     integer i;
     integer i;
     integer num=llGetListLength(ScriptList);
     do
   
    for (i = 0; i < num; ++i)
     {
     {
         name = llList2String(ScriptList,i);
         name = llList2String(ScriptList, i);
       
 
         if (llGetInventoryType(name) == INVENTORY_SCRIPT)
         if (llGetInventoryType(name) == INVENTORY_SCRIPT)
         {
         {
Line 601: Line 669:
         }
         }
     }
     }
    while (++i < num);
}
}
//////////////////////////////////////
// default
//////////////////////////////////////


default
default
{
{
     ////////////////////// state_entry() /////////////////////////
     on_rez(integer start_param)
 
    {
     state_entry()
        llResetScript();
    }
 
    changed(integer perm)
    {
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
            llResetScript();
    }
 
     state_entry()
     {
     {
         llOwnerSay("Welcome to SL Mail, released under GNU software licence.");
         llOwnerSay("Welcome to SL Mail, released under GNU software licence.");
Line 618: Line 692:
         llOwnerSay("Free memory: " + (string)llGetFreeMemory());
         llOwnerSay("Free memory: " + (string)llGetFreeMemory());
         ResetOtherScripts();
         ResetOtherScripts();
       
 
         if (ErrorFlag)
         if (ErrorFlag)
             state Error;
             state Error;
     }
     }
   
 
    /////////////////////// on_rez() //////////////////////////////
   
    on_rez(integer start_param)
    {
        llResetScript();
    }
   
    /////////////////////// changed() ////////////////////////////
   
    changed(integer mask)
    {
        if (mask & CHANGED_INVENTORY)
        {
            llResetScript();
        }
    }
   
    /////////////////////// link_message() /////////////////////////
   
     link_message(integer sender, integer msgid, string message, key id)
     link_message(integer sender, integer msgid, string message, key id)
     {
     {
         if (msgid == MSG_STATUS_ONLINE)
         if (msgid == MSG_STATUS_ONLINE)
        {
             StatusOnline = TRUE;
             StatusOnline = TRUE;
        }
 
         else if (msgid == MSG_STATUS_OFFLINE)
         else if (msgid == MSG_STATUS_OFFLINE)
        {
             StatusOnline = FALSE;
             StatusOnline = FALSE;
        }
 
         else if (msgid == MSG_ADDRESSBOOK_LOADED)
         else if (msgid == MSG_ADDRESSBOOK_LOADED)
        {
             state running;
             state running;
        }
 
         else if (msgid == MSG_ADDRESSBOOK_ERROR)
         else if (msgid == MSG_ADDRESSBOOK_ERROR)
        {
             state Error;
             state Error;
        }
     }
     }
}
}
///////////////////////////////////////
// state running
//////////////////////////////////////


state running
state running
{
{
     ///////////////////////// state_entry() //////////////////////
     on_rez(integer start_param)
    {
        llResetScript();
    }
 
    changed(integer perm)
    {
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
            llResetScript();
    }


     state_entry()
     state_entry()
     {
     {
         llListen(0, "", llGetOwner(), "");
         llListen(PUBLIC_CHANNEL, "", llGetOwner(), "");
         llOwnerSay("SL mail ready.\nSend and Get mail in Second Life.\nType @HELP for more info.");
         llOwnerSay("SL mail ready.\nSend and Get mail in Second Life.\nType @HELP for more info.");
         llMessageLinked(LINK_THIS, MSG_MESSAGE_INFO, "", NULL_KEY);
         llMessageLinked(LINK_THIS, MSG_MESSAGE_INFO, "", NULL_KEY);
Line 680: Line 735:
     }
     }


    //////////////////////// on_rez() ////////////////////////////
   
    on_rez(integer start_param)
    {
        llResetScript();
    }
   
    //////////////////////// listen() ////////////////////////////
   
     listen(integer channel, string name, key id, string message)
     listen(integer channel, string name, key id, string message)
     {
     {
Line 699: Line 745:


         if (nargs > 1) argstr=llGetSubString(message, llSubStringIndex(message, " ") + 1, -1);
         if (nargs > 1) argstr=llGetSubString(message, llSubStringIndex(message, " ") + 1, -1);
           
 
         if (Recording)
         if (Recording)
         {
         {
Line 712: Line 758:
             {
             {
                 Recording = FALSE;
                 Recording = FALSE;
               
 
                 SendMail(llDumpList2String(ChatRecording, "\n"));
                 SendMail(llDumpList2String(ChatRecording, "\n"));
                 return;
                 return;
Line 763: Line 809:
             {
             {
                 string mailaddress=llStringTrim(argstr,STRING_TRIM);
                 string mailaddress=llStringTrim(argstr,STRING_TRIM);
               
 
                 if (mailaddress != "")
                 if (mailaddress != "")
                 {
                 {
Line 777: Line 823:
             {
             {
                 string mailsubject=llStringTrim(argstr,STRING_TRIM);
                 string mailsubject=llStringTrim(argstr,STRING_TRIM);
               
 
                 if (mailsubject != "")
                 if (mailsubject != "")
                 {
                 {
Line 955: Line 1,001:
                 {
                 {
                     integer msgnum = (integer)arg;
                     integer msgnum = (integer)arg;
                   
 
                     if (msgnum < 0 || msgnum >= NumMessages)
                     if (msgnum < 0 || msgnum >= NumMessages)
                     {
                     {
Line 980: Line 1,026:
                     BodyList = llDeleteSubList(BodyList,CurrentMessage,CurrentMessage);
                     BodyList = llDeleteSubList(BodyList,CurrentMessage,CurrentMessage);
                     --NumMessages;
                     --NumMessages;
                   
 
                     if (NumMessages == 0)
                     if (NumMessages == 0)
                     {
                     {
Line 1,000: Line 1,046:
                 else
                 else
                 {
                 {
                     TimeList=[];
                     TimeList = [];
                     AddressList=[];
                     AddressList = [];
                     SubjectList=[];
                     SubjectList = [];
                     BodyList=[];
                     BodyList = [];
                     NumMessages=0;
                     NumMessages = 0;
                     CurrentMessage=-1;
                     CurrentMessage = -1;
                 }
                 }
                 return;
                 return;
Line 1,047: Line 1,093:
         }
         }
     }
     }
   
 
    ///////////////////////// timer() ///////////////////////////////
   
     timer()
     timer()
     {
     {
         llGetNextEmail("", "");
         llGetNextEmail("", "");
     }
     }
   
 
    ///////////////////////// email() ///////////////////////////////
   
     email(string time, string address, string subject, string body, integer queued)
     email(string time, string address, string subject, string body, integer queued)
     {
     {
Line 1,065: Line 1,107:
         BodyList = (BodyList=[]) + BodyList + [body];
         BodyList = (BodyList=[]) + BodyList + [body];
         ++NumMessages;
         ++NumMessages;
       
 
         if (CurrentMessage == -1)
         if (CurrentMessage == -1)
         {
         {
Line 1,076: Line 1,118:
     }
     }


    ///////////////////// link_message() //////////////////////////
   
     link_message(integer sender, integer msgid, string message, key id)
     link_message(integer sender, integer msgid, string message, key id)
     {
     {
Line 1,094: Line 1,134:
     }
     }
}
}
//////////////////////////////////
// state not_running
//////////////////////////////////


state not_running
state not_running
{
{
    ///////////////////// state_entry() //////////////////////////
   
    state_entry()
    {
        llOwnerSay("SL Mailbox switched OFF. Say @ON to switch it back on.");
        llListen(0, "", llGetOwner(), "");
    }
   
    //////////////////// on_rez() ///////////////////////////////
   
     on_rez(integer start_param)
     on_rez(integer start_param)
     {
     {
         llResetScript();
         llResetScript();
     }
     }
   
 
    //////////////////// listen() ////////////////////////////////
     changed(integer perm)
   
     listen(integer chan, string name, key id, string message)
     {
     {
         string command=llToUpper(message);
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
       
            llResetScript();
    }
 
    state_entry()
    {
        llOwnerSay("SL Mailbox switched OFF. Say @ON to switch it back on.");
        llListen(PUBLIC_CHANNEL, "", llGetOwner(), "");
    }
 
    listen(integer chan, string name, key id, string message)
    {
         string command=llToUpper(message);
 
         if (command == COMMAND_ON)
         if (command == COMMAND_ON)
         {
         {
Line 1,127: Line 1,163:
         }
         }
     }
     }
   
 
    ///////////////////// link_message() //////////////////////////
   
     link_message(integer sender, integer msgid, string message, key id)
     link_message(integer sender, integer msgid, string message, key id)
     {
     {
Line 1,142: Line 1,176:
     }
     }
}
}
//////////////////////////////////
// state Error
//////////////////////////////////


state Error
state Error
{
{
     state_entry()
     on_rez(integer start_param)
     {
     {
         llOwnerSay("Error in SL Mail: not all component scripts found.");
         llResetScript();
     }
     }


     changed(integer mask)
     changed(integer perm)
     {
     {
         if (mask & CHANGED_INVENTORY)
         if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
        {
             llResetScript();
             llResetScript();
        }
     }
     }
       
 
     on_rez(integer start_param)
     state_entry()
     {
     {
         llResetScript();
         llOwnerSay("Error in SL Mail: not all component scripts found.");
     }
     }
}
}
Line 1,171: Line 1,199:
// End SL Mail V1.2.lsl
// End SL Mail V1.2.lsl
//////////////////////////////////
//////////////////////////////////
</pre>
</syntaxhighlight>
</div>
</div>
</div>


===Script: SL Mail Address Book V1.2===
<div id="box">
 
==Script: SL Mail Address Book V1.2==
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">
<pre>
Read and interpret the contents of the Address Book.
<syntaxhighlight lang="lsl2">
// SL Mail AddressBook V1.2.lsl
// SL Mail AddressBook V1.2.lsl
//
//
Line 1,876: Line 1,908:
// End SL Mail AddressBook V1.2.lsl
// End SL Mail AddressBook V1.2.lsl
/////////////////////////////////////
/////////////////////////////////////
</pre>
</syntaxhighlight>
</div>
</div>
</div>


===Script: SL Mail FetchNotecard V1.2===
<div id="box">
 
==Script: SL Mail FetchNotecard V1.2==
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">
<pre>
Fetch the contents of a notecard and return it via link message as a string.
<syntaxhighlight lang="lsl2">
// SL Mail FetchNotecard V1.2.lsl
// SL Mail FetchNotecard V1.2.lsl
//
//
Line 1,959: Line 1,995:


// End SL Mail FetchNotecard V1.2.lsl
// End SL Mail FetchNotecard V1.2.lsl
</pre>
</syntaxhighlight>
</div>
</div>
</div>
<div id="box">


===Script: SL OnlineStatus V1.2===
==Script: SL OnlineStatus V1.2==
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">
<pre>
Fetch online status of owner.
<syntaxhighlight lang="lsl2">
// SL Mail OnlineStatus V1.2.lsl
// SL Mail OnlineStatus V1.2.lsl
//
//
Line 2,021: Line 2,061:


// End SL Mail OnlineStatus V1.2.lsl
// End SL Mail OnlineStatus V1.2.lsl
</pre>
</syntaxhighlight>
</div>
</div>
</div>


===Script: SL Mail Help V1.2===
<div id="box">
 
==Script: SL Mail Help V1.2==
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">
<pre>
Display help information.
 
<syntaxhighlight lang="lsl2">
// SL Mail Help V1.2.lsl
// SL Mail Help V1.2.lsl
//
//
Line 2,081: Line 2,126:


// End SL Mail Help V1.2.lsl
// End SL Mail Help V1.2.lsl
</pre>
</syntaxhighlight>
</div>
</div>
</div>
[[Image:slmail.info.jpg]]


{{LSLC|Library}}
{{LSLC|Library}}

Latest revision as of 15:10, 7 April 2022

Updated: 10-2-2011

Second Life mail client

Send and receive email from within Second Life from and to any address.

Implemented with Address Book function (in a notecard named 'Address Book') to stores addresses by name and to compile groups of named addresses. Current version is stand-alone (no server functionality) and uses chat commands only.

BannerSLMail.jpg

URL: slmail.info (website coming soon!) url slmail.info no longer used for SL Mail, and was taken for some other purpose.

Release Notes V1.2

SL Mail V1.2 Release notes (edition 2, 4 sept 2007)

IMPORTANT NOTICE

Development of SL Mail was abandoned midyear 2008. The domain slmail.info was abandoned and is now taken by another domain holder (ISS) who has nothing to do with SL Mail.

SL Mail is considered for redevelopment under the same terms, using a version of the Serverless Key Exchange script. This allows mail clients to exchange their key adresses to other rezzed mail clients in world, and send each other info about the public name they use, and other info that could be usefull. This protocol does not use a central server.

See: Serverless Key Exchange

Future annoucements about SL Mail development will be made here.

LICENCE INFO

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

PRODUCTION & MAINTANENCE

by Logic Scripted Products and Script Services

Logicbanner1.jpg

URL: logicscripts.org (website coming soon!)

SLurl: Logic Scripted products and Script services🖈

Contact: Flennan Roffo

DESCRIPTION

SL Mail V1.2 is a simple tool to send and receive mail from within SL. You can either compose a message yourself using chat recording, or choose a notecard from inventory to send. The Address Book functionality allows you to use short names for mail recipients stored in a notecard. The current version does not have a persisent mail address. Watch out for the upcoming next release (V1.3) which will facilitate persisent in-world mail addresses and will be enhanced with many new features (See: Requests For Change (RFC).

COMMANDS

Command Description
@Help Show Help about available commands
@Record Starts a chat recording (for owner only).
@Stop Stops the chat recording without sending
@Continue Continues a previously stopped chat recording. Lines will be appended to you recording buffer.
@Show Shows the content of the recording buffer.
@Send If recording, stops the recording, and sends the mail (need to supply mail address and subject first!)
@Send <notecard> Sends the content of the notecard, instead of the chat recording.
@Ignore Ignores your current chat recording.
@Mail <address> Define the mail address to send to.
@Mail Shows the current mail address to send to.
@Subject <subject> Define the subject of the mail.
@Subject Shows the current mail subject.
@Home <address> Set the HOME address of the owner (mail will be forwarder to this address when offline).
@Home Show the HOME address.
@Info Shows the mail address the object this script is running in uses (e.g. <object-key>@lsl.secondlife.com).
@Read Reads the current message from the mail buffer (the mail buffer gets filled with mail you receive)
@Select <msg> Select a message number from the availabe messages in the mail buffer.
@Next Make next message the current message.
@Prev Make previous message the current message.
@First Make the first message the current message.
@Last Make the last message the current message.
@Clear Clear the mail buffer
@Delete Deletes only the current message
@Reply Starts a chat recording to reply to the current message. Mail adress and subject are filled in automatically.

Note:

  • Commands are case insensitive ("@read" == "@READ"). Arguments might not be case insensitive.

ADDRESS BOOK FORMAT

A notecard of the name 'Address Book' is read during initialization which may contain Address Book items and Groups. The notecard is split in two sections, which need to be defined in that particular order.

[ADDRESS BOOK]

Defines the section in which you can put names for mail addresses, like this:

Name1=account@thisdomain.com

Name2=user@otherdomain.com

etc.

[GROUP]

Define groups of previously defined names under a new name, like for instance:

MyGroup=Name1, Name2

Note that names of groups and individual mail adresses share the same namespace and must be unique.

So when you filled in your mail adresses in the Address Book, you can now use:

@Mail Name1

To send mail to <account@thisdomain.com>

or you can use

@Mail MyGroup

To send mail to <account@thisdomain.com> and <user@otherdomain.com> Mails will be sent one after another.

Notes:

  • Names for Addreses and Groups are case insensitive (e.g. "mary" == "MARY")
  • The special address 'Self' can be used in commands where otherwise an address specification can be used. 'Self' is case insensitive (e.g. "self" == "SELF")

LIMITATIONS

The current version is limited by the following:

  • Chat recording lines are limited by the 255 byte limit
  • Output of lines from the mail buffer are also limited by this 255 byte limit
  • Incoming mail is limited to a 1000 (1024?) byte limit
  • Outgoing mai is limited by a 4096 byte limit, which includes the standard mail header (region/location info on the first two lines)

We are making adjustments to this release to help overcome these limitations. At least the line lenght limit can be overcome by introducing a line continuation character (for example a hyphen '-') that when used at the end of a line appends the next line, so lines longer then 255 bytes are possible then.

To overcome the outgoing mail length limit, the mail can be broken textualy into parts, and each part can be send seperatedely, with an indication in the subject or in an introduced primary line of the mail body to indicate that the mail was split into parts. For incoming mail and at least to receive mail from other SL Mail programs, a protocol will be introduced to receive mail in parts that are joined together after receiving them to form a single mail.

These enhancements will be made available in the next release (V1.3) of SL Mail

BUGS

Report any bugs you find in this release to Logic (IM: Flennan Roffo).

REQUEST FOR CHANGE (RFC)

Do you have additional wishes for enhancement of this product? Contact Flennan Roffo and we will see if they can be implemented in the next release.

Existing RFC's are:

  • Menu support for commands.
  • Lines on incoming/outgoing mail longer than 255 bytes.
  • Send messages longer than 4096 (minus SL mail header) bytes.
  • Receive messages longer than 1024 bytes.
  • Permanent mail addresses for the SL Mail client (like: <Flennan.Roffo@somedomain.sl>)

Add your RFC!

SL MAIL SUPPORT & DEVELOPMENT

In-world join the group "SL Mail" for bug reports, feature requests, the latest SL Mail release, and notices about development issues.

Scripts

Script: SL Mail v1.2

SL Mail core module which scans for incoming mail messages and executes commands used by the owner.

// SL Mail V1.2.lsl
// (c) 2007 Logic Scripted Products and Script Services
// Flennan Roffo

// VERSION
// Version: 1.2

// HISTORY
// Date         Version     Author              Comment
// --------------------------------------------------------------------------------------
// 28 aug 2007  V 1.0       Flennan Roffo       Created
//
//  1 sep 2007  V 1.1       Flennan Roffo       @Reply empties the recording buffer.
//                                                @Send while recording ends recording
//                                                and sends the mail if a mail address
//                                                and subject are supplied.
//                                                Fixed incorrect date in Unix2DateTime.
//  3 sep 2007  V 1.2       Flennan Roffo       Notification options:
//                                              - When online, send an IM when mail is received.
//                                              - When offline, send to HOME mail adress.
//                                              Adressbook
//                                              - Read adress book from config notecard.

// LICENCE
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
// (Also available at http://www.gnu.org/copyleft/gpl.html) 

// DESCRIPTION
// Let's you record a message and sent to email and get messages and reply to messages.
// Must supply a valid mail adress and non-empty subject using chat commands.

// TODO:
// 1) Check for the maximum length of an email message (including newline and header).
// 2) Read adress book from notecard.                                    *DONE*
// 3) Send incoming mail to personal mail adress when offline.            *DONE*
// 4) Alert with an IM when online upon receiving new mail.                *DONE*

string   SL_MAIL_VERSION    = "V 1.2";
string   SL_MAIL_DATE       = "02 sep 2007";
integer  ErrorFlag          = FALSE;
list     ScriptList         = ["SL Mail Help V1.2", "SL Mail AddressBook V1.2",
                                "SL Mail OnlineStatus V1.2", "SL Mail FetchNotecard V1.2" ];
string   LSL_DOMAIN         = "lsl.secondlife.com";
integer  StatusOnline       = FALSE;

// Commands

string  COMMAND_RECORD     = "@RECORD";
string  COMMAND_CONTINUE   = "@CONTINUE";
string  COMMAND_STOP       = "@STOP";
string  COMMAND_MAIL       = "@MAIL";
string  COMMAND_SUBJECT    = "@SUBJECT";
string  COMMAND_SEND       = "@SEND";
string  COMMAND_SEE        = "@SEE";
string  COMMAND_IGNORE     = "@IGNORE";
string  COMMAND_READ       = "@READ";
string  COMMAND_DELETE     = "@DELETE";
string  COMMAND_CLEAR      = "@CLEAR";
string  COMMAND_REPLY      = "@REPLY";
string  COMMAND_NEXT       = "@NEXT";
string  COMMAND_PREV       = "@PREV";
string  COMMAND_FIRST      = "@FIRST";
string  COMMAND_LAST       = "@LAST";
string  COMMAND_LIST       = "@LIST";
string  COMMAND_SELECT     = "@SELECT";
string  COMMAND_HELP       = "@HELP";
string  COMMAND_INFO       = "@INFO";
string  COMMAND_RESET      = "@RESET";
string  COMMAND_SHOW       = "@SHOW";
string  COMMAND_ON         = "@ON";
string  COMMAND_OFF        = "@OFF";
string  COMMAND_HOME       = "@HOME";
string  COMMAND_LOAD       = "@LOAD";
string  COMMAND_ADDRESS    = "@ADDRESS";

// Recording

integer Recording = FALSE;

list    ChatRecording;
integer NumLines;
integer Length;

list     TimeList;
list     AddressList;
list     SubjectList;
list     BodyList;
integer  NumMessages;
integer  CurrentMessage = -1;

float    MAIL_FETCH_INTERVAL = 5.0;

// Link Messages

integer MSG_HELP                        = 10000;
integer MSG_STATUS_ONLINE               = 30000;
integer MSG_STATUS_OFFLINE              = 30100;

integer MSG_MESSAGE_SETADDRESS          = 90000;
integer MSG_MESSAGE_GETADDRESS          = 90050;
integer MSG_MESSAGE_SETSUBJECT          = 90100;
integer MSG_MESSAGE_GETSUBJECT          = 90150;
integer MSG_MESSAGE_SEND                = 90200;
integer MSG_MESSAGE_SENDHOME            = 90300;
integer MSG_MESSAGE_SEND_NOTECARD       = 90400;
integer MSG_MESSAGE_ADDRESSBOOK         = 90500;
integer MSG_MESSAGE_GETHOME             = 90600;
integer MSG_MESSAGE_SETHOME             = 90700;
integer MSG_MESSAGE_INFO                = 90800;

integer MSG_ADDRESSBOOK_LOADED          = 91000;
integer MSG_ADDRESSBOOK_ERROR           = 91100;

integer MSG_MESSAGE_REPLY               = 99000;

integer MSG_NOTECARD_READ               = 100000;
integer MSG_NOTECARD_FETCHED            = 100100;

// Unix Time conversion

integer DAYS_PER_YEAR        = 365;//  Non leap year
integer SECONDS_PER_YEAR     = 31536000;//  Non leap year
integer SECONDS_PER_DAY      = 86400;
integer SECONDS_PER_HOUR     = 3600;
integer SECONDS_PER_MINUTE   = 60;

integer isLeapYear(integer year)
{
    if (year % 4 == 0)
    {
        if (year % 100 == 0)
        {
            if (year % 400 == 0)
                return TRUE;
        //  else
                return FALSE;
        }
    //  else
            return TRUE;
    }
//  else
        return FALSE;
}

integer DaysPerMonth(integer year,integer month)
{
    if (month < 8)
    {
        if (month % 2 == 0)
        {
            if (month == 2)
            {
                if (isLeapYear(year))
                    return 29;

            //  else
                    return 28;
            }
        //  else
                return 30;
        }
    //  else
            return 31;
    }
    else
    {
        if (month % 2 == 0)
            return 31;

    //  else
            return 30;
    }
}

integer DaysPerYear(integer year)
{
    if (isLeapYear(year))
        return DAYS_PER_YEAR + 1;
    else
        return DAYS_PER_YEAR;
}

list Unix2DateTime(integer unixtime)
{
    integer days_since_1_1_1970     = unixtime / SECONDS_PER_DAY;
    integer day = days_since_1_1_1970 + 1;
    integer year  = 1970;
    integer days_per_year = DaysPerYear(year);

    while (day > days_per_year)
    {
        day -= days_per_year;
        ++year;
        days_per_year = DaysPerYear(year);
    }

    integer month = 1;
    integer days_per_month = DaysPerMonth(year,month);

    while (day > days_per_month)
    {
        day -= days_per_month;

        if (++month > 12)
        {
            ++year;
            month = 1;
        }

        days_per_month = DaysPerMonth(year,month);
    }

    integer seconds_since_midnight  = unixtime % SECONDS_PER_DAY;
    integer hour          = seconds_since_midnight / SECONDS_PER_HOUR;
    integer second         = seconds_since_midnight % SECONDS_PER_HOUR;
    integer minute      = second / SECONDS_PER_MINUTE;
    second               = second % SECONDS_PER_MINUTE;

    return [ year, month, day, hour, minute, second ];
}

list MonthNameList = [     "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                        "JUL", "AUG", "SEP", "OCT" , "NOV", "DEC" ];

string MonthName(integer month)
{
    if (0 <= month < 12)
        return llList2String(MonthNameList, month);
//  else
        return "";
}

string DateString(list timelist)
{
    integer year       = llList2Integer(timelist, 0);
    integer month      = llList2Integer(timelist, 1);
    integer day        = llList2Integer(timelist, 2);

    return (string)day + "-" + MonthName(month - 1) + "-" + (string)year;
}

string TimeString(list timelist)
{
    integer hour          = llList2Integer(timelist, 3);
    integer minute         = llList2Integer(timelist, 4);
    integer second         = llList2Integer(timelist, 5);
    string  hourstr     = llGetSubString("0" + (string)hour, -2, -1);
    string  minutestr   = llGetSubString("0" + (string)minute, -2, -1);
    string  secondstr   = llGetSubString("0" + (string)second, -2, -1);

    return
        hourstr + ":" + minutestr + ":" + secondstr;
}

SendMail(string body)
{
    // llOwnerSay("Sending message to '" + address + "' with subject '" + subject +"'.");
    llMessageLinked(LINK_THIS, MSG_MESSAGE_SEND, body, NULL_KEY);
}

SendHome(string subject, string body)
{
    llMessageLinked(LINK_THIS, MSG_MESSAGE_SENDHOME, body, (key)subject);
}

Show()
{
    llOwnerSay("Current chat recording is:");
    llOwnerSay("--------------------------");

    integer i;
    do
    {
        llOwnerSay(llList2String(ChatRecording, i));
    }
    while (++i < NumLines);

    llOwnerSay("--------------------------");
    llOwnerSay("Lines is " + (string)NumLines);
    llOwnerSay("Message length is " + (string)Length);
}

Read()
{
    llOwnerSay("Reading message " + (string)CurrentMessage + " from mail buffer.");
    integer time = (integer)llList2Integer(TimeList,CurrentMessage);
    list timelist=Unix2DateTime(time);
    string Address = llList2String(AddressList,CurrentMessage);
    string Subject = llList2String(SubjectList,CurrentMessage);
    string Body    = llList2String(BodyList,CurrentMessage);
    llOwnerSay("Date   : " + DateString(timelist));
    llOwnerSay("Time   : " + TimeString(timelist));
    llOwnerSay("Address: " + Address);
    llOwnerSay("Subject: " + Subject);
    llOwnerSay("Begin of message body:");
    llOwnerSay("-------------------------");
    list bodylist=llParseStringKeepNulls(Body, [ "\n" ], []);
    integer num=llGetListLength(bodylist);

    integer i;
    do
    {
        llOwnerSay(llList2String(bodylist, i));
    }
    while (++i < num);

    llOwnerSay("-------------------------");
    llOwnerSay("End of message body.");
}

List()
{
    llOwnerSay("The mail buffer contains the following messages:\n");

    integer i;
    do
    {
        integer time=llList2Integer(TimeList,i);
        list timelist=Unix2DateTime(time);
        if (/* 0 < */ i) llOwnerSay("+-+-+-+-+-+-+-+-+-+-+-+-");
        llOwnerSay("Message: " + (string)i);
        llOwnerSay("Date   : " + DateString(timelist));
        llOwnerSay("Time   : " + TimeString(timelist));
        llOwnerSay("From:    " + llList2String(AddressList,i));
        llOwnerSay("Subject: " + llList2String(SubjectList,i));
    }
    while (++i < NumMessages);
}

NotifyNewMail(string time, string address, string subject, string body)
{
    list timelist=Unix2DateTime((integer)time);

    if (StatusOnline)
    {
        llInstantMessage(llGetOwner(), "You received a new mail message on "
                                    + DateString(timelist) + " at " + TimeString(timelist)
                                    + " from " + address
                                    + " with the subject " + subject);
    }
    else
    {
        SendHome("FW: " + subject, "Received at: " + DateString(timelist) + " " + TimeString(timelist) + "\nFrom: " +  address
                    + "\nBegin message body:\n-------------------\n" + body + "\n-------------------\nEnd message body.");
    }
}

ResetOtherScripts()
{
    string name;
    integer num=llGetListLength(ScriptList);

    integer i;
    do
    {
        name = llList2String(ScriptList, i);

        if (llGetInventoryType(name) == INVENTORY_SCRIPT)
        {
            llResetOtherScript(name);
        }
        else
        {
            llOwnerSay("Error - script '" + name + "' not found.");
            ErrorFlag = TRUE;
        }
    }
    while (++i < num);
}

default
{
    on_rez(integer start_param)
    {
        llResetScript();
    }

    changed(integer perm)
    {
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
            llResetScript();
    }

    state_entry()
    {
        llOwnerSay("Welcome to SL Mail, released under GNU software licence.");
        llOwnerSay("Version " + SL_MAIL_VERSION + " " + SL_MAIL_DATE);
        llOwnerSay("Produced by Logic Scripted Products & Product Services");
        llOwnerSay("Free memory: " + (string)llGetFreeMemory());
        ResetOtherScripts();

        if (ErrorFlag)
            state Error;
    }

    link_message(integer sender, integer msgid, string message, key id)
    {
        if (msgid == MSG_STATUS_ONLINE)
            StatusOnline = TRUE;

        else if (msgid == MSG_STATUS_OFFLINE)
            StatusOnline = FALSE;

        else if (msgid == MSG_ADDRESSBOOK_LOADED)
            state running;

        else if (msgid == MSG_ADDRESSBOOK_ERROR)
            state Error;
    }
}

state running
{
    on_rez(integer start_param)
    {
        llResetScript();
    }

    changed(integer perm)
    {
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
            llResetScript();
    }

    state_entry()
    {
        llListen(PUBLIC_CHANNEL, "", llGetOwner(), "");
        llOwnerSay("SL mail ready.\nSend and Get mail in Second Life.\nType @HELP for more info.");
        llMessageLinked(LINK_THIS, MSG_MESSAGE_INFO, "", NULL_KEY);
        llGetNextEmail("", "");
        llSetTimerEvent(MAIL_FETCH_INTERVAL);
    }

    listen(integer channel, string name, key id, string message)
    {
        string trimmessage=llStringTrim(message,STRING_TRIM);
        list   parse = llParseStringKeepNulls(trimmessage, [ " "], []);
        string command = llToUpper(llList2String(parse,0));
        string arg     = llStringTrim(llList2String(parse,1),STRING_TRIM);
        integer nargs  = llGetListLength(parse);
        string argstr="";

        if (nargs > 1) argstr=llGetSubString(message, llSubStringIndex(message, " ") + 1, -1);

        if (Recording)
        {
            if (command == COMMAND_STOP)
            {
                llOwnerSay("Recording stopped. Type @SHOW to see the message you have entered,"
                            + " or @SEND to send it.");
                Recording = FALSE;
                return;
            }
            if (command == COMMAND_SEND)
            {
                Recording = FALSE;

                SendMail(llDumpList2String(ChatRecording, "\n"));
                return;
            }
            else
            {
                ChatRecording = (ChatRecording=[]) + ChatRecording + [ message ];
                ++NumLines;
                Length += llStringLength(message) + 2;
                return;
            }
        }
        else
        {
            if (command == COMMAND_RECORD)
            {
                llOwnerSay("Start recording your message. Type @STOP to end recording.");
                ChatRecording = [];
                NumLines=0;
                Length=0;
                Recording = TRUE;
                return;
            }
            if (command == COMMAND_CONTINUE)
            {
                llOwnerSay("Continue recording your message. Type @STOP to end recording.");
                Recording = TRUE;
                return;
            }
            if (command == COMMAND_REPLY)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    string address=llList2String(AddressList,CurrentMessage);
                    string subject="Re: " + llList2String(SubjectList,CurrentMessage);
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_SETADDRESS, address, NULL_KEY);
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_SETSUBJECT, subject, NULL_KEY);
                    ChatRecording = [];
                    NumLines=0;
                    Length=0;
                    Recording = TRUE;
                }
                return;
            }
            if (command == COMMAND_MAIL)
            {
                string mailaddress=llStringTrim(argstr,STRING_TRIM);

                if (mailaddress != "")
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_SETADDRESS, mailaddress, NULL_KEY);
                }
                else
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_GETADDRESS, "", NULL_KEY);
                }
                return;
            }
            if (command == COMMAND_SUBJECT)
            {
                string mailsubject=llStringTrim(argstr,STRING_TRIM);

                if (mailsubject != "")
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_SETSUBJECT, mailsubject, NULL_KEY);
                }
                else
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_GETSUBJECT, "", NULL_KEY);
                }
                return;
            }
            if (command == COMMAND_STOP)
            {
                llOwnerSay("Not recording.");
                return;
            }
            if (command == COMMAND_SEND)
            {
                if (nargs == 1)
                {
                    if (NumLines == 0)
                    {
                        llOwnerSay("No lines recorded.");
                    }
                    else
                    {
                        SendMail(llDumpList2String(ChatRecording, "\n"));
                    }
                }
                else
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_SEND_NOTECARD, argstr, NULL_KEY);
                }
                return;
            }
            if (command == COMMAND_IGNORE)
            {
                if (NumLines == 0)
                {
                    llOwnerSay("No lines recorded.");
                }
                else
                {
                    ChatRecording = [];
                    NumLines = 0;
                    Length = 0;
                }
                return;
            }
            if (command == COMMAND_SHOW)
            {
                if (NumLines == 0)
                {
                    llOwnerSay("No lines recorded.");
                }
                else
                {
                    Show();
                }
                return;
            }
            if (command == COMMAND_READ)
            {
                if (NumMessages == 0)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    Read();
                }
                return;
            }
            if (command == COMMAND_NEXT)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    if (CurrentMessage == NumMessages - 1)
                    {
                        llOwnerSay("Already at last message.");
                    }
                    else
                    {
                        ++CurrentMessage;
                        Read();
                    }
                }
                return;
            }
            if (command == COMMAND_PREV)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    if (CurrentMessage == 0)
                    {
                        llOwnerSay("Already at first message.");
                    }
                    else
                    {
                        --CurrentMessage;
                        Read();
                    }
                }
                return;
            }
            if (command == COMMAND_FIRST)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    if (CurrentMessage == 0)
                    {
                        llOwnerSay("Already at first message.");
                    }
                    else
                    {
                        CurrentMessage = 0;
                        Read();
                    }
                }
                return;
            }
            if (command == COMMAND_LAST)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    if (CurrentMessage == NumMessages - 1)
                    {
                        llOwnerSay("Already at last message.");
                    }
                    else
                    {
                        CurrentMessage = NumMessages - 1;
                        Read();
                    }
                }
                return;
            }
            if (command == COMMAND_LIST)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    List();
                }
                return;
            }
            if (command == COMMAND_SELECT)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else if (nargs == 1)
                {
                    llOwnerSay("Need to supply a message number.");
                }
                else
                {
                    integer msgnum = (integer)arg;

                    if (msgnum < 0 || msgnum >= NumMessages)
                    {
                        llOwnerSay("Message number " + arg + " not in mail buffer.");
                    }
                    else
                    {
                        CurrentMessage = msgnum;
                    }
                }
                return;
            }
            if (command == COMMAND_DELETE)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    TimeList = llDeleteSubList(TimeList,CurrentMessage,CurrentMessage);
                    AddressList = llDeleteSubList(AddressList,CurrentMessage,CurrentMessage);
                    SubjectList = llDeleteSubList(SubjectList,CurrentMessage,CurrentMessage);
                    BodyList = llDeleteSubList(BodyList,CurrentMessage,CurrentMessage);
                    --NumMessages;

                    if (NumMessages == 0)
                    {
                        CurrentMessage = -1;
                    }
                    else if (CurrentMessage >= NumMessages)
                    {
                        CurrentMessage = NumMessages - 1;
                    }
                }
                return;
            }
            if (command == COMMAND_CLEAR)
            {
                if (CurrentMessage == -1)
                {
                    llOwnerSay("The mail buffer is empty.");
                }
                else
                {
                    TimeList = [];
                    AddressList = [];
                    SubjectList = [];
                    BodyList = [];
                    NumMessages = 0;
                    CurrentMessage = -1;
                }
                return;
            }
            if (command == COMMAND_HELP)
            {
                llMessageLinked(LINK_THIS, MSG_HELP, arg, NULL_KEY);
                return;
            }
            if (command == COMMAND_INFO)
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_INFO, "", NULL_KEY);
                return;
            }
            if (command == COMMAND_SHOW)
            {
                Show();
                return;
            }
            if (command == COMMAND_RESET)
            {
                llResetScript();
                return;
            }
            if (command == COMMAND_OFF)
            {
                state not_running;
            }
            if (command == COMMAND_HOME)
            {
                if (nargs==1)
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_GETHOME, "", NULL_KEY);
                }
                else
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_SETHOME, argstr, NULL_KEY);
                }
                return;
            }
        }
    }

    timer()
    {
        llGetNextEmail("", "");
    }

    email(string time, string address, string subject, string body, integer queued)
    {
        NotifyNewMail(time, address, subject, body);
        TimeList = (TimeList=[]) + TimeList + [time];
        AddressList = (AddressList=[]) + AddressList + [address];
        SubjectList = (SubjectList=[]) + SubjectList + [subject];
        BodyList = (BodyList=[]) + BodyList + [body];
        ++NumMessages;

        if (CurrentMessage == -1)
        {
            CurrentMessage = 0;
        }
        else
        {
            CurrentMessage = NumMessages - 1;
        }
    }

    link_message(integer sender, integer msgid, string message, key id)
    {
        if (msgid == MSG_STATUS_ONLINE)
        {
            StatusOnline = TRUE;
        }
        else if (msgid == MSG_STATUS_OFFLINE)
        {
            StatusOnline = FALSE;
        }
        else if (msgid == MSG_MESSAGE_REPLY)
        {
            llOwnerSay(message);
        }
    }
}

state not_running
{
    on_rez(integer start_param)
    {
        llResetScript();
    }

    changed(integer perm)
    {
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
            llResetScript();
    }

    state_entry()
    {
        llOwnerSay("SL Mailbox switched OFF. Say @ON to switch it back on.");
        llListen(PUBLIC_CHANNEL, "", llGetOwner(), "");
    }

    listen(integer chan, string name, key id, string message)
    {
        string command=llToUpper(message);

        if (command == COMMAND_ON)
        {
            state running;
        }
    }

    link_message(integer sender, integer msgid, string message, key id)
    {
        if (msgid == MSG_STATUS_ONLINE)
        {
            StatusOnline = TRUE;
        }
        else if (msgid == MSG_STATUS_OFFLINE)
        {
            StatusOnline = FALSE;
        }
    }
}

state Error
{
    on_rez(integer start_param)
    {
        llResetScript();
    }

    changed(integer perm)
    {
        if (perm & (CHANGED_OWNER | CHANGED_INVENTORY))
            llResetScript();
    }

    state_entry()
    {
        llOwnerSay("Error in SL Mail: not all component scripts found.");
    }
}

//////////////////////////////////
// End SL Mail V1.2.lsl
//////////////////////////////////

Script: SL Mail Address Book V1.2

Read and interpret the contents of the Address Book.

// SL Mail AddressBook V1.2.lsl
//
// DESCRIPTION
// 
//  Read the Address Book from a notecard named "Address Book".
//
//  The Address Book has the following format:
//  
//  [ADDRESS BOOK]
//  Name1=username@yourdomain.com
//  Name2=otheruser@otherdomain.com
//  ...
//  [GROUP]
//  group1=Name1, Name2
//  ...
//
//  Notes:
//  1. Group and Name share the same namespace, and must be unique.
//  2. It is allowed to have a name in the addressbook with multiple addresses, seperated by comma's.
// 
/////////////////////////////////////////////////

string  CurrentMailAddress = "";
string  CurrentMailSubject = "";
integer StatusOnline = FALSE;

// Fetch or Send notecard

string  Notecard2Fetch="";

// Address Book Names

list    AddressBookList=[];
list    MailAddressList=[];

string  STRING_HOME="HOME";
string  STRING_SELF="SELF";

// Address Book Groups

list   GroupList=[];
list   GroupNameList=[];

string LSL_DOMAIN = "lsl.secondlife.com";

// Read Address Book notecard

string  ADDRESSBOOK_NOTECARD="Address Book";
integer Line=0;
key     reqAddressBook=NULL_KEY;
float   TIMEOUT_INTERVAL=5.0;

integer Section=0;
integer SECTION_ADDRESS_BOOK=1;
integer SECTION_GROUP=2;
string  STRING_ADDRESS_BOOK="ADDRESS BOOK";
string  STRING_GROUP="GROUP";

// Links messages

integer MSG_MESSAGE_SETADDRESS          = 90000;
integer MSG_MESSAGE_GETADDRESS          = 90050;
integer MSG_MESSAGE_SETSUBJECT          = 90100;
integer MSG_MESSAGE_GETSUBJECT          = 90150;
integer MSG_MESSAGE_SEND                = 90200;
integer MSG_MESSAGE_SENDHOME            = 90300;
integer MSG_MESSAGE_SEND_NOTECARD       = 90400;
integer MSG_MESSAGE_ADDRESSBOOK         = 90500;
integer MSG_MESSAGE_GETHOME             = 90600;
integer MSG_MESSAGE_SETHOME             = 90700;
integer MSG_MESSAGE_INFO                = 90800;
integer MSG_MESSAGE_NOTIFY              = 90900;

integer MSG_ADDRESSBOOK_LOADED            = 91000;
integer MSG_ADDRESSBOOK_ERROR            = 91100;

integer MSG_MESSAGE_REPLY               = 99000;

integer MSG_NOTECARD_READ               = 100000;
integer MSG_NOTECARD_FETCHED            = 100100;

///////////////////////////// GetSelfAddress() ///////////////////////

string GetSelfAddress()
{
    return (string)llGetKey() + "@" + LSL_DOMAIN;
}

///////////////////////////// Info() /////////////////////////////////

Info()
{
    llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "The mail address for this object is: " 
                    + GetSelfAddress() + ".", NULL_KEY);
}

///////////////////////////////// IsValidKeyFormat() //////////////////////

integer IsValidKeyFormat(string str)
{
    string keychars = "0123456789abcdef";

    if (llStringLength(str) != 36)
        return FALSE;
        
    if( (llGetSubString( str, 8, 8 )   != "-" ||
        llGetSubString( str, 13, 13 )  != "-" ||
        llGetSubString( str, 18, 18 )  != "-" ||
        llGetSubString( str, 23, 23 )  != "-" ) )
        return FALSE;

    integer i;
    
    for (i = 0; i < 8; ++i)
    {
        if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
            return FALSE;
    }

    for (i = 9; i < 13; ++i)
    {
        if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
            return FALSE;
    }

    for (i = 14; i < 18; ++i)
    {
        if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
            return FALSE;
    }

    for (i = 19; i < 23; ++i)
    {
        if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
            return FALSE;
    }

    for (i = 24; i < 36; ++i)
    {
        if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
            return FALSE;
    }

    return TRUE;
}

///////////////////////////////// ValidMailAddress() ////////////////////////

integer ValidMailAddress(string arg)
{
    string adress = llToLower(llStringTrim(arg,STRING_TRIM));
    
    if (adress == "")
        return FALSE;
        
    list parse = llParseStringKeepNulls(adress, [ "@" ], []);
    string account = llList2String(parse,0);
    string domain  = llList2String(parse,1);
    
    if (llGetListLength(parse) != 2 || llStringLength(account) == 0 || llStringLength(domain) == 0)
        return FALSE;
    
    if (domain == LSL_DOMAIN)
    {
        return IsValidKeyFormat(account);
    }
    else
    {
        list parsedomain = llParseString2List(domain, [ "." ], []);
    
        if (llGetListLength(parsedomain) < 2)
            return FALSE;
        
        integer num = llGetListLength(parsedomain);
        integer i;
        
        for (i = 0; i < num; ++i)
        {
            if (llStringLength(llList2String(parsedomain,i)) < 2)
            {
                return FALSE;
            }
        }
    }
    
    return TRUE;
}

/////////////////////////////////// GetAddressStr() ///////////////////

string GetAndCheckAddressStr(string address)
{
    list addresslist = llCSV2List(address);
    integer num=llGetListLength(addresslist);
    integer i;
    string addressstr="";
    string curaddress="";
    
    for (i = 0; i < num; ++i)
    {
        curaddress=llList2String(addresslist,i);
        
        if (ValidMailAddress(curaddress))
        {
            addressstr = (addressstr="") + addressstr + curaddress + ",";
        }
        else
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid address string: '" 
                            + curaddress + "'.", NULL_KEY);
        }
    }
    
    return llGetSubString(addressstr, 0, -2);
}

///////////////////////////// ProcessLine() ////////////////////////////

ProcessLine(string rawline)
{
    string line=llStringTrim(rawline,STRING_TRIM);
    string first=llGetSubString(line,0,0);
    string last=llGetSubString(line,-1,-1);
    
    // Ignore a blank line or comment line
    if (line == "" || first == "#")
        return;

    // Check for the right section
    
    if (first == "[" && last == "]")
    {
        string sectionstr=llToUpper(llStringTrim(llGetSubString(line,1,-2),STRING_TRIM));
        
        if (sectionstr == STRING_ADDRESS_BOOK)
        {
            Section = SECTION_ADDRESS_BOOK;
        }
        else if (sectionstr == STRING_GROUP)
        {
            if (Section == 0)
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid syntax in '" + ADDRESSBOOK_NOTECARD 
                        + "' at line " + (string)(Line + 1) 
                        + ". Group section defined before Address Book section.", NULL_KEY);
            }
            else
            {
                Section = SECTION_GROUP;
            }
        }
        else
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid section header '" + sectionstr 
                        + "' in " + ADDRESSBOOK_NOTECARD + " at line " + (string)(Line + 1) + ".", NULL_KEY);
        }
        
        return;
    }
    
    // Must be in a section
    
    if (Section == 0)
    {
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid syntax in '" + ADDRESSBOOK_NOTECARD 
                        + "' at line " + (string)(Line + 1) + ". No section header.", NULL_KEY);
        return;
    }
    
    list     parse=llParseString2List(line, [ "=" ], []);
    integer  num=llGetListLength(parse);
    
    // Format must be correct
    
    if (num != 2)
    {
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid syntax in '" + ADDRESSBOOK_NOTECARD 
                        + "' at line " +  (string)(Line+1) + ":\n" + rawline, NULL_KEY);
        return;
    }
    
    if (Section == SECTION_ADDRESS_BOOK)
    {
        string name=llStringTrim(llList2String(parse,0),STRING_TRIM);
        
        if (llToUpper(name) == STRING_SELF)
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid address book entry '" + name 
                            + "'. Reserved word in '" + ADDRESSBOOK_NOTECARD 
                        + "' at line " + (string)(Line + 1) + ".", NULL_KEY);
        }
        else if (llListFindList(AddressBookList, [ name ]) == -1)
        {
            string address=llStringTrim(llList2String(parse,1),STRING_TRIM);
            string addressstr=GetAndCheckAddressStr(address);
            
            if (addressstr != "")
            {
                AddressBookList     = (AddressBookList=[])     + AddressBookList     + [ name ];
                MailAddressList     = (MailAddressList=[])     + MailAddressList     + [ addressstr ];
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "No valid mail address for '" + name 
                    + "'. Entry skipped in '" + ADDRESSBOOK_NOTECARD + "' at line " + (string)(Line + 1) 
                    + ".", NULL_KEY);
            }
        }
        else
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Name '" + name 
                + "' already defined. Entry skipped in '" + ADDRESSBOOK_NOTECARD + "' at line " 
                + (string)(Line + 1) + ".", NULL_KEY);
        }
    }
    else // Group
    {
        string group=llStringTrim(llList2String(parse,0),STRING_TRIM);
        
        if (llToUpper(group) == STRING_SELF)
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Invalid address book entry '" + group 
                            + "'. Reserved word in '" + ADDRESSBOOK_NOTECARD 
                        + "' at line " + (string)(Line + 1) + ".", NULL_KEY);
        }
        else if (llListFindList(AddressBookList, [ group ]) == -1)
        {
            if (llListFindList(GroupList, [ group ]) == -1)
            {
                string names=llStringTrim(llList2String(parse,1),STRING_TRIM);
                list   namelist=llCSV2List(names);
                integer num=llGetListLength(namelist);
                integer i;
                integer index;
                string  name;
                string  namestr="";
        
                for (i = 0; i < num; ++i)
                {
                    name=llList2String(namelist,i);
                    index=llListFindList(AddressBookList, [ name ]);
            
                    if (index == -1)
                    {
                        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Name '" + name + "' defined in Group '" 
                                + group + "' not found in '" + ADDRESSBOOK_NOTECARD + "' at line " 
                                + (string)(Line + 1) + ".", NULL_KEY); 
                    }
                    else
                    {
                        namestr = (namestr="") + namestr + name + ",";
                    }
                }

                if (namestr != "")
                {    
                    namestr=llGetSubString(namestr,0,-2);
                    GroupList         = (GroupList=[])         + GroupList     + [ group ];
                    GroupNameList     = (GroupNameList=[])    + GroupNameList + [ namestr ];
                }
                else
                {
                    llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Group '" + group 
                        + "' has no valid names. Entry skipped in '" + ADDRESSBOOK_NOTECARD + "' at line " 
                        + (string)(Line + 1) + ".", NULL_KEY);
                }
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Group '" + group 
                    + "' already defined in Group. Entry skipped in '" + ADDRESSBOOK_NOTECARD + "' at line " 
                    + (string)(Line + 1) + ".", NULL_KEY);
            }
        }
        else
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Group '" + group
                + "' already defined in Address Book. Entry skipped in '" + ADDRESSBOOK_NOTECARD + "' at line " 
                + (string)(Line + 1) + ".", NULL_KEY);            
        }
    }
}

//////////////////////////////// GetGroupAddressList ///////////////

string GetGroupAddressList(string group)
{
    integer index=llListFindList(GroupList, [ group ]);
    integer i;
    integer num=llGetListLength(GroupList);
    string  grouplist="";
    
    if (index == -1)
    {
        return "";
    }
    else
    {
        for (i = index; i < num; ++i)
        {
            if (llList2String(GroupList,i) == group)
            {
                grouplist = (grouplist="") + grouplist + llList2String(GroupNameList,i) + ",";
            }
        }
        
        if (grouplist != "") grouplist=llGetSubString(grouplist,0,-2);   // remove last ","
        return grouplist;
    }
}

//////////////////////////////// SetMailAddress() //////////////////

SetMailAddress(string address)
{
    integer index=llListFindList(AddressBookList, [ address ]);
    
    if (index == -1)
    {
        index=llListFindList(GroupList, [ address ]);
        
        if (index == -1)
        {
            if (llToUpper(address) == STRING_SELF)
            {
                CurrentMailAddress = GetSelfAddress();
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail address set to: " 
                                + CurrentMailAddress, NULL_KEY);
            }
            else if(ValidMailAddress(address))
            {
                CurrentMailAddress = address;
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail address set to: " 
                                + CurrentMailAddress, NULL_KEY);
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Mail adress " + address 
                            + " : not found in adress book or invalid format.", NULL_KEY);
            }
        }
        else
        {
            CurrentMailAddress = GetGroupAddressList(address);
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail subject set to: " 
                            + CurrentMailAddress, NULL_KEY);
        }
    }
    else
    {
        CurrentMailAddress = llList2String(MailAddressList,index);
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail address set to: " 
                        + CurrentMailAddress, NULL_KEY);
    }
}

/////////////////////////////////// SendMail() ///////////////////////

SendMail(string message)
{
    if (CurrentMailAddress == "")
    {
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "SendMail: No mail address defined.", NULL_KEY);
    }
    else if (CurrentMailSubject == "")
    {
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "SendMail: No subject defined.", NULL_KEY);
    }
    else
    {
        list mailaddresslist=llCSV2List(CurrentMailAddress);
        integer num=llGetListLength(mailaddresslist);
        integer i;
        string address;
        
        for (i = 0; i < num; ++i)
        {
            address=llList2String(mailaddresslist,i);
            llEmail(address, CurrentMailSubject, message);
        }
    }
}

/////////////////////////////////// SendMailHome() ////////////////////

SendMailHome(string message, string subject)
{
    integer index=llListFindList(AddressBookList, [ STRING_HOME ]);
    
    if (index == -1)
    {
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, STRING_HOME 
                + " not set. Set your home adress with @HOME <address>.", NULL_KEY);
    }
    else
    {
        string homeaddress=llList2String(AddressBookList,index);
        list   homelist=llCSV2List(homeaddress);
        integer num=llGetListLength(homelist);
        integer i;
        string address;
        
        for (i = 0; i < num; ++i)
        {
            address=llList2String(homelist,i);
            llEmail(address, subject, message);
        }
    }
}

/////////////////////////////////// GetHomeAddress() //////////////////

string GetHomeAddress()
{
    integer index=llListFindList(AddressBookList, [ STRING_HOME ]);
    
    if (index == -1)
    {
        return "not set";
    }
    else
    {
        return llList2String(MailAddressList,index);
    }
}

/////////////////////////////////// SetHomeAddress() //////////////////

SetHomeAddress(string address)
{
    integer index=llListFindList(AddressBookList, [ STRING_HOME ]);
    
    if (index == -1)
    {
        AddressBookList     = (AddressBookList=[]) + [ STRING_HOME ] + AddressBookList;
        MailAddressList     = (MailAddressList=[]) + [ GetAndCheckAddressStr(address) ]  + MailAddressList;
    }
    else
    {
        MailAddressList = llListReplaceList(MailAddressList, [ GetAndCheckAddressStr(address) ], index, index);
    }
}

////////////////////////////////////
// default
////////////////////////////////////

default
{
    ///////////////////// state_entry() //////////////////
    
    state_entry()
    {
        Line=0;
        Section=0;
        llSetTimerEvent(TIMEOUT_INTERVAL);
        reqAddressBook=llGetNotecardLine(ADDRESSBOOK_NOTECARD, 0);
    }
    
    ///////////////////// dataserver() ////////////////////
    
    dataserver(key id, string data)
    {
        if (id == reqAddressBook)
        {
            reqAddressBook = NULL_KEY;
            
            if (data != EOF)
            {
                ProcessLine(data);
                reqAddressBook=llGetNotecardLine(ADDRESSBOOK_NOTECARD, ++Line);
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_ADDRESSBOOK_LOADED, "", NULL_KEY);
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Address Book loaded.", NULL_KEY);
                state running;
            }
        }
    }
    
    //////////////////////// timer() ///////////////////////////
    
    timer()
    {
        llMessageLinked(LINK_THIS, MSG_ADDRESSBOOK_ERROR, "", NULL_KEY);
        llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Could not read Address Book: " 
                            + ADDRESSBOOK_NOTECARD + ".", NULL_KEY);
        llSetTimerEvent(0.0);
    }
}

////////////////////////////////////////////
// state running
////////////////////////////////////////////

state running
{
    state_entry()
    {
        llMessageLinked(LINK_THIS, MSG_ADDRESSBOOK_LOADED, "", NULL_KEY);
    }
    
    ///////////////////////// link_message() ////////////////////
    
    link_message(integer sender, integer msgid, string message, key id)
    {
        if (msgid == MSG_MESSAGE_GETSUBJECT)
        {
            if (CurrentMailSubject == "")
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail subject is empty.", NULL_KEY);
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail subject is: " 
                            + CurrentMailSubject, NULL_KEY);
            }
        }
        else if (msgid == MSG_MESSAGE_SETSUBJECT)
        {
            CurrentMailSubject = message;
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail subject set to: " 
                            + CurrentMailSubject, NULL_KEY);
        }
        else if (msgid == MSG_MESSAGE_GETADDRESS)
        {
            if (CurrentMailAddress == "")
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail address is empty.", NULL_KEY);        
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Current mail address is: " 
                            + CurrentMailAddress, NULL_KEY);        
            }
        }
        else if (msgid == MSG_MESSAGE_SETADDRESS)
        {
            SetMailAddress(message);
        }
        else if (msgid == MSG_MESSAGE_SEND)
        {
            if (message == "")
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Body of message to send is empty. " 
                                +  "No mail is send.", NULL_KEY);
            }
            else
            {
                SendMail(message);
            }
        }
        else if (msgid == MSG_MESSAGE_SENDHOME)
        {
            SendMailHome(message, (string)id);
        }
        else if (msgid == MSG_MESSAGE_SEND_NOTECARD)
        {
            if (llGetInventoryType(message) == INVENTORY_NOTECARD)
            {
                Notecard2Fetch = message;                
                llMessageLinked(LINK_THIS, MSG_NOTECARD_READ, message, NULL_KEY);
            }
            else
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Notecard " + message + " not found.", NULL_KEY);
            }
        }
        else if (msgid == MSG_NOTECARD_FETCHED)
        {
            if (message == "")
            {
                llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Notecard " + Notecard2Fetch 
                            + " was empty or could not be read.", NULL_KEY);
            }
            else
            {
                SendMail(message);
            }
        }
        else if (msgid == MSG_MESSAGE_GETHOME)
        {
            llMessageLinked(LINK_THIS, MSG_MESSAGE_REPLY, "Home address is: " + GetHomeAddress() + ".", NULL_KEY);
        }
        else if (msgid == MSG_MESSAGE_SETHOME)
        {
            SetHomeAddress(message);
        }
        else if (msgid == MSG_MESSAGE_INFO)
        {
            Info();
        }
    }
}

/////////////////////////////////////
// End SL Mail AddressBook V1.2.lsl
/////////////////////////////////////

Script: SL Mail FetchNotecard V1.2

Fetch the contents of a notecard and return it via link message as a string.

// SL Mail FetchNotecard V1.2.lsl
//
//

// Notecard

integer Line=0;
string  Notecard2Fetch="";
key     reqNotecard=NULL_KEY;
float   TIMEOUT_INTERVAL=10.0;
integer Fetched=FALSE;

// Lines

list LineList=[];

// Link messages

integer MSG_NOTECARD_READ               = 100000;
integer MSG_NOTECARD_FETCHED            = 100100;

///////////////////////////////////////////
// default
//////////////////////////////////////////

default
{
    /////////////////////// dataserver() //////////////////////////////
    
    dataserver(key id, string data)
    {
        if (id == reqNotecard)
        {
            reqNotecard=NULL_KEY;
            
            if (data != EOF)
            {
                LineList = (LineList=[]) + LineList + [ data ];
                reqNotecard = llGetNotecardLine(Notecard2Fetch, ++Line);
            }
            else
            {
                Fetched=TRUE;
                llMessageLinked(LINK_THIS, MSG_NOTECARD_FETCHED, llDumpList2String(LineList, "\n"), NULL_KEY);
                llSetTimerEvent(0.0);
            }
        }
    }

    /////////////////////// timer() ///////////////////////////////////
    
    timer()
    {
        if (!Fetched)
        {
            llMessageLinked(LINK_THIS, MSG_NOTECARD_FETCHED, "", NULL_KEY);
            llSetTimerEvent(0.0);
        }
    }
    
    /////////////////////// link_message() ////////////////////////////
    
    link_message(integer send, integer msgid, string message, key id)
    {
        if (msgid == MSG_NOTECARD_READ)
        {
            Line=0;
            Notecard2Fetch=message;
            LineList=[];
            Fetched=FALSE;
            reqNotecard=llGetNotecardLine(Notecard2Fetch, Line);
            llSetTimerEvent(TIMEOUT_INTERVAL);
        }
    }
}

// End SL Mail FetchNotecard V1.2.lsl

Script: SL OnlineStatus V1.2

Fetch online status of owner.

// SL Mail OnlineStatus V1.2.lsl
//
//

// Online status

key     reqOnlineStatus=NULL_KEY;
float   STATUS_INTERVAL=2.0;

// Link messages

integer MSG_STATUS_ONLINE       = 30000;
integer MS_STATUS_OFFLINE       = 30100;

////////////////////////////////
// default
////////////////////////////////

default
{
    ///////////////////////// state_entry() /////////////////////////
    
    state_entry()
    {
        reqOnlineStatus=llRequestAgentData(llGetOwner(), DATA_ONLINE);
        llSetTimerEvent(STATUS_INTERVAL);
    }
    
    ///////////////////////// dataserver() //////////////////////////
    
    dataserver(key id, string data)
    {
        if (id == reqOnlineStatus)
        {
            reqOnlineStatus = NULL_KEY;
        
            if (data=="1")
            {
                llMessageLinked(LINK_THIS, MSG_STATUS_ONLINE, "", NULL_KEY);
            }
            else if (data=="0")
            {
                llMessageLinked(LINK_THIS, MS_STATUS_OFFLINE, "", NULL_KEY);
            }
        }
    }
    
    ///////////////////////// timer() ///////////////////////////////
    
    timer()
    {
        reqOnlineStatus = llRequestAgentData(llGetOwner(), DATA_ONLINE);
    }    
}

// End SL Mail OnlineStatus V1.2.lsl

Script: SL Mail Help V1.2

Display help information.

// SL Mail Help V1.2.lsl
//
//

integer MSG_HELP    = 10000;

///////////////////////////////// Help() ///////////////////////////////////////

Help()
{
    llOwnerSay("HELP for SL Mail V1.2.");
    llOwnerSay("COMMANDS:");
    llOwnerSay("@HELP               - displays help info.");
    llOwnerSay("@INFO               - display mailaddress of this object.");
    llOwnerSay("@RECORD             - start recording.");
    llOwnerSay("@CONTINUE           - continue recording.");
    llOwnerSay("@STOP               - stop recording.");
    llOwnerSay("@IGNORE             - ignore the recording.");
    llOwnerSay("@SHOW               - show what your recorded.");
    llOwnerSay("@SEND               - send the recording.");
    llOwnerSay("@MAIL <mailadress>  - set the mail adress.");
    llOwnerSay("@SUBJECT subject    - set the mail subject.");
    llOwnerSay("@LIST               - list messages in mail buffer.");
    llOwnerSay("@SELECT <message>   - select message from mail buffer.");
    llOwnerSay("@READ               - read current message from mail buffer.");
    llOwnerSay("@NEXT               - read next message from mail buffer.");
    llOwnerSay("@PREV               - read previous message from mail buffer.");
    llOwnerSay("@FIRST              - read first message from mail buffer.");
    llOwnerSay("@LAST               - read last message from mail buffer.");
    llOwnerSay("@DELETE             - delete current message from mail buffer.");
    llOwnerSay("@CLEAR              - clear the mail buffer.");
    llOwnerSay("@REPLY              - reply to current message.");
    llOwnerSay("@RESET              - reset the script.");
    llOwnerSay("@HOME               - set the home mail adress.");
    llOwnerSay("@OFF                - switch mailbox OFF.");
    llOwnerSay("@ON                 - switch mailbox ON.");
}

/////////////////////////////////////////////
// default
/////////////////////////////////////////////

default
{
    link_message(integer sender, integer msgid, string message, key id)
    {
        if (msgid == MSG_HELP)
        {
            Help();
        }
    }
}

// End SL Mail Help V1.2.lsl

Slmail.info.jpg