diff --git a/README b/README index fef62f9b8..0a9b90086 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ Welcome and thanks for trying out Gajim. python2.4 (python2.3 should work too) pygtk2.6 or higher python-libglade -python-pysqlite2 +pysqlite2 (aka. python-pysqlite2) some distros also split too much python standard library. I know SUSE does. In such distros you also need python-xml diff --git a/po/POTFILES.in b/po/POTFILES.in index 8cfcd6f2a..43caf5224 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -29,6 +29,7 @@ src/systraywin32.py src/tabbed_chat_window.py src/tooltips.py src/vcard.py +src/common/check_paths.py src/common/GnuPG.py src/common/GnuPGInterface.py src/common/__init__.py diff --git a/scripts/migrate_logs_to_dot9_db.py b/scripts/migrate_logs_to_dot9_db.py index 8d6fa7b88..c0aa88071 100755 --- a/scripts/migrate_logs_to_dot9_db.py +++ b/scripts/migrate_logs_to_dot9_db.py @@ -175,10 +175,3 @@ if __name__ == '__main__': f.write('You can always run the migration script to import your old logs to the database\n') f.write('Thank you\n') f.close() - # after huge import create the indices (they are slow on massive insert) - cur.executescript( - ''' - CREATE UNIQUE INDEX jids_already_index ON jids (jid); - CREATE INDEX jid_id_index ON logs (jid_id); - ''' - ) diff --git a/src/common/check_paths.py b/src/common/check_paths.py new file mode 100644 index 000000000..e342fd50d --- /dev/null +++ b/src/common/check_paths.py @@ -0,0 +1,101 @@ +## Gajim Team: +## - Yann Le Boulanger +## - Vincent Hanquez +## - Nikos Kouremenos +## - Travis Shirk +## +## Copyright (C) 2003-2005 Gajim Team +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 2 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +import os +import sys +import stat + +import gajim +import logger +import i18n + +_ = i18n._ +Q_ = i18n.Q_ + +from pysqlite2 import dbapi2 as sqlite # DO NOT MOVE ABOVE OF import gajim + +def create_log_db(): + print _('creating logs database') + con = sqlite.connect(logger.LOG_DB_PATH) + cur = con.cursor() + # create the tables + # kind can be + # status, gcstatus, gc_msg, (we only recv for those 3), + # single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent + # to meet all our needs + # logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code + # jids.jid text column will be JID if TC-related, room_jid if GC-related, + # ROOM_JID/nick if pm-related. + cur.executescript( + ''' + CREATE TABLE jids( + jid_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, + jid TEXT UNIQUE + ); + + CREATE TABLE logs( + log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, + jid_id INTEGER, + contact_name TEXT, + time INTEGER, + kind TEXT, + show TEXT, + message TEXT + ); + ''' + ) + + con.commit() + +def check_and_possible_create_paths(): + LOG_DB_PATH = logger.LOG_DB_PATH + VCARDPATH = gajim.VCARDPATH + dot_gajim = os.path.dirname(VCARDPATH) + if os.path.isfile(dot_gajim): + print _('%s is file but it should be a directory') % dot_gajim + print _('Gajim will now exit') + sys.exit() + elif os.path.isdir(dot_gajim): + s = os.stat(dot_gajim) + if s.st_mode & stat.S_IROTH: # others have read permission! + os.chmod(dot_gajim, 0700) # rwx------ + + if not os.path.exists(VCARDPATH): + print _('creating %s directory') % VCARDPATH + os.mkdir(VCARDPATH, 0700) + elif os.path.isfile(VCARDPATH): + print _('%s is file but it should be a directory') % VCARDPATH + print _('Gajim will now exit') + sys.exit() + + if not os.path.exists(LOG_DB_PATH): + create_log_db() + elif os.path.isdir(LOG_DB_PATH): + print _('%s is directory but should be file') % LOG_DB_PATH + print _('Gajim will now exit') + sys.exit() + + else: # dot_gajim doesn't exist + if dot_gajim: # is '' on win9x so avoid that + print _('creating %s directory') % dot_gajim + os.mkdir(dot_gajim, 0700) + if not os.path.isdir(VCARDPATH): + print _('creating %s directory') % VCARDPATH + os.mkdir(VCARDPATH, 0700) + if not os.path.isfile(LOG_DB_PATH): + create_log_db() diff --git a/src/common/connection.py b/src/common/connection.py index ca484ca98..c9c01a49e 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -364,12 +364,12 @@ class Connection: if not msg.getTag('body'): #no return self.dispatch('GC_MSG', (frm, msgtxt, tim)) - gajim.logger.write('gc', msgtxt, frm, tim = tim) + gajim.logger.write('gc_msg', frm, msgtxt, tim = tim) elif mtype == 'normal': # it's single message log_msgtxt = msgtxt if subject: log_msgtxt = _('Subject: %s\n%s') % (subject, msgtxt) - gajim.logger.write('incoming', log_msgtxt, frm, tim = tim) + gajim.logger.write('single_msg_recv', frm, log_msgtxt, tim = tim) if invite is not None: item = invite.getTag('invite') jid_from = item.getAttr('from') @@ -387,7 +387,7 @@ class Connection: if subject: log_msgtxt = _('Subject: %s\n%s') % (subject, msgtxt) if msg.getTag('body'): - gajim.logger.write('incoming', log_msgtxt, frm, tim = tim) + gajim.logger.write('chat_msg_recv', frm, log_msgtxt, tim = tim) self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, subject, chatstate)) # END messageCB @@ -469,7 +469,7 @@ class Connection: self.dispatch('ERROR_ANSWER', ('', jid_stripped, errmsg, errcode)) if not ptype or ptype == 'unavailable': - gajim.logger.write('status', status, who, show) + gajim.logger.write('gcstatus', who, status, show) self.dispatch('GC_NOTIFY', (jid_stripped, show, status, resource, prs.getRole(), prs.getAffiliation(), prs.getJid(), prs.getReason(), prs.getActor(), prs.getStatusCode(), @@ -517,7 +517,7 @@ class Connection: else: self.vcard_shas[jid_stripped] = avatar_sha if not ptype or ptype == 'unavailable': - gajim.logger.write('status', status, jid_stripped, show) + gajim.logger.write('status', jid_stripped, status, show) self.dispatch('NOTIFY', (jid_stripped, show, status, resource, prio, keyID)) # END presenceCB @@ -1898,7 +1898,11 @@ class Connection: if subject: log_msg = _('Subject: %s\n%s') % (subject, msg) if log_msg: - gajim.logger.write('outgoing', log_msg, jid) + if type == 'chat': + kind = 'chat_msg_sent' + else: + kind = 'single_msg_sent' + gajim.logger.write(kind, jid, log_msg) self.dispatch('MSGSENT', (jid, msg, keyID)) def send_stanza(self, stanza): diff --git a/src/common/gajim.py b/src/common/gajim.py index 16d84ae4d..b30eae2da 100644 --- a/src/common/gajim.py +++ b/src/common/gajim.py @@ -23,7 +23,7 @@ import logging import mutex import common.config -import common.logger + interface = None # The actual interface (the gtk one for the moment) version = '0.9' @@ -37,6 +37,7 @@ h.setFormatter(f) log = logging.getLogger('Gajim') log.addHandler(h) +import common.logger logger = common.logger.Logger() # init the logger if os.name == 'nt': @@ -45,25 +46,21 @@ if os.name == 'nt': else: DATA_DIR = os.path.join('..', 'data') try: - # Documents and Settings\[User Name]\Application Data\Gajim\logs + # Documents and Settings\[User Name]\Application Data\Gajim LOGPATH = os.path.join(os.environ['appdata'], 'Gajim', 'Logs') # deprecated - LOG_DB_PATH = os.path.join(os.environ['appdata'], 'Gajim', 'logs.db') VCARDPATH = os.path.join(os.environ['appdata'], 'Gajim', 'Vcards') except KeyError: - # win9x, ./Logs etc + # win9x, in cwd LOGPATH = 'Logs' # deprecated - LOG_DB_PATH = 'logs.db' VCARDPATH = 'Vcards' else: # Unices DATA_DIR = '../data' LOGPATH = os.path.expanduser('~/.gajim/logs') # deprecated - LOG_DB_PATH = os.path.expanduser('~/.gajim/logs.db') VCARDPATH = os.path.expanduser('~/.gajim/vcards') try: LOGPATH = LOGPATH.decode(sys.getfilesystemencoding()) VCARDPATH = VCARDPATH.decode(sys.getfilesystemencoding()) - LOG_DB_PATH = LOG_DB_PATH.decode(sys.getfilesystemencoding()) except: pass diff --git a/src/common/helpers.py b/src/common/helpers.py index 8736f09cb..ec0a21641 100644 --- a/src/common/helpers.py +++ b/src/common/helpers.py @@ -23,8 +23,10 @@ import urllib import errno import sys import stat +from pysqlite2 import dbapi2 as sqlite import gajim +import logger from common import i18n from common.xmpp_stringprep import nodeprep, resourceprep, nameprep @@ -49,8 +51,8 @@ def parse_jid(jidstring): resource = None # Search for delimiters - user_sep = jidstring.find("@") - res_sep = jidstring.find("/") + user_sep = jidstring.find('@') + res_sep = jidstring.find('/') if user_sep == -1: if res_sep == -1: @@ -136,53 +138,6 @@ def temp_failure_retry(func, *args, **kwargs): else: raise -def check_paths(): - LOGPATH = gajim.LOGPATH - VCARDPATH = gajim.VCARDPATH - dot_gajim = os.path.dirname(LOGPATH) - if os.path.isfile(dot_gajim): - print _('%s is file but it should be a directory') % dot_gajim - print _('Gajim will now exit') - sys.exit() - elif os.path.isdir(dot_gajim): - s = os.stat(dot_gajim) - if s.st_mode & stat.S_IROTH: # others have read permission! - os.chmod(dot_gajim, 0700) # rwx------ - - if not os.path.exists(LOGPATH): - print _('creating %s directory') % LOGPATH - os.mkdir(LOGPATH, 0700) - elif os.path.isfile(LOGPATH): - print _('%s is file but it should be a directory') % LOGPATH - print _('Gajim will now exit') - sys.exit() - elif os.path.isdir(LOGPATH): - s = os.stat(LOGPATH) - if s.st_mode & stat.S_IROTH: # others have read permission! - os.chmod(LOGPATH, 0700) # rwx------ - - if not os.path.exists(VCARDPATH): - print _('creating %s directory') % VCARDPATH - os.mkdir(VCARDPATH, 0700) - elif os.path.isfile(VCARDPATH): - print _('%s is file but it should be a directory') % VCARDPATH - print _('Gajim will now exit') - sys.exit() - elif os.path.isdir(VCARDPATH): - s = os.stat(VCARDPATH) - if s.st_mode & stat.S_IROTH: # others have read permission! - os.chmod(VCARDPATH, 0700) # rwx------ - else: # dot_gajim doesn't exist - if dot_gajim: # is '' on win9x so avoid that - print _('creating %s directory') % dot_gajim - os.mkdir(dot_gajim, 0700) - if not os.path.isdir(LOGPATH): - print _('creating %s directory') % LOGPATH - os.mkdir(LOGPATH, 0700) - if not os.path.isdir(VCARDPATH): - print _('creating %s directory') % VCARDPATH - os.mkdir(VCARDPATH, 0700) - def convert_bytes(string): suffix = '' # IEC standard says KiB = 1024 bytes KB = 1000 bytes @@ -452,7 +407,7 @@ def play_sound(event): return if not os.path.exists(path_to_soundfile): return - if os.name == 'nt': + if os.name == 'nt': try: winsound.PlaySound(path_to_soundfile, winsound.SND_FILENAME|winsound.SND_ASYNC) diff --git a/src/common/logger.py b/src/common/logger.py index abae160c6..28c4c1b2f 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -18,202 +18,217 @@ ## import os +import sys import time +import datetime -import common.gajim from common import i18n _ = i18n._ -import helpers +try: + from pysqlite2 import dbapi2 as sqlite +except ImportError: + error = _('pysqlite2 (aka python-pysqlite2) dependency is missing. '\ + 'After you install pysqlite3, if you want to migrate your logs '\ + 'to the new database, please read: http://trac.gajim.org/wiki/MigrateLogToDot9DB ' + 'Exiting...' + ) + print >> sys.stderr, error + sys.exit() + +GOT_JIDS_ALREADY_IN_DB = False + +if os.name == 'nt': + try: + # Documents and Settings\[User Name]\Application Data\Gajim\logs.db + LOG_DB_PATH = os.path.join(os.environ['appdata'], 'Gajim', 'logs.db') + except KeyError: + # win9x, ./logs.db + LOG_DB_PATH = 'logs.db' +else: # Unices + LOG_DB_PATH = os.path.expanduser('~/.gajim/logs.db') + +try: + LOG_DB_PATH = LOG_DB_PATH.decode(sys.getfilesystemencoding()) +except: + pass class Logger: def __init__(self): - pass + if not os.path.exists(LOG_DB_PATH): + # this can happen only the first time (the time we create the db) + # db is created in src/common/checks_paths.py + return + + self.get_jids_already_in_db() - def write(self, kind, msg, jid, show = None, tim = None): + def get_jids_already_in_db(self): + con = sqlite.connect(LOG_DB_PATH) + cur = con.cursor() + cur.execute('SELECT jid FROM jids') + rows = cur.fetchall() # list of tupples: (u'aaa@bbb',), (u'cc@dd',)] + self.jids_already_in = [] + for row in rows: + # row[0] is first item of row (the only result here, the jid) + self.jids_already_in.append(row[0]) + con.close() + GOT_JIDS_ALREADY_IN_DB = True + + def jid_is_from_pm(cur, jid): + '''if jid is gajim@conf/nkour it's likely a pm one, how we know + gajim@conf is not a normal guy and nkour is not his resource? + we ask if gajim@conf is already in jids (as room) + this fails if user disable logging for room and only enables for + pm (so higly unlikely) and if we fail we do not force chaos + (user will see the first pm as if it was message in room's public chat)''' + + possible_room_jid, possible_nick = jid.split('/', 1) + + cur.execute('SELECT jid_id FROM jids WHERE jid="%s"' % possible_room_jid) + jid_id = cur.fetchone()[0] + if jid_id: + return True + else: + return False + + def get_jid_id(self, jid): + '''jids table has jid and jid_id + logs table has log_id, jid_id, contact_name, time, kind, show, message + so to ask logs we need jid_id that matches our jid in jids table + this method asks jid and returns the jid_id for later sql-ing on logs + ''' + con = sqlite.connect(LOG_DB_PATH) + cur = con.cursor() + + if jid in self.jids_already_in: # we already have jids in DB + cur.execute('SELECT jid_id FROM jids WHERE jid="%s"' % jid) + jid_id = cur.fetchone()[0] + else: # oh! a new jid :), we add him now + cur.execute('INSERT INTO jids (jid) VALUES (?)', (jid,)) + con.commit() + jid_id = cur.lastrowid + self.jids_already_in.append(jid) + return jid_id + + def write(self, kind, jid, message = None, show = None, tim = None): + '''write a row (status, gcstatus, message etc) to logs database + kind can be status, gcstatus, gc_msg, (we only recv for those 3), + single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent + we cannot know if it is pm or normal chat message, we try to guess + see jid_is_from_pm() + + we analyze jid and store it as follows: + jids.jid text column will hold JID if TC-related, room_jid if GC-related, + ROOM_JID/nick if pm-related.''' + + if not GOT_JIDS_ALREADY_IN_DB: + self.get_jids_already_in_db() + + con = sqlite.connect(LOG_DB_PATH) + cur = con.cursor() + jid = jid.lower() - if not tim: - tim = time.time() + contact_name_col = None # holds nickname for kinds gcstatus, gc_msg + # message holds the message unless kind is status or gcstatus, + # then it holds status message + message_col = message + show_col = show + if tim: + time_col = int(float(time.mktime(tim))) else: - tim = time.mktime(tim) + time_col = int(float(time.time())) - if not msg: - msg = '' - - msg = helpers.to_one_line(msg) - if len(jid.split('/')) > 1: - ji, nick = jid.split('/', 1) - else: - ji = jid - nick = '' - files = [] - if kind == 'status': # we save time:jid:show:msg - if not show: - show = 'online' - if common.gajim.config.get('log_notif_in_user_file'): - path_to_file = os.path.join(common.gajim.LOGPATH, ji) - if os.path.isdir(path_to_file): - jid = 'gcstatus' - msg = show + ':' + msg - show = nick - files.append(ji + '/' + ji) - if os.path.isfile(jid): - files.append(jid) - else: - files.append(ji) - if common.gajim.config.get('log_notif_in_sep_file'): - files.append('notify.log') - elif kind == 'incoming': # we save time:recv:message - path_to_file = os.path.join(common.gajim.LOGPATH, ji) - if os.path.isdir(path_to_file): - files.append(jid) - else: - files.append(ji) - jid = 'recv' - show = msg - msg = '' - elif kind == 'outgoing': # we save time:sent:message - path_to_file = os.path.join(common.gajim.LOGPATH, ji) - if os.path.isdir(path_to_file): - files.append(jid) - else: - files.append(ji) - jid = 'sent' - show = msg - msg = '' - elif kind == 'gc': # we save time:gc:nick:message - # create the folder if needed - ji_fn = os.path.join(common.gajim.LOGPATH, ji) - if os.path.isfile(ji_fn): - os.remove(ji_fn) - if not os.path.isdir(ji_fn): - os.mkdir(ji_fn, 0700) - files.append(ji + '/' + ji) - jid = 'gc' - show = nick - # convert to utf8 before writing to file if needed - if isinstance(tim, unicode): - tim = tim.encode('utf-8') - if isinstance(jid, unicode): - jid = jid.encode('utf-8') - if isinstance(show, unicode): - show = show.encode('utf-8') - if msg and isinstance(msg, unicode): - msg = msg.encode('utf-8') - for f in files: - path_to_file = os.path.join(common.gajim.LOGPATH, f) - if os.path.isdir(path_to_file): - return - # this does it rw-r-r by default but is in a dir with 700 so it's ok - fil = open(path_to_file, 'a') - fil.write('%s:%s:%s' % (tim, jid, show)) - if msg: - fil.write(':' + msg) - fil.write('\n') - fil.close() - - def __get_path_to_file(self, fjid): - jid = fjid.split('/')[0] - path_to_file = os.path.join(common.gajim.LOGPATH, jid) - if os.path.isdir(path_to_file): - if fjid == jid: # we want to read the gc history - path_to_file = os.path.join(common.gajim.LOGPATH, jid + '/' + jid) - else: #we want to read pm history - path_to_file = os.path.join(common.gajim.LOGPATH, fjid) - return path_to_file - - def get_no_of_lines(self, fjid): - '''returns total number of lines in a log file - returns 0 if log file does not exist''' - fjid = fjid.lower() - path_to_file = self.__get_path_to_file(fjid) - if not os.path.isfile(path_to_file): - return 0 - f = open(path_to_file, 'r') - return len(f.readlines()) # number of lines - - # FIXME: remove me when refactor in TC is done - def read_from_line_to_line(self, fjid, begin_from_line, end_line): - '''returns the text in the lines (list), - returns empty list if log file does not exist''' - fjid = fjid.lower() - path_to_file = self.__get_path_to_file(fjid) - if not os.path.isfile(path_to_file): - return [] - - lines = [] + def commit_to_db(values, cur = cur): + sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message) '\ + 'VALUES (?, ?, ?, ?, ?, ?)' + cur.execute(sql, values) + cur.connection.commit() - fil = open(path_to_file, 'r') - #fil.readlines(begin_from_line) # skip the previous lines - no_of_lines = begin_from_line # number of lines between being and end - while (no_of_lines < begin_from_line and fil.readline()): - no_of_lines += 1 - - print begin_from_line, end_line - while no_of_lines < end_line: - line = fil.readline().decode('utf-8') - print `line`, '@', no_of_lines - if line: - line = helpers.from_one_line(line) - lineSplited = line.split(':') - if len(lineSplited) > 2: - lines.append(lineSplited) - no_of_lines += 1 - else: # emplty line (we are at the end of file) - break - return lines - - def get_last_conversation_lines(self, jid, how_many_lines, timeout): - '''accepts how many lines to restore and when to time them out - (mark them as too old), returns the lines (list), empty list if log file - does not exist''' - fjid = fjid.lower() - path_to_file = self.__get_path_to_file(fjid) - if not os.path.isfile(path_to_file): - return [] - - - def get_conversation_for_date(self, fjid, year, month, day): - '''returns the text in the lines (list), - returns empty list if log file does not exist''' - fjid = fjid.lower() - path_to_file = self.__get_path_to_file(fjid) - if not os.path.isfile(path_to_file): - return [] - - lines = [] - f = open(path_to_file, 'r') - done = False - found_first_line_that_matches = False - while not done: - # it should be utf8 (I don't decode for optimization reasons) - line = f.readline() - if line: - line = helpers.from_one_line(line) - splitted_line = line.split(':') - if len(splitted_line) > 2: - # line[0] is date, line[1] is type of message - # line[2:] is message - date = splitted_line[0] - date = time.localtime(float(date)) - # eg. 2005 - line_year = int(time.strftime('%Y', date)) - # (01 - 12) - line_month = int(time.strftime('%m', date)) - # (01 - 31) - line_day = int(time.strftime('%d', date)) + jid_id = self.get_jid_id(jid) - # now check if that line is one of the lines we want - # (if it is in the date we want) - if line_year == year and line_month == month and line_day == day: - if found_first_line_that_matches is False: - found_first_line_that_matches = True - lines.append(splitted_line) - else: - if found_first_line_that_matches: # we had a match before - done = True # but no more. so we're done with that date - - else: - done = True + if kind == 'status': # we store (not None) time, jid, show, msg + # status for roster items + if show is None: + show_col = 'online' - return lines + values = (jid_id, contact_name_col, time_col, kind, show_col, message_col) + commit_to_db(values) + elif kind == 'gcstatus': + # status in ROOM (for pm status see status) + if show is None: + show_col = 'online' + + jid, nick = jid.split('/', 1) + + jid_id = self.get_jid_id(jid) # re-get jid_id for the new jid + contact_name_col = nick + values = (jid_id, contact_name_col, time_col, kind, show_col, message_col) + commit_to_db(values) + elif kind == 'gc_msg': + if jid.find('/') != -1: # if it has a / + jid, nick = jid.split('/', 1) + else: + # it's server message f.e. error message + # when user tries to ban someone but he's not allowed to + nick = None + jid_id = self.get_jid_id(jid) # re-get jid_id for the new jid + contact_name_col = nick + + values = (jid_id, contact_name_col, time_col, kind, show_col, message_col) + commit_to_db(values) + elif kind in ('single_msg_recv', 'chat_msg_recv', 'chat_msg_sent', 'single_msg_sent'): + values = (jid_id, contact_name_col, time_col, kind, show_col, message_col) + commit_to_db(values) + #con.close() + + def get_last_conversation_lines(self, jid, restore_how_many_rows, + pending_how_many, timeout): + '''accepts how many rows to restore and when to time them out (in minutes) + (mark them as too old) and number of messages that are in queue + and are already logged but pending to be viewed, + returns a list of tupples containg time, kind, message, + list with empty tupple if nothing found to meet our demands''' + now = int(float(time.time())) + jid = jid.lower() + jid_id = self.get_jid_id(jid) + con = sqlite.connect(LOG_DB_PATH) + cur = con.cursor() + # so if we ask last 5 lines and we have 2 pending we get + # 3 - 8 (we avoid the last 2 lines but we still return 5 asked) + cur.execute(''' + SELECT time, kind, message FROM logs + WHERE jid_id = %d AND kind IN + ('single_msg_recv', 'chat_msg_recv', 'chat_msg_sent', 'single_msg_sent') + ORDER BY time DESC LIMIT %d OFFSET %d + ''' % (jid_id, restore_how_many_rows, pending_how_many) + ) + + results = cur.fetchall() + results.reverse() + return results + + def get_conversation_for_date(self, jid, year, month, day): + '''returns contact_name, time, kind, show, message + for each row in a list of tupples, + returns list with empty tupple if we found nothing to meet our demands''' + jid = jid.lower() + jid_id = self.get_jid_id(jid) + + # gimme unixtime from year month day: + d = datetime.date(2005, 10, 3) + local_time = d.timetuple() # time tupple (compat with time.localtime()) + start_of_day = int(time.mktime(local_time)) # we have time since epoch baby :) + + now = time.time() + + con = sqlite.connect(LOG_DB_PATH) + cur = con.cursor() + cur.execute(''' + SELECT contact_name, time, kind, show, message FROM logs + WHERE jid_id = %d + AND time BETWEEN %d AND %d + ORDER BY time + ''' % (jid_id, start_of_day, now)) + + results = cur.fetchall() + return results diff --git a/src/gajim.py b/src/gajim.py index 6f6746449..122e74da8 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -63,6 +63,9 @@ except ImportError: dlg.destroy() sys.exit() +from common import check_paths +check_paths.check_and_possible_create_paths() + path = os.getcwd() if '.svn' in os.listdir(path): # import gtkexcepthook only for those that run svn @@ -1219,7 +1222,6 @@ class Interface: sys.exit(1) def __init__(self): - helpers.check_paths() gajim.interface = self self.default_values = { 'inmsgcolor': gajim.config.get('inmsgcolor'), diff --git a/src/history_window.py b/src/history_window.py index 5a4cda3a2..e629dba94 100644 --- a/src/history_window.py +++ b/src/history_window.py @@ -86,67 +86,64 @@ class HistoryWindow: '''adds all the lines for given date in textbuffer''' self.history_buffer.set_text('') # clear the buffer first lines = gajim.logger.get_conversation_for_date(self.jid, year, month, day) + # lines holds list with tupples that have: + # contact_name, time, kind, show, message for line in lines: - # line[0] is date, line[1] is type of message - # line[2:] is message - date = line[0] - self.add_new_line(date, line[1], line[2:]) + # line[0] is contact_name, line[1] is time of message + # line[2] is kind, line[3] is show, line[4] is message + self.add_new_line(line[0], line[1], line[2], line[3], line[4]) - def add_new_line(self, date, type, data): + def add_new_line(self, contact_name, tim, kind, show, message): '''add a new line in textbuffer''' buf = self.history_buffer end_iter = buf.get_end_iter() - tim = time.strftime('[%X] ', time.localtime(float(date))) - buf.insert(end_iter, tim) - name = None + tim = time.strftime('[%X] ', time.localtime(float(tim))) + buf.insert(end_iter, tim) # add time tag_name = '' tag_msg = '' - if type == 'gc': - name = data[0] - msg = ':'.join(data[1:]) + + if kind == 'gc_msg': tag_name = 'incoming' - elif type == 'gcstatus': - nick = data[0] - show = data[1] - status_msg = ':'.join(data[2:]) - if status_msg: - msg = _('%(nick)s is now %(status)s: %(status_msg)s') % {'nick': nick, - 'status': helpers.get_uf_show(show), 'status_msg': status_msg } + elif kind in ('single_msg_recv', 'chat_msg_recv'): + try: + contact_name = gajim.contacts[self.account][self.jid][0].name + except: + contact_name = self.jid.split('@')[0] + tag_name = 'incoming' + elif kind in ('single_msg_sent', 'chat_msg_sent'): + contact_name = gajim.nicks[self.account] + tag_name = 'outgoing' + elif kind == 'gcstatus': + # message here (if not None) is status message + if message: + message = _('%(nick)s is now %(status)s: %(status_msg)s') %\ + {'nick': contact_name, 'status': helpers.get_uf_show(show), + 'status_msg': message } else: - show = show[:-1] # remove last \n - msg = _('%(nick)s is now %(status)s\n') % {'nick': nick, + message = _('%(nick)s is now %(status)s') % {'nick': contact_name, 'status': helpers.get_uf_show(show) } tag_msg = 'status' - elif type == 'recv': - try: - name = gajim.contacts[self.account][self.jid][0].name - except: - name = None - if not name: - name = self.jid.split('@')[0] - msg = ':'.join(data[0:]) - tag_name = 'incoming' - elif type == 'sent': - name = gajim.nicks[self.account] - msg = ':'.join(data[0:]) - tag_name = 'outgoing' - else: # status - status_msg = ':'.join(data[1:]) - if status_msg: - msg = _('Status is now: %(status)s: %(status_msg)s') % \ - {'status': helpers.get_uf_show(data[0]), 'status_msg': status_msg} + else: # 'status' + # message here (if not None) is status message + if message: + message = _('Status is now: %(status)s: %(status_msg)s') % \ + {'status': helpers.get_uf_show(show), 'status_msg': message} else: - data[0] = data[0][:-1] # remove last \n - msg = _('Status is now: %(status)s\n') % { 'status': - helpers.get_uf_show(data[0]) } + message = _('Status is now: %(status)s') % { 'status': + helpers.get_uf_show(show) } tag_msg = 'status' - if name: + # do not do this if gcstats, avoid dupping contact_name + # eg. nkour: nkour is now Offline + if contact_name and kind != 'gcstatus': + # add stuff before and after contact name before_str = gajim.config.get('before_nickname') after_str = gajim.config.get('after_nickname') - format = before_str + name + after_str + ' ' + format = before_str + contact_name + after_str + ' ' buf.insert_with_tags_by_name(end_iter, format, tag_name) + + message = message + '\n' if tag_msg: - buf.insert_with_tags_by_name(end_iter, msg, tag_msg) + buf.insert_with_tags_by_name(end_iter, message, tag_msg) else: - buf.insert(end_iter, msg) + buf.insert(end_iter, message) diff --git a/src/tabbed_chat_window.py b/src/tabbed_chat_window.py index 22b228d5f..1b50da6c0 100644 --- a/src/tabbed_chat_window.py +++ b/src/tabbed_chat_window.py @@ -892,8 +892,6 @@ class TabbedChatWindow(chat.Chat): if gajim.jid_is_transport(jid): return - return # FIXME: the logic below works, but needs db so return atm - # How many lines to restore and when to time them out restore_how_many = gajim.config.get('restore_lines') timeout = gajim.config.get('restore_timeout') # in minutes