version 0.1.7
[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                 // get board list\r
125                 st=m_db->Prepare("SELECT tblBoard.BoardName FROM tblBoard INNER JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID WHERE tblMessageBoard.MessageID=?;");\r
126                 st.Bind(0,messageid);\r
127                 st.Step();\r
128                 while(st.RowReturned())\r
129                 {\r
130                         std::string tempval;\r
131                         st.ResultText(0,tempval);\r
132                         m_boards.push_back(tempval);\r
133                         st.Step();\r
134                 }\r
135                 st.Finalize();\r
136 \r
137                 // get in reply to list\r
138                 st=m_db->Prepare("SELECT ReplyToMessageUUID, ReplyOrder FROM tblMessageReplyTo INNER JOIN tblMessage ON tblMessageReplyTo.MessageID=tblMessage.MessageID WHERE tblMessage.MessageID=?;");\r
139                 st.Bind(0,messageid);\r
140                 st.Step();\r
141                 while(st.RowReturned())\r
142                 {\r
143                         std::string tempval;\r
144                         int tempint;\r
145                         st.ResultText(0,tempval);\r
146                         st.ResultInt(1,tempint);\r
147                         m_inreplyto[tempint]=tempval;\r
148                         st.Step();\r
149                 }\r
150                 st.Finalize();\r
151 \r
152                 return true;\r
153         }\r
154         else\r
155         {\r
156                 return false;\r
157         }\r
158 \r
159 }\r
160 \r
161 const bool Message::Load(const std::string &messageuuid)\r
162 {\r
163         SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE MessageUUID=?;");\r
164         st.Bind(0,messageuuid);\r
165         st.Step();\r
166 \r
167         if(st.RowReturned())\r
168         {\r
169                 int messageid;\r
170                 st.ResultInt(0,messageid);\r
171 \r
172                 return Load(messageid);\r
173         }\r
174         else\r
175         {\r
176                 return false;\r
177         }\r
178 }\r
179 \r
180 const bool Message::LoadNext(const long messageid, const long boardid)\r
181 {\r
182         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID>?";\r
183         if(boardid!=-1)\r
184         {\r
185                 sql+=" AND tblMessageBoard.BoardID=?";\r
186         }\r
187         sql+=";";\r
188 \r
189         SQLite3DB::Statement st=m_db->Prepare(sql);\r
190 \r
191         st.Bind(0,messageid);\r
192         if(boardid!=-1)\r
193         {\r
194                 st.Bind(1,boardid);\r
195         }\r
196         st.Step();\r
197 \r
198         if(st.RowReturned())\r
199         {\r
200                 int result;\r
201                 st.ResultInt(0,result);\r
202                 return Load(result,boardid);\r
203         }\r
204         else\r
205         {\r
206                 return false;\r
207         }\r
208 }\r
209 \r
210 const bool Message::LoadPrevious(const long messageid, const long boardid)\r
211 {\r
212         std::string sql="SELECT tblMessage.MessageID FROM tblMessage INNER JOIN tblMessageBoard ON tblMessage.MessageID=tblMessageBoard.MessageID WHERE tblMessage.MessageID<?";\r
213         if(boardid!=-1)\r
214         {\r
215                 sql+=" AND tblMessageBoard.BoardID=?";\r
216         }\r
217         sql+=" ORDER BY tblMessage.MessageID DESC;";\r
218 \r
219         SQLite3DB::Statement st=m_db->Prepare(sql);\r
220 \r
221         st.Bind(0,messageid);\r
222         if(boardid!=-1)\r
223         {\r
224                 st.Bind(1,boardid);\r
225         }\r
226         st.Step();\r
227 \r
228         if(st.RowReturned())\r
229         {\r
230                 int result;\r
231                 st.ResultInt(0,result);\r
232                 return Load(result,boardid);\r
233         }\r
234         else\r
235         {\r
236                 return false;\r
237         }\r
238 }\r
239 \r
240 const bool Message::ParseNNTPMessage(const std::string &nntpmessage)\r
241 {\r
242 \r
243         Initialize();\r
244 \r
245         UUIDGenerator uuid;\r
246         CMimeMessage mime;\r
247         mime.Load(nntpmessage.c_str(),nntpmessage.size());\r
248 \r
249         // get header info\r
250         // date is always set to now regardless of what message has\r
251         m_datetime.SetToGMTime();\r
252         // messageuuid is always a unique id we generate regardless of message message-id\r
253         m_messageuuid=uuid.Generate();\r
254         // get from\r
255         if(mime.GetFieldValue("From"))\r
256         {\r
257                 m_fromname=mime.GetFieldValue("From");\r
258                 // strip off everything between () and <> and any whitespace\r
259                 std::string::size_type startpos=m_fromname.find("(");\r
260                 std::string::size_type endpos;\r
261                 if(startpos!=std::string::npos)\r
262                 {\r
263                         endpos=m_fromname.find(")",startpos);\r
264                         if(endpos!=std::string::npos)\r
265                         {\r
266                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
267                         }\r
268                 }\r
269                 startpos=m_fromname.find("<");\r
270                 if(startpos!=std::string::npos)\r
271                 {\r
272                         endpos=m_fromname.find(">",startpos);\r
273                         if(endpos!=std::string::npos)\r
274                         {\r
275                                 m_fromname.erase(startpos,(endpos-startpos)+1);\r
276                         }\r
277                 }\r
278                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
279 \r
280                 // trim off " from beginning and end\r
281                 if(m_fromname.size()>0 && m_fromname[0]=='\"')\r
282                 {\r
283                         m_fromname.erase(0,1);\r
284                 }\r
285                 if(m_fromname.size()>0 && m_fromname[m_fromname.size()-1]=='\"')\r
286                 {\r
287                         m_fromname.erase(m_fromname.size()-1,1);\r
288                 }\r
289 \r
290                 m_fromname=StringFunctions::TrimWhitespace(m_fromname);\r
291         }\r
292         else\r
293         {\r
294                 m_fromname="Anonymous";\r
295         }\r
296         // get boards posted to\r
297         if(mime.GetFieldValue("Newsgroups"))\r
298         {\r
299                 std::string temp=mime.GetFieldValue("Newsgroups");\r
300                 std::vector<std::string> parts;\r
301                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
302                 for(std::vector<std::string>::iterator i=parts.begin(); i!=parts.end(); i++)\r
303                 {\r
304                         (*i)=StringFunctions::Replace((*i),"<","");\r
305                         (*i)=StringFunctions::Replace((*i),">","");\r
306                         (*i)=StringFunctions::TrimWhitespace((*i));\r
307                         if((*i)!="")\r
308                         {\r
309                                 m_boards.push_back((*i));\r
310                         }\r
311                 }\r
312         }\r
313         // followup-to board - must be done after board vector populated\r
314         if(mime.GetFieldValue("Followup-To"))\r
315         {\r
316                 m_replyboardname=mime.GetFieldValue("Followup-To");\r
317         }\r
318         else\r
319         {\r
320                 if(m_boards.size()>0)\r
321                 {\r
322                         m_replyboardname=m_boards[0];\r
323                 }\r
324         }\r
325         // subject\r
326         if(mime.GetFieldValue("Subject"))\r
327         {\r
328                 m_subject=mime.GetFieldValue("Subject");\r
329         }\r
330         else\r
331         {\r
332                 m_subject="No Subject";\r
333         }\r
334         // references\r
335         if(mime.GetFieldValue("References"))\r
336         {\r
337                 std::string temp=mime.GetFieldValue("References");\r
338                 std::vector<std::string> parts;\r
339                 int count=0;\r
340                 StringFunctions::SplitMultiple(temp,", \t",parts);\r
341                 for(std::vector<std::string>::reverse_iterator i=parts.rbegin(); i!=parts.rend(); i++)\r
342                 {\r
343                         // get rid of < and > and any whitespace\r
344                         (*i)=StringFunctions::Replace((*i),"<","");\r
345                         (*i)=StringFunctions::Replace((*i),">","");\r
346                         (*i)=StringFunctions::TrimWhitespace((*i));\r
347                         // erase @ and everything after\r
348                         if((*i).find("@")!=std::string::npos)\r
349                         {\r
350                                 (*i).erase((*i).find("@"));\r
351                         }\r
352                         if((*i)!="")\r
353                         {\r
354                                 m_inreplyto[count++]=(*i);\r
355                         }\r
356                 }\r
357         }\r
358 \r
359         CMimeBody::CBodyList mbl;\r
360         mime.GetBodyPartList(mbl);\r
361 \r
362         // append all text parts of nntp message to body\r
363         for(CMimeBody::CBodyList::iterator i=mbl.begin(); i!=mbl.end(); i++)\r
364         {\r
365                 if((*i)->IsText() && (*i)->GetContent())\r
366                 {\r
367                         m_body+=(char *)(*i)->GetContent();\r
368                 }\r
369         }\r
370 \r
371         return true;\r
372 }\r
373 \r
374 void Message::StartFreenetInsert()\r
375 {\r
376         MessageXML xml;\r
377         int localidentityid=-1;\r
378 \r
379         xml.SetMessageID(m_messageuuid);\r
380         xml.SetSubject(m_subject);\r
381         xml.SetBody(m_body);\r
382         xml.SetReplyBoard(m_replyboardname);\r
383         xml.SetDate(m_datetime.Format("%Y-%m-%d"));\r
384         xml.SetTime(m_datetime.Format("%H:%M:%S"));\r
385         \r
386         for(std::vector<std::string>::iterator i=m_boards.begin(); i!=m_boards.end(); i++)\r
387         {\r
388                 xml.AddBoard((*i));\r
389         }\r
390         \r
391         for(std::map<long,std::string>::iterator j=m_inreplyto.begin(); j!=m_inreplyto.end(); j++)\r
392         {\r
393                 xml.AddInReplyTo((*j).first,(*j).second);\r
394         }\r
395 \r
396         // find identity to insert with\r
397         SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblLocalIdentity WHERE Name=?;");\r
398         st.Bind(0,m_fromname);\r
399         st.Step();\r
400 \r
401         // couldn't find identity with this name - insert a new identity\r
402         if(!st.RowReturned())\r
403         {\r
404                 DateTime now;\r
405                 now.SetToGMTime();\r
406                 st=m_db->Prepare("INSERT INTO tblLocalIdentity(Name) VALUES(?);");\r
407                 st.Bind(0,m_fromname);\r
408                 st.Step(true);\r
409                 localidentityid=st.GetLastInsertRowID();\r
410         }\r
411         else\r
412         {\r
413                 st.ResultInt(0,localidentityid);\r
414         }\r
415 \r
416         st=m_db->Prepare("INSERT INTO tblMessageInserts(LocalIdentityID,MessageUUID,MessageXML) VALUES(?,?,?);");\r
417         st.Bind(0,localidentityid);\r
418         st.Bind(1,m_messageuuid);\r
419         st.Bind(2,xml.GetXML());\r
420         st.Step();\r
421 \r
422 }\r