1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 """
28 This module allows to access the on-disk database of logs
29 """
30
31 import os
32 import sys
33 import time
34 import datetime
35 from gzip import GzipFile
36 from cStringIO import StringIO
37
38 import exceptions
39 import gajim
40
41 import sqlite3 as sqlite
42
43 import configpaths
44 LOG_DB_PATH = configpaths.gajimpaths['LOG_DB']
45 LOG_DB_FOLDER, LOG_DB_FILE = os.path.split(LOG_DB_PATH)
46 CACHE_DB_PATH = configpaths.gajimpaths['CACHE_DB']
47
50 (
51 self.JID_NORMAL_TYPE,
52 self.JID_ROOM_TYPE
53 ) = range(2)
54
55 (
56 self.KIND_STATUS,
57 self.KIND_GCSTATUS,
58 self.KIND_GC_MSG,
59 self.KIND_SINGLE_MSG_RECV,
60 self.KIND_CHAT_MSG_RECV,
61 self.KIND_SINGLE_MSG_SENT,
62 self.KIND_CHAT_MSG_SENT,
63 self.KIND_ERROR
64 ) = range(8)
65
66 (
67 self.SHOW_ONLINE,
68 self.SHOW_CHAT,
69 self.SHOW_AWAY,
70 self.SHOW_XA,
71 self.SHOW_DND,
72 self.SHOW_OFFLINE
73 ) = range(6)
74
75 (
76 self.TYPE_AIM,
77 self.TYPE_GG,
78 self.TYPE_HTTP_WS,
79 self.TYPE_ICQ,
80 self.TYPE_MSN,
81 self.TYPE_QQ,
82 self.TYPE_SMS,
83 self.TYPE_SMTP,
84 self.TYPE_TLEN,
85 self.TYPE_YAHOO,
86 self.TYPE_NEWMAIL,
87 self.TYPE_RSS,
88 self.TYPE_WEATHER,
89 self.TYPE_MRIM,
90 ) = range(14)
91
92 (
93 self.SUBSCRIPTION_NONE,
94 self.SUBSCRIPTION_TO,
95 self.SUBSCRIPTION_FROM,
96 self.SUBSCRIPTION_BOTH,
97 ) = range(4)
98
99 constants = Constants()
100
116
118 if self.con:
119 self.con.close()
120 self.con = None
121 self.cur = None
122
124 self.close_db()
125
126
127
128
129 back = os.getcwd()
130 os.chdir(LOG_DB_FOLDER)
131
132
133
134
135 self.con = sqlite.connect(LOG_DB_FILE, timeout=20.0,
136 isolation_level='IMMEDIATE')
137 os.chdir(back)
138 self.cur = self.con.cursor()
139 self.set_synchronous(False)
140
146
148 try:
149 if sync:
150 self.cur.execute("PRAGMA synchronous = NORMAL")
151 else:
152 self.cur.execute("PRAGMA synchronous = OFF")
153 except sqlite.Error, e:
154 gajim.log.debug("Failed to set_synchronous(%s): %s" % (sync, str(e)))
155
159
161 """
162 Helper to commit
163 """
164 self.cur.execute(sql_to_commit)
165 try:
166 self.con.commit()
167 except sqlite.OperationalError, e:
168 print >> sys.stderr, str(e)
169
171 try:
172 self.cur.execute('SELECT jid FROM jids')
173
174 rows = self.cur.fetchall()
175 except sqlite.DatabaseError:
176 raise exceptions.DatabaseMalformed
177 self.jids_already_in = []
178 for row in rows:
179
180 if row[0] == '':
181
182 pass
183 else:
184 self.jids_already_in.append(row[0])
185
187 return self.jids_already_in
188
190 """
191 If jid is gajim@conf/nkour it's likely a pm one, how we know gajim@conf
192 is not a normal guy and nkour is not his resource? we ask if gajim@conf
193 is already in jids (with type room jid) this fails if user disables
194 logging for room and only enables for pm (so higly unlikely) and if we
195 fail we do not go chaos (user will see the first pm as if it was message
196 in room's public chat) and after that all okay
197 """
198 if jid.find('/') > -1:
199 possible_room_jid = jid.split('/', 1)[0]
200 return self.jid_is_room_jid(possible_room_jid)
201 else:
202
203 return False
204
206 self.cur.execute('SELECT jid_id FROM jids WHERE jid=? AND type=?',
207 (jid, constants.JID_ROOM_TYPE))
208 row = self.cur.fetchone()
209 if row is None:
210 return False
211 else:
212 return True
213
215 """
216 jids table has jid and jid_id logs table has log_id, jid_id,
217 contact_name, time, kind, show, message so to ask logs we need jid_id
218 that matches our jid in jids table this method wants jid and returns the
219 jid_id for later sql-ing on logs typestr can be 'ROOM' or anything else
220 depending on the type of JID and is only needed to be specified when the
221 JID is new in DB
222 """
223 if jid.find('/') != -1:
224 jid_is_from_pm = self.jid_is_from_pm(jid)
225 if not jid_is_from_pm:
226 jid = jid.split('/', 1)[0]
227 if jid in self.jids_already_in:
228 self.cur.execute('SELECT jid_id FROM jids WHERE jid=?', [jid])
229 row = self.cur.fetchone()
230 if row:
231 return row[0]
232
233 if typestr == 'ROOM':
234 typ = constants.JID_ROOM_TYPE
235 else:
236 typ = constants.JID_NORMAL_TYPE
237 try:
238 self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid,
239 typ))
240 self.con.commit()
241 except sqlite.IntegrityError, e:
242
243 self.get_jids_already_in_db()
244 return self.get_jid_id(jid, typestr)
245 except sqlite.OperationalError, e:
246 raise exceptions.PysqliteOperationalError(str(e))
247 jid_id = self.cur.lastrowid
248 self.jids_already_in.append(jid)
249 return jid_id
250
252 """
253 Convert from string style to constant ints for db
254 """
255 if kind == 'status':
256 kind_col = constants.KIND_STATUS
257 elif kind == 'gcstatus':
258 kind_col = constants.KIND_GCSTATUS
259 elif kind == 'gc_msg':
260 kind_col = constants.KIND_GC_MSG
261 elif kind == 'single_msg_recv':
262 kind_col = constants.KIND_SINGLE_MSG_RECV
263 elif kind == 'single_msg_sent':
264 kind_col = constants.KIND_SINGLE_MSG_SENT
265 elif kind == 'chat_msg_recv':
266 kind_col = constants.KIND_CHAT_MSG_RECV
267 elif kind == 'chat_msg_sent':
268 kind_col = constants.KIND_CHAT_MSG_SENT
269 elif kind == 'error':
270 kind_col = constants.KIND_ERROR
271
272 if show == 'online':
273 show_col = constants.SHOW_ONLINE
274 elif show == 'chat':
275 show_col = constants.SHOW_CHAT
276 elif show == 'away':
277 show_col = constants.SHOW_AWAY
278 elif show == 'xa':
279 show_col = constants.SHOW_XA
280 elif show == 'dnd':
281 show_col = constants.SHOW_DND
282 elif show == 'offline':
283 show_col = constants.SHOW_OFFLINE
284 elif show is None:
285 show_col = None
286 else:
287
288 show_col = 'UNKNOWN'
289
290 return kind_col, show_col
291
325
327 """
328 Convert from constant ints for db to string style
329 """
330 if type_id == constants.TYPE_AIM:
331 return 'aim'
332 if type_id == constants.TYPE_GG:
333 return 'gadu-gadu'
334 if type_id == constants.TYPE_HTTP_WS:
335 return 'http-ws'
336 if type_id == constants.TYPE_ICQ:
337 return 'icq'
338 if type_id == constants.TYPE_MSN:
339 return 'msn'
340 if type_id == constants.TYPE_QQ:
341 return 'qq'
342 if type_id == constants.TYPE_SMS:
343 return 'sms'
344 if type_id == constants.TYPE_SMTP:
345 return 'smtp'
346 if type_id == constants.TYPE_TLEN:
347 return 'tlen'
348 if type_id == constants.TYPE_YAHOO:
349 return 'yahoo'
350 if type_id == constants.TYPE_NEWMAIL:
351 return 'newmail'
352 if type_id == constants.TYPE_RSS:
353 return 'rss'
354 if type_id == constants.TYPE_WEATHER:
355 return 'weather'
356 if type_id == constants.TYPE_MRIM:
357 return 'mrim'
358
360 """
361 Convert from string style to constant ints for db
362 """
363 if sub == 'none':
364 return constants.SUBSCRIPTION_NONE
365 if sub == 'to':
366 return constants.SUBSCRIPTION_TO
367 if sub == 'from':
368 return constants.SUBSCRIPTION_FROM
369 if sub == 'both':
370 return constants.SUBSCRIPTION_BOTH
371
373 """
374 Convert from constant ints for db to string style
375 """
376 if sub == constants.SUBSCRIPTION_NONE:
377 return 'none'
378 if sub == constants.SUBSCRIPTION_TO:
379 return 'to'
380 if sub == constants.SUBSCRIPTION_FROM:
381 return 'from'
382 if sub == constants.SUBSCRIPTION_BOTH:
383 return 'both'
384
386 sql = '''INSERT INTO logs (jid_id, contact_name, time, kind, show,
387 message, subject) VALUES (?, ?, ?, ?, ?, ?, ?)'''
388 try:
389 self.cur.execute(sql, values)
390 except sqlite.DatabaseError:
391 raise exceptions.DatabaseMalformed
392 except sqlite.OperationalError, e:
393 raise exceptions.PysqliteOperationalError(str(e))
394 message_id = None
395 try:
396 self.con.commit()
397 if write_unread:
398 message_id = self.cur.lastrowid
399 except sqlite.OperationalError, e:
400 print >> sys.stderr, str(e)
401 if message_id:
402 self.insert_unread_events(message_id, values[0])
403 return message_id
404
406 """
407 Add unread message with id: message_id
408 """
409 sql = 'INSERT INTO unread_messages VALUES (%d, %d, 0)' % (message_id,
410 jid_id)
411 self.simple_commit(sql)
412
414 """
415 Mark all messages with ids in message_ids as read
416 """
417 ids = ','.join([str(i) for i in message_ids])
418 sql = 'DELETE FROM unread_messages WHERE message_id IN (%s)' % ids
419 self.simple_commit(sql)
420
422 """
423 Mark unread message as shown un GUI
424 """
425 sql = 'UPDATE unread_messages SET shown = 1 where message_id = %s' % \
426 msg_id
427 self.simple_commit(sql)
428
430 """
431 Set shown field to False in unread_messages table
432 """
433 sql = 'UPDATE unread_messages SET shown = 0'
434 self.simple_commit(sql)
435
437 """
438 Get all unread messages
439 """
440 all_messages = []
441 try:
442 self.cur.execute(
443 'SELECT message_id, shown from unread_messages')
444 results = self.cur.fetchall()
445 except Exception:
446 pass
447 for message in results:
448 msg_id = message[0]
449 shown = message[1]
450
451
452
453 self.cur.execute('''
454 SELECT logs.log_line_id, logs.message, logs.time, logs.subject,
455 jids.jid
456 FROM logs, jids
457 WHERE logs.log_line_id = %d AND logs.jid_id = jids.jid_id
458 ''' % msg_id
459 )
460 results = self.cur.fetchall()
461 if len(results) == 0:
462
463 self.set_read_messages([msg_id])
464 continue
465 all_messages.append(results[0] + (shown,))
466 return all_messages
467
468 - def write(self, kind, jid, message=None, show=None, tim=None, subject=None):
469 """
470 Write a row (status, gcstatus, message etc) to logs database
471
472 kind can be status, gcstatus, gc_msg, (we only recv for those 3),
473 single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent we cannot
474 know if it is pm or normal chat message, we try to guess see
475 jid_is_from_pm()
476
477 We analyze jid and store it as follows:
478 jids.jid text column will hold JID if TC-related, room_jid if GC-related,
479 ROOM_JID/nick if pm-related.
480 """
481
482 if self.jids_already_in == []:
483 self.open_db()
484
485 contact_name_col = None
486
487
488 message_col = message
489 subject_col = subject
490 if tim:
491 time_col = int(float(time.mktime(tim)))
492 else:
493 time_col = int(float(time.time()))
494
495 kind_col, show_col = self.convert_human_values_to_db_api_values(kind,
496 show)
497
498 write_unread = False
499
500
501 if kind == 'status':
502
503 try:
504 jid_id = self.get_jid_id(jid)
505 except exceptions.PysqliteOperationalError, e:
506 raise exceptions.PysqliteOperationalError(str(e))
507 if show is None:
508 show_col = constants.SHOW_ONLINE
509
510 elif kind == 'gcstatus':
511
512 if show is None:
513 show_col = constants.SHOW_ONLINE
514 jid, nick = jid.split('/', 1)
515 try:
516
517 jid_id = self.get_jid_id(jid, 'ROOM')
518 except exceptions.PysqliteOperationalError, e:
519 raise exceptions.PysqliteOperationalError(str(e))
520 contact_name_col = nick
521
522 elif kind == 'gc_msg':
523 if jid.find('/') != -1:
524 jid, nick = jid.split('/', 1)
525 else:
526
527
528 nick = None
529 try:
530
531 jid_id = self.get_jid_id(jid, 'ROOM')
532 except exceptions.PysqliteOperationalError, e:
533 raise exceptions.PysqliteOperationalError(str(e))
534 contact_name_col = nick
535 else:
536 try:
537 jid_id = self.get_jid_id(jid)
538 except exceptions.PysqliteOperationalError, e:
539 raise exceptions.PysqliteOperationalError(str(e))
540 if kind == 'chat_msg_recv':
541 if not self.jid_is_from_pm(jid):
542
543 write_unread = True
544
545 if show_col == 'UNKNOWN':
546 return
547
548 values = (jid_id, contact_name_col, time_col, kind_col, show_col,
549 message_col, subject_col)
550 return self.commit_to_db(values, write_unread)
551
554 """
555 Accept how many rows to restore and when to time them out (in minutes)
556 (mark them as too old) and number of messages that are in queue and are
557 already logged but pending to be viewed, returns a list of tupples
558 containg time, kind, message, list with empty tupple if nothing found to
559 meet our demands
560 """
561 try:
562 self.get_jid_id(jid)
563 except exceptions.PysqliteOperationalError, e:
564
565 return []
566 where_sql = self._build_contact_where(account, jid)
567
568 now = int(float(time.time()))
569 timed_out = now - (timeout * 60)
570
571
572 try:
573 self.cur.execute('''
574 SELECT time, kind, message FROM logs
575 WHERE (%s) AND kind IN (%d, %d, %d, %d, %d) AND time > %d
576 ORDER BY time DESC LIMIT %d OFFSET %d
577 ''' % (where_sql, constants.KIND_SINGLE_MSG_RECV,
578 constants.KIND_CHAT_MSG_RECV, constants.KIND_SINGLE_MSG_SENT,
579 constants.KIND_CHAT_MSG_SENT, constants.KIND_ERROR,
580 timed_out, restore_how_many_rows, pending_how_many)
581 )
582
583 results = self.cur.fetchall()
584 except sqlite.DatabaseError:
585 raise exceptions.DatabaseMalformed
586 results.reverse()
587 return results
588
590
591
592
593 d = datetime.date(year, month, day)
594 local_time = d.timetuple()
595
596 start_of_day = int(time.mktime(local_time))
597 return start_of_day
598
600 """
601 Return contact_name, time, kind, show, message, subject
602
603 For each row in a list of tupples, returns list with empty tupple if we
604 found nothing to meet our demands
605 """
606 try:
607 self.get_jid_id(jid)
608 except exceptions.PysqliteOperationalError, e:
609
610 return []
611 where_sql = self._build_contact_where(account, jid)
612
613 start_of_day = self.get_unix_time_from_date(year, month, day)
614 seconds_in_a_day = 86400
615 last_second_of_day = start_of_day + seconds_in_a_day - 1
616
617 self.cur.execute('''
618 SELECT contact_name, time, kind, show, message, subject FROM logs
619 WHERE (%s)
620 AND time BETWEEN %d AND %d
621 ORDER BY time
622 ''' % (where_sql, start_of_day, last_second_of_day))
623
624 results = self.cur.fetchall()
625 return results
626
628 """
629 Returns contact_name, time, kind, show, message
630
631 For each row in a list of tupples, returns list with empty tupple if we
632 found nothing to meet our demands
633 """
634 try:
635 self.get_jid_id(jid)
636 except exceptions.PysqliteOperationalError, e:
637
638 return []
639
640 if False:
641 try:
642 self.cur.execute(query)
643 except sqlite.OperationalError, e:
644 results = [('', '', '', '', str(e))]
645 return results
646
647 else:
648 where_sql = self._build_contact_where(account, jid)
649 like_sql = '%' + query.replace("'", "''") + '%'
650 self.cur.execute('''
651 SELECT contact_name, time, kind, show, message, subject FROM logs
652 WHERE (%s) AND message LIKE '%s'
653 ORDER BY time
654 ''' % (where_sql, like_sql))
655
656 results = self.cur.fetchall()
657 return results
658
660 """
661 Return the list of days that have logs (not status messages)
662 """
663 try:
664 self.get_jid_id(jid)
665 except exceptions.PysqliteOperationalError, e:
666
667 return []
668 days_with_logs = []
669 where_sql = self._build_contact_where(account, jid)
670
671
672 start_of_month = self.get_unix_time_from_date(year, month, 1)
673 seconds_in_a_day = 86400
674 last_second_of_month = start_of_month + (seconds_in_a_day * max_day) - 1
675
676
677
678
679
680 self.cur.execute('''
681 SELECT DISTINCT time/(86400)*86400 FROM logs
682 WHERE (%s)
683 AND time BETWEEN %d AND %d
684 AND kind NOT IN (%d, %d)
685 ORDER BY time
686 ''' % (where_sql, start_of_month, last_second_of_month,
687 constants.KIND_STATUS, constants.KIND_GCSTATUS))
688 result = self.cur.fetchall()
689
690
691 for line in result:
692 days_with_logs[0:0]=[time.gmtime(line[0])[2]]
693
694 return days_with_logs
695
697 """
698 Return last time (in seconds since EPOCH) for which we had logs
699 (excluding statuses)
700 """
701 where_sql = ''
702 if not is_room:
703 where_sql = self._build_contact_where(account, jid)
704 else:
705 try:
706 jid_id = self.get_jid_id(jid, 'ROOM')
707 except exceptions.PysqliteOperationalError, e:
708
709 return None
710 where_sql = 'jid_id = %s' % jid_id
711 self.cur.execute('''
712 SELECT MAX(time) FROM logs
713 WHERE (%s)
714 AND kind NOT IN (%d, %d)
715 ''' % (where_sql, constants.KIND_STATUS, constants.KIND_GCSTATUS))
716
717 results = self.cur.fetchone()
718 if results is not None:
719 result = results[0]
720 else:
721 result = None
722 return result
723
725 """
726 Return FASTLY last time (in seconds since EPOCH) for which we had logs
727 for that room from rooms_last_message_time table
728 """
729 try:
730 jid_id = self.get_jid_id(jid, 'ROOM')
731 except exceptions.PysqliteOperationalError, e:
732
733 return None
734 where_sql = 'jid_id = %s' % jid_id
735 self.cur.execute('''
736 SELECT time FROM rooms_last_message_time
737 WHERE (%s)
738 ''' % (where_sql))
739
740 results = self.cur.fetchone()
741 if results is not None:
742 result = results[0]
743 else:
744 result = None
745 return result
746
748 """
749 Set last time (in seconds since EPOCH) for which we had logs for that
750 room in rooms_last_message_time table
751 """
752 jid_id = self.get_jid_id(jid, 'ROOM')
753
754 sql = 'REPLACE INTO rooms_last_message_time VALUES (%d, %d)' % \
755 (jid_id, time)
756 self.simple_commit(sql)
757
779
781 """
782 Save the type of the transport in DB
783 """
784 type_id = self.convert_human_transport_type_to_db_api_values(type_)
785 if not type_id:
786
787 return
788 self.cur.execute(
789 'SELECT type from transports_cache WHERE transport = "%s"' % jid)
790 results = self.cur.fetchall()
791 if results:
792 result = results[0][0]
793 if result == type_id:
794 return
795 sql = 'UPDATE transports_cache SET type = %d WHERE transport = "%s"' %\
796 (type_id, jid)
797 self.simple_commit(sql)
798 return
799 sql = 'INSERT INTO transports_cache VALUES ("%s", %d)' % (jid, type_id)
800 self.simple_commit(sql)
801
803 """
804 Return all the type of the transports in DB
805 """
806 self.cur.execute(
807 'SELECT * from transports_cache')
808 results = self.cur.fetchall()
809 if not results:
810 return {}
811 answer = {}
812 for result in results:
813 answer[result[0]] = self.convert_api_values_to_human_transport_type(
814 result[1])
815 return answer
816
817
818
819
820
821
822
823
825 """
826 Iterate over caps cache data stored in the database
827
828 The iterator values are pairs of (node, ver, ext, identities, features):
829 identities == {'category':'foo', 'type':'bar', 'name':'boo'},
830 features being a list of feature namespaces.
831 """
832
833
834
835 try:
836 self.cur.execute('SELECT hash_method, hash, data FROM caps_cache;')
837 except sqlite.OperationalError:
838
839
840 return
841
842
843 to_be_removed = []
844 for hash_method, hash_, data in self.cur:
845
846
847
848
849 try:
850 data = GzipFile(fileobj=StringIO(str(data))).read().decode(
851 'utf-8').split('\0')
852 except IOError:
853
854 to_be_removed.append((hash_method, hash_))
855 continue
856 i = 0
857 identities = list()
858 features = list()
859 while i < (len(data) - 3) and data[i] != 'FEAT':
860 category = data[i]
861 type_ = data[i + 1]
862 lang = data[i + 2]
863 name = data[i + 3]
864 identities.append({'category': category, 'type': type_,
865 'xml:lang': lang, 'name': name})
866 i += 4
867 i+=1
868 while i < len(data):
869 features.append(data[i])
870 i += 1
871
872
873 yield hash_method, hash_, identities, features
874 for hash_method, hash_ in to_be_removed:
875 sql = '''DELETE FROM caps_cache WHERE hash_method = "%s" AND
876 hash = "%s"''' % (hash_method, hash_)
877 self.simple_commit(sql)
878
879 - def add_caps_entry(self, hash_method, hash_, identities, features):
880 data = []
881 for identity in identities:
882
883 if identity['category'] == 'FEAT':
884 return
885 data.extend((identity.get('category'), identity.get('type', ''),
886 identity.get('xml:lang', ''), identity.get('name', '')))
887 data.append('FEAT')
888 data.extend(features)
889 data = '\0'.join(data)
890
891 string = StringIO()
892 gzip = GzipFile(fileobj=string, mode='w')
893 data = data.encode('utf-8')
894 gzip.write(data)
895 gzip.close()
896 data = string.getvalue()
897 self.cur.execute('''
898 INSERT INTO caps_cache ( hash_method, hash, data, last_seen )
899 VALUES (?, ?, ?, ?);
900 ''', (hash_method, hash_, buffer(data), int(time.time())))
901
902 try:
903 self.con.commit()
904 except sqlite.OperationalError, e:
905 print >> sys.stderr, str(e)
906
908 sql = '''UPDATE caps_cache SET last_seen = %d
909 WHERE hash_method = "%s" and hash = "%s"''' % \
910 (int(time.time()), method, hash_)
911 self.simple_commit(sql)
912
914 """
915 Remove caps which was not seen for 3 months
916 """
917 sql = '''DELETE FROM caps_cache WHERE last_seen < %d''' % \
918 int(time.time() - 3*30*24*3600)
919 self.simple_commit(sql)
920
922 """
923 Replace current roster in DB by a new one
924
925 accout_name is the name of the account to change.
926 roster_version is the version of the new roster.
927 roster is the new version.
928 """
929
930
931
932 gajim.config.set_per('accounts', account_name, 'roster_version', '')
933
934 account_jid = gajim.get_jid_from_account(account_name)
935 account_jid_id = self.get_jid_id(account_jid)
936
937
938 self.remove_roster(account_jid)
939
940
941 for jid in roster:
942 self.add_or_update_contact(account_jid, jid, roster[jid]['name'],
943 roster[jid]['subscription'], roster[jid]['ask'],
944 roster[jid]['groups'], commit=False)
945 self.con.commit()
946
947
948
949 gajim.config.set_per('accounts', account_name, 'roster_version',
950 roster_version)
951
968
1003
1005 """
1006 Return the accound_jid roster in NonBlockingRoster format
1007 """
1008 data = {}
1009 account_jid_id = self.get_jid_id(account_jid)
1010
1011
1012 self.cur.execute('''
1013 SELECT j.jid, re.jid_id, re.name, re.subscription, re.ask
1014 FROM roster_entry re, jids j
1015 WHERE re.account_jid_id=? AND j.jid_id=re.jid_id''', (account_jid_id,))
1016 for jid, jid_id, name, subscription, ask in self.cur:
1017 data[jid] = {}
1018 if name:
1019 data[jid]['name'] = name
1020 else:
1021 data[jid]['name'] = None
1022 data[jid]['subscription'] = \
1023 self.convert_db_api_values_to_human_subscription_values(
1024 subscription)
1025 data[jid]['groups'] = []
1026 data[jid]['resources'] = {}
1027 if ask:
1028 data[jid]['ask'] = 'subscribe'
1029 else:
1030 data[jid]['ask'] = None
1031 data[jid]['id'] = jid_id
1032
1033
1034 for jid in data:
1035 self.cur.execute('''
1036 SELECT group_name FROM roster_group
1037 WHERE account_jid_id=? AND jid_id=?''',
1038 (account_jid_id, data[jid]['id']))
1039 for (group_name,) in self.cur:
1040 data[jid]['groups'].append(group_name)
1041 del data[jid]['id']
1042
1043 return data
1044
1046 """
1047 Remove all entry from account_jid roster
1048 """
1049 account_jid_id = self.get_jid_id(account_jid)
1050
1051 self.cur.execute('DELETE FROM roster_entry WHERE account_jid_id=?',
1052 (account_jid_id,))
1053 self.cur.execute('DELETE FROM roster_group WHERE account_jid_id=?',
1054 (account_jid_id,))
1055 self.con.commit()
1056