version 0.3.29
[fms.git] / src / message.cpp
1 #include "../include/message.h"\r
2 #include "../include/nntp/mime/Mime.h"\r
3 #include "../include/stringfunctions.h"\r
4 #include "../include/freenet/messagexml.h"\r
5 #include "../include/option.h"\r
6 \r
7 #include <Poco/DateTimeParser.h>\r
8 #include <Poco/DateTimeFormatter.h>\r
9 #include <Poco/UUIDGenerator.h>\r
10 #include <Poco/UUID.h>\r
11 #include <algorithm>\r
12 \r
13 #ifdef DO_CHARSET_CONVERSION\r
14         #include "../include/charsetconverter.h"\r
15 #endif\r
16 \r
17 #ifdef XMEM\r
18         #include <xmem.h>\r
19 #endif\r
20 \r
21 Message::Message(SQLite3DB::DB *db):IDatabase(db)\r
22 {\r
23         Initialize();\r
24 }\r
25 \r
26 Message::Message(SQLite3DB::DB *db, const long messageid):IDatabase(db)\r
27 {\r
28         Load(messageid);\r
29 }\r
30 \r
31 const bool Message::CheckForAdministrationBoard(const std::vector<std::string> &boards)\r
32 {\r
33         std::string name;\r
34         SQLite3DB::Statement st=m_db->Prepare("SELECT BoardName FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID;");\r
35         st.Step();\r
36         \r
37         while(st.RowReturned())\r
38         {\r
39                 st.ResultText(0,name);\r
40 \r
41                 if(std::find(boards.begin(),boards.end(),name)!=boards.end())\r
42                 {\r
43                         return true;\r
44                 }\r
45                 \r
46                 st.Step();\r
47         }\r
48 \r
49         return false;\r
50 }\r
51 \r
52 const bool Message::Create(const long localidentityid, const long boardid, const std::string &subject, const std::string &body, const std::string &references)\r
53 {\r
54         Initialize();\r
55 \r
56         Poco::UUIDGenerator uuidgen;\r
57         Poco::UUID uuid;\r
58 \r
59         // get header info\r
60         // date is always set to now regardless of what message has\r
61         m_datetime=Poco::Timestamp();\r
62 \r
63         // messageuuid is always a unique id we generate regardless of message message-id\r
64         try\r
65         {\r
66                 uuid=uuidgen.createRandom();\r
67                 m_messageuuid=uuid.toString();\r
68                 StringFunctions::UpperCase(m_messageuuid,m_messageuuid);\r
69         }\r
70         catch(...)\r
71         {\r
72                 m_log->fatal("Message::ParseNNTPMessage could not create UUID");\r
73         }\r
74         \r
75         // get from\r
76         SQLite3DB::Statement st=m_db->Prepare("SELECT Name FROM tblLocalIdentity WHERE LocalIdentityID=?;");\r
77         st.Bind(0,localidentityid);\r
78         st.Step();\r
79         if(st.RowReturned())\r
80         {\r
81                 st.ResultText(0,m_fromname);\r
82         }\r
83 \r
84         // get boards posted to\r
85         std::string boardname="";\r
86         SQLite3DB::Statement boardst=m_db->Prepare("SELECT BoardName FROM tblBoard WHERE BoardID=?;");\r
87         boardst.Bind(0,boardid);\r
88         boardst.Step();\r
89         if(boardst.RowReturned())\r
90         {\r
91                 boardst.ResultText(0,boardname);\r
92         }\r
93 \r
94         m_boards.push_back(boardname);\r
95         m_replyboardname=boardname;\r
96 \r
97         m_subject=subject;\r
98 \r
99         m_body=body;\r
100 \r
101         if(references!="")\r
102         {\r
103                 m_inreplyto[0]=references;\r
104         }\r
105 \r
106         return true;\r
107 }\r
108 \r
109 const int Message::FindLocalIdentityID(const std::string &name)\r
110 {\r
111         SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblLocalIdentity WHERE Name=?;");\r
112         st.Bind(0,name);\r
113         st.Step();\r
114         if(st.RowReturned())\r
115         {\r
116                 int result=-1;\r
117                 st.ResultInt(0,result);\r
118                 return result;\r
119         }\r
120         else\r
121         {\r
122                 if(m_addnewpostfromidentities==true)\r
123                 {\r
124                         Poco::DateTime now;\r
125                         st=m_db->Prepare("INSERT INTO tblLocalIdentity(Name,DateCreated) VALUES(?,?);");\r
126                         st.Bind(0,name);\r
127                         st.Bind(1,Poco::DateTimeFormatter::format(now,"%Y-%m-%d %H:%M:%S"));\r
128                         st.Step(true);\r
129                         return st.GetLastInsertRowID();\r
130                 }\r
131                 else\r
132                 {\r
133                         return -1;\r
134                 }\r
135         }\r
136 }\r
137 \r
138 const std::string Message::GetNNTPArticleID() const\r
139 {\r
140         // old message - before 0.1.12 - doesn't have @domain so add @freenetproject.org\r
141         if(m_messageuuid.find("@")==std::string::npos)\r
142         {\r
143                 return "<"+m_messageuuid+"@freenetproject.org>";\r
144         }\r
145         else\r
146         {\r
147                 return "<"+m_messageuuid+">";\r
148         }\r
149 }\r
150 \r
151 const std::string Message::GetNNTPBody() const\r
152 {\r
153         return m_body;\r
154 }\r
155 \r
156 const std::string Message::GetNNTPHeaders() const\r
157 {\r
158         std::string rval("");\r
159 \r
160         rval+="From: "+m_fromname+"\r\n";\r
161         rval+="Newsgroups: ";\r
162         for(std::vector<std::string>::const_iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
163         {\r
164                 if(i!=m_boards.begin())\r
165                 {\r
166                         rval+=",";\r
167                 }\r
168                 rval+=(*i);\r
169         }\r
170         rval+="\r\n";\r
171         rval+="Subject: "+m_subject+"\r\n";\r
172         // format time as  : Wdy, DD Mon YY HH:MM:SS TIMEZONE\r
173         rval+="Date: "+Poco::DateTimeFormatter::format(m_datetime,"%w, %d %b %y %H:%M:%S -0000")+"\r\n";\r
174         if(m_inreplyto.size()>0)\r
175         {\r
176                 rval+="References: ";\r
177                 for(std::map<long,std::string>::const_reverse_iterator j=m_inreplyto.rbegin(); j!=m_inreplyto.rend(); j++)\r
178                 {\r
179                         if(j!=m_inreplyto.rend())\r
180                         {\r
181                                 rval+=" ";\r
182                         }\r
183                         // old message - before 0.1.12 - doesn't have @domain so add @freenetproject.org\r
184                         if((*j).second.find("@")==std::string::npos)\r
185                         {\r
186                                 rval+="<"+(*j).second+"@freenetproject.org>";\r
187                         }\r
188                         else\r
189                         {\r
190                                 rval+="<"+(*j).second+">";\r
191                         }\r
192                 }\r
193                 rval+="\r\n";\r
194         }\r
195         rval+="Followup-To: "+m_replyboardname+"\r\n";\r
196         rval+="Path: freenet\r\n";\r
197         rval+="Message-ID: "+GetNNTPArticleID()+"\r\n";\r
198         rval+="Content-Type: text/plain; charset=UTF-8\r\n";\r
199 \r
200         return rval;\r
201 }\r
202 \r
203 void Message::HandleAdministrationMessage()\r
204 {\r
205         // only continue if this message was actually a reply to another message\r
206         if(m_inreplyto.size()>0)\r
207         {\r
208                 int localidentityid=-1;\r
209                 int boardid=0;\r
210                 std::string boardname="";\r
211                 std::string identityname="";\r
212                 int identityid;\r
213                 int changemessagetrust=0;\r
214                 int changetrustlisttrust=0;\r
215                 int origmessagetrust=0;\r
216                 int origtrustlisttrust=0;\r
217                 SQLite3DB::Statement st=m_db->Prepare("SELECT tblBoard.BoardID,BoardName,ModifyLocalMessageTrust,ModifyLocalTrustListTrust FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID;");\r
218                 st.Step();\r
219 \r
220                 localidentityid=FindLocalIdentityID(m_fromname);\r
221 \r
222                 while(st.RowReturned() && localidentityid!=-1)\r
223                 {\r
224                         st.ResultInt(0,boardid);\r
225                         st.ResultText(1,boardname);\r
226                         st.ResultInt(2,changemessagetrust);\r
227                         st.ResultInt(3,changetrustlisttrust);\r
228 \r
229                         if(std::find(m_boards.begin(),m_boards.end(),boardname)!=m_boards.end())\r
230                         {\r
231                                 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
232                                 origmess.Bind(0,localidentityid);\r
233                                 origmess.Bind(1,m_inreplyto[0]);\r
234                                 origmess.Step();\r
235 \r
236                                 if(origmess.RowReturned())\r
237                                 {\r
238                                         origmess.ResultInt(0,identityid);\r
239                                         origmess.ResultText(1,identityname);\r
240                                         if(origmess.ResultNull(2)==false)\r
241                                         {\r
242                                                 origmess.ResultInt(2,origmessagetrust);\r
243                                         }\r
244                                         else\r
245                                         {\r
246                                                 //origmessagetrust=m_minlocalmessagetrust;\r
247                                                 origmessagetrust=50;\r
248                                         }\r
249                                         if(origmess.ResultNull(3)==false)\r
250                                         {\r
251                                                 origmess.ResultInt(3,origtrustlisttrust);\r
252                                         }\r
253                                         else\r
254                                         {\r
255                                                 //origtrustlisttrust=m_minlocaltrustlisttrust;\r
256                                                 origtrustlisttrust=50;\r
257                                         }\r
258 \r
259                                         origmessagetrust+=changemessagetrust;\r
260                                         origtrustlisttrust+=changetrustlisttrust;\r
261 \r
262                                         origmessagetrust<0 ? origmessagetrust=0 : false;\r
263                                         origmessagetrust>100 ? origmessagetrust=100 : false;\r
264                                         origtrustlisttrust<0 ? origtrustlisttrust=0 : false;\r
265                                         origtrustlisttrust>100 ? origtrustlisttrust=100 : false;\r
266 \r
267                                         // make sure we have a record in tblIdentityTrust\r
268                                         SQLite3DB::Statement ins=m_db->Prepare("INSERT INTO tblIdentityTrust(LocalIdentityID,IdentityID) VALUES(?,?);");\r
269                                         ins.Bind(0,localidentityid);\r
270                                         ins.Bind(1,identityid);\r
271                                         ins.Step();\r
272 \r
273                                         // update new trust levels\r
274                                         SQLite3DB::Statement update=m_db->Prepare("UPDATE tblIdentityTrust SET LocalMessageTrust=?, LocalTrustListTrust=? WHERE IdentityID=? AND LocalIdentityID=?;");\r
275                                         update.Bind(0,origmessagetrust);\r
276                                         update.Bind(1,origtrustlisttrust);\r
277                                         update.Bind(2,identityid);\r
278                                         update.Bind(3,localidentityid);\r
279                                         update.Step();\r
280 \r
281                                         // insert message to show what id was changed and what current levels are\r
282                                         int lastid=0;\r
283                                         std::string messagebody;\r
284                                         std::string messagetruststr="";\r
285                                         std::string trustlisttruststr="";\r
286 \r
287                                         Poco::UUIDGenerator uuidgen;\r
288                                         Poco::UUID uuid;\r
289 \r
290                                         try\r
291                                         {\r
292                                                 uuid=uuidgen.createRandom();\r
293                                         }\r
294                                         catch(...)\r
295                                         {\r
296                                                 m_log->fatal("Message::HandleAdministrationMessage could not generate a UUID");\r
297                                         }\r
298 \r
299                                         Poco::DateTime now;\r
300                                         StringFunctions::Convert(origmessagetrust,messagetruststr);\r
301                                         StringFunctions::Convert(origtrustlisttrust,trustlisttruststr);\r
302                                         messagebody="Trust List of "+m_fromname+"\r\n";\r
303                                         messagebody="Trust Changed for "+identityname+"\r\n";\r
304                                         messagebody+="Local Message Trust : "+messagetruststr+"\r\n";\r
305                                         messagebody+="Local Trust List Trust : "+trustlisttruststr+"\r\n";\r
306                                         SQLite3DB::Statement insert=m_db->Prepare("INSERT INTO tblMessage(FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body) VALUES('FMS',?,?,?,?,?,?);");\r
307                                         insert.Bind(0,Poco::DateTimeFormatter::format(now,"%Y-%m-%d"));\r
308                                         insert.Bind(1,Poco::DateTimeFormatter::format(now,"%H:%M:%S"));\r
309                                         insert.Bind(2,identityname+" Trust Changed");\r
310                                         std::string uuidstr=uuid.toString();\r
311                                         StringFunctions::UpperCase(uuidstr,uuidstr);\r
312                                         insert.Bind(3,uuidstr);\r
313                                         insert.Bind(4,boardid);\r
314                                         insert.Bind(5,messagebody);\r
315                                         insert.Step(true);\r
316                                         lastid=insert.GetLastInsertRowID();\r
317 \r
318                                         insert=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
319                                         insert.Bind(0,lastid);\r
320                                         insert.Bind(1,boardid);\r
321                                         insert.Step();\r
322 \r
323                                         m_log->debug("Message::HandleAdministrationMessage updated "+identityname+" to "+messagetruststr+" , "+trustlisttruststr);\r
324 \r
325                                 }\r
326                         }\r
327 \r
328                         st.Step();\r
329                 }\r
330         }\r
331 \r
332 }\r
333 \r
334 void Message::HandleChangeTrust()\r
335 {\r
336         if(m_changemessagetrustonreply!=0 && m_inreplyto.size()>0)\r
337         {\r
338                 int localidentityid=FindLocalIdentityID(m_fromname);\r
339                 if(localidentityid!=-1)\r
340                 {\r
341                         // make sure we have a record in tblIdentityTrust\r
342                         SQLite3DB::Statement ins=m_db->Prepare("INSERT INTO tblIdentityTrust(LocalIdentityID,IdentityID) VALUES(?,?);");\r
343 \r
344                         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
345                         st.Bind(0,localidentityid);\r
346                         st.Bind(1,m_inreplyto[0]);\r
347                         st.Step();\r
348                         if(st.RowReturned())\r
349                         {\r
350                                 int identityid=0;\r
351                                 int localmessagetrust=0;\r
352 \r
353                                 st.ResultInt(0,identityid);\r
354                                 if(st.ResultNull(1)==false)\r
355                                 {\r
356                                         st.ResultInt(1,localmessagetrust);\r
357                                 }\r
358                                 else\r
359                                 {\r
360                                         //localmessagetrust=m_minlocalmessagetrust;\r
361                                         localmessagetrust=50;\r
362                                 }\r
363 \r
364                                 localmessagetrust+=m_changemessagetrustonreply;\r
365                                 if(localmessagetrust<0)\r
366                                 {\r
367                                         localmessagetrust=0;\r
368                                 }\r
369                                 if(localmessagetrust>100)\r
370                                 {\r
371                                         localmessagetrust=100;\r
372                                 }\r
373 \r
374                                 ins.Bind(0,localidentityid);\r
375                                 ins.Bind(1,identityid);\r
376                                 ins.Step();\r
377 \r
378                                 SQLite3DB::Statement st2=m_db->Prepare("UPDATE tblIdentityTrust SET LocalMessageTrust=? WHERE IdentityID=? AND LocalIdentityID=?;");\r
379                                 st2.Bind(0,localmessagetrust);\r
380                                 st2.Bind(1,identityid);\r
381                                 st2.Bind(2,localidentityid);\r
382                                 st2.Step();\r
383 \r
384                         }\r
385                 }\r
386         }\r
387 }\r
388 \r
389 void Message::Initialize()\r
390 {\r
391         std::string tempval="";\r
392         m_messageid=-1;\r
393         m_messageuuid="";\r
394         m_subject="";\r
395         m_body="";\r
396         m_replyboardname="";\r
397         m_datetime=Poco::Timestamp();\r
398         m_fromname="";\r
399         m_boards.clear();\r
400         m_inreplyto.clear();\r
401         m_fileattachments.clear();\r
402         m_changemessagetrustonreply=0;\r
403         Option option(m_db);\r
404 \r
405         option.Get("ChangeMessageTrustOnReply",tempval);\r
406         StringFunctions::Convert(tempval,m_changemessagetrustonreply);\r
407         option.Get("AddNewPostFromIdentities",tempval);\r
408         if(tempval=="true")\r
409         {\r
410                 m_addnewpostfromidentities=true;\r
411         }\r
412         else\r
413         {\r
414                 m_addnewpostfromidentities=false;\r
415         }\r
416         tempval="50";\r
417         option.Get("MinLocalMessageTrust",tempval);\r
418         StringFunctions::Convert(tempval,m_minlocalmessagetrust);\r
419         tempval="51";\r
420         option.Get("MinLocalTrustListTrust",tempval);\r
421         StringFunctions::Convert(tempval,m_minlocaltrustlisttrust);\r
422 }\r
423 \r
424 const bool Message::Load(const long messageid, const long boardid)\r
425 {\r
426         \r
427         Initialize();\r
428 \r
429         std::string sql;\r
430         \r
431         sql="SELECT tblMessage.MessageID, MessageUUID, Subject, Body, tblBoard.BoardName, MessageDate, MessageTime, FromName FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID INNER JOIN tblBoard ON tblMessage.ReplyBoardID=tblBoard.BoardID WHERE tblMessage.MessageID=?";\r
432         if(boardid!=-1)\r
433         {\r
434                 sql+=" AND tblMessageBoard.BoardID=?";\r
435         }\r
436         sql+=";";\r
437 \r
438         SQLite3DB::Statement st=m_db->Prepare(sql);\r
439         st.Bind(0,messageid);\r
440         if(boardid!=-1)\r
441         {\r
442                 st.Bind(1,boardid);\r
443         }\r
444         st.Step();\r
445 \r
446         if(st.RowReturned())\r
447         {\r
448                 std::string tempdate;\r
449                 std::string temptime;\r
450                 int tempint=-1;\r
451                 st.ResultInt(0,tempint);\r
452                 m_messageid=tempint;\r
453                 st.ResultText(1,m_messageuuid);\r
454                 st.ResultText(2,m_subject);\r
455                 st.ResultText(3,m_body);\r
456                 st.ResultText(4,m_replyboardname);\r
457                 st.ResultText(5,tempdate);\r
458                 st.ResultText(6,temptime);\r
459                 st.ResultText(7,m_fromname);\r
460                 st.Finalize();\r
461 \r
462                 int tzdiff=0;\r
463                 if(Poco::DateTimeParser::tryParse(tempdate + " " + temptime,m_datetime,tzdiff)==false)\r
464                 {\r
465                         m_log->error("Message::Load couldn't parse date/time "+tempdate+" "+temptime);\r
466                 }\r
467 \r
468                 // strip off any \r\n in subject\r
469                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
470 \r
471                 // get board list\r
472                 st=m_db->Prepare("SELECT tblBoard.BoardName FROM tblBoard INNER JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID WHERE tblMessageBoard.MessageID=?;");\r
473                 st.Bind(0,messageid);\r
474                 st.Step();\r
475                 while(st.RowReturned())\r
476                 {\r
477                         std::string tempval;\r
478                         st.ResultText(0,tempval);\r
479                         m_boards.push_back(tempval);\r
480                         st.Step();\r
481                 }\r
482                 st.Finalize();\r
483 \r
484                 // get in reply to list\r
485                 st=m_db->Prepare("SELECT ReplyToMessageUUID, ReplyOrder FROM tblMessageReplyTo INNER JOIN tblMessage ON tblMessageReplyTo.MessageID=tblMessage.MessageID WHERE tblMessage.MessageID=?;");\r
486                 st.Bind(0,messageid);\r
487                 st.Step();\r
488                 while(st.RowReturned())\r
489                 {\r
490                         std::string tempval;\r
491                         int tempint;\r
492                         st.ResultText(0,tempval);\r
493                         st.ResultInt(1,tempint);\r
494                         m_inreplyto[tempint]=tempval;\r
495                         st.Step();\r
496                 }\r
497                 st.Finalize();\r
498 \r
499                 return true;\r
500         }\r
501         else\r
502         {\r
503                 return false;\r
504         }\r
505 \r
506 }\r
507 \r
508 const bool Message::Load(const std::string &messageuuid)\r
509 {\r
510 \r
511         std::string uuid=messageuuid;\r
512 \r
513         if(uuid.size()>0 && uuid[0]=='<')\r
514         {\r
515                 uuid.erase(0,1);\r
516         }\r
517         if(uuid.size()>0 && uuid[uuid.size()-1]=='>')\r
518         {\r
519                 uuid.erase(uuid.size()-1);\r
520         }\r
521         if(uuid.find("@freenetproject.org")!=std::string::npos)\r
522         {\r
523                 uuid.erase(uuid.find("@freenetproject.org"));\r
524         }\r
525 \r
526         SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE MessageUUID=?;");\r
527         st.Bind(0,uuid);\r
528         st.Step();\r
529 \r
530         if(st.RowReturned())\r
531         {\r
532                 int messageid;\r
533                 st.ResultInt(0,messageid);\r
534 \r
535                 return Load(messageid);\r
536         }\r
537         else\r
538         {\r
539                 return false;\r
540         }\r
541 }\r
542 \r
543 const bool Message::LoadNext(const long messageid, const long boardid)\r
544 {\r
545         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID>?";\r
546         if(boardid!=-1)\r
547         {\r
548                 sql+=" AND tblMessageBoard.BoardID=?";\r
549         }\r
550         sql+=";";\r
551 \r
552         SQLite3DB::Statement st=m_db->Prepare(sql);\r
553 \r
554         st.Bind(0,messageid);\r
555         if(boardid!=-1)\r
556         {\r
557                 st.Bind(1,boardid);\r
558         }\r
559         st.Step();\r
560 \r
561         if(st.RowReturned())\r
562         {\r
563                 int result;\r
564                 st.ResultInt(0,result);\r
565                 return Load(result,boardid);\r
566         }\r
567         else\r
568         {\r
569                 return false;\r
570         }\r
571 }\r
572 \r
573 const bool Message::LoadPrevious(const long messageid, const long boardid)\r
574 {\r
575         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID<?";\r
576         if(boardid!=-1)\r
577         {\r
578                 sql+=" AND tblMessageBoard.BoardID=?";\r
579         }\r
580         sql+=" ORDER BY tblMessage.MessageID DESC;";\r
581 \r
582         SQLite3DB::Statement st=m_db->Prepare(sql);\r
583 \r
584         st.Bind(0,messageid);\r
585         if(boardid!=-1)\r
586         {\r
587                 st.Bind(1,boardid);\r
588         }\r
589         st.Step();\r
590 \r
591         if(st.RowReturned())\r
592         {\r
593                 int result;\r
594                 st.ResultInt(0,result);\r
595                 return Load(result,boardid);\r
596         }\r
597         else\r
598         {\r
599                 return false;\r
600         }\r
601 }\r
602 \r
603 const bool Message::ParseNNTPMessage(const std::string &nntpmessage)\r
604 {\r
605 \r
606         Initialize();\r
607 \r
608         Poco::UUIDGenerator uuidgen;\r
609         Poco::UUID uuid;\r
610         CMimeMessage mime;\r
611         mime.Load(nntpmessage.c_str(),nntpmessage.size());\r
612 \r
613         // get header info\r
614         // date is always set to now regardless of what message has\r
615         m_datetime=Poco::Timestamp();\r
616 \r
617         // messageuuid is always a unique id we generate regardless of message message-id\r
618         try\r
619         {\r
620                 uuid=uuidgen.createRandom();\r
621                 m_messageuuid=uuid.toString();\r
622                 StringFunctions::UpperCase(m_messageuuid,m_messageuuid);\r
623         }\r
624         catch(...)\r
625         {\r
626                 m_log->fatal("Message::ParseNNTPMessage could not create UUID");\r
627         }\r
628         \r
629         // get from\r
630         if(mime.GetFieldValue("From"))\r
631         {\r
632                 m_fromname=mime.GetFieldValue("From");\r
633                 // remove any path folding\r
634                 m_fromname=StringFunctions::Replace(m_fromname,"\r\n","");\r
635                 m_fromname=StringFunctions::Replace(m_fromname,"\t","");\r
636                 // strip off everything between () and <> and any whitespace\r
637                 std::string::size_type startpos=m_fromname.find("(");\r
638                 std::string::size_type endpos;\r
639                 if(startpos!=std::string::npos)\r
640                 {\r
641                         endpos=m_fromname.find(")",startpos);\r
642                         if(endpos!=std::string::npos)\r
643                         {\r
644                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
645                         }\r
646                 }\r
647                 startpos=m_fromname.find("<");\r
648                 if(startpos!=std::string::npos)\r
649                 {\r
650                         endpos=m_fromname.find(">",startpos);\r
651                         if(endpos!=std::string::npos)\r
652                         {\r
653                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
654                         }\r
655                 }\r
656                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
657 \r
658                 // trim off " from beginning and end\r
659                 if(m_fromname.size()>0 && m_fromname[0]=='\"')\r
660                 {\r
661                         m_fromname.erase(0,1);\r
662                 }\r
663                 if(m_fromname.size()>0 && m_fromname[m_fromname.size()-1]=='\"')\r
664                 {\r
665                         m_fromname.erase(m_fromname.size()-1,1);\r
666                 }\r
667 \r
668                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
669         }\r
670         else\r
671         {\r
672                 m_fromname="Anonymous";\r
673         }\r
674         // get boards posted to\r
675         if(mime.GetFieldValue("Newsgroups"))\r
676         {\r
677                 std::string temp=mime.GetFieldValue("Newsgroups");\r
678                 // remove any path folding\r
679                 temp=StringFunctions::Replace(temp,"\r\n","");\r
680                 temp=StringFunctions::Replace(temp,"\t","");\r
681                 std::vector<std::string> parts;\r
682                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
683                 for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
684                 {\r
685                         (*i)=StringFunctions::Replace((*i),"<","");\r
686                         (*i)=StringFunctions::Replace((*i),">","");\r
687                         (*i)=StringFunctions::TrimWhitespace((*i));\r
688                         if((*i)!="")\r
689                         {\r
690                                 m_boards.push_back((*i));\r
691                         }\r
692                 }\r
693         }\r
694         // followup-to board - must be done after board vector populated\r
695         if(mime.GetFieldValue("Followup-To"))\r
696         {\r
697                 m_replyboardname=mime.GetFieldValue("Followup-To");\r
698                 // remove any path folding\r
699                 m_replyboardname=StringFunctions::Replace(m_replyboardname,"\r\n","");\r
700                 m_replyboardname=StringFunctions::Replace(m_replyboardname,"\t","");\r
701                 std::vector<std::string> parts;\r
702                 StringFunctions::Split(m_replyboardname,",",parts);\r
703                 if(parts.size()>1)\r
704                 {\r
705                         m_replyboardname=parts[0];\r
706                 }\r
707         }\r
708         else\r
709         {\r
710                 if(m_boards.size()>0)\r
711                 {\r
712                         m_replyboardname=m_boards[0];\r
713                 }\r
714         }\r
715         // subject\r
716         if(mime.GetFieldValue("Subject"))\r
717         {\r
718                 m_subject=mime.GetFieldValue("Subject");\r
719                 // remove any path folding\r
720                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
721                 m_subject=StringFunctions::Replace(m_subject,"\t","");\r
722 #if DO_CHARSET_CONVERSION\r
723                 if(mime.GetFieldCharset("Subject"))\r
724                 {\r
725                         std::string charset=mime.GetFieldCharset("Subject");\r
726                         CharsetConverter ccv;\r
727                         if(charset!="" && charset!="UTF-8" && ccv.SetConversion(charset,"UTF-8"))\r
728                         {\r
729                                 std::string output="";\r
730                                 ccv.Convert(m_subject,output);\r
731                                 m_subject=output;\r
732                         }\r
733                 }\r
734 #endif\r
735         }\r
736         else\r
737         {\r
738                 m_subject="No Subject";\r
739         }\r
740         // references\r
741         if(mime.GetFieldValue("References"))\r
742         {\r
743                 std::string temp=mime.GetFieldValue("References");\r
744                 // remove any path folding\r
745                 temp=StringFunctions::Replace(temp,"\r\n","");\r
746                 temp=StringFunctions::Replace(temp,"\t"," ");\r
747                 std::vector<std::string> parts;\r
748                 int count=0;\r
749                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
750                 for(std::vector<std::string>::reverse_iterator i=parts.rbegin(); i!=parts.rend(); i++)\r
751                 {\r
752                         if((*i).size()>2)\r
753                         {\r
754                                 // get rid of < and > and any whitespace\r
755                                 (*i)=StringFunctions::Replace((*i),"<","");\r
756                                 (*i)=StringFunctions::Replace((*i),">","");\r
757                                 (*i)=StringFunctions::TrimWhitespace((*i));\r
758                                 /*\r
759                                 // erase @ and everything after\r
760                                 if((*i).find("@")!=std::string::npos)\r
761                                 {\r
762                                         (*i).erase((*i).find("@"));\r
763                                 }\r
764                                 */\r
765                                 // only erase after @ if message is old type with @freenetproject.org\r
766                                 if((*i).find("@freenetproject.org")!=std::string::npos)\r
767                                 {\r
768                                         (*i).erase((*i).find("@"));\r
769                                 }\r
770                                 if((*i)!="")\r
771                                 {\r
772                                         m_inreplyto[count++]=(*i);\r
773                                 }\r
774                         }\r
775                 }\r
776         }\r
777 \r
778         CMimeBody::CBodyList mbl;\r
779         mime.GetBodyPartList(mbl);\r
780 \r
781         // append all text parts of nntp message to body\r
782         for(CMimeBody::CBodyList::iterator i=mbl.begin(); i!=mbl.end(); i++)\r
783         {\r
784                 if((*i)->IsText() && (*i)->GetContent())\r
785                 {\r
786                         std::string bodypart=(char *)(*i)->GetContent();\r
787 #ifdef DO_CHARSET_CONVERSION\r
788                         std::string charset=(*i)->GetCharset();\r
789                         if(charset!="" && charset!="UTF-8")\r
790                         {\r
791                                 CharsetConverter ccv;\r
792                                 if(ccv.SetConversion(charset,"UTF-8"))\r
793                                 {\r
794                                         std::string output="";\r
795                                         ccv.Convert(bodypart,output);\r
796                                         bodypart=output;\r
797                                 }\r
798                         }\r
799 #endif\r
800                         m_body+=bodypart;\r
801                 }\r
802                 // add a binary file attachment\r
803                 else if(((*i)->GetName()!="" || (*i)->GetFilename()!="") && (*i)->GetLength()>0 && (*i)->GetContent())\r
804                 {\r
805                         std::string filename="";\r
806                         std::string contenttype="";\r
807                         std::vector<unsigned char> data((*i)->GetContent(),(*i)->GetContent()+(*i)->GetContentLength());\r
808                         if((*i)->GetContentType())\r
809                         {\r
810                                 contenttype=(*i)->GetContentType();\r
811                                 // find first ; tab cr or lf and erase it and everything after it\r
812                                 std::string::size_type endpos=contenttype.find_first_of(";\t\r\n ");\r
813                                 if(endpos!=std::string::npos)\r
814                                 {\r
815                                         contenttype.erase(endpos);\r
816                                 }\r
817                         }\r
818                         filename=(*i)->GetFilename();\r
819                         if(filename=="")\r
820                         {\r
821                                 filename=(*i)->GetName();\r
822                         }\r
823                         m_fileattachments.push_back(fileattachment(filename,contenttype,data));\r
824                 }\r
825         }\r
826 \r
827         return true;\r
828 }\r
829 \r
830 const bool Message::StartFreenetInsert()\r
831 {\r
832 \r
833         MessageXML xml;\r
834         int localidentityid=-1;\r
835 \r
836         StripAdministrationBoards();\r
837 \r
838         if(m_boards.size()>0)\r
839         {\r
840 \r
841                 xml.SetMessageID(m_messageuuid);\r
842                 xml.SetSubject(m_subject);\r
843                 xml.SetBody(m_body);\r
844                 xml.SetReplyBoard(m_replyboardname);\r
845                 \r
846                 for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
847                 {\r
848                         xml.AddBoard((*i));\r
849                 }\r
850                 \r
851                 for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
852                 {\r
853                         xml.AddInReplyTo((*j).first,(*j).second);\r
854                 }\r
855 \r
856                 localidentityid=FindLocalIdentityID(m_fromname);\r
857                 if(localidentityid==-1)\r
858                 {\r
859                         return false;\r
860                 }\r
861 \r
862                 // add the message delay if there is one\r
863                 SQLite3DB::Statement st=m_db->Prepare("SELECT MinMessageDelay,MaxMessageDelay FROM tblLocalIdentity WHERE LocalIdentityID=?;");\r
864                 st.Bind(0,localidentityid);\r
865                 st.Step();\r
866                 if(st.RowReturned())\r
867                 {\r
868                         int min=0;\r
869                         int max=0;\r
870                         st.ResultInt(0,min);\r
871                         st.ResultInt(1,max);\r
872 \r
873                         min<0 ? min=0 : false;\r
874                         max<0 ? max=0 : false;\r
875                         min>max ? min=max : false;\r
876 \r
877                         if(min==max)\r
878                         {\r
879                                 m_datetime+=Poco::Timespan(0,0,min,0,0);\r
880                         }\r
881                         else if(max>min)\r
882                         {\r
883                                 int delay=(rand()%(max-min))+min;\r
884                                 m_datetime+=Poco::Timespan(0,0,delay,0,0);\r
885                         }\r
886 \r
887                 }\r
888                 st.Finalize();\r
889 \r
890                 // set date in xml file AFTER we set the delay\r
891                 xml.SetDate(Poco::DateTimeFormatter::format(m_datetime,"%Y-%m-%d"));\r
892                 xml.SetTime(Poco::DateTimeFormatter::format(m_datetime,"%H:%M:%S"));\r
893 \r
894                 st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML,SendDate) VALUES(?,?,?,?);");\r
895                 st.Bind(0,localidentityid);\r
896                 st.Bind(1,m_messageuuid);\r
897                 st.Bind(2,xml.GetXML());\r
898                 st.Bind(3,Poco::DateTimeFormatter::format(m_datetime,"%Y-%m-%d %H:%M:%S"));\r
899                 st.Step();\r
900 \r
901                 // insert file attachments into database\r
902                 st=m_db->Prepare("INSERT INTO tblFileInserts(MessageUUID,FileName,Size,MimeType,Data) VALUES(?,?,?,?,?);");\r
903                 for(std::vector<fileattachment>::iterator i=m_fileattachments.begin(); i!=m_fileattachments.end(); i++)\r
904                 {\r
905                         st.Bind(0,m_messageuuid);\r
906                         st.Bind(1,(*i).m_filename);\r
907                         st.Bind(2,(long)(*i).m_data.size());\r
908                         st.Bind(3,(*i).m_mimetype);\r
909                         st.Bind(4,&((*i).m_data[0]),(*i).m_data.size());\r
910                         st.Step();\r
911                         st.Reset();\r
912                 }\r
913 \r
914                 HandleChangeTrust();\r
915 \r
916         }\r
917 \r
918         return true;\r
919 \r
920 }\r
921 \r
922 void Message::StripAdministrationBoards()\r
923 {\r
924         SQLite3DB::Statement st=m_db->Prepare("SELECT tblBoard.BoardID FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID WHERE BoardName=?;");\r
925         for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); )\r
926         {\r
927                 st.Bind(0,(*i));\r
928                 st.Step();\r
929                 if(st.RowReturned())\r
930                 {\r
931                         if(m_replyboardname==(*i))\r
932                         {\r
933                                 m_replyboardname="";\r
934                         }\r
935                         i=m_boards.erase(i);\r
936                 }\r
937                 else\r
938                 {\r
939                         i++;\r
940                 }\r
941                 st.Reset();\r
942         }\r
943         if(m_replyboardname=="" && m_boards.begin()!=m_boards.end())\r
944         {\r
945                 m_replyboardname=(*m_boards.begin());\r
946         }\r
947 }\r