| 31 | | pass |
| 32 | | |
| 33 | | def write(self, kind, msg, jid, show = None, tim = None): |
| | 58 | if not os.path.exists(LOG_DB_PATH): |
| | 59 | # this can happen only the first time (the time we create the db) |
| | 60 | # db is created in src/common/checks_paths.py |
| | 61 | return |
| | 62 | |
| | 63 | self.get_jids_already_in_db() |
| | 64 | |
| | 65 | def get_jids_already_in_db(self): |
| | 66 | con = sqlite.connect(LOG_DB_PATH) |
| | 67 | cur = con.cursor() |
| | 68 | cur.execute('SELECT jid FROM jids') |
| | 69 | rows = cur.fetchall() # list of tupples: (u'aaa@bbb',), (u'cc@dd',)] |
| | 70 | self.jids_already_in = [] |
| | 71 | for row in rows: |
| | 72 | # row[0] is first item of row (the only result here, the jid) |
| | 73 | self.jids_already_in.append(row[0]) |
| | 74 | con.close() |
| | 75 | GOT_JIDS_ALREADY_IN_DB = True |
| | 76 | |
| | 77 | def jid_is_from_pm(cur, jid): |
| | 78 | '''if jid is gajim@conf/nkour it's likely a pm one, how we know |
| | 79 | gajim@conf is not a normal guy and nkour is not his resource? |
| | 80 | we ask if gajim@conf is already in jids (as room) |
| | 81 | this fails if user disable logging for room and only enables for |
| | 82 | pm (so higly unlikely) and if we fail we do not force chaos |
| | 83 | (user will see the first pm as if it was message in room's public chat)''' |
| | 84 | |
| | 85 | possible_room_jid, possible_nick = jid.split('/', 1) |
| | 86 | |
| | 87 | cur.execute('SELECT jid_id FROM jids WHERE jid="%s"' % possible_room_jid) |
| | 88 | jid_id = cur.fetchone()[0] |
| | 89 | if jid_id: |
| | 90 | return True |
| | 91 | else: |
| | 92 | return False |
| | 93 | |
| | 94 | def get_jid_id(self, jid): |
| | 95 | '''jids table has jid and jid_id |
| | 96 | logs table has log_id, jid_id, contact_name, time, kind, show, message |
| | 97 | so to ask logs we need jid_id that matches our jid in jids table |
| | 98 | this method asks jid and returns the jid_id for later sql-ing on logs |
| | 99 | ''' |
| | 100 | con = sqlite.connect(LOG_DB_PATH) |
| | 101 | cur = con.cursor() |
| | 102 | |
| | 103 | if jid in self.jids_already_in: # we already have jids in DB |
| | 104 | cur.execute('SELECT jid_id FROM jids WHERE jid="%s"' % jid) |
| | 105 | jid_id = cur.fetchone()[0] |
| | 106 | else: # oh! a new jid :), we add him now |
| | 107 | cur.execute('INSERT INTO jids (jid) VALUES (?)', (jid,)) |
| | 108 | con.commit() |
| | 109 | jid_id = cur.lastrowid |
| | 110 | self.jids_already_in.append(jid) |
| | 111 | return jid_id |
| | 112 | |
| | 113 | def write(self, kind, jid, message = None, show = None, tim = None): |
| | 114 | '''write a row (status, gcstatus, message etc) to logs database |
| | 115 | kind can be status, gcstatus, gc_msg, (we only recv for those 3), |
| | 116 | single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent |
| | 117 | we cannot know if it is pm or normal chat message, we try to guess |
| | 118 | see jid_is_from_pm() |
| | 119 | |
| | 120 | we analyze jid and store it as follows: |
| | 121 | jids.jid text column will hold JID if TC-related, room_jid if GC-related, |
| | 122 | ROOM_JID/nick if pm-related.''' |
| | 123 | |
| | 124 | if not GOT_JIDS_ALREADY_IN_DB: |
| | 125 | self.get_jids_already_in_db() |
| | 126 | |
| | 127 | con = sqlite.connect(LOG_DB_PATH) |
| | 128 | cur = con.cursor() |
| | 129 | |
| 38 | | tim = time.mktime(tim) |
| 39 | | |
| 40 | | if not msg: |
| 41 | | msg = '' |
| 42 | | |
| 43 | | msg = helpers.to_one_line(msg) |
| 44 | | if len(jid.split('/')) > 1: |
| 45 | | ji, nick = jid.split('/', 1) |
| 46 | | else: |
| 47 | | ji = jid |
| 48 | | nick = '' |
| 49 | | files = [] |
| 50 | | if kind == 'status': # we save time:jid:show:msg |
| 51 | | if not show: |
| 52 | | show = 'online' |
| 53 | | if common.gajim.config.get('log_notif_in_user_file'): |
| 54 | | path_to_file = os.path.join(common.gajim.LOGPATH, ji) |
| 55 | | if os.path.isdir(path_to_file): |
| 56 | | jid = 'gcstatus' |
| 57 | | msg = show + ':' + msg |
| 58 | | show = nick |
| 59 | | files.append(ji + '/' + ji) |
| 60 | | if os.path.isfile(jid): |
| 61 | | files.append(jid) |
| 62 | | else: |
| 63 | | files.append(ji) |
| 64 | | if common.gajim.config.get('log_notif_in_sep_file'): |
| 65 | | files.append('notify.log') |
| 66 | | elif kind == 'incoming': # we save time:recv:message |
| 67 | | path_to_file = os.path.join(common.gajim.LOGPATH, ji) |
| 68 | | if os.path.isdir(path_to_file): |
| 69 | | files.append(jid) |
| | 139 | time_col = int(float(time.time())) |
| | 140 | |
| | 141 | def commit_to_db(values, cur = cur): |
| | 142 | sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message) '\ |
| | 143 | 'VALUES (?, ?, ?, ?, ?, ?)' |
| | 144 | cur.execute(sql, values) |
| | 145 | cur.connection.commit() |
| | 146 | |
| | 147 | jid_id = self.get_jid_id(jid) |
| | 148 | |
| | 149 | if kind == 'status': # we store (not None) time, jid, show, msg |
| | 150 | # status for roster items |
| | 151 | if show is None: |
| | 152 | show_col = 'online' |
| | 153 | |
| | 154 | values = (jid_id, contact_name_col, time_col, kind, show_col, message_col) |
| | 155 | commit_to_db(values) |
| | 156 | elif kind == 'gcstatus': |
| | 157 | # status in ROOM (for pm status see status) |
| | 158 | if show is None: |
| | 159 | show_col = 'online' |
| | 160 | |
| | 161 | jid, nick = jid.split('/', 1) |
| | 162 | |
| | 163 | jid_id = self.get_jid_id(jid) # re-get jid_id for the new jid |
| | 164 | contact_name_col = nick |
| | 165 | values = (jid_id, contact_name_col, time_col, kind, show_col, message_col) |
| | 166 | commit_to_db(values) |
| | 167 | elif kind == 'gc_msg': |
| | 168 | if jid.find('/') != -1: # if it has a / |
| | 169 | jid, nick = jid.split('/', 1) |
| 71 | | files.append(ji) |
| 72 | | jid = 'recv' |
| 73 | | show = msg |
| 74 | | msg = '' |
| 75 | | elif kind == 'outgoing': # we save time:sent:message |
| 76 | | path_to_file = os.path.join(common.gajim.LOGPATH, ji) |
| 77 | | if os.path.isdir(path_to_file): |
| 78 | | files.append(jid) |
| 79 | | else: |
| 80 | | files.append(ji) |
| 81 | | jid = 'sent' |
| 82 | | show = msg |
| 83 | | msg = '' |
| 84 | | elif kind == 'gc': # we save time:gc:nick:message |
| 85 | | # create the folder if needed |
| 86 | | ji_fn = os.path.join(common.gajim.LOGPATH, ji) |
| 87 | | if os.path.isfile(ji_fn): |
| 88 | | os.remove(ji_fn) |
| 89 | | if not os.path.isdir(ji_fn): |
| 90 | | os.mkdir(ji_fn, 0700) |
| 91 | | files.append(ji + '/' + ji) |
| 92 | | jid = 'gc' |
| 93 | | show = nick |
| 94 | | # convert to utf8 before writing to file if needed |
| 95 | | if isinstance(tim, unicode): |
| 96 | | tim = tim.encode('utf-8') |
| 97 | | if isinstance(jid, unicode): |
| 98 | | jid = jid.encode('utf-8') |
| 99 | | if isinstance(show, unicode): |
| 100 | | show = show.encode('utf-8') |
| 101 | | if msg and isinstance(msg, unicode): |
| 102 | | msg = msg.encode('utf-8') |
| 103 | | for f in files: |
| 104 | | path_to_file = os.path.join(common.gajim.LOGPATH, f) |
| 105 | | if os.path.isdir(path_to_file): |
| 106 | | return |
| 107 | | # this does it rw-r-r by default but is in a dir with 700 so it's ok |
| 108 | | fil = open(path_to_file, 'a') |
| 109 | | fil.write('%s:%s:%s' % (tim, jid, show)) |
| 110 | | if msg: |
| 111 | | fil.write(':' + msg) |
| 112 | | fil.write('\n') |
| 113 | | fil.close() |
| 114 | | |
| 115 | | def __get_path_to_file(self, fjid): |
| 116 | | jid = fjid.split('/')[0] |
| 117 | | path_to_file = os.path.join(common.gajim.LOGPATH, jid) |
| 118 | | if os.path.isdir(path_to_file): |
| 119 | | if fjid == jid: # we want to read the gc history |
| 120 | | path_to_file = os.path.join(common.gajim.LOGPATH, jid + '/' + jid) |
| 121 | | else: #we want to read pm history |
| 122 | | path_to_file = os.path.join(common.gajim.LOGPATH, fjid) |
| 123 | | return path_to_file |
| 124 | | |
| 125 | | def get_no_of_lines(self, fjid): |
| 126 | | '''returns total number of lines in a log file |
| 127 | | returns 0 if log file does not exist''' |
| 128 | | fjid = fjid.lower() |
| 129 | | path_to_file = self.__get_path_to_file(fjid) |
| 130 | | if not os.path.isfile(path_to_file): |
| 131 | | return 0 |
| 132 | | f = open(path_to_file, 'r') |
| 133 | | return len(f.readlines()) # number of lines |
| 134 | | |
| 135 | | # FIXME: remove me when refactor in TC is done |
| 136 | | def read_from_line_to_line(self, fjid, begin_from_line, end_line): |
| 137 | | '''returns the text in the lines (list), |
| 138 | | returns empty list if log file does not exist''' |
| 139 | | fjid = fjid.lower() |
| 140 | | path_to_file = self.__get_path_to_file(fjid) |
| 141 | | if not os.path.isfile(path_to_file): |
| 142 | | return [] |
| 143 | | |
| 144 | | lines = [] |
| 145 | | |
| 146 | | fil = open(path_to_file, 'r') |
| 147 | | #fil.readlines(begin_from_line) # skip the previous lines |
| 148 | | no_of_lines = begin_from_line # number of lines between being and end |
| 149 | | while (no_of_lines < begin_from_line and fil.readline()): |
| 150 | | no_of_lines += 1 |
| 151 | | |
| 152 | | print begin_from_line, end_line |
| 153 | | while no_of_lines < end_line: |
| 154 | | line = fil.readline().decode('utf-8') |
| 155 | | print `line`, '@', no_of_lines |
| 156 | | if line: |
| 157 | | line = helpers.from_one_line(line) |
| 158 | | lineSplited = line.split(':') |
| 159 | | if len(lineSplited) > 2: |
| 160 | | lines.append(lineSplited) |
| 161 | | no_of_lines += 1 |
| 162 | | else: # emplty line (we are at the end of file) |
| 163 | | break |
| 164 | | return lines |
| 165 | | |
| 166 | | def get_last_conversation_lines(self, jid, how_many_lines, timeout): |
| 167 | | '''accepts how many lines to restore and when to time them out |
| 168 | | (mark them as too old), returns the lines (list), empty list if log file |
| 169 | | does not exist''' |
| 170 | | fjid = fjid.lower() |
| 171 | | path_to_file = self.__get_path_to_file(fjid) |
| 172 | | if not os.path.isfile(path_to_file): |
| 173 | | return [] |
| 174 | | |
| 175 | | |
| 176 | | def get_conversation_for_date(self, fjid, year, month, day): |
| 177 | | '''returns the text in the lines (list), |
| 178 | | returns empty list if log file does not exist''' |
| 179 | | fjid = fjid.lower() |
| 180 | | path_to_file = self.__get_path_to_file(fjid) |
| 181 | | if not os.path.isfile(path_to_file): |
| 182 | | return [] |
| 183 | | |
| 184 | | lines = [] |
| 185 | | f = open(path_to_file, 'r') |
| 186 | | done = False |
| 187 | | found_first_line_that_matches = False |
| 188 | | while not done: |
| 189 | | # it should be utf8 (I don't decode for optimization reasons) |
| 190 | | line = f.readline() |
| 191 | | if line: |
| 192 | | line = helpers.from_one_line(line) |
| 193 | | splitted_line = line.split(':') |
| 194 | | if len(splitted_line) > 2: |
| 195 | | # line[0] is date, line[1] is type of message |
| 196 | | # line[2:] is message |
| 197 | | date = splitted_line[0] |
| 198 | | date = time.localtime(float(date)) |
| 199 | | # eg. 2005 |
| 200 | | line_year = int(time.strftime('%Y', date)) |
| 201 | | # (01 - 12) |
| 202 | | line_month = int(time.strftime('%m', date)) |
| 203 | | # (01 - 31) |
| 204 | | line_day = int(time.strftime('%d', date)) |
| 205 | | |
| 206 | | # now check if that line is one of the lines we want |
| 207 | | # (if it is in the date we want) |
| 208 | | if line_year == year and line_month == month and line_day == day: |
| 209 | | if found_first_line_that_matches is False: |
| 210 | | found_first_line_that_matches = True |
| 211 | | lines.append(splitted_line) |
| 212 | | else: |
| 213 | | if found_first_line_that_matches: # we had a match before |
| 214 | | done = True # but no more. so we're done with that date |
| | 171 | # it's server message f.e. error message |
| | 172 | # when user tries to ban someone but he's not allowed to |
| | 173 | nick = None |
| | 174 | jid_id = self.get_jid_id(jid) # re-get jid_id for the new jid |
| | 175 | contact_name_col = nick |