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 glob
9import os
10import re
11import shutil
12import subprocess
13import time
14import zipfile
15
16from lib.core.common import dataToStdout
17from lib.core.common import getLatestRevision
18from lib.core.common import getSafeExString
19from lib.core.common import openFile
20from lib.core.common import pollProcess
21from lib.core.common import readInput
22from lib.core.convert import getText
23from lib.core.data import conf
24from lib.core.data import logger
25from lib.core.data import paths
26from lib.core.revision import getRevisionNumber
27from lib.core.settings import GIT_REPOSITORY
28from lib.core.settings import IS_WIN
29from lib.core.settings import VERSION
30from lib.core.settings import ZIPBALL_PAGE
31from thirdparty.six.moves import urllib as _urllib
32
33def update():
34    if not conf.updateAll:
35        return
36
37    success = False
38
39    if not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")):
40        warnMsg = "not a git repository. It is recommended to clone the 'sqlmapproject/sqlmap' repository "
41        warnMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY
42        logger.warn(warnMsg)
43
44        if VERSION == getLatestRevision():
45            logger.info("already at the latest revision '%s'" % getRevisionNumber())
46            return
47
48        message = "do you want to try to fetch the latest 'zipball' from repository and extract it (experimental) ? [y/N]"
49        if readInput(message, default='N', boolean=True):
50            directory = os.path.abspath(paths.SQLMAP_ROOT_PATH)
51
52            try:
53                open(os.path.join(directory, "sqlmap.py"), "w+b")
54            except Exception as ex:
55                errMsg = "unable to update content of directory '%s' ('%s')" % (directory, getSafeExString(ex))
56                logger.error(errMsg)
57            else:
58                attrs = os.stat(os.path.join(directory, "sqlmap.py")).st_mode
59                for wildcard in ('*', ".*"):
60                    for _ in glob.glob(os.path.join(directory, wildcard)):
61                        try:
62                            if os.path.isdir(_):
63                                shutil.rmtree(_)
64                            else:
65                                os.remove(_)
66                        except:
67                            pass
68
69                if glob.glob(os.path.join(directory, '*')):
70                    errMsg = "unable to clear the content of directory '%s'" % directory
71                    logger.error(errMsg)
72                else:
73                    try:
74                        archive = _urllib.request.urlretrieve(ZIPBALL_PAGE)[0]
75
76                        with zipfile.ZipFile(archive) as f:
77                            for info in f.infolist():
78                                info.filename = re.sub(r"\Asqlmap[^/]+", "", info.filename)
79                                if info.filename:
80                                    f.extract(info, directory)
81
82                        filepath = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py")
83                        if os.path.isfile(filepath):
84                            with openFile(filepath, "rb") as f:
85                                version = re.search(r"(?m)^VERSION\s*=\s*['\"]([^'\"]+)", f.read()).group(1)
86                                logger.info("updated to the latest version '%s#dev'" % version)
87                                success = True
88                    except Exception as ex:
89                        logger.error("update could not be completed ('%s')" % getSafeExString(ex))
90                    else:
91                        if not success:
92                            logger.error("update could not be completed")
93                        else:
94                            try:
95                                os.chmod(os.path.join(directory, "sqlmap.py"), attrs)
96                            except OSError:
97                                logger.warning("could not set the file attributes of '%s'" % os.path.join(directory, "sqlmap.py"))
98    else:
99        infoMsg = "updating sqlmap to the latest development revision from the "
100        infoMsg += "GitHub repository"
101        logger.info(infoMsg)
102
103        debugMsg = "sqlmap will try to update itself using 'git' command"
104        logger.debug(debugMsg)
105
106        dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X"))
107
108        output = ""
109        try:
110            process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH)
111            pollProcess(process, True)
112            output, _ = process.communicate()
113            success = not process.returncode
114        except Exception as ex:
115            success = False
116            output = getSafeExString(ex)
117        finally:
118            output = getText(output)
119
120        if success:
121            logger.info("%s the latest revision '%s'" % ("already at" if "Already" in output else "updated to", getRevisionNumber()))
122        else:
123            if "Not a git repository" in output:
124                errMsg = "not a valid git repository. Please checkout the 'sqlmapproject/sqlmap' repository "
125                errMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY
126                logger.error(errMsg)
127            else:
128                logger.error("update could not be completed ('%s')" % re.sub(r"\W+", " ", output).strip())
129
130    if not success:
131        if IS_WIN:
132            infoMsg = "for Windows platform it's recommended "
133            infoMsg += "to use a GitHub for Windows client for updating "
134            infoMsg += "purposes (http://windows.github.com/) or just "
135            infoMsg += "download the latest snapshot from "
136            infoMsg += "https://github.com/sqlmapproject/sqlmap/downloads"
137        else:
138            infoMsg = "for Linux platform it's recommended "
139            infoMsg += "to install a standard 'git' package (e.g.: 'sudo apt-get install git')"
140
141        logger.info(infoMsg)
142