1 #include "../include/global.h"
\r
2 #include "../include/datetime.h"
\r
3 #include "../include/logfile.h"
\r
4 #include "../include/option.h"
\r
5 #include "../include/stringfunctions.h"
\r
6 #include "../include/db/sqlite3db.h"
\r
7 #include "../include/freenet/freenetmasterthread.h"
\r
8 #include "../include/nntp/nntplistener.h"
\r
11 #include <winsock2.h>
\r
22 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
24 db->Open("fms.db3");
\r
25 db->SetBusyTimeout(10000); // set timeout to 10 seconds
\r
26 db->Execute("VACUUM;");
\r
28 db->Execute("CREATE TABLE IF NOT EXISTS tblOption(\
\r
29 Option TEXT UNIQUE,\
\r
30 OptionValue TEXT NOT NULL,\
\r
31 OptionDescription TEXT\
\r
34 db->Execute("CREATE TABLE IF NOT EXISTS tblLocalIdentity(\
\r
35 LocalIdentityID INTEGER PRIMARY KEY,\
\r
39 SingleUse BOOL CHECK(SingleUse IN('true','false')) DEFAULT 'false',\
\r
40 PublishTrustList BOOL CHECK(PublishTrustList IN('true','false')) DEFAULT 'false',\
\r
41 PublishBoardList BOOL CHECK(PublishBoardList IN('true','false')) DEFAULT 'false',\
\r
42 InsertingIdentity BOOL CHECK(InsertingIdentity IN('true','false')) DEFAULT 'false',\
\r
43 LastInsertedIdentity DATETIME,\
\r
44 InsertingPuzzle BOOL CHECK(InsertingPuzzle IN('true','false')) DEFAULT 'false',\
\r
45 LastInsertedPuzzle DATETIME,\
\r
46 InsertingTrustList BOOL CHECK(InsertingTrustList IN('true','false')) DEFAULT 'false',\
\r
47 LastInsertedTrustList DATETIME,\
\r
48 InsertingBoardList BOOL CHECK(InsertingBoardList IN('true','false')) DEFAULT 'false',\
\r
49 LastInsertedBoardList DATETIME,\
\r
50 InsertingMessageList BOOL CHECK(InsertingMessageList IN('true','false')) DEFAULT 'false',\
\r
51 LastInsertedMessageList DATETIME\
\r
54 db->Execute("CREATE TABLE IF NOT EXISTS tblLocalIdentityInserts(\
\r
55 LocalIdentityID INTEGER,\
\r
57 InsertIndex INTEGER\
\r
60 db->Execute("CREATE TABLE IF NOT EXISTS tblTrustListInserts(\
\r
61 LocalIdentityID INTEGER,\
\r
63 InsertIndex INTEGER\
\r
66 db->Execute("CREATE TABLE IF NOT EXISTS tblTrustListRequests(\
\r
67 IdentityID INTEGER,\
\r
69 RequestIndex INTEGER,\
\r
70 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
73 db->Execute("CREATE TABLE IF NOT EXISTS tblIntroductionPuzzleInserts(\
\r
75 LocalIdentityID INTEGER,\
\r
77 InsertIndex INTEGER,\
\r
81 PuzzleSolution TEXT,\
\r
82 FoundSolution BOOL CHECK(FoundSolution IN('true','false')) DEFAULT 'false'\
\r
85 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentity(\
\r
86 IdentityID INTEGER PRIMARY KEY,\
\r
87 PublicKey TEXT UNIQUE,\
\r
89 SingleUse BOOL CHECK(SingleUse IN('true','false')) DEFAULT 'false',\
\r
90 PublishTrustList BOOL CHECK(PublishTrustList IN('true','false')) DEFAULT 'false',\
\r
91 PublishBoardList BOOL CHECK(PublishBoardList IN('true','false')) DEFAULT 'false',\
\r
92 DateAdded DATETIME,\
\r
94 LocalMessageTrust INTEGER CHECK(LocalMessageTrust BETWEEN 0 AND 100) DEFAULT 50,\
\r
95 PeerMessageTrust INTEGER CHECK(PeerMessageTrust BETWEEN 0 AND 100) DEFAULT 50,\
\r
96 LocalTrustListTrust INTEGER CHECK(LocalTrustListTrust BETWEEN 0 AND 100) DEFAULT 50,\
\r
97 PeerTrustListTrust INTEGER CHECK(PeerTrustListTrust BETWEEN 0 AND 100) DEFAULT 50\
\r
100 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentityRequests(\
\r
101 IdentityID INTEGER,\
\r
103 RequestIndex INTEGER,\
\r
104 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
107 db->Execute("CREATE TABLE IF NOT EXISTS tblIntroductionPuzzleRequests(\
\r
108 IdentityID INTEGER,\
\r
110 RequestIndex INTEGER,\
\r
111 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false',\
\r
118 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentityIntroductionInserts(\
\r
119 LocalIdentityID INTEGER,\
\r
123 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
126 db->Execute("CREATE TABLE IF NOT EXISTS tblPeerTrust(\
\r
127 IdentityID INTEGER,\
\r
128 TargetIdentityID INTEGER,\
\r
129 MessageTrust INTEGER CHECK(MessageTrust BETWEEN 0 AND 100),\
\r
130 TrustListTrust INTEGER CHECK(TrustListTrust BETWEEN 0 AND 100)\
\r
133 db->Execute("CREATE TABLE IF NOT EXISTS tblBoard(\
\r
134 BoardID INTEGER PRIMARY KEY,\
\r
135 BoardName TEXT UNIQUE,\
\r
136 BoardDescription TEXT,\
\r
137 DateAdded DATETIME\
\r
140 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded) VALUES('fms','Freenet Message System','2007-12-01');");
\r
141 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded) VALUES('freenet','Discussion about Freenet','2007-12-01');");
\r
142 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded) VALUES('public','Public discussion','2007-12-01');");
\r
143 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded) VALUES('test','Test board','2007-12-01');");
\r
145 db->Execute("CREATE TABLE IF NOT EXISTS tblMessage(\
\r
146 MessageID INTEGER PRIMARY KEY,\
\r
147 IdentityID INTEGER,\
\r
152 MessageUUID TEXT UNIQUE,\
\r
153 ReplyBoardID INTEGER,\
\r
157 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageReplyTo(\
\r
158 MessageID INTEGER,\
\r
159 ReplyToMessageUUID TEXT,\
\r
160 ReplyOrder INTEGER\
\r
163 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageBoard(\
\r
164 MessageID INTEGER,\
\r
168 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageListRequests(\
\r
169 IdentityID INTEGER,\
\r
171 RequestIndex INTEGER,\
\r
172 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
175 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageRequests(\
\r
176 IdentityID INTEGER,\
\r
178 RequestIndex INTEGER,\
\r
179 FromMessageList BOOL CHECK(FromMessageList IN('true','false')) DEFAULT 'false',\
\r
180 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
183 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageInserts(\
\r
184 LocalIdentityID INTEGER,\
\r
186 InsertIndex INTEGER,\
\r
187 MessageUUID TEXT UNIQUE,\
\r
189 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
192 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageListInserts(\
\r
193 LocalIdentityID INTEGER,\
\r
195 InsertIndex INTEGER,\
\r
196 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
199 // MessageInserter will insert a record into this temp table which the MessageListInserter will query for and insert a MessageList when needed
\r
200 db->Execute("CREATE TEMPORARY TABLE IF NOT EXISTS tmpMessageListInsert(\
\r
201 LocalIdentityID INTEGER,\
\r
205 // low / high / message count for each board
\r
206 db->Execute("DROP VIEW IF EXISTS vwBoardStats; \
\r
207 CREATE VIEW IF NOT EXISTS vwBoardStats AS \
\r
208 SELECT tblBoard.BoardID AS 'BoardID', IFNULL(MIN(MessageID),0) AS 'LowMessageID', IFNULL(MAX(MessageID),0) AS 'HighMessageID', COUNT(MessageID) AS 'MessageCount' \
\r
209 FROM tblBoard LEFT JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID \
\r
210 WHERE MessageID>=0 OR MessageID IS NULL \
\r
211 GROUP BY tblBoard.BoardID;");
\r
213 // calculates peer trust
\r
214 db->Execute("DROP VIEW IF EXISTS vwCalculatedPeerTrust; \
\r
215 CREATE VIEW IF NOT EXISTS vwCalculatedPeerTrust AS \
\r
216 SELECT TargetIdentityID, \
\r
217 ROUND(SUM(MessageTrust*(LocalMessageTrust/100.0))/SUM(LocalMessageTrust/100.0),0) AS 'PeerMessageTrust', \
\r
218 ROUND(SUM(TrustListTrust*(LocalTrustListTrust/100.0))/SUM(LocalTrustListTrust/100.0),0) AS 'PeerTrustListTrust' \
\r
219 FROM tblPeerTrust INNER JOIN tblIdentity ON tblPeerTrust.IdentityID=tblIdentity.IdentityID \
\r
220 WHERE LocalTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalTrustListTrust') \
\r
221 AND ( PeerTrustListTrust IS NULL OR PeerTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerTrustListTrust') ) \
\r
222 GROUP BY TargetIdentityID;");
\r
224 // update PeerTrustLevel when deleting a record from tblPeerTrust
\r
225 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteOntblPeerTrust AFTER DELETE ON tblPeerTrust \
\r
228 UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=old.TargetIdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=old.TargetIdentityID) WHERE IdentityID=old.TargetIdentityID;\
\r
231 // update PeerTrustLevel when inserting a record into tblPeerTrust
\r
232 db->Execute("CREATE TRIGGER IF NOT EXISTS trgInsertOntblPeerTrust AFTER INSERT ON tblPeerTrust \
\r
235 UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=new.TargetIdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=new.TargetIdentityID) WHERE IdentityID=new.TargetIdentityID;\
\r
238 // update PeerTrustLevel when updating a record in tblPeerTrust
\r
239 db->Execute("CREATE TRIGGER IF NOT EXISTS trgUpdateOntblPeerTrust AFTER UPDATE ON tblPeerTrust \
\r
242 UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=old.TargetIdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=old.TargetIdentityID) WHERE IdentityID=old.TargetIdentityID;\
\r
243 UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=new.TargetIdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=new.TargetIdentityID) WHERE IdentityID=new.TargetIdentityID;\
\r
246 // recalculate all Peer TrustLevels when updating Local TrustLevels on tblIdentity - doesn't really need to be all, but rather all identities the updated identity has a trust level for. It's easier to update everyone for now.
\r
247 db->Execute("CREATE TRIGGER IF NOT EXISTS trgUpdateLocalTrustLevels AFTER UPDATE OF LocalMessageTrust,LocalTrustListTrust ON tblIdentity \
\r
250 UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=IdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=IdentityID);\
\r
253 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteMessage AFTER DELETE ON tblMessage \
\r
256 DELETE FROM tblMessageBoard WHERE tblMessageBoard.MessageID=old.MessageID;\
\r
257 DELETE FROM tblMessageReplyTo WHERE tblMessageReplyTo.MessageID=old.MessageID;\
\r
260 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteIdentity AFTER DELETE ON tblIdentity \
\r
263 DELETE FROM tblIdentityRequests WHERE IdentityID=old.IdentityID;\
\r
264 DELETE FROM tblIntroductionPuzzleRequests WHERE IdentityID=old.IdentityID;\
\r
265 DELETE FROM tblMessageListRequests WHERE IdentityID=old.IdentityID;\
\r
266 DELETE FROM tblMessageRequests WHERE IdentityID=old.IdentityID;\
\r
267 DELETE FROM tblPeerTrust WHERE IdentityID=old.IdentityID;\
\r
268 DELETE FROM tblTrustListRequests WHERE IdentityID=old.IdentityID;\
\r
271 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteLocalIdentity AFTER DELETE ON tblLocalIdentity \
\r
274 DELETE FROM tblIdentityIntroductionInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
275 DELETE FROM tblIntroductionPuzzleInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
276 DELETE FROM tblLocalIdentityInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
277 DELETE FROM tblMessageInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
278 DELETE FROM tblMessageListInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
279 DELETE FROM tblTrustListInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
282 // delete introduction puzzles that were half-way inserted
\r
283 db->Execute("DELETE FROM tblIntroductionPuzzleInserts WHERE Day IS NULL AND InsertIndex IS NULL;");
\r
285 // delete stale introduction puzzles (2 or more days old)
\r
286 date.SetToGMTime();
\r
287 date.Add(0,0,0,-2);
\r
288 db->Execute("DELETE FROM tblIntroductionPuzzleInserts WHERE Day<='"+date.Format("%Y-%m-%d")+"';");
\r
289 db->Execute("DELETE FROM tblIntroductionPuzzleRequests WHERE Day<='"+date.Format("%Y-%m-%d")+"';");
\r
291 // insert SomeDude's public key
\r
292 date.SetToGMTime();
\r
293 // db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded) VALUES('SSK@NuBL7aaJ6Cn4fB7GXFb9Zfi8w1FhPyW3oKgU9TweZMw,iXez4j3qCpd596TxXiJgZyTq9o-CElEuJxm~jNNZAuA,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"');");
\r
297 void SetupDefaultOptions()
\r
299 // OptionValue should always be inserted as a string, even if the option really isn't a string - just to keep the field data type consistent
\r
301 std::ostringstream tempstr; // must set tempstr to "" between db inserts
\r
302 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
303 SQLite3DB::Statement st=db->Prepare("INSERT INTO tblOption(Option,OptionValue,OptionDescription) VALUES(?,?,?);");
\r
307 tempstr << LogFile::LOGLEVEL_DEBUG;
\r
308 st.Bind(0,"LogLevel");
\r
309 st.Bind(1,tempstr.str());
\r
310 st.Bind(2,"The maximum logging level that will be written to file. 0=Fatal Errors, 1=Errors, 2=Warnings, 3=Informational Messages, 4=Debug Messages. Higher levels will include all messages from the previous levels.");
\r
315 st.Bind(0,"NNTPListenPort");
\r
317 st.Bind(2,"The port that the NNTP service will listen for incoming connections.");
\r
321 // NNTPBindAddresses
\r
322 st.Bind(0,"NNTPBindAddresses");
\r
323 st.Bind(1,"localhost");
\r
324 st.Bind(2,"A comma separated list of valid IPv4 or IPv6 addresses/hostnames that the NNTP service will try to bind to.");
\r
328 st.Bind(0,"NNTPAllowPost");
\r
330 st.Bind(2,"Allow posting messages from NNTP. Setting to false will make the newsgroups read only.");
\r
335 st.Bind(0,"StartNNTP");
\r
337 st.Bind(2,"Start NNTP service.");
\r
341 // StartFreenetUpdater
\r
342 st.Bind(0,"StartFreenetUpdater");
\r
344 st.Bind(2,"Start Freenet Updater thread.");
\r
349 st.Bind(0,"FCPHost");
\r
350 st.Bind(1,"localhost");
\r
351 st.Bind(2,"Host name or address of Freenet node.");
\r
356 st.Bind(0,"FCPPort");
\r
358 st.Bind(2,"The port that Freenet is listening for FCP connections on.");
\r
362 st.Bind(0,"MessageBase");
\r
364 st.Bind(2,"A unique string shared by all clients who want to communicate with each other. This should not be changed unless you want to create your own separate communications network.");
\r
368 st.Bind(0,"MaxIdentityRequests");
\r
370 st.Bind(2,"Maximum number of concurrent requests for new Identity xml files");
\r
374 st.Bind(0,"MaxIdentityIntroductionRequests");
\r
376 st.Bind(2,"Maximum number of concurrent identities requesting IdentityIntroduction xml files. Each identity may have multiple requests pending.");
\r
380 st.Bind(0,"MaxIntroductionPuzzleRequests");
\r
382 st.Bind(2,"Maximum number of concurrent requests for new IntroductionPuzzle xml files");
\r
386 st.Bind(0,"MaxTrustListRequests");
\r
388 st.Bind(2,"Maximum number of concurrent requests for new Trust Lists");
\r
392 st.Bind(0,"MaxMessageListRequests");
\r
394 st.Bind(2,"Maximum number of concurrent requests for new Message Lists");
\r
398 st.Bind(0,"MaxMessageRequests");
\r
400 st.Bind(2,"Maximum number of concurrent requests for new Messages");
\r
404 st.Bind(0,"MinLocalMessageTrust");
\r
406 st.Bind(2,"Specifies a local message trust level that a peer must have before its messages will be downloaded.");
\r
410 st.Bind(0,"MinPeerMessageTrust");
\r
412 st.Bind(2,"Specifies a peer message trust level that a peer must have before its messages will be downloaded.");
\r
416 st.Bind(0,"MinLocalTrustListTrust");
\r
418 st.Bind(2,"Specifies a local trust list trust level that a peer must have before its trust list will be included in the weighted average. Any peers below this number will be excluded from the results.");
\r
422 st.Bind(0,"MinPeerTrustListTrust");
\r
424 st.Bind(2,"Specifies a peer trust list trust level that a peer must have before its trust list will be included in the weighted average. Any peers below this number will be excluded from the results.");
\r
428 st.Bind(0,"MessageDownloadMaxDaysBackward");
\r
430 st.Bind(2,"The maximum number of days backward that messages will be downloaded from each identity");
\r
434 st.Bind(0,"MessageListDaysBackward");
\r
436 st.Bind(2,"The number of days backward that messages you have inserted will appear in your MessageLists");
\r
442 void SetupLogFile()
\r
445 std::string configval;
\r
448 date.SetToGMTime();
\r
450 LogFile::Instance()->SetFileName("fms-"+date.Format("%Y-%m-%d")+".log");
\r
451 LogFile::Instance()->OpenFile();
\r
452 LogFile::Instance()->SetWriteNewLine(true);
\r
453 LogFile::Instance()->SetWriteDate(true);
\r
454 LogFile::Instance()->SetWriteLogLevel(true);
\r
456 if(Option::Instance()->Get("LogLevel",configval)==false)
\r
459 Option::Instance()->Set("LogLevel",configval);
\r
461 if(StringFunctions::Convert(configval,loglevel)==false)
\r
463 loglevel=LogFile::LOGLEVEL_DEBUG;
\r
464 Option::Instance()->Set("LogLevel",loglevel);
\r
466 LogFile::Instance()->SetLogLevel((LogFile::LogLevel)loglevel);
\r
469 void SetupNetwork()
\r
473 WSAStartup(MAKEWORD(2,2),&wsadata);
\r
477 void ShutdownNetwork()
\r
484 void ShutdownThreads(std::vector<PThread::Thread *> &threads)
\r
486 std::vector<PThread::Thread *>::iterator i;
\r
487 for(i=threads.begin(); i!=threads.end(); i++)
\r
489 /* if((*i)->wait(1)==false)
\r
503 for(i=threads.begin(); i!=threads.end(); i++)
\r
505 LogFile::Instance()->WriteLog(LogFile::LOGLEVEL_DEBUG,"ShutdownThreads waiting for thread to exit.");
\r
515 void StartThreads(std::vector<PThread::Thread *> &threads)
\r
517 std::string startfreenet;
\r
518 std::string startnntp;
\r
520 if(Option::Instance()->Get("StartFreenetUpdater",startfreenet)==false)
\r
522 startfreenet="true";
\r
523 Option::Instance()->Set("StartFreenetUpdater","true");
\r
526 if(Option::Instance()->Get("StartNNTP",startnntp)==false)
\r
529 Option::Instance()->Set("StartNNTP","true");
\r
532 if(startfreenet=="true")
\r
534 PThread::Thread *t=new PThread::Thread(new FreenetMasterThread());
\r
535 threads.push_back(t);
\r
538 if(startnntp=="true")
\r
540 PThread::Thread *t=new PThread::Thread(new NNTPListener());
\r
541 threads.push_back(t);
\r