version 0.1.10
[fms.git] / src / message.cpp
1 #include "../include/message.h"\r
2 #include "../include/nntp/mime/Mime.h"\r
3 #include "../include/uuidgenerator.h"\r
4 #include "../include/stringfunctions.h"\r
5 #include "../include/freenet/messagexml.h"\r
6 \r
7 #include <algorithm>\r
8 \r
9 #ifdef XMEM\r
10         #include <xmem.h>\r
11 #endif\r
12 \r
13 Message::Message()\r
14 {\r
15         Initialize();\r
16 }\r
17 \r
18 Message::Message(const long messageid)\r
19 {\r
20         Load(messageid);\r
21 }\r
22 \r
23 const bool Message::CheckForAdministrationBoard(const std::vector<std::string> &boards)\r
24 {\r
25         std::string name;\r
26         SQLite3DB::Statement st=m_db->Prepare("SELECT BoardName FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID;");\r
27         st.Step();\r
28         \r
29         while(st.RowReturned())\r
30         {\r
31                 st.ResultText(0,name);\r
32 \r
33                 if(std::find(boards.begin(),boards.end(),name)!=boards.end())\r
34                 {\r
35                         return true;\r
36                 }\r
37                 \r
38                 st.Step();\r
39         }\r
40 \r
41         return false;\r
42 }\r
43 \r
44 const std::string Message::GetNNTPArticleID() const\r
45 {\r
46         return "<"+m_messageuuid+"@freenetproject.org>";\r
47 }\r
48 \r
49 const std::string Message::GetNNTPBody() const\r
50 {\r
51         return m_body;\r
52 }\r
53 \r
54 const std::string Message::GetNNTPHeaders() const\r
55 {\r
56         std::string rval("");\r
57 \r
58         rval+="From: "+m_fromname+"\r\n";\r
59         rval+="Newsgroups: ";\r
60         for(std::vector<std::string>::const_iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
61         {\r
62                 if(i!=m_boards.begin())\r
63                 {\r
64                         rval+=",";\r
65                 }\r
66                 rval+=(*i);\r
67         }\r
68         rval+="\r\n";\r
69         rval+="Subject: "+m_subject+"\r\n";\r
70         // format time as  : Wdy, DD Mon YY HH:MM:SS TIMEZONE\r
71         rval+="Date: "+m_datetime.Format("%a, %d %b %y %H:%M:%S -0000")+"\r\n";\r
72         if(m_inreplyto.size()>0)\r
73         {\r
74                 rval+="References: ";\r
75                 for(std::map<long,std::string>::const_reverse_iterator j=m_inreplyto.rbegin(); j!=m_inreplyto.rend(); j++)\r
76                 {\r
77                         if(j!=m_inreplyto.rend())\r
78                         {\r
79                                 rval+=" ";\r
80                         }\r
81                         rval+="<"+(*j).second+"@freenetproject.org>";\r
82                 }\r
83                 rval+="\r\n";\r
84         }\r
85         rval+="Followup-To: "+m_replyboardname+"\r\n";\r
86         rval+="Path: freenet\r\n";\r
87         rval+="Message-ID: "+GetNNTPArticleID()+"\r\n";\r
88         rval+="Content-Type: text/plain; charset=UTF-8\r\n";\r
89 \r
90         return rval;\r
91 }\r
92 \r
93 void Message::HandleAdministrationMessage()\r
94 {\r
95         // only continue if this message was actually a reply to another message\r
96         if(m_inreplyto.size()>0)\r
97         {\r
98                 int boardid=0;\r
99                 std::string boardname="";\r
100                 std::string identityname="";\r
101                 int identityid;\r
102                 int changemessagetrust=0;\r
103                 int changetrustlisttrust=0;\r
104                 int origmessagetrust=0;\r
105                 int origtrustlisttrust=0;\r
106                 SQLite3DB::Statement st=m_db->Prepare("SELECT tblBoard.BoardID,BoardName,ModifyLocalMessageTrust,ModifyLocalTrustListTrust FROM tblBoard INNER JOIN tblAdministrationBoard ON tblBoard.BoardID=tblAdministrationBoard.BoardID;");\r
107                 st.Step();\r
108 \r
109                 while(st.RowReturned())\r
110                 {\r
111                         st.ResultInt(0,boardid);\r
112                         st.ResultText(1,boardname);\r
113                         st.ResultInt(2,changemessagetrust);\r
114                         st.ResultInt(3,changetrustlisttrust);\r
115 \r
116                         if(std::find(m_boards.begin(),m_boards.end(),boardname)!=m_boards.end())\r
117                         {\r
118                                 SQLite3DB::Statement origmess=m_db->Prepare("SELECT tblIdentity.IdentityID,tblIdentity.Name,tblIdentity.LocalMessageTrust,tblIdentity.LocalTrustListTrust FROM tblIdentity INNER JOIN tblMessage ON tblIdentity.IdentityID=tblMessage.IdentityID WHERE tblMessage.MessageUUID=?;");\r
119                                 origmess.Bind(0,m_inreplyto[0]);\r
120                                 origmess.Step();\r
121 \r
122                                 if(origmess.RowReturned())\r
123                                 {\r
124                                         origmess.ResultInt(0,identityid);\r
125                                         origmess.ResultText(1,identityname);\r
126                                         origmess.ResultInt(2,origmessagetrust);\r
127                                         origmess.ResultInt(3,origtrustlisttrust);\r
128 \r
129                                         origmessagetrust+=changemessagetrust;\r
130                                         origtrustlisttrust+=changetrustlisttrust;\r
131 \r
132                                         if(origmessagetrust<0)\r
133                                         {\r
134                                                 origmessagetrust=0;\r
135                                         }\r
136                                         if(origmessagetrust>100)\r
137                                         {\r
138                                                 origmessagetrust=100;\r
139                                         }\r
140                                         if(origtrustlisttrust<0)\r
141                                         {\r
142                                                 origtrustlisttrust=0;\r
143                                         }\r
144                                         if(origtrustlisttrust>100)\r
145                                         {\r
146                                                 origtrustlisttrust=100;\r
147                                         }\r
148 \r
149                                         // update new trust levels\r
150                                         SQLite3DB::Statement update=m_db->Prepare("UPDATE tblIdentity SET LocalMessageTrust=?, LocalTrustListTrust=? WHERE IdentityID=?;");\r
151                                         update.Bind(0,origmessagetrust);\r
152                                         update.Bind(1,origtrustlisttrust);\r
153                                         update.Bind(2,identityid);\r
154                                         update.Step();\r
155 \r
156                                         // insert message to show what id was changed and what current levels are\r
157                                         int lastid=0;\r
158                                         std::string messagebody;\r
159                                         std::string messagetruststr="";\r
160                                         std::string trustlisttruststr="";\r
161                                         UUIDGenerator uuid;\r
162                                         DateTime now;\r
163                                         now.SetToGMTime();\r
164                                         StringFunctions::Convert(origmessagetrust,messagetruststr);\r
165                                         StringFunctions::Convert(origtrustlisttrust,trustlisttruststr);\r
166                                         messagebody="Trust Changed for "+identityname+"\r\n";\r
167                                         messagebody+="Local Message Trust : "+messagetruststr+"\r\n";\r
168                                         messagebody+="Local Trust List Trust : "+trustlisttruststr+"\r\n";\r
169                                         SQLite3DB::Statement insert=m_db->Prepare("INSERT INTO tblMessage(FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body) VALUES('FMS',?,?,?,?,?,?);");\r
170                                         insert.Bind(0,now.Format("%Y-%m-%d"));\r
171                                         insert.Bind(1,now.Format("%H:%M:%S"));\r
172                                         insert.Bind(2,identityname+" Trust Changed");\r
173                                         insert.Bind(3,uuid.Generate());\r
174                                         insert.Bind(4,boardid);\r
175                                         insert.Bind(5,messagebody);\r
176                                         insert.Step(true);\r
177                                         lastid=insert.GetLastInsertRowID();\r
178 \r
179                                         insert=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
180                                         insert.Bind(0,lastid);\r
181                                         insert.Bind(1,boardid);\r
182                                         insert.Step();\r
183 \r
184                                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"Message::HandleAdministrationMessage updated "+identityname+" to "+messagetruststr+" , "+trustlisttruststr);\r
185 \r
186                                 }\r
187                         }\r
188 \r
189                         st.Step();\r
190                 }\r
191         }\r
192 \r
193 }\r
194 \r
195 void Message::Initialize()\r
196 {\r
197         m_messageid=-1;\r
198         m_messageuuid="";\r
199         m_subject="";\r
200         m_body="";\r
201         m_replyboardname="";\r
202         m_datetime.Set();\r
203         m_fromname="";\r
204         m_boards.clear();\r
205         m_inreplyto.clear();\r
206 }\r
207 \r
208 const bool Message::Load(const long messageid, const long boardid)\r
209 {\r
210         \r
211         Initialize();\r
212 \r
213         std::string sql;\r
214         \r
215         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
216         if(boardid!=-1)\r
217         {\r
218                 sql+=" AND tblMessageBoard.BoardID=?";\r
219         }\r
220         sql+=";";\r
221 \r
222         SQLite3DB::Statement st=m_db->Prepare(sql);\r
223         st.Bind(0,messageid);\r
224         if(boardid!=-1)\r
225         {\r
226                 st.Bind(1,boardid);\r
227         }\r
228         st.Step();\r
229 \r
230         if(st.RowReturned())\r
231         {\r
232                 std::string tempdate;\r
233                 std::string temptime;\r
234                 int tempint=-1;\r
235                 st.ResultInt(0,tempint);\r
236                 m_messageid=tempint;\r
237                 st.ResultText(1,m_messageuuid);\r
238                 st.ResultText(2,m_subject);\r
239                 st.ResultText(3,m_body);\r
240                 st.ResultText(4,m_replyboardname);\r
241                 st.ResultText(5,tempdate);\r
242                 st.ResultText(6,temptime);\r
243                 m_datetime.Set(tempdate + " " + temptime);\r
244                 st.ResultText(7,m_fromname);\r
245                 st.Finalize();\r
246 \r
247                 // strip off any \r\n in subject\r
248                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
249 \r
250                 // get board list\r
251                 st=m_db->Prepare("SELECT tblBoard.BoardName FROM tblBoard INNER JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID WHERE tblMessageBoard.MessageID=?;");\r
252                 st.Bind(0,messageid);\r
253                 st.Step();\r
254                 while(st.RowReturned())\r
255                 {\r
256                         std::string tempval;\r
257                         st.ResultText(0,tempval);\r
258                         m_boards.push_back(tempval);\r
259                         st.Step();\r
260                 }\r
261                 st.Finalize();\r
262 \r
263                 // get in reply to list\r
264                 st=m_db->Prepare("SELECT ReplyToMessageUUID, ReplyOrder FROM tblMessageReplyTo INNER JOIN tblMessage ON tblMessageReplyTo.MessageID=tblMessage.MessageID WHERE tblMessage.MessageID=?;");\r
265                 st.Bind(0,messageid);\r
266                 st.Step();\r
267                 while(st.RowReturned())\r
268                 {\r
269                         std::string tempval;\r
270                         int tempint;\r
271                         st.ResultText(0,tempval);\r
272                         st.ResultInt(1,tempint);\r
273                         m_inreplyto[tempint]=tempval;\r
274                         st.Step();\r
275                 }\r
276                 st.Finalize();\r
277 \r
278                 return true;\r
279         }\r
280         else\r
281         {\r
282                 return false;\r
283         }\r
284 \r
285 }\r
286 \r
287 const bool Message::Load(const std::string &messageuuid)\r
288 {\r
289         SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE MessageUUID=?;");\r
290         st.Bind(0,messageuuid);\r
291         st.Step();\r
292 \r
293         if(st.RowReturned())\r
294         {\r
295                 int messageid;\r
296                 st.ResultInt(0,messageid);\r
297 \r
298                 return Load(messageid);\r
299         }\r
300         else\r
301         {\r
302                 return false;\r
303         }\r
304 }\r
305 \r
306 const bool Message::LoadNext(const long messageid, const long boardid)\r
307 {\r
308         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID>?";\r
309         if(boardid!=-1)\r
310         {\r
311                 sql+=" AND tblMessageBoard.BoardID=?";\r
312         }\r
313         sql+=";";\r
314 \r
315         SQLite3DB::Statement st=m_db->Prepare(sql);\r
316 \r
317         st.Bind(0,messageid);\r
318         if(boardid!=-1)\r
319         {\r
320                 st.Bind(1,boardid);\r
321         }\r
322         st.Step();\r
323 \r
324         if(st.RowReturned())\r
325         {\r
326                 int result;\r
327                 st.ResultInt(0,result);\r
328                 return Load(result,boardid);\r
329         }\r
330         else\r
331         {\r
332                 return false;\r
333         }\r
334 }\r
335 \r
336 const bool Message::LoadPrevious(const long messageid, const long boardid)\r
337 {\r
338         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID<?";\r
339         if(boardid!=-1)\r
340         {\r
341                 sql+=" AND tblMessageBoard.BoardID=?";\r
342         }\r
343         sql+=" ORDER BY tblMessage.MessageID DESC;";\r
344 \r
345         SQLite3DB::Statement st=m_db->Prepare(sql);\r
346 \r
347         st.Bind(0,messageid);\r
348         if(boardid!=-1)\r
349         {\r
350                 st.Bind(1,boardid);\r
351         }\r
352         st.Step();\r
353 \r
354         if(st.RowReturned())\r
355         {\r
356                 int result;\r
357                 st.ResultInt(0,result);\r
358                 return Load(result,boardid);\r
359         }\r
360         else\r
361         {\r
362                 return false;\r
363         }\r
364 }\r
365 \r
366 const bool Message::ParseNNTPMessage(const std::string &nntpmessage)\r
367 {\r
368 \r
369         Initialize();\r
370 \r
371         UUIDGenerator uuid;\r
372         CMimeMessage mime;\r
373         mime.Load(nntpmessage.c_str(),nntpmessage.size());\r
374 \r
375         // get header info\r
376         // date is always set to now regardless of what message has\r
377         m_datetime.SetToGMTime();\r
378         // messageuuid is always a unique id we generate regardless of message message-id\r
379         m_messageuuid=uuid.Generate();\r
380         // get from\r
381         if(mime.GetFieldValue("From"))\r
382         {\r
383                 m_fromname=mime.GetFieldValue("From");\r
384                 // remove any path folding\r
385                 m_fromname=StringFunctions::Replace(m_fromname,"\r\n","");\r
386                 // strip off everything between () and <> and any whitespace\r
387                 std::string::size_type startpos=m_fromname.find("(");\r
388                 std::string::size_type endpos;\r
389                 if(startpos!=std::string::npos)\r
390                 {\r
391                         endpos=m_fromname.find(")",startpos);\r
392                         if(endpos!=std::string::npos)\r
393                         {\r
394                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
395                         }\r
396                 }\r
397                 startpos=m_fromname.find("<");\r
398                 if(startpos!=std::string::npos)\r
399                 {\r
400                         endpos=m_fromname.find(">",startpos);\r
401                         if(endpos!=std::string::npos)\r
402                         {\r
403                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
404                         }\r
405                 }\r
406                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
407 \r
408                 // trim off " from beginning and end\r
409                 if(m_fromname.size()>0 && m_fromname[0]=='\"')\r
410                 {\r
411                         m_fromname.erase(0,1);\r
412                 }\r
413                 if(m_fromname.size()>0 && m_fromname[m_fromname.size()-1]=='\"')\r
414                 {\r
415                         m_fromname.erase(m_fromname.size()-1,1);\r
416                 }\r
417 \r
418                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
419         }\r
420         else\r
421         {\r
422                 m_fromname="Anonymous";\r
423         }\r
424         // get boards posted to\r
425         if(mime.GetFieldValue("Newsgroups"))\r
426         {\r
427                 std::string temp=mime.GetFieldValue("Newsgroups");\r
428                 // remove any path folding\r
429                 temp=StringFunctions::Replace(temp,"\r\n","");\r
430                 std::vector<std::string> parts;\r
431                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
432                 for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
433                 {\r
434                         (*i)=StringFunctions::Replace((*i),"<","");\r
435                         (*i)=StringFunctions::Replace((*i),">","");\r
436                         (*i)=StringFunctions::TrimWhitespace((*i));\r
437                         if((*i)!="")\r
438                         {\r
439                                 m_boards.push_back((*i));\r
440                         }\r
441                 }\r
442         }\r
443         // followup-to board - must be done after board vector populated\r
444         if(mime.GetFieldValue("Followup-To"))\r
445         {\r
446                 m_replyboardname=mime.GetFieldValue("Followup-To");\r
447                 // remove any path folding\r
448                 m_replyboardname=StringFunctions::Replace(m_replyboardname,"\r\n","");\r
449         }\r
450         else\r
451         {\r
452                 if(m_boards.size()>0)\r
453                 {\r
454                         m_replyboardname=m_boards[0];\r
455                 }\r
456         }\r
457         // subject\r
458         if(mime.GetFieldValue("Subject"))\r
459         {\r
460                 m_subject=mime.GetFieldValue("Subject");\r
461                 // remove any path folding\r
462                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
463         }\r
464         else\r
465         {\r
466                 m_subject="No Subject";\r
467         }\r
468         // references\r
469         if(mime.GetFieldValue("References"))\r
470         {\r
471                 std::string temp=mime.GetFieldValue("References");\r
472                 // remove any path folding\r
473                 temp=StringFunctions::Replace(temp,"\r\n","");\r
474                 std::vector<std::string> parts;\r
475                 int count=0;\r
476                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
477                 for(std::vector<std::string>::reverse_iterator i=parts.rbegin(); i!=parts.rend(); i++)\r
478                 {\r
479                         // get rid of < and > and any whitespace\r
480                         (*i)=StringFunctions::Replace((*i),"<","");\r
481                         (*i)=StringFunctions::Replace((*i),">","");\r
482                         (*i)=StringFunctions::TrimWhitespace((*i));\r
483                         // erase @ and everything after\r
484                         if((*i).find("@")!=std::string::npos)\r
485                         {\r
486                                 (*i).erase((*i).find("@"));\r
487                         }\r
488                         if((*i)!="")\r
489                         {\r
490                                 m_inreplyto[count++]=(*i);\r
491                         }\r
492                 }\r
493         }\r
494 \r
495         CMimeBody::CBodyList mbl;\r
496         mime.GetBodyPartList(mbl);\r
497 \r
498         // append all text parts of nntp message to body\r
499         for(CMimeBody::CBodyList::iterator i=mbl.begin(); i!=mbl.end(); i++)\r
500         {\r
501                 if((*i)->IsText() && (*i)->GetContent())\r
502                 {\r
503                         m_body+=(char *)(*i)->GetContent();\r
504                 }\r
505         }\r
506 \r
507         return true;\r
508 }\r
509 \r
510 void Message::StartFreenetInsert()\r
511 {\r
512 \r
513         MessageXML xml;\r
514         int localidentityid=-1;\r
515 \r
516         xml.SetMessageID(m_messageuuid);\r
517         xml.SetSubject(m_subject);\r
518         xml.SetBody(m_body);\r
519         xml.SetReplyBoard(m_replyboardname);\r
520         xml.SetDate(m_datetime.Format("%Y-%m-%d"));\r
521         xml.SetTime(m_datetime.Format("%H:%M:%S"));\r
522         \r
523         for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
524         {\r
525                 xml.AddBoard((*i));\r
526         }\r
527         \r
528         for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
529         {\r
530                 xml.AddInReplyTo((*j).first,(*j).second);\r
531         }\r
532 \r
533         // find identity to insert with\r
534         SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblLocalIdentity WHERE Name=?;");\r
535         st.Bind(0,m_fromname);\r
536         st.Step();\r
537 \r
538         // couldn't find identity with this name - insert a new identity\r
539         if(!st.RowReturned())\r
540         {\r
541                 DateTime now;\r
542                 now.SetToGMTime();\r
543                 st=m_db->Prepare("INSERT INTO tblLocalIdentity(Name) VALUES(?);");\r
544                 st.Bind(0,m_fromname);\r
545                 st.Step(true);\r
546                 localidentityid=st.GetLastInsertRowID();\r
547         }\r
548         else\r
549         {\r
550                 st.ResultInt(0,localidentityid);\r
551         }\r
552 \r
553         st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML) VALUES(?,?,?);");\r
554         st.Bind(0,localidentityid);\r
555         st.Bind(1,m_messageuuid);\r
556         st.Bind(2,xml.GetXML());\r
557         st.Step();\r
558 \r
559 }\r