version 0.3.29
[fms.git] / src / message.cpp
index dee644d..4c897e3 100644 (file)
 #include "../include/message.h"\r
 #include "../include/nntp/mime/Mime.h"\r
-#include "../include/uuidgenerator.h"\r
 #include "../include/stringfunctions.h"\r
 #include "../include/freenet/messagexml.h"\r
+#include "../include/option.h"\r
 \r
+#include <Poco/DateTimeParser.h>\r
+#include <Poco/DateTimeFormatter.h>\r
+#include <Poco/UUIDGenerator.h>\r
+#include <Poco/UUID.h>\r
 #include <algorithm>\r
 \r
+#ifdef DO_CHARSET_CONVERSION\r
+       #include "../include/charsetconverter.h"\r
+#endif\r
+\r
 #ifdef XMEM\r
        #include <xmem.h>\r
 #endif\r
 \r
-Message::Message()\r
+Message::Message(SQLite3DB::DB *db):IDatabase(db)\r
 {\r
        Initialize();\r
 }\r
 \r
-Message::Message(const long messageid)\r
+Message::Message(SQLite3DB::DB *db, const long messageid):IDatabase(db)\r
 {\r
        Load(messageid);\r
 }\r
 \r
+const bool Message::CheckForAdministrationBoard(const std::vector<std::string> &boards)\r
+{\r
+       std::string name;\r
+       SQLite3DB::Statement st=m_db->Prepare("SELECT BoardName FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID;");\r
+       st.Step();\r
+       \r
+       while(st.RowReturned())\r
+       {\r
+               st.ResultText(0,name);\r
+\r
+               if(std::find(boards.begin(),boards.end(),name)!=boards.end())\r
+               {\r
+                       return true;\r
+               }\r
+               \r
+               st.Step();\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+const bool Message::Create(const long localidentityid, const long boardid, const std::string &subject, const std::string &body, const std::string &references)\r
+{\r
+       Initialize();\r
+\r
+       Poco::UUIDGenerator uuidgen;\r
+       Poco::UUID uuid;\r
+\r
+       // get header info\r
+       // date is always set to now regardless of what message has\r
+       m_datetime=Poco::Timestamp();\r
+\r
+       // messageuuid is always a unique id we generate regardless of message message-id\r
+       try\r
+       {\r
+               uuid=uuidgen.createRandom();\r
+               m_messageuuid=uuid.toString();\r
+               StringFunctions::UpperCase(m_messageuuid,m_messageuuid);\r
+       }\r
+       catch(...)\r
+       {\r
+               m_log->fatal("Message::ParseNNTPMessage could not create UUID");\r
+       }\r
+       \r
+       // get from\r
+       SQLite3DB::Statement st=m_db->Prepare("SELECT Name FROM tblLocalIdentity WHERE LocalIdentityID=?;");\r
+       st.Bind(0,localidentityid);\r
+       st.Step();\r
+       if(st.RowReturned())\r
+       {\r
+               st.ResultText(0,m_fromname);\r
+       }\r
+\r
+       // get boards posted to\r
+       std::string boardname="";\r
+       SQLite3DB::Statement boardst=m_db->Prepare("SELECT BoardName FROM tblBoard WHERE BoardID=?;");\r
+       boardst.Bind(0,boardid);\r
+       boardst.Step();\r
+       if(boardst.RowReturned())\r
+       {\r
+               boardst.ResultText(0,boardname);\r
+       }\r
+\r
+       m_boards.push_back(boardname);\r
+       m_replyboardname=boardname;\r
+\r
+       m_subject=subject;\r
+\r
+       m_body=body;\r
+\r
+       if(references!="")\r
+       {\r
+               m_inreplyto[0]=references;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+const int Message::FindLocalIdentityID(const std::string &name)\r
+{\r
+       SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblLocalIdentity WHERE Name=?;");\r
+       st.Bind(0,name);\r
+       st.Step();\r
+       if(st.RowReturned())\r
+       {\r
+               int result=-1;\r
+               st.ResultInt(0,result);\r
+               return result;\r
+       }\r
+       else\r
+       {\r
+               if(m_addnewpostfromidentities==true)\r
+               {\r
+                       Poco::DateTime now;\r
+                       st=m_db->Prepare("INSERT INTO tblLocalIdentity(Name,DateCreated) VALUES(?,?);");\r
+                       st.Bind(0,name);\r
+                       st.Bind(1,Poco::DateTimeFormatter::format(now,"%Y-%m-%d %H:%M:%S"));\r
+                       st.Step(true);\r
+                       return st.GetLastInsertRowID();\r
+               }\r
+               else\r
+               {\r
+                       return -1;\r
+               }\r
+       }\r
+}\r
+\r
 const std::string Message::GetNNTPArticleID() const\r
 {\r
-       return "<"+m_messageuuid+"@freenetproject.org>";\r
+       // old message - before 0.1.12 - doesn't have @domain so add @freenetproject.org\r
+       if(m_messageuuid.find("@")==std::string::npos)\r
+       {\r
+               return "<"+m_messageuuid+"@freenetproject.org>";\r
+       }\r
+       else\r
+       {\r
+               return "<"+m_messageuuid+">";\r
+       }\r
 }\r
 \r
 const std::string Message::GetNNTPBody() const\r
@@ -47,7 +170,7 @@ const std::string Message::GetNNTPHeaders() const
        rval+="\r\n";\r
        rval+="Subject: "+m_subject+"\r\n";\r
        // format time as  : Wdy, DD Mon YY HH:MM:SS TIMEZONE\r
-       rval+="Date: "+m_datetime.Format("%a, %d %b %y %H:%M:%S -0000")+"\r\n";\r
+       rval+="Date: "+Poco::DateTimeFormatter::format(m_datetime,"%w, %d %b %y %H:%M:%S -0000")+"\r\n";\r
        if(m_inreplyto.size()>0)\r
        {\r
                rval+="References: ";\r
@@ -57,7 +180,15 @@ const std::string Message::GetNNTPHeaders() const
                        {\r
                                rval+=" ";\r
                        }\r
-                       rval+="<"+(*j).second+"@freenetproject.org>";\r
+                       // old message - before 0.1.12 - doesn't have @domain so add @freenetproject.org\r
+                       if((*j).second.find("@")==std::string::npos)\r
+                       {\r
+                               rval+="<"+(*j).second+"@freenetproject.org>";\r
+                       }\r
+                       else\r
+                       {\r
+                               rval+="<"+(*j).second+">";\r
+                       }\r
                }\r
                rval+="\r\n";\r
        }\r
@@ -69,17 +200,225 @@ const std::string Message::GetNNTPHeaders() const
        return rval;\r
 }\r
 \r
+void Message::HandleAdministrationMessage()\r
+{\r
+       // only continue if this message was actually a reply to another message\r
+       if(m_inreplyto.size()>0)\r
+       {\r
+               int localidentityid=-1;\r
+               int boardid=0;\r
+               std::string boardname="";\r
+               std::string identityname="";\r
+               int identityid;\r
+               int changemessagetrust=0;\r
+               int changetrustlisttrust=0;\r
+               int origmessagetrust=0;\r
+               int origtrustlisttrust=0;\r
+               SQLite3DB::Statement st=m_db->Prepare("SELECT tblBoard.BoardID,BoardName,ModifyLocalMessageTrust,ModifyLocalTrustListTrust FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID;");\r
+               st.Step();\r
+\r
+               localidentityid=FindLocalIdentityID(m_fromname);\r
+\r
+               while(st.RowReturned() && localidentityid!=-1)\r
+               {\r
+                       st.ResultInt(0,boardid);\r
+                       st.ResultText(1,boardname);\r
+                       st.ResultInt(2,changemessagetrust);\r
+                       st.ResultInt(3,changetrustlisttrust);\r
+\r
+                       if(std::find(m_boards.begin(),m_boards.end(),boardname)!=m_boards.end())\r
+                       {\r
+                               SQLite3DB::Statement origmess=m_db->Prepare("SELECT tblIdentity.IdentityID,tblIdentity.Name,tblIdentityTrust.LocalMessageTrust,tblIdentityTrust.LocalTrustListTrust FROM tblIdentity INNER JOIN tblMessage ON tblIdentity.IdentityID=tblMessage.IdentityID LEFT JOIN (SELECT IdentityID,LocalMessageTrust,LocalTrustListTrust FROM tblIdentityTrust WHERE LocalIdentityID=?) AS 'tblIdentityTrust' ON tblIdentity.IdentityID=tblIdentityTrust.IdentityID WHERE tblMessage.MessageUUID=?;");\r
+                               origmess.Bind(0,localidentityid);\r
+                               origmess.Bind(1,m_inreplyto[0]);\r
+                               origmess.Step();\r
+\r
+                               if(origmess.RowReturned())\r
+                               {\r
+                                       origmess.ResultInt(0,identityid);\r
+                                       origmess.ResultText(1,identityname);\r
+                                       if(origmess.ResultNull(2)==false)\r
+                                       {\r
+                                               origmess.ResultInt(2,origmessagetrust);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               //origmessagetrust=m_minlocalmessagetrust;\r
+                                               origmessagetrust=50;\r
+                                       }\r
+                                       if(origmess.ResultNull(3)==false)\r
+                                       {\r
+                                               origmess.ResultInt(3,origtrustlisttrust);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               //origtrustlisttrust=m_minlocaltrustlisttrust;\r
+                                               origtrustlisttrust=50;\r
+                                       }\r
+\r
+                                       origmessagetrust+=changemessagetrust;\r
+                                       origtrustlisttrust+=changetrustlisttrust;\r
+\r
+                                       origmessagetrust<0 ? origmessagetrust=0 : false;\r
+                                       origmessagetrust>100 ? origmessagetrust=100 : false;\r
+                                       origtrustlisttrust<0 ? origtrustlisttrust=0 : false;\r
+                                       origtrustlisttrust>100 ? origtrustlisttrust=100 : false;\r
+\r
+                                       // make sure we have a record in tblIdentityTrust\r
+                                       SQLite3DB::Statement ins=m_db->Prepare("INSERT INTO tblIdentityTrust(LocalIdentityID,IdentityID) VALUES(?,?);");\r
+                                       ins.Bind(0,localidentityid);\r
+                                       ins.Bind(1,identityid);\r
+                                       ins.Step();\r
+\r
+                                       // update new trust levels\r
+                                       SQLite3DB::Statement update=m_db->Prepare("UPDATE tblIdentityTrust SET LocalMessageTrust=?, LocalTrustListTrust=? WHERE IdentityID=? AND LocalIdentityID=?;");\r
+                                       update.Bind(0,origmessagetrust);\r
+                                       update.Bind(1,origtrustlisttrust);\r
+                                       update.Bind(2,identityid);\r
+                                       update.Bind(3,localidentityid);\r
+                                       update.Step();\r
+\r
+                                       // insert message to show what id was changed and what current levels are\r
+                                       int lastid=0;\r
+                                       std::string messagebody;\r
+                                       std::string messagetruststr="";\r
+                                       std::string trustlisttruststr="";\r
+\r
+                                       Poco::UUIDGenerator uuidgen;\r
+                                       Poco::UUID uuid;\r
+\r
+                                       try\r
+                                       {\r
+                                               uuid=uuidgen.createRandom();\r
+                                       }\r
+                                       catch(...)\r
+                                       {\r
+                                               m_log->fatal("Message::HandleAdministrationMessage could not generate a UUID");\r
+                                       }\r
+\r
+                                       Poco::DateTime now;\r
+                                       StringFunctions::Convert(origmessagetrust,messagetruststr);\r
+                                       StringFunctions::Convert(origtrustlisttrust,trustlisttruststr);\r
+                                       messagebody="Trust List of "+m_fromname+"\r\n";\r
+                                       messagebody="Trust Changed for "+identityname+"\r\n";\r
+                                       messagebody+="Local Message Trust : "+messagetruststr+"\r\n";\r
+                                       messagebody+="Local Trust List Trust : "+trustlisttruststr+"\r\n";\r
+                                       SQLite3DB::Statement insert=m_db->Prepare("INSERT INTO tblMessage(FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body) VALUES('FMS',?,?,?,?,?,?);");\r
+                                       insert.Bind(0,Poco::DateTimeFormatter::format(now,"%Y-%m-%d"));\r
+                                       insert.Bind(1,Poco::DateTimeFormatter::format(now,"%H:%M:%S"));\r
+                                       insert.Bind(2,identityname+" Trust Changed");\r
+                                       std::string uuidstr=uuid.toString();\r
+                                       StringFunctions::UpperCase(uuidstr,uuidstr);\r
+                                       insert.Bind(3,uuidstr);\r
+                                       insert.Bind(4,boardid);\r
+                                       insert.Bind(5,messagebody);\r
+                                       insert.Step(true);\r
+                                       lastid=insert.GetLastInsertRowID();\r
+\r
+                                       insert=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
+                                       insert.Bind(0,lastid);\r
+                                       insert.Bind(1,boardid);\r
+                                       insert.Step();\r
+\r
+                                       m_log->debug("Message::HandleAdministrationMessage updated "+identityname+" to "+messagetruststr+" , "+trustlisttruststr);\r
+\r
+                               }\r
+                       }\r
+\r
+                       st.Step();\r
+               }\r
+       }\r
+\r
+}\r
+\r
+void Message::HandleChangeTrust()\r
+{\r
+       if(m_changemessagetrustonreply!=0 && m_inreplyto.size()>0)\r
+       {\r
+               int localidentityid=FindLocalIdentityID(m_fromname);\r
+               if(localidentityid!=-1)\r
+               {\r
+                       // make sure we have a record in tblIdentityTrust\r
+                       SQLite3DB::Statement ins=m_db->Prepare("INSERT INTO tblIdentityTrust(LocalIdentityID,IdentityID) VALUES(?,?);");\r
+\r
+                       SQLite3DB::Statement st=m_db->Prepare("SELECT tblIdentity.IdentityID,tblIdentityTrust.LocalMessageTrust FROM tblIdentity INNER JOIN tblMessage ON tblIdentity.IdentityID=tblMessage.IdentityID LEFT JOIN (SELECT IdentityID,LocalMessageTrust FROM tblIdentityTrust WHERE LocalIdentityID=?) AS 'tblIdentityTrust' ON tblIdentity.IdentityID=tblIdentityTrust.IdentityID WHERE tblMessage.MessageUUID=?;");\r
+                       st.Bind(0,localidentityid);\r
+                       st.Bind(1,m_inreplyto[0]);\r
+                       st.Step();\r
+                       if(st.RowReturned())\r
+                       {\r
+                               int identityid=0;\r
+                               int localmessagetrust=0;\r
+\r
+                               st.ResultInt(0,identityid);\r
+                               if(st.ResultNull(1)==false)\r
+                               {\r
+                                       st.ResultInt(1,localmessagetrust);\r
+                               }\r
+                               else\r
+                               {\r
+                                       //localmessagetrust=m_minlocalmessagetrust;\r
+                                       localmessagetrust=50;\r
+                               }\r
+\r
+                               localmessagetrust+=m_changemessagetrustonreply;\r
+                               if(localmessagetrust<0)\r
+                               {\r
+                                       localmessagetrust=0;\r
+                               }\r
+                               if(localmessagetrust>100)\r
+                               {\r
+                                       localmessagetrust=100;\r
+                               }\r
+\r
+                               ins.Bind(0,localidentityid);\r
+                               ins.Bind(1,identityid);\r
+                               ins.Step();\r
+\r
+                               SQLite3DB::Statement st2=m_db->Prepare("UPDATE tblIdentityTrust SET LocalMessageTrust=? WHERE IdentityID=? AND LocalIdentityID=?;");\r
+                               st2.Bind(0,localmessagetrust);\r
+                               st2.Bind(1,identityid);\r
+                               st2.Bind(2,localidentityid);\r
+                               st2.Step();\r
+\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
 void Message::Initialize()\r
 {\r
+       std::string tempval="";\r
        m_messageid=-1;\r
        m_messageuuid="";\r
        m_subject="";\r
        m_body="";\r
        m_replyboardname="";\r
-       m_datetime.Set();\r
+       m_datetime=Poco::Timestamp();\r
        m_fromname="";\r
        m_boards.clear();\r
        m_inreplyto.clear();\r
+       m_fileattachments.clear();\r
+       m_changemessagetrustonreply=0;\r
+       Option option(m_db);\r
+\r
+       option.Get("ChangeMessageTrustOnReply",tempval);\r
+       StringFunctions::Convert(tempval,m_changemessagetrustonreply);\r
+       option.Get("AddNewPostFromIdentities",tempval);\r
+       if(tempval=="true")\r
+       {\r
+               m_addnewpostfromidentities=true;\r
+       }\r
+       else\r
+       {\r
+               m_addnewpostfromidentities=false;\r
+       }\r
+       tempval="50";\r
+       option.Get("MinLocalMessageTrust",tempval);\r
+       StringFunctions::Convert(tempval,m_minlocalmessagetrust);\r
+       tempval="51";\r
+       option.Get("MinLocalTrustListTrust",tempval);\r
+       StringFunctions::Convert(tempval,m_minlocaltrustlisttrust);\r
 }\r
 \r
 const bool Message::Load(const long messageid, const long boardid)\r
@@ -117,10 +456,18 @@ const bool Message::Load(const long messageid, const long boardid)
                st.ResultText(4,m_replyboardname);\r
                st.ResultText(5,tempdate);\r
                st.ResultText(6,temptime);\r
-               m_datetime.Set(tempdate + " " + temptime);\r
                st.ResultText(7,m_fromname);\r
                st.Finalize();\r
 \r
+               int tzdiff=0;\r
+               if(Poco::DateTimeParser::tryParse(tempdate + " " + temptime,m_datetime,tzdiff)==false)\r
+               {\r
+                       m_log->error("Message::Load couldn't parse date/time "+tempdate+" "+temptime);\r
+               }\r
+\r
+               // strip off any \r\n in subject\r
+               m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
+\r
                // get board list\r
                st=m_db->Prepare("SELECT tblBoard.BoardName FROM tblBoard INNER JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID WHERE tblMessageBoard.MessageID=?;");\r
                st.Bind(0,messageid);\r
@@ -160,8 +507,24 @@ const bool Message::Load(const long messageid, const long boardid)
 \r
 const bool Message::Load(const std::string &messageuuid)\r
 {\r
+\r
+       std::string uuid=messageuuid;\r
+\r
+       if(uuid.size()>0 && uuid[0]=='<')\r
+       {\r
+               uuid.erase(0,1);\r
+       }\r
+       if(uuid.size()>0 && uuid[uuid.size()-1]=='>')\r
+       {\r
+               uuid.erase(uuid.size()-1);\r
+       }\r
+       if(uuid.find("@freenetproject.org")!=std::string::npos)\r
+       {\r
+               uuid.erase(uuid.find("@freenetproject.org"));\r
+       }\r
+\r
        SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE MessageUUID=?;");\r
-       st.Bind(0,messageuuid);\r
+       st.Bind(0,uuid);\r
        st.Step();\r
 \r
        if(st.RowReturned())\r
@@ -242,19 +605,34 @@ const bool Message::ParseNNTPMessage(const std::string &nntpmessage)
 \r
        Initialize();\r
 \r
-       UUIDGenerator uuid;\r
+       Poco::UUIDGenerator uuidgen;\r
+       Poco::UUID uuid;\r
        CMimeMessage mime;\r
        mime.Load(nntpmessage.c_str(),nntpmessage.size());\r
 \r
        // get header info\r
        // date is always set to now regardless of what message has\r
-       m_datetime.SetToGMTime();\r
+       m_datetime=Poco::Timestamp();\r
+\r
        // messageuuid is always a unique id we generate regardless of message message-id\r
-       m_messageuuid=uuid.Generate();\r
+       try\r
+       {\r
+               uuid=uuidgen.createRandom();\r
+               m_messageuuid=uuid.toString();\r
+               StringFunctions::UpperCase(m_messageuuid,m_messageuuid);\r
+       }\r
+       catch(...)\r
+       {\r
+               m_log->fatal("Message::ParseNNTPMessage could not create UUID");\r
+       }\r
+       \r
        // get from\r
        if(mime.GetFieldValue("From"))\r
        {\r
                m_fromname=mime.GetFieldValue("From");\r
+               // remove any path folding\r
+               m_fromname=StringFunctions::Replace(m_fromname,"\r\n","");\r
+               m_fromname=StringFunctions::Replace(m_fromname,"\t","");\r
                // strip off everything between () and <> and any whitespace\r
                std::string::size_type startpos=m_fromname.find("(");\r
                std::string::size_type endpos;\r
@@ -297,6 +675,9 @@ const bool Message::ParseNNTPMessage(const std::string &nntpmessage)
        if(mime.GetFieldValue("Newsgroups"))\r
        {\r
                std::string temp=mime.GetFieldValue("Newsgroups");\r
+               // remove any path folding\r
+               temp=StringFunctions::Replace(temp,"\r\n","");\r
+               temp=StringFunctions::Replace(temp,"\t","");\r
                std::vector<std::string> parts;\r
                StringFunctions::SplitMultiple(temp,", \t",parts);\r
                for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
@@ -314,6 +695,15 @@ const bool Message::ParseNNTPMessage(const std::string &nntpmessage)
        if(mime.GetFieldValue("Followup-To"))\r
        {\r
                m_replyboardname=mime.GetFieldValue("Followup-To");\r
+               // remove any path folding\r
+               m_replyboardname=StringFunctions::Replace(m_replyboardname,"\r\n","");\r
+               m_replyboardname=StringFunctions::Replace(m_replyboardname,"\t","");\r
+               std::vector<std::string> parts;\r
+               StringFunctions::Split(m_replyboardname,",",parts);\r
+               if(parts.size()>1)\r
+               {\r
+                       m_replyboardname=parts[0];\r
+               }\r
        }\r
        else\r
        {\r
@@ -326,6 +716,22 @@ const bool Message::ParseNNTPMessage(const std::string &nntpmessage)
        if(mime.GetFieldValue("Subject"))\r
        {\r
                m_subject=mime.GetFieldValue("Subject");\r
+               // remove any path folding\r
+               m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
+               m_subject=StringFunctions::Replace(m_subject,"\t","");\r
+#if DO_CHARSET_CONVERSION\r
+               if(mime.GetFieldCharset("Subject"))\r
+               {\r
+                       std::string charset=mime.GetFieldCharset("Subject");\r
+                       CharsetConverter ccv;\r
+                       if(charset!="" && charset!="UTF-8" && ccv.SetConversion(charset,"UTF-8"))\r
+                       {\r
+                               std::string output="";\r
+                               ccv.Convert(m_subject,output);\r
+                               m_subject=output;\r
+                       }\r
+               }\r
+#endif\r
        }\r
        else\r
        {\r
@@ -335,23 +741,36 @@ const bool Message::ParseNNTPMessage(const std::string &nntpmessage)
        if(mime.GetFieldValue("References"))\r
        {\r
                std::string temp=mime.GetFieldValue("References");\r
+               // remove any path folding\r
+               temp=StringFunctions::Replace(temp,"\r\n","");\r
+               temp=StringFunctions::Replace(temp,"\t"," ");\r
                std::vector<std::string> parts;\r
                int count=0;\r
                StringFunctions::SplitMultiple(temp,", \t",parts);\r
                for(std::vector<std::string>::reverse_iterator i=parts.rbegin(); i!=parts.rend(); i++)\r
                {\r
-                       // get rid of < and > and any whitespace\r
-                       (*i)=StringFunctions::Replace((*i),"<","");\r
-                       (*i)=StringFunctions::Replace((*i),">","");\r
-                       (*i)=StringFunctions::TrimWhitespace((*i));\r
-                       // erase @ and everything after\r
-                       if((*i).find("@")!=std::string::npos)\r
-                       {\r
-                               (*i).erase((*i).find("@"));\r
-                       }\r
-                       if((*i)!="")\r
+                       if((*i).size()>2)\r
                        {\r
-                               m_inreplyto[count++]=(*i);\r
+                               // get rid of < and > and any whitespace\r
+                               (*i)=StringFunctions::Replace((*i),"<","");\r
+                               (*i)=StringFunctions::Replace((*i),">","");\r
+                               (*i)=StringFunctions::TrimWhitespace((*i));\r
+                               /*\r
+                               // erase @ and everything after\r
+                               if((*i).find("@")!=std::string::npos)\r
+                               {\r
+                                       (*i).erase((*i).find("@"));\r
+                               }\r
+                               */\r
+                               // only erase after @ if message is old type with @freenetproject.org\r
+                               if((*i).find("@freenetproject.org")!=std::string::npos)\r
+                               {\r
+                                       (*i).erase((*i).find("@"));\r
+                               }\r
+                               if((*i)!="")\r
+                               {\r
+                                       m_inreplyto[count++]=(*i);\r
+                               }\r
                        }\r
                }\r
        }\r
@@ -364,59 +783,165 @@ const bool Message::ParseNNTPMessage(const std::string &nntpmessage)
        {\r
                if((*i)->IsText() && (*i)->GetContent())\r
                {\r
-                       m_body+=(char *)(*i)->GetContent();\r
+                       std::string bodypart=(char *)(*i)->GetContent();\r
+#ifdef DO_CHARSET_CONVERSION\r
+                       std::string charset=(*i)->GetCharset();\r
+                       if(charset!="" && charset!="UTF-8")\r
+                       {\r
+                               CharsetConverter ccv;\r
+                               if(ccv.SetConversion(charset,"UTF-8"))\r
+                               {\r
+                                       std::string output="";\r
+                                       ccv.Convert(bodypart,output);\r
+                                       bodypart=output;\r
+                               }\r
+                       }\r
+#endif\r
+                       m_body+=bodypart;\r
+               }\r
+               // add a binary file attachment\r
+               else if(((*i)->GetName()!="" || (*i)->GetFilename()!="") && (*i)->GetLength()>0 && (*i)->GetContent())\r
+               {\r
+                       std::string filename="";\r
+                       std::string contenttype="";\r
+                       std::vector<unsigned char> data((*i)->GetContent(),(*i)->GetContent()+(*i)->GetContentLength());\r
+                       if((*i)->GetContentType())\r
+                       {\r
+                               contenttype=(*i)->GetContentType();\r
+                               // find first ; tab cr or lf and erase it and everything after it\r
+                               std::string::size_type endpos=contenttype.find_first_of(";\t\r\n ");\r
+                               if(endpos!=std::string::npos)\r
+                               {\r
+                                       contenttype.erase(endpos);\r
+                               }\r
+                       }\r
+                       filename=(*i)->GetFilename();\r
+                       if(filename=="")\r
+                       {\r
+                               filename=(*i)->GetName();\r
+                       }\r
+                       m_fileattachments.push_back(fileattachment(filename,contenttype,data));\r
                }\r
        }\r
 \r
        return true;\r
 }\r
 \r
-void Message::StartFreenetInsert()\r
+const bool Message::StartFreenetInsert()\r
 {\r
+\r
        MessageXML xml;\r
        int localidentityid=-1;\r
 \r
-       xml.SetMessageID(m_messageuuid);\r
-       xml.SetSubject(m_subject);\r
-       xml.SetBody(m_body);\r
-       xml.SetReplyBoard(m_replyboardname);\r
-       xml.SetDate(m_datetime.Format("%Y-%m-%d"));\r
-       xml.SetTime(m_datetime.Format("%H:%M:%S"));\r
-       \r
-       for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
-       {\r
-               xml.AddBoard((*i));\r
-       }\r
-       \r
-       for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
+       StripAdministrationBoards();\r
+\r
+       if(m_boards.size()>0)\r
        {\r
-               xml.AddInReplyTo((*j).first,(*j).second);\r
+\r
+               xml.SetMessageID(m_messageuuid);\r
+               xml.SetSubject(m_subject);\r
+               xml.SetBody(m_body);\r
+               xml.SetReplyBoard(m_replyboardname);\r
+               \r
+               for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
+               {\r
+                       xml.AddBoard((*i));\r
+               }\r
+               \r
+               for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
+               {\r
+                       xml.AddInReplyTo((*j).first,(*j).second);\r
+               }\r
+\r
+               localidentityid=FindLocalIdentityID(m_fromname);\r
+               if(localidentityid==-1)\r
+               {\r
+                       return false;\r
+               }\r
+\r
+               // add the message delay if there is one\r
+               SQLite3DB::Statement st=m_db->Prepare("SELECT MinMessageDelay,MaxMessageDelay FROM tblLocalIdentity WHERE LocalIdentityID=?;");\r
+               st.Bind(0,localidentityid);\r
+               st.Step();\r
+               if(st.RowReturned())\r
+               {\r
+                       int min=0;\r
+                       int max=0;\r
+                       st.ResultInt(0,min);\r
+                       st.ResultInt(1,max);\r
+\r
+                       min<0 ? min=0 : false;\r
+                       max<0 ? max=0 : false;\r
+                       min>max ? min=max : false;\r
+\r
+                       if(min==max)\r
+                       {\r
+                               m_datetime+=Poco::Timespan(0,0,min,0,0);\r
+                       }\r
+                       else if(max>min)\r
+                       {\r
+                               int delay=(rand()%(max-min))+min;\r
+                               m_datetime+=Poco::Timespan(0,0,delay,0,0);\r
+                       }\r
+\r
+               }\r
+               st.Finalize();\r
+\r
+               // set date in xml file AFTER we set the delay\r
+               xml.SetDate(Poco::DateTimeFormatter::format(m_datetime,"%Y-%m-%d"));\r
+               xml.SetTime(Poco::DateTimeFormatter::format(m_datetime,"%H:%M:%S"));\r
+\r
+               st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML,SendDate) VALUES(?,?,?,?);");\r
+               st.Bind(0,localidentityid);\r
+               st.Bind(1,m_messageuuid);\r
+               st.Bind(2,xml.GetXML());\r
+               st.Bind(3,Poco::DateTimeFormatter::format(m_datetime,"%Y-%m-%d %H:%M:%S"));\r
+               st.Step();\r
+\r
+               // insert file attachments into database\r
+               st=m_db->Prepare("INSERT INTO tblFileInserts(MessageUUID,FileName,Size,MimeType,Data) VALUES(?,?,?,?,?);");\r
+               for(std::vector<fileattachment>::iterator i=m_fileattachments.begin(); i!=m_fileattachments.end(); i++)\r
+               {\r
+                       st.Bind(0,m_messageuuid);\r
+                       st.Bind(1,(*i).m_filename);\r
+                       st.Bind(2,(long)(*i).m_data.size());\r
+                       st.Bind(3,(*i).m_mimetype);\r
+                       st.Bind(4,&((*i).m_data[0]),(*i).m_data.size());\r
+                       st.Step();\r
+                       st.Reset();\r
+               }\r
+\r
+               HandleChangeTrust();\r
+\r
        }\r
 \r
-       // find identity to insert with\r
-       SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblLocalIdentity WHERE Name=?;");\r
-       st.Bind(0,m_fromname);\r
-       st.Step();\r
+       return true;\r
 \r
-       // couldn't find identity with this name - insert a new identity\r
-       if(!st.RowReturned())\r
+}\r
+\r
+void Message::StripAdministrationBoards()\r
+{\r
+       SQLite3DB::Statement st=m_db->Prepare("SELECT tblBoard.BoardID FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID WHERE BoardName=?;");\r
+       for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); )\r
        {\r
-               DateTime now;\r
-               now.SetToGMTime();\r
-               st=m_db->Prepare("INSERT INTO tblLocalIdentity(Name) VALUES(?);");\r
-               st.Bind(0,m_fromname);\r
-               st.Step(true);\r
-               localidentityid=st.GetLastInsertRowID();\r
+               st.Bind(0,(*i));\r
+               st.Step();\r
+               if(st.RowReturned())\r
+               {\r
+                       if(m_replyboardname==(*i))\r
+                       {\r
+                               m_replyboardname="";\r
+                       }\r
+                       i=m_boards.erase(i);\r
+               }\r
+               else\r
+               {\r
+                       i++;\r
+               }\r
+               st.Reset();\r
        }\r
-       else\r
+       if(m_replyboardname=="" && m_boards.begin()!=m_boards.end())\r
        {\r
-               st.ResultInt(0,localidentityid);\r
+               m_replyboardname=(*m_boards.begin());\r
        }\r
-\r
-       st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML) VALUES(?,?,?);");\r
-       st.Bind(0,localidentityid);\r
-       st.Bind(1,m_messageuuid);\r
-       st.Bind(2,xml.GetXML());\r
-       st.Step();\r
-\r
 }\r