mirror of
git://f0xx.org/android_cast
synced 2026-07-29 05:17:39 +03:00
sync
This commit is contained in:
441
docs/specs/20100611_3_url_shortener.md
Normal file
441
docs/specs/20100611_3_url_shortener.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# URL shortener service — specification
|
||||
|
||||
**Document type:** SPEC
|
||||
**Source draft:** [docs/drafts/20100611_3_url_shortener.txt](../drafts/20100611_3_url_shortener.txt)
|
||||
**Design review:** [docs/DRs/20100611_3_url_shortener.md](../DRs/20100611_3_url_shortener.md)
|
||||
**Status:** Post-alpha (next after alpha); priority may shift with BE capacity/load
|
||||
**Severity:** Medium
|
||||
**Public host:** `https://s.f0xx.org`
|
||||
**Repo placement:** `backend/url-shortener` (git submodule → `git://f0xx.org/androidcast_project/url-shortener`)
|
||||
|
||||
**Documentation index:** [README.md](../README.md)
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- toc -->
|
||||
- [1. Purpose](#1-purpose)
|
||||
- [2. Scope](#2-scope)
|
||||
- [3. Requirements](#3-requirements)
|
||||
- [3.1 Must](#31-must)
|
||||
- [3.2 Should](#32-should)
|
||||
- [4. Architecture](#4-architecture)
|
||||
- [5. Authentication and trust](#5-authentication-and-trust)
|
||||
- [6. REST API](#6-rest-api)
|
||||
- [6.1 Shorten — GET](#61-shorten-get)
|
||||
- [6.2 Shorten — POST](#62-shorten-post)
|
||||
- [6.3 Error response](#63-error-response)
|
||||
- [6.4 Success response](#64-success-response)
|
||||
- [6.5 QR](#65-qr)
|
||||
- [7. Redirect and permalink behaviour](#7-redirect-and-permalink-behaviour)
|
||||
- [8. Slug algorithm](#8-slug-algorithm)
|
||||
- [9. Database schema](#9-database-schema)
|
||||
- [9.1 links](#91-links)
|
||||
- [9.2 bearer_tokens](#92-bearertokens)
|
||||
- [9.3 signer_keys](#93-signerkeys)
|
||||
- [9.4 audit_log](#94-auditlog)
|
||||
- [10. QR codes](#10-qr-codes)
|
||||
- [11. OpenAPI](#11-openapi)
|
||||
- [12. Deployment](#12-deployment)
|
||||
- [13. Security](#13-security)
|
||||
- [14. Observability and limits](#14-observability-and-limits)
|
||||
- [15. Tests](#15-tests)
|
||||
- [16. Out of scope (v1)](#16-out-of-scope-v1)
|
||||
- [17. Related docs](#17-related-docs)
|
||||
<!-- /toc -->
|
||||
|
||||
**Documentation index:** [README.md](README.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
Provide **short, encoded URLs on demand** for long targets. The service is a **helper** for other apps and operators (Android Cast BE console, curl scripts, future mobile share flows) — not a user-facing product on its own.
|
||||
|
||||
**High-level flow:**
|
||||
|
||||
1. A client (any HTTP app; Android Cast not required) requests shortening of a long URL.
|
||||
2. The client authenticates with a **project bearer token** (and optionally a **signer** for elevated TTL).
|
||||
3. The service validates trust, creates or returns a cached short link, and responds with JSON.
|
||||
4. End users open `https://s.f0xx.org/{slug}` → HTTP redirect to the original URL (while TTL valid).
|
||||
|
||||
---
|
||||
|
||||
## 2. Scope
|
||||
|
||||
| In scope (v1) | Out of scope (v1) |
|
||||
|---------------|-------------------|
|
||||
| Standalone opt-in service (not inside crash-reporter PHP app) | Alpha release blocker |
|
||||
| REST API + OpenAPI 3 document | Custom domains per tenant |
|
||||
| PHP + nginx + MariaDB stack | Click analytics dashboard |
|
||||
| Permalink reuse for same `(bearer, url)` | Link preview / OG scraper |
|
||||
| Time-limited and permanent links (signer-gated) | Federated multi-region |
|
||||
| QR code PNG for any short URL | Mobile SDK (HTTP only v1) |
|
||||
|
||||
**Scheduling:** Post-alpha by default. PO may reorder if BE load is low and another product track needs short links urgently (see DR §Estimates).
|
||||
|
||||
---
|
||||
|
||||
## 3. Requirements
|
||||
|
||||
### 3.1 Must
|
||||
|
||||
| ID | Requirement |
|
||||
|----|-------------|
|
||||
| R1 | Standalone service; development **separate** from main BE crash/tickets app |
|
||||
| R2 | REST API documented with **OpenAPI 3** (`openapi.yaml` in repo) |
|
||||
| R3 | Stack: **PHP 8.x**, **nginx**, **MariaDB**; OSS dependencies only (MIT/BSD/Apache) |
|
||||
| R4 | Project path: `backend/url-shortener` submodule |
|
||||
| R5 | **QR code** generation (PNG) for shortened URLs |
|
||||
| R6 | **Permalink** semantics: same bearer + same normalized URL → same slug while record valid |
|
||||
| R7 | **TTL**: temporary links expire; `ttl=0` permanent only for trusted signers |
|
||||
| R8 | JSON request/response contract (§6) |
|
||||
| R9 | Reverse lookup: `GET /{slug}` → `302` to stored origin (or `410` when expired) |
|
||||
|
||||
### 3.2 Should
|
||||
|
||||
| ID | Requirement |
|
||||
|----|-------------|
|
||||
| S1 | Support **GET** (query) and **POST** (JSON body) shorten endpoints |
|
||||
| S2 | Rate limits per bearer token |
|
||||
| S3 | Audit log table for create/access/deny |
|
||||
| S4 | Admin token mint/revoke via BE RBAC (shared operator session) — see DR |
|
||||
| S5 | Unit tests (PHP) + curl-based smoke script in `scripts/` |
|
||||
|
||||
---
|
||||
|
||||
## 4. Architecture
|
||||
|
||||
```text
|
||||
Client (curl, BE job, future app)
|
||||
│ HTTPS shorten API
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ FE — Gentoo (s.f0xx.org TLS) │
|
||||
│ nginx reverse_proxy │
|
||||
└──────────────┬──────────────────────┘
|
||||
│ proxy_pass → BE :80
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ BE — Alpine │
|
||||
│ nginx → php-fpm (url-shortener) │
|
||||
│ MariaDB (url_shortener schema) │
|
||||
└─────────────────────────────────────┘
|
||||
|
||||
Browser / QR scan
|
||||
│ GET https://s.f0xx.org/{slug}
|
||||
▼
|
||||
same path → lookup → 302 Location: origin
|
||||
```
|
||||
|
||||
- **Not** mounted under `/app/androidcast_project/crashes/` — dedicated vhost `s.f0xx.org` (DR §Deployment).
|
||||
- **Opt-in deploy:** absent from default BE sync until operator enables submodule + nginx fragment.
|
||||
|
||||
---
|
||||
|
||||
## 5. Authentication and trust
|
||||
|
||||
Normative decisions: [DR §Authentication and trust](../DRs/20100611_3_url_shortener.md#authentication-and-trust).
|
||||
|
||||
| Concept | Spec |
|
||||
|---------|------|
|
||||
| **bearer** | Project-scoped API token (`varchar(128)`), stored hashed in DB |
|
||||
| **signer** | Optional second secret proving **elevated** rights (permanent TTL, high quotas) |
|
||||
| **demo token** | Pre-seeded read-only or low-quota token for smoke tests |
|
||||
| **App-wide session** | Human operators mint/revoke bearer tokens via BE console using existing `ac_crash_sess` RBAC — not used for machine `curl` shorten calls |
|
||||
|
||||
**Trust checks (shorten):**
|
||||
|
||||
1. `bearer` required → `401` if missing/unknown/revoked.
|
||||
2. Lookup existing row for `(bearer_id, normalized_origin)`:
|
||||
- If found and not expired → return cached short URL; `ttl` in response = **remaining seconds**.
|
||||
- If found but expired → `409` / error code `TTL_EXPIRED` (do not extend without new shorten).
|
||||
3. If not found → require **whitelist/trusted** bearer for create.
|
||||
4. If `ttl=0` (permanent) → require valid **signer** with `can_permanent` capability.
|
||||
5. If `ttl>0` → bearer must have `can_temporary` (default for trusted tokens).
|
||||
|
||||
---
|
||||
|
||||
## 6. REST API
|
||||
|
||||
Base: `https://s.f0xx.org/api/v1`
|
||||
|
||||
### 6.1 Shorten — GET
|
||||
|
||||
```http
|
||||
GET /api/v1/shorten?url={URL_ENCODED}&bearer={TOKEN}&signer={SIGNER}&ttl={TTL}
|
||||
```
|
||||
|
||||
| Param | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `url` | yes | URL-encoded long URL (max 2048 chars after decode) |
|
||||
| `bearer` | yes | API token |
|
||||
| `signer` | if `ttl=0` | Signer secret for permanent links |
|
||||
| `ttl` | no | Seconds until expiry; `0` = permanent (signer required); default `86400` |
|
||||
|
||||
### 6.2 Shorten — POST
|
||||
|
||||
```http
|
||||
POST /api/v1/shorten
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"url": "https://defender.net/very/long/link#with_anchors¶m1=value1",
|
||||
"bearer": "4abef433d23ab5x&&83^c6",
|
||||
"signer": "optional-signer-key",
|
||||
"ttl": 3600
|
||||
}
|
||||
```
|
||||
|
||||
Prefer POST when URL length or sensitive query strings make GET impractical.
|
||||
|
||||
### 6.3 Error response
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "ACCESS_DENIED",
|
||||
"url": "https://defender.net/...",
|
||||
"bearer": "4abef…",
|
||||
"desc": "access denied"
|
||||
}
|
||||
```
|
||||
|
||||
| `code` | HTTP | Meaning |
|
||||
|--------|------|---------|
|
||||
| `0` | 200 | Success (success body below) |
|
||||
| `ACCESS_DENIED` | 403 | Bearer not trusted / revoked |
|
||||
| `MISSING_BEARER` | 401 | No bearer |
|
||||
| `INVALID_URL` | 400 | Malformed or disallowed scheme |
|
||||
| `TTL_EXPIRED` | 409 | Cached link expired |
|
||||
| `SIGNER_REQUIRED` | 403 | `ttl=0` without valid signer |
|
||||
| `RATE_LIMITED` | 429 | Quota exceeded |
|
||||
| `INTERNAL` | 500 | Server error |
|
||||
|
||||
### 6.4 Success response
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "0",
|
||||
"url": "https://defender.net/very/long/link#with_anchors¶m1=value1",
|
||||
"u": "https://s.f0xx.org/a1b2c3d4e5f6",
|
||||
"bearer": "4abef433d23ab5x&&83^c6",
|
||||
"ttl": "3542"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `url` | Original URL (normalized canonical form) |
|
||||
| `u` | Short URL |
|
||||
| `bearer` | Echo of bearer (truncated in logs only) |
|
||||
| `ttl` | Remaining seconds; `"0"` if permanent |
|
||||
|
||||
Optional fields (v1.1 suggestion in DR): `qr` — URL to QR PNG.
|
||||
|
||||
### 6.5 QR
|
||||
|
||||
```http
|
||||
GET /api/v1/qr/{slug}.png?size=256
|
||||
```
|
||||
|
||||
Returns `image/png` encoding `https://s.f0xx.org/{slug}`. `404` if slug unknown; `410` if expired.
|
||||
|
||||
---
|
||||
|
||||
## 7. Redirect and permalink behaviour
|
||||
|
||||
| Request | Behaviour |
|
||||
|---------|-----------|
|
||||
| `GET /{slug}` | `302 Found` → `Location: origin`; update `accessed_at` |
|
||||
| Expired slug | `410 Gone` HTML or JSON if `Accept: application/json` |
|
||||
| Unknown slug | `404 Not Found` |
|
||||
| `HEAD /{slug}` | Same as GET without body |
|
||||
|
||||
**Permalink:** For a given `(bearer, normalized_origin)`, the **same slug** is returned on repeat shorten while the row is valid. New slug only after expiry + delete or explicit admin purge.
|
||||
|
||||
**URL normalization (before hash/store):**
|
||||
|
||||
- Trim whitespace
|
||||
- Lowercase scheme and host
|
||||
- Preserve path, query, fragment
|
||||
- Reject non-`http`/`https` schemes
|
||||
|
||||
---
|
||||
|
||||
## 8. Slug algorithm
|
||||
|
||||
**Normative (per DR):** SHA-256 truncated — **not** Java `String.hashCode` or character interleaving from the draft.
|
||||
|
||||
```
|
||||
normalized = normalize_url(url)
|
||||
material = bearer_id + "\x1f" + normalized
|
||||
digest = SHA256(material)
|
||||
slug = lowercase_hex(digest[0:6]) // 12 hex chars → ~48 bits
|
||||
short_url = "https://s.f0xx.org/" + slug
|
||||
```
|
||||
|
||||
**Collision handling:** On insert conflict on `slug`, append `1`…`f` suffix (max 4 chars) or re-hash with salt counter — see DR §Slug algorithm.
|
||||
|
||||
**Reverse lookup:** Primary key lookup on `slug` column (indexed unique). No recomputation at redirect time.
|
||||
|
||||
_Draft note preserved in DR §Slug algorithm — interleave + Java hash rejected for production._
|
||||
|
||||
---
|
||||
|
||||
## 9. Database schema
|
||||
|
||||
Database: `url_shortener` (separate schema or DB — DR §Data store).
|
||||
|
||||
### 9.1 `links`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | `BIGINT UNSIGNED AUTO_INCREMENT` | PK |
|
||||
| `bearer_id` | `INT UNSIGNED` | FK → `bearer_tokens.id` |
|
||||
| `signer_id` | `INT UNSIGNED NULL` | FK → `signer_keys.id` if used |
|
||||
| `slug` | `VARCHAR(16)` | Unique, indexed |
|
||||
| `origin` | `TEXT` | Normalized original URL |
|
||||
| `origin_hash` | `CHAR(64)` | SHA-256 of origin (dedup aid) |
|
||||
| `ttl_seconds` | `INT UNSIGNED` | `0` = permanent |
|
||||
| `expires_at` | `DATETIME NULL` | NULL when permanent |
|
||||
| `created_at` | `DATETIME` | |
|
||||
| `updated_at` | `DATETIME` | |
|
||||
| `accessed_at` | `DATETIME NULL` | Last redirect |
|
||||
| `access_count` | `BIGINT UNSIGNED` | Default 0 |
|
||||
|
||||
Unique index: `(bearer_id, origin_hash)` where `expires_at IS NULL OR expires_at > NOW()` — implementation via application check + partial unique strategy per MariaDB version (DR).
|
||||
|
||||
### 9.2 `bearer_tokens`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | `INT UNSIGNED AUTO_INCREMENT` | |
|
||||
| `token_hash` | `CHAR(64)` | SHA-256 of bearer secret |
|
||||
| `label` | `VARCHAR(128)` | Operator label |
|
||||
| `trusted` | `TINYINT(1)` | Whitelist flag |
|
||||
| `can_temporary` | `TINYINT(1)` | Default 1 for trusted |
|
||||
| `can_permanent` | `TINYINT(1)` | Default 0; needs signer anyway |
|
||||
| `rate_limit_per_hour` | `INT UNSIGNED` | Default 1000 |
|
||||
| `revoked_at` | `DATETIME NULL` | |
|
||||
| `created_at` | `DATETIME` | |
|
||||
|
||||
### 9.3 `signer_keys`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | `INT UNSIGNED AUTO_INCREMENT` | |
|
||||
| `key_hash` | `CHAR(64)` | SHA-256 of signer secret |
|
||||
| `label` | `VARCHAR(128)` | |
|
||||
| `can_permanent` | `TINYINT(1)` | |
|
||||
| `revoked_at` | `DATETIME NULL` | |
|
||||
|
||||
### 9.4 `audit_log`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | `BIGINT UNSIGNED AUTO_INCREMENT` | |
|
||||
| `event` | `VARCHAR(32)` | `shorten`, `redirect`, `deny`, … |
|
||||
| `bearer_id` | `INT UNSIGNED NULL` | |
|
||||
| `slug` | `VARCHAR(16) NULL` | |
|
||||
| `ip` | `VARCHAR(45)` | |
|
||||
| `detail` | `TEXT` | |
|
||||
| `created_at` | `DATETIME` | |
|
||||
|
||||
---
|
||||
|
||||
## 10. QR codes
|
||||
|
||||
- Library: **endroid/qr-code** (MIT) or pure-PHP equivalent — license entry required in BE licenses file before merge.
|
||||
- Default size: 256×256 PNG, ECC level M.
|
||||
- Content: full short URL string (`https://s.f0xx.org/{slug}`).
|
||||
- Cache: optional filesystem cache keyed by slug+size (DR suggestion).
|
||||
|
||||
---
|
||||
|
||||
## 11. OpenAPI
|
||||
|
||||
Ship `backend/url-shortener/openapi.yaml` covering:
|
||||
|
||||
- `POST /api/v1/shorten`
|
||||
- `GET /api/v1/shorten` (documented; mark `POST` preferred)
|
||||
- `GET /api/v1/qr/{slug}.png`
|
||||
- `GET /{slug}` redirect (document as `GET /r/{slug}` if internal router separates API — DR §Routing)
|
||||
|
||||
Serve docs at `GET /api/v1/docs` (Swagger UI static bundle or Redoc) — opt-in on staging only.
|
||||
|
||||
---
|
||||
|
||||
## 12. Deployment
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Submodule path | `backend/url-shortener` |
|
||||
| Remote | `git://f0xx.org/androidcast_project/url-shortener` (when available) |
|
||||
| BE docroot | `/var/www/localhost/htdocs/apps/s/` (suggested — confirm in DR) |
|
||||
| nginx | New fragment `url-shortener.conf`; FE `server_name s.f0xx.org` |
|
||||
| PHP-FPM | Same pool as other BE apps or dedicated pool if load warrants |
|
||||
| MariaDB | Schema `url_shortener` |
|
||||
| Enable | Operator copies submodule, runs migration, includes nginx fragment, reload |
|
||||
|
||||
**Not required** for alpha BE validation script `validate_be_services.sh` until service is enabled.
|
||||
|
||||
---
|
||||
|
||||
## 13. Security
|
||||
|
||||
| Topic | Rule |
|
||||
|-------|------|
|
||||
| Secrets at rest | Store only hashes of bearer/signer |
|
||||
| TLS | Required on public FE |
|
||||
| SSRF | Block shorten targets to `127.0.0.0/8`, `10.0.0.0/8`, `169.254.0.0/16`, `metadata` hosts |
|
||||
| Open redirect | Only stored origins issued by trusted bearers |
|
||||
| Logging | Never log full bearer/signer; truncate to 6 chars |
|
||||
| CSRF | N/A for token-based API; admin UI uses session + CSRF token |
|
||||
|
||||
Optional HMAC request signing for GET — DR §Suggestions.
|
||||
|
||||
---
|
||||
|
||||
## 14. Observability and limits
|
||||
|
||||
| Limit | Default |
|
||||
|-------|---------|
|
||||
| Max URL length | 2048 |
|
||||
| Max slug length | 16 |
|
||||
| Rate limit | 1000 shorten/hour per bearer |
|
||||
| Audit retention | 90 days (cron purge script) |
|
||||
|
||||
Metrics (suggestion): redirect count, 4xx/5xx rate — post-v1 Prometheus or nginx logs.
|
||||
|
||||
---
|
||||
|
||||
## 15. Tests
|
||||
|
||||
| Layer | Location |
|
||||
|-------|----------|
|
||||
| PHP unit | `backend/url-shortener/tests/` — normalize, slug, TTL math, auth |
|
||||
| Smoke | `backend/url-shortener/scripts/smoke_shorten.sh` |
|
||||
| BE validation | Optional line in `validate_be_services.sh` when deployed |
|
||||
|
||||
---
|
||||
|
||||
## 16. Out of scope (v1)
|
||||
|
||||
- Link editing after creation
|
||||
- Custom slugs chosen by user
|
||||
- Analytics UI
|
||||
- Integration inside Android APK (use HTTP from BE or scripts first)
|
||||
|
||||
---
|
||||
|
||||
## 17. Related docs
|
||||
|
||||
- [DR — design review](../DRs/20100611_3_url_shortener.md)
|
||||
- [INFRA.md](../INFRA.md) — FE → BE proxy
|
||||
- [20260610_SERVICES.md](../20260610_SERVICES.md) — service catalog (add row when live)
|
||||
- [ROADMAP.md](../ROADMAP.md) — post-alpha track
|
||||
540
docs/specs/20100611_3_url_shortener.pdf
Normal file
540
docs/specs/20100611_3_url_shortener.pdf
Normal file
@@ -0,0 +1,540 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R /F3 32 0 R /F4 34 0 R /F5 35 0 R /F6 36 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 37 0 R /Fit ] /Rect [ 45.68504 716.2874 84.37304 727.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 37 0 R /Fit ] /Rect [ 45.68504 705.2874 77.26104 716.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 37 0 R /Fit ] /Rect [ 45.68504 694.2874 104.373 705.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 37 0 R /Fit ] /Rect [ 59.68504 683.8499 88.44754 693.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 37 0 R /Fit ] /Rect [ 59.68504 673.8499 95.54254 683.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 37 0 R /Fit ] /Rect [ 45.68504 663.2874 97.26104 674.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 38 0 R /Fit ] /Rect [ 45.68504 652.2874 138.629 663.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
11 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 38 0 R /Fit ] /Rect [ 45.68504 641.2874 91.03704 652.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
12 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 38 0 R /Fit ] /Rect [ 59.68504 630.8499 125.55 640.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
13 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 38 0 R /Fit ] /Rect [ 59.68504 620.8499 130.5525 630.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
14 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 38 0 R /Fit ] /Rect [ 59.68504 610.8499 121.7925 620.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
15 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 39 0 R /Fit ] /Rect [ 59.68504 600.8499 133.47 610.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
16 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 39 0 R /Fit ] /Rect [ 59.68504 590.8499 83.44504 600.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
17 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 39 0 R /Fit ] /Rect [ 45.68504 580.2874 174.181 591.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
18 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 39 0 R /Fit ] /Rect [ 45.68504 569.2874 105.709 580.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
19 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 39 0 R /Fit ] /Rect [ 45.68504 558.2874 119.053 569.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
20 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 39 0 R /Fit ] /Rect [ 59.68504 547.8499 87.19504 557.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
21 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 40 0 R /Fit ] /Rect [ 59.68504 537.8499 120.135 547.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
22 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 40 0 R /Fit ] /Rect [ 59.68504 527.8499 112.2075 537.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
23 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 40 0 R /Fit ] /Rect [ 59.68504 517.8499 102.63 527.8499 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
24 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 40 0 R /Fit ] /Rect [ 45.68504 507.2874 94.59704 518.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
25 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 40 0 R /Fit ] /Rect [ 45.68504 496.2874 91.49304 507.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
26 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 41 0 R /Fit ] /Rect [ 45.68504 485.2874 101.709 496.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
27 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 41 0 R /Fit ] /Rect [ 45.68504 474.2874 87.92504 485.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
28 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 41 0 R /Fit ] /Rect [ 45.68504 463.2874 141.269 474.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
29 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 41 0 R /Fit ] /Rect [ 45.68504 452.2874 78.58904 463.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
30 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 41 0 R /Fit ] /Rect [ 45.68504 441.2874 120.389 452.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
31 0 obj
|
||||
<<
|
||||
/Border [ 0 0 0 ] /Contents () /Dest [ 41 0 R /Fit ] /Rect [ 45.68504 430.2874 105.717 441.2874 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
32 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
33 0 obj
|
||||
<<
|
||||
/Annots [ 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R
|
||||
14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R
|
||||
24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R ] /Contents 74 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 73 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0
|
||||
/Trans <<
|
||||
|
||||
>> /Type /Page
|
||||
>>
|
||||
endobj
|
||||
34 0 obj
|
||||
<<
|
||||
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
35 0 obj
|
||||
<<
|
||||
/BaseFont /Symbol /Name /F5 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
36 0 obj
|
||||
<<
|
||||
/BaseFont /ZapfDingbats /Name /F6 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
37 0 obj
|
||||
<<
|
||||
/Contents 75 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 73 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
38 0 obj
|
||||
<<
|
||||
/Contents 76 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 73 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
39 0 obj
|
||||
<<
|
||||
/Contents 77 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 73 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
40 0 obj
|
||||
<<
|
||||
/Contents 78 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 73 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
41 0 obj
|
||||
<<
|
||||
/Contents 79 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 73 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
42 0 obj
|
||||
<<
|
||||
/Outlines 44 0 R /PageMode /UseNone /Pages 73 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
43 0 obj
|
||||
<<
|
||||
/Author (Android Cast project) /CreationDate (D:20260611175417+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260611175417+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (URL shortener service \204 specification) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
44 0 obj
|
||||
<<
|
||||
/Count 31 /First 45 0 R /Last 72 0 R /Type /Outlines
|
||||
>>
|
||||
endobj
|
||||
45 0 obj
|
||||
<<
|
||||
/Dest [ 37 0 R /Fit ] /Next 46 0 R /Parent 44 0 R /Title (1. Purpose)
|
||||
>>
|
||||
endobj
|
||||
46 0 obj
|
||||
<<
|
||||
/Dest [ 37 0 R /Fit ] /Next 47 0 R /Parent 44 0 R /Prev 45 0 R /Title (2. Scope)
|
||||
>>
|
||||
endobj
|
||||
47 0 obj
|
||||
<<
|
||||
/Count 2 /Dest [ 37 0 R /Fit ] /First 48 0 R /Last 49 0 R /Next 50 0 R /Parent 44 0 R
|
||||
/Prev 46 0 R /Title (3. Requirements)
|
||||
>>
|
||||
endobj
|
||||
48 0 obj
|
||||
<<
|
||||
/Dest [ 37 0 R /Fit ] /Next 49 0 R /Parent 47 0 R /Title (3.1 Must)
|
||||
>>
|
||||
endobj
|
||||
49 0 obj
|
||||
<<
|
||||
/Dest [ 37 0 R /Fit ] /Parent 47 0 R /Prev 48 0 R /Title (3.2 Should)
|
||||
>>
|
||||
endobj
|
||||
50 0 obj
|
||||
<<
|
||||
/Dest [ 37 0 R /Fit ] /Next 51 0 R /Parent 44 0 R /Prev 47 0 R /Title (4. Architecture)
|
||||
>>
|
||||
endobj
|
||||
51 0 obj
|
||||
<<
|
||||
/Dest [ 38 0 R /Fit ] /Next 52 0 R /Parent 44 0 R /Prev 50 0 R /Title (5. Authentication and trust)
|
||||
>>
|
||||
endobj
|
||||
52 0 obj
|
||||
<<
|
||||
/Count 5 /Dest [ 38 0 R /Fit ] /First 53 0 R /Last 57 0 R /Next 58 0 R /Parent 44 0 R
|
||||
/Prev 51 0 R /Title (6. REST API)
|
||||
>>
|
||||
endobj
|
||||
53 0 obj
|
||||
<<
|
||||
/Dest [ 38 0 R /Fit ] /Next 54 0 R /Parent 52 0 R /Title (6.1 Shorten \204 GET)
|
||||
>>
|
||||
endobj
|
||||
54 0 obj
|
||||
<<
|
||||
/Dest [ 38 0 R /Fit ] /Next 55 0 R /Parent 52 0 R /Prev 53 0 R /Title (6.2 Shorten \204 POST)
|
||||
>>
|
||||
endobj
|
||||
55 0 obj
|
||||
<<
|
||||
/Dest [ 38 0 R /Fit ] /Next 56 0 R /Parent 52 0 R /Prev 54 0 R /Title (6.3 Error response)
|
||||
>>
|
||||
endobj
|
||||
56 0 obj
|
||||
<<
|
||||
/Dest [ 39 0 R /Fit ] /Next 57 0 R /Parent 52 0 R /Prev 55 0 R /Title (6.4 Success response)
|
||||
>>
|
||||
endobj
|
||||
57 0 obj
|
||||
<<
|
||||
/Dest [ 39 0 R /Fit ] /Parent 52 0 R /Prev 56 0 R /Title (6.5 QR)
|
||||
>>
|
||||
endobj
|
||||
58 0 obj
|
||||
<<
|
||||
/Dest [ 39 0 R /Fit ] /Next 59 0 R /Parent 44 0 R /Prev 52 0 R /Title (7. Redirect and permalink behaviour)
|
||||
>>
|
||||
endobj
|
||||
59 0 obj
|
||||
<<
|
||||
/Dest [ 39 0 R /Fit ] /Next 60 0 R /Parent 44 0 R /Prev 58 0 R /Title (8. Slug algorithm)
|
||||
>>
|
||||
endobj
|
||||
60 0 obj
|
||||
<<
|
||||
/Count 4 /Dest [ 39 0 R /Fit ] /First 61 0 R /Last 64 0 R /Next 65 0 R /Parent 44 0 R
|
||||
/Prev 59 0 R /Title (9. Database schema)
|
||||
>>
|
||||
endobj
|
||||
61 0 obj
|
||||
<<
|
||||
/Dest [ 39 0 R /Fit ] /Next 62 0 R /Parent 60 0 R /Title (9.1 links)
|
||||
>>
|
||||
endobj
|
||||
62 0 obj
|
||||
<<
|
||||
/Dest [ 40 0 R /Fit ] /Next 63 0 R /Parent 60 0 R /Prev 61 0 R /Title (9.2 bearer_tokens)
|
||||
>>
|
||||
endobj
|
||||
63 0 obj
|
||||
<<
|
||||
/Dest [ 40 0 R /Fit ] /Next 64 0 R /Parent 60 0 R /Prev 62 0 R /Title (9.3 signer_keys)
|
||||
>>
|
||||
endobj
|
||||
64 0 obj
|
||||
<<
|
||||
/Dest [ 40 0 R /Fit ] /Parent 60 0 R /Prev 63 0 R /Title (9.4 audit_log)
|
||||
>>
|
||||
endobj
|
||||
65 0 obj
|
||||
<<
|
||||
/Dest [ 40 0 R /Fit ] /Next 66 0 R /Parent 44 0 R /Prev 60 0 R /Title (10. QR codes)
|
||||
>>
|
||||
endobj
|
||||
66 0 obj
|
||||
<<
|
||||
/Dest [ 40 0 R /Fit ] /Next 67 0 R /Parent 44 0 R /Prev 65 0 R /Title (11. OpenAPI)
|
||||
>>
|
||||
endobj
|
||||
67 0 obj
|
||||
<<
|
||||
/Dest [ 41 0 R /Fit ] /Next 68 0 R /Parent 44 0 R /Prev 66 0 R /Title (12. Deployment)
|
||||
>>
|
||||
endobj
|
||||
68 0 obj
|
||||
<<
|
||||
/Dest [ 41 0 R /Fit ] /Next 69 0 R /Parent 44 0 R /Prev 67 0 R /Title (13. Security)
|
||||
>>
|
||||
endobj
|
||||
69 0 obj
|
||||
<<
|
||||
/Dest [ 41 0 R /Fit ] /Next 70 0 R /Parent 44 0 R /Prev 68 0 R /Title (14. Observability and limits)
|
||||
>>
|
||||
endobj
|
||||
70 0 obj
|
||||
<<
|
||||
/Dest [ 41 0 R /Fit ] /Next 71 0 R /Parent 44 0 R /Prev 69 0 R /Title (15. Tests)
|
||||
>>
|
||||
endobj
|
||||
71 0 obj
|
||||
<<
|
||||
/Dest [ 41 0 R /Fit ] /Next 72 0 R /Parent 44 0 R /Prev 70 0 R /Title (16. Out of scope \(v1\))
|
||||
>>
|
||||
endobj
|
||||
72 0 obj
|
||||
<<
|
||||
/Dest [ 41 0 R /Fit ] /Parent 44 0 R /Prev 71 0 R /Title (17. Related docs)
|
||||
>>
|
||||
endobj
|
||||
73 0 obj
|
||||
<<
|
||||
/Count 6 /Kids [ 33 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
74 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1029
|
||||
>>
|
||||
stream
|
||||
Gatn&Df=)Y&B<Vr;r#P*J[(b*Is<nS5tFB/QLJcijX+jYR`<0e#5e+!\h_pF:$uOlC8Jqln&bBQpDa9m+1D'L[K%CBGlO04j%un/`?^3/*+n<[cQfl*)%2U<K#WnL.A9T[j*c0?d#:I=!YXT$9f!oki-EP^XH2k=llM?W\O?W+-7/lY'7`e&7O,QPeo:Qcl5XW8NDmMG@$S9:.g=OpVdKa0[B$a!Y1<IWPWtgg"7I;fO)coDo9A<h&b/Z&TI`kU4T9a[3?mHX1n3!)=QoCtq27:lq<Sc"*tR):GHr/+q%bMadGM]q2XB[m^WKoF.T4-iF=6bG"VJ@Ri#dD;n)Mao`W*H4b>*Up6)ApU)eV;:@a#AG7t2bOm39YqF@KY%)N9sP^sXVFQ4kYr4a95US;2\c"Kp8#Yr-:^([!&*SBYO$s!96/GA4Z*^u3s\SHh.a:$,8/HL,AWG4M*gIPTn;JQ#K)X](K(NR)JM)N$,.4h:iAH=*sCp,+hi(]PloV'EgXRh[6>cG+[Eq;L+qp-$6N/+1R@?#fct.m6k9cmnfX(%[@#7g9DS,D)=<g43%Dd(N6"'7"7#0be+t.;KGkObHgZi:>$CRqA@S5rob<)Ja9QFpE"`Ah1L%hnW,Intss4n^*JqU%DmJJ\#h1Z<M+n\n:7o(J-$$:>ef,\Wg-GXW"U4*r,MLr(BVC@r7Sk^nVL!-o@CQ[S*C>_Wd'FI3e=/&gFRI)VsF_UMbJ_Yg<G\OCR+X7An`YGUe;j5"b>:UYi/<a*YjP34"HZo3tP0="B69?uD1%h)nV'Vf*sCkCJ)9qg&a3VR'+LLaD37"!#U4iJ7IMb@rYnbI0e7Y3L2t[e4XSEB#&[d"mt0H;VSj/j-4S0tM)%ZI>It"5s^\)?+(dXQ?]mcg.B*2:T5.#ls-6@2XE8nr,;gfm^afVWd6k'O>+uXjdU6PYml'[YP]okna#G2;-RXfcSuBA>+5G!e+jiJ[U@0fk7g/eLtJb\4/N`j"TDnGg.\db78Tr_Im$BkgeYU%lWIhQ2~>endstream
|
||||
endobj
|
||||
75 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3500
|
||||
>>
|
||||
stream
|
||||
Gau0F>B?9+&q801ka/E7:M^0O<DMq6'Dj%8lPr!LcC:h3#f[K3@Q7V?frb5AgC42bP.8Td.#Epqkaq&'l2JDg;*`-^P5Xic!'\\:r4<";KD>TtR\[dt(2i:'rdLb]HYU-&=LtSbBj$V%:,I>/S+/qDPf4mgjr7--qK[u3)-A.\c9BV_7b>LK2`sVRGn'T2)9dSBHF$p&R,-YIb.aAF7`rL+kKj3&99N'k^eCPd_nh?!LLa2q/"CjS28s&P/$`t>aq+/8)YlVW[>5G:n46/XOre*ibVpAqa5/:rLd-1_q=#N1V_'.K\31%pRr9ui@OGm&2mRcB-Ps35Y&.:"!re>_>*;C;IC2'3!+FrFa@fiJA%t@Cp)Xikp6%:\]E-%_D$QQM^9IJC?SbHdmu0JN)TLV:^2_at2a+OK`p(LT:RAfY^`&3?2h:9OC<3!u8J9%0j)HV5a=f30LfQ[Y`q7_31Zb<\d'k#jS$ZsU9A=aQJNH?i]-t@rq9EHVne-c-j[QbZcO1V:hHl.PZs^IBJ$j*-W^V2HPJSMR4)cG'\W.[X5JfthjA+,aU;^7W,;RB*23WuD\:ZQ!/5oSQ3mdlS[?_;k`+7O^g;1!qfqG[4MF-XM=L!bU>[9ffK)7A37EiBAC1&/DVFI]"MmDNgOa-HPE_X%21s.A!%Qn]`b?amYH5hJB^uZM?X/:A/L^8A?X<=dC'i/9Fk%bG]O.JImdp<H+;uE*]iP(qH*0I<Ogk6ib't>BjS"5?`7OPjk]`^NmRq2%Y"L7IlH"!A.K0BCB'7""!T>O$bU_/BA=F02;n_2!<#@BU6M_eHhAjRUo\j;56]Qh6-L#Y\\F&OH[R<f07pc4F^=<HRj=^&SEm(mOC)PGk&?bFqc_T-'HB4JR?Tah/@l`72!&(.G\_!sQ[hi!U;]Y^$2r1;.2!G^aCgI1u.e;Lhpp7bo>N$d6G82?KU5jN")No&1(:)MO*_d9njeb7d_jZ^'m\t,R^"Gu<o$'4]UZfbfP3!)a5$QhYY0[B%j[bSn9:B\,p^[=ag/:Q5K3Na:#G@<\M?(CSU^S7@7E,'es4k[R4As6#b1rbO?6p,h7JOLRW5+kX5fZkB?R&q8$?LW32mAYo^Ta9GUiVo5#rRkiUZ?H*0BbK3o@bPQOD<[]T990#cqs!C\8(-"Ne<>/;\hUcO\jIYAI0]0#XZTj0nm-?ik;C3a>fkd,rROnJ,g.m%k7?C9PT9rtIrtFQK8)EJ$A7iSU4[&m'ciHR0i_:@;C@38=s04oRqN/>=b*\MBQ+WA.Em;MbR>0sIm]@jflk2u%WmggE>gZ`DI50k::WDI6*<95P`%-2j^Pt_\J^N=\S!C.O%$$UafTp"M"I*c8`k\5"3aEJT'0ob8dfrR.[(:o3(:elRSZOm;,``KqrIMT%C:h5V5bR#7,Zdd%60'j$SNnq("*B3-!h,rb&AOd?=-)+;'TNbpKt5]e&NTKLNJfdQjD`Z*HP?Z!g.AB<07O!2H7TdAi>FFqlfK1mP(TeD^ob=N#m1=Bt2#U)!f-F<GZJ.."mi:G[S!/6Vgd<C8"XQ%fs\j(]=*RN]d48,K'mdL$hMA=8*$#e0?:egOI2$*eTA'Z4qJ(S3j6k%d"oI4t0Bkftb-\g=VVld3C=UALTaM2nYQ8(8Dj'S-+Ab5*!b='Dd7dhpNpi]JKUu\GJseeIGfS'1;^`/TYjJ077]u&OrJ$+-Hd/Jkon]!n96?\QBH&kqglKKD;"LK\pbPS((4rh"`IR>&=X/<5\GZ&4M0I5<idr8shLA4IFfKXPQd%hf:b4]D`Tb0S:,b5i?l4WrIYMI_M!sAnnmYc(EJ2mgu9W(V+gtb=_Qj_^oSbX5D+.YN9')8rpl!0X4[tn>U*:jBZK8VkO2jG,553M?ZbtPm2Uc)Xs`SJ:EI]bd!E;I**BFS)gV8K.\7SKDp<.GA/B*cA?\6QD80;%NTY9^TKt\3Q2H5C+G8q?]bXZf]^c)ddOJ>=N!X]^(0!%;,aKF&b/:^QgHptaE_Vg0Tucp.0Q0QE<VrS(ir`f6.)<DF)hcBdurH<QtG3nM74Xqh[?&kRO+AVHFu;4.7W!!+?i4T1<nJqV7O0`0^=<3dq*C\K2j=hckghA$@Kb@f855";_)$4d'-t!`/Z^"WT[?\M"^%h0=ueDU0&d6SobLr3XBUbjI5Y5_:7O\V_A4\Ri*p2Wk&k&.sdhh<(H!#I<+.OZ'?>L1@eJQ_&6seoU(W<*o5qu&5rb\DmB3FN-\:T(L_Lb;cZM2i"Ye0VGpoW9ljMhM%Hp)?Hl)cK`B'uo,d][C5e5UZp>ebLC;X,_KqTF&DJ9oQ?^0[pkW^k)G7m],oLCM@PMVj9r`p"TdRP-)o((ZUZ8+GZiDnPKKPV;:"=.99TJ:h(2n160:S7M.Ubn%NN&I-]4G6BX4*Aj1qJ*c]@dVk&e4*r_""?-L4F*,2b2c>cGq#>fAionb$hF7Zj$WfWRc^Fl\!=%[WWPBang&1Oj2I&b/9[6fp]fGNSZhQ6L)tOZK>u4&AC/Y(!Xq!dOaf*K_LD2S@!.2S0(A1nbL;bknuWTqk9rcgDYG1/#h^g,11*3ESem/MJc);n+3GBA?YqnTBWNL=W)1\lrj-J'2WQI.\oC;iJb:!0JtM([[9f1*`^F%dhPdXHcCG^gT]?W,^^8_aGsT9OI<k:QukPLG=dtms-/uWhbi<j[#mF]olDUGB3_skT@iZ0OO\u6%[K]jqr0kQSCd=A:5<7G.d0uiW.NEg2?jt3VN]kAH9foGdO@?ihA>0+%JM(&k8T(INH5*+E=U?.2`/IQ12Db?"N6.Qck#]"!OO'I7cQX+flO+T2(u!JUYHLn=&Sl2!f3.Aag,@I.g>9#*j,ZG"VIl92X+t1W[i4p^+1?<AZ7R:]->+f2Wi+VP>tOlmIZdR_3qpgBJh*i:5<"'<2dDq"fi@f(i-6+'P9=.?=6HhZq8HGN\W?4%OY(U#.(^.rSukG+L?mk.XF.sXCZPsm:`C;m;t/;[hHZsK_)=D1oBgCi.](FrX)hBW.PUkHE]`r&1ME!VR&+TURNSea$@=6FYH*MY6`j\rmdeJGI%B)A;P.P>^C+Bnc45Y.(lu$dghNM0MW.-3*b__fe6kNp\\]:n)hHi!aDuP'Q1G1X46eT9Z;+5BBeS,,l+j"Z0aM5NlO\p?@OChRe:&am=u03^8Qt'O>0iU%)7&,=h,JFkLgaRr^M_.#p8*IZ`,'%rHS!lMQLt^BQr2:Oa.oknL/8^`OnUfE[Gp1R/Ib4(pb9jWf'mED:QB&>g4P+['LkrNcTIVF@5>&Bu^@$2e7[@[M771)IR3)NGAA</;%*)Rf1K[8>b`t(s#[p?qRJO#]4da;$/P-ncj1jkj2b?[\MbIO`cL890Eb;A;1t.D7R&&QgFljp6l+cOgF`oma/q_m[Ci';tZ4KLd)Fm9opm]gQ?p&]-kSr,n5eQqH1]SDgpf,*:TIVhG:C-YNXN>!/ppXCn8tP9*n34XaLZ3'qZA.8`T+8CMe7amXMT^*cM~>endstream
|
||||
endobj
|
||||
76 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3122
|
||||
>>
|
||||
stream
|
||||
Gb!#^lVHQ`(&uQd0qp-)[i<@/^U^'RO5.FUP\!E9og@5BYU@AI?[/)+MYQ(U`in$TSN(&YQITklB'.<_TAMY8$&S.A9)c4\KJ(;Yn=,(VN5dY!.>!/k[qI`bH9%.oSVen1L+4ZDdptb/9a/f_rUE(GZQSg0*)la+7-$m4[RX+`5Z'!`Og$:5B^EP]5_/kq6O(<"LZ%>0NISa$<aCQgYH%oJ^MN?1]DZiLf=2JrrG@?;)!gB9p#@/[P9(BLinKq66r1$s1c\"h:U<;NJH>emn_[/2QS`/n)FhQt$IsShIJ;ohg%tn64d8Jo%1-P?6++:6JlSr7m,\eL).Ko(R?.A8+1oaEm(s2PT@U.Ae#^4<_tI,[3"Pnmd"@E^6uJXAK.*T\NREXXY;,ou&io%L1;.g6gkqIVV]NssnUDMiM)/I-_(,W-#3bpdE(Bh`S*h(;kj*2Hk&DBHZb^%?J-PJd>s*OW+Pc8SE/"scU6*=brE[2JK',S4s#F6#kVfY9lid;*$6f^;0UDhjd.cZ@,RV+8l+@3XDbrk'B,b1"[0S6%:.p]po$?'dmMVOe]Q=1`P-3u"i,l5J(Z]nc[eGk"/:=CCB]7]RgHn"c=WNSX*"_"f!lq_TMg;c16*5B>7>/o7)q:i5;/]Cf!EB-IjZHpjS`ne_)lJ&7%mWJ/YA*2).h=,ZjA`)AYusr0=_RBh.-b"RpDk>$iI%0Gq';"0=@9DCn6jFu/DQVj4LXf4csuFP-jXNTiZLS;-]]lkLb0[]^g=Mbr@(B8Z\U_`#s68,$)YAS;+)^;J#QilM$oAa.QU=rbHe/M\lcml@)GR,(P/tk*%b:*Rk</)"XUpl))L4k0kIr>blF?V!LL-gTKp\/E&0RCRJ#;oC=e\^!1K*NUmT/j,=+/jXNF(q9hj+H,j6^9'oDPG_^Q7O,c6)<XJ*?R"!Bn9?[s@#PAS^lVCb,$:-nR!m#\*0%Y<a]kUO^UFChFn#Td`&'C^q/&<lSfns,=\D)G:-VMp%nf]XgS/K2&Z!g^*aM:s,K7j59TFN]HM^U-Y.)>db784c4Eh1F^P2[Ppuo$FE<FIsnu5-lBmD00cIKAtgWqN/m8Bl?QeCJLTq:Vt-4Zq)Y1ioK_>@ZW6n`11I@)aX2.^22n57]O&'&t&:OG>Wsd[3h%p/O6n#80olte-[='i?P'O8&@p>Ip+5O<*8G6,qfcd*?_;Lo9b*k+F/:Ok:(R:^AWZGn9k<D?5>h#=JilGe]@<qWj/N>UitLpSkde313Me6qi"E$M\*86mK^J<!fR+ej0bM8PIV)YTnn!`.uGd6fbOn!\?CQ%@G$'PC^R%J4PT:i'/tVs\BPD1?\$[ad`7I4q!e@\4>'JLJZ5:N/KM\9-MRoUTHl-,GY)=PqW>#lj>M8`&SmiNN[Yd<mQ1rG(KW'^n:MP`K!)XF4^uZ]#>udX!S"8NaH1V^L.,@VaTd6K^H[_]"7(goU&*rif-@55YBbilei=\@>Vo%g`:EY3j_JkLYI('m%$[[<!^,QP:kD:In<>Rki@-@o;n`8)UpL+tklSk:l3s2:iZVgjHQ+S69MLU*;f9Q)M?1J')g3D1>XK!2%L!D#8(KKB'q*`%Ve<UBMk[6U@s-,S#nnVYmR<tP;?DUcI9ANlOV^_[Q2a_u;$TP_$PZI<;g2gb<fG)B@,X?I_5j1sR,%d5.[j8`qk!@g!BQaH4"::G)T!cBC2?[$btK/T$\TbcEnC+G[AR_C<2tAG9O/().a<_6%e+CK^a_0$$(U':K"cqoRL*``,4^j"aTVqq*f0gcE;OEV&TR/Tf[#?%/;1r3d<WFkLnN^NI'2/0Nj@C'/gaW[AeQ8ZWQ2uU['\f9AmH8V7&(%c`sV0m'Fg=W3rJ1>c,9$sZV@fZb_-Q'2L9N*GlYMRoG&,*TSos`q=&JK$X?Q!Mc<ga\P\M=l<`s@G@Ki(L^fNMVhC6hq6O8I,7e/t+K7!!@XNFa.uC?sc@%<_R\ml<PLIkAA-k96Y%snf?qY]ao5>=fi_KqWl3AjQP"3Q8i`-f68bd6P;-Oa$I+.t6qQZa4"4WXIKZ?aGS1HT"&*P78?5Q'U=uF5s2T4N+PrW+Se[bpIBF;&8p'm!#A6giHloCcd"S=AfNHo0ERh8f@K$etlDIZrb7aL@WCR@t^cl"C2%a<!`)`'t'N0-bMVUNJhfFf+!\L`)51E:VK''mN%SR60?][YpSg.^'`Q<!qdYke2;.c#2kGP1?LGl;WF3o9]0ptO\\G982HKV#r>DSFs:+$NGEP'Da]r__XfO`ON!+[OXa.%fj6"W-Jd7luI<HaG"U9ked0?kd)u?!0<u["Io"OjVJ%NfNjN#J!'DXo9TFRNUmiGlZfY%/mUnYE17[7FD^>I8-+E3Q\5Yi7X6-G.:O$hjV$3>8>\FCh5$!U0AQ6>Y")15!Mc427a?iIHklAAKs,6-ja6GLA/b.FRS/'Hj7P$RDnJh&>XfiJ,;oNqj=iYD`_QKL$C]rhHtF&P:^BjgBK=R^1u:TFXtt';slhqFLFH?XfCV26q&u.7#65la&0^"7-?W1o<qeee7CUE;-=Bj[d'n,fnp>icp9O9O$6'JVj>$j8_U#k7O,EtGhK=c]VN>7bt!*U"CZ?IC[l_M7aTKrZ,tGMLJq"o9+rCll85"\p,*AY8n@S:K"`Q73N,&m1=e60[5*A4MZm@RUZ$S#-E2%N$N-d\TjU/%[FD($\Tl2]>A%#>&I!!>\/!.q8Ll\=JEh(ge_P]oFb"VMH#[Iu=Jr,M\;jp!#Bkge>m3DS`r5EW">!6`d*:Fa%=YO(eP3KdQep.46]jJp6cV2j#-Xm$IBqe[Gd+!kC;<KiJ>\8hn9/Y@Hjah(-R$MRdQ)Dr$S!0H.?dHk#H+S`7ll97h8V3q<8UGG%/F7r<o'=$&DnL=miqVm=8cdCWc!O_!rHMF*C.</n5PRq'Sj--KnR;=fnR1IL2rJEH;,s^3X=R#2;=u,'/[/qSH/:j:@bR[AHBA1/<1_MM'7pBe)W_k3*mF6Mn^rg4*58$W0CE?gOj&o9SPJ$(otQ$@_'"PX7n)\>tN$$3,7D!^UUinN^hL`iSDcJ\n^SKN[Y7N<gT:`;6=`?!J0fQ@TepM8C"q:mGmVPUd?&:Z>QIUKT?$6oCpXt~>endstream
|
||||
endobj
|
||||
77 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3400
|
||||
>>
|
||||
stream
|
||||
Gb"/)qet61&cUU9`P@65b132qHqErR./Zm]j_VnuPbLFn$8f;V(fR6ILEAZas7Xb/-#+_u9.).qoUs,8\\#2[1HXKA1F)u=5:$2C0*0A=d!eK8-nq4nd3K1@f?bHYI6Ad":STG^.adcj_&oP/_m!.(]8EVh+K;4]GIeo>FN)`8K_$F\%3ln?FAl(n@_`qW6:bPY1Wj7#U5uDCl6E,,2;l7@;'n:O(mi]VY+Zr%X",ibX-s:T@S:dQfPdD;e?0.#;R7iW7TE=_7V54P]p0$MjlsQ(99c)\&kjXFAHr]n%HB)qrg?@TKV,Y?U)T9NO\J-X8lbBXnbBOnYUZ+TdI9935k#5jI+P!3ZE*$=iNH=GWA`:hHrr[$6:k1U,1h`)e^Idi.'[V-75/FEq6.EGH%nEG"cnl!oeSp9DQ9#/Mi6G*1qi5bqRt(i.MA1X@kX:P*`c-ZZ00UDBaB/!6Q@FM9H@fAX$$U[eCgT>UXN1.2uV.&5Id/RpWeN,iF*0%c+n%L*7dr-Z%E#e$rbRqFm*"-Fn>og4hL,h:-T8m&*lPQ4JrPfLMAI8`q!_-\AU`IK]'A&je4'\<FdcHZ5u%=a[oD7NQ9Z"hVW5J6P4MT'$i#J4KP4<@P9:^KEIh3hZJ:1\`$jJ;S#T11?r3UHGf!OpZ]#OAq[$p%Q&mlD/#u2En/.9d#)?%D0@8Lq-*:O`etsriPG(i4@S\nhs4lFdMhCdGW+taYPU9WZ&#]=jDQM@&NE8)`I:F2fYR`$;sN^66-XC^g`$aK"@,IFoZ!VJ&^4r?F[X+P$rAbhK3_"-"9/=@,^#B4VA%n&LK@!/%Z_oG?`kh(]C]Kq2TaJRH/l><kud1J,D\)RC(jnh9h=S#/U18P+ITDii)S0TKY>fa=%*5>oL]3l'b*^Q.h-`'1uMB1!WSDm<]AU1jpa^[ZU;NO>$e:SWldeC:=``X'^_MPj(l5:FEQ)ed4qImLSQ)<G6_D,4)j-+776)!+PdH5'JS4".`*$igD,Z#UsU[]U\^'O:8s^P3TZlN6d"DX'dF7#[6gqVS8)ZE)!5$l[Z#<2Xl#$3=ocSoRlETcA,5nqH92ErG'=P=X6?qHG3Fn]%Lj_V`bdj&cYY?)kA:u#IU_e+[=!<5K[Hh8Y%L>_,#fnRE$D=a,AAJh4\7q!Dd29tYuO;hqTRS3OYIb49XF0N9N+q];6;qYl.)i;qG3UHNS)LeWk,&IQhQ!ed-XTQC6FS`2/hk+ho:&TCd4;*s1`-K6JQB%'n#1BY<Go6WZi=dnIi[)1pTJm't]%si]OP-s$J_*]U@mbm:5A*?/P2QM)XRN54`sDr>0K4g[YI([BYQS^u"^f3H%P#n"i$C)^-iPYV1WR$"Z/Zm$-gQc>%Y2A.ZuUF:,F:)5hCW/E_,'n:GogJaYbj=+/Qb+Ee(TrW&E64I[A>V<,#*$UiJSHmeaoEc.@BhEolXfrdDh'ufe,;a:7..T=M!p(Ee!hejM,?R6Nr@XkD'[Y42q?U?>od$9WeW.n3n+*<+N4R^(T1Zo9(qp'$3h6cFe']PM[1F-O3UB_tTLm%S(7]_H;d`^V1odOhFY@2/1DJ&0jU3':hM$*N0K\O>.FI7r!VGamL&b`![9ELE"8NDVUh]Xk!kGj<IDlqgg")35ogV60`1B1W`q=D?h#cV_Wh)N=b1h;7XriELU2I^4al7]I,ON'lU8%cCfV)>1<<F.Sc=H30ufpG]Z"*RMACp0?/M'&\<)F"IY!#l9%J+!W'rYBBb7,BXDY9euE+p6N.]$7h;*E90-^hK#EVGK]1%N2m]lE__OO?0l_3sNlP1,MX>*HuU64h*dU+#aPXV]'kmqEJ*>2mU.@rL+iir<V8(?=VG+anen'WVDbjq\(VLWNlToK#Dm.d%"J/e[8'Xr8MAun$1IFEYo2qV\#ZbP*>hP,V'*k%R8001KE^6Xa9<HKGF29W(9cTS99_T&-3-@!h]$5Hp>?UAnBp3UQ5K]=kn2HE4`1G'Wfj-F#/NX^I29T7:YbsZ1lQ^W!_t+3B'DiLHuJQcHpcEWUG1HQf87bGnJ;);Qq?QmT%bAfqYc9=Qg,p.$>sWCD`j*_Q"M'47Eh_h4uS/:K_RnOF#'UJ9U3](<kS>lLV_Iacj.H=Qd$>%#jafC+\TOi^J\mq=LeFSWD35WmlZddMRgMO*l?Q]qSO9&PG6+)350WUT(m[G[u6O3kb_!qO!BbQHka!,AS($68!ngBD=8Be>%hW6$`n^_*q,X/<ODFP&O/"R#\0Z8M/ItisZ*4c$*?)^;[U-Tr\reJ12)P0SP2q/_X'\5EY4=/45OMf&!eCN/cp[#e,g#l(AJQGKH'edqE=G!_#`sa7%p^_)\]O7iL)^7.Z1X.A9rKH`0=m]4.5d\mnDr,$*?OPnbrI!'*aJHigY$RP/k4a8?R]Ajl_^BASggUe;QQ09!sJJcB<eh<,cO]dk>cSL%3F,dM_iUJt-`odICPG%etE]+U'ilWN'a&KkC2"dYq*))iAQZ*jXJOQMU[9'AO-%qGu%YF3f8ZT#t&?sW7n'4_T,r#k,,I]skE*plF:LcLpBh"7=c@Abj`!QAAQ/o*8MEIIcN;<5!Le]ks=RA,r[+r1J-N$>G?,FU-`C/)!=.5ehFj7G/7]a634Y,bk\S:cR!jT/c8%,%P]K91@@R66Vb1&b>a.n;TP.oQGDUJTpJmn"t.,!?*AaSU5d$;;9.Bp-[7]6nAkA+CO"[+ifuYUNRd[&1qa40&p(DU7fjXUB>j*3EjdTn@3Nc+`M3"l%iKIG80L@WnQ-Tlt4"Lib_AP':SeW\4G"F!DG9(5RkcO69_[qBN$G+c+*F#Q>,6=Nhs7G?/q+e.Y<&.=s/'Q#Q[KMQCl)Am:(Tb\$8V0KVLMQ5Ku<LFs.d2p=Qc[KhrVYuN-p))BNr-q-qNhcaY+/Hi_<FGs,hN>4ON%lD4tis!8IZJ&4A/Oi&7l+oN2cF\l]'mJge9,>HOhk<j0@u9Y`<11Bg2A2Z@lsR@ri[iM&K*dGk7dM`r\-=[Hb$gHTR@"h=q!Tdc.%iiMPN3nl?8;Li03qudbTigF<>TkH,dUU0A/$%0KuMQU@2d+(1h%-@$Co%HHPG1s.VXcC:La_%X.hu!+Cn)`BuSY/8M;WGoajJGG>EEBU)1.QQ"CVBVE%qYgJus.7+l)#\6('KSBLLtiOLP-AB<GsJ6#Ajh1YX!)QkM$If1f4W+Qh+UK"cbl[beas(_'sW78(LTt3dcr]@dr[39hn]\;s<)ug\<\D.+r4E"!bH<TolKj.o\B_L$+L=Gho<IZ]Wc\(k4ZqECU4T7>-%k`UMS).QN,@62;rUkM<>=qZ`IGTS8?a)!p,Q'Tepk-V2^OB+`1>d?sgf$G#Ft,bi!*!0XaXTI8NSTj@Y5aN6dJ%p%WLRVq91sPO<pbI5rr<`gM)5~>endstream
|
||||
endobj
|
||||
78 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2851
|
||||
>>
|
||||
stream
|
||||
Gb"/)CJWt](B)O^\0'0UNT9RkN-6KcBKEI2kR_o\V+A\GQ,J;b)r%&JZeGGnf1t\bNZV7*7\`tADMVRuVlEM*cFGA*,6!\bpM:$&B`e7Z"Gd2E3=nVU?ucDePG@:cZHK3bJg_9I1i7ab"pu,=;WFBWETG--A.]Mu+@noKi0_Rs:mh9),RcM,2\[\?9hQ)3W`0R5<&u-481Uo^FMQGV<a8YiJ1D.6EA=9l_fAa#0c=*;Z5\-R)1(OTi+3n%_033W77]=hYfI.`PV'`p#3^d7_PP)$D1-rVmkFPdLAZonHW["WJuFT[0'L4#"R\d(_opHH3K3C0Y%ha7`IU8gS7,PPLYrJN`^&5bk^EF8,e3g!C;08<Uh[O9,l^'Q]PD4R_%YXX_N'DQ6kAWEX@$/e&$dg&goNU'"U!`]_#HM;T7&^?:O>L!IUF:fWJ1J$pTFJ-VB<Wt/c*/CQ\!B8H7uK=[bFOc5P!mp?^#XoQuEFLN4`P03qaZFJo0OB#O]VAdi8e$)_2Fl1L.@e_[jMQ0:Ka5?^T1J"PnJd4iVUMhL%g)9R@P_eioJ0ePmi.3c@fgaE\'Ub]BnjSLAs^r+=MT<Jk.qN@Et+9It*FIfl>&oQ$oM2=RCk]H*^'I64+\K38_SdDV)pIQG$,%'M<$pNrbEMJE]AiS0O,9_9`d[f`seRLurpki#$-l![sH=_u(68-X!\CYrUh%6ln#De/gN2lKV7lSE00)MX4,iJ6Td]1`qRk3eKP7O[RR>^s)W=Y"V>k$dDS)cjk*TR#K\%57Oc5F*2pbad.m\TT/./$ONBs1]W^L86=LJb%^gjcQ4Q[mWN?Y_'!lESq>aA9'_+a<('>@PM<k3mLW%,5+=u,^dh!d/N*in1dX(O-<5QV_eFF9eHI=dKFYF\HI:P'@=)H'(@>E5[T[I^o'!iT5k85Dr8fK06[^jQ+%DQ-6[`gP9>e;G*q=^llf+'>+-._.VOQo>\KQQkW1`V$2,8+;jsD%FCO!DdL["GHKP?gipjPoVH[>P;V(WlhBOn1`9;T%S=nRbO:89.+"V*;9#^NP-4K@+blM_%OF-kK(u>*Jm2a&<,tS>gjZ@R<kEHk;SdR3[$)[/Qp2'Ug>LM1<r1$O[So4gqNC%[!m84](\:ZFmnSnfcAmh+kTAYD-@9e!04!.6_#dk%\:T[%MW&8$ZO]u>=X9=1N7@m_R+(\&fHq)s+FmJ-i)N7K$,?MK")Nq2E+NTE>"[Q4dJ^P=*-jCTiK+Vr)]/i&O5ctSdZiF0_-ORB6_2&l^lL71B[lPARJm07e-j>GGZR!?ml.*TUWF/.WB>%dDIiY10)kV-R+4u-7?:<!.5@:uJ@`*-SR6ie/Y_>oul2B]*@5p:e?X-rs(lU9(.\YJjs(!oo1qNVK4@YZ%&s+o\gT;(7M?^FZjRIS>a*a].HNXBCE?iSMT0U1$SmH$i"jl%[:;/qpLJ^Del4on?1*$63<sJ64_?ps*JX,,YG!m>i9u5U>lU-80>lMrh+!S;e]tb%7mpYka\Dd2XW?WN>a(a!\aIShb;UcaTG_Oi6UPK5kg,/a0/u;SSgK6A$mcL!A5NlD4#dFS^E9ri1`KWM:]lifc!<l5bab!k_WC4#6BkHN`GN^0B06hgA704-c;@ts9cSub#3.'S>OF:Kg!s&l1]uKU6LCSb9!kJR;'LNO5;'+3k!YeAO13@K9El3*KX^5!c8?V@rYPQ03*I&JL("CA"nKYBn5(;;=\NYJE`FA]=;.&G\2#6X"iE&R[+rLh35;nS]j4@Tt_m`QMXbMmD%P\kcIUHL"pGQC9E!gL^$MXbuS&ciriR_3Z0jgO:U6TJI8#&u)nl6'o^Tr=1g/]e4G0./W7Fn^^=d*UNMl`p]L(<K;.B=8+D5$?;]_VUoWa-mhaOT&]BE&PQs3j@J;a@UO.,ma/'1Mi/Y7jA&5t_tj`/e*uWgd3/)$d=A92(P`0V+='^tHOW90=@lp1m@\dL.8aQ7atA/FR;,9+VR./F%*_nk:?,IFDS<p?h@]bA=V>r,'A0gGMoYgqkXAepC_AEgt2Ehc1(8lCXu./K1$8K=:4kJse7'reM.E"R2@bKNZ<`(ubKtZnL`?9sei*?E9D=r%u3M[<btRA:_f2iBW6]/495>&;b%[,c,fJIa0$*6knSF(hT@^TM6mc+p1QdW[^?W,421Qr=u+Wd<2t)+6.:pZ7AJomOAcjXh_9Frc<#u2s0%m;"&]i_;3TK^g@?=q"Dl_d-%h8@QLFj[eGRtHUchQ!knpu-p]R*O&<,epiMWFL+p(W$BK;sTgi>8?pPZJ#8'>H&R;_3pB5#d:79c.48ib;[/ZdH+K='oQLm.%E]^l1#T%,di1R#gi0pK&])(,h3:gBS+jgV?f8]ri.#Dm)a1&$aGCak&FN-(nr:$N>%u%RhrO^tkMuJ4coCK_nJRZ3,75X\FFIno,-1^9qnJ*e-8ObSmKFqM+/5CdE]"tY$gd'Z;T4?H9b?tma(CM/$V&5[O$.<3XDe=hWAf5jF*YI>MVs`g:OJ*gc(%gd;g5#TDC&8,oeBHSZ0"V$CE/o.:PQlOHgLL)gYVB3SGn&'3(E1V['8G(qC,IO.1UPZegRC:38D(<K$gHhCFCRB:8jXGA]67Fc<&^SV='=Br*FEKUeh=6_K;Y`pPY:fQS^RQtX9OZLoug/EHJb]/cbXL=$bfiKOL)sjo"0ug\\D[a_fIA])--eA2%]I$c\.)Yn\,T[lR7Taqe#a@IL*G#\#0Q#'iR]Oa-Znj4NjC2Em:72/Q!G!:r"];?q*kC'5'=qEHg;6gt)V;+6"&R7eR63f<0fT*?LSXMIl;D9Y,p>jtWH`Sf?is`-]2\3ZFA(ZD+"*MRjJqE:cBTKd2o~>endstream
|
||||
endobj
|
||||
79 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2766
|
||||
>>
|
||||
stream
|
||||
Gb!#^gN)%.&q0LUoV45.O9Bg#4?9Z0nZJF-,\<QPjap#o@R?1qJFmEfIN+MBM6_Ai]j"^@6q*m((SK1=THO@@IRs!YYQX_`!W@lUTeMl8Yquh82fLc;q>5h],V<5A4PR*UF^<6GI1+rM@=42:\d3l/&h$a5lW:H<Se\")LcLt<WkejG`TbR/-eS`lr.N11b06LQfpZqD1dGkr9QS7;ApDh4:!cSk0jG5Gf;?8N;"7,O?F((4dFTe?%X]NK#S+2u:oF/S1=SH4UD4+ff`*S],1b%D(u55;5!:1>U"D4`oig2a%\%Y)9[JFk<['sn:iM>/TW`CP`'=+a<@J`nSd8<W6)DD!$;Rc\^2.%pC/W;2S>-eu`T!EVC#48B-$Dp,`9C]lll$l,dq'H[0o$Le(-Q-lA]!.Y0kJ0SSqMDBifA,BMMrof1g33ZL,h)J&RVEgrk'9@^1F5>l]&XKI(R_f;>.W)c]%%"/JW>t-)!l)0OQY_=GcYt&$c:g<UQEprTVd_-`(UVm#a^kS&YC*6d$0gAEXGL^4\0L7%-$#U1tHK3d;G47jdl+V^FW&!u<OfRH]V<%8*1n<`5:\l`aqaT6$Fj&FM!uWZY#JO]XrWSVrD'lQk,2G#)rfP'N9SGK4V@?1F-WpqQ>HQjGZP[2q62DC4VT[euSOF)Nft_:@ZH99P3'/fkY6XO?fAC1WjAb_kWpSX$gjG9=$VDS,(C\EYf[&$%OHJOrD="W)]GW)<[p!?huKJ>ir*7%tR;.1>*k$,Mcu[KK1(Tq:$p&[@8?h*KIV-%NG+rA[pK,s/?r&&#!ri2qBpW;=jcOuC;FFPbLEIDlB5E+OWC'laPc/H09)njL>MRcAXW->0h_K/aB=VV"gIm[mM$._&[+"((s?s8>(-4R-*.Jc5&b+"*Kd:DM,WDR!D/`\9$"C'4,n)`rX5+q9DD:"gW)a(bmjd-_H/Q:"2oG"1u#*,o\JgoVSB2`h-:<":%%fbqT'Ee)_Wd*`,prd&jTV<X`_l-I%RW0jYr)OfuT@L!tjTS>op!rLZ3.tYl0%/%;G]A?li9/EADJ*2sP@9)*qFet^=5-]@??u#!lZ8`2L'ET/.B_AB-D?TaPPWu)b(^4/ZEj;f):U2C'N;u?ZGd/\aX`7Q]"!t*^REZCRHZhD5Pk(:^N-UMC.!t2(r0Bj]B"IcOdEVF!A*CXBFm=+j&T$@s]32[cbjiVfe_<[W]toAKJsA%RY-l.^O7H@0]K_O&L\#a3#bFUqC88p8,]L7"6k0]@'#256aTZ-@3eJkoJdYjRo6(sm#'9X_c+#imiG(lP15:@q.\F^XJF'@sKc5U,Bfrulp=pjE8a'e4hnNPd3ZqpAY#[YXpM2=3S7\^\md2aVZWEkjP@PQINi[jpe$]U'icJm@nr."NBDVo9F`qGpIbrHI7TP^\dYG?46'&rK_LP7(WNmVP7RTe',XR[<nT"C,RE1:bc?^kFKncN[d.0ml7r-AiJaK^iYj--HcQ>^!k:k*>\A""gWLskmh,SW,TiePM^fm/i+lcpUD3;YL^Zjnb*Qc`:4S^sJ&sq_XmeRU$E#Btkiu2s;BJ5]M0pI0<nma9M2Blqdq5;W,a;Q'eG"7Zrj0@I,`k"#ClY]9F,5]Xc5#misY>J2I91bZm-$?XqfrV_q[W>HDK<&1YY@0.?)_%50,g+dG1p6kIZrW^%A?T)F'`DPsI(_]C;Qs3?6VrW!4/kc35;">LT#ASR^FaZBmSr+KZ3S.O**`#.bA$\,1b4-N%+EY_qc(sL2N;1ARD=ssib04;eBF(qRZ=dc[tA]^h0i:m*l6e6?^[VDVk['5E&VO_HSgoj/&'ptQH/m&qiaB_E(AlQ4i<?.%E(AspM'1.[m1DGHWO$F0$?5Hp\&mBM%,5c0<?3D\;^;2c#E&P</m(;l&.A7YX8+kU\9$Yo6DBHl3MrQ<'gF8J@'5[aBg.IT*D%Ji(.?:c%:T8i;PFse64Gah%,/h\\f8sUW0HZCi&s_!n<2;NHf2!#_n/%NJM-!<&6E6KCST"B-I]D[\JmtEW>"lMK)"Fk"0_#f(64c5<jcsqk/Jk'iP!kNs+l=MPl"/c\nsq\tOquZOb]W(/Bu`S#M!2ge(s&f\.6;GhZr.6ro3:[l)TfXJ?L1CH%r8HS1(^Z[O;"<flZheuaQMHYKHDYARSL-r'kfTE+XTaeS8q+A_iumh0C?>bE+M"P#;nI]r4ceE0>#Z".kHDgkaFi#,3bnWIIAD$urs/\lT>aGcI1'RD/!lFm>Nf0s'3;OK*i;p&_A45j>E-f#`dqHj_4Vi<Z.38bs$@)%&pjQ_aB_W'`(r,%cMNMjL6&1$Wc8@gH8h?iL=44[73"Q*oRIGoHH,LgW96]-$$'Gu]E%N`se9_CP[OKK/W:*3'N*VS']@;&DIo2,-i%;SAiHfDVU'.(Bn+ZE>t=>n?ar<7e^VfcRCpr6(Q8(mp*b:Hp7[7EWg80oVLoL1W:Rd:")5+_Q,)#lU6ZBn5sOWW1<$p+Al"27Mc^:=`mI4_4UUCY>)5N)J-?b#_F3ddJlmU1eI7U:Hd\_33(Qpj=_VR*HGFYrC<blifh<[BFV?!Rn/4>(O0eSd<kGXHl'Xc+8XD+.5(nLN\#jan3-1#btl/0"R`R)^f2\#[oe[bUf;AG_PuHXE/og[9348?LlDUsI<;H^2L_Ve2sreCj(?2(7$<@Q]T&7t4fo\K"\rQPZ:8JXC3"hOh)-"HbmMJKg*qgO;@dq<3*=:>b-mkkk1pmJ1&L=i'@Sf=dS;<8V`u:At>P@!_Y~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 80
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000146 00000 n
|
||||
0000000253 00000 n
|
||||
0000000365 00000 n
|
||||
0000000513 00000 n
|
||||
0000000661 00000 n
|
||||
0000000808 00000 n
|
||||
0000000956 00000 n
|
||||
0000001104 00000 n
|
||||
0000001252 00000 n
|
||||
0000001400 00000 n
|
||||
0000001549 00000 n
|
||||
0000001696 00000 n
|
||||
0000001845 00000 n
|
||||
0000001994 00000 n
|
||||
0000002141 00000 n
|
||||
0000002290 00000 n
|
||||
0000002438 00000 n
|
||||
0000002586 00000 n
|
||||
0000002734 00000 n
|
||||
0000002883 00000 n
|
||||
0000003031 00000 n
|
||||
0000003180 00000 n
|
||||
0000003327 00000 n
|
||||
0000003476 00000 n
|
||||
0000003625 00000 n
|
||||
0000003773 00000 n
|
||||
0000003922 00000 n
|
||||
0000004070 00000 n
|
||||
0000004219 00000 n
|
||||
0000004367 00000 n
|
||||
0000004515 00000 n
|
||||
0000004631 00000 n
|
||||
0000005045 00000 n
|
||||
0000005151 00000 n
|
||||
0000005229 00000 n
|
||||
0000005313 00000 n
|
||||
0000005519 00000 n
|
||||
0000005725 00000 n
|
||||
0000005931 00000 n
|
||||
0000006137 00000 n
|
||||
0000006343 00000 n
|
||||
0000006430 00000 n
|
||||
0000006745 00000 n
|
||||
0000006820 00000 n
|
||||
0000006912 00000 n
|
||||
0000007015 00000 n
|
||||
0000007164 00000 n
|
||||
0000007254 00000 n
|
||||
0000007346 00000 n
|
||||
0000007456 00000 n
|
||||
0000007578 00000 n
|
||||
0000007723 00000 n
|
||||
0000007825 00000 n
|
||||
0000007941 00000 n
|
||||
0000008054 00000 n
|
||||
0000008169 00000 n
|
||||
0000008257 00000 n
|
||||
0000008387 00000 n
|
||||
0000008499 00000 n
|
||||
0000008651 00000 n
|
||||
0000008742 00000 n
|
||||
0000008854 00000 n
|
||||
0000008964 00000 n
|
||||
0000009059 00000 n
|
||||
0000009166 00000 n
|
||||
0000009272 00000 n
|
||||
0000009381 00000 n
|
||||
0000009488 00000 n
|
||||
0000009611 00000 n
|
||||
0000009715 00000 n
|
||||
0000009833 00000 n
|
||||
0000009931 00000 n
|
||||
0000010027 00000 n
|
||||
0000011148 00000 n
|
||||
0000014740 00000 n
|
||||
0000017954 00000 n
|
||||
0000021446 00000 n
|
||||
0000024389 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<475ca66bc5e001998636d1469a46808e><475ca66bc5e001998636d1469a46808e>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 43 0 R
|
||||
/Root 42 0 R
|
||||
/Size 80
|
||||
>>
|
||||
startxref
|
||||
27247
|
||||
%%EOF
|
||||
Reference in New Issue
Block a user