def initNoRosterItemsDB(): con = DB(TEST_NOROSTER_NAME) c = con.cursor() try: c.execute("CREATE TABLE jids (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\ jid TEXT NOT NULL,\ password TEXT NOT NULL,\ UNIQUE(jid))") c.execute("CREATE TABLE roster (userid INTEGER REFERENCES jids NOT NULL,\ contactid INTEGER REFERENCES jids NOT NULL,\ name TEXT,\ subscription INTEGER DEFAULT 0,\ PRIMARY KEY (userid, contactid)\ )") c.execute("CREATE TABLE rostergroups (groupid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\ userid INTEGER REFERENCES jids NOT NULL,\ name TEXT NOT NULL,\ UNIQUE(userid, name)\ )") c.execute("CREATE TABLE rostergroupitems\ (groupid INTEGER REFERENCES rostergroup NOT NULL,\ contactid INTEGER REFERENCES jids NOT NULL,\ PRIMARY KEY (groupid, contactid))") c.execute("INSERT INTO jids (jid, password) VALUES ('bob@localhost', 'test')") c.execute("INSERT INTO jids (jid, password) VALUES ('alice@localhost', 'test')") con.commit() except sqlite.OperationalError, e: if e.message.find('already exists') >= 0: pass else: raise
def populateDB(): """Creates a sample database""" con = DB() c = con.cursor() try: c.execute( "CREATE TABLE jids (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\ jid TEXT NOT NULL,\ password TEXT NOT NULL,\ UNIQUE(jid))") c.execute( "CREATE TABLE roster (userid INTEGER REFERENCES jids NOT NULL,\ contactid INTEGER REFERENCES jids NOT NULL,\ name TEXT,\ subscription INTEGER DEFAULT 0,\ PRIMARY KEY (userid, contactid)\ )") c.execute( "CREATE TABLE rostergroups (groupid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\ userid INTEGER REFERENCES jids NOT NULL,\ name TEXT NOT NULL,\ UNIQUE(userid, name)\ )") c.execute("CREATE TABLE rostergroupitems\ (groupid INTEGER REFERENCES rostergroup NOT NULL,\ contactid INTEGER REFERENCES jids NOT NULL,\ PRIMARY KEY (groupid, contactid))") c.execute( "INSERT INTO jids (jid, password) VALUES ('tro@localhost', 'test')" ) c.execute( "INSERT INTO jids (jid, password) VALUES ('dv@localhost', 'test')") c.execute( "INSERT INTO jids (jid, password) VALUES ('bob@localhost', 'test')" ) c.execute( "INSERT INTO jids (jid, password) VALUES ('alice@localhost', 'test')" ) con.commit() # c.execute("INSERT INTO roster (userid, contactid, subscription) VALUES (1, 2, 8)") # c.execute("INSERT INTO roster (userid, contactid, subscription) VALUES (2, 1, 8)") # c.execute("INSERT INTO rostergroups (userid, name) VALUES (1, 'friends')") # c.execute("INSERT INTO rostergroups (userid, name) VALUES (1, 'weirdos')") # c.execute("INSERT INTO rostergroupitems (groupid, contactid) VALUES (1, 2)") except sqlite.OperationalError, e: if e.message.find('already exists') >= 0: pass else: raise
def populateDB(): """Creates a sample database""" con = DB() c = con.cursor() try: c.execute("CREATE TABLE IF NOT EXISTS jids (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\ jid TEXT NOT NULL,\ password TEXT NOT NULL,\ UNIQUE(jid))") c.execute("CREATE TABLE IF NOT EXISTS roster (userid INTEGER REFERENCES jids NOT NULL,\ contactid INTEGER REFERENCES jids NOT NULL,\ name TEXT,\ subscription INTEGER DEFAULT 0,\ PRIMARY KEY (userid, contactid)\ )") c.execute("CREATE TABLE IF NOT EXISTS offline (fromid INTEGER REFERENCES jids NOT NULL,\ toid INTEGER REFERENCES jids NOT NULL,\ time TIMESTAMP,\ content TEXT\ )") c.execute("CREATE TABLE IF NOT EXISTS rostergroups (groupid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\ userid INTEGER REFERENCES jids NOT NULL,\ name TEXT NOT NULL,\ UNIQUE(userid, name)\ )") c.execute("CREATE TABLE IF NOT EXISTS rostergroupitems\ (groupid INTEGER REFERENCES rostergroup NOT NULL,\ contactid INTEGER REFERENCES jids NOT NULL,\ PRIMARY KEY (groupid, contactid))") c.execute("INSERT OR IGNORE INTO jids (jid, password) VALUES ('foo@localhost', 'foo')") c.execute("INSERT OR IGNORE INTO jids (jid, password) VALUES ('bar@localhost', 'bar')") c.execute("INSERT OR IGNORE INTO jids (jid, password) VALUES ('test@localhost', 'test')") c.execute("INSERT OR IGNORE INTO jids (jid, password) VALUES ('admin@localhost', 'admin')") con.commit() # c.execute("INSERT INTO roster (userid, contactid, subscription) VALUES (1, 2, 8)") # c.execute("INSERT INTO roster (userid, contactid, subscription) VALUES (2, 1, 8)") # c.execute("INSERT INTO rostergroups (userid, name) VALUES (1, 'friends')") # c.execute("INSERT INTO rostergroups (userid, name) VALUES (1, 'weirdos')") # c.execute("INSERT INTO rostergroupitems (groupid, contactid) VALUES (1, 2)") except sqlite.OperationalError, e: if e.message.find('already exists') >= 0: pass else: raise e
def handle(self, tree, msg, lastRetVal=None): # generate error response tree def get_error_tree(iq, type, code, tag): result = Element('iq', {'type': 'error', 'id': iq.get('id')}) result.append(iq) err_tree = Element('error', {'type': type, 'code': code,}) SubElement(err_tree, tag, {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'}) result.append(err_tree) return result if len(tree) > 0: # get the original iq msg origIQ = tree else: logging.warning("[%s] Original <iq> missing:\n%s", self.__class__, tostring(tree)) return lastRetVal id = origIQ.get('id') if id: try: username_tree = origIQ[0][0] password_tree = origIQ[0][1] if username_tree.tag == '{jabber:iq:register}username' and password_tree.tag == '{jabber:iq:register}password': username = username_tree.text password = password_tree.text # ruling out illegal username if not re.match("^[a-zA-Z0-9_.-]+$", username): raise Exception('Username not accepted') # write to database try: con = DB() with closing(con.cursor()) as cursor: cursor.execute("INSERT INTO jids (jid, password) VALUES ('%s@%s', '%s')" % (username, msg.conn.server.hostname, password)) con.commit() res = Element('iq', {'type': 'result', 'id': id}) query = deepcopy(origIQ[0]) query.insert(0, Element('registered')) res.append(query) return chainOutput(lastRetVal, res) # conflict response except sqlite.IntegrityError as e: if e.message.find('column jid is not unique') >= 0: logging.warning("[%s] Username conflict in <iq>:\n%s", self.__class__, str(e), tostring(origIQ)) res = get_error_tree(origIQ, 'cancel', '409', 'conflict') return chainOutput(lastRetVal, res) else: raise e else: raise Exception('IQ missing registration fields') # error response except Exception as e: error_string = str(e) logging.warning("[%s] Register failed '%s' in <iq>:\n%s", self.__class__, str(e) if error_string else 'Unknown error', tostring(origIQ)) res = get_error_tree(origIQ, 'modify', '406', 'not-acceptable') return chainOutput(lastRetVal, res) else: logging.warning("[%s] No id in <iq>:\n%s", self.__class__, tostring(origIQ)) return lastRetVal
def act(): d = msg.conn.data retVal = lastRetVal jid = d['user']['jid'] resource = d['user']['resource'] roster = Roster(jid) presTree = deepcopy(tree) presTree.set('from', '%s/%s' % (jid, resource)) probes = [] init_rosters = [] offline_msgs = [] if tree.get('to') is None and not d['user']['active']: # initial presence # TODO: we don't need to do it every time. we can cache the # data after the first resource is active and just resend # that to all new resources d['user']['active'] = True # get jids of the contacts whose status we're interested in cjids = roster.getPresenceSubscriptions() probeTree = Element('presence', { 'type': 'probe', 'from' : '%s/%s' \ % (jid, resource) }) # TODO: replace this with a more efficient router handler for cjid in cjids: probeTree.set('to', cjid) probeRouteData = { 'to' : cjid, 'data' : deepcopy(probeTree) } probes.append(probeRouteData) # they're sent first. see below # send initial roster list to this user rosterTree = Element('presence', { 'type': 'unavailable', 'to' : '%s/%s' \ % (jid, resource) }) for cjid in cjids: rosterTree.set('from', cjid) rosterRouterData = { 'to' : '%s/%s' % (jid, resource), 'data' : deepcopy(rosterTree) } init_rosters.append(rosterRouterData) # send offline message to this user try: con = DB() result = [] to_jid = JID(jid) with closing(con.cursor()) as cursor: cursor.execute("SELECT fromid, time, content FROM offline WHERE toid = %d ORDER BY time DESC" % (to_jid.getNumId())) con.commit() result = cursor.fetchall() with closing(con.cursor()) as cursor: cursor.execute("DELETE FROM offline WHERE toid = %d" % (to_jid.getNumId())) con.commit() for fromid, time, content in result: fromJID = JID(fromid, True).getBare() toJID = '%s/%s' % (jid, resource) reply = Element('message', { 'to': toJID, 'from': fromJID, 'type': 'chat' }) body = Element('body') body.text = content reply.append(body) delay = Element('delay', { 'xmlns': 'urn:xmpp:delay', 'from': fromJID, 'stamp': time.strftime("%Y-%m-%dT%H:%M:%SZ") }) reply.append(delay) routeData = { 'to' : toJID, 'data': reply } offline_msgs.append(routeData) logging.debug("[%s] Sending %d offline messages to %s", self.__class__, len(offline_msgs), to_jid.getBare()) except Exception as e: logging.warning("[%s] Failed to read offline messages: %s", self.__class__, str(e)) # broadcast to other resources of this user retVal = self.broadcastToOtherResources(presTree, msg, retVal, jid, resource) elif tree.get('to') is not None: # TODO: directed presence return elif tree.get('type') == 'unavailable': # broadcast to other resources of this user d['user']['active'] = False retVal = self.broadcastToOtherResources(presTree, msg, retVal, jid, resource) # record this stanza as the last presence sent from this client lastPresence = deepcopy(tree) lastPresence.set('from', '%s/%s' % (jid, resource)) d['user']['lastPresence'] = lastPresence # lookup contacts interested in presence cjids = roster.getPresenceSubscribers() # TODO: replace this with another router handler that would send # it out to all cjids in a batch instead of queuing a handler # for each for cjid in cjids: presTree.set('to', cjid) presRouteData = { 'to' : cjid, 'data' : deepcopy(presTree) } retVal = chainOutput(retVal, presRouteData) msg.setNextHandler('route-server') # send the probes first for probe in probes: msg.setNextHandler('route-server') retVal = chainOutput(retVal, probe) # send initial rosters for init_roster in init_rosters: msg.setNextHandler('route-client') retVal = chainOutput(retVal, init_roster) # send offline messages for offline_msg in offline_msgs: msg.setNextHandler('route-client') retVal = chainOutput(retVal, offline_msg) return retVal