1#!/usr/bin/env python
2
3"""
4Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
5See the file 'LICENSE' for copying permission
6"""
7
8import cgi
9import hashlib
10import os
11import re
12import shutil
13import tempfile
14import threading
15
16from lib.core.common import Backend
17from lib.core.common import checkFile
18from lib.core.common import dataToDumpFile
19from lib.core.common import dataToStdout
20from lib.core.common import getSafeExString
21from lib.core.common import isListLike
22from lib.core.common import isMultiThreadMode
23from lib.core.common import normalizeUnicode
24from lib.core.common import openFile
25from lib.core.common import prioritySortColumns
26from lib.core.common import randomInt
27from lib.core.common import safeCSValue
28from lib.core.common import unsafeSQLIdentificatorNaming
29from lib.core.compat import xrange
30from lib.core.convert import getBytes
31from lib.core.convert import getConsoleLength
32from lib.core.convert import getText
33from lib.core.convert import getUnicode
34from lib.core.convert import htmlEscape
35from lib.core.data import conf
36from lib.core.data import kb
37from lib.core.data import logger
38from lib.core.dicts import DUMP_REPLACEMENTS
39from lib.core.enums import CONTENT_STATUS
40from lib.core.enums import CONTENT_TYPE
41from lib.core.enums import DBMS
42from lib.core.enums import DUMP_FORMAT
43from lib.core.exception import SqlmapGenericException
44from lib.core.exception import SqlmapSystemException
45from lib.core.exception import SqlmapValueException
46from lib.core.replication import Replication
47from lib.core.settings import DUMP_FILE_BUFFER_SIZE
48from lib.core.settings import HTML_DUMP_CSS_STYLE
49from lib.core.settings import IS_WIN
50from lib.core.settings import METADB_SUFFIX
51from lib.core.settings import MIN_BINARY_DISK_DUMP_SIZE
52from lib.core.settings import TRIM_STDOUT_DUMP_SIZE
53from lib.core.settings import UNICODE_ENCODING
54from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT
55from lib.core.settings import VERSION_STRING
56from lib.core.settings import WINDOWS_RESERVED_NAMES
57from lib.utils.safe2bin import safechardecode
58from thirdparty import six
59from thirdparty.magic import magic
60
61class Dump(object):
62    """
63    This class defines methods used to parse and output the results
64    of SQL injection actions
65    """
66
67    def __init__(self):
68        self._outputFile = None
69        self._outputFP = None
70        self._lock = threading.Lock()
71
72    def _write(self, data, newline=True, console=True, content_type=None):
73        if conf.api:
74            dataToStdout(data, content_type=content_type, status=CONTENT_STATUS.COMPLETE)
75            return
76
77        text = "%s%s" % (data, "\n" if newline else " ")
78
79        if console:
80            dataToStdout(text)
81
82        multiThreadMode = isMultiThreadMode()
83        if multiThreadMode:
84            self._lock.acquire()
85
86        try:
87            self._outputFP.write(text)
88        except IOError as ex:
89            errMsg = "error occurred while writing to log file ('%s')" % getSafeExString(ex)
90            raise SqlmapGenericException(errMsg)
91
92        if multiThreadMode:
93            self._lock.release()
94
95        kb.dataOutputFlag = True
96
97    def flush(self):
98        if self._outputFP:
99            try:
100                self._outputFP.flush()
101            except IOError:
102                pass
103
104    def setOutputFile(self):
105        self._outputFile = os.path.join(conf.outputPath, "log")
106        try:
107            self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb")
108        except IOError as ex:
109            errMsg = "error occurred while opening log file ('%s')" % getSafeExString(ex)
110            raise SqlmapGenericException(errMsg)
111
112    def getOutputFile(self):
113        return self._outputFile
114
115    def singleString(self, data, content_type=None):
116        self._write(data, content_type=content_type)
117
118    def string(self, header, data, content_type=None, sort=True):
119        if conf.api:
120            self._write(data, content_type=content_type)
121            return
122
123        if isListLike(data):
124            self.lister(header, data, content_type, sort)
125        elif data is not None:
126            _ = getUnicode(data)
127
128            if _.endswith("\r\n"):
129                _ = _[:-2]
130
131            elif _.endswith("\n"):
132                _ = _[:-1]
133
134            if _.strip(' '):
135                _ = _.strip(' ')
136
137            if "\n" in _:
138                self._write("%s:\n---\n%s\n---" % (header, _))
139            else:
140                self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, six.string_types) else _))
141
142    def lister(self, header, elements, content_type=None, sort=True):
143        if elements and sort:
144            try:
145                elements = set(elements)
146                elements = list(elements)
147                elements.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _)
148            except:
149                pass
150
151        if conf.api:
152            self._write(elements, content_type=content_type)
153            return
154
155        if elements:
156            self._write("%s [%d]:" % (header, len(elements)))
157
158        for element in elements:
159            if isinstance(element, six.string_types):
160                self._write("[*] %s" % element)
161            elif isListLike(element):
162                self._write("[*] " + ", ".join(getUnicode(e) for e in element))
163
164        if elements:
165            self._write("")
166
167    def banner(self, data):
168        self.string("banner", data, content_type=CONTENT_TYPE.BANNER)
169
170    def currentUser(self, data):
171        self.string("current user", data, content_type=CONTENT_TYPE.CURRENT_USER)
172
173    def currentDb(self, data):
174        if Backend.isDbms(DBMS.MAXDB):
175            self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB)
176        elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2):
177            self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB)
178        else:
179            self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB)
180
181    def hostname(self, data):
182        self.string("hostname", data, content_type=CONTENT_TYPE.HOSTNAME)
183
184    def dba(self, data):
185        self.string("current user is DBA", data, content_type=CONTENT_TYPE.IS_DBA)
186
187    def users(self, users):
188        self.lister("database management system users", users, content_type=CONTENT_TYPE.USERS)
189
190    def statements(self, statements):
191        self.lister("SQL statements", statements, content_type=CONTENT_TYPE.STATEMENTS)
192
193    def userSettings(self, header, userSettings, subHeader, content_type=None):
194        self._areAdmins = set()
195
196        if isinstance(userSettings, (tuple, list, set)):
197            self._areAdmins = userSettings[1]
198            userSettings = userSettings[0]
199
200        users = [_ for _ in userSettings.keys() if _ is not None]
201        users.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _)
202
203        if conf.api:
204            self._write(userSettings, content_type=content_type)
205            return
206
207        if userSettings:
208            self._write("%s:" % header)
209
210        for user in users:
211            settings = userSettings[user]
212
213            if settings is None:
214                stringSettings = ""
215            else:
216                stringSettings = " [%d]:" % len(settings)
217
218            if user in self._areAdmins:
219                self._write("[*] %s (administrator)%s" % (user, stringSettings))
220            else:
221                self._write("[*] %s%s" % (user, stringSettings))
222
223            if settings:
224                settings.sort()
225
226                for setting in settings:
227                    self._write("    %s: %s" % (subHeader, setting))
228
229        if userSettings:
230            self.singleString("")
231
232    def dbs(self, dbs):
233        self.lister("available databases", dbs, content_type=CONTENT_TYPE.DBS)
234
235    def dbTables(self, dbTables):
236        if isinstance(dbTables, dict) and len(dbTables) > 0:
237            if conf.api:
238                self._write(dbTables, content_type=CONTENT_TYPE.TABLES)
239                return
240
241            maxlength = 0
242
243            for tables in dbTables.values():
244                for table in tables:
245                    if table and isListLike(table):
246                        table = table[0]
247
248                    maxlength = max(maxlength, getConsoleLength(unsafeSQLIdentificatorNaming(getUnicode(table))))
249
250            lines = "-" * (int(maxlength) + 2)
251
252            for db, tables in dbTables.items():
253                tables.sort()
254
255                self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database")
256
257                if len(tables) == 1:
258                    self._write("[1 table]")
259                else:
260                    self._write("[%d tables]" % len(tables))
261
262                self._write("+%s+" % lines)
263
264                for table in tables:
265                    if table and isListLike(table):
266                        table = table[0]
267
268                    table = unsafeSQLIdentificatorNaming(table)
269                    blank = " " * (maxlength - getConsoleLength(getUnicode(table)))
270                    self._write("| %s%s |" % (table, blank))
271
272                self._write("+%s+\n" % lines)
273        elif dbTables is None or len(dbTables) == 0:
274            self.singleString("No tables found", content_type=CONTENT_TYPE.TABLES)
275        else:
276            self.string("tables", dbTables, content_type=CONTENT_TYPE.TABLES)
277
278    def dbTableColumns(self, tableColumns, content_type=None):
279        if isinstance(tableColumns, dict) and len(tableColumns) > 0:
280            if conf.api:
281                self._write(tableColumns, content_type=content_type)
282                return
283
284            for db, tables in tableColumns.items():
285                if not db:
286                    db = "All"
287
288                for table, columns in tables.items():
289                    maxlength1 = 0
290                    maxlength2 = 0
291
292                    colType = None
293
294                    colList = list(columns.keys())
295                    colList.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _)
296
297                    for column in colList:
298                        colType = columns[column]
299
300                        column = unsafeSQLIdentificatorNaming(column)
301                        maxlength1 = max(maxlength1, len(column or ""))
302                        maxlength2 = max(maxlength2, len(colType or ""))
303
304                    maxlength1 = max(maxlength1, len("COLUMN"))
305                    lines1 = "-" * (maxlength1 + 2)
306
307                    if colType is not None:
308                        maxlength2 = max(maxlength2, len("TYPE"))
309                        lines2 = "-" * (maxlength2 + 2)
310
311                    self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db else "Current database", unsafeSQLIdentificatorNaming(table)))
312
313                    if len(columns) == 1:
314                        self._write("[1 column]")
315                    else:
316                        self._write("[%d columns]" % len(columns))
317
318                    if colType is not None:
319                        self._write("+%s+%s+" % (lines1, lines2))
320                    else:
321                        self._write("+%s+" % lines1)
322
323                    blank1 = " " * (maxlength1 - len("COLUMN"))
324
325                    if colType is not None:
326                        blank2 = " " * (maxlength2 - len("TYPE"))
327
328                    if colType is not None:
329                        self._write("| Column%s | Type%s |" % (blank1, blank2))
330                        self._write("+%s+%s+" % (lines1, lines2))
331                    else:
332                        self._write("| Column%s |" % blank1)
333                        self._write("+%s+" % lines1)
334
335                    for column in colList:
336                        colType = columns[column]
337
338                        column = unsafeSQLIdentificatorNaming(column)
339                        blank1 = " " * (maxlength1 - len(column))
340
341                        if colType is not None:
342                            blank2 = " " * (maxlength2 - len(colType))
343                            self._write("| %s%s | %s%s |" % (column, blank1, colType, blank2))
344                        else:
345                            self._write("| %s%s |" % (column, blank1))
346
347                    if colType is not None:
348                        self._write("+%s+%s+\n" % (lines1, lines2))
349                    else:
350                        self._write("+%s+\n" % lines1)
351
352    def dbTablesCount(self, dbTables):
353        if isinstance(dbTables, dict) and len(dbTables) > 0:
354            if conf.api:
355                self._write(dbTables, content_type=CONTENT_TYPE.COUNT)
356                return
357
358            maxlength1 = len("Table")
359            maxlength2 = len("Entries")
360
361            for ctables in dbTables.values():
362                for tables in ctables.values():
363                    for table in tables:
364                        maxlength1 = max(maxlength1, getConsoleLength(getUnicode(table)))
365
366            for db, counts in dbTables.items():
367                self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database")
368
369                lines1 = "-" * (maxlength1 + 2)
370                blank1 = " " * (maxlength1 - len("Table"))
371                lines2 = "-" * (maxlength2 + 2)
372                blank2 = " " * (maxlength2 - len("Entries"))
373
374                self._write("+%s+%s+" % (lines1, lines2))
375                self._write("| Table%s | Entries%s |" % (blank1, blank2))
376                self._write("+%s+%s+" % (lines1, lines2))
377
378                sortedCounts = list(counts.keys())
379                sortedCounts.sort(reverse=True)
380
381                for count in sortedCounts:
382                    tables = counts[count]
383
384                    if count is None:
385                        count = "Unknown"
386
387                    tables.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _)
388
389                    for table in tables:
390                        blank1 = " " * (maxlength1 - getConsoleLength(getUnicode(table)))
391                        blank2 = " " * (maxlength2 - len(str(count)))
392                        self._write("| %s%s | %d%s |" % (table, blank1, count, blank2))
393
394                self._write("+%s+%s+\n" % (lines1, lines2))
395        else:
396            logger.error("unable to retrieve the number of entries for any table")
397
398    def dbTableValues(self, tableValues):
399        replication = None
400        rtable = None
401        dumpFP = None
402        appendToFile = False
403        warnFile = False
404
405        if tableValues is None:
406            return
407
408        db = tableValues["__infos__"]["db"]
409        if not db:
410            db = "All"
411        table = tableValues["__infos__"]["table"]
412
413        if conf.api:
414            self._write(tableValues, content_type=CONTENT_TYPE.DUMP_TABLE)
415            return
416
417        dumpDbPath = os.path.join(conf.dumpPath, unsafeSQLIdentificatorNaming(db))
418
419        if conf.dumpFormat == DUMP_FORMAT.SQLITE:
420            replication = Replication(os.path.join(conf.dumpPath, "%s.sqlite3" % unsafeSQLIdentificatorNaming(db)))
421        elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML):
422            if not os.path.isdir(dumpDbPath):
423                try:
424                    os.makedirs(dumpDbPath)
425                except:
426                    warnFile = True
427
428                    _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(db))
429                    dumpDbPath = os.path.join(conf.dumpPath, "%s-%s" % (_, hashlib.md5(getBytes(db)).hexdigest()[:8]))
430
431                    if not os.path.isdir(dumpDbPath):
432                        try:
433                            os.makedirs(dumpDbPath)
434                        except Exception as ex:
435                            tempDir = tempfile.mkdtemp(prefix="sqlmapdb")
436                            warnMsg = "unable to create dump directory "
437                            warnMsg += "'%s' (%s). " % (dumpDbPath, getSafeExString(ex))
438                            warnMsg += "Using temporary directory '%s' instead" % tempDir
439                            logger.warn(warnMsg)
440
441                            dumpDbPath = tempDir
442
443            dumpFileName = os.path.join(dumpDbPath, re.sub(r'[\\/]', UNSAFE_DUMP_FILEPATH_REPLACEMENT, "%s.%s" % (unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower())))
444            if not checkFile(dumpFileName, False):
445                try:
446                    openFile(dumpFileName, "w+b").close()
447                except SqlmapSystemException:
448                    raise
449                except:
450                    warnFile = True
451
452                    _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(table)))
453                    if len(_) < len(table) or IS_WIN and table.upper() in WINDOWS_RESERVED_NAMES:
454                        _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(table))
455                        dumpFileName = os.path.join(dumpDbPath, "%s-%s.%s" % (_, hashlib.md5(getBytes(table)).hexdigest()[:8], conf.dumpFormat.lower()))
456                    else:
457                        dumpFileName = os.path.join(dumpDbPath, "%s.%s" % (_, conf.dumpFormat.lower()))
458            else:
459                appendToFile = any((conf.limitStart, conf.limitStop))
460
461                if not appendToFile:
462                    count = 1
463                    while True:
464                        candidate = "%s.%d" % (dumpFileName, count)
465                        if not checkFile(candidate, False):
466                            try:
467                                shutil.copyfile(dumpFileName, candidate)
468                            except IOError:
469                                pass
470                            break
471                        else:
472                            count += 1
473
474            dumpFP = openFile(dumpFileName, "wb" if not appendToFile else "ab", buffering=DUMP_FILE_BUFFER_SIZE)
475
476        count = int(tableValues["__infos__"]["count"])
477        separator = str()
478        field = 1
479        fields = len(tableValues) - 1
480
481        columns = prioritySortColumns(list(tableValues.keys()))
482
483        if conf.col:
484            cols = conf.col.split(',')
485            columns = sorted(columns, key=lambda _: cols.index(_) if _ in cols else 0)
486
487        for column in columns:
488            if column != "__infos__":
489                info = tableValues[column]
490                lines = "-" * (int(info["length"]) + 2)
491                separator += "+%s" % lines
492
493        separator += "+"
494        self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db else "Current database", unsafeSQLIdentificatorNaming(table)))
495
496        if conf.dumpFormat == DUMP_FORMAT.SQLITE:
497            cols = []
498
499            for column in columns:
500                if column != "__infos__":
501                    colType = Replication.INTEGER
502
503                    for value in tableValues[column]['values']:
504                        try:
505                            if not value or value == " ":  # NULL
506                                continue
507
508                            int(value)
509                        except ValueError:
510                            colType = None
511                            break
512
513                    if colType is None:
514                        colType = Replication.REAL
515
516                        for value in tableValues[column]['values']:
517                            try:
518                                if not value or value == " ":  # NULL
519                                    continue
520
521                                float(value)
522                            except ValueError:
523                                colType = None
524                                break
525
526                    cols.append((unsafeSQLIdentificatorNaming(column), colType if colType else Replication.TEXT))
527
528            rtable = replication.createTable(table, cols)
529        elif conf.dumpFormat == DUMP_FORMAT.HTML:
530            dataToDumpFile(dumpFP, "<!DOCTYPE html>\n<html>\n<head>\n")
531            dataToDumpFile(dumpFP, "<meta http-equiv=\"Content-type\" content=\"text/html;charset=%s\">\n" % UNICODE_ENCODING)
532            dataToDumpFile(dumpFP, "<meta name=\"generator\" content=\"%s\" />\n" % VERSION_STRING)
533            dataToDumpFile(dumpFP, "<title>%s</title>\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table)))
534            dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE)
535            dataToDumpFile(dumpFP, "\n</head>\n<body>\n<table>\n<thead>\n<tr>\n")
536
537        if count == 1:
538            self._write("[1 entry]")
539        else:
540            self._write("[%d entries]" % count)
541
542        self._write(separator)
543
544        for column in columns:
545            if column != "__infos__":
546                info = tableValues[column]
547
548                column = unsafeSQLIdentificatorNaming(column)
549                maxlength = int(info["length"])
550                blank = " " * (maxlength - getConsoleLength(column))
551
552                self._write("| %s%s" % (column, blank), newline=False)
553
554                if not appendToFile:
555                    if conf.dumpFormat == DUMP_FORMAT.CSV:
556                        if field == fields:
557                            dataToDumpFile(dumpFP, "%s" % safeCSValue(column))
558                        else:
559                            dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel))
560                    elif conf.dumpFormat == DUMP_FORMAT.HTML:
561                        dataToDumpFile(dumpFP, "<th>%s</th>" % getUnicode(htmlEscape(column).encode("ascii", "xmlcharrefreplace")))
562
563                field += 1
564
565        if conf.dumpFormat == DUMP_FORMAT.HTML:
566            dataToDumpFile(dumpFP, "\n</tr>\n</thead>\n<tbody>\n")
567
568        self._write("|\n%s" % separator)
569
570        if conf.dumpFormat == DUMP_FORMAT.CSV:
571            dataToDumpFile(dumpFP, "\n" if not appendToFile else "")
572
573        elif conf.dumpFormat == DUMP_FORMAT.SQLITE:
574            rtable.beginTransaction()
575
576        if count > TRIM_STDOUT_DUMP_SIZE:
577            warnMsg = "console output will be trimmed to "
578            warnMsg += "last %d rows due to " % TRIM_STDOUT_DUMP_SIZE
579            warnMsg += "large table size"
580            logger.warning(warnMsg)
581
582        for i in xrange(count):
583            console = (i >= count - TRIM_STDOUT_DUMP_SIZE)
584            field = 1
585            values = []
586
587            if conf.dumpFormat == DUMP_FORMAT.HTML:
588                dataToDumpFile(dumpFP, "<tr>")
589
590            for column in columns:
591                if column != "__infos__":
592                    info = tableValues[column]
593
594                    if len(info["values"]) <= i:
595                        continue
596
597                    if info["values"][i] is None:
598                        value = u''
599                    else:
600                        value = getUnicode(info["values"][i])
601                        value = DUMP_REPLACEMENTS.get(value, value)
602
603                    values.append(value)
604                    maxlength = int(info["length"])
605                    blank = " " * (maxlength - getConsoleLength(value))
606                    self._write("| %s%s" % (value, blank), newline=False, console=console)
607
608                    if len(value) > MIN_BINARY_DISK_DUMP_SIZE and r'\x' in value:
609                        try:
610                            mimetype = getText(magic.from_buffer(value, mime=True))
611                            if any(mimetype.startswith(_) for _ in ("application", "image")):
612                                if not os.path.isdir(dumpDbPath):
613                                    os.makedirs(dumpDbPath)
614
615                                _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(column)))
616                                filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (_, randomInt(8)))
617                                warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath)
618                                logger.warn(warnMsg)
619
620                                with openFile(filepath, "w+b", None) as f:
621                                    _ = safechardecode(value, True)
622                                    f.write(_)
623
624                        except magic.MagicException as ex:
625                            logger.debug(getSafeExString(ex))
626
627                    if conf.dumpFormat == DUMP_FORMAT.CSV:
628                        if field == fields:
629                            dataToDumpFile(dumpFP, "%s" % safeCSValue(value))
630                        else:
631                            dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel))
632                    elif conf.dumpFormat == DUMP_FORMAT.HTML:
633                        dataToDumpFile(dumpFP, "<td>%s</td>" % getUnicode(htmlEscape(value).encode("ascii", "xmlcharrefreplace")))
634
635                    field += 1
636
637            if conf.dumpFormat == DUMP_FORMAT.SQLITE:
638                try:
639                    rtable.insert(values)
640                except SqlmapValueException:
641                    pass
642            elif conf.dumpFormat == DUMP_FORMAT.CSV:
643                dataToDumpFile(dumpFP, "\n")
644            elif conf.dumpFormat == DUMP_FORMAT.HTML:
645                dataToDumpFile(dumpFP, "</tr>\n")
646
647            self._write("|", console=console)
648
649        self._write("%s\n" % separator)
650
651        if conf.dumpFormat == DUMP_FORMAT.SQLITE:
652            rtable.endTransaction()
653            logger.info("table '%s.%s' dumped to sqlite3 database '%s'" % (db, table, replication.dbpath))
654
655        elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML):
656            if conf.dumpFormat == DUMP_FORMAT.HTML:
657                dataToDumpFile(dumpFP, "</tbody>\n</table>\n</body>\n</html>")
658            else:
659                dataToDumpFile(dumpFP, "\n")
660            dumpFP.close()
661
662            msg = "table '%s.%s' dumped to %s file '%s'" % (db, table, conf.dumpFormat, dumpFileName)
663            if not warnFile:
664                logger.info(msg)
665            else:
666                logger.warn(msg)
667
668    def dbColumns(self, dbColumnsDict, colConsider, dbs):
669        if conf.api:
670            self._write(dbColumnsDict, content_type=CONTENT_TYPE.COLUMNS)
671            return
672
673        for column in dbColumnsDict.keys():
674            if colConsider == "1":
675                colConsiderStr = "s LIKE '%s' were" % unsafeSQLIdentificatorNaming(column)
676            else:
677                colConsiderStr = " '%s' was" % unsafeSQLIdentificatorNaming(column)
678
679            found = {}
680            for db, tblData in dbs.items():
681                for tbl, colData in tblData.items():
682                    for col, dataType in colData.items():
683                        if column.lower() in col.lower():
684                            if db in found:
685                                if tbl in found[db]:
686                                    found[db][tbl][col] = dataType
687                                else:
688                                    found[db][tbl] = {col: dataType}
689                            else:
690                                found[db] = {}
691                                found[db][tbl] = {col: dataType}
692
693                            continue
694
695            if found:
696                msg = "column%s found in the " % colConsiderStr
697                msg += "following databases:"
698                self._write(msg)
699
700                self.dbTableColumns(found)
701
702    def sqlQuery(self, query, queryRes):
703        self.string(query, queryRes, content_type=CONTENT_TYPE.SQL_QUERY)
704
705    def rFile(self, fileData):
706        self.lister("files saved to", fileData, sort=False, content_type=CONTENT_TYPE.FILE_READ)
707
708    def registerValue(self, registerData):
709        self.string("Registry key value data", registerData, content_type=CONTENT_TYPE.REG_READ, sort=False)
710
711# object to manage how to print the retrieved queries output to
712# standard output and sessions file
713dumper = Dump()
714