1 # BridgeDB by Nick Mathewson.
2 # Copyright (c) 2007-2009, The Tor Project, Inc.
3 # See LICENSE for licensing information
8 import bridgedb
.Bridges
13 toHex
= binascii
.b2a_hex
14 fromHex
= binascii
.a2b_hex
18 return "'%s'" % v
.replace("'", "''")
21 return time
.strftime("%Y-%m-%d %H:%M", time
.gmtime(t
))
23 return calendar
.timegm(time
.strptime(t
, "%Y-%m-%d %H:%M"))
27 A SqliteDict wraps a SQLite table and makes it look like a
28 Python dictionary. In addition to the single key and value
29 columns, there can be a number of "fixed" columns, such that
30 the dictionary only contains elements of the table where the
31 fixed columns are set appropriately.
33 def __init__(self
, conn
, cursor
, table
, fixedcolnames
, fixedcolvalues
,
35 assert len(fixedcolnames
) == len(fixedcolvalues
)
38 keys
= ", ".join(fixedcolnames
+(keycol
,valcol
))
39 vals
= "".join("%s, "%_escapeValue(v
) for v
in fixedcolvalues
)
40 constraint
= "WHERE %s = ?"%keycol
42 constraint
+= "".join(
43 " AND %s = %s"%(c
,_escapeValue(v
))
44 for c
,v
in zip(fixedcolnames
, fixedcolvalues
))
46 self
._getStmt
= "SELECT %s FROM %s %s"%(valcol
,table
,constraint
)
47 self
._delStmt
= "DELETE FROM %s %s"%(table
,constraint
)
48 self
._setStmt
= "INSERT OR REPLACE INTO %s (%s) VALUES (%s?, ?)"%(
51 constraint
= " AND ".join("%s = %s"%(c
,_escapeValue(v
))
52 for c
,v
in zip(fixedcolnames
, fixedcolvalues
))
54 whereClause
= " WHERE %s"%constraint
58 self
._keysStmt
= "SELECT %s FROM %s%s"%(keycol
,table
,whereClause
)
60 def __setitem__(self
, k
, v
):
61 self
._cursor
.execute(self
._setStmt
, (k
,v
))
62 def __delitem__(self
, k
):
63 self
._cursor
.execute(self
._delStmt
, (k
,))
64 if self
._cursor
.rowcount
== 0:
66 def __getitem__(self
, k
):
67 self
._cursor
.execute(self
._getStmt
, (k
,))
68 val
= self
._cursor
.fetchone()
74 self
._cursor
.execute(self
._getStmt
, (k
,))
75 return self
._cursor
.rowcount
!= 0
76 def get(self
, k
, v
=None):
77 self
._cursor
.execute(self
._getStmt
, (k
,))
78 val
= self
._cursor
.fetchone()
83 def setdefault(self
, k
, v
):
90 self
._cursor
.execute(self
._keysStmt
)
91 return [ key
for (key
,) in self
._cursor
.fetchall() ]
99 # The old DB system was just a key->value mapping DB, with special key
100 # prefixes to indicate which database they fell into.
102 # sp|<ID> -- given to bridgesplitter; maps bridgeID to ring name.
103 # em|<emailaddr> -- given to emailbaseddistributor; maps email address
104 # to concatenated ID.
105 # fs|<ID> -- Given to BridgeTracker, maps to time when a router was
106 # first seen (YYYY-MM-DD HH:MM)
107 # ls|<ID> -- given to bridgetracker, maps to time when a router was
108 # last seen (YYYY-MM-DD HH:MM)
110 # We no longer want to use em| at all, since we're not doing that kind
111 # of persistence any more.
113 # Here is the SQL schema.
116 CREATE TABLE Config (
117 key PRIMARY KEY NOT NULL,
121 CREATE TABLE Bridges (
122 id INTEGER PRIMARY KEY NOT NULL,
131 CREATE UNIQUE INDEX BridgesKeyIndex ON Bridges ( hex_key );
133 CREATE TABLE EmailedBridges (
134 email PRIMARY KEY NOT NULL,
138 CREATE INDEX EmailedBridgesWhenMailed on EmailedBridges ( email );
140 INSERT INTO Config VALUES ( 'schema-version', 1 );
145 def __init__(self
, sqlite_fname
, db_fname
=None):
147 self
._conn
= openDatabase(sqlite_fname
)
149 self
._conn
= openOrConvertDatabase(sqlite_fname
, db_fname
)
150 self
._cur
= self
._conn
.cursor()
159 def insertBridgeAndGetRing(self
, bridge
, setRing
, seenAt
):
160 '''updates info about bridge, setting ring to setRing if none was set.
161 Returns the bridge's ring.
165 t
= timeToStr(seenAt
)
166 h
= bridge
.fingerprint
167 assert len(h
) == HEX_ID_LEN
169 cur
.execute("SELECT id, distributor "
170 "FROM Bridges WHERE hex_key = ?", (h
,))
174 # Update last_seen and address.
175 cur
.execute("UPDATE Bridges SET address = ?, or_port = ?, "
176 "last_seen = ? WHERE id = ?",
177 (bridge
.ip
, bridge
.orport
, timeToStr(seenAt
), idx
))
181 cur
.execute("INSERT INTO Bridges (hex_key, address, or_port, "
182 "distributor, first_seen, last_seen) "
183 "VALUES (?, ?, ?, ?, ?, ?)",
184 (h
, bridge
.ip
, bridge
.orport
, setRing
, t
, t
))
187 def cleanEmailedBridges(self
, expireBefore
):
189 t
= timeToStr(expireBefore
)
191 cur
.execute("DELETE FROM EmailedBridges WHERE when_mailed < ?", (t
,));
193 def getEmailTime(self
, addr
):
195 cur
.execute("SELECT when_mailed FROM EmailedBridges WHERE "
196 "email = ?", (addr
,))
200 return strToTime(v
[0])
202 def setEmailTime(self
, addr
, whenMailed
):
204 t
= timeToStr(whenMailed
)
205 cur
.execute("INSERT OR REPLACE INTO EmailedBridges "
206 "(email,when_mailed) VALUES (?,?)", (addr
, t
))
208 def openDatabase(sqlite_file
):
209 conn
= sqlite3
.Connection(sqlite_file
)
213 cur
.execute("SELECT value FROM Config WHERE key = 'schema-version'")
214 val
, = cur
.fetchone()
216 logging
.warn("Unknown schema version %s in database.", val
)
217 except sqlite3
.OperationalError
:
218 logging
.warn("No Config table found in DB; creating tables")
219 cur
.executescript(SCHEMA1_SCRIPT
)
226 def openOrConvertDatabase(sqlite_file
, db_file
):
227 """Open a sqlite database, converting it from a db file if needed."""
228 if os
.path
.exists(sqlite_file
):
229 return openDatabase(sqlite_file
)
231 conn
= sqlite3
.Connection(sqlite_file
)
233 cur
.executescript(SCHEMA1_SCRIPT
)
239 db
= anydbm
.open(db_file
, 'r')
244 # We handle all the sp| keys first, since other tables have
245 # dependencies on Bridges.
248 if k
.startswith("sp|"):
250 cur
.execute("INSERT INTO Bridges ( hex_key, distributor ) "
251 "VALUES (?, ?)", (toHex(k
[3:]),v
))
252 # Now we handle the other key types.
255 if k
.startswith("fs|"):
257 cur
.execute("UPDATE Bridges SET first_seen = ? "
258 "WHERE hex_key = ?", (v
, toHex(k
[3:])))
259 elif k
.startswith("ls|"):
261 cur
.execute("UPDATE Bridges SET last_seen = ? "
262 "WHERE hex_key = ?", (v
, toHex(k
[3:])))
263 #elif k.startswith("em|"):
264 # keys = list(toHex(i) for i in
265 # bridgedb.Bridges.chopString(v, bridgedb.Bridges.ID_LEN))
266 # cur.executemany("INSERT INTO EmailedBridges ( email, id ) "
267 # "SELECT ?, id FROM Bridges WHERE hex_key = ?",
268 # [(k[3:],i) for i in keys])
269 elif k
.startswith("sp|") or k
.startswith("em|"):
272 logging
.warn("Unrecognized key %r", k
)
276 os
.unlink(sqlite_file
)