0445e294e71125b5612aae3978b82a1b83456b12
[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()\r
22 {\r
23         Initialize();\r
24 }\r
25 \r
26 Message::Message(const long messageid)\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::Instance()->Get("ChangeMessageTrustOnReply",tempval);\r
404         StringFunctions::Convert(tempval,m_changemessagetrustonreply);\r
405         Option::Instance()->Get("AddNewPostFromIdentities",tempval);\r
406         if(tempval=="true")\r
407         {\r
408                 m_addnewpostfromidentities=true;\r
409         }\r
410         else\r
411         {\r
412                 m_addnewpostfromidentities=false;\r
413         }\r
414         tempval="50";\r
415         Option::Instance()->Get("MinLocalMessageTrust",tempval);\r
416         StringFunctions::Convert(tempval,m_minlocalmessagetrust);\r
417         tempval="51";\r
418         Option::Instance()->Get("MinLocalTrustListTrust",tempval);\r
419         StringFunctions::Convert(tempval,m_minlocaltrustlisttrust);\r
420 }\r
421 \r
422 const bool Message::Load(const long messageid, const long boardid)\r
423 {\r
424         \r
425         Initialize();\r
426 \r
427         std::string sql;\r
428         \r
429         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
430         if(boardid!=-1)\r
431         {\r
432                 sql+=" AND tblMessageBoard.BoardID=?";\r
433         }\r
434         sql+=";";\r
435 \r
436         SQLite3DB::Statement st=m_db->Prepare(sql);\r
437         st.Bind(0,messageid);\r
438         if(boardid!=-1)\r
439         {\r
440                 st.Bind(1,boardid);\r
441         }\r
442         st.Step();\r
443 \r
444         if(st.RowReturned())\r
445         {\r
446                 std::string tempdate;\r
447                 std::string temptime;\r
448                 int tempint=-1;\r
449                 st.ResultInt(0,tempint);\r
450                 m_messageid=tempint;\r
451                 st.ResultText(1,m_messageuuid);\r
452                 st.ResultText(2,m_subject);\r
453                 st.ResultText(3,m_body);\r
454                 st.ResultText(4,m_replyboardname);\r
455                 st.ResultText(5,tempdate);\r
456                 st.ResultText(6,temptime);\r
457                 st.ResultText(7,m_fromname);\r
458                 st.Finalize();\r
459 \r
460                 int tzdiff=0;\r
461                 if(Poco::DateTimeParser::tryParse(tempdate + " " + temptime,m_datetime,tzdiff)==false)\r
462                 {\r
463                         m_log->error("Message::Load couldn't parse date/time "+tempdate+" "+temptime);\r
464                 }\r
465 \r
466                 // strip off any \r\n in subject\r
467                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
468 \r
469                 // get board list\r
470                 st=m_db->Prepare("SELECT tblBoard.BoardName FROM tblBoard INNER JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID WHERE tblMessageBoard.MessageID=?;");\r
471                 st.Bind(0,messageid);\r
472                 st.Step();\r
473                 while(st.RowReturned())\r
474                 {\r
475                         std::string tempval;\r
476                         st.ResultText(0,tempval);\r
477                         m_boards.push_back(tempval);\r
478                         st.Step();\r
479                 }\r
480                 st.Finalize();\r
481 \r
482                 // get in reply to list\r
483                 st=m_db->Prepare("SELECT ReplyToMessageUUID, ReplyOrder FROM tblMessageReplyTo INNER JOIN tblMessage ON tblMessageReplyTo.MessageID=tblMessage.MessageID WHERE tblMessage.MessageID=?;");\r
484                 st.Bind(0,messageid);\r
485                 st.Step();\r
486                 while(st.RowReturned())\r
487                 {\r
488                         std::string tempval;\r
489                         int tempint;\r
490                         st.ResultText(0,tempval);\r
491                         st.ResultInt(1,tempint);\r
492                         m_inreplyto[tempint]=tempval;\r
493                         st.Step();\r
494                 }\r
495                 st.Finalize();\r
496 \r
497                 return true;\r
498         }\r
499         else\r
500         {\r
501                 return false;\r
502         }\r
503 \r
504 }\r
505 \r
506 const bool Message::Load(const std::string &messageuuid)\r
507 {\r
508 \r
509         std::string uuid=messageuuid;\r
510 \r
511         if(uuid.size()>0 && uuid[0]=='<')\r
512         {\r
513                 uuid.erase(0,1);\r
514         }\r
515         if(uuid.size()>0 && uuid[uuid.size()-1]=='>')\r
516         {\r
517                 uuid.erase(uuid.size()-1);\r
518         }\r
519         if(uuid.find("@freenetproject.org")!=std::string::npos)\r
520         {\r
521                 uuid.erase(uuid.find("@freenetproject.org"));\r
522         }\r
523 \r
524         SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE MessageUUID=?;");\r
525         st.Bind(0,uuid);\r
526         st.Step();\r
527 \r
528         if(st.RowReturned())\r
529         {\r
530                 int messageid;\r
531                 st.ResultInt(0,messageid);\r
532 \r
533                 return Load(messageid);\r
534         }\r
535         else\r
536         {\r
537                 return false;\r
538         }\r
539 }\r
540 \r
541 const bool Message::LoadNext(const long messageid, const long boardid)\r
542 {\r
543         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID>?";\r
544         if(boardid!=-1)\r
545         {\r
546                 sql+=" AND tblMessageBoard.BoardID=?";\r
547         }\r
548         sql+=";";\r
549 \r
550         SQLite3DB::Statement st=m_db->Prepare(sql);\r
551 \r
552         st.Bind(0,messageid);\r
553         if(boardid!=-1)\r
554         {\r
555                 st.Bind(1,boardid);\r
556         }\r
557         st.Step();\r
558 \r
559         if(st.RowReturned())\r
560         {\r
561                 int result;\r
562                 st.ResultInt(0,result);\r
563                 return Load(result,boardid);\r
564         }\r
565         else\r
566         {\r
567                 return false;\r
568         }\r
569 }\r
570 \r
571 const bool Message::LoadPrevious(const long messageid, const long boardid)\r
572 {\r
573         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID<?";\r
574         if(boardid!=-1)\r
575         {\r
576                 sql+=" AND tblMessageBoard.BoardID=?";\r
577         }\r
578         sql+=" ORDER BY tblMessage.MessageID DESC;";\r
579 \r
580         SQLite3DB::Statement st=m_db->Prepare(sql);\r
581 \r
582         st.Bind(0,messageid);\r
583         if(boardid!=-1)\r
584         {\r
585                 st.Bind(1,boardid);\r
586         }\r
587         st.Step();\r
588 \r
589         if(st.RowReturned())\r
590         {\r
591                 int result;\r
592                 st.ResultInt(0,result);\r
593                 return Load(result,boardid);\r
594         }\r
595         else\r
596         {\r
597                 return false;\r
598         }\r
599 }\r
600 \r
601 const bool Message::ParseNNTPMessage(const std::string &nntpmessage)\r
602 {\r
603 \r
604         Initialize();\r
605 \r
606         Poco::UUIDGenerator uuidgen;\r
607         Poco::UUID uuid;\r
608         CMimeMessage mime;\r
609         mime.Load(nntpmessage.c_str(),nntpmessage.size());\r
610 \r
611         // get header info\r
612         // date is always set to now regardless of what message has\r
613         m_datetime=Poco::Timestamp();\r
614 \r
615         // messageuuid is always a unique id we generate regardless of message message-id\r
616         try\r
617         {\r
618                 uuid=uuidgen.createRandom();\r
619                 m_messageuuid=uuid.toString();\r
620                 StringFunctions::UpperCase(m_messageuuid,m_messageuuid);\r
621         }\r
622         catch(...)\r
623         {\r
624                 m_log->fatal("Message::ParseNNTPMessage could not create UUID");\r
625         }\r
626         \r
627         // get from\r
628         if(mime.GetFieldValue("From"))\r
629         {\r
630                 m_fromname=mime.GetFieldValue("From");\r
631                 // remove any path folding\r
632                 m_fromname=StringFunctions::Replace(m_fromname,"\r\n","");\r
633                 m_fromname=StringFunctions::Replace(m_fromname,"\t","");\r
634                 // strip off everything between () and <> and any whitespace\r
635                 std::string::size_type startpos=m_fromname.find("(");\r
636                 std::string::size_type endpos;\r
637                 if(startpos!=std::string::npos)\r
638                 {\r
639                         endpos=m_fromname.find(")",startpos);\r
640                         if(endpos!=std::string::npos)\r
641                         {\r
642                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
643                         }\r
644                 }\r
645                 startpos=m_fromname.find("<");\r
646                 if(startpos!=std::string::npos)\r
647                 {\r
648                         endpos=m_fromname.find(">",startpos);\r
649                         if(endpos!=std::string::npos)\r
650                         {\r
651                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
652                         }\r
653                 }\r
654                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
655 \r
656                 // trim off " from beginning and end\r
657                 if(m_fromname.size()>0 && m_fromname[0]=='\"')\r
658                 {\r
659                         m_fromname.erase(0,1);\r
660                 }\r
661                 if(m_fromname.size()>0 && m_fromname[m_fromname.size()-1]=='\"')\r
662                 {\r
663                         m_fromname.erase(m_fromname.size()-1,1);\r
664                 }\r
665 \r
666                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
667         }\r
668         else\r
669         {\r
670                 m_fromname="Anonymous";\r
671         }\r
672         // get boards posted to\r
673         if(mime.GetFieldValue("Newsgroups"))\r
674         {\r
675                 std::string temp=mime.GetFieldValue("Newsgroups");\r
676                 // remove any path folding\r
677                 temp=StringFunctions::Replace(temp,"\r\n","");\r
678                 temp=StringFunctions::Replace(temp,"\t","");\r
679                 std::vector<std::string> parts;\r
680                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
681                 for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
682                 {\r
683                         (*i)=StringFunctions::Replace((*i),"<","");\r
684                         (*i)=StringFunctions::Replace((*i),">","");\r
685                         (*i)=StringFunctions::TrimWhitespace((*i));\r
686                         if((*i)!="")\r
687                         {\r
688                                 m_boards.push_back((*i));\r
689                         }\r
690                 }\r
691         }\r
692         // followup-to board - must be done after board vector populated\r
693         if(mime.GetFieldValue("Followup-To"))\r
694         {\r
695                 m_replyboardname=mime.GetFieldValue("Followup-To");\r
696                 // remove any path folding\r
697                 m_replyboardname=StringFunctions::Replace(m_replyboardname,"\r\n","");\r
698                 m_replyboardname=StringFunctions::Replace(m_replyboardname,"\t","");\r
699                 std::vector<std::string> parts;\r
700                 StringFunctions::Split(m_replyboardname,",",parts);\r
701                 if(parts.size()>1)\r
702                 {\r
703                         m_replyboardname=parts[0];\r
704                 }\r
705         }\r
706         else\r
707         {\r
708                 if(m_boards.size()>0)\r
709                 {\r
710                         m_replyboardname=m_boards[0];\r
711                 }\r
712         }\r
713         // subject\r
714         if(mime.GetFieldValue("Subject"))\r
715         {\r
716                 m_subject=mime.GetFieldValue("Subject");\r
717                 // remove any path folding\r
718                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
719                 m_subject=StringFunctions::Replace(m_subject,"\t","");\r
720 #if DO_CHARSET_CONVERSION\r
721                 if(mime.GetFieldCharset("Subject"))\r
722                 {\r
723                         std::string charset=mime.GetFieldCharset("Subject");\r
724                         CharsetConverter ccv;\r
725                         if(charset!="" && charset!="UTF-8" && ccv.SetConversion(charset,"UTF-8"))\r
726                         {\r
727                                 std::string output="";\r
728                                 ccv.Convert(m_subject,output);\r
729                                 m_subject=output;\r
730                         }\r
731                 }\r
732 #endif\r
733         }\r
734         else\r
735         {\r
736                 m_subject="No Subject";\r
737         }\r
738         // references\r
739         if(mime.GetFieldValue("References"))\r
740         {\r
741                 std::string temp=mime.GetFieldValue("References");\r
742                 // remove any path folding\r
743                 temp=StringFunctions::Replace(temp,"\r\n","");\r
744                 temp=StringFunctions::Replace(temp,"\t"," ");\r
745                 std::vector<std::string> parts;\r
746                 int count=0;\r
747                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
748                 for(std::vector<std::string>::reverse_iterator i=parts.rbegin(); i!=parts.rend(); i++)\r
749                 {\r
750                         if((*i).size()>2)\r
751                         {\r
752                                 // get rid of < and > and any whitespace\r
753                                 (*i)=StringFunctions::Replace((*i),"<","");\r
754                                 (*i)=StringFunctions::Replace((*i),">","");\r
755                                 (*i)=StringFunctions::TrimWhitespace((*i));\r
756                                 /*\r
757                                 // erase @ and everything after\r
758                                 if((*i).find("@")!=std::string::npos)\r
759                                 {\r
760                                         (*i).erase((*i).find("@"));\r
761                                 }\r
762                                 */\r
763                                 // only erase after @ if message is old type with @freenetproject.org\r
764                                 if((*i).find("@freenetproject.org")!=std::string::npos)\r
765                                 {\r
766                                         (*i).erase((*i).find("@"));\r
767                                 }\r
768                                 if((*i)!="")\r
769                                 {\r
770                                         m_inreplyto[count++]=(*i);\r
771                                 }\r
772                         }\r
773                 }\r
774         }\r
775 \r
776         CMimeBody::CBodyList mbl;\r
777         mime.GetBodyPartList(mbl);\r
778 \r
779         // append all text parts of nntp message to body\r
780         for(CMimeBody::CBodyList::iterator i=mbl.begin(); i!=mbl.end(); i++)\r
781         {\r
782                 if((*i)->IsText() && (*i)->GetContent())\r
783                 {\r
784                         std::string bodypart=(char *)(*i)->GetContent();\r
785 #ifdef DO_CHARSET_CONVERSION\r
786                         std::string charset=(*i)->GetCharset();\r
787                         if(charset!="" && charset!="UTF-8")\r
788                         {\r
789                                 CharsetConverter ccv;\r
790                                 if(ccv.SetConversion(charset,"UTF-8"))\r
791                                 {\r
792                                         std::string output="";\r
793                                         ccv.Convert(bodypart,output);\r
794                                         bodypart=output;\r
795                                 }\r
796                         }\r
797 #endif\r
798                         m_body+=bodypart;\r
799                 }\r
800                 // add a binary file attachment\r
801                 else if(((*i)->GetName()!="" || (*i)->GetFilename()!="") && (*i)->GetLength()>0 && (*i)->GetContent())\r
802                 {\r
803                         std::string filename="";\r
804                         std::string contenttype="";\r
805                         std::vector<unsigned char> data((*i)->GetContent(),(*i)->GetContent()+(*i)->GetContentLength());\r
806                         if((*i)->GetContentType())\r
807                         {\r
808                                 contenttype=(*i)->GetContentType();\r
809                                 // find first ; tab cr or lf and erase it and everything after it\r
810                                 std::string::size_type endpos=contenttype.find_first_of(";\t\r\n ");\r
811                                 if(endpos!=std::string::npos)\r
812                                 {\r
813                                         contenttype.erase(endpos);\r
814                                 }\r
815                         }\r
816                         filename=(*i)->GetFilename();\r
817                         if(filename=="")\r
818                         {\r
819                                 filename=(*i)->GetName();\r
820                         }\r
821                         m_fileattachments.push_back(fileattachment(filename,contenttype,data));\r
822                 }\r
823         }\r
824 \r
825         return true;\r
826 }\r
827 \r
828 const bool Message::StartFreenetInsert()\r
829 {\r
830 \r
831         MessageXML xml;\r
832         int localidentityid=-1;\r
833 \r
834         StripAdministrationBoards();\r
835 \r
836         if(m_boards.size()>0)\r
837         {\r
838 \r
839                 xml.SetMessageID(m_messageuuid);\r
840                 xml.SetSubject(m_subject);\r
841                 xml.SetBody(m_body);\r
842                 xml.SetReplyBoard(m_replyboardname);\r
843                 \r
844                 for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
845                 {\r
846                         xml.AddBoard((*i));\r
847                 }\r
848                 \r
849                 for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
850                 {\r
851                         xml.AddInReplyTo((*j).first,(*j).second);\r
852                 }\r
853 \r
854                 localidentityid=FindLocalIdentityID(m_fromname);\r
855                 if(localidentityid==-1)\r
856                 {\r
857                         return false;\r
858                 }\r
859 \r
860                 // add the message delay if there is one\r
861                 SQLite3DB::Statement st=m_db->Prepare("SELECT MinMessageDelay,MaxMessageDelay FROM tblLocalIdentity WHERE LocalIdentityID=?;");\r
862                 st.Bind(0,localidentityid);\r
863                 st.Step();\r
864                 if(st.RowReturned())\r
865                 {\r
866                         int min=0;\r
867                         int max=0;\r
868                         st.ResultInt(0,min);\r
869                         st.ResultInt(1,max);\r
870 \r
871                         min<0 ? min=0 : false;\r
872                         max<0 ? max=0 : false;\r
873                         min>max ? min=max : false;\r
874 \r
875                         if(min==max)\r
876                         {\r
877                                 m_datetime+=Poco::Timespan(0,0,min,0,0);\r
878                         }\r
879                         else if(max>min)\r
880                         {\r
881                                 int delay=(rand()%(max-min))+min;\r
882                                 m_datetime+=Poco::Timespan(0,0,delay,0,0);\r
883                         }\r
884 \r
885                 }\r
886                 st.Finalize();\r
887 \r
888                 // set date in xml file AFTER we set the delay\r
889                 xml.SetDate(Poco::DateTimeFormatter::format(m_datetime,"%Y-%m-%d"));\r
890                 xml.SetTime(Poco::DateTimeFormatter::format(m_datetime,"%H:%M:%S"));\r
891 \r
892                 st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML,SendDate) VALUES(?,?,?,?);");\r
893                 st.Bind(0,localidentityid);\r
894                 st.Bind(1,m_messageuuid);\r
895                 st.Bind(2,xml.GetXML());\r
896                 st.Bind(3,Poco::DateTimeFormatter::format(m_datetime,"%Y-%m-%d %H:%M:%S"));\r
897                 st.Step();\r
898 \r
899                 // insert file attachments into database\r
900                 st=m_db->Prepare("INSERT INTO tblFileInserts(MessageUUID,FileName,Size,MimeType,Data) VALUES(?,?,?,?,?);");\r
901                 for(std::vector<fileattachment>::iterator i=m_fileattachments.begin(); i!=m_fileattachments.end(); i++)\r
902                 {\r
903                         st.Bind(0,m_messageuuid);\r
904                         st.Bind(1,(*i).m_filename);\r
905                         st.Bind(2,(long)(*i).m_data.size());\r
906                         st.Bind(3,(*i).m_mimetype);\r
907                         st.Bind(4,&((*i).m_data[0]),(*i).m_data.size());\r
908                         st.Step();\r
909                         st.Reset();\r
910                 }\r
911 \r
912                 HandleChangeTrust();\r
913 \r
914         }\r
915 \r
916         return true;\r
917 \r
918 }\r
919 \r
920 void Message::StripAdministrationBoards()\r
921 {\r
922         SQLite3DB::Statement st=m_db->Prepare("SELECT tblBoard.BoardID FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID WHERE BoardName=?;");\r
923         for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); )\r
924         {\r
925                 st.Bind(0,(*i));\r
926                 st.Step();\r
927                 if(st.RowReturned())\r
928                 {\r
929                         if(m_replyboardname==(*i))\r
930                         {\r
931                                 m_replyboardname="";\r
932                         }\r
933                         i=m_boards.erase(i);\r
934                 }\r
935                 else\r
936                 {\r
937                         i++;\r
938                 }\r
939                 st.Reset();\r
940         }\r
941         if(m_replyboardname=="" && m_boards.begin()!=m_boards.end())\r
942         {\r
943                 m_replyboardname=(*m_boards.begin());\r
944         }\r
945 }\r