version 0.3.10
[fms.git] / src / dbmaintenancethread.cpp
1 #include "../include/dbmaintenancethread.h"\r
2 #include "../include/stringfunctions.h"\r
3 #include "../include/option.h"\r
4 \r
5 #include <Poco/Timestamp.h>\r
6 #include <Poco/Timespan.h>\r
7 #include <Poco/DateTimeFormatter.h>\r
8 #include <Poco/Thread.h>\r
9 \r
10 DBMaintenanceThread::DBMaintenanceThread()\r
11 {\r
12         // move last maintenance times back so they will all run soon\r
13         m_last10minute=Poco::Timestamp();\r
14         m_last30minute=Poco::Timestamp();\r
15         m_last30minute-=Poco::Timespan(0,0,11,0,0);\r
16         m_last1hour=Poco::Timestamp();\r
17         m_last1hour-=Poco::Timespan(0,0,49,0,0);\r
18         m_last6hour=Poco::Timestamp();\r
19         m_last6hour-=Poco::Timespan(0,5,42,0,0);\r
20         m_last1day=Poco::Timestamp();\r
21         m_last1day-=Poco::Timespan(0,23,51,0,0);\r
22 \r
23         m_deletemessagesolderthan=180;\r
24         std::string tempval="180";\r
25         Option::Instance()->Get("DeleteMessagesOlderThan",tempval);\r
26         StringFunctions::Convert(tempval,m_deletemessagesolderthan);\r
27 \r
28 }\r
29 \r
30 \r
31 void DBMaintenanceThread::Do10MinuteMaintenance()\r
32 {\r
33 \r
34         m_log->debug("PeriodicDBMaintenance::Do10MinuteMaintenance");\r
35 }\r
36 \r
37 void DBMaintenanceThread::Do30MinuteMaintenance()\r
38 {\r
39 \r
40         m_log->debug("PeriodicDBMaintenance::Do30MinuteMaintenance");\r
41 }\r
42 \r
43 void DBMaintenanceThread::Do1HourMaintenance()\r
44 {\r
45         // recalculate all trust levels - this is CPU instensive\r
46         // do 1 identity at a time as doing it with 1 UPDATE statement locks that database for the duration\r
47         SQLite3DB::Statement st=m_db->Prepare("SELECT TargetIdentityID,PeerMessageTrust,PeerTrustListTrust FROM vwCalculatedPeerTrust;");\r
48         SQLite3DB::Statement upd=m_db->Prepare("UPDATE tblIdentity SET PeerMessageTrust=?, PeerTrustListTrust=? WHERE IdentityID=?");\r
49         st.Step();\r
50         while(st.RowReturned())\r
51         {\r
52                 int identityid=0;\r
53                 int trust=0;\r
54                 \r
55                 st.ResultInt(0,identityid);\r
56 \r
57                 upd.Bind(0,identityid);\r
58                 if(st.ResultNull(1)==false)\r
59                 {\r
60                         trust=0;\r
61                         st.ResultInt(1,trust);\r
62                         upd.Bind(0,trust);\r
63                 }\r
64                 else\r
65                 {\r
66                         upd.Bind(0);\r
67                 }\r
68                 if(st.ResultNull(2)==false)\r
69                 {\r
70                         trust=0;\r
71                         st.ResultInt(2,trust);\r
72                         upd.Bind(1,trust);\r
73                 }\r
74                 else\r
75                 {\r
76                         upd.Bind(1);\r
77                 }\r
78                 upd.Bind(2,identityid);\r
79                 upd.Step();\r
80                 upd.Reset();\r
81 \r
82                 st.Step();\r
83         }\r
84 \r
85         // set null peer trust for identities without a calculated trust\r
86         st=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE IdentityID NOT IN (SELECT TargetIdentityID FROM vwCalculatedPeerTrust);");\r
87         upd=m_db->Prepare("UPDATE tblIdentity SET PeerMessageTrust=NULL, PeerTrustListTrust=NULL WHERE IdentityID=?;");\r
88         st.Step();\r
89         while(st.RowReturned())\r
90         {\r
91                 int identityid=0;\r
92                 st.ResultInt(0,identityid);\r
93                 upd.Bind(0,identityid);\r
94                 upd.Step();\r
95                 upd.Reset();\r
96                 st.Step();\r
97         }\r
98 \r
99         // insert all identities not in trust list already\r
100         m_db->Execute("INSERT INTO tblIdentityTrust(LocalIdentityID,IdentityID) SELECT LocalIdentityID,IdentityID FROM tblLocalIdentity,tblIdentity WHERE LocalIdentityID || '_' || IdentityID NOT IN (SELECT LocalIdentityID || '_' || IdentityID FROM tblIdentityTrust);");\r
101 \r
102         m_log->debug("PeriodicDBMaintenance::Do1HourMaintenance");\r
103 }\r
104 \r
105 void DBMaintenanceThread::Do6HourMaintenance()\r
106 {\r
107 \r
108         // if we remove a board and the reply boardid is still set to it, we need to replace it with a boardid that does exist\r
109         SQLite3DB::Statement st=m_db->Prepare("SELECT MessageID FROM tblMessage WHERE ReplyBoardID NOT IN (SELECT BoardID FROM tblBoard);");\r
110         SQLite3DB::Statement st2=m_db->Prepare("SELECT BoardID FROM tblMessageBoard WHERE MessageID=?;");\r
111         SQLite3DB::Statement upd=m_db->Prepare("UPDATE tblMessage SET ReplyBoardID=? WHERE MessageID=?;");\r
112         st.Step();\r
113         while(st.RowReturned())\r
114         {\r
115                 // find a valid boardid for the message\r
116                 int messageid=0;\r
117                 int boardid=0;\r
118 \r
119                 st.ResultInt(0,messageid);\r
120 \r
121                 st2.Bind(0,messageid);\r
122                 st2.Step();\r
123                 if(st2.RowReturned())\r
124                 {\r
125                         st2.ResultInt(0,boardid);\r
126                         upd.Bind(0,boardid);\r
127                         upd.Bind(1,messageid);\r
128                         upd.Step();\r
129                         upd.Reset();\r
130                 }\r
131                 st2.Reset();\r
132                 \r
133                 st.Step();\r
134         }\r
135 \r
136         m_log->debug("PeriodicDBMaintenance::Do6HourMaintenance");\r
137 }\r
138 \r
139 void DBMaintenanceThread::Do1DayMaintenance()\r
140 {\r
141         Poco::DateTime date;\r
142 \r
143         // delete all puzzles 2 or more days old\r
144         date=Poco::Timestamp();\r
145         date-=Poco::Timespan(2,0,0,0,0);\r
146         m_db->Execute("DELETE FROM tblIntroductionPuzzleInserts WHERE Day<='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
147         m_db->Execute("DELETE FROM tblIntroductionPuzzleRequests WHERE Day<='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
148 \r
149         // delete all identities we've never seen and were added more than 20 days ago\r
150         date=Poco::Timestamp();\r
151         date-=Poco::Timespan(20,0,0,0,0);\r
152         m_db->Execute("DELETE FROM tblIdentity WHERE LastSeen IS NULL AND DateAdded<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
153 \r
154         // delete old identity requests - we don't need them anymore\r
155         date=Poco::Timestamp();\r
156         date-=Poco::Timespan(2,0,0,0,0);\r
157         m_db->Execute("DELETE FROM tblIdentityRequests WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
158 \r
159         // delete old board list inserts/requests - we don't need them anymore\r
160         date=Poco::Timestamp();\r
161         date-=Poco::Timespan(2,0,0,0,0);\r
162         m_db->Execute("DELETE FROM tblBoardListInserts WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
163         m_db->Execute("DELETE FROM tblBoardListRequests WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
164 \r
165         // delete old local identity inserts - we don't need them anymore\r
166         date=Poco::Timestamp();\r
167         date-=Poco::Timespan(2,0,0,0,0);\r
168         m_db->Execute("DELETE FROM tblLocalIdentityInserts WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
169 \r
170         // delete old message list inserts/requests - we don't need them anymore\r
171         date=Poco::Timestamp();\r
172         date-=Poco::Timespan(2,0,0,0,0);\r
173         m_db->Execute("DELETE FROM tblMessageListInserts WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
174         m_db->Execute("DELETE FROM tblMessageListRequests WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
175 \r
176         // delete old trust list inserts/requests - we don't need them anymore\r
177         date=Poco::Timestamp();\r
178         date-=Poco::Timespan(2,0,0,0,0);\r
179         m_db->Execute("DELETE FROM tblTrustListInserts WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
180         m_db->Execute("DELETE FROM tblTrustListRequests WHERE Day<'"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"';");\r
181 \r
182         // delete trust lists from identities we aren't trusting anymore\r
183         m_db->Execute("DELETE FROM tblPeerTrust WHERE IdentityID NOT IN (SELECT IdentityID FROM tblIdentity WHERE (LocalTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalTrustListTrust')) AND (PeerTrustListTrust IS NULL OR PeerTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerTrustListTrust')));");\r
184 \r
185         // remove identityid from messages where the identity has been deleted\r
186         m_db->Execute("UPDATE tblMessage SET IdentityID=NULL WHERE IdentityID NOT IN (SELECT IdentityID FROM tblIdentity);");\r
187 \r
188         // try to re-attach messages from identities that were previously deleted, but have been since re-added\r
189         // first get the names from messages that have a NULL IdentityID\r
190         SQLite3DB::Statement st=m_db->Prepare("SELECT FromName FROM tblMessage WHERE IdentityID IS NULL GROUP BY FromName;");\r
191         st.Step();\r
192         while(st.RowReturned())\r
193         {\r
194                 std::string name="";\r
195                 std::string namepart="";\r
196                 std::string publickey="";\r
197                 int identityid=0;\r
198                 st.ResultText(0,name);\r
199 \r
200                 std::vector<std::string> parts;\r
201                 StringFunctions::Split(name,"@",parts);\r
202 \r
203                 // name can have a @ in it - so reattach all parts except the last which is the key\r
204                 for(long i=0; i<parts.size()-1; i++)\r
205                 {\r
206                         if(i!=0)\r
207                         {\r
208                                 namepart+="@";\r
209                         }\r
210                         namepart+=parts[i];\r
211                 }\r
212 \r
213                 // find identities with this name\r
214                 SQLite3DB::Statement st2=m_db->Prepare("SELECT IdentityID,PublicKey FROM tblIdentity WHERE Name=?;");\r
215                 st2.Bind(0,namepart);\r
216                 st2.Step();\r
217                 while(st2.RowReturned())\r
218                 {\r
219                         publickey="";\r
220                         identityid=0;\r
221                         st2.ResultText(1,publickey);\r
222                         // check if public key matches 2nd part\r
223                         if(parts.size()>1 && publickey.find(parts[1])==4)\r
224                         {\r
225                                 // we have the identity - so update the messages table with the identityid\r
226                                 st2.ResultInt(0,identityid);\r
227 \r
228                                 SQLite3DB::Statement st3=m_db->Prepare("UPDATE tblMessage SET IdentityID=? WHERE FromName=? AND IdentityID IS NULL;");\r
229                                 st3.Bind(0,identityid);\r
230                                 st3.Bind(1,name);\r
231                                 st3.Step();\r
232                         }\r
233                         st2.Step();\r
234                 }\r
235 \r
236                 st.Step();\r
237         }\r
238 \r
239         // delete single use identities that are older than 7 days\r
240         date=Poco::Timestamp();\r
241         date-=Poco::Timespan(7,0,0,0,0);\r
242         st=m_db->Prepare("DELETE FROM tblIdentity WHERE SingleUse='true' AND DateAdded<?;");\r
243         st.Bind(0,Poco::DateTimeFormatter::format(date,"%Y-%m-%d %H:%M:%S"));\r
244         st.Step();\r
245 \r
246         // delete local single use identities that are older than 7 days\r
247         date=Poco::Timestamp();\r
248         date-=Poco::Timespan(7,0,0,0,0);\r
249         st=m_db->Prepare("DELETE FROM tblLocalIdentity WHERE SingleUse='true' AND DateCreated<?;");\r
250         st.Bind(0,Poco::DateTimeFormatter::format(date,"%Y-%m-%d %H:%M:%S"));\r
251         st.Step();\r
252 \r
253         // delete old messages\r
254         date=Poco::Timestamp();\r
255         date-=Poco::Timespan(m_deletemessagesolderthan,0,0,0,0);\r
256         m_log->trace("PeriodicDBMaintenance::Do1DayMaintenance deleting messages prior to "+Poco::DateTimeFormatter::format(date,"%Y-%m-%d"));\r
257         st=m_db->Prepare("DELETE FROM tblMessage WHERE MessageDate<?;");\r
258         st.Bind(0,Poco::DateTimeFormatter::format(date,"%Y-%m-%d"));\r
259         st.Step();\r
260 \r
261         // delete old message requests\r
262         date=Poco::Timestamp();\r
263         date-=Poco::Timespan(90,0,0,0,0);\r
264         st=m_db->Prepare("DELETE FROM tblMessageRequests WHERE Day<?;");\r
265         st.Bind(0,Poco::DateTimeFormatter::format(date,"%Y-%m-%d"));\r
266         st.Step();\r
267 \r
268         // delete tblIdentityTrust for local identities and identities that have been deleted\r
269         m_db->Execute("DELETE FROM tblIdentityTrust WHERE LocalIdentityID NOT IN (SELECT LocalIdentityID FROM tblLocalIdentity);");\r
270         m_db->Execute("DELETE FROM tblIdentityTrust WHERE IdentityID NOT IN (SELECT IdentityID FROM tblIdentity);");\r
271 \r
272         m_log->debug("PeriodicDBMaintenance::Do1DayMaintenance");\r
273 \r
274 }\r
275 \r
276 void DBMaintenanceThread::run()\r
277 {\r
278         m_log->debug("DBMaintenanceThread::run thread started.");\r
279 \r
280         Poco::DateTime now;\r
281 \r
282         do\r
283         {\r
284                 now=Poco::Timestamp();\r
285 \r
286                 if((m_last10minute+Poco::Timespan(0,0,10,0,0))<=now)\r
287                 {\r
288                         Do10MinuteMaintenance();\r
289                         m_last10minute=Poco::Timestamp();\r
290                 }\r
291                 if((m_last30minute+Poco::Timespan(0,0,30,0,0))<=now)\r
292                 {\r
293                         Do30MinuteMaintenance();\r
294                         m_last30minute=Poco::Timestamp();\r
295                 }\r
296                 if((m_last1hour+Poco::Timespan(0,1,0,0,0))<=now)\r
297                 {\r
298                         Do1HourMaintenance();\r
299                         m_last1hour=Poco::Timestamp();\r
300                 }\r
301                 if((m_last6hour+Poco::Timespan(0,6,0,0,0))<=now)\r
302                 {\r
303                         Do6HourMaintenance();\r
304                         m_last6hour=Poco::Timestamp();\r
305                 }\r
306                 if((m_last1day+Poco::Timespan(1,0,0,0,0))<=now)\r
307                 {\r
308                         Do1DayMaintenance();\r
309                         m_last1day=Poco::Timestamp();\r
310                 }\r
311 \r
312                 Poco::Thread::sleep(1000);\r
313         }while(!IsCancelled());\r
314 \r
315         m_log->debug("DBMaintenanceThread::run thread exiting.");\r
316 }\r