version 0.1.8
[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 std::string Message::GetNNTPArticleID() const\r
24 {\r
25         return "<"+m_messageuuid+"@freenetproject.org>";\r
26 }\r
27 \r
28 const std::string Message::GetNNTPBody() const\r
29 {\r
30         return m_body;\r
31 }\r
32 \r
33 const std::string Message::GetNNTPHeaders() const\r
34 {\r
35         std::string rval("");\r
36 \r
37         rval+="From: "+m_fromname+"\r\n";\r
38         rval+="Newsgroups: ";\r
39         for(std::vector<std::string>::const_iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
40         {\r
41                 if(i!=m_boards.begin())\r
42                 {\r
43                         rval+=",";\r
44                 }\r
45                 rval+=(*i);\r
46         }\r
47         rval+="\r\n";\r
48         rval+="Subject: "+m_subject+"\r\n";\r
49         // format time as  : Wdy, DD Mon YY HH:MM:SS TIMEZONE\r
50         rval+="Date: "+m_datetime.Format("%a, %d %b %y %H:%M:%S -0000")+"\r\n";\r
51         if(m_inreplyto.size()>0)\r
52         {\r
53                 rval+="References: ";\r
54                 for(std::map<long,std::string>::const_reverse_iterator j=m_inreplyto.rbegin(); j!=m_inreplyto.rend(); j++)\r
55                 {\r
56                         if(j!=m_inreplyto.rend())\r
57                         {\r
58                                 rval+=" ";\r
59                         }\r
60                         rval+="<"+(*j).second+"@freenetproject.org>";\r
61                 }\r
62                 rval+="\r\n";\r
63         }\r
64         rval+="Followup-To: "+m_replyboardname+"\r\n";\r
65         rval+="Path: freenet\r\n";\r
66         rval+="Message-ID: "+GetNNTPArticleID()+"\r\n";\r
67         rval+="Content-Type: text/plain; charset=UTF-8\r\n";\r
68 \r
69         return rval;\r
70 }\r
71 \r
72 void Message::Initialize()\r
73 {\r
74         m_messageid=-1;\r
75         m_messageuuid="";\r
76         m_subject="";\r
77         m_body="";\r
78         m_replyboardname="";\r
79         m_datetime.Set();\r
80         m_fromname="";\r
81         m_boards.clear();\r
82         m_inreplyto.clear();\r
83 }\r
84 \r
85 const bool Message::Load(const long messageid, const long boardid)\r
86 {\r
87         \r
88         Initialize();\r
89 \r
90         std::string sql;\r
91         \r
92         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
93         if(boardid!=-1)\r
94         {\r
95                 sql+=" AND tblMessageBoard.BoardID=?";\r
96         }\r
97         sql+=";";\r
98 \r
99         SQLite3DB::Statement st=m_db->Prepare(sql);\r
100         st.Bind(0,messageid);\r
101         if(boardid!=-1)\r
102         {\r
103                 st.Bind(1,boardid);\r
104         }\r
105         st.Step();\r
106 \r
107         if(st.RowReturned())\r
108         {\r
109                 std::string tempdate;\r
110                 std::string temptime;\r
111                 int tempint=-1;\r
112                 st.ResultInt(0,tempint);\r
113                 m_messageid=tempint;\r
114                 st.ResultText(1,m_messageuuid);\r
115                 st.ResultText(2,m_subject);\r
116                 st.ResultText(3,m_body);\r
117                 st.ResultText(4,m_replyboardname);\r
118                 st.ResultText(5,tempdate);\r
119                 st.ResultText(6,temptime);\r
120                 m_datetime.Set(tempdate + " " + temptime);\r
121                 st.ResultText(7,m_fromname);\r
122                 st.Finalize();\r
123 \r
124                 // strip off any \r\n in subject\r
125                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
126 \r
127                 // get board list\r
128                 st=m_db->Prepare("SELECT tblBoard.BoardName FROM tblBoard INNER JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID WHERE tblMessageBoard.MessageID=?;");\r
129                 st.Bind(0,messageid);\r
130                 st.Step();\r
131                 while(st.RowReturned())\r
132                 {\r
133                         std::string tempval;\r
134                         st.ResultText(0,tempval);\r
135                         m_boards.push_back(tempval);\r
136                         st.Step();\r
137                 }\r
138                 st.Finalize();\r
139 \r
140                 // get in reply to list\r
141                 st=m_db->Prepare("SELECT ReplyToMessageUUID, ReplyOrder FROM tblMessageReplyTo INNER JOIN tblMessage ON tblMessageReplyTo.MessageID=tblMessage.MessageID WHERE tblMessage.MessageID=?;");\r
142                 st.Bind(0,messageid);\r
143                 st.Step();\r
144                 while(st.RowReturned())\r
145                 {\r
146                         std::string tempval;\r
147                         int tempint;\r
148                         st.ResultText(0,tempval);\r
149                         st.ResultInt(1,tempint);\r
150                         m_inreplyto[tempint]=tempval;\r
151                         st.Step();\r
152                 }\r
153                 st.Finalize();\r
154 \r
155                 return true;\r
156         }\r
157         else\r
158         {\r
159                 return false;\r
160         }\r
161 \r
162 }\r
163 \r
164 const bool Message::Load(const std::string &messageuuid)\r
165 {\r
166         SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE MessageUUID=?;");\r
167         st.Bind(0,messageuuid);\r
168         st.Step();\r
169 \r
170         if(st.RowReturned())\r
171         {\r
172                 int messageid;\r
173                 st.ResultInt(0,messageid);\r
174 \r
175                 return Load(messageid);\r
176         }\r
177         else\r
178         {\r
179                 return false;\r
180         }\r
181 }\r
182 \r
183 const bool Message::LoadNext(const long messageid, const long boardid)\r
184 {\r
185         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID>?";\r
186         if(boardid!=-1)\r
187         {\r
188                 sql+=" AND tblMessageBoard.BoardID=?";\r
189         }\r
190         sql+=";";\r
191 \r
192         SQLite3DB::Statement st=m_db->Prepare(sql);\r
193 \r
194         st.Bind(0,messageid);\r
195         if(boardid!=-1)\r
196         {\r
197                 st.Bind(1,boardid);\r
198         }\r
199         st.Step();\r
200 \r
201         if(st.RowReturned())\r
202         {\r
203                 int result;\r
204                 st.ResultInt(0,result);\r
205                 return Load(result,boardid);\r
206         }\r
207         else\r
208         {\r
209                 return false;\r
210         }\r
211 }\r
212 \r
213 const bool Message::LoadPrevious(const long messageid, const long boardid)\r
214 {\r
215         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID<?";\r
216         if(boardid!=-1)\r
217         {\r
218                 sql+=" AND tblMessageBoard.BoardID=?";\r
219         }\r
220         sql+=" ORDER BY tblMessage.MessageID DESC;";\r
221 \r
222         SQLite3DB::Statement st=m_db->Prepare(sql);\r
223 \r
224         st.Bind(0,messageid);\r
225         if(boardid!=-1)\r
226         {\r
227                 st.Bind(1,boardid);\r
228         }\r
229         st.Step();\r
230 \r
231         if(st.RowReturned())\r
232         {\r
233                 int result;\r
234                 st.ResultInt(0,result);\r
235                 return Load(result,boardid);\r
236         }\r
237         else\r
238         {\r
239                 return false;\r
240         }\r
241 }\r
242 \r
243 const bool Message::ParseNNTPMessage(const std::string &nntpmessage)\r
244 {\r
245 \r
246         Initialize();\r
247 \r
248         UUIDGenerator uuid;\r
249         CMimeMessage mime;\r
250         mime.Load(nntpmessage.c_str(),nntpmessage.size());\r
251 \r
252         // get header info\r
253         // date is always set to now regardless of what message has\r
254         m_datetime.SetToGMTime();\r
255         // messageuuid is always a unique id we generate regardless of message message-id\r
256         m_messageuuid=uuid.Generate();\r
257         // get from\r
258         if(mime.GetFieldValue("From"))\r
259         {\r
260                 m_fromname=mime.GetFieldValue("From");\r
261                 // remove any path folding\r
262                 m_fromname=StringFunctions::Replace(m_fromname,"\r\n","");\r
263                 // strip off everything between () and <> and any whitespace\r
264                 std::string::size_type startpos=m_fromname.find("(");\r
265                 std::string::size_type endpos;\r
266                 if(startpos!=std::string::npos)\r
267                 {\r
268                         endpos=m_fromname.find(")",startpos);\r
269                         if(endpos!=std::string::npos)\r
270                         {\r
271                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
272                         }\r
273                 }\r
274                 startpos=m_fromname.find("<");\r
275                 if(startpos!=std::string::npos)\r
276                 {\r
277                         endpos=m_fromname.find(">",startpos);\r
278                         if(endpos!=std::string::npos)\r
279                         {\r
280                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
281                         }\r
282                 }\r
283                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
284 \r
285                 // trim off " from beginning and end\r
286                 if(m_fromname.size()>0 && m_fromname[0]=='\"')\r
287                 {\r
288                         m_fromname.erase(0,1);\r
289                 }\r
290                 if(m_fromname.size()>0 && m_fromname[m_fromname.size()-1]=='\"')\r
291                 {\r
292                         m_fromname.erase(m_fromname.size()-1,1);\r
293                 }\r
294 \r
295                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
296         }\r
297         else\r
298         {\r
299                 m_fromname="Anonymous";\r
300         }\r
301         // get boards posted to\r
302         if(mime.GetFieldValue("Newsgroups"))\r
303         {\r
304                 std::string temp=mime.GetFieldValue("Newsgroups");\r
305                 // remove any path folding\r
306                 temp=StringFunctions::Replace(temp,"\r\n","");\r
307                 std::vector<std::string> parts;\r
308                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
309                 for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
310                 {\r
311                         (*i)=StringFunctions::Replace((*i),"<","");\r
312                         (*i)=StringFunctions::Replace((*i),">","");\r
313                         (*i)=StringFunctions::TrimWhitespace((*i));\r
314                         if((*i)!="")\r
315                         {\r
316                                 m_boards.push_back((*i));\r
317                         }\r
318                 }\r
319         }\r
320         // followup-to board - must be done after board vector populated\r
321         if(mime.GetFieldValue("Followup-To"))\r
322         {\r
323                 m_replyboardname=mime.GetFieldValue("Followup-To");\r
324                 // remove any path folding\r
325                 m_replyboardname=StringFunctions::Replace(m_replyboardname,"\r\n","");\r
326         }\r
327         else\r
328         {\r
329                 if(m_boards.size()>0)\r
330                 {\r
331                         m_replyboardname=m_boards[0];\r
332                 }\r
333         }\r
334         // subject\r
335         if(mime.GetFieldValue("Subject"))\r
336         {\r
337                 m_subject=mime.GetFieldValue("Subject");\r
338                 // remove any path folding\r
339                 m_subject=StringFunctions::Replace(m_subject,"\r\n","");\r
340         }\r
341         else\r
342         {\r
343                 m_subject="No Subject";\r
344         }\r
345         // references\r
346         if(mime.GetFieldValue("References"))\r
347         {\r
348                 std::string temp=mime.GetFieldValue("References");\r
349                 // remove any path folding\r
350                 temp=StringFunctions::Replace(temp,"\r\n","");\r
351                 std::vector<std::string> parts;\r
352                 int count=0;\r
353                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
354                 for(std::vector<std::string>::reverse_iterator i=parts.rbegin(); i!=parts.rend(); i++)\r
355                 {\r
356                         // get rid of < and > and any whitespace\r
357                         (*i)=StringFunctions::Replace((*i),"<","");\r
358                         (*i)=StringFunctions::Replace((*i),">","");\r
359                         (*i)=StringFunctions::TrimWhitespace((*i));\r
360                         // erase @ and everything after\r
361                         if((*i).find("@")!=std::string::npos)\r
362                         {\r
363                                 (*i).erase((*i).find("@"));\r
364                         }\r
365                         if((*i)!="")\r
366                         {\r
367                                 m_inreplyto[count++]=(*i);\r
368                         }\r
369                 }\r
370         }\r
371 \r
372         CMimeBody::CBodyList mbl;\r
373         mime.GetBodyPartList(mbl);\r
374 \r
375         // append all text parts of nntp message to body\r
376         for(CMimeBody::CBodyList::iterator i=mbl.begin(); i!=mbl.end(); i++)\r
377         {\r
378                 if((*i)->IsText() && (*i)->GetContent())\r
379                 {\r
380                         m_body+=(char *)(*i)->GetContent();\r
381                 }\r
382         }\r
383 \r
384         return true;\r
385 }\r
386 \r
387 void Message::StartFreenetInsert()\r
388 {\r
389         MessageXML xml;\r
390         int localidentityid=-1;\r
391 \r
392         xml.SetMessageID(m_messageuuid);\r
393         xml.SetSubject(m_subject);\r
394         xml.SetBody(m_body);\r
395         xml.SetReplyBoard(m_replyboardname);\r
396         xml.SetDate(m_datetime.Format("%Y-%m-%d"));\r
397         xml.SetTime(m_datetime.Format("%H:%M:%S"));\r
398         \r
399         for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
400         {\r
401                 xml.AddBoard((*i));\r
402         }\r
403         \r
404         for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
405         {\r
406                 xml.AddInReplyTo((*j).first,(*j).second);\r
407         }\r
408 \r
409         // find identity to insert with\r
410         SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblLocalIdentity WHERE Name=?;");\r
411         st.Bind(0,m_fromname);\r
412         st.Step();\r
413 \r
414         // couldn't find identity with this name - insert a new identity\r
415         if(!st.RowReturned())\r
416         {\r
417                 DateTime now;\r
418                 now.SetToGMTime();\r
419                 st=m_db->Prepare("INSERT INTO tblLocalIdentity(Name) VALUES(?);");\r
420                 st.Bind(0,m_fromname);\r
421                 st.Step(true);\r
422                 localidentityid=st.GetLastInsertRowID();\r
423         }\r
424         else\r
425         {\r
426                 st.ResultInt(0,localidentityid);\r
427         }\r
428 \r
429         st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML) VALUES(?,?,?);");\r
430         st.Bind(0,localidentityid);\r
431         st.Bind(1,m_messageuuid);\r
432         st.Bind(2,xml.GetXML());\r
433         st.Step();\r
434 \r
435 }\r