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
9 #include "../include/http/httpthread.h"
\r
10 #include "../include/threadcontroller.h"
\r
13 #include <winsock2.h>
\r
20 bool wantshutdown=false;
\r
22 std::string CreateShortIdentityName(const std::string &name, const std::string &publickey)
\r
24 std::string result="";
\r
25 std::vector<std::string> keyparts;
\r
27 StringFunctions::SplitMultiple(publickey,"@,",keyparts);
\r
30 if(keyparts.size()>1 && keyparts[1].size()>8)
\r
32 result+="@"+keyparts[1].substr(0,4)+"...";
\r
42 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
44 db->Open("fms.db3");
\r
45 db->SetBusyTimeout(10000); // set timeout to 10 seconds
\r
46 //db->Execute("VACUUM;"); // not needed every startup
\r
48 db->Execute("CREATE TABLE IF NOT EXISTS tblDBVersion(\
\r
53 SQLite3DB::Statement st=db->Prepare("SELECT Major,Minor FROM tblDBVersion;");
\r
55 if(st.RowReturned())
\r
59 st.ResultInt(0,major);
\r
60 st.ResultInt(1,minor);
\r
62 if(major==1 && minor==0)
\r
64 ConvertDB0100To0101();
\r
68 if(major==1 && (minor==1 || minor==2))
\r
70 ConvertDB0101To0103();
\r
74 if(major==1 && minor==3)
\r
76 ConvertDB0103To0104();
\r
80 if(major==1 && minor==4)
\r
82 ConvertDB0104To0105();
\r
86 if(major==1 && minor==5)
\r
88 ConvertDB0105To0106();
\r
92 if(major==1 && minor==6)
\r
94 ConvertDB0106To0107();
\r
98 if(major==1 && minor==7)
\r
100 ConvertDB0107To0108();
\r
104 if(major==1 && minor==8)
\r
106 ConvertDB0108To0109();
\r
110 if(major==1 && minor==9)
\r
112 ConvertDB0109To0110();
\r
119 db->Execute("INSERT INTO tblDBVersion(Major,Minor) VALUES(1,10);");
\r
122 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=10;");
\r
124 db->Execute("CREATE TABLE IF NOT EXISTS tblOption(\
\r
125 Option TEXT UNIQUE,\
\r
126 OptionValue TEXT NOT NULL,\
\r
127 OptionDescription TEXT\
\r
130 db->Execute("CREATE TABLE IF NOT EXISTS tblLocalIdentity(\
\r
131 LocalIdentityID INTEGER PRIMARY KEY,\
\r
133 PublicKey TEXT UNIQUE,\
\r
134 PrivateKey TEXT UNIQUE,\
\r
135 SingleUse BOOL CHECK(SingleUse IN('true','false')) DEFAULT 'false',\
\r
136 PublishTrustList BOOL CHECK(PublishTrustList IN('true','false')) DEFAULT 'false',\
\r
137 PublishBoardList BOOL CHECK(PublishBoardList IN('true','false')) DEFAULT 'false',\
\r
138 PublishFreesite BOOL CHECK(PublishFreesite IN('true','false')) DEFAULT 'false',\
\r
139 FreesiteEdition INTEGER,\
\r
140 InsertingIdentity BOOL CHECK(InsertingIdentity IN('true','false')) DEFAULT 'false',\
\r
141 LastInsertedIdentity DATETIME,\
\r
142 InsertingPuzzle BOOL CHECK(InsertingPuzzle IN('true','false')) DEFAULT 'false',\
\r
143 LastInsertedPuzzle DATETIME,\
\r
144 InsertingTrustList BOOL CHECK(InsertingTrustList IN('true','false')) DEFAULT 'false',\
\r
145 LastInsertedTrustList DATETIME,\
\r
146 InsertingBoardList BOOL CHECK(InsertingBoardList IN('true','false')) DEFAULT 'false',\
\r
147 LastInsertedBoardList DATETIME,\
\r
148 InsertingMessageList BOOL CHECK(InsertingMessageList IN('true','false')) DEFAULT 'false',\
\r
149 LastInsertedMessageList DATETIME,\
\r
150 LastInsertedFreesite DATETIME,\
\r
151 DateCreated DATETIME\
\r
154 db->Execute("CREATE TABLE IF NOT EXISTS tblLocalIdentityInserts(\
\r
155 LocalIdentityID INTEGER,\
\r
157 InsertIndex INTEGER\
\r
160 db->Execute("CREATE TABLE IF NOT EXISTS tblTrustListInserts(\
\r
161 LocalIdentityID INTEGER,\
\r
163 InsertIndex INTEGER\
\r
166 db->Execute("CREATE TABLE IF NOT EXISTS tblTrustListRequests(\
\r
167 IdentityID INTEGER,\
\r
169 RequestIndex INTEGER,\
\r
170 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
173 db->Execute("CREATE TABLE IF NOT EXISTS tblIntroductionPuzzleInserts(\
\r
175 LocalIdentityID INTEGER,\
\r
177 InsertIndex INTEGER,\
\r
181 PuzzleSolution TEXT,\
\r
182 FoundSolution BOOL CHECK(FoundSolution IN('true','false')) DEFAULT 'false'\
\r
185 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentity(\
\r
186 IdentityID INTEGER PRIMARY KEY,\
\r
187 PublicKey TEXT UNIQUE,\
\r
189 SingleUse BOOL CHECK(SingleUse IN('true','false')) DEFAULT 'false',\
\r
190 PublishTrustList BOOL CHECK(PublishTrustList IN('true','false')) DEFAULT 'false',\
\r
191 PublishBoardList BOOL CHECK(PublishBoardList IN('true','false')) DEFAULT 'false',\
\r
192 FreesiteEdition INTEGER,\
\r
193 DateAdded DATETIME,\
\r
194 LastSeen DATETIME,\
\r
195 LocalMessageTrust INTEGER CHECK(LocalMessageTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
196 PeerMessageTrust INTEGER CHECK(PeerMessageTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
197 LocalTrustListTrust INTEGER CHECK(LocalTrustListTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
198 PeerTrustListTrust INTEGER CHECK(PeerTrustListTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
200 MessageTrustComment TEXT,\
\r
201 TrustListTrustComment TEXT,\
\r
202 Hidden BOOL CHECK(Hidden IN('true','false')) DEFAULT 'false'\
\r
205 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentityRequests(\
\r
206 IdentityID INTEGER,\
\r
208 RequestIndex INTEGER,\
\r
209 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
212 db->Execute("CREATE TABLE IF NOT EXISTS tblIntroductionPuzzleRequests(\
\r
213 IdentityID INTEGER,\
\r
215 RequestIndex INTEGER,\
\r
216 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false',\
\r
223 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentityIntroductionInserts(\
\r
224 LocalIdentityID INTEGER,\
\r
228 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
231 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentityTrust(\
\r
232 LocalIdentityID INTEGER,\
\r
233 IdentityID INTEGER,\
\r
234 LocalMessageTrust INTEGER CHECK(LocalMessageTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
235 MessageTrustComment TEXT,\
\r
236 LocalTrustListTrust INTEGER CHECK(LocalTrustListTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
237 TrustListTrustComment TEXT\
\r
240 db->Execute("CREATE UNIQUE INDEX IF NOT EXISTS idxIdentityTrust_IDs ON tblIdentityTrust(LocalIdentityID,IdentityID);");
\r
242 db->Execute("CREATE TRIGGER IF NOT EXISTS trgInsertOnIdentityTrust AFTER INSERT ON tblIdentityTrust \
\r
245 UPDATE tblIdentity SET LocalMessageTrust=(SELECT MAX(LocalMessageTrust) FROM tblIdentityTrust WHERE tblIdentityTrust.IdentityID=new.IdentityID GROUP BY tblIdentityTrust.IdentityID), LocalTrustListTrust=(SELECT MAX(LocalTrustListTrust) FROM tblIdentityTrust WHERE tblIdentityTrust.IdentityID=new.IdentityID GROUP BY tblIdentityTrust.IdentityID) WHERE tblIdentity.IdentityID=new.IdentityID; \
\r
248 db->Execute("CREATE TRIGGER IF NOT EXISTS trgUpdateOnIdentityTrust AFTER UPDATE OF LocalMessageTrust,LocalTrustListTrust ON tblIdentityTrust \
\r
251 UPDATE tblIdentity SET LocalMessageTrust=(SELECT MAX(LocalMessageTrust) FROM tblIdentityTrust WHERE tblIdentityTrust.IdentityID=new.IdentityID GROUP BY tblIdentityTrust.IdentityID), LocalTrustListTrust=(SELECT MAX(LocalTrustListTrust) FROM tblIdentityTrust WHERE tblIdentityTrust.IdentityID=new.IdentityID GROUP BY tblIdentityTrust.IdentityID) WHERE tblIdentity.IdentityID=new.IdentityID; \
\r
254 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteOnIdentityTrust AFTER DELETE ON tblIdentityTrust \
\r
257 UPDATE tblIdentity SET LocalMessageTrust=(SELECT MAX(LocalMessageTrust) FROM tblIdentityTrust WHERE tblIdentityTrust.IdentityID=old.IdentityID GROUP BY tblIdentityTrust.IdentityID), LocalTrustListTrust=(SELECT MAX(LocalTrustListTrust) FROM tblIdentityTrust WHERE tblIdentityTrust.IdentityID=old.IdentityID GROUP BY tblIdentityTrust.IdentityID) WHERE tblIdentity.IdentityID=old.IdentityID; \
\r
260 db->Execute("CREATE TABLE IF NOT EXISTS tblPeerTrust(\
\r
261 IdentityID INTEGER,\
\r
262 TargetIdentityID INTEGER,\
\r
263 MessageTrust INTEGER CHECK(MessageTrust BETWEEN 0 AND 100),\
\r
264 TrustListTrust INTEGER CHECK(TrustListTrust BETWEEN 0 AND 100),\
\r
265 MessageTrustComment TEXT,\
\r
266 TrustListTrustComment TEXT\
\r
269 db->Execute("CREATE INDEX IF NOT EXISTS idxPeerTrust_IdentityID ON tblPeerTrust (IdentityID);");
\r
270 db->Execute("CREATE INDEX IF NOT EXISTS idxPeerTrust_TargetIdentityID ON tblPeerTrust (TargetIdentityID);");
\r
272 db->Execute("CREATE TABLE IF NOT EXISTS tblBoard(\
\r
273 BoardID INTEGER PRIMARY KEY,\
\r
274 BoardName TEXT UNIQUE,\
\r
275 BoardDescription TEXT,\
\r
276 DateAdded DATETIME,\
\r
277 SaveReceivedMessages BOOL CHECK(SaveReceivedMessages IN('true','false')) DEFAULT 'true',\
\r
281 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded,AddedMethod) VALUES('fms','Freenet Message System','2007-12-01 12:00:00','Initial Board');");
\r
282 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded,AddedMethod) VALUES('freenet','Discussion about Freenet','2007-12-01 12:00:00','Initialt Board');");
\r
283 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded,AddedMethod) VALUES('public','Public discussion','2007-12-01 12:00:00','Initial Board');");
\r
284 db->Execute("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded,AddedMethod) VALUES('test','Test board','2007-12-01 12:00:00','Initial Board');");
\r
286 db->Execute("CREATE TABLE IF NOT EXISTS tblMessage(\
\r
287 MessageID INTEGER PRIMARY KEY,\
\r
288 IdentityID INTEGER,\
\r
293 MessageUUID TEXT UNIQUE,\
\r
294 ReplyBoardID INTEGER,\
\r
296 MessageIndex INTEGER\
\r
299 db->Execute("CREATE INDEX IF NOT EXISTS idxMessage_IdentityID ON tblMessage (IdentityID);");
\r
301 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageReplyTo(\
\r
302 MessageID INTEGER,\
\r
303 ReplyToMessageUUID TEXT,\
\r
304 ReplyOrder INTEGER\
\r
307 db->Execute("CREATE INDEX IF NOT EXISTS idxMessageReplyTo_MessageID ON tblMessageReplyTo (MessageID);");
\r
309 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageBoard(\
\r
310 MessageID INTEGER,\
\r
314 db->Execute("CREATE INDEX IF NOT EXISTS idxMessageBoard_MessageID ON tblMessageBoard (MessageID);");
\r
315 db->Execute("CREATE INDEX IF NOT EXISTS idxMessageBoard_BoardID ON tblMessageBoard (BoardID);");
\r
317 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageListRequests(\
\r
318 IdentityID INTEGER,\
\r
320 RequestIndex INTEGER,\
\r
321 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
324 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageRequests(\
\r
325 IdentityID INTEGER,\
\r
327 RequestIndex INTEGER,\
\r
328 FromMessageList BOOL CHECK(FromMessageList IN('true','false')) DEFAULT 'false',\
\r
329 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
332 db->Execute("CREATE UNIQUE INDEX IF NOT EXISTS idxMessageRequest ON tblMessageRequests(IdentityID,Day,RequestIndex);");
\r
334 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageInserts(\
\r
335 LocalIdentityID INTEGER,\
\r
337 InsertIndex INTEGER,\
\r
338 MessageUUID TEXT UNIQUE,\
\r
340 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
343 db->Execute("CREATE TABLE IF NOT EXISTS tblFileInserts(\
\r
344 FileInsertID INTEGER PRIMARY KEY,\
\r
353 db->Execute("CREATE TABLE IF NOT EXISTS tblMessageListInserts(\
\r
354 LocalIdentityID INTEGER,\
\r
356 InsertIndex INTEGER,\
\r
357 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
360 db->Execute("CREATE TABLE IF NOT EXISTS tblAdministrationBoard(\
\r
361 BoardID INTEGER UNIQUE,\
\r
362 ModifyLocalMessageTrust INTEGER,\
\r
363 ModifyLocalTrustListTrust INTEGER\
\r
366 db->Execute("CREATE TABLE IF NOT EXISTS tblBoardListInserts(\
\r
367 LocalIdentityID INTEGER,\
\r
369 InsertIndex INTEGER,\
\r
370 Inserted BOOL CHECK(Inserted IN('true','false')) DEFAULT 'false'\
\r
373 db->Execute("CREATE TABLE IF NOT EXISTS tblBoardListRequests(\
\r
374 IdentityID INTEGER,\
\r
376 RequestIndex INTEGER,\
\r
377 Found BOOL CHECK(Found IN('true','false')) DEFAULT 'false'\
\r
380 // MessageInserter will insert a record into this temp table which the MessageListInserter will query for and insert a MessageList when needed
\r
381 db->Execute("CREATE TEMPORARY TABLE IF NOT EXISTS tmpMessageListInsert(\
\r
382 LocalIdentityID INTEGER,\
\r
386 // low / high / message count for each board
\r
387 db->Execute("CREATE VIEW IF NOT EXISTS vwBoardStats AS \
\r
388 SELECT tblBoard.BoardID AS 'BoardID', IFNULL(MIN(MessageID),0) AS 'LowMessageID', IFNULL(MAX(MessageID),0) AS 'HighMessageID', COUNT(MessageID) AS 'MessageCount' \
\r
389 FROM tblBoard LEFT JOIN tblMessageBoard ON tblBoard.BoardID=tblMessageBoard.BoardID \
\r
390 WHERE MessageID>=0 OR MessageID IS NULL \
\r
391 GROUP BY tblBoard.BoardID;");
\r
393 // calculates peer trust
\r
394 // do the (MessageTrust+1)*LocalTrustListTrust/(MessageTrust+1)/100.0 - so if MessageTrust or TrustListTrust is NULL, the calc will be NULL and it won't be included at all in the average
\r
395 // need the +1 so that when the values are 0 the result is not 0
\r
396 db->Execute("DROP VIEW IF EXISTS vwCalculatedPeerTrust;");
\r
397 db->Execute("CREATE VIEW IF NOT EXISTS vwCalculatedPeerTrust AS \
\r
398 SELECT TargetIdentityID, \
\r
399 ROUND(SUM(MessageTrust*(LocalTrustListTrust/100.0))/SUM(((MessageTrust+1)*LocalTrustListTrust/(MessageTrust+1))/100.0),0) AS 'PeerMessageTrust', \
\r
400 ROUND(SUM(TrustListTrust*(LocalTrustListTrust/100.0))/SUM(((TrustListTrust+1)*LocalTrustListTrust/(TrustListTrust+1))/100.0),0) AS 'PeerTrustListTrust' \
\r
401 FROM tblPeerTrust INNER JOIN tblIdentity ON tblPeerTrust.IdentityID=tblIdentity.IdentityID \
\r
402 WHERE LocalTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalTrustListTrust') \
\r
403 AND ( PeerTrustListTrust IS NULL OR PeerTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerTrustListTrust') ) \
\r
404 GROUP BY TargetIdentityID;");
\r
407 These peer trust calculations are too CPU intensive to be triggers - they were called every time a new trust list was processed
\r
408 All trust levels will now be recalculated every hour in the PeriodicDBMaintenance class
\r
410 // drop existing triggers
\r
411 db->Execute("DROP TRIGGER IF EXISTS trgDeleteOntblPeerTrust;");
\r
412 db->Execute("DROP TRIGGER IF EXISTS trgInsertOntblPeerTrust;");
\r
413 db->Execute("DROP TRIGGER IF EXISTS trgUpdateOntblPeerTrust;");
\r
414 db->Execute("DROP TRIGGER IF EXISTS trgUpdateLocalTrustLevels;");
\r
416 // update PeerTrustLevel when deleting a record from tblPeerTrust
\r
417 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteOntblPeerTrust AFTER DELETE ON tblPeerTrust \
\r
420 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
423 // update PeerTrustLevel when inserting a record into tblPeerTrust
\r
424 db->Execute("CREATE TRIGGER IF NOT EXISTS trgInsertOntblPeerTrust AFTER INSERT ON tblPeerTrust \
\r
427 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
430 // update PeerTrustLevel when updating a record in tblPeerTrust
\r
431 db->Execute("CREATE TRIGGER IF NOT EXISTS trgUpdateOntblPeerTrust AFTER UPDATE ON tblPeerTrust \
\r
434 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
435 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
438 // 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
439 db->Execute("CREATE TRIGGER IF NOT EXISTS trgUpdateLocalTrustLevels AFTER UPDATE OF LocalMessageTrust,LocalTrustListTrust ON tblIdentity \
\r
442 UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=IdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=IdentityID);\
\r
446 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteMessage AFTER DELETE ON tblMessage \
\r
449 DELETE FROM tblMessageBoard WHERE tblMessageBoard.MessageID=old.MessageID;\
\r
450 DELETE FROM tblMessageReplyTo WHERE tblMessageReplyTo.MessageID=old.MessageID;\
\r
453 db->Execute("DROP TRIGGER IF EXISTS trgDeleteIdentity;");
\r
454 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteIdentity AFTER DELETE ON tblIdentity \
\r
457 DELETE FROM tblIdentityRequests WHERE IdentityID=old.IdentityID;\
\r
458 DELETE FROM tblIntroductionPuzzleRequests WHERE IdentityID=old.IdentityID;\
\r
459 DELETE FROM tblMessageListRequests WHERE IdentityID=old.IdentityID;\
\r
460 DELETE FROM tblMessageRequests WHERE IdentityID=old.IdentityID;\
\r
461 DELETE FROM tblPeerTrust WHERE IdentityID=old.IdentityID;\
\r
462 DELETE FROM tblTrustListRequests WHERE IdentityID=old.IdentityID;\
\r
463 DELETE FROM tblIdentityTrust WHERE IdentityID=old.IdentityID;\
\r
466 db->Execute("DROP TRIGGER IF EXISTS trgDeleteLocalIdentity;");
\r
467 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteLocalIdentity AFTER DELETE ON tblLocalIdentity \
\r
470 DELETE FROM tblIdentityIntroductionInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
471 DELETE FROM tblIntroductionPuzzleInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
472 DELETE FROM tblLocalIdentityInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
473 DELETE FROM tblMessageInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
474 DELETE FROM tblMessageListInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
475 DELETE FROM tblTrustListInserts WHERE LocalIdentityID=old.LocalIdentityID;\
\r
476 DELETE FROM tblIdentityTrust WHERE LocalIdentityID=old.LocalIdentityID;\
\r
479 db->Execute("CREATE TRIGGER IF NOT EXISTS trgDeleteBoard AFTER DELETE ON tblBoard \
\r
482 DELETE FROM tblMessageBoard WHERE BoardID=old.BoardID;\
\r
485 // delete introduction puzzles that were half-way inserted
\r
486 db->Execute("DELETE FROM tblIntroductionPuzzleInserts WHERE Day IS NULL AND InsertIndex IS NULL;");
\r
488 // delete stale introduction puzzles (2 or more days old)
\r
489 date.SetToGMTime();
\r
490 date.Add(0,0,0,-2);
\r
491 db->Execute("DELETE FROM tblIntroductionPuzzleInserts WHERE Day<='"+date.Format("%Y-%m-%d")+"';");
\r
492 db->Execute("DELETE FROM tblIntroductionPuzzleRequests WHERE Day<='"+date.Format("%Y-%m-%d")+"';");
\r
494 date.SetToGMTime();
\r
495 // insert SomeDude's public key
\r
496 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,LocalTrustListTrust,AddedMethod) VALUES('SSK@NuBL7aaJ6Cn4fB7GXFb9Zfi8w1FhPyW3oKgU9TweZMw,iXez4j3qCpd596TxXiJgZyTq9o-CElEuJxm~jNNZAuA,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"',50,'Initial Identity');");
\r
497 // insert Shadow Panther's public key
\r
498 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@~mimyB1kmH4f7Cgsd2wM2Qv2NxrZHRMM6IY8~7EWRVQ,fxTKkR0TYhgMYb-vEGAv55sMOxCGD2xhE4ZxWHxdPz4,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
499 // insert garfield's public key
\r
500 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@T8l1IEGU4-PoASFzgc2GYhIgRzUvZsKdoQWeuLHuTmM,QLxAPfkGis8l5NafNpSCdbxzXhBlu9WL8svcqJw9Mpo,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
501 // insert alek's public key
\r
502 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@lTjeI6V0lQsktXqaqJ6Iwk4TdsHduQI54rdUpHfhGbg,0oTYfrxxx8OmdU1~60gqpf3781qzEicM4Sz97mJsBM4,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
503 // insert Luke771's public key
\r
504 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@mdXK~ZVlfTZhF1SLBrvZ--i0vOsOpa~w9wv~~psQ-04,gXonsXKc7aexKSO8Gt8Fwre4Qgmmbt2WueO7VzxNKkk,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
505 // insert falafel's public key
\r
506 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@IxVqeqM0LyYdTmYAf5z49SJZUxr7NtQkOqVYG0hvITw,RM2wnMn5zAufCMt5upkkgq25B1elfBAxc7htapIWg1c,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
507 // insert cptn_insano's public key
\r
508 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@bloE1LJ~qzSYUkU2nt7sB9kq060D4HTQC66pk5Q8NpA,DOOASUnp0kj6tOdhZJ-h5Tk7Ka50FSrUgsH7tCG1usU,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
509 // insert Flink's public key
\r
510 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@q2TtkNBOuuniyJ56~8NSopCs3ttwe5KlB31ugZtWmXA,6~PzIupS8YK7L6oFNpXGKJmHT2kBMDfwTg73nHdNur8,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
511 // insert Kane's public key
\r
512 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@Ofm~yZivDJ5Z2fSzZbMiLEUUQaIc0KHRdZMBTaPLO6I,WLm4s4hNbOOurJ6ijfOq4odz7-dN7uTUvYxJRwWnlMI,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
513 // inserts boardstat's public key
\r
514 db->Execute("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES('SSK@aYWBb6zo2AM13XCNhsmmRKMANEx6PG~C15CWjdZziKA,X1pAG4EIqR1gAiyGFVZ1iiw-uTlh460~rFACJ7ZHQXk,AQACAAE/','"+date.Format("%Y-%m-%d %H:%M:%S")+"','Initial Identity');");
\r
516 // TODO remove sometime after 0.1.17
\r
517 FixCapitalBoardNames();
\r
519 // run analyze - may speed up some queries
\r
520 db->Execute("ANALYZE;");
\r
524 void ConvertDB0100To0101()
\r
526 // added unique constraint to public and private key
\r
527 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
528 db->Execute("CREATE TEMPORARY TABLE tblLocalIdentityTemp AS SELECT * FROM tblLocalIdentity;");
\r
529 db->Execute("DROP TABLE IF EXISTS tblLocalIdentity;");
\r
530 db->Execute("CREATE TABLE IF NOT EXISTS tblLocalIdentity(\
\r
531 LocalIdentityID INTEGER PRIMARY KEY,\
\r
533 PublicKey TEXT UNIQUE,\
\r
534 PrivateKey TEXT UNIQUE,\
\r
535 SingleUse BOOL CHECK(SingleUse IN('true','false')) DEFAULT 'false',\
\r
536 PublishTrustList BOOL CHECK(PublishTrustList IN('true','false')) DEFAULT 'false',\
\r
537 PublishBoardList BOOL CHECK(PublishBoardList IN('true','false')) DEFAULT 'false',\
\r
538 InsertingIdentity BOOL CHECK(InsertingIdentity IN('true','false')) DEFAULT 'false',\
\r
539 LastInsertedIdentity DATETIME,\
\r
540 InsertingPuzzle BOOL CHECK(InsertingPuzzle IN('true','false')) DEFAULT 'false',\
\r
541 LastInsertedPuzzle DATETIME,\
\r
542 InsertingTrustList BOOL CHECK(InsertingTrustList IN('true','false')) DEFAULT 'false',\
\r
543 LastInsertedTrustList DATETIME,\
\r
544 InsertingBoardList BOOL CHECK(InsertingBoardList IN('true','false')) DEFAULT 'false',\
\r
545 LastInsertedBoardList DATETIME,\
\r
546 InsertingMessageList BOOL CHECK(InsertingMessageList IN('true','false')) DEFAULT 'false',\
\r
547 LastInsertedMessageList DATETIME\
\r
549 db->Execute("INSERT INTO tblLocalIdentity SELECT * FROM tblLocalIdentityTemp;");
\r
550 db->Execute("DROP TABLE IF EXISTS tblLocalIdentityTemp;");
\r
551 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=1;");
\r
554 void ConvertDB0101To0103()
\r
556 // remove default 50 from trust fields and set default to NULL
\r
557 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
558 db->Execute("CREATE TEMPORARY TABLE tblIdentityTemp AS SELECT * FROM tblIdentity;");
\r
559 db->Execute("DROP TABLE IF EXISTS tblIdentity;");
\r
560 db->Execute("CREATE TABLE IF NOT EXISTS tblIdentity(\
\r
561 IdentityID INTEGER PRIMARY KEY,\
\r
562 PublicKey TEXT UNIQUE,\
\r
564 SingleUse BOOL CHECK(SingleUse IN('true','false')) DEFAULT 'false',\
\r
565 PublishTrustList BOOL CHECK(PublishTrustList IN('true','false')) DEFAULT 'false',\
\r
566 PublishBoardList BOOL CHECK(PublishBoardList IN('true','false')) DEFAULT 'false',\
\r
567 DateAdded DATETIME,\
\r
568 LastSeen DATETIME,\
\r
569 LocalMessageTrust INTEGER CHECK(LocalMessageTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
570 PeerMessageTrust INTEGER CHECK(PeerMessageTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
571 LocalTrustListTrust INTEGER CHECK(LocalTrustListTrust BETWEEN 0 AND 100) DEFAULT NULL,\
\r
572 PeerTrustListTrust INTEGER CHECK(PeerTrustListTrust BETWEEN 0 AND 100) DEFAULT NULL\
\r
574 db->Execute("INSERT INTO tblIdentity SELECT * FROM tblIdentityTemp;");
\r
575 db->Execute("DROP TABLE IF EXISTS tblIdentityTemp;");
\r
577 // add SaveReceivedMessages field to tblBoard
\r
578 db->Execute("ALTER TABLE tblBoard ADD COLUMN SaveReceivedMessages BOOL CHECK(SaveReceivedMessages IN('true','false')) DEFAULT 'true';");
\r
580 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=3;");
\r
583 void ConvertDB0103To0104()
\r
585 // add MessageIndex to tblMessage
\r
587 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
588 db->Execute("ALTER TABLE tblMessage ADD COLUMN MessageIndex INTEGER;");
\r
589 db->Execute("CREATE UNIQUE INDEX IF NOT EXISTS idxMessageRequest ON tblMessageRequests(IdentityID,Day,RequestIndex);");
\r
590 db->Execute("ALTER TABLE tblLocalIdentity ADD COLUMN DateCreated DATETIME;");
\r
591 date.SetToGMTime();
\r
592 db->Execute("UPDATE tblLocalIdentity SET DateCreated='"+date.Format("%Y-%m-%d %H:%M:%S")+"' WHERE DateCreated IS NULL;");
\r
593 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=4;");
\r
596 void ConvertDB0104To0105()
\r
598 // add AddedMethod, MessageTrustComment, TrustListTrustComment to tblIdentity
\r
599 // add MessageTrustComment,TrustListTrustComment to tblPeerTrust
\r
600 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
601 db->Execute("ALTER TABLE tblIdentity ADD COLUMN AddedMethod TEXT;");
\r
602 db->Execute("ALTER TABLE tblIdentity ADD COLUMN MessageTrustComment TEXT;");
\r
603 db->Execute("ALTER TABLE tblIdentity ADD COLUMN TrustListTrustComment TEXT;");
\r
604 db->Execute("ALTER TABLE tblPeerTrust ADD COLUMN MessageTrustComment TEXT;");
\r
605 db->Execute("ALTER TABLE tblPeerTrust ADD COLUMN TrustListTrustComment TEXT;");
\r
606 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=5;");
\r
609 void ConvertDB0105To0106()
\r
611 // add Publish Freesite
\r
612 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
613 db->Execute("ALTER TABLE tblLocalIdentity ADD COLUMN PublishFreesite BOOL CHECK(PublishFreesite IN('true','false')) DEFAULT 'false';");
\r
614 db->Execute("ALTER TABLE tblLocalIdentity ADD COLUMN LastInsertedFreesite DATETIME;");
\r
615 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=6;");
\r
618 void ConvertDB0106To0107()
\r
620 // add AddedMethod to tblBoard
\r
621 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
622 db->Execute("ALTER TABLE tblBoard ADD COLUMN AddedMethod TEXT;");
\r
623 db->Execute("ALTER TABLE tblIdentity ADD COLUMN Hidden BOOL CHECK(Hidden IN('true','false')) DEFAULT 'false';");
\r
624 db->Execute("UPDATE tblIdentity SET Hidden='false' WHERE Hidden IS NULL;");
\r
625 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=7;");
\r
628 void ConvertDB0107To0108()
\r
630 // add FreesiteEdition to tblLocalIdentity and tblIdentity
\r
631 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
632 db->Execute("ALTER TABLE tblLocalIdentity ADD COLUMN FreesiteEdition INTEGER;");
\r
633 db->Execute("ALTER TABLE tblIdentity ADD COLUMN FreesiteEdition INTEGER;");
\r
634 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=8;");
\r
637 void ConvertDB0108To0109()
\r
639 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
640 db->Execute("CREATE TABLE IF NOT EXISTS tblFileInserts(\
\r
641 FileInsertID INTEGER PRIMARY KEY,\
\r
648 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=9;");
\r
651 void ConvertDB0109To0110()
\r
653 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
654 db->Execute("ALTER TABLE tblFileInserts ADD COLUMN MimeType TEXT;");
\r
655 db->Execute("UPDATE tblDBVersion SET Major=1, Minor=10;");
\r
658 void SetupDefaultOptions()
\r
660 // 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
662 std::ostringstream tempstr; // must set tempstr to "" between db inserts
\r
663 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
664 SQLite3DB::Statement st=db->Prepare("INSERT INTO tblOption(Option,OptionValue,OptionDescription) VALUES(?,?,?);");
\r
668 tempstr << LogFile::LOGLEVEL_DEBUG;
\r
669 st.Bind(0,"LogLevel");
\r
670 st.Bind(1,tempstr.str());
\r
671 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
676 st.Bind(0,"NNTPListenPort");
\r
678 st.Bind(2,"The port that the NNTP service will listen for incoming connections.");
\r
682 // NNTPBindAddresses
\r
683 st.Bind(0,"NNTPBindAddresses");
\r
684 st.Bind(1,"localhost");
\r
685 st.Bind(2,"A comma separated list of valid IPv4 or IPv6 addresses/hostnames that the NNTP service will try to bind to.");
\r
689 st.Bind(0,"NNTPAllowPost");
\r
691 st.Bind(2,"Allow posting messages from NNTP. Setting to false will make the newsgroups read only.");
\r
696 st.Bind(0,"StartNNTP");
\r
698 st.Bind(2,"Start NNTP server.");
\r
702 st.Bind(0,"StartHTTP");
\r
704 st.Bind(2,"Start HTTP server. WARNING: If you turn this off, you won't be able to access the administration pages.");
\r
708 st.Bind(0,"HTTPListenPort");
\r
710 st.Bind(2,"Port HTTP server will listen on.");
\r
714 st.Bind(0,"HTTPAccessControl");
\r
715 st.Bind(1,"-0.0.0.0/0,+127.0.0.1");
\r
716 st.Bind(2,"Comma separated list of addresses and/or subnet masks that are allowed access to the administration pages. Default is localhost only. + allows a host, - denies a host.");
\r
720 // StartFreenetUpdater
\r
721 st.Bind(0,"StartFreenetUpdater");
\r
723 st.Bind(2,"Set to true to start the Freenet Updater thread and connect to Freenet. Set to false to prevent communication with Freenet.");
\r
728 st.Bind(0,"FCPHost");
\r
729 st.Bind(1,"127.0.0.1");
\r
730 st.Bind(2,"Host name or address of Freenet node.");
\r
735 st.Bind(0,"FCPPort");
\r
737 st.Bind(2,"The port that Freenet is listening for FCP connections on.");
\r
741 st.Bind(0,"FProxyPort");
\r
743 st.Bind(2,"The port that Freenet is listening for http connections on.");
\r
747 st.Bind(0,"MessageBase");
\r
749 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
753 st.Bind(0,"MaxIdentityRequests");
\r
755 st.Bind(2,"Maximum number of concurrent requests for new Identity xml files");
\r
759 st.Bind(0,"MaxIdentityIntroductionRequests");
\r
761 st.Bind(2,"Maximum number of concurrent identities requesting IdentityIntroduction xml files. Each identity may have multiple requests pending.");
\r
765 st.Bind(0,"MaxIntroductionPuzzleRequests");
\r
767 st.Bind(2,"Maximum number of concurrent requests for new IntroductionPuzzle xml files");
\r
771 st.Bind(0,"MaxTrustListRequests");
\r
773 st.Bind(2,"Maximum number of concurrent requests for new Trust Lists");
\r
777 st.Bind(0,"MaxMessageListRequests");
\r
779 st.Bind(2,"Maximum number of concurrent requests for new Message Lists");
\r
783 st.Bind(0,"MaxMessageRequests");
\r
785 st.Bind(2,"Maximum number of concurrent requests for new Messages");
\r
789 st.Bind(0,"MinLocalMessageTrust");
\r
791 st.Bind(2,"Specifies a local message trust level that a peer must have before its messages will be downloaded.");
\r
795 st.Bind(0,"MinPeerMessageTrust");
\r
797 st.Bind(2,"Specifies a peer message trust level that a peer must have before its messages will be downloaded.");
\r
801 st.Bind(0,"MinLocalTrustListTrust");
\r
803 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
807 st.Bind(0,"MinPeerTrustListTrust");
\r
809 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
813 st.Bind(0,"MessageDownloadMaxDaysBackward");
\r
815 st.Bind(2,"The maximum number of days backward that messages will be downloaded from each identity");
\r
819 st.Bind(0,"MessageListDaysBackward");
\r
821 st.Bind(2,"The number of days backward that messages you have inserted will appear in your MessageLists");
\r
825 st.Bind(0,"MaxPeerMessagesPerDay");
\r
827 st.Bind(2,"The maximum number of messages you will download from each peer on a given day.");
\r
831 st.Bind(0,"MaxBoardListRequests");
\r
833 st.Bind(2,"The maximum number of concurrent requests for new Board Lists. Set to 0 to disable.");
\r
837 st.Bind(0,"MaxBoardsPerMessage");
\r
839 st.Bind(2,"The maximum number of boards a received message may be sent to. Boards over this limit will be ignored.");
\r
843 st.Bind(0,"SaveMessagesFromNewBoards");
\r
845 st.Bind(2,"Set to true to automatically save messages posted to new boards. Set to false to ignore messages to new boards.");
\r
849 st.Bind(0,"ChangeMessageTrustOnReply");
\r
851 st.Bind(2,"How much the local message trust level of an identity should change when you reply to one of their messages.");
\r
855 st.Bind(0,"AddNewPostFromIdentities");
\r
856 st.Bind(1,"false");
\r
857 st.Bind(2,"Set to true to automatically create new identities when you send a message using a new name. If you set this to false, posting messages will fail until you manually create the identity.");
\r
861 st.Bind(0,"DeleteMessagesOlderThan");
\r
863 st.Bind(2,"Automatically delete messages older than this many days.");
\r
869 void SetupLogFile()
\r
872 std::string configval;
\r
875 date.SetToGMTime();
\r
877 LogFile::Instance()->SetFileName("fms-"+date.Format("%Y-%m-%d")+".log");
\r
878 LogFile::Instance()->OpenFile();
\r
879 LogFile::Instance()->SetWriteNewLine(true);
\r
880 LogFile::Instance()->SetWriteDate(true);
\r
881 LogFile::Instance()->SetWriteLogLevel(true);
\r
883 if(Option::Instance()->Get("LogLevel",configval)==false)
\r
886 Option::Instance()->Set("LogLevel",configval);
\r
888 if(StringFunctions::Convert(configval,loglevel)==false)
\r
890 loglevel=LogFile::LOGLEVEL_DEBUG;
\r
891 Option::Instance()->Set("LogLevel",loglevel);
\r
893 LogFile::Instance()->SetLogLevel((LogFile::LogLevel)loglevel);
\r
896 void SetupNetwork()
\r
900 WSAStartup(MAKEWORD(2,2),&wsadata);
\r
906 ThreadController::Instance()->ShutdownThreads();
\r
910 LogFile::Instance()->WriteLog(LogFile::LOGLEVEL_INFO,"FMS shutdown");
\r
911 LogFile::Instance()->WriteNewLine();
\r
914 void ShutdownNetwork()
\r
921 void SigHandler(int signum)
\r
927 void FixCapitalBoardNames()
\r
929 SQLite3DB::DB *db=SQLite3DB::DB::Instance();
\r
931 SQLite3DB::Statement st=db->Prepare("SELECT BoardID,BoardName FROM tblBoard WHERE BoardID NOT IN (SELECT BoardID FROM tblAdministrationBoard);");
\r
932 SQLite3DB::Statement st2=db->Prepare("SELECT BoardID FROM tblBoard WHERE BoardName=?;");
\r
933 SQLite3DB::Statement del=db->Prepare("DELETE FROM tblBoard WHERE BoardID=?;");
\r
934 SQLite3DB::Statement upd=db->Prepare("UPDATE tblBoard SET BoardName=? WHERE BoardID=?;");
\r
935 SQLite3DB::Statement upd2=db->Prepare("UPDATE tblMessage SET ReplyBoardID=? WHERE ReplyBoardID=?;");
\r
936 SQLite3DB::Statement upd3=db->Prepare("UPDATE tblMessageBoard SET BoardID=? WHERE BoardID=?;");
\r
939 while(st.RowReturned())
\r
943 std::string name="";
\r
944 std::string lowername="";
\r
946 st.ResultInt(0,boardid);
\r
947 st.ResultText(1,name);
\r
950 StringFunctions::LowerCase(lowername,lowername);
\r
952 if(name!=lowername)
\r
954 st2.Bind(0,lowername);
\r
957 if(st2.RowReturned())
\r
959 st2.ResultInt(0,newboardid);
\r
961 upd2.Bind(0,newboardid);
\r
962 upd2.Bind(1,boardid);
\r
966 upd3.Bind(0,newboardid);
\r
967 upd3.Bind(1,boardid);
\r
971 del.Bind(0,boardid);
\r
977 upd.Bind(0,lowername);
\r
978 upd.Bind(1,boardid);
\r