# Technical documentation — smsa2020

Evidence date: 2026-10-03.

Source: `git@bitbucket.org:overdrive/smsa2020.git`, `master` at `6c61e14` (2020-02-10).

No README, dependency manifest, or test suite is in the repository. Passwords found in `application.ini.bak` and in `ApplicationController::sendMail` are omitted.

## Architecture

Monolithic PHP application. Zend Framework 1.11.10 is vendored at `library/Zend`. One front controller (`public/index.php`) creates `Zend_Application` with `application/configs/application.ini` and runs it.

Module directory: `application/modules`. The only module is `default`.

Request flow:

1. Root `.htaccess` rewrites any URI that does not already start with `/public/` to `public/{uri}`.
2. `public/.htaccess` serves real files and rewrites everything else to `index.php`.
3. Zend's default router maps `/{controller}/{action}/{param}/{value}`. Module `default` is implicit.
4. Layout `default.phtml` wraps the view, unless the action disables the layout.

`application/Bootstrap.php` defines `_initDbAdapter` and the method body is empty. The database adapter comes from `resources.db` in the ini file, not from this method.

`IndexController::indexAction` contains a POST branch that inserts an application and calls `$this->SavePhoto`. `IndexController` has no `SavePhoto` method. The home form's `action` is `/application/apply/code/...`, so that POST branch is not the path the wizard uses.

## Application structure

```text
smsa2020/
  application/
    configs/application.ini.bak    committed backup; application.ini is gitignored
    layouts/default.phtml
    models/                        Zend_Db_Table classes
    modules/default/
      Bootstrap.php
      controllers/                 Application, Error, Index
      views/scripts/               application, error, index, sean
  library/Zend/                    Framework 1.11.10
  library/PG/                      Shared plugins (not referenced by the two feature controllers)
  library/Other/                   Functions.php, Notifications.php
  public/                          index.php, assets, images, accre/profile-icon.png
  scripts/                         CodeDeploy hooks
  appspec.yml
  bitbucket-pipelines.yml
  codedeploy_deploy.py
```

`public/assets.zip` and `public/assets_old` are committed beside `public/assets`.

## Framework, language, runtime

| Item | Evidence | Value |
|---|---|---|
| Language | `.php` controllers and views | PHP |
| Framework | `library/Zend/Version.php` | Zend Framework 1.11.10 |
| View | `.phtml` | Zend view plus layout |
| Timezone | `public/index.php`, `ApplicationController` | `Africa/Johannesburg` |
| Locale resource | `application.ini.bak` | `af_ZA` |
| Error display | `public/index.php` | `display_errors` 1, `E_ALL\|E_STRICT` |
| Production ini backup | `application.ini.bak` `[production]` | `display_errors` 1 and `displayExceptions` 1 |
| Memory | `IndexController` | `180M` |
| Memory | `ApplicationController` | `512M` |
| Random bytes | `random_id` | `mcrypt_create_iv` |
| Images | `thumbnail_data_uri`, `resize_imagejpg` | GD (`imagecreatetruecolor`, `imagejpeg`) |
| HTTP client | `sendImage`, `remoteFileExists` | curl |

PHP version on the server is **Unknown / Requires Confirmation**. `mcrypt` was removed from PHP in 7.2. A host on PHP 7.2 or later will fail when `random_id` runs.

`public/index.php` defaults `APPLICATION_ENV` to `production` when the environment variable is absent.

Commented lines at the top of `index.php` would print a maintenance message and `exit`. They are commented, so they do not run.

## Database

Adapter in the backup ini: `pdo_mysql`.

| Setting | Backup value |
|---|---|
| Host | `db.footprintapp.net` |
| Database | `teamwizz_SARU` |
| Username | `teamwizz_ADMIN` |
| Password | Present in `application.ini.bak`. Not copied into this document |

`application/configs/application.ini` is listed in `.gitignore`. It was not in the clone. Whether any environment still uses the backup values is **Unknown / Requires Confirmation**.

No `CREATE TABLE` script is in the repository. Table and view names below are the strings in the models.

### Tables

| PHP class | `$_name` |
|---|---|
| `Model_Accreditation` | `Accreditation` |
| `Model_AccreditationList` | `Accreditation_List` |
| `Model_AccreditationsApplications` | `AccreditationApplycation` |
| `Model_AccreditationApplicationCategory` | `Accreditation_Application_Category` |
| `Model_AccreditationCategory` | `Accreditation_Category` |
| `Model_AccreditationRoles` | `AccreditationRoles` |
| `Model_AccreditationApplicationRole` | `Accreditation_Application_Roles` |
| `Model_AccreditationLinks` | `LinkedAccreditations` |
| `Model_AccreditationZone` | `AccreditationsZones` |
| `Model_AccreditationApplicantsServices` | `Accreditation_Applicants_Services` |
| `Model_AccreditationAdministrators` | `Accreditation_Administrators` |
| `Model_AccreditationHistory` | `Accreditation_History` |
| `Model_AccreditationPermits` | `Accreditation_Permits` |
| `Model_AccreditationGates` | `EventGates` |
| `Model_AccreditationLoadingZones` | `Accreditation_Loading_Zones` |
| `Model_AccreditationLoadingServices` | `Accreditation_Load_Services_Types` |
| `Model_AccreditationServices` | `accreditation_Services` |
| `Model_AccreditationEventServices` | `accreditation_Event_Services` |
| `Model_AccreditationTypes` | `AccreditationTypes` |
| `Model_AccreditationQueing` | `AccriditationApplicationQueing` |

The application table name is spelled `AccreditationApplycation` in code.

### Views used by the public controllers

| View | Used by |
|---|---|
| `Accreditations_view` | `getProfile` (`aCode`) |
| `AccreditationCategory_view` | `getAccreditationCategoryPublic` |
| `AccreditationRoles_view` | `getAccreditationRoleLive` |

Other view names exist on models that the public controllers do not construct (`SquadAccredition_view`, `AccriditationNewConfirmedApprovedStatus_view`).

Queries build `WHERE` clauses by concatenating request and route values. That is **Implemented**.

## Routes that the public UI calls

Zend default routing. No custom routes file was found.

| Method and path | Action |
|---|---|
| `GET /` | `IndexController::indexAction` |
| `GET /index/checkemail/email/{email}/ac/{ac}` | `IndexController::checkemailAction` |
| `POST /application/apply/code/{code}` | `ApplicationController::applyAction` |
| `GET /application/login` | `ApplicationController::loginAction` |
| `POST /application/loginnow/ac/{ac}` | `ApplicationController::loginnowAction` |
| `GET /application/profile/code/{code}` | `ApplicationController::profileAction` |
| `POST /application/update/aaid/{aaid}` | `ApplicationController::updateAction` |
| `POST /application/updateimage` | `ApplicationController::updateimageAction` |
| `GET /application/logout` | `ApplicationController::logoutAction` |
| `GET /application/attendance/key/{key}/att/{1\|2}` | `ApplicationController::attendanceAction` |

`ErrorController::errorAction` sets HTTP 404 for missing route, controller, or action, and HTTP 500 otherwise. It logs only if a Log bootstrap resource exists. The backup ini does not configure a log resource. With `displayExceptions` true, the exception is assigned to the view.

## Auth and session

Backup ini: `resources.session.cookie_lifetime = 864000` (10 days).

Namespace `user_session`:

- `AAID` — application row id
- `Code` — `aCode` from `Accreditation_List`

`isloggedIN` redirects to the profile when `AAID > 0`. It is called from `loginAction`, `applyAction`, and `applytestAction`.

`profileAction` redirects to apply when `AAID` is not greater than 0.

`updateAction`, `updateimageAction`, `attendanceAction`, `loginnowAction`, and `checkemailAction` do not call `isloggedIN`.

Sign-in is an equality match on email, reference, and accreditation id. The reference is stored and compared as plain text.

## Mail

`sendMail` builds `Zend_Mail_Transport_Smtp` for `smtp.gmail.com` with auth `login`, SSL `tls`, port 587, username `accreditation@magus.events`. The password literal is in the method and is omitted here.

From: `accreditation@magus.events`, name "Accreditation - Vodacom Super Hero Sunday 2020".

Subject: "Vodacom Super Hero Sunday 2020 Notification".

Body: HTML. The earlier footprintapp.net transport is commented out.

`testAction` sends a fixed body to a hardcoded address and exits. The address is omitted here.

## Files and images

Successful apply does not write the photo on this server. `sendImage` POSTs `ac`, `ref`, and `pic` to `https://www.footprintapp.net/accreditations/application/updateimage`. Timeout option is 86400 seconds. The curl response is not checked. `Content-type: multipart/form-data` is set, then `CURLOPT_POSTFIELDS` is set twice to the same array.

Profile load requests `https://www.footprintapp.net/accre/photos/{Ref}.jpg` with `CURLOPT_NOBODY`. HTTP 200 becomes a data-URI. Any other result uses `/accre/profile-icon.png`.

`SavePhoto` would write `AC_PATH . '/photos/' . {Ref}.jpg`. `AC_PATH` is not defined anywhere in the repository. The apply path does not call `SavePhoto`.

`UPDATE_PATH` in `public/index.php` points at `public/archives/`. That directory was not in the listing. No action in the two controllers reads `UPDATE_PATH`.

## Front-end assets

Layout stylesheets and scripts under `/assets/`:

- Bootstrap (`bootstrap.min.css`)
- Font Awesome
- Animate and `style.css` (markup uses classes `ibox` and `wizard-big`; no package name is declared in a manifest)
- jQuery Steps
- Cropper
- Select2
- iCheck, Chosen, Jasny, bootstrap tags input, dual listbox, awesome-bootstrap-checkbox

`style.css` is linked twice. Favicon links: `favicon.ico` and `/images/icon.png`. Commit `6ad4fe4` is "Update favicon". `public/images/icon.ico` and `icon.png` are in the later diff as binaries.

Browser classification is in the layout, described in the PRD (FR-015).

## Deploy and infrastructure

`bitbucket-pipelines.yml` uses image `python:3.5.1`.

Branches `qa` and `prod` each:

1. Export `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `APPLICATION_NAME`, `DEPLOYMENT_GROUP_NAME`, `S3_BUCKET` from Bitbucket variables prefixed `QA_` or `PROD_`.
2. `apt-get install zip`, `pip install boto3==1.3.0`.
3. `zip -r /tmp/artifact.zip *`.
4. `python codedeploy_deploy.py`.

`codedeploy_deploy.py` uploads the zip to `s3://{S3_BUCKET}/{APPLICATION_NAME}/{timestamp}-bitbucket_builds.zip` and calls `create_deployment`. It passes `deploymentConfigName` from env `DEPLOYMENT_CONFIG`. That variable is not exported in the pipeline file. If it is unset, the AWS call fails. Whether Bitbucket has it as a repository variable is **Unknown / Requires Confirmation**.

The script polls `get_deployment` until `Succeeded`, `Failed`, or `Stopped`. `ignoreApplicationStopFailures` is true. There is no sleep inside the poll loop.

`appspec.yml` (version 0.0, Linux):

- Copies `/` to `/mnt/data/www/html/magus.events/superhero2020.magus.events/`
- Owner and group `apache`
- BeforeInstall: `scripts/install_dependencies` (`yum install -y httpd`) as root, timeout 300
- ApplicationStop: `scripts/remove_archives.sh` as root
- ApplicationStart: `scripts/start_server` (`service httpd start`) as root

`remove_archives.sh` deletes CodeDeploy revision directories under `/opt/codedeploy-agent/deployment-root/` except `deployment-instructions` and the last successful install file.

`scripts/stop_server` stops httpd when `pgrep httpd` returns a process. It is not referenced by the current `appspec.yml`. ApplicationStop used to call it; the later diff switched that hook to `remove_archives.sh`. The assignment in `stop_server` is written with a space before `=`, which is not valid bash.

`master` is not a pipeline branch. Deploy happens only from `qa` and `prod`.

## Background work, queues, storage, monitoring

No cron, queue worker, or scheduler is defined in this repository.

`Model_AccreditationQueing` maps table `AccriditationApplicationQueing` and is not used by the controllers.

No application log path is configured. Deploy logging is the CodeDeploy and pipeline output.

No SonarQube file is in Git. `sonar-project.properties` and `sonarqube_scan.bat` were named when the onboarding issue was opened and are not at `6c61e14`.

## Security notes from the code

These are observations, not a remediation plan.

- Database password and SMTP password are committed.
- `display_errors` is enabled for the production section of the backup ini and in `index.php`.
- Sign-in credential is the application reference.
- `updateAction` and `updateimageAction` do not check the session.
- SQL predicates are concatenated.
- `barcodeAction` includes a file that is not in the tree.
- `testAction` is a routable mail sender.

## Development and testing

No documented local setup beyond the ini backup and `public/index.php`.

`scripts/install_dependencies` installs httpd with yum. It does not install PHP, the MySQL client, or mcrypt.

No PHPUnit, no fixture, no CI test step. The pipeline is deploy-only.

## Configuration inventory

| File | Tracked | Role |
|---|---|---|
| `application/configs/application.ini` | No (gitignored) | Runtime Zend config |
| `application/configs/application.ini.bak` | Yes | Backup of that config |
| `.gitignore` | Yes | Ignores only `application/configs/application.ini` |
| `bitbucket-pipelines.yml` | Yes | QA and prod deploy |
| `appspec.yml` | Yes | CodeDeploy file map and hooks |
| Environment variables | Not in Git | AWS and CodeDeploy names listed above |

Layout names in the ini (`admin`, `union`, `referee`, `gir`, `accreditations`) have no matching layout files in this tree.
