prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  certwatcher.py is not using TLS_CERT_FILENAME nor TLS_KEYPAIR_FILENAME
  certwatcher.py is assuming that the certificate and the key are in /certs and named respectively cert.pem and key.pem
  
  However, Mailu offer two environment variables to allow specific path & filename for the certificate and the key which are used in config.py:
  TLS_CERT_FILENAME
  TLS_KEYPAIR_FILENAME
  
  I would add that in my use case (https-portal as a reverse proxy), those files are not directly in /certs. My environment variables look like:
  TLS_CERT_FILENAME=mail.example.net/production/signed.crt
  TLS_KEYPAIR_FILENAME=mail.example.net/production/domain.key
  Currently, certwatcher.py is monitoring `/certs`, whereas I would need to monitor 
  `/certs/mail.example.net/production`
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `core/nginx/certwatcher.py`
  Content:
  ```
  1 #!/usr/bin/python3
  2 """
  3 Certificate watcher which reloads nginx or reconfigures it, depending on what
  4 happens to externally supplied certificates. Only executed by start.py in case
  5 of TLS_FLAVOR=[mail, cert]
  6 """
  7 
  8 from os.path import exists, split as path_split
  9 from os import system
  10 import time
  11 from watchdog.observers.polling import PollingObserver
  12 from watchdog.events import FileSystemEventHandler, FileDeletedEvent, \
  13     FileCreatedEvent, FileModifiedEvent, FileMovedEvent
  14 
  15 class ChangeHandler(FileSystemEventHandler):
  16     "watchdog-handler listening on any event, executing the correct configuration/reload steps"
  17     @staticmethod
  18     def reload_nginx():
  19         "merely reload nginx without re-configuring everything"
  20         if exists("/var/run/nginx.pid"):
  21             print("Reloading a running nginx")
  22             system("nginx -s reload")
  23 
  24     @staticmethod
  25     def reexec_config():
  26         "execute a reconfiguration of the system, which also reloads"
  27         print("Reconfiguring system")
  28         system("/config.py")
  29 
  30     def on_any_event(self, event):
  31         "event-listener checking if the affected files are the cert-files we're interested in"
  32         if event.is_directory:
  33             return
  34 
  35         filename = path_split(event.src_path)[-1]
  36         if isinstance(event, FileMovedEvent):
  37             filename = path_split(event.dest_path)[-1]
  38 
  39         if filename in ['cert.pem', 'key.pem']:
  40             # all cases except for FileModified need re-configure
  41             if isinstance(event, (FileCreatedEvent, FileMovedEvent, FileDeletedEvent)):
  42                 ChangeHandler.reexec_config()
  43             # file modification needs only a nginx reload without config.py
  44             elif isinstance(event, FileModifiedEvent):
  45                 ChangeHandler.reload_nginx()
  46         # cert files have been moved away, re-configure
  47         elif isinstance(event, FileMovedEvent) and path_split(event.src_path)[-1] in ['cert.pem', 'key.pem']:
  48             ChangeHandler.reexec_config()
  49 
  50 
  51 if __name__ == '__main__':
  52     observer = PollingObserver()
  53     handler = ChangeHandler()
  54     observer.schedule(handler, "/certs", recursive=False)
  55     observer.start()
  56 
  57     try:
  58         while True:
  59             time.sleep(1)
  60     except KeyboardInterrupt:
  61         observer.stop()
  62 
  63     observer.join()
  64
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/core/nginx/certwatcher.py b/core/nginx/certwatcher.py
  --- a/core/nginx/certwatcher.py
  +++ b/core/nginx/certwatcher.py
  @@ -5,8 +5,8 @@
   of TLS_FLAVOR=[mail, cert]
   """
   
  -from os.path import exists, split as path_split
  -from os import system
  +from os.path import exists, split as path_split, join as path_join
  +from os import system, getenv
   import time
   from watchdog.observers.polling import PollingObserver
   from watchdog.events import FileSystemEventHandler, FileDeletedEvent, \
  @@ -14,6 +14,13 @@
   
   class ChangeHandler(FileSystemEventHandler):
       "watchdog-handler listening on any event, executing the correct configuration/reload steps"
  +
  +    def __init__(self, cert_path, keypair_path):
  +        "Initialize a new changehandler"""
  +        super().__init__()
  +        self.cert_path = cert_path
  +        self.keypair_path = keypair_path
  +
       @staticmethod
       def reload_nginx():
           "merely reload nginx without re-configuring everything"
  @@ -32,11 +39,11 @@
           if event.is_directory:
               return
   
  -        filename = path_split(event.src_path)[-1]
  +        filename = event.src_path
           if isinstance(event, FileMovedEvent):
  -            filename = path_split(event.dest_path)[-1]
  +            filename = event.dest_path
   
  -        if filename in ['cert.pem', 'key.pem']:
  +        if filename in [self.cert_path, self.keypair_path]:
               # all cases except for FileModified need re-configure
               if isinstance(event, (FileCreatedEvent, FileMovedEvent, FileDeletedEvent)):
                   ChangeHandler.reexec_config()
  @@ -44,14 +51,21 @@
               elif isinstance(event, FileModifiedEvent):
                   ChangeHandler.reload_nginx()
           # cert files have been moved away, re-configure
  -        elif isinstance(event, FileMovedEvent) and path_split(event.src_path)[-1] in ['cert.pem', 'key.pem']:
  +        elif isinstance(event, FileMovedEvent) and event.src_path in [self.cert_path, self.keypair_path]:
               ChangeHandler.reexec_config()
   
   
   if __name__ == '__main__':
  +    cert_path = path_join("/certs/", getenv("TLS_CERT_FILENAME", default="cert.pem"))
  +    cert_dir = path_split(cert_path)[0]
  +    keypair_path = path_join("/certs/", getenv("TLS_KEYPAIR_FILENAME", default="key.pem"))
  +    keypair_dir = path_split(keypair_path)[0]
  +
       observer = PollingObserver()
  -    handler = ChangeHandler()
  -    observer.schedule(handler, "/certs", recursive=False)
  +    handler = ChangeHandler(cert_path, keypair_path)
  +    observer.schedule(handler, cert_dir, recursive=False)
  +    if keypair_dir != cert_dir:
  +       observer.schedule(handler, keypair_dir, recursive=False)
       observer.start()
   
       try:
  
verification_info: {"golden_diff": "diff --git a/core/nginx/certwatcher.py b/core/nginx/certwatcher.py\n--- a/core/nginx/certwatcher.py\n+++ b/core/nginx/certwatcher.py\n@@ -5,8 +5,8 @@\n of TLS_FLAVOR=[mail, cert]\n \"\"\"\n \n-from os.path import exists, split as path_split\n-from os import system\n+from os.path import exists, split as path_split, join as path_join\n+from os import system, getenv\n import time\n from watchdog.observers.polling import PollingObserver\n from watchdog.events import FileSystemEventHandler, FileDeletedEvent, \\\n@@ -14,6 +14,13 @@\n \n class ChangeHandler(FileSystemEventHandler):\n     \"watchdog-handler listening on any event, executing the correct configuration/reload steps\"\n+\n+    def __init__(self, cert_path, keypair_path):\n+        \"Initialize a new changehandler\"\"\"\n+        super().__init__()\n+        self.cert_path = cert_path\n+        self.keypair_path = keypair_path\n+\n     @staticmethod\n     def reload_nginx():\n         \"merely reload nginx without re-configuring everything\"\n@@ -32,11 +39,11 @@\n         if event.is_directory:\n             return\n \n-        filename = path_split(event.src_path)[-1]\n+        filename = event.src_path\n         if isinstance(event, FileMovedEvent):\n-            filename = path_split(event.dest_path)[-1]\n+            filename = event.dest_path\n \n-        if filename in ['cert.pem', 'key.pem']:\n+        if filename in [self.cert_path, self.keypair_path]:\n             # all cases except for FileModified need re-configure\n             if isinstance(event, (FileCreatedEvent, FileMovedEvent, FileDeletedEvent)):\n                 ChangeHandler.reexec_config()\n@@ -44,14 +51,21 @@\n             elif isinstance(event, FileModifiedEvent):\n                 ChangeHandler.reload_nginx()\n         # cert files have been moved away, re-configure\n-        elif isinstance(event, FileMovedEvent) and path_split(event.src_path)[-1] in ['cert.pem', 'key.pem']:\n+        elif isinstance(event, FileMovedEvent) and event.src_path in [self.cert_path, self.keypair_path]:\n             ChangeHandler.reexec_config()\n \n \n if __name__ == '__main__':\n+    cert_path = path_join(\"/certs/\", getenv(\"TLS_CERT_FILENAME\", default=\"cert.pem\"))\n+    cert_dir = path_split(cert_path)[0]\n+    keypair_path = path_join(\"/certs/\", getenv(\"TLS_KEYPAIR_FILENAME\", default=\"key.pem\"))\n+    keypair_dir = path_split(keypair_path)[0]\n+\n     observer = PollingObserver()\n-    handler = ChangeHandler()\n-    observer.schedule(handler, \"/certs\", recursive=False)\n+    handler = ChangeHandler(cert_path, keypair_path)\n+    observer.schedule(handler, cert_dir, recursive=False)\n+    if keypair_dir != cert_dir:\n+       observer.schedule(handler, keypair_dir, recursive=False)\n     observer.start()\n \n     try:\n", "issue": "certwatcher.py is not using TLS_CERT_FILENAME nor TLS_KEYPAIR_FILENAME\ncertwatcher.py is assuming that the certificate and the key are in /certs and named respectively cert.pem and key.pem\r\n\r\nHowever, Mailu offer two environment variables to allow specific path & filename for the certificate and the key which are used in config.py:\r\nTLS_CERT_FILENAME\r\nTLS_KEYPAIR_FILENAME\r\n\r\nI would add that in my use case (https-portal as a reverse proxy), those files are not directly in /certs. My environment variables look like:\r\nTLS_CERT_FILENAME=mail.example.net/production/signed.crt\r\nTLS_KEYPAIR_FILENAME=mail.example.net/production/domain.key\r\nCurrently, certwatcher.py is monitoring `/certs`, whereas I would need to monitor \r\n`/certs/mail.example.net/production`\n", "before_files": [{"content": "#!/usr/bin/python3\n\"\"\"\nCertificate watcher which reloads nginx or reconfigures it, depending on what\nhappens to externally supplied certificates. Only executed by start.py in case\nof TLS_FLAVOR=[mail, cert]\n\"\"\"\n\nfrom os.path import exists, split as path_split\nfrom os import system\nimport time\nfrom watchdog.observers.polling import PollingObserver\nfrom watchdog.events import FileSystemEventHandler, FileDeletedEvent, \\\n    FileCreatedEvent, FileModifiedEvent, FileMovedEvent\n\nclass ChangeHandler(FileSystemEventHandler):\n    \"watchdog-handler listening on any event, executing the correct configuration/reload steps\"\n    @staticmethod\n    def reload_nginx():\n        \"merely reload nginx without re-configuring everything\"\n        if exists(\"/var/run/nginx.pid\"):\n            print(\"Reloading a running nginx\")\n            system(\"nginx -s reload\")\n\n    @staticmethod\n    def reexec_config():\n        \"execute a reconfiguration of the system, which also reloads\"\n        print(\"Reconfiguring system\")\n        system(\"/config.py\")\n\n    def on_any_event(self, event):\n        \"event-listener checking if the affected files are the cert-files we're interested in\"\n        if event.is_directory:\n            return\n\n        filename = path_split(event.src_path)[-1]\n        if isinstance(event, FileMovedEvent):\n            filename = path_split(event.dest_path)[-1]\n\n        if filename in ['cert.pem', 'key.pem']:\n            # all cases except for FileModified need re-configure\n            if isinstance(event, (FileCreatedEvent, FileMovedEvent, FileDeletedEvent)):\n                ChangeHandler.reexec_config()\n            # file modification needs only a nginx reload without config.py\n            elif isinstance(event, FileModifiedEvent):\n                ChangeHandler.reload_nginx()\n        # cert files have been moved away, re-configure\n        elif isinstance(event, FileMovedEvent) and path_split(event.src_path)[-1] in ['cert.pem', 'key.pem']:\n            ChangeHandler.reexec_config()\n\n\nif __name__ == '__main__':\n    observer = PollingObserver()\n    handler = ChangeHandler()\n    observer.schedule(handler, \"/certs\", recursive=False)\n    observer.start()\n\n    try:\n        while True:\n            time.sleep(1)\n    except KeyboardInterrupt:\n        observer.stop()\n\n    observer.join()\n", "path": "core/nginx/certwatcher.py"}], "after_files": [{"content": "#!/usr/bin/python3\n\"\"\"\nCertificate watcher which reloads nginx or reconfigures it, depending on what\nhappens to externally supplied certificates. Only executed by start.py in case\nof TLS_FLAVOR=[mail, cert]\n\"\"\"\n\nfrom os.path import exists, split as path_split, join as path_join\nfrom os import system, getenv\nimport time\nfrom watchdog.observers.polling import PollingObserver\nfrom watchdog.events import FileSystemEventHandler, FileDeletedEvent, \\\n    FileCreatedEvent, FileModifiedEvent, FileMovedEvent\n\nclass ChangeHandler(FileSystemEventHandler):\n    \"watchdog-handler listening on any event, executing the correct configuration/reload steps\"\n\n    def __init__(self, cert_path, keypair_path):\n        \"Initialize a new changehandler\"\"\"\n        super().__init__()\n        self.cert_path = cert_path\n        self.keypair_path = keypair_path\n\n    @staticmethod\n    def reload_nginx():\n        \"merely reload nginx without re-configuring everything\"\n        if exists(\"/var/run/nginx.pid\"):\n            print(\"Reloading a running nginx\")\n            system(\"nginx -s reload\")\n\n    @staticmethod\n    def reexec_config():\n        \"execute a reconfiguration of the system, which also reloads\"\n        print(\"Reconfiguring system\")\n        system(\"/config.py\")\n\n    def on_any_event(self, event):\n        \"event-listener checking if the affected files are the cert-files we're interested in\"\n        if event.is_directory:\n            return\n\n        filename = event.src_path\n        if isinstance(event, FileMovedEvent):\n            filename = event.dest_path\n\n        if filename in [self.cert_path, self.keypair_path]:\n            # all cases except for FileModified need re-configure\n            if isinstance(event, (FileCreatedEvent, FileMovedEvent, FileDeletedEvent)):\n                ChangeHandler.reexec_config()\n            # file modification needs only a nginx reload without config.py\n            elif isinstance(event, FileModifiedEvent):\n                ChangeHandler.reload_nginx()\n        # cert files have been moved away, re-configure\n        elif isinstance(event, FileMovedEvent) and event.src_path in [self.cert_path, self.keypair_path]:\n            ChangeHandler.reexec_config()\n\n\nif __name__ == '__main__':\n    cert_path = path_join(\"/certs/\", getenv(\"TLS_CERT_FILENAME\", default=\"cert.pem\"))\n    cert_dir = path_split(cert_path)[0]\n    keypair_path = path_join(\"/certs/\", getenv(\"TLS_KEYPAIR_FILENAME\", default=\"key.pem\"))\n    keypair_dir = path_split(keypair_path)[0]\n\n    observer = PollingObserver()\n    handler = ChangeHandler(cert_path, keypair_path)\n    observer.schedule(handler, cert_dir, recursive=False)\n    if keypair_dir != cert_dir:\n       observer.schedule(handler, keypair_dir, recursive=False)\n    observer.start()\n\n    try:\n        while True:\n            time.sleep(1)\n    except KeyboardInterrupt:\n        observer.stop()\n\n    observer.join()\n", "path": "core/nginx/certwatcher.py"}]}