We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
IP to City Database
I think we can provide an IP to city database now instead of just showing country. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `CTFd/utils/initialization/__init__.py`
Content:
```
1 import datetime
2 import logging
3 import os
4 import sys
5 
6 from flask import abort, redirect, render_template, request, session, url_for
7 from sqlalchemy.exc import IntegrityError, InvalidRequestError
8 from werkzeug.middleware.dispatcher import DispatcherMiddleware
9 
10 from CTFd.cache import clear_user_recent_ips
11 from CTFd.exceptions import UserNotFoundException, UserTokenExpiredException
12 from CTFd.models import Tracking, db
13 from CTFd.utils import config, get_config, markdown
14 from CTFd.utils.config import (
15     can_send_mail,
16     ctf_logo,
17     ctf_name,
18     ctf_theme,
19     integrations,
20     is_setup,
21 )
22 from CTFd.utils.config.pages import get_pages
23 from CTFd.utils.dates import isoformat, unix_time, unix_time_millis
24 from CTFd.utils.events import EventManager, RedisEventManager
25 from CTFd.utils.humanize.words import pluralize
26 from CTFd.utils.modes import generate_account_url, get_mode_as_word
27 from CTFd.utils.plugins import (
28     get_configurable_plugins,
29     get_registered_admin_scripts,
30     get_registered_admin_stylesheets,
31     get_registered_scripts,
32     get_registered_stylesheets,
33 )
34 from CTFd.utils.security.auth import login_user, logout_user, lookup_user_token
35 from CTFd.utils.security.csrf import generate_nonce
36 from CTFd.utils.user import (
37     authed,
38     get_current_team_attrs,
39     get_current_user_attrs,
40     get_current_user_recent_ips,
41     get_ip,
42     is_admin,
43 )
44 
45 
46 def init_template_filters(app):
47     app.jinja_env.filters["markdown"] = markdown
48     app.jinja_env.filters["unix_time"] = unix_time
49     app.jinja_env.filters["unix_time_millis"] = unix_time_millis
50     app.jinja_env.filters["isoformat"] = isoformat
51     app.jinja_env.filters["pluralize"] = pluralize
52 
53 
54 def init_template_globals(app):
55     from CTFd.constants import JINJA_ENUMS
56     from CTFd.constants.config import Configs
57     from CTFd.constants.plugins import Plugins
58     from CTFd.constants.sessions import Session
59     from CTFd.constants.static import Static
60     from CTFd.constants.users import User
61     from CTFd.constants.teams import Team
62     from CTFd.forms import Forms
63     from CTFd.utils.config.visibility import (
64         accounts_visible,
65         challenges_visible,
66         registration_visible,
67         scores_visible,
68     )
69     from CTFd.utils.countries import get_countries, lookup_country_code
70     from CTFd.utils.countries.geoip import lookup_ip_address
71 
72     app.jinja_env.globals.update(config=config)
73     app.jinja_env.globals.update(get_pages=get_pages)
74     app.jinja_env.globals.update(can_send_mail=can_send_mail)
75     app.jinja_env.globals.update(get_ctf_name=ctf_name)
76     app.jinja_env.globals.update(get_ctf_logo=ctf_logo)
77     app.jinja_env.globals.update(get_ctf_theme=ctf_theme)
78     app.jinja_env.globals.update(get_configurable_plugins=get_configurable_plugins)
79     app.jinja_env.globals.update(get_registered_scripts=get_registered_scripts)
80     app.jinja_env.globals.update(get_registered_stylesheets=get_registered_stylesheets)
81     app.jinja_env.globals.update(
82         get_registered_admin_scripts=get_registered_admin_scripts
83     )
84     app.jinja_env.globals.update(
85         get_registered_admin_stylesheets=get_registered_admin_stylesheets
86     )
87     app.jinja_env.globals.update(get_config=get_config)
88     app.jinja_env.globals.update(generate_account_url=generate_account_url)
89     app.jinja_env.globals.update(get_countries=get_countries)
90     app.jinja_env.globals.update(lookup_country_code=lookup_country_code)
91     app.jinja_env.globals.update(lookup_ip_address=lookup_ip_address)
92     app.jinja_env.globals.update(accounts_visible=accounts_visible)
93     app.jinja_env.globals.update(challenges_visible=challenges_visible)
94     app.jinja_env.globals.update(registration_visible=registration_visible)
95     app.jinja_env.globals.update(scores_visible=scores_visible)
96     app.jinja_env.globals.update(get_mode_as_word=get_mode_as_word)
97     app.jinja_env.globals.update(integrations=integrations)
98     app.jinja_env.globals.update(authed=authed)
99     app.jinja_env.globals.update(is_admin=is_admin)
100     app.jinja_env.globals.update(get_current_user_attrs=get_current_user_attrs)
101     app.jinja_env.globals.update(get_current_team_attrs=get_current_team_attrs)
102     app.jinja_env.globals.update(get_ip=get_ip)
103     app.jinja_env.globals.update(Configs=Configs)
104     app.jinja_env.globals.update(Plugins=Plugins)
105     app.jinja_env.globals.update(Session=Session)
106     app.jinja_env.globals.update(Static=Static)
107     app.jinja_env.globals.update(Forms=Forms)
108     app.jinja_env.globals.update(User=User)
109     app.jinja_env.globals.update(Team=Team)
110 
111     # Add in JinjaEnums
112     # The reason this exists is that on double import, JinjaEnums are not reinitialized
113     # Thus, if you try to create two jinja envs (e.g. during testing), sometimes
114     # an Enum will not be available to Jinja.
115     # Instead we can just directly grab them from the persisted global dictionary.
116     for k, v in JINJA_ENUMS.items():
117         # .update() can't be used here because it would use the literal value k
118         app.jinja_env.globals[k] = v
119 
120 
121 def init_logs(app):
122     logger_submissions = logging.getLogger("submissions")
123     logger_logins = logging.getLogger("logins")
124     logger_registrations = logging.getLogger("registrations")
125 
126     logger_submissions.setLevel(logging.INFO)
127     logger_logins.setLevel(logging.INFO)
128     logger_registrations.setLevel(logging.INFO)
129 
130     log_dir = app.config["LOG_FOLDER"]
131     if not os.path.exists(log_dir):
132         os.makedirs(log_dir)
133 
134     logs = {
135         "submissions": os.path.join(log_dir, "submissions.log"),
136         "logins": os.path.join(log_dir, "logins.log"),
137         "registrations": os.path.join(log_dir, "registrations.log"),
138     }
139 
140     try:
141         for log in logs.values():
142             if not os.path.exists(log):
143                 open(log, "a").close()
144 
145         submission_log = logging.handlers.RotatingFileHandler(
146             logs["submissions"], maxBytes=10485760, backupCount=5
147         )
148         login_log = logging.handlers.RotatingFileHandler(
149             logs["logins"], maxBytes=10485760, backupCount=5
150         )
151         registration_log = logging.handlers.RotatingFileHandler(
152             logs["registrations"], maxBytes=10485760, backupCount=5
153         )
154 
155         logger_submissions.addHandler(submission_log)
156         logger_logins.addHandler(login_log)
157         logger_registrations.addHandler(registration_log)
158     except IOError:
159         pass
160 
161     stdout = logging.StreamHandler(stream=sys.stdout)
162 
163     logger_submissions.addHandler(stdout)
164     logger_logins.addHandler(stdout)
165     logger_registrations.addHandler(stdout)
166 
167     logger_submissions.propagate = 0
168     logger_logins.propagate = 0
169     logger_registrations.propagate = 0
170 
171 
172 def init_events(app):
173     if app.config.get("CACHE_TYPE") == "redis":
174         app.events_manager = RedisEventManager()
175     elif app.config.get("CACHE_TYPE") == "filesystem":
176         app.events_manager = EventManager()
177     else:
178         app.events_manager = EventManager()
179     app.events_manager.listen()
180 
181 
182 def init_request_processors(app):
183     @app.url_defaults
184     def inject_theme(endpoint, values):
185         if "theme" not in values and app.url_map.is_endpoint_expecting(
186             endpoint, "theme"
187         ):
188             values["theme"] = ctf_theme()
189 
190     @app.before_request
191     def needs_setup():
192         if is_setup() is False:
193             if request.endpoint in (
194                 "views.setup",
195                 "views.integrations",
196                 "views.themes",
197                 "views.files",
198             ):
199                 return
200             else:
201                 return redirect(url_for("views.setup"))
202 
203     @app.before_request
204     def tracker():
205         if request.endpoint == "views.themes":
206             return
207 
208         if authed():
209             user_ips = get_current_user_recent_ips()
210             ip = get_ip()
211 
212             track = None
213             if (ip not in user_ips) or (request.method != "GET"):
214                 track = Tracking.query.filter_by(
215                     ip=get_ip(), user_id=session["id"]
216                 ).first()
217 
218                 if track:
219                     track.date = datetime.datetime.utcnow()
220                 else:
221                     track = Tracking(ip=get_ip(), user_id=session["id"])
222                     db.session.add(track)
223 
224             if track:
225                 try:
226                     db.session.commit()
227                 except (InvalidRequestError, IntegrityError):
228                     db.session.rollback()
229                     db.session.close()
230                     logout_user()
231                 else:
232                     clear_user_recent_ips(user_id=session["id"])
233 
234     @app.before_request
235     def banned():
236         if request.endpoint == "views.themes":
237             return
238 
239         if authed():
240             user = get_current_user_attrs()
241             team = get_current_team_attrs()
242 
243             if user and user.banned:
244                 return (
245                     render_template(
246                         "errors/403.html", error="You have been banned from this CTF"
247                     ),
248                     403,
249                 )
250 
251             if team and team.banned:
252                 return (
253                     render_template(
254                         "errors/403.html",
255                         error="Your team has been banned from this CTF",
256                     ),
257                     403,
258                 )
259 
260     @app.before_request
261     def tokens():
262         token = request.headers.get("Authorization")
263         if token and request.content_type == "application/json":
264             try:
265                 token_type, token = token.split(" ", 1)
266                 user = lookup_user_token(token)
267             except UserNotFoundException:
268                 abort(401)
269             except UserTokenExpiredException:
270                 abort(401, description="Your access token has expired")
271             except Exception:
272                 abort(401)
273             else:
274                 login_user(user)
275 
276     @app.before_request
277     def csrf():
278         try:
279             func = app.view_functions[request.endpoint]
280         except KeyError:
281             abort(404)
282         if hasattr(func, "_bypass_csrf"):
283             return
284         if request.headers.get("Authorization"):
285             return
286         if not session.get("nonce"):
287             session["nonce"] = generate_nonce()
288         if request.method not in ("GET", "HEAD", "OPTIONS", "TRACE"):
289             if request.content_type == "application/json":
290                 if session["nonce"] != request.headers.get("CSRF-Token"):
291                     abort(403)
292             if request.content_type != "application/json":
293                 if session["nonce"] != request.form.get("nonce"):
294                     abort(403)
295 
296     application_root = app.config.get("APPLICATION_ROOT")
297     if application_root != "/":
298 
299         @app.before_request
300         def force_subdirectory_redirect():
301             if request.path.startswith(application_root) is False:
302                 return redirect(
303                     application_root + request.script_root + request.full_path
304                 )
305 
306         app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {application_root: app})
307
```

Path: `CTFd/utils/countries/geoip.py`
Content:
```
1 import geoacumen
2 import maxminddb
3 from flask import current_app
4 
5 IP_ADDR_LOOKUP = maxminddb.open_database(
6     current_app.config.get("GEOIP_DATABASE_PATH", geoacumen.db_path)
7 )
8 
9 
10 def lookup_ip_address(addr):
11     try:
12         response = IP_ADDR_LOOKUP.get(addr)
13         return response["country"]["iso_code"]
14     except (KeyError, ValueError):
15         return None
16
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/CTFd/utils/countries/geoip.py b/CTFd/utils/countries/geoip.py
--- a/CTFd/utils/countries/geoip.py
+++ b/CTFd/utils/countries/geoip.py
@@ -1,9 +1,9 @@
-import geoacumen
+import geoacumen_city
 import maxminddb
 from flask import current_app
 
 IP_ADDR_LOOKUP = maxminddb.open_database(
-    current_app.config.get("GEOIP_DATABASE_PATH", geoacumen.db_path)
+    current_app.config.get("GEOIP_DATABASE_PATH", geoacumen_city.db_path)
 )
 
 
@@ -11,5 +11,13 @@
     try:
         response = IP_ADDR_LOOKUP.get(addr)
         return response["country"]["iso_code"]
-    except (KeyError, ValueError):
+    except (KeyError, ValueError, TypeError):
+        return None
+
+
+def lookup_ip_address_city(addr):
+    try:
+        response = IP_ADDR_LOOKUP.get(addr)
+        return response["city"]["names"]["en"]
+    except (KeyError, ValueError, TypeError):
         return None
diff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py
--- a/CTFd/utils/initialization/__init__.py
+++ b/CTFd/utils/initialization/__init__.py
@@ -67,7 +67,7 @@
         scores_visible,
     )
     from CTFd.utils.countries import get_countries, lookup_country_code
-    from CTFd.utils.countries.geoip import lookup_ip_address
+    from CTFd.utils.countries.geoip import lookup_ip_address, lookup_ip_address_city
 
     app.jinja_env.globals.update(config=config)
     app.jinja_env.globals.update(get_pages=get_pages)
@@ -89,6 +89,7 @@
     app.jinja_env.globals.update(get_countries=get_countries)
     app.jinja_env.globals.update(lookup_country_code=lookup_country_code)
     app.jinja_env.globals.update(lookup_ip_address=lookup_ip_address)
+    app.jinja_env.globals.update(lookup_ip_address_city=lookup_ip_address_city)
     app.jinja_env.globals.update(accounts_visible=accounts_visible)
     app.jinja_env.globals.update(challenges_visible=challenges_visible)
     app.jinja_env.globals.update(registration_visible=registration_visible)

{"golden_diff": "diff --git a/CTFd/utils/countries/geoip.py b/CTFd/utils/countries/geoip.py\n--- a/CTFd/utils/countries/geoip.py\n+++ b/CTFd/utils/countries/geoip.py\n@@ -1,9 +1,9 @@\n-import geoacumen\n+import geoacumen_city\n import maxminddb\n from flask import current_app\n \n IP_ADDR_LOOKUP = maxminddb.open_database(\n-    current_app.config.get(\"GEOIP_DATABASE_PATH\", geoacumen.db_path)\n+    current_app.config.get(\"GEOIP_DATABASE_PATH\", geoacumen_city.db_path)\n )\n \n \n@@ -11,5 +11,13 @@\n     try:\n         response = IP_ADDR_LOOKUP.get(addr)\n         return response[\"country\"][\"iso_code\"]\n-    except (KeyError, ValueError):\n+    except (KeyError, ValueError, TypeError):\n+        return None\n+\n+\n+def lookup_ip_address_city(addr):\n+    try:\n+        response = IP_ADDR_LOOKUP.get(addr)\n+        return response[\"city\"][\"names\"][\"en\"]\n+    except (KeyError, ValueError, TypeError):\n         return None\ndiff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py\n--- a/CTFd/utils/initialization/__init__.py\n+++ b/CTFd/utils/initialization/__init__.py\n@@ -67,7 +67,7 @@\n         scores_visible,\n     )\n     from CTFd.utils.countries import get_countries, lookup_country_code\n-    from CTFd.utils.countries.geoip import lookup_ip_address\n+    from CTFd.utils.countries.geoip import lookup_ip_address, lookup_ip_address_city\n \n     app.jinja_env.globals.update(config=config)\n     app.jinja_env.globals.update(get_pages=get_pages)\n@@ -89,6 +89,7 @@\n     app.jinja_env.globals.update(get_countries=get_countries)\n     app.jinja_env.globals.update(lookup_country_code=lookup_country_code)\n     app.jinja_env.globals.update(lookup_ip_address=lookup_ip_address)\n+    app.jinja_env.globals.update(lookup_ip_address_city=lookup_ip_address_city)\n     app.jinja_env.globals.update(accounts_visible=accounts_visible)\n     app.jinja_env.globals.update(challenges_visible=challenges_visible)\n     app.jinja_env.globals.update(registration_visible=registration_visible)\n", "issue": "IP to City Database\nI think we can provide an IP to city database now instead of just showing country. \n", "before_files": [{"content": "import datetime\nimport logging\nimport os\nimport sys\n\nfrom flask import abort, redirect, render_template, request, session, url_for\nfrom sqlalchemy.exc import IntegrityError, InvalidRequestError\nfrom werkzeug.middleware.dispatcher import DispatcherMiddleware\n\nfrom CTFd.cache import clear_user_recent_ips\nfrom CTFd.exceptions import UserNotFoundException, UserTokenExpiredException\nfrom CTFd.models import Tracking, db\nfrom CTFd.utils import config, get_config, markdown\nfrom CTFd.utils.config import (\n    can_send_mail,\n    ctf_logo,\n    ctf_name,\n    ctf_theme,\n    integrations,\n    is_setup,\n)\nfrom CTFd.utils.config.pages import get_pages\nfrom CTFd.utils.dates import isoformat, unix_time, unix_time_millis\nfrom CTFd.utils.events import EventManager, RedisEventManager\nfrom CTFd.utils.humanize.words import pluralize\nfrom CTFd.utils.modes import generate_account_url, get_mode_as_word\nfrom CTFd.utils.plugins import (\n    get_configurable_plugins,\n    get_registered_admin_scripts,\n    get_registered_admin_stylesheets,\n    get_registered_scripts,\n    get_registered_stylesheets,\n)\nfrom CTFd.utils.security.auth import login_user, logout_user, lookup_user_token\nfrom CTFd.utils.security.csrf import generate_nonce\nfrom CTFd.utils.user import (\n    authed,\n    get_current_team_attrs,\n    get_current_user_attrs,\n    get_current_user_recent_ips,\n    get_ip,\n    is_admin,\n)\n\n\ndef init_template_filters(app):\n    app.jinja_env.filters[\"markdown\"] = markdown\n    app.jinja_env.filters[\"unix_time\"] = unix_time\n    app.jinja_env.filters[\"unix_time_millis\"] = unix_time_millis\n    app.jinja_env.filters[\"isoformat\"] = isoformat\n    app.jinja_env.filters[\"pluralize\"] = pluralize\n\n\ndef init_template_globals(app):\n    from CTFd.constants import JINJA_ENUMS\n    from CTFd.constants.config import Configs\n    from CTFd.constants.plugins import Plugins\n    from CTFd.constants.sessions import Session\n    from CTFd.constants.static import Static\n    from CTFd.constants.users import User\n    from CTFd.constants.teams import Team\n    from CTFd.forms import Forms\n    from CTFd.utils.config.visibility import (\n        accounts_visible,\n        challenges_visible,\n        registration_visible,\n        scores_visible,\n    )\n    from CTFd.utils.countries import get_countries, lookup_country_code\n    from CTFd.utils.countries.geoip import lookup_ip_address\n\n    app.jinja_env.globals.update(config=config)\n    app.jinja_env.globals.update(get_pages=get_pages)\n    app.jinja_env.globals.update(can_send_mail=can_send_mail)\n    app.jinja_env.globals.update(get_ctf_name=ctf_name)\n    app.jinja_env.globals.update(get_ctf_logo=ctf_logo)\n    app.jinja_env.globals.update(get_ctf_theme=ctf_theme)\n    app.jinja_env.globals.update(get_configurable_plugins=get_configurable_plugins)\n    app.jinja_env.globals.update(get_registered_scripts=get_registered_scripts)\n    app.jinja_env.globals.update(get_registered_stylesheets=get_registered_stylesheets)\n    app.jinja_env.globals.update(\n        get_registered_admin_scripts=get_registered_admin_scripts\n    )\n    app.jinja_env.globals.update(\n        get_registered_admin_stylesheets=get_registered_admin_stylesheets\n    )\n    app.jinja_env.globals.update(get_config=get_config)\n    app.jinja_env.globals.update(generate_account_url=generate_account_url)\n    app.jinja_env.globals.update(get_countries=get_countries)\n    app.jinja_env.globals.update(lookup_country_code=lookup_country_code)\n    app.jinja_env.globals.update(lookup_ip_address=lookup_ip_address)\n    app.jinja_env.globals.update(accounts_visible=accounts_visible)\n    app.jinja_env.globals.update(challenges_visible=challenges_visible)\n    app.jinja_env.globals.update(registration_visible=registration_visible)\n    app.jinja_env.globals.update(scores_visible=scores_visible)\n    app.jinja_env.globals.update(get_mode_as_word=get_mode_as_word)\n    app.jinja_env.globals.update(integrations=integrations)\n    app.jinja_env.globals.update(authed=authed)\n    app.jinja_env.globals.update(is_admin=is_admin)\n    app.jinja_env.globals.update(get_current_user_attrs=get_current_user_attrs)\n    app.jinja_env.globals.update(get_current_team_attrs=get_current_team_attrs)\n    app.jinja_env.globals.update(get_ip=get_ip)\n    app.jinja_env.globals.update(Configs=Configs)\n    app.jinja_env.globals.update(Plugins=Plugins)\n    app.jinja_env.globals.update(Session=Session)\n    app.jinja_env.globals.update(Static=Static)\n    app.jinja_env.globals.update(Forms=Forms)\n    app.jinja_env.globals.update(User=User)\n    app.jinja_env.globals.update(Team=Team)\n\n    # Add in JinjaEnums\n    # The reason this exists is that on double import, JinjaEnums are not reinitialized\n    # Thus, if you try to create two jinja envs (e.g. during testing), sometimes\n    # an Enum will not be available to Jinja.\n    # Instead we can just directly grab them from the persisted global dictionary.\n    for k, v in JINJA_ENUMS.items():\n        # .update() can't be used here because it would use the literal value k\n        app.jinja_env.globals[k] = v\n\n\ndef init_logs(app):\n    logger_submissions = logging.getLogger(\"submissions\")\n    logger_logins = logging.getLogger(\"logins\")\n    logger_registrations = logging.getLogger(\"registrations\")\n\n    logger_submissions.setLevel(logging.INFO)\n    logger_logins.setLevel(logging.INFO)\n    logger_registrations.setLevel(logging.INFO)\n\n    log_dir = app.config[\"LOG_FOLDER\"]\n    if not os.path.exists(log_dir):\n        os.makedirs(log_dir)\n\n    logs = {\n        \"submissions\": os.path.join(log_dir, \"submissions.log\"),\n        \"logins\": os.path.join(log_dir, \"logins.log\"),\n        \"registrations\": os.path.join(log_dir, \"registrations.log\"),\n    }\n\n    try:\n        for log in logs.values():\n            if not os.path.exists(log):\n                open(log, \"a\").close()\n\n        submission_log = logging.handlers.RotatingFileHandler(\n            logs[\"submissions\"], maxBytes=10485760, backupCount=5\n        )\n        login_log = logging.handlers.RotatingFileHandler(\n            logs[\"logins\"], maxBytes=10485760, backupCount=5\n        )\n        registration_log = logging.handlers.RotatingFileHandler(\n            logs[\"registrations\"], maxBytes=10485760, backupCount=5\n        )\n\n        logger_submissions.addHandler(submission_log)\n        logger_logins.addHandler(login_log)\n        logger_registrations.addHandler(registration_log)\n    except IOError:\n        pass\n\n    stdout = logging.StreamHandler(stream=sys.stdout)\n\n    logger_submissions.addHandler(stdout)\n    logger_logins.addHandler(stdout)\n    logger_registrations.addHandler(stdout)\n\n    logger_submissions.propagate = 0\n    logger_logins.propagate = 0\n    logger_registrations.propagate = 0\n\n\ndef init_events(app):\n    if app.config.get(\"CACHE_TYPE\") == \"redis\":\n        app.events_manager = RedisEventManager()\n    elif app.config.get(\"CACHE_TYPE\") == \"filesystem\":\n        app.events_manager = EventManager()\n    else:\n        app.events_manager = EventManager()\n    app.events_manager.listen()\n\n\ndef init_request_processors(app):\n    @app.url_defaults\n    def inject_theme(endpoint, values):\n        if \"theme\" not in values and app.url_map.is_endpoint_expecting(\n            endpoint, \"theme\"\n        ):\n            values[\"theme\"] = ctf_theme()\n\n    @app.before_request\n    def needs_setup():\n        if is_setup() is False:\n            if request.endpoint in (\n                \"views.setup\",\n                \"views.integrations\",\n                \"views.themes\",\n                \"views.files\",\n            ):\n                return\n            else:\n                return redirect(url_for(\"views.setup\"))\n\n    @app.before_request\n    def tracker():\n        if request.endpoint == \"views.themes\":\n            return\n\n        if authed():\n            user_ips = get_current_user_recent_ips()\n            ip = get_ip()\n\n            track = None\n            if (ip not in user_ips) or (request.method != \"GET\"):\n                track = Tracking.query.filter_by(\n                    ip=get_ip(), user_id=session[\"id\"]\n                ).first()\n\n                if track:\n                    track.date = datetime.datetime.utcnow()\n                else:\n                    track = Tracking(ip=get_ip(), user_id=session[\"id\"])\n                    db.session.add(track)\n\n            if track:\n                try:\n                    db.session.commit()\n                except (InvalidRequestError, IntegrityError):\n                    db.session.rollback()\n                    db.session.close()\n                    logout_user()\n                else:\n                    clear_user_recent_ips(user_id=session[\"id\"])\n\n    @app.before_request\n    def banned():\n        if request.endpoint == \"views.themes\":\n            return\n\n        if authed():\n            user = get_current_user_attrs()\n            team = get_current_team_attrs()\n\n            if user and user.banned:\n                return (\n                    render_template(\n                        \"errors/403.html\", error=\"You have been banned from this CTF\"\n                    ),\n                    403,\n                )\n\n            if team and team.banned:\n                return (\n                    render_template(\n                        \"errors/403.html\",\n                        error=\"Your team has been banned from this CTF\",\n                    ),\n                    403,\n                )\n\n    @app.before_request\n    def tokens():\n        token = request.headers.get(\"Authorization\")\n        if token and request.content_type == \"application/json\":\n            try:\n                token_type, token = token.split(\" \", 1)\n                user = lookup_user_token(token)\n            except UserNotFoundException:\n                abort(401)\n            except UserTokenExpiredException:\n                abort(401, description=\"Your access token has expired\")\n            except Exception:\n                abort(401)\n            else:\n                login_user(user)\n\n    @app.before_request\n    def csrf():\n        try:\n            func = app.view_functions[request.endpoint]\n        except KeyError:\n            abort(404)\n        if hasattr(func, \"_bypass_csrf\"):\n            return\n        if request.headers.get(\"Authorization\"):\n            return\n        if not session.get(\"nonce\"):\n            session[\"nonce\"] = generate_nonce()\n        if request.method not in (\"GET\", \"HEAD\", \"OPTIONS\", \"TRACE\"):\n            if request.content_type == \"application/json\":\n                if session[\"nonce\"] != request.headers.get(\"CSRF-Token\"):\n                    abort(403)\n            if request.content_type != \"application/json\":\n                if session[\"nonce\"] != request.form.get(\"nonce\"):\n                    abort(403)\n\n    application_root = app.config.get(\"APPLICATION_ROOT\")\n    if application_root != \"/\":\n\n        @app.before_request\n        def force_subdirectory_redirect():\n            if request.path.startswith(application_root) is False:\n                return redirect(\n                    application_root + request.script_root + request.full_path\n                )\n\n        app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {application_root: app})\n", "path": "CTFd/utils/initialization/__init__.py"}, {"content": "import geoacumen\nimport maxminddb\nfrom flask import current_app\n\nIP_ADDR_LOOKUP = maxminddb.open_database(\n    current_app.config.get(\"GEOIP_DATABASE_PATH\", geoacumen.db_path)\n)\n\n\ndef lookup_ip_address(addr):\n    try:\n        response = IP_ADDR_LOOKUP.get(addr)\n        return response[\"country\"][\"iso_code\"]\n    except (KeyError, ValueError):\n        return None\n", "path": "CTFd/utils/countries/geoip.py"}], "after_files": [{"content": "import datetime\nimport logging\nimport os\nimport sys\n\nfrom flask import abort, redirect, render_template, request, session, url_for\nfrom sqlalchemy.exc import IntegrityError, InvalidRequestError\nfrom werkzeug.middleware.dispatcher import DispatcherMiddleware\n\nfrom CTFd.cache import clear_user_recent_ips\nfrom CTFd.exceptions import UserNotFoundException, UserTokenExpiredException\nfrom CTFd.models import Tracking, db\nfrom CTFd.utils import config, get_config, markdown\nfrom CTFd.utils.config import (\n    can_send_mail,\n    ctf_logo,\n    ctf_name,\n    ctf_theme,\n    integrations,\n    is_setup,\n)\nfrom CTFd.utils.config.pages import get_pages\nfrom CTFd.utils.dates import isoformat, unix_time, unix_time_millis\nfrom CTFd.utils.events import EventManager, RedisEventManager\nfrom CTFd.utils.humanize.words import pluralize\nfrom CTFd.utils.modes import generate_account_url, get_mode_as_word\nfrom CTFd.utils.plugins import (\n    get_configurable_plugins,\n    get_registered_admin_scripts,\n    get_registered_admin_stylesheets,\n    get_registered_scripts,\n    get_registered_stylesheets,\n)\nfrom CTFd.utils.security.auth import login_user, logout_user, lookup_user_token\nfrom CTFd.utils.security.csrf import generate_nonce\nfrom CTFd.utils.user import (\n    authed,\n    get_current_team_attrs,\n    get_current_user_attrs,\n    get_current_user_recent_ips,\n    get_ip,\n    is_admin,\n)\n\n\ndef init_template_filters(app):\n    app.jinja_env.filters[\"markdown\"] = markdown\n    app.jinja_env.filters[\"unix_time\"] = unix_time\n    app.jinja_env.filters[\"unix_time_millis\"] = unix_time_millis\n    app.jinja_env.filters[\"isoformat\"] = isoformat\n    app.jinja_env.filters[\"pluralize\"] = pluralize\n\n\ndef init_template_globals(app):\n    from CTFd.constants import JINJA_ENUMS\n    from CTFd.constants.config import Configs\n    from CTFd.constants.plugins import Plugins\n    from CTFd.constants.sessions import Session\n    from CTFd.constants.static import Static\n    from CTFd.constants.users import User\n    from CTFd.constants.teams import Team\n    from CTFd.forms import Forms\n    from CTFd.utils.config.visibility import (\n        accounts_visible,\n        challenges_visible,\n        registration_visible,\n        scores_visible,\n    )\n    from CTFd.utils.countries import get_countries, lookup_country_code\n    from CTFd.utils.countries.geoip import lookup_ip_address, lookup_ip_address_city\n\n    app.jinja_env.globals.update(config=config)\n    app.jinja_env.globals.update(get_pages=get_pages)\n    app.jinja_env.globals.update(can_send_mail=can_send_mail)\n    app.jinja_env.globals.update(get_ctf_name=ctf_name)\n    app.jinja_env.globals.update(get_ctf_logo=ctf_logo)\n    app.jinja_env.globals.update(get_ctf_theme=ctf_theme)\n    app.jinja_env.globals.update(get_configurable_plugins=get_configurable_plugins)\n    app.jinja_env.globals.update(get_registered_scripts=get_registered_scripts)\n    app.jinja_env.globals.update(get_registered_stylesheets=get_registered_stylesheets)\n    app.jinja_env.globals.update(\n        get_registered_admin_scripts=get_registered_admin_scripts\n    )\n    app.jinja_env.globals.update(\n        get_registered_admin_stylesheets=get_registered_admin_stylesheets\n    )\n    app.jinja_env.globals.update(get_config=get_config)\n    app.jinja_env.globals.update(generate_account_url=generate_account_url)\n    app.jinja_env.globals.update(get_countries=get_countries)\n    app.jinja_env.globals.update(lookup_country_code=lookup_country_code)\n    app.jinja_env.globals.update(lookup_ip_address=lookup_ip_address)\n    app.jinja_env.globals.update(lookup_ip_address_city=lookup_ip_address_city)\n    app.jinja_env.globals.update(accounts_visible=accounts_visible)\n    app.jinja_env.globals.update(challenges_visible=challenges_visible)\n    app.jinja_env.globals.update(registration_visible=registration_visible)\n    app.jinja_env.globals.update(scores_visible=scores_visible)\n    app.jinja_env.globals.update(get_mode_as_word=get_mode_as_word)\n    app.jinja_env.globals.update(integrations=integrations)\n    app.jinja_env.globals.update(authed=authed)\n    app.jinja_env.globals.update(is_admin=is_admin)\n    app.jinja_env.globals.update(get_current_user_attrs=get_current_user_attrs)\n    app.jinja_env.globals.update(get_current_team_attrs=get_current_team_attrs)\n    app.jinja_env.globals.update(get_ip=get_ip)\n    app.jinja_env.globals.update(Configs=Configs)\n    app.jinja_env.globals.update(Plugins=Plugins)\n    app.jinja_env.globals.update(Session=Session)\n    app.jinja_env.globals.update(Static=Static)\n    app.jinja_env.globals.update(Forms=Forms)\n    app.jinja_env.globals.update(User=User)\n    app.jinja_env.globals.update(Team=Team)\n\n    # Add in JinjaEnums\n    # The reason this exists is that on double import, JinjaEnums are not reinitialized\n    # Thus, if you try to create two jinja envs (e.g. during testing), sometimes\n    # an Enum will not be available to Jinja.\n    # Instead we can just directly grab them from the persisted global dictionary.\n    for k, v in JINJA_ENUMS.items():\n        # .update() can't be used here because it would use the literal value k\n        app.jinja_env.globals[k] = v\n\n\ndef init_logs(app):\n    logger_submissions = logging.getLogger(\"submissions\")\n    logger_logins = logging.getLogger(\"logins\")\n    logger_registrations = logging.getLogger(\"registrations\")\n\n    logger_submissions.setLevel(logging.INFO)\n    logger_logins.setLevel(logging.INFO)\n    logger_registrations.setLevel(logging.INFO)\n\n    log_dir = app.config[\"LOG_FOLDER\"]\n    if not os.path.exists(log_dir):\n        os.makedirs(log_dir)\n\n    logs = {\n        \"submissions\": os.path.join(log_dir, \"submissions.log\"),\n        \"logins\": os.path.join(log_dir, \"logins.log\"),\n        \"registrations\": os.path.join(log_dir, \"registrations.log\"),\n    }\n\n    try:\n        for log in logs.values():\n            if not os.path.exists(log):\n                open(log, \"a\").close()\n\n        submission_log = logging.handlers.RotatingFileHandler(\n            logs[\"submissions\"], maxBytes=10485760, backupCount=5\n        )\n        login_log = logging.handlers.RotatingFileHandler(\n            logs[\"logins\"], maxBytes=10485760, backupCount=5\n        )\n        registration_log = logging.handlers.RotatingFileHandler(\n            logs[\"registrations\"], maxBytes=10485760, backupCount=5\n        )\n\n        logger_submissions.addHandler(submission_log)\n        logger_logins.addHandler(login_log)\n        logger_registrations.addHandler(registration_log)\n    except IOError:\n        pass\n\n    stdout = logging.StreamHandler(stream=sys.stdout)\n\n    logger_submissions.addHandler(stdout)\n    logger_logins.addHandler(stdout)\n    logger_registrations.addHandler(stdout)\n\n    logger_submissions.propagate = 0\n    logger_logins.propagate = 0\n    logger_registrations.propagate = 0\n\n\ndef init_events(app):\n    if app.config.get(\"CACHE_TYPE\") == \"redis\":\n        app.events_manager = RedisEventManager()\n    elif app.config.get(\"CACHE_TYPE\") == \"filesystem\":\n        app.events_manager = EventManager()\n    else:\n        app.events_manager = EventManager()\n    app.events_manager.listen()\n\n\ndef init_request_processors(app):\n    @app.url_defaults\n    def inject_theme(endpoint, values):\n        if \"theme\" not in values and app.url_map.is_endpoint_expecting(\n            endpoint, \"theme\"\n        ):\n            values[\"theme\"] = ctf_theme()\n\n    @app.before_request\n    def needs_setup():\n        if is_setup() is False:\n            if request.endpoint in (\n                \"views.setup\",\n                \"views.integrations\",\n                \"views.themes\",\n                \"views.files\",\n            ):\n                return\n            else:\n                return redirect(url_for(\"views.setup\"))\n\n    @app.before_request\n    def tracker():\n        if request.endpoint == \"views.themes\":\n            return\n\n        if authed():\n            user_ips = get_current_user_recent_ips()\n            ip = get_ip()\n\n            track = None\n            if (ip not in user_ips) or (request.method != \"GET\"):\n                track = Tracking.query.filter_by(\n                    ip=get_ip(), user_id=session[\"id\"]\n                ).first()\n\n                if track:\n                    track.date = datetime.datetime.utcnow()\n                else:\n                    track = Tracking(ip=get_ip(), user_id=session[\"id\"])\n                    db.session.add(track)\n\n            if track:\n                try:\n                    db.session.commit()\n                except (InvalidRequestError, IntegrityError):\n                    db.session.rollback()\n                    db.session.close()\n                    logout_user()\n                else:\n                    clear_user_recent_ips(user_id=session[\"id\"])\n\n    @app.before_request\n    def banned():\n        if request.endpoint == \"views.themes\":\n            return\n\n        if authed():\n            user = get_current_user_attrs()\n            team = get_current_team_attrs()\n\n            if user and user.banned:\n                return (\n                    render_template(\n                        \"errors/403.html\", error=\"You have been banned from this CTF\"\n                    ),\n                    403,\n                )\n\n            if team and team.banned:\n                return (\n                    render_template(\n                        \"errors/403.html\",\n                        error=\"Your team has been banned from this CTF\",\n                    ),\n                    403,\n                )\n\n    @app.before_request\n    def tokens():\n        token = request.headers.get(\"Authorization\")\n        if token and request.content_type == \"application/json\":\n            try:\n                token_type, token = token.split(\" \", 1)\n                user = lookup_user_token(token)\n            except UserNotFoundException:\n                abort(401)\n            except UserTokenExpiredException:\n                abort(401, description=\"Your access token has expired\")\n            except Exception:\n                abort(401)\n            else:\n                login_user(user)\n\n    @app.before_request\n    def csrf():\n        try:\n            func = app.view_functions[request.endpoint]\n        except KeyError:\n            abort(404)\n        if hasattr(func, \"_bypass_csrf\"):\n            return\n        if request.headers.get(\"Authorization\"):\n            return\n        if not session.get(\"nonce\"):\n            session[\"nonce\"] = generate_nonce()\n        if request.method not in (\"GET\", \"HEAD\", \"OPTIONS\", \"TRACE\"):\n            if request.content_type == \"application/json\":\n                if session[\"nonce\"] != request.headers.get(\"CSRF-Token\"):\n                    abort(403)\n            if request.content_type != \"application/json\":\n                if session[\"nonce\"] != request.form.get(\"nonce\"):\n                    abort(403)\n\n    application_root = app.config.get(\"APPLICATION_ROOT\")\n    if application_root != \"/\":\n\n        @app.before_request\n        def force_subdirectory_redirect():\n            if request.path.startswith(application_root) is False:\n                return redirect(\n                    application_root + request.script_root + request.full_path\n                )\n\n        app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {application_root: app})\n", "path": "CTFd/utils/initialization/__init__.py"}, {"content": "import geoacumen_city\nimport maxminddb\nfrom flask import current_app\n\nIP_ADDR_LOOKUP = maxminddb.open_database(\n    current_app.config.get(\"GEOIP_DATABASE_PATH\", geoacumen_city.db_path)\n)\n\n\ndef lookup_ip_address(addr):\n    try:\n        response = IP_ADDR_LOOKUP.get(addr)\n        return response[\"country\"][\"iso_code\"]\n    except (KeyError, ValueError, TypeError):\n        return None\n\n\ndef lookup_ip_address_city(addr):\n    try:\n        response = IP_ADDR_LOOKUP.get(addr)\n        return response[\"city\"][\"names\"][\"en\"]\n    except (KeyError, ValueError, TypeError):\n        return None\n", "path": "CTFd/utils/countries/geoip.py"}]}