~ubuntu-branches/ubuntu/vivid/sqlmap/vivid

« back to all changes in this revision

Viewing changes to lib/core/common.py

  • Committer: Bazaar Package Importer
  • Author(s): Bernardo Damele A. G.
  • Date: 2009-02-03 23:30:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090203233000-8gpnwfbih0wnqtv5
Tags: upstream-0.6.4
ImportĀ upstreamĀ versionĀ 0.6.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
"""
 
4
$Id: common.py 355 2009-01-28 14:53:11Z inquisb $
 
5
 
 
6
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
 
7
 
 
8
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
 
9
                        and Daniele Bellucci <daniele.bellucci@gmail.com>
 
10
 
 
11
sqlmap is free software; you can redistribute it and/or modify it under
 
12
the terms of the GNU General Public License as published by the Free
 
13
Software Foundation version 2 of the License.
 
14
 
 
15
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
 
16
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
17
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 
18
details.
 
19
 
 
20
You should have received a copy of the GNU General Public License along
 
21
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
 
22
Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
23
"""
 
24
 
 
25
 
 
26
 
 
27
import os
 
28
import random
 
29
import re
 
30
import string
 
31
import sys
 
32
import time
 
33
import urlparse
 
34
 
 
35
 
 
36
from lib.core.convert import urldecode
 
37
from lib.core.data import conf
 
38
from lib.core.data import kb
 
39
from lib.core.data import logger
 
40
from lib.core.data import temp
 
41
from lib.core.exception import sqlmapFilePathException
 
42
from lib.core.data import paths
 
43
from lib.core.settings import SQL_STATEMENTS
 
44
from lib.core.settings import VERSION_STRING
 
45
 
 
46
 
 
47
def paramToDict(place, parameters=None):
 
48
    """
 
49
    Split the parameters into names and values, check if these parameters
 
50
    are within the testable parameters and return in a dictionary.
 
51
 
 
52
    @param place: where sqlmap has to work, can be GET, POST or Cookie.
 
53
    @type place: C{str}
 
54
 
 
55
    @param parameters: parameters string in the format for instance
 
56
    'p1=v1&p2=v2' (GET and POST) or 'p1=v1;p2=v2' (Cookie).
 
57
    @type parameters: C{str}
 
58
 
 
59
    @return: the parameters in a dictionary.
 
60
    @rtype: C{str}
 
61
    """
 
62
 
 
63
    testableParameters = {}
 
64
 
 
65
    if conf.parameters.has_key(place) and not parameters:
 
66
        parameters = conf.parameters[place]
 
67
 
 
68
    parameters = parameters.replace(", ", ",")
 
69
 
 
70
    if place == "Cookie":
 
71
        splitParams = parameters.split(";")
 
72
    else:
 
73
        splitParams = parameters.split("&")
 
74
 
 
75
    for element in splitParams:
 
76
        elem = element.split("=")
 
77
 
 
78
        if len(elem) == 2:
 
79
            parameter = elem[0].replace(" ", "")
 
80
 
 
81
            condition  = not conf.testParameter
 
82
            condition |= parameter in conf.testParameter
 
83
 
 
84
            if condition:
 
85
                value = elem[1]
 
86
                if value:
 
87
                    testableParameters[parameter] = value
 
88
 
 
89
    if conf.testParameter and not testableParameters:
 
90
        paramStr = ", ".join(test for test in conf.testParameter)
 
91
 
 
92
        if len(conf.testParameter) > 1:
 
93
            warnMsg  = "the testable parameters '%s' " % paramStr
 
94
            warnMsg += "you provided are not into the %s" % place
 
95
        else:
 
96
            parameter = conf.testParameter[0]
 
97
 
 
98
            warnMsg  = "the testable parameter '%s' " % paramStr
 
99
            warnMsg += "you provided is not into the %s" % place
 
100
 
 
101
        if conf.multipleTargets:
 
102
            warnMsg += ", skipping to next url"
 
103
 
 
104
        logger.warn(warnMsg)
 
105
 
 
106
    elif len(conf.testParameter) != len(testableParameters.keys()):
 
107
        for parameter in conf.testParameter:
 
108
            if not testableParameters.has_key(parameter):
 
109
                warnMsg  =  "the testable parameter '%s' " % parameter
 
110
                warnMsg += "you provided is not into the %s" % place
 
111
                logger.warn(warnMsg)
 
112
 
 
113
    return testableParameters
 
114
 
 
115
 
 
116
def formatDBMSfp(versions=None):
 
117
    """
 
118
    This function format the back-end DBMS fingerprint value and return its
 
119
    values formatted as a human readable string.
 
120
 
 
121
    @return: detected back-end DBMS based upon fingerprint techniques.
 
122
    @rtype: C{str}
 
123
    """
 
124
 
 
125
    if not versions:
 
126
        versions = kb.dbmsVersion
 
127
 
 
128
    if isinstance(versions, str):
 
129
        return "%s %s" % (kb.dbms, versions)
 
130
    elif isinstance(versions, (list, set, tuple)):
 
131
        return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
 
132
    elif not versions:
 
133
        warnMsg  = "unable to extensively fingerprint the back-end "
 
134
        warnMsg += "DBMS version"
 
135
        logger.warn(warnMsg)
 
136
 
 
137
        return kb.dbms
 
138
 
 
139
 
 
140
def __formatFingerprintString(values, chain=" or "):
 
141
    string = "|".join([v for v in values])
 
142
    return string.replace("|", chain)
 
143
 
 
144
 
 
145
def formatFingerprint(target, info):
 
146
    """
 
147
    This function format the back-end operating system fingerprint value
 
148
    and return its values formatted as a human readable string.
 
149
 
 
150
    Example of info (kb.headersFp) dictionary:
 
151
 
 
152
    {
 
153
      'distrib': set(['Ubuntu']),
 
154
      'type': set(['Linux']),
 
155
      'technology': set(['PHP 5.2.6', 'Apache 2.2.9']),
 
156
      'release': set(['8.10'])
 
157
    }
 
158
 
 
159
    Example of info (kb.bannerFp) dictionary:
 
160
 
 
161
    {
 
162
      'sp': set(['Service Pack 4']),
 
163
      'dbmsVersion': '8.00.194',
 
164
      'dbmsServicePack': '0',
 
165
      'distrib': set(['2000']),
 
166
      'dbmsRelease': '2000',
 
167
      'type': set(['Windows'])
 
168
    }
 
169
 
 
170
    @return: detected back-end operating system based upon fingerprint
 
171
    techniques.
 
172
    @rtype: C{str}
 
173
    """
 
174
 
 
175
    infoStr = ""
 
176
 
 
177
    if info and "type" in info:
 
178
        infoStr += "%s operating system: %s" % (target, __formatFingerprintString(info["type"]))
 
179
 
 
180
        if "distrib" in info:
 
181
            infoStr += " %s" % __formatFingerprintString(info["distrib"])
 
182
 
 
183
        if "release" in info:
 
184
            infoStr += " %s" % __formatFingerprintString(info["release"])
 
185
 
 
186
        if "sp" in info:
 
187
            infoStr += " %s" % __formatFingerprintString(info["sp"])
 
188
 
 
189
        if "codename" in info:
 
190
            infoStr += " (%s)" % __formatFingerprintString(info["codename"])
 
191
 
 
192
    if "technology" in info:
 
193
        infoStr += "\nweb application technology: %s" % __formatFingerprintString(info["technology"], ", ")
 
194
 
 
195
    return infoStr
 
196
 
 
197
 
 
198
def getHtmlErrorFp():
 
199
    """
 
200
    This function parses the knowledge base htmlFp list and return its
 
201
    values formatted as a human readable string.
 
202
 
 
203
    @return: list of possible back-end DBMS based upon error messages
 
204
    parsing.
 
205
    @rtype: C{str}
 
206
    """
 
207
 
 
208
    htmlParsed = ""
 
209
 
 
210
    if not kb.htmlFp:
 
211
        return None
 
212
 
 
213
    if len(kb.htmlFp) == 1:
 
214
        htmlVer = kb.htmlFp[0]
 
215
        htmlParsed = htmlVer
 
216
    elif len(kb.htmlFp) > 1:
 
217
        htmlParsed = " or ".join([htmlFp for htmlFp in kb.htmlFp])
 
218
 
 
219
    return htmlParsed
 
220
 
 
221
 
 
222
def getDocRoot():
 
223
    """
 
224
    This method returns the web application document root based on the
 
225
    detected absolute files paths in the knowledge base.
 
226
    """
 
227
 
 
228
    docRoot = None
 
229
 
 
230
    if kb.absFilePaths:
 
231
        logMsg  = "retrieved the possible injectable "
 
232
        logMsg += "file absolute system paths: "
 
233
        logMsg += "'%s'" % ", ".join(path for path in kb.absFilePaths)
 
234
        logger.info(logMsg)
 
235
    else:
 
236
        warnMsg  = "unable to retrieve the injectable file "
 
237
        warnMsg += "absolute system path"
 
238
        logger.warn(warnMsg)
 
239
 
 
240
    for absFilePath in kb.absFilePaths:
 
241
        if conf.path in absFilePath:
 
242
            index = absFilePath.index(conf.path)
 
243
            docRoot = absFilePath[:index]
 
244
            break
 
245
 
 
246
    if docRoot:
 
247
        logMsg  = "retrieved the remote web server "
 
248
        logMsg += "document root: '%s'" % docRoot
 
249
        logger.info(logMsg)
 
250
    else:
 
251
        warnMsg  = "unable to retrieve the remote web server "
 
252
        warnMsg += "document root"
 
253
        logger.warn(warnMsg)
 
254
 
 
255
    return docRoot
 
256
 
 
257
 
 
258
def getDirectories():
 
259
    """
 
260
    This method calls a function that returns the web application document
 
261
    root and injectable file absolute system path.
 
262
 
 
263
    @return: a set of paths (document root and absolute system path).
 
264
    @rtype: C{set}
 
265
    @todo: replace this function with a site crawling functionality.
 
266
    """
 
267
 
 
268
    directories = set()
 
269
 
 
270
    kb.docRoot = getDocRoot()
 
271
 
 
272
    if kb.docRoot:
 
273
        directories.add(kb.docRoot)
 
274
 
 
275
    pagePath = re.search("^/(.*)/", conf.path)
 
276
 
 
277
    if kb.docRoot and pagePath:
 
278
        pagePath = pagePath.groups()[0]
 
279
 
 
280
        directories.add("%s/%s" % (kb.docRoot, pagePath))
 
281
 
 
282
    return directories
 
283
 
 
284
 
 
285
def filePathToString(filePath):
 
286
    string = filePath.replace("/", "_").replace("\\", "_")
 
287
    string = string.replace(" ", "_").replace(":", "_")
 
288
 
 
289
    return string
 
290
 
 
291
 
 
292
def dataToStdout(data):
 
293
    sys.stdout.write(data)
 
294
    sys.stdout.flush()
 
295
 
 
296
 
 
297
def dataToSessionFile(data):
 
298
    if not conf.sessionFile:
 
299
        return
 
300
 
 
301
    conf.sessionFP.write(data)
 
302
    conf.sessionFP.flush()
 
303
 
 
304
 
 
305
def dataToDumpFile(dumpFile, data):
 
306
    dumpFile.write(data)
 
307
    dumpFile.flush()
 
308
 
 
309
 
 
310
def strToHex(string):
 
311
    """
 
312
    @param string: string to be converted into its hexadecimal value.
 
313
    @type string: C{str}
 
314
 
 
315
    @return: the hexadecimal converted string.
 
316
    @rtype: C{str}
 
317
    """
 
318
 
 
319
    hexStr = ""
 
320
 
 
321
    for character in string:
 
322
        if character == "\n":
 
323
            character = " "
 
324
 
 
325
        hexChar = "%2x" % ord(character)
 
326
        hexChar = hexChar.replace(" ", "0")
 
327
        hexChar = hexChar.upper()
 
328
 
 
329
        hexStr += hexChar
 
330
 
 
331
    return hexStr
 
332
 
 
333
 
 
334
def fileToStr(fileName):
 
335
    """
 
336
    @param fileName: file path to read the content and return as a no
 
337
    NEWLINE string.
 
338
    @type fileName: C{file.open}
 
339
 
 
340
    @return: the file content as a string without TAB and NEWLINE.
 
341
    @rtype: C{str}
 
342
    """
 
343
 
 
344
    filePointer = open(fileName, "r")
 
345
    fileText = filePointer.read()
 
346
 
 
347
    fileText = fileText.replace("    ", "")
 
348
    fileText = fileText.replace("\t", "")
 
349
    fileText = fileText.replace("\r", "")
 
350
    fileText = fileText.replace("\n", " ")
 
351
 
 
352
    return fileText
 
353
 
 
354
 
 
355
def fileToHex(fileName):
 
356
    """
 
357
    @param fileName: file path to read the content and return as an
 
358
    hexadecimal string.
 
359
    @type fileName: C{file.open}
 
360
 
 
361
    @return: the file content as an hexadecimal string.
 
362
    @rtype: C{str}
 
363
    """
 
364
 
 
365
    fileText = fileToStr(fileName)
 
366
    hexFile = strToHex(fileText)
 
367
 
 
368
    return hexFile
 
369
 
 
370
 
 
371
def readInput(message, default=None):
 
372
    """
 
373
    @param message: message to display on terminal.
 
374
    @type message: C{str}
 
375
 
 
376
    @return: a string read from keyboard as input.
 
377
    @rtype: C{str}
 
378
    """
 
379
 
 
380
    if conf.batch and default:
 
381
        infoMsg = "%s%s" % (message, str(default))
 
382
        logger.info(infoMsg)
 
383
 
 
384
        debugMsg = "used the default behaviour, running in batch mode"
 
385
        logger.debug(debugMsg)
 
386
 
 
387
        data = default
 
388
    else:
 
389
        data = raw_input("[%s] [INPUT] %s" % (time.strftime("%X"), message))
 
390
 
 
391
    return data
 
392
 
 
393
 
 
394
def randomRange(start=0, stop=1000):
 
395
    """
 
396
    @param start: starting number.
 
397
    @type start: C{int}
 
398
 
 
399
    @param stop: last number.
 
400
    @type stop: C{int}
 
401
 
 
402
    @return: a random number within the range.
 
403
    @rtype: C{int}
 
404
    """
 
405
 
 
406
    return int(random.randint(start, stop))
 
407
 
 
408
 
 
409
def randomInt(length=4):
 
410
    """
 
411
    @param length: length of the random string.
 
412
    @type length: C{int}
 
413
 
 
414
    @return: a random string of digits.
 
415
    @rtype: C{str}
 
416
    """
 
417
 
 
418
    return int("".join([random.choice(string.digits) for _ in xrange(0, length)]))
 
419
 
 
420
 
 
421
def randomStr(length=5):
 
422
    """
 
423
    @param length: length of the random string.
 
424
    @type length: C{int}
 
425
 
 
426
    @return: a random string of characters.
 
427
    @rtype: C{str}
 
428
    """
 
429
 
 
430
    return "".join([random.choice(string.letters) for _ in xrange(0, length)])
 
431
 
 
432
 
 
433
def sanitizeStr(string):
 
434
    """
 
435
    @param string: string to sanitize: cast to str datatype and replace
 
436
    newlines with one space and strip carriage returns.
 
437
    @type string: C{str}
 
438
 
 
439
    @return: sanitized string
 
440
    @rtype: C{str}
 
441
    """
 
442
 
 
443
    cleanString = str(string)
 
444
    cleanString = cleanString.replace("\n", " ").replace("\r", "")
 
445
 
 
446
    return cleanString
 
447
 
 
448
 
 
449
def checkFile(filename):
 
450
    """
 
451
    @param filename: filename to check if it exists.
 
452
    @type filename: C{str}
 
453
    """
 
454
 
 
455
    if not os.path.exists(filename):
 
456
        raise sqlmapFilePathException, "unable to read file '%s'" % filename
 
457
 
 
458
 
 
459
def replaceNewlineTabs(string):
 
460
    replacedString = string.replace("\n", "__NEWLINE__").replace("\t", "__TAB__")
 
461
    replacedString = replacedString.replace(temp.delimiter, "__DEL__")
 
462
 
 
463
    return replacedString
 
464
 
 
465
 
 
466
def banner():
 
467
    """
 
468
    This function prints sqlmap banner with its version
 
469
    """
 
470
 
 
471
    print """
 
472
    %s coded by Bernardo Damele A. G. <bernardo.damele@gmail.com>
 
473
                      and Daniele Bellucci <daniele.bellucci@gmail.com>
 
474
    """ % VERSION_STRING
 
475
 
 
476
 
 
477
def parsePasswordHash(password):
 
478
    blank = " " * 8
 
479
 
 
480
    if not password or password == " ":
 
481
        password = "NULL"
 
482
 
 
483
    if kb.dbms == "Microsoft SQL Server" and password != "NULL":
 
484
        hexPassword = password
 
485
        password  = "%s\n" % hexPassword
 
486
        password += "%sheader: %s\n" % (blank, hexPassword[:6])
 
487
        password += "%ssalt: %s\n" % (blank, hexPassword[6:14])
 
488
        password += "%smixedcase: %s\n" % (blank, hexPassword[14:54])
 
489
 
 
490
        if kb.dbmsVersion[0] not in ( "2005", "2008" ):
 
491
            password += "%suppercase: %s" % (blank, hexPassword[54:])
 
492
 
 
493
    return password
 
494
 
 
495
 
 
496
def cleanQuery(query):
 
497
    upperQuery = query
 
498
 
 
499
    for sqlStatements in SQL_STATEMENTS.values():
 
500
        for sqlStatement in sqlStatements:
 
501
            sqlStatementEsc = sqlStatement.replace("(", "\\(")
 
502
            queryMatch = re.search("(%s)" % sqlStatementEsc, query, re.I)
 
503
 
 
504
            if queryMatch:
 
505
                upperQuery = upperQuery.replace(queryMatch.group(1), sqlStatement.upper())
 
506
 
 
507
    return upperQuery
 
508
 
 
509
 
 
510
def setPaths():
 
511
    # sqlmap paths
 
512
    paths.SQLMAP_SHELL_PATH      = "%s/shell" % paths.SQLMAP_ROOT_PATH
 
513
    paths.SQLMAP_TXT_PATH        = "%s/txt" % paths.SQLMAP_ROOT_PATH
 
514
    paths.SQLMAP_XML_PATH        = "%s/xml" % paths.SQLMAP_ROOT_PATH
 
515
    paths.SQLMAP_XML_BANNER_PATH = "%s/banner" % paths.SQLMAP_XML_PATH
 
516
    paths.SQLMAP_OUTPUT_PATH     = "%s/output" % paths.SQLMAP_ROOT_PATH
 
517
    paths.SQLMAP_DUMP_PATH       = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
 
518
    paths.SQLMAP_FILES_PATH      = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
 
519
 
 
520
    # sqlmap files
 
521
    paths.SQLMAP_HISTORY         = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
 
522
    paths.SQLMAP_CONFIG          = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
 
523
    paths.FUZZ_VECTORS           = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
 
524
    paths.ERRORS_XML             = "%s/errors.xml" % paths.SQLMAP_XML_PATH
 
525
    paths.QUERIES_XML            = "%s/queries.xml" % paths.SQLMAP_XML_PATH
 
526
    paths.GENERIC_XML            = "%s/generic.xml" % paths.SQLMAP_XML_BANNER_PATH
 
527
    paths.MSSQL_XML              = "%s/mssql.xml" % paths.SQLMAP_XML_BANNER_PATH
 
528
    paths.MYSQL_XML              = "%s/mysql.xml" % paths.SQLMAP_XML_BANNER_PATH
 
529
    paths.ORACLE_XML             = "%s/oracle.xml" % paths.SQLMAP_XML_BANNER_PATH
 
530
    paths.PGSQL_XML              = "%s/postgresql.xml" % paths.SQLMAP_XML_BANNER_PATH
 
531
 
 
532
 
 
533
def weAreFrozen():
 
534
    """
 
535
    Returns whether we are frozen via py2exe.
 
536
    This will affect how we find out where we are located.
 
537
    Reference: http://www.py2exe.org/index.cgi/WhereAmI
 
538
    """
 
539
 
 
540
    return hasattr(sys, "frozen")
 
541
 
 
542
 
 
543
def parseTargetUrl():
 
544
    """
 
545
    Parse target url and set some attributes into the configuration
 
546
    singleton.
 
547
    """
 
548
 
 
549
    if not conf.url:
 
550
        return
 
551
 
 
552
    if not re.search("^http[s]*://", conf.url):
 
553
        if ":443/" in conf.url:
 
554
            conf.url = "https://" + conf.url
 
555
        else:
 
556
            conf.url = "http://" + conf.url
 
557
 
 
558
    __urlSplit     = urlparse.urlsplit(conf.url)
 
559
    __hostnamePort = __urlSplit[1].split(":")
 
560
 
 
561
    conf.scheme    = __urlSplit[0]
 
562
    conf.path      = __urlSplit[2]
 
563
    conf.hostname  = __hostnamePort[0]
 
564
 
 
565
    if len(__hostnamePort) == 2:
 
566
        conf.port = int(__hostnamePort[1])
 
567
    elif conf.scheme == "https":
 
568
        conf.port = 443
 
569
    else:
 
570
        conf.port = 80
 
571
 
 
572
    if __urlSplit[3]:
 
573
        conf.parameters["GET"] = urldecode(__urlSplit[3]).replace("%", "%%")
 
574
 
 
575
    conf.url = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, conf.path)
 
576
 
 
577
 
 
578
def expandAsteriskForColumns(expression):
 
579
    # If the user provided an asterisk rather than the column(s)
 
580
    # name, sqlmap will retrieve the columns itself and reprocess
 
581
    # the SQL query string (expression)
 
582
    asterisk = re.search("^SELECT\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I)
 
583
 
 
584
    if asterisk:
 
585
        infoMsg  = "you did not provide the fields in your query. "
 
586
        infoMsg += "sqlmap will retrieve the column names itself"
 
587
        logger.info(infoMsg)
 
588
 
 
589
        dbTbl = asterisk.group(1)
 
590
 
 
591
        if dbTbl and "." in dbTbl:
 
592
            conf.db, conf.tbl = dbTbl.split(".")
 
593
        else:
 
594
            conf.tbl = dbTbl
 
595
 
 
596
        columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
 
597
 
 
598
        if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
 
599
            columns = columnsDict[conf.db][conf.tbl].keys()
 
600
            columns.sort()
 
601
            columnsStr = ", ".join([column for column in columns])
 
602
            expression = expression.replace("*", columnsStr, 1)
 
603
 
 
604
            infoMsg  = "the query with column names is: "
 
605
            infoMsg += "%s" % expression
 
606
            logger.info(infoMsg)
 
607
 
 
608
    return expression
 
609
 
 
610
 
 
611
def getRange(count, dump=False, plusOne=False):
 
612
    count      = int(count)
 
613
    indexRange = None
 
614
    limitStart = 1
 
615
    limitStop  = count
 
616
 
 
617
    if dump:
 
618
        if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop:
 
619
            limitStop = conf.limitStop
 
620
 
 
621
        if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
 
622
            limitStart = conf.limitStart
 
623
 
 
624
    if kb.dbms == "Oracle" or plusOne == True:
 
625
        indexRange = range(limitStart, limitStop + 1)
 
626
    else:
 
627
        indexRange = range(limitStart - 1, limitStop)
 
628
 
 
629
    return indexRange
 
630
 
 
631
 
 
632
def parseUnionPage(output, expression, partial=False, condition=None):
 
633
    data = []
 
634
 
 
635
    outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) )
 
636
    outCond2 = ( output.startswith("__START__") and output.endswith("__STOP__") )
 
637
 
 
638
    if outCond1 or outCond2:
 
639
        if outCond1:
 
640
            regExpr = '%s(.*?)%s' % (temp.start, temp.stop)
 
641
        elif outCond2:
 
642
            regExpr = '__START__(.*?)__STOP__'
 
643
 
 
644
        output = re.findall(regExpr, output, re.S)
 
645
 
 
646
        if condition == None:
 
647
            condition = (
 
648
                          kb.resumedQueries and conf.url in kb.resumedQueries.keys()
 
649
                          and expression in kb.resumedQueries[conf.url].keys()
 
650
                        )
 
651
 
 
652
        if partial or not condition:
 
653
            logOutput = "".join(["__START__%s__STOP__" % replaceNewlineTabs(value) for value in output])
 
654
            dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, logOutput))
 
655
 
 
656
        output = set(output)
 
657
 
 
658
        for entry in output:
 
659
            info = []
 
660
 
 
661
            if "__DEL__" in entry:
 
662
                entry = entry.split("__DEL__")
 
663
            else:
 
664
                entry = entry.split(temp.delimiter)
 
665
 
 
666
            if len(entry) == 1:
 
667
                data.append(entry[0])
 
668
            else:
 
669
                for value in entry:
 
670
                    info.append(value)
 
671
 
 
672
                data.append(info)
 
673
    else:
 
674
        data = output
 
675
 
 
676
    if len(data) == 1 and isinstance(data[0], str):
 
677
        data = data[0]
 
678
 
 
679
    return data