log system rewrite to use sqlite database instead of plain ascii files. this allows us to scale better (be faster), provide search in history, and save logs for JIDs that are non-ASCII. PLEASE read http://trac.gajim.org/wiki/MigrateLogToDot9DB
This commit is contained in:
parent
767dc426ed
commit
d9cc33cf26
2
README
2
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
'''
|
||||
)
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
## Gajim Team:
|
||||
## - Yann Le Boulanger <asterix@lagaule.org>
|
||||
## - Vincent Hanquez <tab@snarc.org>
|
||||
## - Nikos Kouremenos <kourem@gmail.com>
|
||||
## - Travis Shirk <travis@pobox.com>
|
||||
##
|
||||
## 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()
|
|
@ -364,12 +364,12 @@ class Connection:
|
|||
if not msg.getTag('body'): #no <body>
|
||||
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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
def write(self, kind, msg, jid, show = None, tim = None):
|
||||
jid = jid.lower()
|
||||
if not tim:
|
||||
tim = time.time()
|
||||
else:
|
||||
tim = time.mktime(tim)
|
||||
|
||||
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):
|
||||
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
|
||||
# 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
|
||||
self.get_jids_already_in_db()
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# 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 []
|
||||
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)'''
|
||||
|
||||
lines = []
|
||||
possible_room_jid, possible_nick = jid.split('/', 1)
|
||||
|
||||
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))
|
||||
|
||||
# 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)
|
||||
cur.execute('SELECT jid_id FROM jids WHERE jid="%s"' % possible_room_jid)
|
||||
jid_id = cur.fetchone()[0]
|
||||
if jid_id:
|
||||
return True
|
||||
else:
|
||||
if found_first_line_that_matches: # we had a match before
|
||||
done = True # but no more. so we're done with that date
|
||||
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()
|
||||
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:
|
||||
done = True
|
||||
time_col = int(float(time.time()))
|
||||
|
||||
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()
|
||||
|
||||
jid_id = self.get_jid_id(jid)
|
||||
|
||||
if kind == 'status': # we store (not None) time, jid, show, msg
|
||||
# status for roster items
|
||||
if show is None:
|
||||
show_col = 'online'
|
||||
|
||||
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
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue