diff --git a/.env.example b/.env.example index 78d64a2..f783f85 100644 --- a/.env.example +++ b/.env.example @@ -18,3 +18,8 @@ CATALOG_REFRESH_INTERVAL_SECONDS=21600 # 6*60*60 every ~6 hours # AI Catalog name generation GEMINI_API_KEY= # Optional DEFAULT_GEMINI_MODEL="gemma-3-27b-it" + +# Trakt OAuth (optional - enables Trakt login on the configure page) +# Register your app at https://trakt.tv/oauth/applications +TRAKT_CLIENT_ID= +TRAKT_CLIENT_SECRET= diff --git a/README.md b/README.md index a20ffda..7030285 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ ## Features -- **Personalized Recommendations**: Analyzes your Stremio library to understand your viewing preferences. +- **Personalized Recommendations**: Analyzes your Stremio or Trakt library to understand your viewing preferences. +- **Trakt Integration**: Log in with Trakt instead of Stremio — no Stremio account required. - **Smart Filtering**: Automatically excludes content you've already watched. - **Advanced Scoring**: Recommendations are intelligently weighted by recency and relevance. - **Genre-Based Discovery**: Offers genre-specific catalogs based on your viewing history. @@ -67,7 +68,7 @@ You can pull the latest image from the GitHub Container Registry. ```env # Required TMDB_API_KEY=your_tmdb_api_key_here - TOKEN_SALT=generate_a_random_secure_string_here + TOKEN_SALT=generate_a_random_secure_string_here # python3 -c "import secrets; print(secrets.token_hex(32))" HOST_NAME=your_addon_url # Optional @@ -77,6 +78,12 @@ You can pull the latest image from the GitHub Container Registry. ADDON_NAME=Watchly TOKEN_TTL_SECONDS=0 AUTO_UPDATE_CATALOGS=true + + # Trakt Integration (optional — enables Trakt login on the configure page) + # Register your app at https://trakt.tv/oauth/applications/new + # Set the redirect URI to: https://your_addon_url/tokens/trakt/callback + TRAKT_CLIENT_ID=your_trakt_client_id + TRAKT_CLIENT_SECRET=your_trakt_client_secret ``` 3. **Start the application:** @@ -86,7 +93,7 @@ You can pull the latest image from the GitHub Container Registry. ``` 4. **Configure the addon:** - Open `http://localhost:8000/configure` in your browser to set up your Stremio credentials and install the addon. + Open `http://localhost:8000/configure` in your browser. Log in with either your **Stremio** credentials or your **Trakt** account, then configure your preferences and install the addon. ## Development diff --git a/app/api/endpoints/catalogs.py b/app/api/endpoints/catalogs.py index f1f0763..68baea1 100644 --- a/app/api/endpoints/catalogs.py +++ b/app/api/endpoints/catalogs.py @@ -13,7 +13,7 @@ async def get_catalog(response: Response, type: str, id: str, token: str, extra: if type not in ("movie", "series"): raise HTTPException(status_code=400, detail="Invalid content type. Must be 'movie' or 'series'.") - if len(token) > 30: # normal stremio tokens are 24 length. But we are using this just to be safe. + if len(token) > 80: # Stremio tokens are ~24 chars; Trakt hashed tokens are 40 chars. 80 is a safe upper bound. raise HTTPException(status_code=400, detail="Invalid token.") try: diff --git a/app/api/endpoints/trakt.py b/app/api/endpoints/trakt.py new file mode 100644 index 0000000..63579f4 --- /dev/null +++ b/app/api/endpoints/trakt.py @@ -0,0 +1,330 @@ +""" +Trakt OAuth and token-creation endpoints. + +Flow: +1. GET /tokens/trakt/config → returns client_id + whether Trakt is configured +2. GET /tokens/trakt/authorize → returns the Trakt OAuth2 URL for the popup +3. GET /tokens/trakt/callback → Trakt redirects here after user approves; + exchanges code for tokens, then posts a + message to the opener window and closes. +4. POST /tokens/trakt → Creates/updates a Watchly account using a + Trakt access_token (same body shape as + the main /tokens/ endpoint plus trakt_access_token). +5. POST /tokens/trakt/identity → Lightweight identity-check (returns user info + + existing settings if account exists). +""" + +import secrets +from datetime import datetime, timezone +from typing import Any + +from fastapi import APIRouter, HTTPException, Request +from fastapi.responses import HTMLResponse +from loguru import logger +from pydantic import BaseModel, Field + +from app.core.config import settings +from app.core.security import redact_token +from app.core.settings import CatalogConfig, PosterRatingConfig, UserSettings, get_default_settings +from app.services.manifest import manifest_service +from app.services.token_store import token_store +from app.services.trakt.service import TraktBundle + +router = APIRouter(prefix="/tokens/trakt", tags=["trakt"]) + +# OAuth state values are stored in Redis with a short TTL so they work +# correctly across multiple workers and don't accumulate indefinitely. +_OAUTH_STATE_TTL = 600 # 10 minutes + + +async def _store_oauth_state(state: str) -> None: + from app.services.redis_service import redis_service + await redis_service.set(f"watchly:oauth_state:trakt:{state}", state, _OAUTH_STATE_TTL) + + +async def _consume_oauth_state(state: str) -> bool: + """Returns True and deletes the state if valid, False otherwise.""" + from app.services.redis_service import redis_service + key = f"watchly:oauth_state:trakt:{state}" + value = await redis_service.get(key) + if value: + await redis_service.delete(key) + return True + return False + + +def _get_bundle(access_token: str | None = None) -> TraktBundle: + client_id = settings.TRAKT_CLIENT_ID + client_secret = settings.TRAKT_CLIENT_SECRET + if not client_id or not client_secret: + raise HTTPException( + status_code=503, + detail="Trakt integration is not configured on this server. Set TRAKT_CLIENT_ID and TRAKT_CLIENT_SECRET.", + ) + redirect_uri = f"{settings.HOST_NAME}/tokens/trakt/callback" + return TraktBundle( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + access_token=access_token, + ) + + +# --------------------------------------------------------------------------- +# Request / Response models +# --------------------------------------------------------------------------- + + +class TraktTokenRequest(BaseModel): + trakt_access_token: str = Field(..., description="Trakt OAuth2 access token") + trakt_refresh_token: str | None = Field(default=None, description="Trakt OAuth2 refresh token") + trakt_expires_at: int | None = Field(default=None, description="Unix timestamp when access token expires") + catalogs: list[CatalogConfig] | None = Field(default=None) + language: str = Field(default="en-US") + poster_rating: PosterRatingConfig | None = Field(default=None) + excluded_movie_genres: list[str] = Field(default_factory=list) + excluded_series_genres: list[str] = Field(default_factory=list) + popularity: str = Field(default="balanced") + year_min: int = Field(default=2010) + year_max: int = Field(default=2025) + sorting_order: str = Field(default="default") + simkl_api_key: str | None = Field(default=None) + gemini_api_key: str | None = Field(default=None) + tmdb_api_key: str | None = Field(default=None) + + +class TraktTokenResponse(BaseModel): + token: str + manifestUrl: str + expiresInSeconds: int | None = None + + +# --------------------------------------------------------------------------- +# Endpoints +# --------------------------------------------------------------------------- + + +@router.get("/config") +async def trakt_config(): + """Return whether Trakt is configured and the client_id (needed for the popup).""" + configured = bool(settings.TRAKT_CLIENT_ID and settings.TRAKT_CLIENT_SECRET) + return { + "configured": configured, + "client_id": settings.TRAKT_CLIENT_ID if configured else None, + } + + +@router.get("/authorize") +async def trakt_authorize(): + """Return the Trakt OAuth2 authorization URL for the frontend popup.""" + bundle = _get_bundle() + state = secrets.token_urlsafe(16) + await _store_oauth_state(state) + url, _ = bundle.auth.get_authorize_url(state=state) + await bundle.close() + return {"url": url, "state": state} + + +@router.get("/callback", response_class=HTMLResponse) +async def trakt_callback(request: Request, code: str | None = None, state: str | None = None, error: str | None = None): + """ + Trakt redirects here after the user approves (or denies) the app. + We exchange the code for tokens and post a message back to the opener + window, then close the popup. + """ + # --- error from Trakt --- + if error or not code: + return HTMLResponse(_popup_close_html(success=False, error=error or "Authorization cancelled")) + + # --- verify state to prevent CSRF --- + if not state or not await _consume_oauth_state(state): + return HTMLResponse(_popup_close_html(success=False, error="Invalid OAuth state. Please try again.")) + + # --- exchange code for tokens --- + try: + bundle = _get_bundle() + token_data = await bundle.auth.exchange_code(code) + await bundle.close() + except Exception as exc: + logger.error(f"Trakt callback: token exchange failed: {exc}") + return HTMLResponse(_popup_close_html(success=False, error="Token exchange failed")) + + access_token = token_data.get("access_token") + refresh_token = token_data.get("refresh_token") + expires_in = token_data.get("expires_in", 7776000) # default 90 days + + if not access_token: + return HTMLResponse(_popup_close_html(success=False, error="No access token received")) + + # Calculate expiry unix timestamp + import time + expires_at = int(time.time()) + expires_in + + return HTMLResponse( + _popup_close_html( + success=True, + access_token=access_token, + refresh_token=refresh_token, + expires_at=expires_at, + ) + ) + + +@router.post("/identity", status_code=200) +async def trakt_identity(payload: TraktTokenRequest): + """ + Validate the Trakt access token, return user info and existing settings. + """ + bundle = _get_bundle(access_token=payload.trakt_access_token) + try: + user_info = await bundle.user.get_user_info() + except Exception as exc: + raise HTTPException(status_code=400, detail=f"Invalid Trakt access token: {exc}") from exc + finally: + await bundle.close() + + username = user_info.get("username") or user_info.get("name") or "trakt_user" + # Use a Trakt-namespaced user_id so it can't clash with Stremio IDs + user_id = f"trakt:{username}" + + token = token_store.get_token_from_user_id(user_id) + user_data = await token_store.get_user_data(token) + exists = bool(user_data) + + response: dict[str, Any] = { + "user_id": user_id, + "username": username, + "display": user_info.get("name") or username, + "exists": exists, + } + if exists and user_data: + raw_settings = user_data.get("settings", {}) + try: + user_settings = UserSettings(**raw_settings) + response["settings"] = user_settings.model_dump() + except Exception as e: + logger.warning(f"Failed to normalise settings for {user_id}: {e}") + response["settings"] = raw_settings + + return response + + +@router.post("/", response_model=TraktTokenResponse) +async def create_trakt_token(payload: TraktTokenRequest, request: Request): + """ + Create or update a Watchly account backed by a Trakt access token. + The library is fetched from Trakt; everything else behaves like the + Stremio-based token endpoint. + """ + bundle = _get_bundle(access_token=payload.trakt_access_token) + + try: + user_info = await bundle.user.get_user_info() + except Exception as exc: + await bundle.close() + raise HTTPException(status_code=400, detail=f"Invalid Trakt access token: {exc}") from exc + + username = user_info.get("username") or user_info.get("name") or "trakt_user" + user_id = f"trakt:{username}" + + token = token_store.get_token_from_user_id(user_id) + existing_data = await token_store.get_user_data(token) + + default_settings = get_default_settings() + user_settings = UserSettings( + language=payload.language or default_settings.language, + catalogs=payload.catalogs if payload.catalogs else default_settings.catalogs, + poster_rating=payload.poster_rating, + excluded_movie_genres=payload.excluded_movie_genres, + excluded_series_genres=payload.excluded_series_genres, + year_min=payload.year_min, + year_max=payload.year_max, + popularity=payload.popularity, + sorting_order=payload.sorting_order, + simkl_api_key=payload.simkl_api_key, + gemini_api_key=payload.gemini_api_key, + tmdb_api_key=payload.tmdb_api_key, + ) + + payload_to_store: dict[str, Any] = { + "auth_provider": "trakt", + # authKey stores the Trakt access token (encrypted by token_store) + "authKey": payload.trakt_access_token, + "trakt_refresh_token": payload.trakt_refresh_token, + "trakt_expires_at": payload.trakt_expires_at, + "trakt_username": username, + "email": user_info.get("name") or username, + "settings": user_settings.model_dump(), + "last_updated": existing_data.get("last_updated") if existing_data else datetime.now(timezone.utc).isoformat(), + } + + token = await token_store.store_user_data(user_id, payload_to_store) + account_status = "updated" if existing_data else "created" + logger.info(f"[{redact_token(token)}] Trakt account {account_status} for user {user_id}") + + # Pre-cache library using Trakt data + try: + library_items = await bundle.library.get_library_items() + # Use the shared manifest caching pathway, passing library_items directly + await manifest_service.cache_library_and_profiles_from_items(library_items, user_settings, token) + logger.info(f"[{redact_token(token)}] Trakt library cached ({len(library_items.get('watched', []))} items)") + except Exception as exc: + logger.warning(f"[{redact_token(token)}] Failed to pre-cache Trakt library: {exc}. Will cache on demand.") + finally: + await bundle.close() + + base_url = settings.HOST_NAME + manifest_url = f"{base_url}/{token}/manifest.json" + expires_in = settings.TOKEN_TTL_SECONDS if settings.TOKEN_TTL_SECONDS > 0 else None + + return TraktTokenResponse(token=token, manifestUrl=manifest_url, expiresInSeconds=expires_in) + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _popup_close_html( + success: bool, + error: str | None = None, + access_token: str | None = None, + refresh_token: str | None = None, + expires_at: int | None = None, +) -> str: + """ + Minimal HTML page that posts a message to the opener and closes itself. + The frontend listens for this message via window.addEventListener('message', ...). + """ + if success: + payload_js = ( + f"{{ type: 'trakt_auth_success', " + f"access_token: {_js_str(access_token)}, " + f"refresh_token: {_js_str(refresh_token)}, " + f"expires_at: {expires_at or 'null'} }}" + ) + else: + payload_js = f"{{ type: 'trakt_auth_error', error: {_js_str(error or 'Unknown error')} }}" + + return f""" + +Trakt Authorization + +

+ {'Authorization successful! You can close this window.' if success else f'Authorization failed: {error}'} +

+ + +""" + + +def _js_str(value: str | None) -> str: + import json + return json.dumps(value) diff --git a/app/api/router.py b/app/api/router.py index 72c99bc..0552cc0 100644 --- a/app/api/router.py +++ b/app/api/router.py @@ -7,6 +7,7 @@ from .endpoints.meta import router as meta_router from .endpoints.stats import router as stats_router from .endpoints.tokens import router as tokens_router +from .endpoints.trakt import router as trakt_router from .endpoints.validation import router as validation_router api_router = APIRouter() @@ -20,6 +21,7 @@ async def root(): api_router.include_router(manifest_router) api_router.include_router(catalogs_router) api_router.include_router(tokens_router) +api_router.include_router(trakt_router) api_router.include_router(health_router) api_router.include_router(meta_router) api_router.include_router(announcement_router) diff --git a/app/core/config.py b/app/core/config.py index 5ee7d29..64f24b6 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -45,6 +45,10 @@ class Settings(BaseSettings): DEFAULT_GEMINI_MODEL: str = "gemma-3-27b-it" GEMINI_API_KEY: str | None = None + # Trakt OAuth + TRAKT_CLIENT_ID: str | None = None + TRAKT_CLIENT_SECRET: str | None = None + settings = Settings() diff --git a/app/services/catalog.py b/app/services/catalog.py index 73cb55f..8a24f58 100644 --- a/app/services/catalog.py +++ b/app/services/catalog.py @@ -57,6 +57,53 @@ def build_catalog_entry(self, item, label, config_id, display_at_home: bool = Tr "extra": extra, } + async def _stabilize_catalog_ids(self, catalogs: list[dict], token: str) -> list[dict]: + """ + Replace dynamic content-encoded catalog IDs with stable positional slot IDs + and persist a slot→real_id map in Redis so catalog_service can resolve them. + + watchly.watched.tt0209144 → watchly.watched.slot0 + watchly.loved.tt0816692 → watchly.loved.slot0 + watchly.theme.a:g27... → watchly.theme.slot0 (per type) + + The slot numbers are per-prefix per-type so movie slot0 and series slot0 + are independent, matching the pattern users see in the UI. + """ + from app.services.user_cache import user_cache + + slot_map: dict[str, str] = {} # stable_id → real_id + counters: dict[str, int] = {} # prefix:type → counter + stabilized = [] + + for cat in catalogs: + real_id: str = cat["id"] + cat_type: str = cat.get("type", "") + + # Determine if this is a dynamic ID that needs stabilizing + dynamic_prefixes = ("watchly.watched.", "watchly.loved.", "watchly.theme.") + matched_prefix = next((p for p in dynamic_prefixes if real_id.startswith(p)), None) + + if matched_prefix: + # Strip the prefix to get the content part + content_part = real_id[len(matched_prefix):] + # Only stabilize if the content part looks like an ID (tt...) or theme params + # Fixed IDs like "watchly.rec", "watchly.creators" are left alone + if content_part and not content_part.startswith("slot"): + counter_key = f"{matched_prefix.rstrip('.')}:{cat_type}" + slot_num = counters.get(counter_key, 0) + counters[counter_key] = slot_num + 1 + stable_id = f"{matched_prefix.rstrip('.')}.slot{slot_num}" + slot_map[stable_id] = real_id + stabilized.append({**cat, "id": stable_id}) + continue + + stabilized.append(cat) + + if slot_map: + await user_cache.set_catalog_slot_map(token, slot_map) + + return stabilized + def _get_smart_scored_items(self, library_items: dict, content_type: str, max_items: int = 50) -> list: """ Get smart sampled items for profile building. @@ -236,6 +283,14 @@ async def get_dynamic_catalogs( # 4. Add watchly.rec catalog catalogs.extend(get_catalogs_from_config(user_settings, "watchly.rec", "Top Picks for You", True, True)) + # 5. Stabilize dynamic catalog IDs so external apps (Nuvio, aiostreams) can + # order them consistently. Replace content-encoded IDs like + # "watchly.watched.tt0209144" and "watchly.theme.a:g27..." with stable + # positional slot IDs like "watchly.watched.slot0", "watchly.theme.slot0". + # A Redis mapping records which real ID each slot resolves to at fetch time. + if token: + catalogs = await self._stabilize_catalog_ids(catalogs, token) + # 5. Add watchly.creators catalog catalogs.extend( get_catalogs_from_config(user_settings, "watchly.creators", "From your favourite Creators", False, False) diff --git a/app/services/catalog_updater.py b/app/services/catalog_updater.py index 15dd20f..55fb774 100644 --- a/app/services/catalog_updater.py +++ b/app/services/catalog_updater.py @@ -73,6 +73,10 @@ async def refresh_catalogs_for_credentials( logger.warning(f"[{redact_token(token)}] Attempted to refresh catalogs with no credentials.") raise HTTPException(status_code=401, detail="Invalid or expired token. Please reconfigure the addon.") + # Trakt-backed accounts use a different refresh path + if credentials.get("auth_provider") == "trakt": + return await self._refresh_trakt_catalogs(token, credentials, update_timestamp) + auth_key = credentials.get("authKey") # check if auth key is valid bundle = StremioBundle() @@ -165,6 +169,74 @@ async def refresh_catalogs_for_credentials( finally: await bundle.close() + async def _refresh_trakt_catalogs( + self, token: str, credentials: dict[str, Any], update_timestamp: bool = True + ) -> bool: + """Refresh catalogs for a Trakt-backed account.""" + from app.core.settings import resolve_tmdb_api_key + from app.services.trakt.service import TraktBundle + + user_settings = None + if credentials.get("settings"): + try: + user_settings = UserSettings(**credentials["settings"]) + except Exception as e: + logger.warning(f"[{redact_token(token)}] Failed to parse Trakt user settings: {e}") + return True + + access_token = credentials.get("authKey") # stored as authKey after decrypt + if not access_token or not settings.TRAKT_CLIENT_ID or not settings.TRAKT_CLIENT_SECRET: + logger.warning(f"[{redact_token(token)}] Trakt credentials missing, skipping refresh") + return True + + redirect_uri = f"{settings.HOST_NAME}/tokens/trakt/callback" + + # Refresh access token if near expiry (within 7 days) + import time + expires_at = credentials.get("trakt_expires_at") + refresh_token_value = credentials.get("trakt_refresh_token") + if expires_at and refresh_token_value and int(time.time()) > (int(expires_at) - 604800): + logger.info(f"[{redact_token(token)}] Trakt access token near expiry, refreshing...") + try: + from app.services.trakt.auth import TraktAuthService + auth_service = TraktAuthService( + client_id=settings.TRAKT_CLIENT_ID, + client_secret=settings.TRAKT_CLIENT_SECRET, + redirect_uri=redirect_uri, + ) + token_data = await auth_service.refresh_token(refresh_token_value) + access_token = token_data["access_token"] + credentials["authKey"] = access_token + credentials["trakt_refresh_token"] = token_data.get("refresh_token", refresh_token_value) + credentials["trakt_expires_at"] = int(time.time()) + token_data.get("expires_in", 7776000) + await token_store.update_user_data(token, credentials) + logger.info(f"[{redact_token(token)}] Trakt access token refreshed successfully") + except Exception as e: + logger.warning(f"[{redact_token(token)}] Failed to refresh Trakt token: {e}") + + trakt_bundle = TraktBundle( + client_id=settings.TRAKT_CLIENT_ID, + client_secret=settings.TRAKT_CLIENT_SECRET, + redirect_uri=redirect_uri, + access_token=access_token, + ) + try: + library_items = await trakt_bundle.library.get_library_items() + await manifest_service.cache_library_and_profiles_from_items(library_items, user_settings, token) + + if update_timestamp: + now = datetime.now(timezone.utc) + credentials["last_updated"] = now.replace(microsecond=0).isoformat() + await token_store.update_user_data(token, credentials) + + logger.info(f"[{redact_token(token)}] Trakt catalog refresh complete") + return True + except Exception as e: + logger.exception(f"[{redact_token(token)}] Trakt catalog refresh failed: {e}") + return False + finally: + await trakt_bundle.close() + async def trigger_update(self, token: str, credentials: dict[str, Any]) -> None: """ Trigger a catalog update if needed. diff --git a/app/services/manifest.py b/app/services/manifest.py index dc3c855..4176470 100644 --- a/app/services/manifest.py +++ b/app/services/manifest.py @@ -113,6 +113,32 @@ async def cache_library_and_profiles( return library_items + async def cache_library_and_profiles_from_items( + self, library_items: dict, user_settings: UserSettings, token: str + ) -> None: + """ + Cache library items and build profiles from an already-fetched library dict. + Used by Trakt (and any future non-Stremio provider) where library data is + obtained outside of the Stremio bundle. + """ + await user_cache.set_library_items(token, library_items) + logger.debug(f"[{redact_token(token)}] Cached library items (provider-agnostic)") + + language = user_settings.language + tmdb_key = resolve_tmdb_api_key(user_settings) + integration_service = ProfileIntegration(language=language, tmdb_api_key=tmdb_key) + + for content_type in ["movie", "series"]: + try: + profile, watched_tmdb, watched_imdb = await integration_service.build_profile_from_library( + library_items, content_type + ) + await user_cache.set_profile_and_watched_sets(token, content_type, profile, watched_tmdb, watched_imdb) + logger.debug(f"[{redact_token(token)}] Cached profile for {content_type}") + except Exception as e: + logger.warning(f"[{redact_token(token)}] Failed to cache profile for {content_type}: {e}") + + async def _ensure_library_and_profiles_cached( self, bundle: StremioBundle, auth_key: str, user_settings: UserSettings, token: str ) -> dict[str, Any]: @@ -163,6 +189,43 @@ def _sort_catalogs( return sort_catalogs(catalogs, user_settings) + async def _build_dynamic_catalogs_trakt( + self, creds: dict, user_settings: UserSettings | None, token: str + ) -> list[dict[str, Any]]: + """Build dynamic catalogs for a Trakt-backed account.""" + from app.core.config import settings as app_settings + from app.services.trakt.service import TraktBundle + + # Use cached library if available + library_items = await user_cache.get_library_items(token) + if not library_items: + access_token = creds.get("authKey") # stored as authKey after encryption/decryption + if not access_token or not app_settings.TRAKT_CLIENT_ID or not app_settings.TRAKT_CLIENT_SECRET: + logger.warning(f"[{redact_token(token)}] Trakt credentials missing, cannot fetch library") + return [] + redirect_uri = f"{app_settings.HOST_NAME}/tokens/trakt/callback" + trakt_bundle = TraktBundle( + client_id=app_settings.TRAKT_CLIENT_ID, + client_secret=app_settings.TRAKT_CLIENT_SECRET, + redirect_uri=redirect_uri, + access_token=access_token, + ) + try: + library_items = await trakt_bundle.library.get_library_items() + await user_cache.set_library_items(token, library_items) + finally: + await trakt_bundle.close() + + if not library_items: + return [] + + tmdb_key = resolve_tmdb_api_key(user_settings) + dynamic_catalog_service = DynamicCatalogService( + language=user_settings.language if user_settings else "en-US", + tmdb_api_key=tmdb_key, + ) + return await dynamic_catalog_service.get_dynamic_catalogs(library_items, user_settings, token=token) + async def get_manifest_for_token(self, token: str) -> dict[str, Any]: """ Generate manifest for a given token. @@ -194,19 +257,24 @@ async def get_manifest_for_token(self, token: str) -> dict[str, Any]: base_manifest = self.get_base_manifest() - bundle = StremioBundle() fetched_catalogs = [] try: - # Resolve auth key - auth_key = await self._resolve_auth_key(bundle, creds, token) - - if auth_key: - fetched_catalogs = await self._build_dynamic_catalogs(bundle, auth_key, user_settings, token) + if creds.get("auth_provider") == "trakt": + # Trakt-backed account: fetch library from Trakt, bypass Stremio + fetched_catalogs = await self._build_dynamic_catalogs_trakt(creds, user_settings, token) + else: + bundle = StremioBundle() + try: + # Resolve auth key + auth_key = await self._resolve_auth_key(bundle, creds, token) + + if auth_key: + fetched_catalogs = await self._build_dynamic_catalogs(bundle, auth_key, user_settings, token) + finally: + await bundle.close() except Exception as e: logger.exception(f"[{redact_token(token)}] Dynamic catalog build failed: {e}") fetched_catalogs = [] - finally: - await bundle.close() # Combine base catalogs with fetched catalogs all_catalogs = [c.copy() for c in base_manifest["catalogs"]] + [c.copy() for c in fetched_catalogs] diff --git a/app/services/recommendation/catalog_service.py b/app/services/recommendation/catalog_service.py index e277765..eb94390 100644 --- a/app/services/recommendation/catalog_service.py +++ b/app/services/recommendation/catalog_service.py @@ -133,6 +133,18 @@ async def get_catalog( # continue with the request even if the auto update fails pass + # Resolve stable slot ID to real dynamic catalog ID if needed + slot_prefixes = ("watchly.watched.slot", "watchly.loved.slot", "watchly.theme.slot") + if any(catalog_id.startswith(p) for p in slot_prefixes): + slot_map = await user_cache.get_catalog_slot_map(token) + if slot_map and catalog_id in slot_map: + real_id = slot_map[catalog_id] + logger.debug(f"[{redact_token(token)}...] Resolved slot {catalog_id} → {real_id}") + catalog_id = real_id + else: + logger.warning(f"[{redact_token(token)}...] Slot {catalog_id} not found in slot map, cannot resolve") + raise HTTPException(status_code=404, detail="Catalog slot not found. Please re-configure the addon.") + bundle = StremioBundle() user_settings = None stale_data = None @@ -296,7 +308,9 @@ def _validate_inputs(self, token: str, content_type: str, catalog_id: str) -> No "watchly.liked.all", ] supported_prefixes = ("watchly.theme.", "watchly.loved.", "watchly.watched.") - if catalog_id not in supported_base and not any(catalog_id.startswith(p) for p in supported_prefixes): + # Also accept stable slot IDs (e.g. watchly.watched.slot0, watchly.theme.slot1) + is_slot = any(catalog_id.startswith(p.rstrip(".") + ".slot") for p in supported_prefixes) + if catalog_id not in supported_base and not any(catalog_id.startswith(p) for p in supported_prefixes) and not is_slot: logger.warning(f"Invalid id: {catalog_id}") raise HTTPException( status_code=400, @@ -308,6 +322,14 @@ def _validate_inputs(self, token: str, content_type: str, catalog_id: str) -> No async def _resolve_auth(self, bundle: StremioBundle, credentials: dict, token: str) -> str: auth_key = credentials.get("authKey") + + # Trakt accounts use a Trakt access token stored as authKey. + # Skip Stremio session validation entirely for these accounts. + if credentials.get("auth_provider") == "trakt": + if not auth_key: + raise HTTPException(status_code=401, detail="Trakt session expired. Please reconfigure.") + return auth_key + email = credentials.get("email") password = credentials.get("password") diff --git a/app/services/token_store.py b/app/services/token_store.py index 2f0a8ae..f428b6f 100644 --- a/app/services/token_store.py +++ b/app/services/token_store.py @@ -56,6 +56,16 @@ def _format_key(self, token: str) -> str: return f"{self.KEY_PREFIX}{token}" def get_token_from_user_id(self, user_id: str) -> str: + """ + For Trakt users, generate a stable opaque token from the user_id + so the username is not exposed in the manifest URL. + Stremio users keep their existing behaviour (user_id == token). + """ + if user_id.startswith("trakt:"): + import hashlib + salt = settings.TOKEN_SALT or "watchly" + digest = hashlib.sha256(f"{salt}:{user_id}".encode()).hexdigest() + return digest[:40] return user_id.strip() def get_user_id_from_token(self, token: str) -> str: @@ -75,6 +85,14 @@ async def store_user_data(self, user_id: str, payload: dict[str, Any]) -> str: if storage_data.get("authKey"): storage_data["authKey"] = self.encrypt_token(storage_data["authKey"]) + # Encrypt Trakt refresh token if present + if storage_data.get("trakt_refresh_token"): + try: + if not storage_data["trakt_refresh_token"].startswith("gAAAAAB"): + storage_data["trakt_refresh_token"] = self.encrypt_token(storage_data["trakt_refresh_token"]) + except Exception as exc: + logger.warning(f"Failed to encrypt trakt_refresh_token: {exc}") + # Securely store password if provided (primary login mode) if storage_data.get("password"): try: @@ -250,6 +268,13 @@ async def _get_user_data_cached(self, token: str) -> dict[str, Any] | None: logger.warning(f"Decryption failed for authKey associated with {redact_token(token)}: {e}") # Leave as-is (legacy plaintext or previous failure) pass + + if data.get("trakt_refresh_token"): + try: + if data["trakt_refresh_token"].startswith("gAAAAA"): + data["trakt_refresh_token"] = self.decrypt_token(data["trakt_refresh_token"]) + except Exception as e: + logger.debug(f"Decryption failed for trakt_refresh_token associated with {redact_token(token)}: {e}") if data.get("password"): try: data["password"] = self.decrypt_token(data["password"]) diff --git a/app/services/trakt/__init__.py b/app/services/trakt/__init__.py new file mode 100644 index 0000000..4ddf4c4 --- /dev/null +++ b/app/services/trakt/__init__.py @@ -0,0 +1,3 @@ +from app.services.trakt.service import TraktBundle + +__all__ = ["TraktBundle"] diff --git a/app/services/trakt/auth.py b/app/services/trakt/auth.py new file mode 100644 index 0000000..1b8b17b --- /dev/null +++ b/app/services/trakt/auth.py @@ -0,0 +1,79 @@ +import secrets +from typing import Any + +import httpx + + +# Module-level shared client for connection pooling across token exchanges. +# auth.py only talks to one endpoint (api.trakt.tv/oauth/token) so a single +# persistent client is sufficient and avoids the overhead of creating a new +# TCP connection for every OAuth exchange. +_http_client: httpx.AsyncClient | None = None + + +def _get_http_client() -> httpx.AsyncClient: + global _http_client + if _http_client is None or _http_client.is_closed: + _http_client = httpx.AsyncClient(timeout=15.0) + return _http_client + + +class TraktAuthService: + """ + Handles Trakt OAuth2 authentication (Authorization Code flow). + """ + + TOKEN_URL = "https://api.trakt.tv/oauth/token" + AUTHORIZE_URL = "https://trakt.tv/oauth/authorize" + + def __init__(self, client_id: str, client_secret: str, redirect_uri: str): + self.client_id = client_id + self.client_secret = client_secret + self.redirect_uri = redirect_uri + + def get_authorize_url(self, state: str | None = None) -> tuple[str, str]: + """ + Build the OAuth2 authorization URL and return it along with the state value. + """ + if not state: + state = secrets.token_urlsafe(16) + + params = { + "response_type": "code", + "client_id": self.client_id, + "redirect_uri": self.redirect_uri, + "state": state, + } + query = "&".join(f"{k}={v}" for k, v in params.items()) + return f"{self.AUTHORIZE_URL}?{query}", state + + async def exchange_code(self, code: str) -> dict[str, Any]: + """ + Exchange an authorization code for tokens. + Returns dict with access_token, refresh_token, expires_in, etc. + """ + payload = { + "code": code, + "client_id": self.client_id, + "client_secret": self.client_secret, + "redirect_uri": self.redirect_uri, + "grant_type": "authorization_code", + } + client = _get_http_client() + response = await client.post(self.TOKEN_URL, json=payload) + response.raise_for_status() + return response.json() + + async def refresh_token(self, refresh_token_value: str) -> dict[str, Any]: + """Refresh an expired access token.""" + payload = { + "refresh_token": refresh_token_value, + "client_id": self.client_id, + "client_secret": self.client_secret, + "redirect_uri": self.redirect_uri, + "grant_type": "refresh_token", + } + client = _get_http_client() + response = await client.post(self.TOKEN_URL, json=payload) + response.raise_for_status() + return response.json() diff --git a/app/services/trakt/client.py b/app/services/trakt/client.py new file mode 100644 index 0000000..7c15e04 --- /dev/null +++ b/app/services/trakt/client.py @@ -0,0 +1,18 @@ +from app.core.base_client import BaseClient + + +class TraktClient(BaseClient): + """ + Client for interacting with the Trakt API. + """ + + def __init__(self, client_id: str, access_token: str | None = None, timeout: float = 10.0, max_retries: int = 3): + headers = { + "Content-Type": "application/json", + "trakt-api-version": "2", + "trakt-api-key": client_id, + } + if access_token: + headers["Authorization"] = f"Bearer {access_token}" + + super().__init__(base_url="https://api.trakt.tv", timeout=timeout, max_retries=max_retries, headers=headers) diff --git a/app/services/trakt/library.py b/app/services/trakt/library.py new file mode 100644 index 0000000..ac8d7a6 --- /dev/null +++ b/app/services/trakt/library.py @@ -0,0 +1,136 @@ +from typing import Any + +from loguru import logger + +from app.services.trakt.client import TraktClient + + +class TraktLibraryService: + """ + Fetches and normalises watch history from Trakt into the same shape + that the Stremio library service produces, so the rest of the app + needs no changes. + """ + + def __init__(self, client: TraktClient): + self.client = client + + # ------------------------------------------------------------------ + # Public helpers + # ------------------------------------------------------------------ + + async def get_library_items(self) -> dict[str, list[dict[str, Any]]]: + """ + Return library items in the same shape as StremioLibraryService: + { "watched": [...], "loved": [], "liked": [], "added": [], "removed": [] } + + Each item contains at minimum: + _id – tt... or tmdb:... string + type – "movie" | "series" + name – title + """ + try: + import asyncio + movies, shows = await asyncio.gather( + self._get_history("movies"), + self._get_history("shows"), + ) + + watched: list[dict[str, Any]] = [] + seen_ids: set[str] = set() + + for raw in movies: + item = self._normalise_movie(raw) + if item and item["_id"] not in seen_ids: + seen_ids.add(item["_id"]) + watched.append(item) + + for raw in shows: + item = self._normalise_show(raw) + if item and item["_id"] not in seen_ids: + seen_ids.add(item["_id"]) + watched.append(item) + + logger.info(f"[Trakt] library: {len(watched)} watched items ({len(movies)} movies, {len(shows)} shows)") + + return { + "watched": watched, + "loved": [], + "liked": [], + "added": [], + "removed": [], + } + except Exception as e: + logger.exception(f"[Trakt] Failed to get library items: {e}") + return {"watched": [], "loved": [], "liked": [], "added": [], "removed": []} + + # ------------------------------------------------------------------ + # Private helpers + # ------------------------------------------------------------------ + + async def _get_history(self, media_type: str) -> list[dict[str, Any]]: + """ + Pull watched history for *media_type* ("movies" | "shows"). + Uses the /users/me/watched/:type endpoint which returns a deduplicated + list of everything the user has ever played (no paging needed). + """ + try: + data = await self.client.get(f"/users/me/watched/{media_type}") + if isinstance(data, list): + return data + return [] + except Exception as e: + logger.warning(f"[Trakt] Failed to fetch {media_type} history: {e}") + return [] + + def _get_id(self, ids: dict[str, Any]) -> str | None: + """Return the best available canonical ID (prefer IMDb, fall back to TMDB).""" + imdb = ids.get("imdb") + if imdb: + return imdb # e.g. "tt1234567" + tmdb = ids.get("tmdb") + if tmdb: + return f"tmdb:{tmdb}" + return None + + def _normalise_movie(self, raw: dict[str, Any]) -> dict[str, Any] | None: + movie = raw.get("movie", {}) + ids = movie.get("ids", {}) + canonical_id = self._get_id(ids) + if not canonical_id: + return None + return { + "_id": canonical_id, + "type": "movie", + "name": movie.get("title", ""), + "year": movie.get("year"), + "state": { + "timesWatched": raw.get("plays", 1), + "flaggedWatched": 1, + "lastWatched": raw.get("last_watched_at", ""), + }, + "temp": False, + "removed": False, + "_source": "trakt", + } + + def _normalise_show(self, raw: dict[str, Any]) -> dict[str, Any] | None: + show = raw.get("show", {}) + ids = show.get("ids", {}) + canonical_id = self._get_id(ids) + if not canonical_id: + return None + return { + "_id": canonical_id, + "type": "series", + "name": show.get("title", ""), + "year": show.get("year"), + "state": { + "timesWatched": raw.get("plays", 1), + "flaggedWatched": 1, + "lastWatched": raw.get("last_watched_at", ""), + }, + "temp": False, + "removed": False, + "_source": "trakt", + } diff --git a/app/services/trakt/service.py b/app/services/trakt/service.py new file mode 100644 index 0000000..7f6001c --- /dev/null +++ b/app/services/trakt/service.py @@ -0,0 +1,23 @@ +from app.services.trakt.auth import TraktAuthService +from app.services.trakt.client import TraktClient +from app.services.trakt.library import TraktLibraryService +from app.services.trakt.user import TraktUserService + + +class TraktBundle: + """ + Unified bundle for all Trakt-related services. + """ + + def __init__(self, client_id: str, client_secret: str, redirect_uri: str, access_token: str | None = None): + self.auth = TraktAuthService( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + ) + self._client = TraktClient(client_id=client_id, access_token=access_token) + self.user = TraktUserService(self._client) + self.library = TraktLibraryService(self._client) + + async def close(self): + await self._client.close() diff --git a/app/services/trakt/user.py b/app/services/trakt/user.py new file mode 100644 index 0000000..7d6ccbf --- /dev/null +++ b/app/services/trakt/user.py @@ -0,0 +1,23 @@ +from typing import Any + +from loguru import logger + +from app.services.trakt.client import TraktClient + + +class TraktUserService: + """ + Fetches the authenticated Trakt user's profile. + """ + + def __init__(self, client: TraktClient): + self.client = client + + async def get_user_info(self) -> dict[str, Any]: + """Return the authenticated user's profile (username, name, etc.).""" + try: + data = await self.client.get("/users/me?extended=full") + return data + except Exception as e: + logger.exception(f"Failed to fetch Trakt user info: {e}") + raise diff --git a/app/services/user_cache.py b/app/services/user_cache.py index d1ace43..3e718e9 100644 --- a/app/services/user_cache.py +++ b/app/services/user_cache.py @@ -404,6 +404,31 @@ async def invalidate_catalog(self, token: str, type: str, id: str) -> None: await redis_service.delete(key) logger.debug(f"[{redact_token(token)}...] Invalidated catalog cache for {type}/{id}") + async def get_catalog_slot_map(self, token: str) -> dict[str, str] | None: + """ + Return the slot-to-real-id mapping for dynamic catalogs. + e.g. {"watchly.watched.slot0": "watchly.watched.tt0209144", ...} + """ + key = f"watchly:catalog_slot_map:{token}" + try: + data = await redis_service.get(key) + if data: + import json + return json.loads(data) + except Exception as e: + logger.warning(f"[{redact_token(token)}...] Failed to get catalog slot map: {e}") + return None + + async def set_catalog_slot_map(self, token: str, slot_map: dict[str, str]) -> None: + """Store the slot-to-real-id mapping for dynamic catalogs.""" + key = f"watchly:catalog_slot_map:{token}" + try: + import json + await redis_service.set(key, json.dumps(slot_map), 86400 * 7) + logger.debug(f"[{redact_token(token)}...] Stored catalog slot map ({len(slot_map)} entries)") + except Exception as e: + logger.warning(f"[{redact_token(token)}...] Failed to set catalog slot map: {e}") + async def invalidate_all_catalogs(self, token: str) -> None: """ Invalidate all cached catalogs for a user. diff --git a/app/static/js/main.js b/app/static/js/main.js index d79b92a..2c2f47c 100644 --- a/app/static/js/main.js +++ b/app/static/js/main.js @@ -4,6 +4,7 @@ import { defaultCatalogs } from './constants.js'; import { showToast, initializeFooter, initializeKofi } from './modules/ui.js'; import { initializeNavigation, switchSection, lockNavigationForLoggedOut, initializeMobileNav, updateMobileLayout, unlockNavigation } from './modules/navigation.js'; import { initializeAuth, setStremioLoggedOutState } from './modules/auth.js'; +import { initializeTrakt, setTraktLoggedOutState } from './modules/trakt.js'; import { initializeCatalogList, renderCatalogList, getCatalogs, setCatalogs } from './modules/catalog.js'; import { initializeForm, clearErrors } from './modules/form.js'; @@ -65,6 +66,9 @@ function resetApp() { // Reset Stremio State setStremioLoggedOutState(); + // Reset Trakt State + setTraktLoggedOutState(); + // Reset catalogs catalogsState = JSON.parse(JSON.stringify(defaultCatalogs)); setCatalogs(catalogsState); @@ -118,7 +122,7 @@ document.addEventListener('DOMContentLoaded', () => { } ); - // Initialize authentication + // Initialize authentication (Stremio) initializeAuth( { stremioLoginBtn, @@ -135,6 +139,16 @@ document.addEventListener('DOMContentLoaded', () => { } ); + // Initialize Trakt authentication + initializeTrakt( + { languageSelect }, + { + getCatalogs, + renderCatalogList, + resetApp + } + ); + // Initialize form handling initializeForm( { diff --git a/app/static/js/modules/form.js b/app/static/js/modules/form.js index 4c0c76d..b3f0bd2 100644 --- a/app/static/js/modules/form.js +++ b/app/static/js/modules/form.js @@ -1,6 +1,7 @@ // Form Submission and UI Helpers import { showToast, showConfirm, escapeHtml } from './ui.js'; +import { getTraktTokensFromStorage } from './trakt.js'; import { switchSection } from './navigation.js'; import { MOVIE_GENRES, SERIES_GENRES } from '../constants.js'; @@ -104,9 +105,13 @@ async function initializeFormSubmission() { }); }); + // Determine active provider + const traktTokens = getTraktTokensFromStorage(); + const isTraktLogin = !!(traktTokens && traktTokens.access_token && !sAuthKey && !email); + // Validation - if (!sAuthKey && !(email && password)) { - showError("generalError", "Please login with Stremio or enter email & password."); + if (!sAuthKey && !(email && password) && !isTraktLogin) { + showError("generalError", "Please login with Stremio or Trakt to continue."); switchSection('login'); return; } @@ -143,25 +148,51 @@ async function initializeFormSubmission() { }; } - const payload = { - authKey: sAuthKey || undefined, - email: email || undefined, - password: password || undefined, - catalogs: catalogsToSend, - language: language, - year_min: yearMin, - year_max: yearMax, - popularity: popularity, - sorting_order: sortingOrder, - poster_rating: posterRating, - tmdb_api_key: tmdbApiKey || undefined, - simkl_api_key: simklApiKey, - gemini_api_key: geminiApiKey, - excluded_movie_genres: excludedMovieGenres, - excluded_series_genres: excludedSeriesGenres - }; - - const response = await fetch("/tokens/", { + let endpoint, payload; + + if (isTraktLogin) { + // ---- Trakt submission ---- + endpoint = "/tokens/trakt/"; + payload = { + trakt_access_token: traktTokens.access_token, + trakt_refresh_token: traktTokens.refresh_token || undefined, + trakt_expires_at: traktTokens.expires_at || undefined, + catalogs: catalogsToSend, + language: language, + year_min: yearMin, + year_max: yearMax, + popularity: popularity, + sorting_order: sortingOrder, + poster_rating: posterRating, + tmdb_api_key: tmdbApiKey || undefined, + simkl_api_key: simklApiKey, + gemini_api_key: geminiApiKey, + excluded_movie_genres: excludedMovieGenres, + excluded_series_genres: excludedSeriesGenres + }; + } else { + // ---- Stremio submission ---- + endpoint = "/tokens/"; + payload = { + authKey: sAuthKey || undefined, + email: email || undefined, + password: password || undefined, + catalogs: catalogsToSend, + language: language, + year_min: yearMin, + year_max: yearMax, + popularity: popularity, + sorting_order: sortingOrder, + poster_rating: posterRating, + tmdb_api_key: tmdbApiKey || undefined, + simkl_api_key: simklApiKey, + gemini_api_key: geminiApiKey, + excluded_movie_genres: excludedMovieGenres, + excluded_series_genres: excludedSeriesGenres + }; + } + + const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) diff --git a/app/static/js/modules/trakt.js b/app/static/js/modules/trakt.js new file mode 100644 index 0000000..ec4b82d --- /dev/null +++ b/app/static/js/modules/trakt.js @@ -0,0 +1,381 @@ +// Trakt Authentication Module + +import { showToast } from './ui.js'; +import { switchSection, unlockNavigation } from './navigation.js'; + +// LocalStorage keys for Trakt +const TRAKT_STORAGE_KEY = 'watchly_trakt_auth'; +const EXPIRY_DAYS = 85; // Trakt tokens last 90 days; refresh a bit early + +let languageSelect = null; +let getCatalogs = null; +let renderCatalogList = null; +let resetApp = null; + +// -------------------------------------------------------------------------- +// Public API +// -------------------------------------------------------------------------- + +export function initializeTrakt(domElements, catalogState) { + languageSelect = domElements.languageSelect; + getCatalogs = catalogState.getCatalogs; + renderCatalogList = catalogState.renderCatalogList; + resetApp = catalogState.resetApp; + + initializeTraktConnectButton(); + initializeTraktLogoutButton(); + attemptTraktAutoLogin(); +} + +export function setTraktLoggedOutState() { + clearTraktFromStorage(); + hideTraktStatus(); + + const traktConnectBtn = document.getElementById('traktConnectBtn'); + if (traktConnectBtn) { + traktConnectBtn.classList.remove('hidden'); + } +} + +// -------------------------------------------------------------------------- +// Storage helpers +// -------------------------------------------------------------------------- + +function saveTraktToStorage(authData) { + try { + const expiryDate = new Date(); + expiryDate.setDate(expiryDate.getDate() + EXPIRY_DAYS); + localStorage.setItem(TRAKT_STORAGE_KEY, JSON.stringify({ ...authData, expiresAt: expiryDate.getTime() })); + } catch (e) { + console.warn('Failed to save Trakt auth:', e); + } +} + +function getTraktFromStorage() { + try { + const stored = localStorage.getItem(TRAKT_STORAGE_KEY); + if (!stored) return null; + const data = JSON.parse(stored); + if (data.expiresAt && data.expiresAt < Date.now()) { + clearTraktFromStorage(); + return null; + } + return data; + } catch (e) { + clearTraktFromStorage(); + return null; + } +} + +function clearTraktFromStorage() { + try { localStorage.removeItem(TRAKT_STORAGE_KEY); } catch (e) { /* noop */ } +} + +// -------------------------------------------------------------------------- +// OAuth popup flow +// -------------------------------------------------------------------------- + +function initializeTraktConnectButton() { + const btn = document.getElementById('traktConnectBtn'); + if (!btn) return; + + btn.addEventListener('click', async () => { + setTraktConnecting(true); + try { + // 1. Fetch the authorization URL from backend + const res = await fetch('/tokens/trakt/authorize'); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.detail || 'Failed to start Trakt authorization'); + } + const { url } = await res.json(); + + // 2. Open OAuth popup + const tokens = await openTraktPopup(url); + + // 3. Call identity check to get user info + existing settings + await fetchTraktIdentity(tokens); + + // 4. Save to storage + saveTraktToStorage(tokens); + + unlockNavigation(); + switchSection('config'); + } catch (err) { + showToast(err.message || 'Trakt login failed', 'error'); + } finally { + setTraktConnecting(false); + } + }); +} + +function initializeTraktLogoutButton() { + const btn = document.getElementById('traktLogoutBtn'); + if (!btn) return; + btn.addEventListener('click', () => { + if (resetApp) resetApp(); + }); +} + +/** + * Open a popup window for Trakt OAuth and resolve when the callback page + * posts a message back to us. + */ +function openTraktPopup(url) { + return new Promise((resolve, reject) => { + const width = 600; + const height = 700; + const left = Math.round(window.screenX + (window.outerWidth - width) / 2); + const top = Math.round(window.screenY + (window.outerHeight - height) / 2); + + const popup = window.open( + url, + 'trakt_oauth', + `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes` + ); + + if (!popup) { + reject(new Error('Could not open the authorization popup. Please allow popups for this site.')); + return; + } + + let settled = false; + + function onMessage(event) { + // Only accept messages from our own origin + if (event.origin !== window.location.origin) return; + const data = event.data; + if (!data || typeof data !== 'object') return; + + if (data.type === 'trakt_auth_success') { + if (!settled) { + settled = true; + cleanup(); + resolve({ + access_token: data.access_token, + refresh_token: data.refresh_token, + expires_at: data.expires_at, + }); + } + } else if (data.type === 'trakt_auth_error') { + if (!settled) { + settled = true; + cleanup(); + reject(new Error(data.error || 'Trakt authorization failed')); + } + } + } + + // Also detect if the user closes the popup manually + const pollTimer = setInterval(() => { + if (popup.closed && !settled) { + settled = true; + cleanup(); + reject(new Error('Authorization window was closed')); + } + }, 500); + + function cleanup() { + window.removeEventListener('message', onMessage); + clearInterval(pollTimer); + } + + window.addEventListener('message', onMessage); + }); +} + +// -------------------------------------------------------------------------- +// Identity fetch + settings population +// -------------------------------------------------------------------------- + +async function fetchTraktIdentity(tokens) { + const payload = { + trakt_access_token: tokens.access_token, + trakt_refresh_token: tokens.refresh_token || null, + trakt_expires_at: tokens.expires_at || null, + }; + + const res = await fetch('/tokens/trakt/identity', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.detail || 'Failed to verify Trakt identity'); + } + + const data = await res.json(); + const display = data.display || data.username || 'Trakt User'; + + showTraktStatus(display); + + if (data.exists && data.settings) { + showToast(`Welcome back, ${display}! Loading your settings…`, 'info', 5000); + populateSettings(data.settings); + + const installHeader = document.querySelector('#sect-install h2'); + const installDesc = document.querySelector('#sect-install p'); + if (installHeader) installHeader.textContent = 'Update Settings'; + if (installDesc) installDesc.textContent = 'Update your preferences and re-install.'; + + const btnText = document.querySelector('#submitBtn .btn-text'); + if (btnText) btnText.textContent = 'Update & Re-Install'; + } else { + showToast(`Welcome, ${display}! Setting up your account…`, 'success', 5000); + } + + // Store display name so the form submit can use it + const traktDisplayInput = document.getElementById('traktDisplayName'); + if (traktDisplayInput) traktDisplayInput.value = display; +} + +function populateSettings(s) { + if (s.language && languageSelect) languageSelect.value = s.language; + + const popularitySelect = document.getElementById('popularitySelect'); + const yearMinInput = document.getElementById('yearMin'); + const yearMaxInput = document.getElementById('yearMax'); + const sortingOrderSelect = document.getElementById('sortingOrderSelect'); + + if (s.popularity && popularitySelect) popularitySelect.value = s.popularity; + if (s.year_min && yearMinInput) yearMinInput.value = s.year_min; + if (s.year_max && yearMaxInput) yearMaxInput.value = s.year_max; + if (window.updateYearSlider) window.updateYearSlider(); + if (s.sorting_order && sortingOrderSelect) sortingOrderSelect.value = s.sorting_order; + + const posterRatingProvider = document.getElementById('posterRatingProvider'); + const posterRatingApiKey = document.getElementById('posterRatingApiKey'); + if (posterRatingProvider && posterRatingApiKey && s.poster_rating?.provider && s.poster_rating?.api_key) { + posterRatingProvider.value = s.poster_rating.provider; + posterRatingApiKey.value = s.poster_rating.api_key; + posterRatingProvider.dispatchEvent(new Event('change')); + } + + const tmdbApiKeyInput = document.getElementById('tmdbApiKey'); + if (s.tmdb_api_key && tmdbApiKeyInput) tmdbApiKeyInput.value = s.tmdb_api_key; + + const simklApiKeyInput = document.getElementById('simklApiKey'); + if (s.simkl_api_key && simklApiKeyInput) simklApiKeyInput.value = s.simkl_api_key; + + const geminiApiKeyInput = document.getElementById('geminiApiKey'); + if (s.gemini_api_key && geminiApiKeyInput) geminiApiKeyInput.value = s.gemini_api_key; + + // Genres + document.querySelectorAll('input[name="movie-genre"]').forEach(cb => cb.checked = false); + document.querySelectorAll('input[name="series-genre"]').forEach(cb => cb.checked = false); + if (s.excluded_movie_genres) s.excluded_movie_genres.forEach(id => { + const cb = document.querySelector(`input[name="movie-genre"][value="${id}"]`); + if (cb) cb.checked = true; + }); + if (s.excluded_series_genres) s.excluded_series_genres.forEach(id => { + const cb = document.querySelector(`input[name="series-genre"][value="${id}"]`); + if (cb) cb.checked = true; + }); + + // Catalogs + if (s.catalogs && Array.isArray(s.catalogs)) { + const catalogs = getCatalogs ? getCatalogs() : []; + s.catalogs.forEach(remote => { + const local = catalogs.find(c => c.id === remote.id); + if (local) { + local.enabled = remote.enabled; + if (remote.name) local.name = remote.name; + if (typeof remote.enabled_movie === 'boolean') local.enabledMovie = remote.enabled_movie; + if (typeof remote.enabled_series === 'boolean') local.enabledSeries = remote.enabled_series; + if (typeof remote.display_at_home === 'boolean') local.display_at_home = remote.display_at_home; + if (typeof remote.shuffle === 'boolean') local.shuffle = remote.shuffle; + } + }); + if (renderCatalogList) renderCatalogList(); + } +} + +// -------------------------------------------------------------------------- +// Auto-login +// -------------------------------------------------------------------------- + +async function attemptTraktAutoLogin() { + const stored = getTraktFromStorage(); + if (!stored?.access_token) return; + + try { + await fetchTraktIdentity(stored); + unlockNavigation(); + switchSection('config'); + } catch (err) { + console.warn('Trakt auto-login failed:', err); + clearTraktFromStorage(); + } +} + +// -------------------------------------------------------------------------- +// UI helpers +// -------------------------------------------------------------------------- + +function setTraktConnecting(loading) { + const btn = document.getElementById('traktConnectBtn'); + if (!btn) return; + const text = btn.querySelector('.btn-text'); + const loader = btn.querySelector('.loader'); + btn.disabled = loading; + if (text) text.classList.toggle('hidden', loading); + if (loader) loader.classList.toggle('hidden', !loading); +} + +export function showTraktStatus(displayName) { + const statusSection = document.getElementById('traktStatusSection'); + const displayEl = document.getElementById('traktStatusDisplay'); + const avatarEl = document.getElementById('traktStatusAvatar'); + const connectBtn = document.getElementById('traktConnectBtn'); + + if (displayEl) displayEl.textContent = displayName; + if (avatarEl) avatarEl.textContent = getInitials(displayName); + if (statusSection) statusSection.classList.remove('hidden'); + if (connectBtn) connectBtn.classList.add('hidden'); + + // Sidebar profile + const userProfileWrapper = document.getElementById('user-profile-dropdown-wrapper'); + const userEmail = document.getElementById('user-email'); + const userAvatar = document.getElementById('user-avatar'); + const loginFormCard = document.getElementById('loginFormCard'); + + if (userEmail) userEmail.textContent = displayName; + if (userAvatar) userAvatar.textContent = getInitials(displayName); + if (userProfileWrapper) userProfileWrapper.classList.remove('hidden'); + // Keep loginFormCard visible so users can navigate back and see the Trakt tab + // Instead, switch to Trakt tab so it's clear which provider is active + try { + const saved = localStorage.getItem('watchly_login_tab'); + if (!saved || saved === 'trakt') { + const traktTab = document.getElementById('tabTrakt'); + if (traktTab) traktTab.click(); + } + } catch(e) {} +} + +function hideTraktStatus() { + const statusSection = document.getElementById('traktStatusSection'); + const connectBtn = document.getElementById('traktConnectBtn'); + if (statusSection) statusSection.classList.add('hidden'); + if (connectBtn) connectBtn.classList.remove('hidden'); + + const userProfileWrapper = document.getElementById('user-profile-dropdown-wrapper'); + if (userProfileWrapper) userProfileWrapper.classList.add('hidden'); +} + +function getInitials(name) { + if (!name) return '?'; + const parts = name.trim().split(/[\s._-]+/); + if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase(); + return name.substring(0, 2).toUpperCase(); +} + +// -------------------------------------------------------------------------- +// Exported helpers for form.js +// -------------------------------------------------------------------------- + +export function getTraktTokensFromStorage() { + return getTraktFromStorage(); +} diff --git a/app/templates/components/section_login.html b/app/templates/components/section_login.html index 1443106..79acb4c 100644 --- a/app/templates/components/section_login.html +++ b/app/templates/components/section_login.html @@ -1,23 +1,20 @@ + + + + + diff --git a/app/templates/components/sidebar.html b/app/templates/components/sidebar.html index 865c3fe..cb66d35 100644 --- a/app/templates/components/sidebar.html +++ b/app/templates/components/sidebar.html @@ -88,7 +88,7 @@

"$REPO/app/services/trakt/__init__.py" +echo " wrote app/services/trakt/__init__.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSBhcHAuY29yZS5iYXNlX2NsaWVudCBpbXBvcnQgQmFzZUNsaWVudAoKCmNsYXNzIFRyYWt0Q2xpZW50KEJhc2VDbGllbnQpOgogICAgIiIiCiAgICBDbGllbnQgZm9yIGludGVyYWN0aW5nIHdpdGggdGhlIFRyYWt0IEFQSS4KICAgICIiIgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjbGllbnRfaWQ6IHN0ciwgYWNjZXNzX3Rva2VuOiBzdHIgfCBOb25lID0gTm9uZSwgdGltZW91dDogZmxvYXQgPSAxMC4wLCBtYXhfcmV0cmllczogaW50ID0gMyk6CiAgICAgICAgaGVhZGVycyA9IHsKICAgICAgICAgICAgIkNvbnRlbnQtVHlwZSI6ICJhcHBsaWNhdGlvbi9qc29uIiwKICAgICAgICAgICAgInRyYWt0LWFwaS12ZXJzaW9uIjogIjIiLAogICAgICAgICAgICAidHJha3QtYXBpLWtleSI6IGNsaWVudF9pZCwKICAgICAgICB9CiAgICAgICAgaWYgYWNjZXNzX3Rva2VuOgogICAgICAgICAgICBoZWFkZXJzWyJBdXRob3JpemF0aW9uIl0gPSBmIkJlYXJlciB7YWNjZXNzX3Rva2VufSIKCiAgICAgICAgc3VwZXIoKS5fX2luaXRfXyhiYXNlX3VybD0iaHR0cHM6Ly9hcGkudHJha3QudHYiLCB0aW1lb3V0PXRpbWVvdXQsIG1heF9yZXRyaWVzPW1heF9yZXRyaWVzLCBoZWFkZXJzPWhlYWRlcnMpCg==" | base64 --decode > "$REPO/app/services/trakt/client.py" +echo " wrote app/services/trakt/client.py" + +mkdir -p "$REPO/app/services/trakt" +echo "aW1wb3J0IHNlY3JldHMKZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKaW1wb3J0IGh0dHB4CmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKCgpjbGFzcyBUcmFrdEF1dGhTZXJ2aWNlOgogICAgIiIiCiAgICBIYW5kbGVzIFRyYWt0IE9BdXRoMiBhdXRoZW50aWNhdGlvbiAoRGV2aWNlIENvZGUgLyBBdXRob3JpemF0aW9uIENvZGUgZmxvd3MpLgogICAgIiIiCgogICAgVE9LRU5fVVJMID0gImh0dHBzOi8vYXBpLnRyYWt0LnR2L29hdXRoL3Rva2VuIgogICAgQVVUSE9SSVpFX1VSTCA9ICJodHRwczovL3RyYWt0LnR2L29hdXRoL2F1dGhvcml6ZSIKICAgIERFVklDRV9DT0RFX1VSTCA9ICJodHRwczovL2FwaS50cmFrdC50di9vYXV0aC9kZXZpY2UvY29kZSIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgY2xpZW50X2lkOiBzdHIsIGNsaWVudF9zZWNyZXQ6IHN0ciwgcmVkaXJlY3RfdXJpOiBzdHIpOgogICAgICAgIHNlbGYuY2xpZW50X2lkID0gY2xpZW50X2lkCiAgICAgICAgc2VsZi5jbGllbnRfc2VjcmV0ID0gY2xpZW50X3NlY3JldAogICAgICAgIHNlbGYucmVkaXJlY3RfdXJpID0gcmVkaXJlY3RfdXJpCgogICAgZGVmIGdldF9hdXRob3JpemVfdXJsKHNlbGYsIHN0YXRlOiBzdHIgfCBOb25lID0gTm9uZSkgLT4gdHVwbGVbc3RyLCBzdHJdOgogICAgICAgICIiIgogICAgICAgIEJ1aWxkIHRoZSBPQXV0aDIgYXV0aG9yaXphdGlvbiBVUkwgYW5kIHJldHVybiBpdCBhbG9uZyB3aXRoIHRoZSBzdGF0ZSB2YWx1ZS4KICAgICAgICAiIiIKICAgICAgICBpZiBub3Qgc3RhdGU6CiAgICAgICAgICAgIHN0YXRlID0gc2VjcmV0cy50b2tlbl91cmxzYWZlKDE2KQoKICAgICAgICBwYXJhbXMgPSB7CiAgICAgICAgICAgICJyZXNwb25zZV90eXBlIjogImNvZGUiLAogICAgICAgICAgICAiY2xpZW50X2lkIjogc2VsZi5jbGllbnRfaWQsCiAgICAgICAgICAgICJyZWRpcmVjdF91cmkiOiBzZWxmLnJlZGlyZWN0X3VyaSwKICAgICAgICAgICAgInN0YXRlIjogc3RhdGUsCiAgICAgICAgfQogICAgICAgIHF1ZXJ5ID0gIiYiLmpvaW4oZiJ7a309e3Z9IiBmb3IgaywgdiBpbiBwYXJhbXMuaXRlbXMoKSkKICAgICAgICByZXR1cm4gZiJ7c2VsZi5BVVRIT1JJWkVfVVJMfT97cXVlcnl9Iiwgc3RhdGUKCiAgICBhc3luYyBkZWYgZXhjaGFuZ2VfY29kZShzZWxmLCBjb2RlOiBzdHIpIC0+IGRpY3Rbc3RyLCBBbnldOgogICAgICAgICIiIgogICAgICAgIEV4Y2hhbmdlIGFuIGF1dGhvcml6YXRpb24gY29kZSBmb3IgdG9rZW5zLgogICAgICAgIFJldHVybnMgZGljdCB3aXRoIGFjY2Vzc190b2tlbiwgcmVmcmVzaF90b2tlbiwgZXhwaXJlc19pbiwgZXRjLgogICAgICAgICIiIgogICAgICAgIHBheWxvYWQgPSB7CiAgICAgICAgICAgICJjb2RlIjogY29kZSwKICAgICAgICAgICAgImNsaWVudF9pZCI6IHNlbGYuY2xpZW50X2lkLAogICAgICAgICAgICAiY2xpZW50X3NlY3JldCI6IHNlbGYuY2xpZW50X3NlY3JldCwKICAgICAgICAgICAgInJlZGlyZWN0X3VyaSI6IHNlbGYucmVkaXJlY3RfdXJpLAogICAgICAgICAgICAiZ3JhbnRfdHlwZSI6ICJhdXRob3JpemF0aW9uX2NvZGUiLAogICAgICAgIH0KICAgICAgICB0cnk6CiAgICAgICAgICAgIGFzeW5jIHdpdGggaHR0cHguQXN5bmNDbGllbnQodGltZW91dD0xNS4wKSBhcyBjbGllbnQ6CiAgICAgICAgICAgICAgICByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5wb3N0KHNlbGYuVE9LRU5fVVJMLCBqc29uPXBheWxvYWQpCiAgICAgICAgICAgICAgICByZXNwb25zZS5yYWlzZV9mb3Jfc3RhdHVzKCkKICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZS5qc29uKCkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJUcmFrdCB0b2tlbiBleGNoYW5nZSBmYWlsZWQ6IHtlfSIpCiAgICAgICAgICAgIHJhaXNlCgogICAgYXN5bmMgZGVmIHJlZnJlc2hfdG9rZW4oc2VsZiwgcmVmcmVzaF90b2tlbl92YWx1ZTogc3RyKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICAiIiJSZWZyZXNoIGFuIGV4cGlyZWQgYWNjZXNzIHRva2VuLiIiIgogICAgICAgIHBheWxvYWQgPSB7CiAgICAgICAgICAgICJyZWZyZXNoX3Rva2VuIjogcmVmcmVzaF90b2tlbl92YWx1ZSwKICAgICAgICAgICAgImNsaWVudF9pZCI6IHNlbGYuY2xpZW50X2lkLAogICAgICAgICAgICAiY2xpZW50X3NlY3JldCI6IHNlbGYuY2xpZW50X3NlY3JldCwKICAgICAgICAgICAgInJlZGlyZWN0X3VyaSI6IHNlbGYucmVkaXJlY3RfdXJpLAogICAgICAgICAgICAiZ3JhbnRfdHlwZSI6ICJyZWZyZXNoX3Rva2VuIiwKICAgICAgICB9CiAgICAgICAgdHJ5OgogICAgICAgICAgICBhc3luYyB3aXRoIGh0dHB4LkFzeW5jQ2xpZW50KHRpbWVvdXQ9MTUuMCkgYXMgY2xpZW50OgogICAgICAgICAgICAgICAgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQucG9zdChzZWxmLlRPS0VOX1VSTCwganNvbj1wYXlsb2FkKQogICAgICAgICAgICAgICAgcmVzcG9uc2UucmFpc2VfZm9yX3N0YXR1cygpCiAgICAgICAgICAgICAgICByZXR1cm4gcmVzcG9uc2UuanNvbigpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiVHJha3QgdG9rZW4gcmVmcmVzaCBmYWlsZWQ6IHtlfSIpCiAgICAgICAgICAgIHJhaXNlCg==" | base64 --decode > "$REPO/app/services/trakt/auth.py" +echo " wrote app/services/trakt/auth.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBsb2d1cnUgaW1wb3J0IGxvZ2dlcgoKZnJvbSBhcHAuc2VydmljZXMudHJha3QuY2xpZW50IGltcG9ydCBUcmFrdENsaWVudAoKCmNsYXNzIFRyYWt0VXNlclNlcnZpY2U6CiAgICAiIiIKICAgIEZldGNoZXMgdGhlIGF1dGhlbnRpY2F0ZWQgVHJha3QgdXNlcidzIHByb2ZpbGUuCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgY2xpZW50OiBUcmFrdENsaWVudCk6CiAgICAgICAgc2VsZi5jbGllbnQgPSBjbGllbnQKCiAgICBhc3luYyBkZWYgZ2V0X3VzZXJfaW5mbyhzZWxmKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICAiIiJSZXR1cm4gdGhlIGF1dGhlbnRpY2F0ZWQgdXNlcidzIHByb2ZpbGUgKHVzZXJuYW1lLCBuYW1lLCBldGMuKS4iIiIKICAgICAgICB0cnk6CiAgICAgICAgICAgIGRhdGEgPSBhd2FpdCBzZWxmLmNsaWVudC5nZXQoIi91c2Vycy9tZT9leHRlbmRlZD1mdWxsIikKICAgICAgICAgICAgcmV0dXJuIGRhdGEKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJGYWlsZWQgdG8gZmV0Y2ggVHJha3QgdXNlciBpbmZvOiB7ZX0iKQogICAgICAgICAgICByYWlzZQo=" | base64 --decode > "$REPO/app/services/trakt/user.py" +echo " wrote app/services/trakt/user.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBsb2d1cnUgaW1wb3J0IGxvZ2dlcgoKZnJvbSBhcHAuc2VydmljZXMudHJha3QuY2xpZW50IGltcG9ydCBUcmFrdENsaWVudAoKCmNsYXNzIFRyYWt0TGlicmFyeVNlcnZpY2U6CiAgICAiIiIKICAgIEZldGNoZXMgYW5kIG5vcm1hbGlzZXMgd2F0Y2ggaGlzdG9yeSBmcm9tIFRyYWt0IGludG8gdGhlIHNhbWUgc2hhcGUKICAgIHRoYXQgdGhlIFN0cmVtaW8gbGlicmFyeSBzZXJ2aWNlIHByb2R1Y2VzLCBzbyB0aGUgcmVzdCBvZiB0aGUgYXBwCiAgICBuZWVkcyBubyBjaGFuZ2VzLgogICAgIiIiCgogICAgIyBIb3cgbWFueSBwYWdlcyBvZiBoaXN0b3J5IHRvIHB1bGwgKDEgMDAwIGl0ZW1zIC8gcGFnZSkKICAgIE1BWF9QQUdFUyA9IDEwCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGNsaWVudDogVHJha3RDbGllbnQpOgogICAgICAgIHNlbGYuY2xpZW50ID0gY2xpZW50CgogICAgIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICAgICMgUHVibGljIGhlbHBlcnMKICAgICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgogICAgYXN5bmMgZGVmIGdldF9saWJyYXJ5X2l0ZW1zKHNlbGYpIC0+IGRpY3Rbc3RyLCBsaXN0W2RpY3Rbc3RyLCBBbnldXV06CiAgICAgICAgIiIiCiAgICAgICAgUmV0dXJuIGxpYnJhcnkgaXRlbXMgaW4gdGhlIHNhbWUgc2hhcGUgYXMgU3RyZW1pb0xpYnJhcnlTZXJ2aWNlOgogICAgICAgIHsgIndhdGNoZWQiOiBbLi4uXSwgImxvdmVkIjogW10sICJsaWtlZCI6IFtdLCAiYWRkZWQiOiBbXSwgInJlbW92ZWQiOiBbXSB9CgogICAgICAgIEVhY2ggaXRlbSBjb250YWlucyBhdCBtaW5pbXVtOgogICAgICAgICAgX2lkICAgICAgIOKAkyB0dC4uLiBvciB0bWRiOi4uLiBzdHJpbmcKICAgICAgICAgIHR5cGUgICAgICDigJMgIm1vdmllIiB8ICJzZXJpZXMiCiAgICAgICAgICBuYW1lICAgICAg4oCTIHRpdGxlCiAgICAgICAgIiIiCiAgICAgICAgdHJ5OgogICAgICAgICAgICBtb3ZpZXMgPSBhd2FpdCBzZWxmLl9nZXRfaGlzdG9yeSgibW92aWVzIikKICAgICAgICAgICAgc2hvd3MgPSBhd2FpdCBzZWxmLl9nZXRfaGlzdG9yeSgic2hvd3MiKQoKICAgICAgICAgICAgd2F0Y2hlZDogbGlzdFtkaWN0W3N0ciwgQW55XV0gPSBbXQogICAgICAgICAgICBzZWVuX2lkczogc2V0W3N0cl0gPSBzZXQoKQoKICAgICAgICAgICAgZm9yIHJhdyBpbiBtb3ZpZXM6CiAgICAgICAgICAgICAgICBpdGVtID0gc2VsZi5fbm9ybWFsaXNlX21vdmllKHJhdykKICAgICAgICAgICAgICAgIGlmIGl0ZW0gYW5kIGl0ZW1bIl9pZCJdIG5vdCBpbiBzZWVuX2lkczoKICAgICAgICAgICAgICAgICAgICBzZWVuX2lkcy5hZGQoaXRlbVsiX2lkIl0pCiAgICAgICAgICAgICAgICAgICAgd2F0Y2hlZC5hcHBlbmQoaXRlbSkKCiAgICAgICAgICAgIGZvciByYXcgaW4gc2hvd3M6CiAgICAgICAgICAgICAgICBpdGVtID0gc2VsZi5fbm9ybWFsaXNlX3Nob3cocmF3KQogICAgICAgICAgICAgICAgaWYgaXRlbSBhbmQgaXRlbVsiX2lkIl0gbm90IGluIHNlZW5faWRzOgogICAgICAgICAgICAgICAgICAgIHNlZW5faWRzLmFkZChpdGVtWyJfaWQiXSkKICAgICAgICAgICAgICAgICAgICB3YXRjaGVkLmFwcGVuZChpdGVtKQoKICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbVHJha3RdIGxpYnJhcnk6IHtsZW4od2F0Y2hlZCl9IHdhdGNoZWQgaXRlbXMgKHtsZW4obW92aWVzKX0gbW92aWVzLCB7bGVuKHNob3dzKX0gc2hvd3MpIikKCiAgICAgICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICAgICAid2F0Y2hlZCI6IHdhdGNoZWQsCiAgICAgICAgICAgICAgICAibG92ZWQiOiBbXSwKICAgICAgICAgICAgICAgICJsaWtlZCI6IFtdLAogICAgICAgICAgICAgICAgImFkZGVkIjogW10sCiAgICAgICAgICAgICAgICAicmVtb3ZlZCI6IFtdLAogICAgICAgICAgICB9CiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW1RyYWt0XSBGYWlsZWQgdG8gZ2V0IGxpYnJhcnkgaXRlbXM6IHtlfSIpCiAgICAgICAgICAgIHJldHVybiB7IndhdGNoZWQiOiBbXSwgImxvdmVkIjogW10sICJsaWtlZCI6IFtdLCAiYWRkZWQiOiBbXSwgInJlbW92ZWQiOiBbXX0KCiAgICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICAgIyBQcml2YXRlIGhlbHBlcnMKICAgICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgogICAgYXN5bmMgZGVmIF9nZXRfaGlzdG9yeShzZWxmLCBtZWRpYV90eXBlOiBzdHIpIC0+IGxpc3RbZGljdFtzdHIsIEFueV1dOgogICAgICAgICIiIgogICAgICAgIFB1bGwgd2F0Y2hlZCBoaXN0b3J5IGZvciAqbWVkaWFfdHlwZSogKCJtb3ZpZXMiIHwgInNob3dzIikuCiAgICAgICAgVXNlcyB0aGUgL3VzZXJzL21lL3dhdGNoZWQvOnR5cGUgZW5kcG9pbnQgd2hpY2ggcmV0dXJucyBhIGRlZHVwbGljYXRlZAogICAgICAgIGxpc3Qgb2YgZXZlcnl0aGluZyB0aGUgdXNlciBoYXMgZXZlciBwbGF5ZWQgKG5vIHBhZ2luZyBuZWVkZWQpLgogICAgICAgICIiIgogICAgICAgIHRyeToKICAgICAgICAgICAgZGF0YSA9IGF3YWl0IHNlbGYuY2xpZW50LmdldChmIi91c2Vycy9tZS93YXRjaGVkL3ttZWRpYV90eXBlfSIpCiAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoZGF0YSwgbGlzdCk6CiAgICAgICAgICAgICAgICByZXR1cm4gZGF0YQogICAgICAgICAgICByZXR1cm4gW10KICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW1RyYWt0XSBGYWlsZWQgdG8gZmV0Y2gge21lZGlhX3R5cGV9IGhpc3Rvcnk6IHtlfSIpCiAgICAgICAgICAgIHJldHVybiBbXQoKICAgIGRlZiBfZ2V0X2lkKHNlbGYsIGlkczogZGljdFtzdHIsIEFueV0pIC0+IHN0ciB8IE5vbmU6CiAgICAgICAgIiIiUmV0dXJuIHRoZSBiZXN0IGF2YWlsYWJsZSBjYW5vbmljYWwgSUQgKHByZWZlciBJTURiLCBmYWxsIGJhY2sgdG8gVE1EQikuIiIiCiAgICAgICAgaW1kYiA9IGlkcy5nZXQoImltZGIiKQogICAgICAgIGlmIGltZGI6CiAgICAgICAgICAgIHJldHVybiBpbWRiICAjIGUuZy4gInR0MTIzNDU2NyIKICAgICAgICB0bWRiID0gaWRzLmdldCgidG1kYiIpCiAgICAgICAgaWYgdG1kYjoKICAgICAgICAgICAgcmV0dXJuIGYidG1kYjp7dG1kYn0iCiAgICAgICAgcmV0dXJuIE5vbmUKCiAgICBkZWYgX25vcm1hbGlzZV9tb3ZpZShzZWxmLCByYXc6IGRpY3Rbc3RyLCBBbnldKSAtPiBkaWN0W3N0ciwgQW55XSB8IE5vbmU6CiAgICAgICAgbW92aWUgPSByYXcuZ2V0KCJtb3ZpZSIsIHt9KQogICAgICAgIGlkcyA9IG1vdmllLmdldCgiaWRzIiwge30pCiAgICAgICAgY2Fub25pY2FsX2lkID0gc2VsZi5fZ2V0X2lkKGlkcykKICAgICAgICBpZiBub3QgY2Fub25pY2FsX2lkOgogICAgICAgICAgICByZXR1cm4gTm9uZQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJfaWQiOiBjYW5vbmljYWxfaWQsCiAgICAgICAgICAgICJ0eXBlIjogIm1vdmllIiwKICAgICAgICAgICAgIm5hbWUiOiBtb3ZpZS5nZXQoInRpdGxlIiwgIiIpLAogICAgICAgICAgICAieWVhciI6IG1vdmllLmdldCgieWVhciIpLAogICAgICAgICAgICAic3RhdGUiOiB7CiAgICAgICAgICAgICAgICAidGltZXNXYXRjaGVkIjogcmF3LmdldCgicGxheXMiLCAxKSwKICAgICAgICAgICAgICAgICJmbGFnZ2VkV2F0Y2hlZCI6IDEsCiAgICAgICAgICAgICAgICAibGFzdFdhdGNoZWQiOiByYXcuZ2V0KCJsYXN0X3dhdGNoZWRfYXQiLCAiIiksCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJfc291cmNlIjogInRyYWt0IiwKICAgICAgICB9CgogICAgZGVmIF9ub3JtYWxpc2Vfc2hvdyhzZWxmLCByYXc6IGRpY3Rbc3RyLCBBbnldKSAtPiBkaWN0W3N0ciwgQW55XSB8IE5vbmU6CiAgICAgICAgc2hvdyA9IHJhdy5nZXQoInNob3ciLCB7fSkKICAgICAgICBpZHMgPSBzaG93LmdldCgiaWRzIiwge30pCiAgICAgICAgY2Fub25pY2FsX2lkID0gc2VsZi5fZ2V0X2lkKGlkcykKICAgICAgICBpZiBub3QgY2Fub25pY2FsX2lkOgogICAgICAgICAgICByZXR1cm4gTm9uZQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJfaWQiOiBjYW5vbmljYWxfaWQsCiAgICAgICAgICAgICJ0eXBlIjogInNlcmllcyIsCiAgICAgICAgICAgICJuYW1lIjogc2hvdy5nZXQoInRpdGxlIiwgIiIpLAogICAgICAgICAgICAieWVhciI6IHNob3cuZ2V0KCJ5ZWFyIiksCiAgICAgICAgICAgICJzdGF0ZSI6IHsKICAgICAgICAgICAgICAgICJ0aW1lc1dhdGNoZWQiOiByYXcuZ2V0KCJwbGF5cyIsIDEpLAogICAgICAgICAgICAgICAgImZsYWdnZWRXYXRjaGVkIjogMSwKICAgICAgICAgICAgICAgICJsYXN0V2F0Y2hlZCI6IHJhdy5nZXQoImxhc3Rfd2F0Y2hlZF9hdCIsICIiKSwKICAgICAgICAgICAgfSwKICAgICAgICAgICAgIl9zb3VyY2UiOiAidHJha3QiLAogICAgICAgIH0K" | base64 --decode > "$REPO/app/services/trakt/library.py" +echo " wrote app/services/trakt/library.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSBhcHAuc2VydmljZXMudHJha3QuYXV0aCBpbXBvcnQgVHJha3RBdXRoU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy50cmFrdC5jbGllbnQgaW1wb3J0IFRyYWt0Q2xpZW50CmZyb20gYXBwLnNlcnZpY2VzLnRyYWt0LmxpYnJhcnkgaW1wb3J0IFRyYWt0TGlicmFyeVNlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMudHJha3QudXNlciBpbXBvcnQgVHJha3RVc2VyU2VydmljZQoKCmNsYXNzIFRyYWt0QnVuZGxlOgogICAgIiIiCiAgICBVbmlmaWVkIGJ1bmRsZSBmb3IgYWxsIFRyYWt0LXJlbGF0ZWQgc2VydmljZXMuCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgY2xpZW50X2lkOiBzdHIsIGNsaWVudF9zZWNyZXQ6IHN0ciwgcmVkaXJlY3RfdXJpOiBzdHIsIGFjY2Vzc190b2tlbjogc3RyIHwgTm9uZSA9IE5vbmUpOgogICAgICAgIHNlbGYuYXV0aCA9IFRyYWt0QXV0aFNlcnZpY2UoCiAgICAgICAgICAgIGNsaWVudF9pZD1jbGllbnRfaWQsCiAgICAgICAgICAgIGNsaWVudF9zZWNyZXQ9Y2xpZW50X3NlY3JldCwKICAgICAgICAgICAgcmVkaXJlY3RfdXJpPXJlZGlyZWN0X3VyaSwKICAgICAgICApCiAgICAgICAgc2VsZi5fY2xpZW50ID0gVHJha3RDbGllbnQoY2xpZW50X2lkPWNsaWVudF9pZCwgYWNjZXNzX3Rva2VuPWFjY2Vzc190b2tlbikKICAgICAgICBzZWxmLnVzZXIgPSBUcmFrdFVzZXJTZXJ2aWNlKHNlbGYuX2NsaWVudCkKICAgICAgICBzZWxmLmxpYnJhcnkgPSBUcmFrdExpYnJhcnlTZXJ2aWNlKHNlbGYuX2NsaWVudCkKCiAgICBhc3luYyBkZWYgY2xvc2Uoc2VsZik6CiAgICAgICAgYXdhaXQgc2VsZi5fY2xpZW50LmNsb3NlKCkK" | base64 --decode > "$REPO/app/services/trakt/service.py" +echo " wrote app/services/trakt/service.py" + +mkdir -p "$REPO/app/api/endpoints" +echo "IiIiClRyYWt0IE9BdXRoIGFuZCB0b2tlbi1jcmVhdGlvbiBlbmRwb2ludHMuCgpGbG93OgoxLiBHRVQgIC90b2tlbnMvdHJha3QvY29uZmlnICAgICAgICDihpIgcmV0dXJucyBjbGllbnRfaWQgKyB3aGV0aGVyIFRyYWt0IGlzIGNvbmZpZ3VyZWQKMi4gR0VUICAvdG9rZW5zL3RyYWt0L2F1dGhvcml6ZSAgICAg4oaSIHJldHVybnMgdGhlIFRyYWt0IE9BdXRoMiBVUkwgZm9yIHRoZSBwb3B1cAozLiBHRVQgIC90b2tlbnMvdHJha3QvY2FsbGJhY2sgICAgICDihpIgVHJha3QgcmVkaXJlY3RzIGhlcmUgYWZ0ZXIgdXNlciBhcHByb3ZlczsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjaGFuZ2VzIGNvZGUgZm9yIHRva2VucywgdGhlbiBwb3N0cyBhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgdG8gdGhlIG9wZW5lciB3aW5kb3cgYW5kIGNsb3Nlcy4KNC4gUE9TVCAvdG9rZW5zL3RyYWt0ICAgICAgICAgICAgICAg4oaSIENyZWF0ZXMvdXBkYXRlcyBhIFdhdGNobHkgYWNjb3VudCB1c2luZyBhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYWt0IGFjY2Vzc190b2tlbiAoc2FtZSBib2R5IHNoYXBlIGFzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZSBtYWluIC90b2tlbnMvIGVuZHBvaW50IHBsdXMgdHJha3RfYWNjZXNzX3Rva2VuKS4KNS4gUE9TVCAvdG9rZW5zL3RyYWt0L2lkZW50aXR5ICAgICAg4oaSIExpZ2h0d2VpZ2h0IGlkZW50aXR5LWNoZWNrIChyZXR1cm5zIHVzZXIgaW5mbwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICArIGV4aXN0aW5nIHNldHRpbmdzIGlmIGFjY291bnQgZXhpc3RzKS4KIiIiCgppbXBvcnQgc2VjcmV0cwpmcm9tIGRhdGV0aW1lIGltcG9ydCBkYXRldGltZSwgdGltZXpvbmUKZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBmYXN0YXBpIGltcG9ydCBBUElSb3V0ZXIsIEhUVFBFeGNlcHRpb24sIFJlcXVlc3QKZnJvbSBmYXN0YXBpLnJlc3BvbnNlcyBpbXBvcnQgSFRNTFJlc3BvbnNlCmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKZnJvbSBweWRhbnRpYyBpbXBvcnQgQmFzZU1vZGVsLCBGaWVsZAoKZnJvbSBhcHAuY29yZS5jb25maWcgaW1wb3J0IHNldHRpbmdzCmZyb20gYXBwLmNvcmUuc2VjdXJpdHkgaW1wb3J0IHJlZGFjdF90b2tlbgpmcm9tIGFwcC5jb3JlLnNldHRpbmdzIGltcG9ydCBDYXRhbG9nQ29uZmlnLCBQb3N0ZXJSYXRpbmdDb25maWcsIFVzZXJTZXR0aW5ncywgZ2V0X2RlZmF1bHRfc2V0dGluZ3MKZnJvbSBhcHAuc2VydmljZXMubWFuaWZlc3QgaW1wb3J0IG1hbmlmZXN0X3NlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMudG9rZW5fc3RvcmUgaW1wb3J0IHRva2VuX3N0b3JlCmZyb20gYXBwLnNlcnZpY2VzLnRyYWt0LnNlcnZpY2UgaW1wb3J0IFRyYWt0QnVuZGxlCgpyb3V0ZXIgPSBBUElSb3V0ZXIocHJlZml4PSIvdG9rZW5zL3RyYWt0IiwgdGFncz1bInRyYWt0Il0pCgojIEluLW1lbW9yeSBzdG9yZSBmb3Igc2hvcnQtbGl2ZWQgT0F1dGggc3RhdGUgdmFsdWVzLgojIFRoaXMgaXMgcGVyLXByb2Nlc3Mgb25seTsgZm9yIG11bHRpLXdvcmtlciBkZXBsb3ltZW50cyBSZWRpcyB3b3VsZCBiZSBiZXR0ZXIsCiMgYnV0IENTUkYgcHJvdGVjdGlvbiBpcyBzdGlsbCBpbXByb3ZlZCB2ZXJzdXMgbm90aGluZy4KX29hdXRoX3N0YXRlczogZGljdFtzdHIsIHN0cl0gPSB7fQoKCmRlZiBfZ2V0X2J1bmRsZShhY2Nlc3NfdG9rZW46IHN0ciB8IE5vbmUgPSBOb25lKSAtPiBUcmFrdEJ1bmRsZToKICAgIGNsaWVudF9pZCA9IHNldHRpbmdzLlRSQUtUX0NMSUVOVF9JRAogICAgY2xpZW50X3NlY3JldCA9IHNldHRpbmdzLlRSQUtUX0NMSUVOVF9TRUNSRVQKICAgIGlmIG5vdCBjbGllbnRfaWQgb3Igbm90IGNsaWVudF9zZWNyZXQ6CiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbigKICAgICAgICAgICAgc3RhdHVzX2NvZGU9NTAzLAogICAgICAgICAgICBkZXRhaWw9IlRyYWt0IGludGVncmF0aW9uIGlzIG5vdCBjb25maWd1cmVkIG9uIHRoaXMgc2VydmVyLiBTZXQgVFJBS1RfQ0xJRU5UX0lEIGFuZCBUUkFLVF9DTElFTlRfU0VDUkVULiIsCiAgICAgICAgKQogICAgcmVkaXJlY3RfdXJpID0gZiJ7c2V0dGluZ3MuSE9TVF9OQU1FfS90b2tlbnMvdHJha3QvY2FsbGJhY2siCiAgICByZXR1cm4gVHJha3RCdW5kbGUoCiAgICAgICAgY2xpZW50X2lkPWNsaWVudF9pZCwKICAgICAgICBjbGllbnRfc2VjcmV0PWNsaWVudF9zZWNyZXQsCiAgICAgICAgcmVkaXJlY3RfdXJpPXJlZGlyZWN0X3VyaSwKICAgICAgICBhY2Nlc3NfdG9rZW49YWNjZXNzX3Rva2VuLAogICAgKQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgUmVxdWVzdCAvIFJlc3BvbnNlIG1vZGVscwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmNsYXNzIFRyYWt0VG9rZW5SZXF1ZXN0KEJhc2VNb2RlbCk6CiAgICB0cmFrdF9hY2Nlc3NfdG9rZW46IHN0ciA9IEZpZWxkKC4uLiwgZGVzY3JpcHRpb249IlRyYWt0IE9BdXRoMiBhY2Nlc3MgdG9rZW4iKQogICAgdHJha3RfcmVmcmVzaF90b2tlbjogc3RyIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSwgZGVzY3JpcHRpb249IlRyYWt0IE9BdXRoMiByZWZyZXNoIHRva2VuIikKICAgIHRyYWt0X2V4cGlyZXNfYXQ6IGludCB8IE5vbmUgPSBGaWVsZChkZWZhdWx0PU5vbmUsIGRlc2NyaXB0aW9uPSJVbml4IHRpbWVzdGFtcCB3aGVuIGFjY2VzcyB0b2tlbiBleHBpcmVzIikKICAgIGNhdGFsb2dzOiBsaXN0W0NhdGFsb2dDb25maWddIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSkKICAgIGxhbmd1YWdlOiBzdHIgPSBGaWVsZChkZWZhdWx0PSJlbi1VUyIpCiAgICBwb3N0ZXJfcmF0aW5nOiBQb3N0ZXJSYXRpbmdDb25maWcgfCBOb25lID0gRmllbGQoZGVmYXVsdD1Ob25lKQogICAgZXhjbHVkZWRfbW92aWVfZ2VucmVzOiBsaXN0W3N0cl0gPSBGaWVsZChkZWZhdWx0X2ZhY3Rvcnk9bGlzdCkKICAgIGV4Y2x1ZGVkX3Nlcmllc19nZW5yZXM6IGxpc3Rbc3RyXSA9IEZpZWxkKGRlZmF1bHRfZmFjdG9yeT1saXN0KQogICAgcG9wdWxhcml0eTogc3RyID0gRmllbGQoZGVmYXVsdD0iYmFsYW5jZWQiKQogICAgeWVhcl9taW46IGludCA9IEZpZWxkKGRlZmF1bHQ9MjAxMCkKICAgIHllYXJfbWF4OiBpbnQgPSBGaWVsZChkZWZhdWx0PTIwMjUpCiAgICBzb3J0aW5nX29yZGVyOiBzdHIgPSBGaWVsZChkZWZhdWx0PSJkZWZhdWx0IikKICAgIHNpbWtsX2FwaV9rZXk6IHN0ciB8IE5vbmUgPSBGaWVsZChkZWZhdWx0PU5vbmUpCiAgICBnZW1pbmlfYXBpX2tleTogc3RyIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSkKICAgIHRtZGJfYXBpX2tleTogc3RyIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSkKCgpjbGFzcyBUcmFrdFRva2VuUmVzcG9uc2UoQmFzZU1vZGVsKToKICAgIHRva2VuOiBzdHIKICAgIG1hbmlmZXN0VXJsOiBzdHIKICAgIGV4cGlyZXNJblNlY29uZHM6IGludCB8IE5vbmUgPSBOb25lCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBFbmRwb2ludHMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpAcm91dGVyLmdldCgiL2NvbmZpZyIpCmFzeW5jIGRlZiB0cmFrdF9jb25maWcoKToKICAgICIiIlJldHVybiB3aGV0aGVyIFRyYWt0IGlzIGNvbmZpZ3VyZWQgYW5kIHRoZSBjbGllbnRfaWQgKG5lZWRlZCBmb3IgdGhlIHBvcHVwKS4iIiIKICAgIGNvbmZpZ3VyZWQgPSBib29sKHNldHRpbmdzLlRSQUtUX0NMSUVOVF9JRCBhbmQgc2V0dGluZ3MuVFJBS1RfQ0xJRU5UX1NFQ1JFVCkKICAgIHJldHVybiB7CiAgICAgICAgImNvbmZpZ3VyZWQiOiBjb25maWd1cmVkLAogICAgICAgICJjbGllbnRfaWQiOiBzZXR0aW5ncy5UUkFLVF9DTElFTlRfSUQgaWYgY29uZmlndXJlZCBlbHNlIE5vbmUsCiAgICB9CgoKQHJvdXRlci5nZXQoIi9hdXRob3JpemUiKQphc3luYyBkZWYgdHJha3RfYXV0aG9yaXplKCk6CiAgICAiIiJSZXR1cm4gdGhlIFRyYWt0IE9BdXRoMiBhdXRob3JpemF0aW9uIFVSTCBmb3IgdGhlIGZyb250ZW5kIHBvcHVwLiIiIgogICAgYnVuZGxlID0gX2dldF9idW5kbGUoKQogICAgc3RhdGUgPSBzZWNyZXRzLnRva2VuX3VybHNhZmUoMTYpCiAgICBfb2F1dGhfc3RhdGVzW3N0YXRlXSA9IHN0YXRlICAjIG1pbmltYWwgYW50aS1DU1JGCiAgICB1cmwsIF8gPSBidW5kbGUuYXV0aC5nZXRfYXV0aG9yaXplX3VybChzdGF0ZT1zdGF0ZSkKICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCiAgICByZXR1cm4geyJ1cmwiOiB1cmwsICJzdGF0ZSI6IHN0YXRlfQoKCkByb3V0ZXIuZ2V0KCIvY2FsbGJhY2siLCByZXNwb25zZV9jbGFzcz1IVE1MUmVzcG9uc2UpCmFzeW5jIGRlZiB0cmFrdF9jYWxsYmFjayhyZXF1ZXN0OiBSZXF1ZXN0LCBjb2RlOiBzdHIgfCBOb25lID0gTm9uZSwgc3RhdGU6IHN0ciB8IE5vbmUgPSBOb25lLCBlcnJvcjogc3RyIHwgTm9uZSA9IE5vbmUpOgogICAgIiIiCiAgICBUcmFrdCByZWRpcmVjdHMgaGVyZSBhZnRlciB0aGUgdXNlciBhcHByb3ZlcyAob3IgZGVuaWVzKSB0aGUgYXBwLgogICAgV2UgZXhjaGFuZ2UgdGhlIGNvZGUgZm9yIHRva2VucyBhbmQgcG9zdCBhIG1lc3NhZ2UgYmFjayB0byB0aGUgb3BlbmVyCiAgICB3aW5kb3csIHRoZW4gY2xvc2UgdGhlIHBvcHVwLgogICAgIiIiCiAgICAjIC0tLSBlcnJvciBmcm9tIFRyYWt0IC0tLQogICAgaWYgZXJyb3Igb3Igbm90IGNvZGU6CiAgICAgICAgcmV0dXJuIEhUTUxSZXNwb25zZShfcG9wdXBfY2xvc2VfaHRtbChzdWNjZXNzPUZhbHNlLCBlcnJvcj1lcnJvciBvciAiQXV0aG9yaXphdGlvbiBjYW5jZWxsZWQiKSkKCiAgICAjIC0tLSBleGNoYW5nZSBjb2RlIGZvciB0b2tlbnMgLS0tCiAgICB0cnk6CiAgICAgICAgYnVuZGxlID0gX2dldF9idW5kbGUoKQogICAgICAgIHRva2VuX2RhdGEgPSBhd2FpdCBidW5kbGUuYXV0aC5leGNoYW5nZV9jb2RlKGNvZGUpCiAgICAgICAgYXdhaXQgYnVuZGxlLmNsb3NlKCkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgIGxvZ2dlci5lcnJvcihmIlRyYWt0IGNhbGxiYWNrOiB0b2tlbiBleGNoYW5nZSBmYWlsZWQ6IHtleGN9IikKICAgICAgICByZXR1cm4gSFRNTFJlc3BvbnNlKF9wb3B1cF9jbG9zZV9odG1sKHN1Y2Nlc3M9RmFsc2UsIGVycm9yPSJUb2tlbiBleGNoYW5nZSBmYWlsZWQiKSkKCiAgICBhY2Nlc3NfdG9rZW4gPSB0b2tlbl9kYXRhLmdldCgiYWNjZXNzX3Rva2VuIikKICAgIHJlZnJlc2hfdG9rZW4gPSB0b2tlbl9kYXRhLmdldCgicmVmcmVzaF90b2tlbiIpCiAgICBleHBpcmVzX2luID0gdG9rZW5fZGF0YS5nZXQoImV4cGlyZXNfaW4iLCA3Nzc2MDAwKSAgIyBkZWZhdWx0IDkwIGRheXMKCiAgICBpZiBub3QgYWNjZXNzX3Rva2VuOgogICAgICAgIHJldHVybiBIVE1MUmVzcG9uc2UoX3BvcHVwX2Nsb3NlX2h0bWwoc3VjY2Vzcz1GYWxzZSwgZXJyb3I9Ik5vIGFjY2VzcyB0b2tlbiByZWNlaXZlZCIpKQoKICAgICMgQ2FsY3VsYXRlIGV4cGlyeSB1bml4IHRpbWVzdGFtcAogICAgaW1wb3J0IHRpbWUKICAgIGV4cGlyZXNfYXQgPSBpbnQodGltZS50aW1lKCkpICsgZXhwaXJlc19pbgoKICAgIHJldHVybiBIVE1MUmVzcG9uc2UoCiAgICAgICAgX3BvcHVwX2Nsb3NlX2h0bWwoCiAgICAgICAgICAgIHN1Y2Nlc3M9VHJ1ZSwKICAgICAgICAgICAgYWNjZXNzX3Rva2VuPWFjY2Vzc190b2tlbiwKICAgICAgICAgICAgcmVmcmVzaF90b2tlbj1yZWZyZXNoX3Rva2VuLAogICAgICAgICAgICBleHBpcmVzX2F0PWV4cGlyZXNfYXQsCiAgICAgICAgKQogICAgKQoKCkByb3V0ZXIucG9zdCgiL2lkZW50aXR5Iiwgc3RhdHVzX2NvZGU9MjAwKQphc3luYyBkZWYgdHJha3RfaWRlbnRpdHkocGF5bG9hZDogVHJha3RUb2tlblJlcXVlc3QpOgogICAgIiIiCiAgICBWYWxpZGF0ZSB0aGUgVHJha3QgYWNjZXNzIHRva2VuLCByZXR1cm4gdXNlciBpbmZvIGFuZCBleGlzdGluZyBzZXR0aW5ncy4KICAgICIiIgogICAgYnVuZGxlID0gX2dldF9idW5kbGUoYWNjZXNzX3Rva2VuPXBheWxvYWQudHJha3RfYWNjZXNzX3Rva2VuKQogICAgdHJ5OgogICAgICAgIHVzZXJfaW5mbyA9IGF3YWl0IGJ1bmRsZS51c2VyLmdldF91c2VyX2luZm8oKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDAsIGRldGFpbD1mIkludmFsaWQgVHJha3QgYWNjZXNzIHRva2VuOiB7ZXhjfSIpIGZyb20gZXhjCiAgICBmaW5hbGx5OgogICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCgogICAgdXNlcm5hbWUgPSB1c2VyX2luZm8uZ2V0KCJ1c2VybmFtZSIpIG9yIHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciAidHJha3RfdXNlciIKICAgICMgVXNlIGEgVHJha3QtbmFtZXNwYWNlZCB1c2VyX2lkIHNvIGl0IGNhbid0IGNsYXNoIHdpdGggU3RyZW1pbyBJRHMKICAgIHVzZXJfaWQgPSBmInRyYWt0Ont1c2VybmFtZX0iCgogICAgdG9rZW4gPSB0b2tlbl9zdG9yZS5nZXRfdG9rZW5fZnJvbV91c2VyX2lkKHVzZXJfaWQpCiAgICB1c2VyX2RhdGEgPSBhd2FpdCB0b2tlbl9zdG9yZS5nZXRfdXNlcl9kYXRhKHRva2VuKQogICAgZXhpc3RzID0gYm9vbCh1c2VyX2RhdGEpCgogICAgcmVzcG9uc2U6IGRpY3Rbc3RyLCBBbnldID0gewogICAgICAgICJ1c2VyX2lkIjogdXNlcl9pZCwKICAgICAgICAidXNlcm5hbWUiOiB1c2VybmFtZSwKICAgICAgICAiZGlzcGxheSI6IHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciB1c2VybmFtZSwKICAgICAgICAiZXhpc3RzIjogZXhpc3RzLAogICAgfQogICAgaWYgZXhpc3RzIGFuZCB1c2VyX2RhdGE6CiAgICAgICAgcmF3X3NldHRpbmdzID0gdXNlcl9kYXRhLmdldCgic2V0dGluZ3MiLCB7fSkKICAgICAgICB0cnk6CiAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBVc2VyU2V0dGluZ3MoKipyYXdfc2V0dGluZ3MpCiAgICAgICAgICAgIHJlc3BvbnNlWyJzZXR0aW5ncyJdID0gdXNlcl9zZXR0aW5ncy5tb2RlbF9kdW1wKCkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIG5vcm1hbGlzZSBzZXR0aW5ncyBmb3Ige3VzZXJfaWR9OiB7ZX0iKQogICAgICAgICAgICByZXNwb25zZVsic2V0dGluZ3MiXSA9IHJhd19zZXR0aW5ncwoKICAgIHJldHVybiByZXNwb25zZQoKCkByb3V0ZXIucG9zdCgiLyIsIHJlc3BvbnNlX21vZGVsPVRyYWt0VG9rZW5SZXNwb25zZSkKYXN5bmMgZGVmIGNyZWF0ZV90cmFrdF90b2tlbihwYXlsb2FkOiBUcmFrdFRva2VuUmVxdWVzdCwgcmVxdWVzdDogUmVxdWVzdCk6CiAgICAiIiIKICAgIENyZWF0ZSBvciB1cGRhdGUgYSBXYXRjaGx5IGFjY291bnQgYmFja2VkIGJ5IGEgVHJha3QgYWNjZXNzIHRva2VuLgogICAgVGhlIGxpYnJhcnkgaXMgZmV0Y2hlZCBmcm9tIFRyYWt0OyBldmVyeXRoaW5nIGVsc2UgYmVoYXZlcyBsaWtlIHRoZQogICAgU3RyZW1pby1iYXNlZCB0b2tlbiBlbmRwb2ludC4KICAgICIiIgogICAgYnVuZGxlID0gX2dldF9idW5kbGUoYWNjZXNzX3Rva2VuPXBheWxvYWQudHJha3RfYWNjZXNzX3Rva2VuKQoKICAgIHRyeToKICAgICAgICB1c2VyX2luZm8gPSBhd2FpdCBidW5kbGUudXNlci5nZXRfdXNlcl9pbmZvKCkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDAsIGRldGFpbD1mIkludmFsaWQgVHJha3QgYWNjZXNzIHRva2VuOiB7ZXhjfSIpIGZyb20gZXhjCgogICAgdXNlcm5hbWUgPSB1c2VyX2luZm8uZ2V0KCJ1c2VybmFtZSIpIG9yIHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciAidHJha3RfdXNlciIKICAgIHVzZXJfaWQgPSBmInRyYWt0Ont1c2VybmFtZX0iCgogICAgdG9rZW4gPSB0b2tlbl9zdG9yZS5nZXRfdG9rZW5fZnJvbV91c2VyX2lkKHVzZXJfaWQpCiAgICBleGlzdGluZ19kYXRhID0gYXdhaXQgdG9rZW5fc3RvcmUuZ2V0X3VzZXJfZGF0YSh0b2tlbikKCiAgICBkZWZhdWx0X3NldHRpbmdzID0gZ2V0X2RlZmF1bHRfc2V0dGluZ3MoKQogICAgdXNlcl9zZXR0aW5ncyA9IFVzZXJTZXR0aW5ncygKICAgICAgICBsYW5ndWFnZT1wYXlsb2FkLmxhbmd1YWdlIG9yIGRlZmF1bHRfc2V0dGluZ3MubGFuZ3VhZ2UsCiAgICAgICAgY2F0YWxvZ3M9cGF5bG9hZC5jYXRhbG9ncyBpZiBwYXlsb2FkLmNhdGFsb2dzIGVsc2UgZGVmYXVsdF9zZXR0aW5ncy5jYXRhbG9ncywKICAgICAgICBwb3N0ZXJfcmF0aW5nPXBheWxvYWQucG9zdGVyX3JhdGluZywKICAgICAgICBleGNsdWRlZF9tb3ZpZV9nZW5yZXM9cGF5bG9hZC5leGNsdWRlZF9tb3ZpZV9nZW5yZXMsCiAgICAgICAgZXhjbHVkZWRfc2VyaWVzX2dlbnJlcz1wYXlsb2FkLmV4Y2x1ZGVkX3Nlcmllc19nZW5yZXMsCiAgICAgICAgeWVhcl9taW49cGF5bG9hZC55ZWFyX21pbiwKICAgICAgICB5ZWFyX21heD1wYXlsb2FkLnllYXJfbWF4LAogICAgICAgIHBvcHVsYXJpdHk9cGF5bG9hZC5wb3B1bGFyaXR5LAogICAgICAgIHNvcnRpbmdfb3JkZXI9cGF5bG9hZC5zb3J0aW5nX29yZGVyLAogICAgICAgIHNpbWtsX2FwaV9rZXk9cGF5bG9hZC5zaW1rbF9hcGlfa2V5LAogICAgICAgIGdlbWluaV9hcGlfa2V5PXBheWxvYWQuZ2VtaW5pX2FwaV9rZXksCiAgICAgICAgdG1kYl9hcGlfa2V5PXBheWxvYWQudG1kYl9hcGlfa2V5LAogICAgKQoKICAgIHBheWxvYWRfdG9fc3RvcmU6IGRpY3Rbc3RyLCBBbnldID0gewogICAgICAgICJhdXRoX3Byb3ZpZGVyIjogInRyYWt0IiwKICAgICAgICAidHJha3RfYWNjZXNzX3Rva2VuIjogcGF5bG9hZC50cmFrdF9hY2Nlc3NfdG9rZW4sCiAgICAgICAgInRyYWt0X3JlZnJlc2hfdG9rZW4iOiBwYXlsb2FkLnRyYWt0X3JlZnJlc2hfdG9rZW4sCiAgICAgICAgInRyYWt0X2V4cGlyZXNfYXQiOiBwYXlsb2FkLnRyYWt0X2V4cGlyZXNfYXQsCiAgICAgICAgInRyYWt0X3VzZXJuYW1lIjogdXNlcm5hbWUsCiAgICAgICAgImVtYWlsIjogdXNlcl9pbmZvLmdldCgibmFtZSIpIG9yIHVzZXJuYW1lLAogICAgICAgICJzZXR0aW5ncyI6IHVzZXJfc2V0dGluZ3MubW9kZWxfZHVtcCgpLAogICAgfQogICAgaWYgZXhpc3RpbmdfZGF0YToKICAgICAgICBwYXlsb2FkX3RvX3N0b3JlWyJsYXN0X3VwZGF0ZWQiXSA9IGV4aXN0aW5nX2RhdGEuZ2V0KCJsYXN0X3VwZGF0ZWQiKQogICAgZWxzZToKICAgICAgICBwYXlsb2FkX3RvX3N0b3JlWyJsYXN0X3VwZGF0ZWQiXSA9IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpLmlzb2Zvcm1hdCgpCgogICAgIyBFbmNyeXB0IFRyYWt0IHRva2VucyB2aWEgdG9rZW5fc3RvcmUncyBlbmNyeXB0aW9uCiAgICAjIFdlIHN0b3JlIHRoZW0gaW4gYSBmaWVsZCB0b2tlbl9zdG9yZSB1bmRlcnN0YW5kczsgdXNlIGEgc21hbGwgd29ya2Fyb3VuZDoKICAgICMgc3RvcmUgYXMgYXV0aEtleSB0ZW1wb3JhcmlseSBzbyB0b2tlbl9zdG9yZSBlbmNyeXB0cyBpdCwgdGhlbiByZW5hbWUuCiAgICAjIEFjdHVhbGx5LCB0b2tlbl9zdG9yZSBlbmNyeXB0cyBgYXV0aEtleWAgbmF0aXZlbHkg4oCUIHN0b3JlIGFjY2VzcyB0b2tlbiB0aGVyZS4KICAgIHBheWxvYWRfdG9fc3RvcmVbImF1dGhLZXkiXSA9IHBheWxvYWQudHJha3RfYWNjZXNzX3Rva2VuCgogICAgdG9rZW4gPSBhd2FpdCB0b2tlbl9zdG9yZS5zdG9yZV91c2VyX2RhdGEodXNlcl9pZCwgcGF5bG9hZF90b19zdG9yZSkKICAgIGFjY291bnRfc3RhdHVzID0gInVwZGF0ZWQiIGlmIGV4aXN0aW5nX2RhdGEgZWxzZSAiY3JlYXRlZCIKICAgIGxvZ2dlci5pbmZvKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVHJha3QgYWNjb3VudCB7YWNjb3VudF9zdGF0dXN9IGZvciB1c2VyIHt1c2VyX2lkfSIpCgogICAgIyBQcmUtY2FjaGUgbGlicmFyeSB1c2luZyBUcmFrdCBkYXRhCiAgICB0cnk6CiAgICAgICAgbGlicmFyeV9pdGVtcyA9IGF3YWl0IGJ1bmRsZS5saWJyYXJ5LmdldF9saWJyYXJ5X2l0ZW1zKCkKICAgICAgICAjIFVzZSB0aGUgc2hhcmVkIG1hbmlmZXN0IGNhY2hpbmcgcGF0aHdheSwgcGFzc2luZyBsaWJyYXJ5X2l0ZW1zIGRpcmVjdGx5CiAgICAgICAgYXdhaXQgbWFuaWZlc3Rfc2VydmljZS5jYWNoZV9saWJyYXJ5X2FuZF9wcm9maWxlc19mcm9tX2l0ZW1zKGxpYnJhcnlfaXRlbXMsIHVzZXJfc2V0dGluZ3MsIHRva2VuKQogICAgICAgIGxvZ2dlci5pbmZvKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVHJha3QgbGlicmFyeSBjYWNoZWQgKHtsZW4obGlicmFyeV9pdGVtcy5nZXQoJ3dhdGNoZWQnLCBbXSkpfSBpdGVtcykiKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gcHJlLWNhY2hlIFRyYWt0IGxpYnJhcnk6IHtleGN9LiBXaWxsIGNhY2hlIG9uIGRlbWFuZC4iKQogICAgZmluYWxseToKICAgICAgICBhd2FpdCBidW5kbGUuY2xvc2UoKQoKICAgIGJhc2VfdXJsID0gc2V0dGluZ3MuSE9TVF9OQU1FCiAgICBtYW5pZmVzdF91cmwgPSBmIntiYXNlX3VybH0ve3Rva2VufS9tYW5pZmVzdC5qc29uIgogICAgZXhwaXJlc19pbiA9IHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTIGlmIHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTID4gMCBlbHNlIE5vbmUKCiAgICByZXR1cm4gVHJha3RUb2tlblJlc3BvbnNlKHRva2VuPXRva2VuLCBtYW5pZmVzdFVybD1tYW5pZmVzdF91cmwsIGV4cGlyZXNJblNlY29uZHM9ZXhwaXJlc19pbikKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEhlbHBlcnMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpkZWYgX3BvcHVwX2Nsb3NlX2h0bWwoCiAgICBzdWNjZXNzOiBib29sLAogICAgZXJyb3I6IHN0ciB8IE5vbmUgPSBOb25lLAogICAgYWNjZXNzX3Rva2VuOiBzdHIgfCBOb25lID0gTm9uZSwKICAgIHJlZnJlc2hfdG9rZW46IHN0ciB8IE5vbmUgPSBOb25lLAogICAgZXhwaXJlc19hdDogaW50IHwgTm9uZSA9IE5vbmUsCikgLT4gc3RyOgogICAgIiIiCiAgICBNaW5pbWFsIEhUTUwgcGFnZSB0aGF0IHBvc3RzIGEgbWVzc2FnZSB0byB0aGUgb3BlbmVyIGFuZCBjbG9zZXMgaXRzZWxmLgogICAgVGhlIGZyb250ZW5kIGxpc3RlbnMgZm9yIHRoaXMgbWVzc2FnZSB2aWEgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCAuLi4pLgogICAgIiIiCiAgICBpZiBzdWNjZXNzOgogICAgICAgIHBheWxvYWRfanMgPSAoCiAgICAgICAgICAgIGYie3sgdHlwZTogJ3RyYWt0X2F1dGhfc3VjY2VzcycsICIKICAgICAgICAgICAgZiJhY2Nlc3NfdG9rZW46IHtfanNfc3RyKGFjY2Vzc190b2tlbil9LCAiCiAgICAgICAgICAgIGYicmVmcmVzaF90b2tlbjoge19qc19zdHIocmVmcmVzaF90b2tlbil9LCAiCiAgICAgICAgICAgIGYiZXhwaXJlc19hdDoge2V4cGlyZXNfYXQgb3IgJ251bGwnfSB9fSIKICAgICAgICApCiAgICBlbHNlOgogICAgICAgIHBheWxvYWRfanMgPSBmInt7IHR5cGU6ICd0cmFrdF9hdXRoX2Vycm9yJywgZXJyb3I6IHtfanNfc3RyKGVycm9yIG9yICdVbmtub3duIGVycm9yJyl9IH19IgoKICAgIHJldHVybiBmIiIiPCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD48dGl0bGU+VHJha3QgQXV0aG9yaXphdGlvbjwvdGl0bGU+PC9oZWFkPgo8Ym9keT4KPHAgc3R5bGU9ImZvbnQtZmFtaWx5OnNhbnMtc2VyaWY7dGV4dC1hbGlnbjpjZW50ZXI7bWFyZ2luLXRvcDozcmVtIj4KICB7J0F1dGhvcml6YXRpb24gc3VjY2Vzc2Z1bCEgWW91IGNhbiBjbG9zZSB0aGlzIHdpbmRvdy4nIGlmIHN1Y2Nlc3MgZWxzZSBmJ0F1dGhvcml6YXRpb24gZmFpbGVkOiB7ZXJyb3J9J30KPC9wPgo8c2NyaXB0PgogIHRyeSB7ewogICAgaWYgKHdpbmRvdy5vcGVuZXIpIHt7CiAgICAgIHdpbmRvdy5vcGVuZXIucG9zdE1lc3NhZ2Uoe3BheWxvYWRfanN9LCB3aW5kb3cubG9jYXRpb24ub3JpZ2luKTsKICAgIH19CiAgfX0gY2F0Y2goZSkge3t9fQogIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7eyB3aW5kb3cuY2xvc2UoKTsgfX0sIDEwMDApOwo8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+IiIiCgoKZGVmIF9qc19zdHIodmFsdWU6IHN0ciB8IE5vbmUpIC0+IHN0cjoKICAgIGlmIHZhbHVlIGlzIE5vbmU6CiAgICAgICAgcmV0dXJuICJudWxsIgogICAgZXNjYXBlZCA9IHZhbHVlLnJlcGxhY2UoIlxcIiwgIlxcXFwiKS5yZXBsYWNlKCciJywgJ1xcIicpLnJlcGxhY2UoIlxuIiwgIlxcbiIpCiAgICByZXR1cm4gZicie2VzY2FwZWR9IicK" | base64 --decode > "$REPO/app/api/endpoints/trakt.py" +echo " wrote app/api/endpoints/trakt.py" + +mkdir -p "$REPO/app/static/js/modules" +echo "Ly8gVHJha3QgQXV0aGVudGljYXRpb24gTW9kdWxlCgppbXBvcnQgeyBzaG93VG9hc3QgfSBmcm9tICcuL3VpLmpzJzsKaW1wb3J0IHsgc3dpdGNoU2VjdGlvbiwgdW5sb2NrTmF2aWdhdGlvbiB9IGZyb20gJy4vbmF2aWdhdGlvbi5qcyc7CgovLyBMb2NhbFN0b3JhZ2Uga2V5cyBmb3IgVHJha3QKY29uc3QgVFJBS1RfU1RPUkFHRV9LRVkgPSAnd2F0Y2hseV90cmFrdF9hdXRoJzsKY29uc3QgRVhQSVJZX0RBWVMgPSA4NTsgLy8gVHJha3QgdG9rZW5zIGxhc3QgOTAgZGF5czsgcmVmcmVzaCBhIGJpdCBlYXJseQoKbGV0IGxhbmd1YWdlU2VsZWN0ID0gbnVsbDsKbGV0IGdldENhdGFsb2dzID0gbnVsbDsKbGV0IHJlbmRlckNhdGFsb2dMaXN0ID0gbnVsbDsKbGV0IHJlc2V0QXBwID0gbnVsbDsKCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIFB1YmxpYyBBUEkKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplVHJha3QoZG9tRWxlbWVudHMsIGNhdGFsb2dTdGF0ZSkgewogICAgbGFuZ3VhZ2VTZWxlY3QgPSBkb21FbGVtZW50cy5sYW5ndWFnZVNlbGVjdDsKICAgIGdldENhdGFsb2dzID0gY2F0YWxvZ1N0YXRlLmdldENhdGFsb2dzOwogICAgcmVuZGVyQ2F0YWxvZ0xpc3QgPSBjYXRhbG9nU3RhdGUucmVuZGVyQ2F0YWxvZ0xpc3Q7CiAgICByZXNldEFwcCA9IGNhdGFsb2dTdGF0ZS5yZXNldEFwcDsKCiAgICBpbml0aWFsaXplVHJha3RDb25uZWN0QnV0dG9uKCk7CiAgICBpbml0aWFsaXplVHJha3RMb2dvdXRCdXR0b24oKTsKICAgIGF0dGVtcHRUcmFrdEF1dG9Mb2dpbigpOwp9CgpleHBvcnQgZnVuY3Rpb24gc2V0VHJha3RMb2dnZWRPdXRTdGF0ZSgpIHsKICAgIGNsZWFyVHJha3RGcm9tU3RvcmFnZSgpOwogICAgaGlkZVRyYWt0U3RhdHVzKCk7CgogICAgY29uc3QgdHJha3RDb25uZWN0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0Q29ubmVjdEJ0bicpOwogICAgaWYgKHRyYWt0Q29ubmVjdEJ0bikgewogICAgICAgIHRyYWt0Q29ubmVjdEJ0bi5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgIH0KfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gU3RvcmFnZSBoZWxwZXJzCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpmdW5jdGlvbiBzYXZlVHJha3RUb1N0b3JhZ2UoYXV0aERhdGEpIHsKICAgIHRyeSB7CiAgICAgICAgY29uc3QgZXhwaXJ5RGF0ZSA9IG5ldyBEYXRlKCk7CiAgICAgICAgZXhwaXJ5RGF0ZS5zZXREYXRlKGV4cGlyeURhdGUuZ2V0RGF0ZSgpICsgRVhQSVJZX0RBWVMpOwogICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFRSQUtUX1NUT1JBR0VfS0VZLCBKU09OLnN0cmluZ2lmeSh7IC4uLmF1dGhEYXRhLCBleHBpcmVzQXQ6IGV4cGlyeURhdGUuZ2V0VGltZSgpIH0pKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICBjb25zb2xlLndhcm4oJ0ZhaWxlZCB0byBzYXZlIFRyYWt0IGF1dGg6JywgZSk7CiAgICB9Cn0KCmZ1bmN0aW9uIGdldFRyYWt0RnJvbVN0b3JhZ2UoKSB7CiAgICB0cnkgewogICAgICAgIGNvbnN0IHN0b3JlZCA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKFRSQUtUX1NUT1JBR0VfS0VZKTsKICAgICAgICBpZiAoIXN0b3JlZCkgcmV0dXJuIG51bGw7CiAgICAgICAgY29uc3QgZGF0YSA9IEpTT04ucGFyc2Uoc3RvcmVkKTsKICAgICAgICBpZiAoZGF0YS5leHBpcmVzQXQgJiYgZGF0YS5leHBpcmVzQXQgPCBEYXRlLm5vdygpKSB7CiAgICAgICAgICAgIGNsZWFyVHJha3RGcm9tU3RvcmFnZSgpOwogICAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIGRhdGE7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgY2xlYXJUcmFrdEZyb21TdG9yYWdlKCk7CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICB9Cn0KCmZ1bmN0aW9uIGNsZWFyVHJha3RGcm9tU3RvcmFnZSgpIHsKICAgIHRyeSB7IGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKFRSQUtUX1NUT1JBR0VfS0VZKTsgfSBjYXRjaCAoZSkgeyAvKiBub29wICovIH0KfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gT0F1dGggcG9wdXAgZmxvdwovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZnVuY3Rpb24gaW5pdGlhbGl6ZVRyYWt0Q29ubmVjdEJ1dHRvbigpIHsKICAgIGNvbnN0IGJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0cmFrdENvbm5lY3RCdG4nKTsKICAgIGlmICghYnRuKSByZXR1cm47CgogICAgYnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKCkgPT4gewogICAgICAgIHNldFRyYWt0Q29ubmVjdGluZyh0cnVlKTsKICAgICAgICB0cnkgewogICAgICAgICAgICAvLyAxLiBGZXRjaCB0aGUgYXV0aG9yaXphdGlvbiBVUkwgZnJvbSBiYWNrZW5kCiAgICAgICAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKCcvdG9rZW5zL3RyYWt0L2F1dGhvcml6ZScpOwogICAgICAgICAgICBpZiAoIXJlcy5vaykgewogICAgICAgICAgICAgICAgY29uc3QgZXJyID0gYXdhaXQgcmVzLmpzb24oKS5jYXRjaCgoKSA9PiAoe30pKTsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihlcnIuZGV0YWlsIHx8ICdGYWlsZWQgdG8gc3RhcnQgVHJha3QgYXV0aG9yaXphdGlvbicpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnN0IHsgdXJsIH0gPSBhd2FpdCByZXMuanNvbigpOwoKICAgICAgICAgICAgLy8gMi4gT3BlbiBPQXV0aCBwb3B1cAogICAgICAgICAgICBjb25zdCB0b2tlbnMgPSBhd2FpdCBvcGVuVHJha3RQb3B1cCh1cmwpOwoKICAgICAgICAgICAgLy8gMy4gQ2FsbCBpZGVudGl0eSBjaGVjayB0byBnZXQgdXNlciBpbmZvICsgZXhpc3Rpbmcgc2V0dGluZ3MKICAgICAgICAgICAgYXdhaXQgZmV0Y2hUcmFrdElkZW50aXR5KHRva2Vucyk7CgogICAgICAgICAgICAvLyA0LiBTYXZlIHRvIHN0b3JhZ2UKICAgICAgICAgICAgc2F2ZVRyYWt0VG9TdG9yYWdlKHRva2Vucyk7CgogICAgICAgICAgICB1bmxvY2tOYXZpZ2F0aW9uKCk7CiAgICAgICAgICAgIHN3aXRjaFNlY3Rpb24oJ2NvbmZpZycpOwogICAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgICAgICBzaG93VG9hc3QoZXJyLm1lc3NhZ2UgfHwgJ1RyYWt0IGxvZ2luIGZhaWxlZCcsICdlcnJvcicpOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHNldFRyYWt0Q29ubmVjdGluZyhmYWxzZSk7CiAgICAgICAgfQogICAgfSk7Cn0KCmZ1bmN0aW9uIGluaXRpYWxpemVUcmFrdExvZ291dEJ1dHRvbigpIHsKICAgIGNvbnN0IGJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0cmFrdExvZ291dEJ0bicpOwogICAgaWYgKCFidG4pIHJldHVybjsKICAgIGJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsKICAgICAgICBpZiAocmVzZXRBcHApIHJlc2V0QXBwKCk7CiAgICB9KTsKfQoKLyoqCiAqIE9wZW4gYSBwb3B1cCB3aW5kb3cgZm9yIFRyYWt0IE9BdXRoIGFuZCByZXNvbHZlIHdoZW4gdGhlIGNhbGxiYWNrIHBhZ2UKICogcG9zdHMgYSBtZXNzYWdlIGJhY2sgdG8gdXMuCiAqLwpmdW5jdGlvbiBvcGVuVHJha3RQb3B1cCh1cmwpIHsKICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgY29uc3Qgd2lkdGggPSA2MDA7CiAgICAgICAgY29uc3QgaGVpZ2h0ID0gNzAwOwogICAgICAgIGNvbnN0IGxlZnQgPSBNYXRoLnJvdW5kKHdpbmRvdy5zY3JlZW5YICsgKHdpbmRvdy5vdXRlcldpZHRoIC0gd2lkdGgpIC8gMik7CiAgICAgICAgY29uc3QgdG9wID0gTWF0aC5yb3VuZCh3aW5kb3cuc2NyZWVuWSArICh3aW5kb3cub3V0ZXJIZWlnaHQgLSBoZWlnaHQpIC8gMik7CgogICAgICAgIGNvbnN0IHBvcHVwID0gd2luZG93Lm9wZW4oCiAgICAgICAgICAgIHVybCwKICAgICAgICAgICAgJ3RyYWt0X29hdXRoJywKICAgICAgICAgICAgYHdpZHRoPSR7d2lkdGh9LGhlaWdodD0ke2hlaWdodH0sbGVmdD0ke2xlZnR9LHRvcD0ke3RvcH0scmVzaXphYmxlPXllcyxzY3JvbGxiYXJzPXllc2AKICAgICAgICApOwoKICAgICAgICBpZiAoIXBvcHVwKSB7CiAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ0NvdWxkIG5vdCBvcGVuIHRoZSBhdXRob3JpemF0aW9uIHBvcHVwLiBQbGVhc2UgYWxsb3cgcG9wdXBzIGZvciB0aGlzIHNpdGUuJykpOwogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQoKICAgICAgICBsZXQgc2V0dGxlZCA9IGZhbHNlOwoKICAgICAgICBmdW5jdGlvbiBvbk1lc3NhZ2UoZXZlbnQpIHsKICAgICAgICAgICAgLy8gT25seSBhY2NlcHQgbWVzc2FnZXMgZnJvbSBvdXIgb3duIG9yaWdpbgogICAgICAgICAgICBpZiAoZXZlbnQub3JpZ2luICE9PSB3aW5kb3cubG9jYXRpb24ub3JpZ2luKSByZXR1cm47CiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBldmVudC5kYXRhOwogICAgICAgICAgICBpZiAoIWRhdGEgfHwgdHlwZW9mIGRhdGEgIT09ICdvYmplY3QnKSByZXR1cm47CgogICAgICAgICAgICBpZiAoZGF0YS50eXBlID09PSAndHJha3RfYXV0aF9zdWNjZXNzJykgewogICAgICAgICAgICAgICAgaWYgKCFzZXR0bGVkKSB7CiAgICAgICAgICAgICAgICAgICAgc2V0dGxlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgY2xlYW51cCgpOwogICAgICAgICAgICAgICAgICAgIHJlc29sdmUoewogICAgICAgICAgICAgICAgICAgICAgICBhY2Nlc3NfdG9rZW46IGRhdGEuYWNjZXNzX3Rva2VuLAogICAgICAgICAgICAgICAgICAgICAgICByZWZyZXNoX3Rva2VuOiBkYXRhLnJlZnJlc2hfdG9rZW4sCiAgICAgICAgICAgICAgICAgICAgICAgIGV4cGlyZXNfYXQ6IGRhdGEuZXhwaXJlc19hdCwKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT09ICd0cmFrdF9hdXRoX2Vycm9yJykgewogICAgICAgICAgICAgICAgaWYgKCFzZXR0bGVkKSB7CiAgICAgICAgICAgICAgICAgICAgc2V0dGxlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgY2xlYW51cCgpOwogICAgICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoZGF0YS5lcnJvciB8fCAnVHJha3QgYXV0aG9yaXphdGlvbiBmYWlsZWQnKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vIEFsc28gZGV0ZWN0IGlmIHRoZSB1c2VyIGNsb3NlcyB0aGUgcG9wdXAgbWFudWFsbHkKICAgICAgICBjb25zdCBwb2xsVGltZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7CiAgICAgICAgICAgIGlmIChwb3B1cC5jbG9zZWQgJiYgIXNldHRsZWQpIHsKICAgICAgICAgICAgICAgIHNldHRsZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgY2xlYW51cCgpOwogICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignQXV0aG9yaXphdGlvbiB3aW5kb3cgd2FzIGNsb3NlZCcpKTsKICAgICAgICAgICAgfQogICAgICAgIH0sIDUwMCk7CgogICAgICAgIGZ1bmN0aW9uIGNsZWFudXAoKSB7CiAgICAgICAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgb25NZXNzYWdlKTsKICAgICAgICAgICAgY2xlYXJJbnRlcnZhbChwb2xsVGltZXIpOwogICAgICAgIH0KCiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBvbk1lc3NhZ2UpOwogICAgfSk7Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIElkZW50aXR5IGZldGNoICsgc2V0dGluZ3MgcG9wdWxhdGlvbgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYXN5bmMgZnVuY3Rpb24gZmV0Y2hUcmFrdElkZW50aXR5KHRva2VucykgewogICAgY29uc3QgcGF5bG9hZCA9IHsKICAgICAgICB0cmFrdF9hY2Nlc3NfdG9rZW46IHRva2Vucy5hY2Nlc3NfdG9rZW4sCiAgICAgICAgdHJha3RfcmVmcmVzaF90b2tlbjogdG9rZW5zLnJlZnJlc2hfdG9rZW4gfHwgbnVsbCwKICAgICAgICB0cmFrdF9leHBpcmVzX2F0OiB0b2tlbnMuZXhwaXJlc19hdCB8fCBudWxsLAogICAgfTsKCiAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCgnL3Rva2Vucy90cmFrdC9pZGVudGl0eScsIHsKICAgICAgICBtZXRob2Q6ICdQT1NUJywKICAgICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSwKICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSwKICAgIH0pOwoKICAgIGlmICghcmVzLm9rKSB7CiAgICAgICAgY29uc3QgZXJyID0gYXdhaXQgcmVzLmpzb24oKS5jYXRjaCgoKSA9PiAoe30pKTsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoZXJyLmRldGFpbCB8fCAnRmFpbGVkIHRvIHZlcmlmeSBUcmFrdCBpZGVudGl0eScpOwogICAgfQoKICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXMuanNvbigpOwogICAgY29uc3QgZGlzcGxheSA9IGRhdGEuZGlzcGxheSB8fCBkYXRhLnVzZXJuYW1lIHx8ICdUcmFrdCBVc2VyJzsKCiAgICBzaG93VHJha3RTdGF0dXMoZGlzcGxheSk7CgogICAgaWYgKGRhdGEuZXhpc3RzICYmIGRhdGEuc2V0dGluZ3MpIHsKICAgICAgICBzaG93VG9hc3QoYFdlbGNvbWUgYmFjaywgJHtkaXNwbGF5fSEgTG9hZGluZyB5b3VyIHNldHRpbmdz4oCmYCwgJ2luZm8nLCA1MDAwKTsKICAgICAgICBwb3B1bGF0ZVNldHRpbmdzKGRhdGEuc2V0dGluZ3MpOwoKICAgICAgICBjb25zdCBpbnN0YWxsSGVhZGVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3NlY3QtaW5zdGFsbCBoMicpOwogICAgICAgIGNvbnN0IGluc3RhbGxEZXNjID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3NlY3QtaW5zdGFsbCBwJyk7CiAgICAgICAgaWYgKGluc3RhbGxIZWFkZXIpIGluc3RhbGxIZWFkZXIudGV4dENvbnRlbnQgPSAnVXBkYXRlIFNldHRpbmdzJzsKICAgICAgICBpZiAoaW5zdGFsbERlc2MpIGluc3RhbGxEZXNjLnRleHRDb250ZW50ID0gJ1VwZGF0ZSB5b3VyIHByZWZlcmVuY2VzIGFuZCByZS1pbnN0YWxsLic7CgogICAgICAgIGNvbnN0IGJ0blRleHQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjc3VibWl0QnRuIC5idG4tdGV4dCcpOwogICAgICAgIGlmIChidG5UZXh0KSBidG5UZXh0LnRleHRDb250ZW50ID0gJ1VwZGF0ZSAmIFJlLUluc3RhbGwnOwogICAgfSBlbHNlIHsKICAgICAgICBzaG93VG9hc3QoYFdlbGNvbWUsICR7ZGlzcGxheX0hIFNldHRpbmcgdXAgeW91ciBhY2NvdW504oCmYCwgJ3N1Y2Nlc3MnLCA1MDAwKTsKICAgIH0KCiAgICAvLyBTdG9yZSBkaXNwbGF5IG5hbWUgc28gdGhlIGZvcm0gc3VibWl0IGNhbiB1c2UgaXQKICAgIGNvbnN0IHRyYWt0RGlzcGxheUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0RGlzcGxheU5hbWUnKTsKICAgIGlmICh0cmFrdERpc3BsYXlJbnB1dCkgdHJha3REaXNwbGF5SW5wdXQudmFsdWUgPSBkaXNwbGF5Owp9CgpmdW5jdGlvbiBwb3B1bGF0ZVNldHRpbmdzKHMpIHsKICAgIGlmIChzLmxhbmd1YWdlICYmIGxhbmd1YWdlU2VsZWN0KSBsYW5ndWFnZVNlbGVjdC52YWx1ZSA9IHMubGFuZ3VhZ2U7CgogICAgY29uc3QgcG9wdWxhcml0eVNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwb3B1bGFyaXR5U2VsZWN0Jyk7CiAgICBjb25zdCB5ZWFyTWluSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgneWVhck1pbicpOwogICAgY29uc3QgeWVhck1heElucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3llYXJNYXgnKTsKICAgIGNvbnN0IHNvcnRpbmdPcmRlclNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzb3J0aW5nT3JkZXJTZWxlY3QnKTsKCiAgICBpZiAocy5wb3B1bGFyaXR5ICYmIHBvcHVsYXJpdHlTZWxlY3QpIHBvcHVsYXJpdHlTZWxlY3QudmFsdWUgPSBzLnBvcHVsYXJpdHk7CiAgICBpZiAocy55ZWFyX21pbiAmJiB5ZWFyTWluSW5wdXQpIHllYXJNaW5JbnB1dC52YWx1ZSA9IHMueWVhcl9taW47CiAgICBpZiAocy55ZWFyX21heCAmJiB5ZWFyTWF4SW5wdXQpIHllYXJNYXhJbnB1dC52YWx1ZSA9IHMueWVhcl9tYXg7CiAgICBpZiAod2luZG93LnVwZGF0ZVllYXJTbGlkZXIpIHdpbmRvdy51cGRhdGVZZWFyU2xpZGVyKCk7CiAgICBpZiAocy5zb3J0aW5nX29yZGVyICYmIHNvcnRpbmdPcmRlclNlbGVjdCkgc29ydGluZ09yZGVyU2VsZWN0LnZhbHVlID0gcy5zb3J0aW5nX29yZGVyOwoKICAgIGNvbnN0IHBvc3RlclJhdGluZ1Byb3ZpZGVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bvc3RlclJhdGluZ1Byb3ZpZGVyJyk7CiAgICBjb25zdCBwb3N0ZXJSYXRpbmdBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncG9zdGVyUmF0aW5nQXBpS2V5Jyk7CiAgICBpZiAocG9zdGVyUmF0aW5nUHJvdmlkZXIgJiYgcG9zdGVyUmF0aW5nQXBpS2V5ICYmIHMucG9zdGVyX3JhdGluZz8ucHJvdmlkZXIgJiYgcy5wb3N0ZXJfcmF0aW5nPy5hcGlfa2V5KSB7CiAgICAgICAgcG9zdGVyUmF0aW5nUHJvdmlkZXIudmFsdWUgPSBzLnBvc3Rlcl9yYXRpbmcucHJvdmlkZXI7CiAgICAgICAgcG9zdGVyUmF0aW5nQXBpS2V5LnZhbHVlID0gcy5wb3N0ZXJfcmF0aW5nLmFwaV9rZXk7CiAgICAgICAgcG9zdGVyUmF0aW5nUHJvdmlkZXIuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScpKTsKICAgIH0KCiAgICBjb25zdCB0bWRiQXBpS2V5SW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndG1kYkFwaUtleScpOwogICAgaWYgKHMudG1kYl9hcGlfa2V5ICYmIHRtZGJBcGlLZXlJbnB1dCkgdG1kYkFwaUtleUlucHV0LnZhbHVlID0gcy50bWRiX2FwaV9rZXk7CgogICAgY29uc3Qgc2lta2xBcGlLZXlJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzaW1rbEFwaUtleScpOwogICAgaWYgKHMuc2lta2xfYXBpX2tleSAmJiBzaW1rbEFwaUtleUlucHV0KSBzaW1rbEFwaUtleUlucHV0LnZhbHVlID0gcy5zaW1rbF9hcGlfa2V5OwoKICAgIGNvbnN0IGdlbWluaUFwaUtleUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2dlbWluaUFwaUtleScpOwogICAgaWYgKHMuZ2VtaW5pX2FwaV9rZXkgJiYgZ2VtaW5pQXBpS2V5SW5wdXQpIGdlbWluaUFwaUtleUlucHV0LnZhbHVlID0gcy5nZW1pbmlfYXBpX2tleTsKCiAgICAvLyBHZW5yZXMKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJ2lucHV0W25hbWU9Im1vdmllLWdlbnJlIl0nKS5mb3JFYWNoKGNiID0+IGNiLmNoZWNrZWQgPSBmYWxzZSk7CiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dFtuYW1lPSJzZXJpZXMtZ2VucmUiXScpLmZvckVhY2goY2IgPT4gY2IuY2hlY2tlZCA9IGZhbHNlKTsKICAgIGlmIChzLmV4Y2x1ZGVkX21vdmllX2dlbnJlcykgcy5leGNsdWRlZF9tb3ZpZV9nZW5yZXMuZm9yRWFjaChpZCA9PiB7CiAgICAgICAgY29uc3QgY2IgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBpbnB1dFtuYW1lPSJtb3ZpZS1nZW5yZSJdW3ZhbHVlPSIke2lkfSJdYCk7CiAgICAgICAgaWYgKGNiKSBjYi5jaGVja2VkID0gdHJ1ZTsKICAgIH0pOwogICAgaWYgKHMuZXhjbHVkZWRfc2VyaWVzX2dlbnJlcykgcy5leGNsdWRlZF9zZXJpZXNfZ2VucmVzLmZvckVhY2goaWQgPT4gewogICAgICAgIGNvbnN0IGNiID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihgaW5wdXRbbmFtZT0ic2VyaWVzLWdlbnJlIl1bdmFsdWU9IiR7aWR9Il1gKTsKICAgICAgICBpZiAoY2IpIGNiLmNoZWNrZWQgPSB0cnVlOwogICAgfSk7CgogICAgLy8gQ2F0YWxvZ3MKICAgIGlmIChzLmNhdGFsb2dzICYmIEFycmF5LmlzQXJyYXkocy5jYXRhbG9ncykpIHsKICAgICAgICBjb25zdCBjYXRhbG9ncyA9IGdldENhdGFsb2dzID8gZ2V0Q2F0YWxvZ3MoKSA6IFtdOwogICAgICAgIHMuY2F0YWxvZ3MuZm9yRWFjaChyZW1vdGUgPT4gewogICAgICAgICAgICBjb25zdCBsb2NhbCA9IGNhdGFsb2dzLmZpbmQoYyA9PiBjLmlkID09PSByZW1vdGUuaWQpOwogICAgICAgICAgICBpZiAobG9jYWwpIHsKICAgICAgICAgICAgICAgIGxvY2FsLmVuYWJsZWQgPSByZW1vdGUuZW5hYmxlZDsKICAgICAgICAgICAgICAgIGlmIChyZW1vdGUubmFtZSkgbG9jYWwubmFtZSA9IHJlbW90ZS5uYW1lOwogICAgICAgICAgICAgICAgaWYgKHR5cGVvZiByZW1vdGUuZW5hYmxlZF9tb3ZpZSA9PT0gJ2Jvb2xlYW4nKSBsb2NhbC5lbmFibGVkTW92aWUgPSByZW1vdGUuZW5hYmxlZF9tb3ZpZTsKICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgcmVtb3RlLmVuYWJsZWRfc2VyaWVzID09PSAnYm9vbGVhbicpIGxvY2FsLmVuYWJsZWRTZXJpZXMgPSByZW1vdGUuZW5hYmxlZF9zZXJpZXM7CiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHJlbW90ZS5kaXNwbGF5X2F0X2hvbWUgPT09ICdib29sZWFuJykgbG9jYWwuZGlzcGxheV9hdF9ob21lID0gcmVtb3RlLmRpc3BsYXlfYXRfaG9tZTsKICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgcmVtb3RlLnNodWZmbGUgPT09ICdib29sZWFuJykgbG9jYWwuc2h1ZmZsZSA9IHJlbW90ZS5zaHVmZmxlOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgaWYgKHJlbmRlckNhdGFsb2dMaXN0KSByZW5kZXJDYXRhbG9nTGlzdCgpOwogICAgfQp9CgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBBdXRvLWxvZ2luCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgphc3luYyBmdW5jdGlvbiBhdHRlbXB0VHJha3RBdXRvTG9naW4oKSB7CiAgICBjb25zdCBzdG9yZWQgPSBnZXRUcmFrdEZyb21TdG9yYWdlKCk7CiAgICBpZiAoIXN0b3JlZD8uYWNjZXNzX3Rva2VuKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgICBhd2FpdCBmZXRjaFRyYWt0SWRlbnRpdHkoc3RvcmVkKTsKICAgICAgICB1bmxvY2tOYXZpZ2F0aW9uKCk7CiAgICAgICAgc3dpdGNoU2VjdGlvbignY29uZmlnJyk7CiAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLndhcm4oJ1RyYWt0IGF1dG8tbG9naW4gZmFpbGVkOicsIGVycik7CiAgICAgICAgY2xlYXJUcmFrdEZyb21TdG9yYWdlKCk7CiAgICB9Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIFVJIGhlbHBlcnMKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmZ1bmN0aW9uIHNldFRyYWt0Q29ubmVjdGluZyhsb2FkaW5nKSB7CiAgICBjb25zdCBidG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RDb25uZWN0QnRuJyk7CiAgICBpZiAoIWJ0bikgcmV0dXJuOwogICAgY29uc3QgdGV4dCA9IGJ0bi5xdWVyeVNlbGVjdG9yKCcuYnRuLXRleHQnKTsKICAgIGNvbnN0IGxvYWRlciA9IGJ0bi5xdWVyeVNlbGVjdG9yKCcubG9hZGVyJyk7CiAgICBidG4uZGlzYWJsZWQgPSBsb2FkaW5nOwogICAgaWYgKHRleHQpIHRleHQuY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgbG9hZGluZyk7CiAgICBpZiAobG9hZGVyKSBsb2FkZXIuY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgIWxvYWRpbmcpOwp9CgpleHBvcnQgZnVuY3Rpb24gc2hvd1RyYWt0U3RhdHVzKGRpc3BsYXlOYW1lKSB7CiAgICBjb25zdCBzdGF0dXNTZWN0aW9uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0U3RhdHVzU2VjdGlvbicpOwogICAgY29uc3QgZGlzcGxheUVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0U3RhdHVzRGlzcGxheScpOwogICAgY29uc3QgYXZhdGFyRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RTdGF0dXNBdmF0YXInKTsKICAgIGNvbnN0IGNvbm5lY3RCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RDb25uZWN0QnRuJyk7CgogICAgaWYgKGRpc3BsYXlFbCkgZGlzcGxheUVsLnRleHRDb250ZW50ID0gZGlzcGxheU5hbWU7CiAgICBpZiAoYXZhdGFyRWwpIGF2YXRhckVsLnRleHRDb250ZW50ID0gZ2V0SW5pdGlhbHMoZGlzcGxheU5hbWUpOwogICAgaWYgKHN0YXR1c1NlY3Rpb24pIHN0YXR1c1NlY3Rpb24uY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICBpZiAoY29ubmVjdEJ0bikgY29ubmVjdEJ0bi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKCiAgICAvLyBTaWRlYmFyIHByb2ZpbGUKICAgIGNvbnN0IHVzZXJQcm9maWxlV3JhcHBlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1c2VyLXByb2ZpbGUtZHJvcGRvd24td3JhcHBlcicpOwogICAgY29uc3QgdXNlckVtYWlsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VzZXItZW1haWwnKTsKICAgIGNvbnN0IHVzZXJBdmF0YXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndXNlci1hdmF0YXInKTsKICAgIGNvbnN0IGxvZ2luRm9ybUNhcmQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9naW5Gb3JtQ2FyZCcpOwoKICAgIGlmICh1c2VyRW1haWwpIHVzZXJFbWFpbC50ZXh0Q29udGVudCA9IGRpc3BsYXlOYW1lOwogICAgaWYgKHVzZXJBdmF0YXIpIHVzZXJBdmF0YXIudGV4dENvbnRlbnQgPSBnZXRJbml0aWFscyhkaXNwbGF5TmFtZSk7CiAgICBpZiAodXNlclByb2ZpbGVXcmFwcGVyKSB1c2VyUHJvZmlsZVdyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICBpZiAobG9naW5Gb3JtQ2FyZCkgbG9naW5Gb3JtQ2FyZC5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKfQoKZnVuY3Rpb24gaGlkZVRyYWt0U3RhdHVzKCkgewogICAgY29uc3Qgc3RhdHVzU2VjdGlvbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0cmFrdFN0YXR1c1NlY3Rpb24nKTsKICAgIGNvbnN0IGNvbm5lY3RCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RDb25uZWN0QnRuJyk7CiAgICBpZiAoc3RhdHVzU2VjdGlvbikgc3RhdHVzU2VjdGlvbi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKICAgIGlmIChjb25uZWN0QnRuKSBjb25uZWN0QnRuLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwoKICAgIGNvbnN0IHVzZXJQcm9maWxlV3JhcHBlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1c2VyLXByb2ZpbGUtZHJvcGRvd24td3JhcHBlcicpOwogICAgY29uc3QgbG9naW5Gb3JtQ2FyZCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2dpbkZvcm1DYXJkJyk7CiAgICBpZiAodXNlclByb2ZpbGVXcmFwcGVyKSB1c2VyUHJvZmlsZVdyYXBwZXIuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICBpZiAobG9naW5Gb3JtQ2FyZCkgbG9naW5Gb3JtQ2FyZC5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKfQoKZnVuY3Rpb24gZ2V0SW5pdGlhbHMobmFtZSkgewogICAgaWYgKCFuYW1lKSByZXR1cm4gJz8nOwogICAgY29uc3QgcGFydHMgPSBuYW1lLnRyaW0oKS5zcGxpdCgvW1xzLl8tXSsvKTsKICAgIGlmIChwYXJ0cy5sZW5ndGggPj0gMikgcmV0dXJuIChwYXJ0c1swXVswXSArIHBhcnRzWzFdWzBdKS50b1VwcGVyQ2FzZSgpOwogICAgcmV0dXJuIG5hbWUuc3Vic3RyaW5nKDAsIDIpLnRvVXBwZXJDYXNlKCk7Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIEV4cG9ydGVkIGhlbHBlcnMgZm9yIGZvcm0uanMKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmV4cG9ydCBmdW5jdGlvbiBnZXRUcmFrdFRva2Vuc0Zyb21TdG9yYWdlKCkgewogICAgcmV0dXJuIGdldFRyYWt0RnJvbVN0b3JhZ2UoKTsKfQo=" | base64 --decode > "$REPO/app/static/js/modules/trakt.js" +echo " wrote app/static/js/modules/trakt.js" + +mkdir -p "$REPO/app/templates/components" +echo "PCEtLSBTRUNUSU9OIDE6IExPR0lOIC0tPgo8c2VjdGlvbiBpZD0ic2VjdC1sb2dpbiIgY2xhc3M9InNwYWNlLXktNiBoaWRkZW4gYW5pbWF0ZS1mYWRlLWluIj4KICAgIDxkaXYgY2xhc3M9Im1iLTgiPgogICAgICAgIDxoMiBjbGFzcz0idGV4dC0zeGwgZm9udC1ib2xkIHRleHQtd2hpdGUgbWItMiI+Q29ubmVjdCBZb3VyIExpYnJhcnk8L2gyPgogICAgICAgIDxwIGNsYXNzPSJ0ZXh0LXNsYXRlLTQwMCI+TG9nIGluIHdpdGggU3RyZW1pbyBvciBUcmFrdCBzbyBXYXRjaGx5IGNhbiByZWFkIHlvdXIgd2F0Y2ggaGlzdG9yeS48L3A+CiAgICA8L2Rpdj4KCiAgICA8IS0tIExvZ2dlZCBJbiBTdGF0dXMgKHNob3duIGFmdGVyIGxvZ2luIC0gc2hhcmVkIGFjcm9zcyBib3RoIHByb3ZpZGVycykgLS0+CiAgICA8ZGl2IGlkPSJsb2dpblN0YXR1c1NlY3Rpb24iIGNsYXNzPSJoaWRkZW4gYmctbmV1dHJhbC05MDAvNjAgYm9yZGVyIGJvcmRlci13aGl0ZS8xMCByb3VuZGVkLTJ4bCBwLTYgbWQ6cC04IGJhY2tkcm9wLWJsdXItc20gc2hhZG93LXhsIHNoYWRvdy1ibGFjay8yMCI+CiAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIGdhcC00Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idy0xMCBoLTEwIHJvdW5kZWQtZnVsbCBiZy13aGl0ZSB0ZXh0LWJsYWNrIHJpbmctMSByaW5nLXdoaXRlLzEwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGZvbnQtYm9sZCB0ZXh0LXNtIGZsZXgtc2hyaW5rLTAiCiAgICAgICAgICAgICAgICAgICAgaWQ9ImxvZ2luU3RhdHVzQXZhdGFyIj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9InRleHQteHMgdGV4dC1zbGF0ZS01MDAgbWItMC41Ij5Mb2dnZWQgaW4gYXM8L2Rpdj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ0ZXh0LXNtIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gdHJ1bmNhdGUiIGlkPSJsb2dpblN0YXR1c0VtYWlsIj48L2Rpdj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJsb2dpblN0YXR1c0xvZ291dEJ0biIKICAgICAgICAgICAgICAgIGNsYXNzPSJmbGV4LXNocmluay0wIGJnLXJlZC02MDAgaG92ZXI6YmctcmVkLTcwMCB0ZXh0LXdoaXRlIGZvbnQtbWVkaXVtIHB5LTIuNSBweC00IHJvdW5kZWQteGwgdHJhbnNpdGlvbiBib3JkZXIgYm9yZGVyLXJlZC03MDAgc2hhZG93LWxnIHNoYWRvdy1yZWQtOTAwLzIwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGdhcC0yIj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNCBoLTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIgogICAgICAgICAgICAgICAgICAgICAgICBkPSJNMTcgMTZsNC00bTAgMGwtNC00bTQgNEg3bTYgNHYxYTMgMyAwIDAxLTMgM0g2YTMgMyAwIDAxLTMtM1Y3YTMgMyAwIDAxMy0zaDRhMyAzIDAgMDEzIDN2MSI+CiAgICAgICAgICAgICAgICAgICAgPC9wYXRoPgogICAgICAgICAgICAgICAgPC9zdmc+CiAgICAgICAgICAgICAgICA8c3Bhbj5Mb2dvdXQ8L3NwYW4+CiAgICAgICAgICAgIDwvYnV0dG9uPgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CgogICAgPGRpdiBpZD0ibG9naW5Gb3JtQ2FyZCIgY2xhc3M9ImJnLW5ldXRyYWwtOTAwLzYwIGJvcmRlciBib3JkZXItd2hpdGUvMTAgcm91bmRlZC0yeGwgcC02IG1kOnAtOCBiYWNrZHJvcC1ibHVyLXNtIHNoYWRvdy14bCBzaGFkb3ctYmxhY2svMjAiPgoKICAgICAgICA8IS0tIFByb3ZpZGVyIFRhYnMgLS0+CiAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBnYXAtMSBtYi02IGJnLW5ldXRyYWwtODAwLzYwIHAtMSByb3VuZGVkLXhsIj4KICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJ0YWJTdHJlbWlvIgogICAgICAgICAgICAgICAgY2xhc3M9ImxvZ2luLXRhYiBmbGV4LTEgcHktMi41IHB4LTQgcm91bmRlZC1sZyB0ZXh0LXNtIGZvbnQtbWVkaXVtIHRyYW5zaXRpb24tYWxsIGJnLXdoaXRlIHRleHQtYmxhY2sgc2hhZG93IgogICAgICAgICAgICAgICAgZGF0YS10YWI9InN0cmVtaW8iPgogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGdhcC0yIj4KICAgICAgICAgICAgICAgICAgICA8aW1nIHNyYz0iaHR0cHM6Ly9zdHJlbWlvLmNvbS93ZWJzaXRlL3N0cmVtaW8tbG9nby1zbWFsbC5wbmciIGNsYXNzPSJ3LTQgaC00IiBhbHQ9IiI+CiAgICAgICAgICAgICAgICAgICAgU3RyZW1pbwogICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJ0YWJUcmFrdCIKICAgICAgICAgICAgICAgIGNsYXNzPSJsb2dpbi10YWIgZmxleC0xIHB5LTIuNSBweC00IHJvdW5kZWQtbGcgdGV4dC1zbSBmb250LW1lZGl1bSB0cmFuc2l0aW9uLWFsbCB0ZXh0LXNsYXRlLTQwMCBob3Zlcjp0ZXh0LXdoaXRlIgogICAgICAgICAgICAgICAgZGF0YS10YWI9InRyYWt0Ij4KICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBnYXAtMiI+CiAgICAgICAgICAgICAgICAgICAgPCEtLSBUcmFrdCBpY29uIChzaW1wbGUgInQiIGJhZGdlKSAtLT4KICAgICAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPSJ3LTQgaC00IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9ImN1cnJlbnRDb2xvciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMiAyQzYuNDc3IDIgMiA2LjQ3NyAyIDEyczQuNDc3IDEwIDEwIDEwIDEwLTQuNDc3IDEwLTEwUzE3LjUyMyAyIDEyIDJ6bS0uNSA1aDEuNXY2aDN2MS41aC00LjVWN3oiLz4KICAgICAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgICAgICAgICBUcmFrdAogICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPCEtLSA9PT09PSBTVFJFTUlPIFBBTkVMID09PT09IC0tPgogICAgICAgIDxkaXYgaWQ9InBhbmVsU3RyZW1pbyI+CiAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0ic3RyZW1pb0xvZ2luQnRuIgogICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBiZy1zdHJlbWlvIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcHktNCByb3VuZGVkLXhsIHRyYW5zaXRpb24gZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTMgYm9yZGVyIGJvcmRlci1zdHJlbWlvLWJvcmRlciBzaGFkb3ctbGcgc2hhZG93LXN0cmVtaW8vMjAgZ3JvdXAgaG92ZXI6Ymctd2hpdGUgaG92ZXI6dGV4dC1ibGFjayBob3Zlcjpib3JkZXItd2hpdGUvMTAiPgogICAgICAgICAgICAgICAgPGltZyBzcmM9Imh0dHBzOi8vc3RyZW1pby5jb20vd2Vic2l0ZS9zdHJlbWlvLWxvZ28tc21hbGwucG5nIgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ3LTYgaC02IGdyb3VwLWhvdmVyOnNjYWxlLTExMCB0cmFuc2l0aW9uLXRyYW5zZm9ybSIgYWx0PSJTdHJlbWlvIj4KICAgICAgICAgICAgICAgIDxzcGFuIGlkPSJzdHJlbWlvTG9naW5UZXh0IiBjbGFzcz0idGV4dC1sZyI+TG9naW4gd2l0aCBTdHJlbWlvPC9zcGFuPgogICAgICAgICAgICA8L2J1dHRvbj4KCiAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJoaWRkZW4iIGlkPSJhdXRoS2V5Ij4KCiAgICAgICAgICAgIDwhLS0gRGl2aWRlciAtLT4KICAgICAgICAgICAgPGRpdiBpZD0iZW1haWxQd2REaXZpZGVyIiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgbXktNiI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJoLXB4IGJnLXdoaXRlLzEwIHctZnVsbCI+PC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ0ZXh0LXhzIHRleHQtc2xhdGUtNTAwIj5vcjwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iaC1weCBiZy13aGl0ZS8xMCB3LWZ1bGwiPjwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDwhLS0gRW1haWwvUGFzc3dvcmQgTG9naW4gLS0+CiAgICAgICAgICAgIDxkaXYgaWQ9ImVtYWlsUHdkU2VjdGlvbiIgY2xhc3M9ImdyaWQgZ2FwLTMiPgogICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzPSJ0ZXh0LXhzIHRleHQtc2xhdGUtNDAwIj5FbWFpbDwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgaWQ9ImVtYWlsSW5wdXQiIHR5cGU9ImVtYWlsIiBhdXRvY29tcGxldGU9ImVtYWlsIiBpbnB1dG1vZGU9ImVtYWlsIgogICAgICAgICAgICAgICAgICAgIHNwZWxsY2hlY2s9ImZhbHNlIiByZXF1aXJlZCBwbGFjZWhvbGRlcj0ieW91QGV4YW1wbGUuY29tIgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ3LWZ1bGwgYmctbmV1dHJhbC05MDAgYm9yZGVyIGJvcmRlci1zbGF0ZS03MDAgcm91bmRlZC14bCBweC00IHB5LTMuNSB0ZXh0LXdoaXRlIHBsYWNlaG9sZGVyLXNsYXRlLTUwMCBmb2N1czpyaW5nLTIgZm9jdXM6cmluZy13aGl0ZS8yMCBmb2N1czpib3JkZXItd2hpdGUvMzAgb3V0bGluZS1ub25lIHRyYW5zaXRpb24tYWxsIj4KICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzcz0idGV4dC14cyB0ZXh0LXNsYXRlLTQwMCI+UGFzc3dvcmQ8L2xhYmVsPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0icmVsYXRpdmUiPgogICAgICAgICAgICAgICAgICAgIDxpbnB1dCBpZD0icGFzc3dvcmRJbnB1dCIgdHlwZT0icGFzc3dvcmQiIGF1dG9jb21wbGV0ZT0iY3VycmVudC1wYXNzd29yZCIKICAgICAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9IllvdXIgU3RyZW1pbyBwYXNzd29yZCIKICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBiZy1uZXV0cmFsLTkwMCBib3JkZXIgYm9yZGVyLXNsYXRlLTcwMCByb3VuZGVkLXhsIHBsLTQgcHItMTIgcHktMy41IHRleHQtd2hpdGUgcGxhY2Vob2xkZXItc2xhdGUtNTAwIGZvY3VzOnJpbmctMiBmb2N1czpyaW5nLXdoaXRlLzIwIGZvY3VzOmJvcmRlci13aGl0ZS8zMCBvdXRsaW5lLW5vbmUgdHJhbnNpdGlvbi1hbGwiPgogICAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIgogICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz0idG9nZ2xlLWJ0biBhYnNvbHV0ZSByaWdodC0yIHRvcC0xLzIgLXRyYW5zbGF0ZS15LTEvMiBiZy13aGl0ZS8xMCBob3ZlcjpiZy13aGl0ZS8yMCB0ZXh0LXdoaXRlIHAtMiByb3VuZGVkLWxnIgogICAgICAgICAgICAgICAgICAgICAgICBhcmlhLWxhYmVsPSJTaG93IHBhc3N3b3JkIiB0aXRsZT0iU2hvdyIgZGF0YS10YXJnZXQ9InBhc3N3b3JkSW5wdXQiPgogICAgICAgICAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPSJ3LTQgaC00IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEgMTJzNC03IDExLTcgMTEgNyAxMSA3LTQgNy0xMSA3LTExLTctMTEtN3oiIC8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIzIiAvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJlbWFpbFB3ZENvbnRpbnVlQnRuIgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJtdC0yIHctZnVsbCBiZy13aGl0ZSB0ZXh0LWJsYWNrIGhvdmVyOmJnLXdoaXRlLzkwIGZvbnQtbWVkaXVtIHB5LTMgcm91bmRlZC14bCB0cmFuc2l0aW9uIGJvcmRlciBib3JkZXItd2hpdGUvMTAgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTIiPgogICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJidG4tdGV4dCI+Q29udGludWUgd2l0aCBFbWFpbDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJsb2FkZXIgaGlkZGVuIHctNSBoLTUgYm9yZGVyLTIgYm9yZGVyLWJsYWNrLzMwIGJvcmRlci10LWJsYWNrIHJvdW5kZWQtZnVsbCBhbmltYXRlLXNwaW4iPgogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPC9idXR0b24+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgPCEtLSBJbmxpbmUgZXJyb3IgZm9yIGVtYWlsL3Bhc3N3b3JkIGxvZ2luIC0tPgogICAgICAgICAgICA8ZGl2IGlkPSJlbWFpbFB3ZEVycm9yIiBjbGFzcz0iaGlkZGVuIG10LTMgcC0zIGJnLXJlZC01MDAvMTAgYm9yZGVyIGJvcmRlci1yZWQtNTAwLzIwIHJvdW5kZWQteGwgdGV4dC1yZWQtMjAwIHRleHQtc20iPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDwhLS0gRGlzY2xhaW1lciAtLT4KICAgICAgICAgICAgPGRpdiBpZD0iZW1haWxQd2REaXNjbGFpbWVyIgogICAgICAgICAgICAgICAgY2xhc3M9Im10LTQgdGV4dC14cyBsZWFkaW5nLXJlbGF4ZWQgYmcteWVsbG93LTUwMC8xMCBib3JkZXIgYm9yZGVyLXllbGxvdy01MDAvMzAgdGV4dC15ZWxsb3ctMjAwIHJvdW5kZWQteGwgcC0zIj4KICAgICAgICAgICAgICAgIDxzdHJvbmcgY2xhc3M9InRleHQteWVsbG93LTMwMCI+V2h5IGVtYWlsICZhbXA7IHBhc3N3b3JkPzwvc3Ryb25nPgogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImJsb2NrIG10LTEiPldlIHN0b3JlIHlvdXIgY3JlZGVudGlhbHMgc2VjdXJlbHkgdG8gZ2VuZXJhdGUgYSBmcmVzaCBTdHJlbWlvIGF1dGgKICAgICAgICAgICAgICAgICAgICBrZXkgYXV0b21hdGljYWxseSB3aGVuIG5lZWRlZC4gVGhpcyBhdm9pZHMgZXhwaXJlZCBrZXlzIGFuZCBrZWVwcyB5b3VyIGFkZG9uIHdvcmtpbmcKICAgICAgICAgICAgICAgICAgICB3aXRob3V0IG1hbnVhbCByZS1sb2dpbi48L3NwYW4+CiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYmxvY2sgbXQtMiI+UHJlZmVyIG5vdCB0byBzaGFyZSB5b3VyIHBhc3N3b3JkPyBVc2UgdGhlIFN0cmVtaW8gbG9naW4gYWJvdmUgdG8KICAgICAgICAgICAgICAgICAgICBzdXBwbHkgYW4gYXV0aCBrZXkuIE5vdGU6IGF1dGgga2V5cyBjYW4gZXhwaXJlIGFuZCBtYXkgcmVxdWlyZSBwZXJpb2RpYwogICAgICAgICAgICAgICAgICAgIHJlLWF1dGhlbnRpY2F0aW9uLjwvc3Bhbj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICAgICAgPCEtLSBlbmQgI3BhbmVsU3RyZW1pbyAtLT4KCiAgICAgICAgPCEtLSA9PT09PSBUUkFLVCBQQU5FTCA9PT09PSAtLT4KICAgICAgICA8ZGl2IGlkPSJwYW5lbFRyYWt0IiBjbGFzcz0iaGlkZGVuIj4KCiAgICAgICAgICAgIDwhLS0gTG9nZ2VkLWluIHN0YXRlIC0tPgogICAgICAgICAgICA8ZGl2IGlkPSJ0cmFrdFN0YXR1c1NlY3Rpb24iIGNsYXNzPSJoaWRkZW4gbWItNCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWJldHdlZW4gZ2FwLTQgcC00IGJnLW5ldXRyYWwtODAwLzYwIHJvdW5kZWQteGwgYm9yZGVyIGJvcmRlci13aGl0ZS8xMCI+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ3LTEwIGgtMTAgcm91bmRlZC1mdWxsIGJnLVsjZWQxYzI0XSB0ZXh0LXdoaXRlIHJpbmctMSByaW5nLXdoaXRlLzEwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGZvbnQtYm9sZCB0ZXh0LXNtIGZsZXgtc2hyaW5rLTAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0idHJha3RTdGF0dXNBdmF0YXIiPlQ8L2Rpdj4KICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC14cyB0ZXh0LXNsYXRlLTUwMCBtYi0wLjUiPkNvbm5lY3RlZCBhczwvZGl2PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC1zbSB0ZXh0LXdoaXRlIGZvbnQtbWVkaXVtIHRydW5jYXRlIiBpZD0idHJha3RTdGF0dXNEaXNwbGF5Ij48L2Rpdj4KICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJ0cmFrdExvZ291dEJ0biIKICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9ImZsZXgtc2hyaW5rLTAgYmctcmVkLTYwMCBob3ZlcjpiZy1yZWQtNzAwIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcHktMiBweC0zIHJvdW5kZWQteGwgdHJhbnNpdGlvbiBib3JkZXIgYm9yZGVyLXJlZC03MDAgdGV4dC1zbSI+CiAgICAgICAgICAgICAgICAgICAgICAgIERpc2Nvbm5lY3QKICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDwhLS0gQ29ubmVjdCBidXR0b24gLS0+CiAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0idHJha3RDb25uZWN0QnRuIgogICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBiZy1bI2VkMWMyNF0gaG92ZXI6YmctWyNjNDEwMTldIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcHktNCByb3VuZGVkLXhsIHRyYW5zaXRpb24gZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTMgYm9yZGVyIGJvcmRlci1bI2IwMGUxNV0gc2hhZG93LWxnIHNoYWRvdy1yZWQtOTAwLzIwIj4KICAgICAgICAgICAgICAgIDwhLS0gVHJha3QgIlQiIGxvZ28gYXMgc2ltcGxlIFNWRyAtLT4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNiBoLTYgZmxleC1zaHJpbmstMCIgdmlld0JveD0iMCAwIDMyIDMyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgY3g9IjE2IiBjeT0iMTYiIHI9IjE2IiBmaWxsPSJ3aGl0ZSIvPgogICAgICAgICAgICAgICAgICAgIDx0ZXh0IHg9IjE2IiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9ImJvbGQiIGZpbGw9IiNlZDFjMjQiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIj50PC90ZXh0PgogICAgICAgICAgICAgICAgPC9zdmc+CiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYnRuLXRleHQgdGV4dC1sZyI+Q29ubmVjdCB3aXRoIFRyYWt0PC9zcGFuPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0ibG9hZGVyIGhpZGRlbiB3LTUgaC01IGJvcmRlci0yIGJvcmRlci13aGl0ZS8zMCBib3JkZXItdC13aGl0ZSByb3VuZGVkLWZ1bGwgYW5pbWF0ZS1zcGluIj48L2Rpdj4KICAgICAgICAgICAgPC9idXR0b24+CgogICAgICAgICAgICA8IS0tIEhpZGRlbiBmaWVsZCB0byBjYXJyeSBUcmFrdCBkaXNwbGF5IG5hbWUgdGhyb3VnaCB0byBmb3JtIHN1Ym1pc3Npb24gLS0+CiAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJoaWRkZW4iIGlkPSJ0cmFrdERpc3BsYXlOYW1lIj4KCiAgICAgICAgICAgIDxkaXYgY2xhc3M9Im10LTQgdGV4dC14cyBsZWFkaW5nLXJlbGF4ZWQgYmctYmx1ZS01MDAvMTAgYm9yZGVyIGJvcmRlci1ibHVlLTUwMC8zMCB0ZXh0LWJsdWUtMjAwIHJvdW5kZWQteGwgcC0zIj4KICAgICAgICAgICAgICAgIDxzdHJvbmcgY2xhc3M9InRleHQtYmx1ZS0zMDAiPkhvdyBpdCB3b3Jrczwvc3Ryb25nPgogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImJsb2NrIG10LTEiPkNsaWNraW5nICJDb25uZWN0IHdpdGggVHJha3QiIG9wZW5zIGEgc2VjdXJlIFRyYWt0IGF1dGhvcml6YXRpb24KICAgICAgICAgICAgICAgICAgICB3aW5kb3cuIEFmdGVyIHlvdSBhcHByb3ZlIGFjY2VzcywgV2F0Y2hseSB1c2VzIHlvdXIgVHJha3Qgd2F0Y2ggaGlzdG9yeSB0byBnZW5lcmF0ZQogICAgICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyDigJQgbm8gU3RyZW1pbyBhY2NvdW50IG5lZWRlZC48L3NwYW4+CiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYmxvY2sgbXQtMiI+WW91ciBUcmFrdCBhY2Nlc3MgdG9rZW4gaXMgc3RvcmVkIGVuY3J5cHRlZCBvbiB0aGUgc2VydmVyIGFuZAogICAgICAgICAgICAgICAgICAgIGlzIG5ldmVyIHNoYXJlZCBvciBleHBvc2VkIGluIFVSTHMuPC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgICAgICA8IS0tIGVuZCAjcGFuZWxUcmFrdCAtLT4KCiAgICA8L2Rpdj4KICAgIDwhLS0gZW5kICNsb2dpbkZvcm1DYXJkIC0tPgo8L3NlY3Rpb24+Cgo8c2NyaXB0PgovLyBUYWIgc3dpdGNoaW5nIGxvZ2ljIChpbmxpbmUgdG8gYXZvaWQgbW9kdWxlLWxvYWRpbmcgb3JkZXIgaXNzdWVzKQooZnVuY3Rpb24gKCkgewogICAgZnVuY3Rpb24gc3dpdGNoTG9naW5UYWIodGFiKSB7CiAgICAgICAgY29uc3QgdGFicyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5sb2dpbi10YWInKTsKICAgICAgICBjb25zdCBwYW5lbFN0cmVtaW8gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncGFuZWxTdHJlbWlvJyk7CiAgICAgICAgY29uc3QgcGFuZWxUcmFrdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYW5lbFRyYWt0Jyk7CgogICAgICAgIHRhYnMuZm9yRWFjaCh0ID0+IHsKICAgICAgICAgICAgY29uc3QgYWN0aXZlID0gdC5kYXRhc2V0LnRhYiA9PT0gdGFiOwogICAgICAgICAgICB0LmNsYXNzTGlzdC50b2dnbGUoJ2JnLXdoaXRlJywgYWN0aXZlKTsKICAgICAgICAgICAgdC5jbGFzc0xpc3QudG9nZ2xlKCd0ZXh0LWJsYWNrJywgYWN0aXZlKTsKICAgICAgICAgICAgdC5jbGFzc0xpc3QudG9nZ2xlKCdzaGFkb3cnLCBhY3RpdmUpOwogICAgICAgICAgICB0LmNsYXNzTGlzdC50b2dnbGUoJ3RleHQtc2xhdGUtNDAwJywgIWFjdGl2ZSk7CiAgICAgICAgICAgIHQuY2xhc3NMaXN0LnRvZ2dsZSgnaG92ZXI6dGV4dC13aGl0ZScsICFhY3RpdmUpOwogICAgICAgIH0pOwoKICAgICAgICBpZiAocGFuZWxTdHJlbWlvKSBwYW5lbFN0cmVtaW8uY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgdGFiICE9PSAnc3RyZW1pbycpOwogICAgICAgIGlmIChwYW5lbFRyYWt0KSBwYW5lbFRyYWt0LmNsYXNzTGlzdC50b2dnbGUoJ2hpZGRlbicsIHRhYiAhPT0gJ3RyYWt0Jyk7CgogICAgICAgIC8vIFBlcnNpc3QgY2hvaWNlCiAgICAgICAgdHJ5IHsgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ3dhdGNobHlfbG9naW5fdGFiJywgdGFiKTsgfSBjYXRjaCAoZSkgeyB9CiAgICB9CgogICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGZ1bmN0aW9uICgpIHsKICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcubG9naW4tdGFiJykuZm9yRWFjaChidG4gPT4gewogICAgICAgICAgICBidG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiBzd2l0Y2hMb2dpblRhYihidG4uZGF0YXNldC50YWIpKTsKICAgICAgICB9KTsKCiAgICAgICAgLy8gUmVzdG9yZSBsYXN0IHRhYgogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGNvbnN0IHNhdmVkID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oJ3dhdGNobHlfbG9naW5fdGFiJyk7CiAgICAgICAgICAgIGlmIChzYXZlZCA9PT0gJ3RyYWt0Jykgc3dpdGNoTG9naW5UYWIoJ3RyYWt0Jyk7CiAgICAgICAgfSBjYXRjaCAoZSkgeyB9CiAgICB9KTsKfSkoKTsKPC9zY3JpcHQ+Cgo8c2NyaXB0PgovLyBIaWRlIHRoZSBUcmFrdCB0YWIgaWYgVHJha3QgaXMgbm90IGNvbmZpZ3VyZWQgb24gdGhpcyBzZXJ2ZXIKKGZ1bmN0aW9uICgpIHsKICAgIGZldGNoKCcvdG9rZW5zL3RyYWt0L2NvbmZpZycpCiAgICAgICAgLnRoZW4ociA9PiByLmpzb24oKSkKICAgICAgICAudGhlbihkYXRhID0+IHsKICAgICAgICAgICAgaWYgKCFkYXRhLmNvbmZpZ3VyZWQpIHsKICAgICAgICAgICAgICAgIGNvbnN0IHRyYWt0VGFiID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RhYlRyYWt0Jyk7CiAgICAgICAgICAgICAgICBpZiAodHJha3RUYWIpIHRyYWt0VGFiLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgICAgICAgIH0KICAgICAgICB9KQogICAgICAgIC5jYXRjaCgoKSA9PiB7CiAgICAgICAgICAgIC8vIElmIHRoZSBlbmRwb2ludCBmYWlscyAodW5saWtlbHkpLCBsZWF2ZSB0YWJzIGFzLWlzCiAgICAgICAgfSk7Cn0pKCk7Cjwvc2NyaXB0PgoK" | base64 --decode > "$REPO/app/templates/components/section_login.html" +echo " wrote app/templates/components/section_login.html" + +mkdir -p "$REPO/app/api" +echo "ZnJvbSBmYXN0YXBpIGltcG9ydCBBUElSb3V0ZXIKCmZyb20gLmVuZHBvaW50cy5hbm5vdW5jZW1lbnQgaW1wb3J0IHJvdXRlciBhcyBhbm5vdW5jZW1lbnRfcm91dGVyCmZyb20gLmVuZHBvaW50cy5jYXRhbG9ncyBpbXBvcnQgcm91dGVyIGFzIGNhdGFsb2dzX3JvdXRlcgpmcm9tIC5lbmRwb2ludHMuaGVhbHRoIGltcG9ydCByb3V0ZXIgYXMgaGVhbHRoX3JvdXRlcgpmcm9tIC5lbmRwb2ludHMubWFuaWZlc3QgaW1wb3J0IHJvdXRlciBhcyBtYW5pZmVzdF9yb3V0ZXIKZnJvbSAuZW5kcG9pbnRzLm1ldGEgaW1wb3J0IHJvdXRlciBhcyBtZXRhX3JvdXRlcgpmcm9tIC5lbmRwb2ludHMuc3RhdHMgaW1wb3J0IHJvdXRlciBhcyBzdGF0c19yb3V0ZXIKZnJvbSAuZW5kcG9pbnRzLnRva2VucyBpbXBvcnQgcm91dGVyIGFzIHRva2Vuc19yb3V0ZXIKZnJvbSAuZW5kcG9pbnRzLnRyYWt0IGltcG9ydCByb3V0ZXIgYXMgdHJha3Rfcm91dGVyCmZyb20gLmVuZHBvaW50cy52YWxpZGF0aW9uIGltcG9ydCByb3V0ZXIgYXMgdmFsaWRhdGlvbl9yb3V0ZXIKCmFwaV9yb3V0ZXIgPSBBUElSb3V0ZXIoKQoKCkBhcGlfcm91dGVyLmdldCgiLyIpCmFzeW5jIGRlZiByb290KCk6CiAgICByZXR1cm4geyJtZXNzYWdlIjogIldhdGNobHkgQVBJIGlzIHJ1bm5pbmcifQoKCmFwaV9yb3V0ZXIuaW5jbHVkZV9yb3V0ZXIobWFuaWZlc3Rfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKGNhdGFsb2dzX3JvdXRlcikKYXBpX3JvdXRlci5pbmNsdWRlX3JvdXRlcih0b2tlbnNfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKHRyYWt0X3JvdXRlcikKYXBpX3JvdXRlci5pbmNsdWRlX3JvdXRlcihoZWFsdGhfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKG1ldGFfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKGFubm91bmNlbWVudF9yb3V0ZXIpCmFwaV9yb3V0ZXIuaW5jbHVkZV9yb3V0ZXIoc3RhdHNfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKHZhbGlkYXRpb25fcm91dGVyKQo=" | base64 --decode > "$REPO/app/api/router.py" +echo " wrote app/api/router.py" + +mkdir -p "$REPO/app/core" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IExpdGVyYWwKCmZyb20gcHlkYW50aWNfc2V0dGluZ3MgaW1wb3J0IEJhc2VTZXR0aW5ncywgU2V0dGluZ3NDb25maWdEaWN0Cgpmcm9tIGFwcC5jb3JlLnZlcnNpb24gaW1wb3J0IF9fdmVyc2lvbl9fCgoKY2xhc3MgU2V0dGluZ3MoQmFzZVNldHRpbmdzKToKICAgICIiIkFwcGxpY2F0aW9uIHNldHRpbmdzIGxvYWRlZCBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcy4iIiIKCiAgICBtb2RlbF9jb25maWcgPSBTZXR0aW5nc0NvbmZpZ0RpY3QoCiAgICAgICAgZW52X2ZpbGU9Ii5lbnYiLAogICAgICAgIGVudl9maWxlX2VuY29kaW5nPSJ1dGYtOCIsCiAgICAgICAgY2FzZV9zZW5zaXRpdmU9RmFsc2UsCiAgICAgICAgZXh0cmE9ImFsbG93IiwKICAgICkKCiAgICBUTURCX0FQSV9LRVk6IHN0ciB8IE5vbmUgPSBOb25lCiAgICBQT1JUOiBpbnQgPSA4MDAwCiAgICBBRERPTl9JRDogc3RyID0gImNvbS5iaW1hbC53YXRjaGx5IgogICAgQURET05fTkFNRTogc3RyID0gIldhdGNobHkiCiAgICBSRURJU19VUkw6IHN0ciA9ICJyZWRpczovL3JlZGlzOjYzNzkvMCIKICAgICMgTWF4aW11bSBudW1iZXIgb2YgY29ubmVjdGlvbnMgUmVkaXMgY2xpZW50IHdpbGwgb3BlbiBwZXIgcHJvY2VzcwogICAgIyBTZXQgY29uc2VydmF0aXZlbHkgdG8gYXZvaWQgdW5ib3VuZGVkIGNvbm5lY3Rpb24gZ3Jvd3RoIHVuZGVyIGhpZ2ggY29uY3VycmVuY3kKICAgIFJFRElTX01BWF9DT05ORUNUSU9OUzogaW50ID0gMjAKICAgICMgSWYgdG90YWwgY29ubmVjdGVkIGNsaWVudHMgcmVwb3J0ZWQgYnkgUmVkaXMgZXhjZWVkcyB0aGlzLCBiYWNrZ3JvdW5kCiAgICAjIFJlZGlzLWhlYXZ5IGpvYnMgd2lsbCBiYWNrIG9mZi4gVHVuZSBhY2NvcmRpbmcgdG8geW91ciBSZWRpcyBjYXBhY2l0eS4KICAgIFJFRElTX0NPTk5FQ1RJT05TX1RIUkVTSE9MRDogaW50ID0gMTAwCiAgICBSRURJU19UT0tFTl9LRVk6IHN0ciA9ICJ3YXRjaGx5OnRva2VuOiIKICAgIFRPS0VOX1NBTFQ6IHN0ciA9ICJjaGFuZ2UtbWUiCiAgICBUT0tFTl9UVExfU0VDT05EUzogaW50ID0gMCAgIyAwID0gbmV2ZXIgZXhwaXJlCiAgICBBTk5PVU5DRU1FTlRfSFRNTDogc3RyID0gIiIKICAgIEFVVE9fVVBEQVRFX0NBVEFMT0dTOiBib29sID0gVHJ1ZQogICAgQ0FUQUxPR19SRUZSRVNIX0lOVEVSVkFMX1NFQ09ORFM6IGludCA9IDg2NDAwICAjIDI0IGhvdXJzCiAgICBBUFBfRU5WOiBMaXRlcmFsWyJkZXZlbG9wbWVudCIsICJwcm9kdWN0aW9uIiwgInZlcmNlbCJdID0gInByb2R1Y3Rpb24iCiAgICBIT1NUX05BTUU6IHN0ciA9ICJodHRwczovLzFjY2VhNDMwMTU4Ny13YXRjaGx5LmJhYnktYmVhbXVwLmNsdWIiCgogICAgUkVDT01NRU5EQVRJT05fU09VUkNFX0lURU1TX0xJTUlUOiBpbnQgPSAxMAogICAgTElCUkFSWV9JVEVNU19MSU1JVDogaW50ID0gMjAKCiAgICBDQVRBTE9HX0NBQ0hFX1RUTDogaW50ID0gNDMyMDAgICMgMTIgaG91cnMKICAgIENBVEFMT0dfU1RBTEVfVFRMOiBpbnQgPSA2MDQ4MDAgICMgNyBkYXlzIChzb2Z0IGV4cGlyYXRpb24gZmFsbGJhY2spCgogICAgIyBBSQogICAgREVGQVVMVF9HRU1JTklfTU9ERUw6IHN0ciA9ICJnZW1tYS0zLTI3Yi1pdCIKICAgIEdFTUlOSV9BUElfS0VZOiBzdHIgfCBOb25lID0gTm9uZQoKICAgICMgVHJha3QgT0F1dGgKICAgIFRSQUtUX0NMSUVOVF9JRDogc3RyIHwgTm9uZSA9IE5vbmUKICAgIFRSQUtUX0NMSUVOVF9TRUNSRVQ6IHN0ciB8IE5vbmUgPSBOb25lCgoKc2V0dGluZ3MgPSBTZXR0aW5ncygpCgpBUFBfVkVSU0lPTiA9IF9fdmVyc2lvbl9fCg==" | base64 --decode > "$REPO/app/core/config.py" +echo " wrote app/core/config.py" + +mkdir -p "$REPO/app/static/js" +echo "Ly8gTWFpbiBlbnRyeSBwb2ludCAtIGluaXRpYWxpemVzIGFsbCBtb2R1bGVzCgppbXBvcnQgeyBkZWZhdWx0Q2F0YWxvZ3MgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7CmltcG9ydCB7IHNob3dUb2FzdCwgaW5pdGlhbGl6ZUZvb3RlciwgaW5pdGlhbGl6ZUtvZmkgfSBmcm9tICcuL21vZHVsZXMvdWkuanMnOwppbXBvcnQgeyBpbml0aWFsaXplTmF2aWdhdGlvbiwgc3dpdGNoU2VjdGlvbiwgbG9ja05hdmlnYXRpb25Gb3JMb2dnZWRPdXQsIGluaXRpYWxpemVNb2JpbGVOYXYsIHVwZGF0ZU1vYmlsZUxheW91dCwgdW5sb2NrTmF2aWdhdGlvbiB9IGZyb20gJy4vbW9kdWxlcy9uYXZpZ2F0aW9uLmpzJzsKaW1wb3J0IHsgaW5pdGlhbGl6ZUF1dGgsIHNldFN0cmVtaW9Mb2dnZWRPdXRTdGF0ZSB9IGZyb20gJy4vbW9kdWxlcy9hdXRoLmpzJzsKaW1wb3J0IHsgaW5pdGlhbGl6ZVRyYWt0LCBzZXRUcmFrdExvZ2dlZE91dFN0YXRlIH0gZnJvbSAnLi9tb2R1bGVzL3RyYWt0LmpzJzsKaW1wb3J0IHsgaW5pdGlhbGl6ZUNhdGFsb2dMaXN0LCByZW5kZXJDYXRhbG9nTGlzdCwgZ2V0Q2F0YWxvZ3MsIHNldENhdGFsb2dzIH0gZnJvbSAnLi9tb2R1bGVzL2NhdGFsb2cuanMnOwppbXBvcnQgeyBpbml0aWFsaXplRm9ybSwgY2xlYXJFcnJvcnMgfSBmcm9tICcuL21vZHVsZXMvZm9ybS5qcyc7CgovLyBJbml0aWFsaXplIGNhdGFsb2dzIHN0YXRlCmxldCBjYXRhbG9nc1N0YXRlID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShkZWZhdWx0Q2F0YWxvZ3MpKTsKCi8vIERPTSBFbGVtZW50cwpjb25zdCBjb25maWdGb3JtID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpZ0Zvcm0nKTsKY29uc3QgY2F0YWxvZ0xpc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2F0YWxvZ0xpc3QnKTsKY29uc3QgbW92aWVHZW5yZUxpc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbW92aWVHZW5yZUxpc3QnKTsKY29uc3Qgc2VyaWVzR2VucmVMaXN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Nlcmllc0dlbnJlTGlzdCcpOwpjb25zdCBzdWJtaXRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc3VibWl0QnRuJyk7CmNvbnN0IHN0cmVtaW9Mb2dpbkJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdHJlbWlvTG9naW5CdG4nKTsKY29uc3Qgc3RyZW1pb0xvZ2luVGV4dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdHJlbWlvTG9naW5UZXh0Jyk7CmNvbnN0IGVtYWlsSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZW1haWxJbnB1dCcpOwpjb25zdCBwYXNzd29yZElucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bhc3N3b3JkSW5wdXQnKTsKY29uc3QgZW1haWxQd2RDb250aW51ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlbWFpbFB3ZENvbnRpbnVlQnRuJyk7CmNvbnN0IGxhbmd1YWdlU2VsZWN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhbmd1YWdlU2VsZWN0Jyk7CmNvbnN0IGNvbmZpZ05leHRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlnTmV4dEJ0bicpOwpjb25zdCBjYXRhbG9nc05leHRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2F0YWxvZ3NOZXh0QnRuJyk7CmNvbnN0IHN1Y2Nlc3NSZXNldEJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdWNjZXNzUmVzZXRCdG4nKTsKY29uc3QgYnRuR2V0U3RhcnRlZCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdidG4tZ2V0LXN0YXJ0ZWQnKTsKCmNvbnN0IG5hdkl0ZW1zID0gewogICAgd2VsY29tZTogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25hdi13ZWxjb21lJyksCiAgICBsb2dpbjogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25hdi1sb2dpbicpLAogICAgY29uZmlnOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmF2LWNvbmZpZycpLAogICAgY2F0YWxvZ3M6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduYXYtY2F0YWxvZ3MnKSwKICAgIGluc3RhbGw6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduYXYtaW5zdGFsbCcpCn07Cgpjb25zdCBzZWN0aW9ucyA9IHsKICAgIHdlbGNvbWU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWN0LXdlbGNvbWUnKSwKICAgIGxvZ2luOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1sb2dpbicpLAogICAgY29uZmlnOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1jb25maWcnKSwKICAgIGNhdGFsb2dzOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1jYXRhbG9ncycpLAogICAgaW5zdGFsbDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3QtaW5zdGFsbCcpLAogICAgc3VjY2VzczogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3Qtc3VjY2VzcycpCn07CgovLyBNYWluIHNjcm9sbCBjb250YWluZXIKY29uc3QgbWFpbkVsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignbWFpbicpOwoKLy8gUmVzZXQgQXBwIEZ1bmN0aW9uCmZ1bmN0aW9uIHJlc2V0QXBwKCkgewogICAgaWYgKGNvbmZpZ0Zvcm0pIGNvbmZpZ0Zvcm0ucmVzZXQoKTsKICAgIGNsZWFyRXJyb3JzKCk7CgogICAgLy8gUmVzZXQgTmF2aWdhdGlvbiBpcyBub3cgQmFjayB0byBXZWxjb21lCiAgICBzd2l0Y2hTZWN0aW9uKCd3ZWxjb21lJyk7CgogICAgLy8gTG9jayBOYXZzCiAgICBPYmplY3Qua2V5cyhuYXZJdGVtcykuZm9yRWFjaChrZXkgPT4gewogICAgICAgIGlmIChrZXkgIT09ICdsb2dpbicgJiYga2V5ICE9PSAnd2VsY29tZScpIHsKICAgICAgICAgICAgaWYgKG5hdkl0ZW1zW2tleV0pIG5hdkl0ZW1zW2tleV0uY2xhc3NMaXN0LmFkZCgnZGlzYWJsZWQnKTsKICAgICAgICB9CiAgICB9KTsKCiAgICAvLyBSZXNldCBTdHJlbWlvIFN0YXRlCiAgICBzZXRTdHJlbWlvTG9nZ2VkT3V0U3RhdGUoKTsKCiAgICAvLyBSZXNldCBUcmFrdCBTdGF0ZQogICAgc2V0VHJha3RMb2dnZWRPdXRTdGF0ZSgpOwoKICAgIC8vIFJlc2V0IGNhdGFsb2dzCiAgICBjYXRhbG9nc1N0YXRlID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShkZWZhdWx0Q2F0YWxvZ3MpKTsKICAgIHNldENhdGFsb2dzKGNhdGFsb2dzU3RhdGUpOwogICAgcmVuZGVyQ2F0YWxvZ0xpc3QoKTsKCiAgICAvLyBTaG93IEZvcm0KICAgIGlmIChjb25maWdGb3JtKSBjb25maWdGb3JtLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgaWYgKHNlY3Rpb25zLnN1Y2Nlc3MpIHNlY3Rpb25zLnN1Y2Nlc3MuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7Cn0KCi8vIFdlbGNvbWUgRmxvdyBMb2dpYwpmdW5jdGlvbiBpbml0aWFsaXplV2VsY29tZUZsb3coKSB7CiAgICBpZiAoIWJ0bkdldFN0YXJ0ZWQpIHJldHVybjsKCiAgICAvLyBTdXBwb3J0IG1vYmlsZSB0YXBzIHJlbGlhYmx5IHdoaWxlIGF2b2lkaW5nIGRvdWJsZS1maXJlICh0b3VjaCAtPiBjbGljaykKICAgIGxldCB0b3VjaGVkID0gZmFsc2U7CiAgICBjb25zdCBoYW5kbGVHZXRTdGFydGVkID0gKGUpID0+IHsKICAgICAgICBpZiAoZS50eXBlID09PSAnY2xpY2snICYmIHRvdWNoZWQpIHJldHVybjsKICAgICAgICBpZiAoZS50eXBlID09PSAndG91Y2hzdGFydCcpIHRvdWNoZWQgPSB0cnVlOwogICAgICAgIGlmIChuYXZJdGVtcy5sb2dpbikgbmF2SXRlbXMubG9naW4uY2xhc3NMaXN0LnJlbW92ZSgnZGlzYWJsZWQnKTsKICAgICAgICBzd2l0Y2hTZWN0aW9uKCdsb2dpbicpOwogICAgfTsKCiAgICBidG5HZXRTdGFydGVkLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgaGFuZGxlR2V0U3RhcnRlZCk7CiAgICBidG5HZXRTdGFydGVkLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBoYW5kbGVHZXRTdGFydGVkLCB7IHBhc3NpdmU6IHRydWUgfSk7Cn0KCi8vIEluaXRpYWxpemUgZXZlcnl0aGluZwpkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4gewogICAgLy8gU3RhcnQgYXQgV2VsY29tZQogICAgc3dpdGNoU2VjdGlvbignd2VsY29tZScpOwogICAgaW5pdGlhbGl6ZVdlbGNvbWVGbG93KCk7CgogICAgLy8gSW5pdGlhbGl6ZSBhbGwgbW9kdWxlcwogICAgaW5pdGlhbGl6ZU5hdmlnYXRpb24oewogICAgICAgIG5hdkl0ZW1zLAogICAgICAgIHNlY3Rpb25zLAogICAgICAgIG1haW5FbAogICAgfSk7CgogICAgLy8gQnkgZGVmYXVsdCwgZW5zdXJlIGxvZ2dlZC1vdXQgdXNlcnMgc2VlIG9ubHkgV2VsY29tZS9Mb2dpbgogICAgbG9ja05hdmlnYXRpb25Gb3JMb2dnZWRPdXQoKTsKCiAgICAvLyBJbml0aWFsaXplIGNhdGFsb2cgbWFuYWdlbWVudCAtIHNldCBjYXRhbG9ncyBmaXJzdAogICAgc2V0Q2F0YWxvZ3MoY2F0YWxvZ3NTdGF0ZSk7CiAgICBpbml0aWFsaXplQ2F0YWxvZ0xpc3QoCiAgICAgICAgeyBjYXRhbG9nTGlzdCB9LAogICAgICAgIHsKICAgICAgICAgICAgY2F0YWxvZ3M6IGNhdGFsb2dzU3RhdGUsCiAgICAgICAgICAgIHJlbmRlckNhdGFsb2dMaXN0CiAgICAgICAgfQogICAgKTsKCiAgICAvLyBJbml0aWFsaXplIGF1dGhlbnRpY2F0aW9uIChTdHJlbWlvKQogICAgaW5pdGlhbGl6ZUF1dGgoCiAgICAgICAgewogICAgICAgICAgICBzdHJlbWlvTG9naW5CdG4sCiAgICAgICAgICAgIHN0cmVtaW9Mb2dpblRleHQsCiAgICAgICAgICAgIGVtYWlsSW5wdXQsCiAgICAgICAgICAgIHBhc3N3b3JkSW5wdXQsCiAgICAgICAgICAgIGVtYWlsUHdkQ29udGludWVCdG4sCiAgICAgICAgICAgIGxhbmd1YWdlU2VsZWN0CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAgIGdldENhdGFsb2dzLAogICAgICAgICAgICByZW5kZXJDYXRhbG9nTGlzdCwKICAgICAgICAgICAgcmVzZXRBcHAKICAgICAgICB9CiAgICApOwoKICAgIC8vIEluaXRpYWxpemUgVHJha3QgYXV0aGVudGljYXRpb24KICAgIGluaXRpYWxpemVUcmFrdCgKICAgICAgICB7IGxhbmd1YWdlU2VsZWN0IH0sCiAgICAgICAgewogICAgICAgICAgICBnZXRDYXRhbG9ncywKICAgICAgICAgICAgcmVuZGVyQ2F0YWxvZ0xpc3QsCiAgICAgICAgICAgIHJlc2V0QXBwCiAgICAgICAgfQogICAgKTsKCiAgICAvLyBJbml0aWFsaXplIGZvcm0gaGFuZGxpbmcKICAgIGluaXRpYWxpemVGb3JtKAogICAgICAgIHsKICAgICAgICAgICAgY29uZmlnRm9ybSwKICAgICAgICAgICAgc3VibWl0QnRuLAogICAgICAgICAgICBlbWFpbElucHV0LAogICAgICAgICAgICBwYXNzd29yZElucHV0LAogICAgICAgICAgICBsYW5ndWFnZVNlbGVjdCwKICAgICAgICAgICAgbW92aWVHZW5yZUxpc3QsCiAgICAgICAgICAgIHNlcmllc0dlbnJlTGlzdAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgICBnZXRDYXRhbG9ncywKICAgICAgICAgICAgcmVzZXRBcHAKICAgICAgICB9CiAgICApOwoKICAgIC8vIEluaXRpYWxpemUgbW9iaWxlIG5hdmlnYXRpb24KICAgIGluaXRpYWxpemVNb2JpbGVOYXYoKTsKCiAgICAvLyBJbml0aWFsaXplIFVJIGNvbXBvbmVudHMKICAgIGluaXRpYWxpemVGb290ZXIoKTsKICAgIGluaXRpYWxpemVLb2ZpKCk7CgogICAgLy8gTGF5b3V0IGFkanVzdG1lbnRzIGZvciBmaXhlZCBtb2JpbGUgaGVhZGVyCiAgICB1cGRhdGVNb2JpbGVMYXlvdXQoKTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCB1cGRhdGVNb2JpbGVMYXlvdXQpOwogICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ29yaWVudGF0aW9uY2hhbmdlJywgdXBkYXRlTW9iaWxlTGF5b3V0KTsKCiAgICAvLyBOZXh0IEJ1dHRvbnMKICAgIGlmIChjb25maWdOZXh0QnRuKSBjb25maWdOZXh0QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4gc3dpdGNoU2VjdGlvbignY2F0YWxvZ3MnKSk7CiAgICBpZiAoY2F0YWxvZ3NOZXh0QnRuKSBjYXRhbG9nc05leHRCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiBzd2l0Y2hTZWN0aW9uKCdpbnN0YWxsJykpOwoKICAgIC8vIFJlc2V0IEJ1dHRvbnMKICAgIGNvbnN0IHJlc2V0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Jlc2V0QnRuJyk7CiAgICBpZiAocmVzZXRCdG4pIHJlc2V0QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcmVzZXRBcHApOwogICAgaWYgKHN1Y2Nlc3NSZXNldEJ0bikgc3VjY2Vzc1Jlc2V0QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcmVzZXRBcHApOwp9KTsKCi8vIE1ha2UgcmVzZXRBcHAgYXZhaWxhYmxlIGdsb2JhbGx5IGZvciBhdXRoIG1vZHVsZQp3aW5kb3cucmVzZXRBcHAgPSByZXNldEFwcDsKd2luZG93LnN3aXRjaFNlY3Rpb24gPSBzd2l0Y2hTZWN0aW9uOwp3aW5kb3cudW5sb2NrTmF2aWdhdGlvbiA9IHVubG9ja05hdmlnYXRpb247Cg==" | base64 --decode > "$REPO/app/static/js/main.js" +echo " wrote app/static/js/main.js" + +mkdir -p "$REPO/app/static/js/modules" +echo "Ly8gRm9ybSBTdWJtaXNzaW9uIGFuZCBVSSBIZWxwZXJzCgppbXBvcnQgeyBzaG93VG9hc3QsIHNob3dDb25maXJtLCBlc2NhcGVIdG1sIH0gZnJvbSAnLi91aS5qcyc7CmltcG9ydCB7IGdldFRyYWt0VG9rZW5zRnJvbVN0b3JhZ2UgfSBmcm9tICcuL3RyYWt0LmpzJzsKaW1wb3J0IHsgc3dpdGNoU2VjdGlvbiB9IGZyb20gJy4vbmF2aWdhdGlvbi5qcyc7CmltcG9ydCB7IE1PVklFX0dFTlJFUywgU0VSSUVTX0dFTlJFUyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7CgovLyBET00gRWxlbWVudHMgLSB3aWxsIGJlIGluaXRpYWxpemVkCmxldCBjb25maWdGb3JtID0gbnVsbDsKbGV0IHN1Ym1pdEJ0biA9IG51bGw7CmxldCBlbWFpbElucHV0ID0gbnVsbDsKbGV0IHBhc3N3b3JkSW5wdXQgPSBudWxsOwpsZXQgbGFuZ3VhZ2VTZWxlY3QgPSBudWxsOwpsZXQgbW92aWVHZW5yZUxpc3QgPSBudWxsOwpsZXQgc2VyaWVzR2VucmVMaXN0ID0gbnVsbDsKbGV0IGdldENhdGFsb2dzID0gbnVsbDsKbGV0IHJlc2V0QXBwID0gbnVsbDsKCmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplRm9ybShkb21FbGVtZW50cywgY2F0YWxvZ1N0YXRlKSB7CiAgICBjb25maWdGb3JtID0gZG9tRWxlbWVudHMuY29uZmlnRm9ybTsKICAgIHN1Ym1pdEJ0biA9IGRvbUVsZW1lbnRzLnN1Ym1pdEJ0bjsKICAgIGVtYWlsSW5wdXQgPSBkb21FbGVtZW50cy5lbWFpbElucHV0OwogICAgcGFzc3dvcmRJbnB1dCA9IGRvbUVsZW1lbnRzLnBhc3N3b3JkSW5wdXQ7CiAgICBsYW5ndWFnZVNlbGVjdCA9IGRvbUVsZW1lbnRzLmxhbmd1YWdlU2VsZWN0OwogICAgbW92aWVHZW5yZUxpc3QgPSBkb21FbGVtZW50cy5tb3ZpZUdlbnJlTGlzdDsKICAgIHNlcmllc0dlbnJlTGlzdCA9IGRvbUVsZW1lbnRzLnNlcmllc0dlbnJlTGlzdDsKICAgIGdldENhdGFsb2dzID0gY2F0YWxvZ1N0YXRlLmdldENhdGFsb2dzOwogICAgcmVzZXRBcHAgPSBjYXRhbG9nU3RhdGUucmVzZXRBcHA7CgogICAgaW5pdGlhbGl6ZUZvcm1TdWJtaXNzaW9uKCk7CiAgICBpbml0aWFsaXplR2VucmVMaXN0cygpOwogICAgaW5pdGlhbGl6ZUxhbmd1YWdlU2VsZWN0KCk7CiAgICBpbml0aWFsaXplUGFzc3dvcmRUb2dnbGVzKCk7CiAgICBpbml0aWFsaXplU3VjY2Vzc0FjdGlvbnMoKTsKICAgIGluaXRpYWxpemVQb3N0ZXJSYXRpbmdQcm92aWRlcigpOwogICAgaW5pdGlhbGl6ZVRtZGIoKTsKICAgIGluaXRpYWxpemVTaW1rbCgpOwogICAgaW5pdGlhbGl6ZUdlbWluaSgpOwogICAgaW5pdGlhbGl6ZVllYXJTbGlkZXIoKTsKfQoKLy8gRm9ybSBTdWJtaXNzaW9uCmFzeW5jIGZ1bmN0aW9uIGluaXRpYWxpemVGb3JtU3VibWlzc2lvbigpIHsKICAgIGlmICghc3VibWl0QnRuKSByZXR1cm47CgogICAgc3VibWl0QnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgYXN5bmMgKGUpID0+IHsKICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7CiAgICAgICAgY2xlYXJFcnJvcnMoKTsKCiAgICAgICAgY29uc3Qgc0F1dGhLZXkgPSAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImF1dGhLZXkiKS52YWx1ZSB8fCAnJykudHJpbSgpOwogICAgICAgIGNvbnN0IGVtYWlsID0gZW1haWxJbnB1dD8udmFsdWUudHJpbSgpOwogICAgICAgIGNvbnN0IHBhc3N3b3JkID0gcGFzc3dvcmRJbnB1dD8udmFsdWU7CiAgICAgICAgY29uc3QgbGFuZ3VhZ2UgPSBsYW5ndWFnZVNlbGVjdC52YWx1ZTsKICAgICAgICBjb25zdCBwb3B1bGFyaXR5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvcHVsYXJpdHlTZWxlY3QiKT8udmFsdWUgfHwgImJhbGFuY2VkIjsKICAgICAgICBjb25zdCB5ZWFyTWluID0gcGFyc2VJbnQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInllYXJNaW4iKT8udmFsdWUgfHwgIjE5ODAiKTsKICAgICAgICBjb25zdCB5ZWFyTWF4ID0gcGFyc2VJbnQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInllYXJNYXgiKT8udmFsdWUgfHwgIjIwMjYiKTsKICAgICAgICBjb25zdCBzb3J0aW5nT3JkZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic29ydGluZ09yZGVyU2VsZWN0Iik/LnZhbHVlIHx8ICJkZWZhdWx0IjsKICAgICAgICBjb25zdCBwb3N0ZXJSYXRpbmdQcm92aWRlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdQcm92aWRlciIpPy52YWx1ZSB8fCAiIjsKICAgICAgICBjb25zdCBwb3N0ZXJSYXRpbmdBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicG9zdGVyUmF0aW5nQXBpS2V5Iik/LnZhbHVlLnRyaW0oKSB8fCAiIjsKICAgICAgICBjb25zdCBleGNsdWRlZE1vdmllR2VucmVzID0gQXJyYXkuZnJvbShkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dFtuYW1lPSJtb3ZpZS1nZW5yZSJdOmNoZWNrZWQnKSkubWFwKGNiID0+IGNiLnZhbHVlKTsKICAgICAgICBjb25zdCBleGNsdWRlZFNlcmllc0dlbnJlcyA9IEFycmF5LmZyb20oZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnaW5wdXRbbmFtZT0ic2VyaWVzLWdlbnJlIl06Y2hlY2tlZCcpKS5tYXAoY2IgPT4gY2IudmFsdWUpOwogICAgICAgIGNvbnN0IHRtZGJBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYkFwaUtleSIpPy52YWx1ZS50cmltKCkgfHwgIiI7CiAgICAgICAgY29uc3Qgc2lta2xBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXkiKT8udmFsdWUudHJpbSgpIHx8ICIiOwogICAgICAgIGNvbnN0IGdlbWluaUFwaUtleSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJnZW1pbmlBcGlLZXkiKT8udmFsdWUudHJpbSgpIHx8ICIiOwoKICAgICAgICBjb25zdCBjYXRhbG9nc1RvU2VuZCA9IFtdOwogICAgICAgIGNvbnN0IGNhdGFsb2dzID0gZ2V0Q2F0YWxvZ3MgPyBnZXRDYXRhbG9ncygpIDogW107CiAgICAgICAgLy8gR2V0IGVuYWJsZWQgc3RhdGUgZnJvbSBjYXRhbG9nIG9iamVjdHMgKHVwZGF0ZWQgYnkgdmlzaWJpbGl0eSBidXR0b24pCiAgICAgICAgY2F0YWxvZ3MuZm9yRWFjaChvcmlnaW5hbENhdGFsb2cgPT4gewogICAgICAgICAgICBjb25zdCBjYXRhbG9nSWQgPSBvcmlnaW5hbENhdGFsb2cuaWQ7CiAgICAgICAgICAgIGNvbnN0IGVuYWJsZWQgPSBvcmlnaW5hbENhdGFsb2cuZW5hYmxlZCAhPT0gZmFsc2U7CgogICAgICAgICAgICAvLyBHZXQgZW5hYmxlZF9tb3ZpZSBhbmQgZW5hYmxlZF9zZXJpZXMgZnJvbSB0b2dnbGUgYnV0dG9ucwogICAgICAgICAgICBjb25zdCBhY3RpdmVCdG4gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGAuY2F0YWxvZy10eXBlLWJ0bltkYXRhLWNhdGFsb2ctaWQ9IiR7Y2F0YWxvZ0lkfSJdLmJnLXdoaXRlYCk7CiAgICAgICAgICAgIGxldCBlbmFibGVkTW92aWUgPSB0cnVlOwogICAgICAgICAgICBsZXQgZW5hYmxlZFNlcmllcyA9IHRydWU7CgogICAgICAgICAgICBpZiAoYWN0aXZlQnRuKSB7CiAgICAgICAgICAgICAgICBjb25zdCBtb2RlID0gYWN0aXZlQnRuLmRhdGFzZXQubW9kZTsKICAgICAgICAgICAgICAgIGlmIChtb2RlID09PSAnbW92aWUnKSB7CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZE1vdmllID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICBlbmFibGVkU2VyaWVzID0gZmFsc2U7CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG1vZGUgPT09ICdzZXJpZXMnKSB7CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZE1vdmllID0gZmFsc2U7CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZFNlcmllcyA9IHRydWU7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIC8vICdib3RoJyBvciBkZWZhdWx0CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZE1vdmllID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICBlbmFibGVkU2VyaWVzID0gdHJ1ZTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIC8vIEZhbGxiYWNrIHRvIGNhdGFsb2cgc3RhdGUKICAgICAgICAgICAgICAgIGVuYWJsZWRNb3ZpZSA9IG9yaWdpbmFsQ2F0YWxvZy5lbmFibGVkTW92aWUgIT09IGZhbHNlOwogICAgICAgICAgICAgICAgZW5hYmxlZFNlcmllcyA9IG9yaWdpbmFsQ2F0YWxvZy5lbmFibGVkU2VyaWVzICE9PSBmYWxzZTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgY2F0YWxvZ3NUb1NlbmQucHVzaCh7CiAgICAgICAgICAgICAgICBpZDogY2F0YWxvZ0lkLAogICAgICAgICAgICAgICAgbmFtZTogb3JpZ2luYWxDYXRhbG9nLm5hbWUsCiAgICAgICAgICAgICAgICBlbmFibGVkOiBlbmFibGVkLAogICAgICAgICAgICAgICAgZW5hYmxlZF9tb3ZpZTogZW5hYmxlZE1vdmllLAogICAgICAgICAgICAgICAgZW5hYmxlZF9zZXJpZXM6IGVuYWJsZWRTZXJpZXMsCiAgICAgICAgICAgICAgICBkaXNwbGF5X2F0X2hvbWU6IG9yaWdpbmFsQ2F0YWxvZy5kaXNwbGF5X2F0X2hvbWUgIT09IGZhbHNlLCAvLyBEZWZhdWx0IHRvIHRydWUgaWYgbm90IHNldAogICAgICAgICAgICAgICAgc2h1ZmZsZTogb3JpZ2luYWxDYXRhbG9nLnNodWZmbGUgPT09IHRydWUsIC8vIERlZmF1bHQgdG8gZmFsc2UgaWYgbm90IHNldAogICAgICAgICAgICB9KTsKICAgICAgICB9KTsKCiAgICAgICAgLy8gRGV0ZXJtaW5lIGFjdGl2ZSBwcm92aWRlcgogICAgICAgIGNvbnN0IHRyYWt0VG9rZW5zID0gZ2V0VHJha3RUb2tlbnNGcm9tU3RvcmFnZSgpOwogICAgICAgIGNvbnN0IGlzVHJha3RMb2dpbiA9ICEhKHRyYWt0VG9rZW5zICYmIHRyYWt0VG9rZW5zLmFjY2Vzc190b2tlbiAmJiAhc0F1dGhLZXkgJiYgIWVtYWlsKTsKCiAgICAgICAgLy8gVmFsaWRhdGlvbgogICAgICAgIGlmICghc0F1dGhLZXkgJiYgIShlbWFpbCAmJiBwYXNzd29yZCkgJiYgIWlzVHJha3RMb2dpbikgewogICAgICAgICAgICBzaG93RXJyb3IoImdlbmVyYWxFcnJvciIsICJQbGVhc2UgbG9naW4gd2l0aCBTdHJlbWlvIG9yIFRyYWt0IHRvIGNvbnRpbnVlLiIpOwogICAgICAgICAgICBzd2l0Y2hTZWN0aW9uKCdsb2dpbicpOwogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQoKICAgICAgICBpZiAoIXRtZGJBcGlLZXkpIHsKICAgICAgICAgICAgc2hvd0Vycm9yKCJnZW5lcmFsRXJyb3IiLCAiVE1EQiBBUEkga2V5IGlzIHJlcXVpcmVkLiIpOwogICAgICAgICAgICBjb25zdCB0bWRiSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYkFwaUtleSIpOwogICAgICAgICAgICBpZiAodG1kYklucHV0KSB7CiAgICAgICAgICAgICAgICB0bWRiSW5wdXQuZm9jdXMoKTsKICAgICAgICAgICAgICAgIHRtZGJJbnB1dC5zY3JvbGxJbnRvVmlldyh7IGJlaGF2aW9yOiAic21vb3RoIiwgYmxvY2s6ICJjZW50ZXIiIH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybjsKICAgICAgICB9CgogICAgICAgIC8vIFZhbGlkYXRlIHBvc3RlciByYXRpbmcgQVBJIGtleSBpZiBwcm92aWRlZAogICAgICAgIGlmIChwb3N0ZXJSYXRpbmdQcm92aWRlciAmJiBwb3N0ZXJSYXRpbmdBcGlLZXkpIHsKICAgICAgICAgICAgaWYgKHdpbmRvdy52YWxpZGF0ZVBvc3RlclJhdGluZ0FwaUtleSkgewogICAgICAgICAgICAgICAgY29uc3QgaXNWYWxpZCA9IGF3YWl0IHdpbmRvdy52YWxpZGF0ZVBvc3RlclJhdGluZ0FwaUtleSgpOwogICAgICAgICAgICAgICAgaWYgKCFpc1ZhbGlkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBzZXRMb2FkaW5nKHRydWUpOwoKICAgICAgICB0cnkgewogICAgICAgICAgICAvLyBCdWlsZCBwb3N0ZXJfcmF0aW5nIHBheWxvYWQKICAgICAgICAgICAgbGV0IHBvc3RlclJhdGluZyA9IG51bGw7CiAgICAgICAgICAgIGlmIChwb3N0ZXJSYXRpbmdQcm92aWRlciAmJiBwb3N0ZXJSYXRpbmdBcGlLZXkpIHsKICAgICAgICAgICAgICAgIHBvc3RlclJhdGluZyA9IHsKICAgICAgICAgICAgICAgICAgICBwcm92aWRlcjogcG9zdGVyUmF0aW5nUHJvdmlkZXIsCiAgICAgICAgICAgICAgICAgICAgYXBpX2tleTogcG9zdGVyUmF0aW5nQXBpS2V5CiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9CgogICAgICAgICAgICBsZXQgZW5kcG9pbnQsIHBheWxvYWQ7CgogICAgICAgICAgICBpZiAoaXNUcmFrdExvZ2luKSB7CiAgICAgICAgICAgICAgICAvLyAtLS0tIFRyYWt0IHN1Ym1pc3Npb24gLS0tLQogICAgICAgICAgICAgICAgZW5kcG9pbnQgPSAiL3Rva2Vucy90cmFrdC8iOwogICAgICAgICAgICAgICAgcGF5bG9hZCA9IHsKICAgICAgICAgICAgICAgICAgICB0cmFrdF9hY2Nlc3NfdG9rZW46IHRyYWt0VG9rZW5zLmFjY2Vzc190b2tlbiwKICAgICAgICAgICAgICAgICAgICB0cmFrdF9yZWZyZXNoX3Rva2VuOiB0cmFrdFRva2Vucy5yZWZyZXNoX3Rva2VuIHx8IHVuZGVmaW5lZCwKICAgICAgICAgICAgICAgICAgICB0cmFrdF9leHBpcmVzX2F0OiB0cmFrdFRva2Vucy5leHBpcmVzX2F0IHx8IHVuZGVmaW5lZCwKICAgICAgICAgICAgICAgICAgICBjYXRhbG9nczogY2F0YWxvZ3NUb1NlbmQsCiAgICAgICAgICAgICAgICAgICAgbGFuZ3VhZ2U6IGxhbmd1YWdlLAogICAgICAgICAgICAgICAgICAgIHllYXJfbWluOiB5ZWFyTWluLAogICAgICAgICAgICAgICAgICAgIHllYXJfbWF4OiB5ZWFyTWF4LAogICAgICAgICAgICAgICAgICAgIHBvcHVsYXJpdHk6IHBvcHVsYXJpdHksCiAgICAgICAgICAgICAgICAgICAgc29ydGluZ19vcmRlcjogc29ydGluZ09yZGVyLAogICAgICAgICAgICAgICAgICAgIHBvc3Rlcl9yYXRpbmc6IHBvc3RlclJhdGluZywKICAgICAgICAgICAgICAgICAgICB0bWRiX2FwaV9rZXk6IHRtZGJBcGlLZXkgfHwgdW5kZWZpbmVkLAogICAgICAgICAgICAgICAgICAgIHNpbWtsX2FwaV9rZXk6IHNpbWtsQXBpS2V5LAogICAgICAgICAgICAgICAgICAgIGdlbWluaV9hcGlfa2V5OiBnZW1pbmlBcGlLZXksCiAgICAgICAgICAgICAgICAgICAgZXhjbHVkZWRfbW92aWVfZ2VucmVzOiBleGNsdWRlZE1vdmllR2VucmVzLAogICAgICAgICAgICAgICAgICAgIGV4Y2x1ZGVkX3Nlcmllc19nZW5yZXM6IGV4Y2x1ZGVkU2VyaWVzR2VucmVzCiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgLy8gLS0tLSBTdHJlbWlvIHN1Ym1pc3Npb24gLS0tLQogICAgICAgICAgICAgICAgZW5kcG9pbnQgPSAiL3Rva2Vucy8iOwogICAgICAgICAgICAgICAgcGF5bG9hZCA9IHsKICAgICAgICAgICAgICAgICAgICBhdXRoS2V5OiBzQXV0aEtleSB8fCB1bmRlZmluZWQsCiAgICAgICAgICAgICAgICAgICAgZW1haWw6IGVtYWlsIHx8IHVuZGVmaW5lZCwKICAgICAgICAgICAgICAgICAgICBwYXNzd29yZDogcGFzc3dvcmQgfHwgdW5kZWZpbmVkLAogICAgICAgICAgICAgICAgICAgIGNhdGFsb2dzOiBjYXRhbG9nc1RvU2VuZCwKICAgICAgICAgICAgICAgICAgICBsYW5ndWFnZTogbGFuZ3VhZ2UsCiAgICAgICAgICAgICAgICAgICAgeWVhcl9taW46IHllYXJNaW4sCiAgICAgICAgICAgICAgICAgICAgeWVhcl9tYXg6IHllYXJNYXgsCiAgICAgICAgICAgICAgICAgICAgcG9wdWxhcml0eTogcG9wdWxhcml0eSwKICAgICAgICAgICAgICAgICAgICBzb3J0aW5nX29yZGVyOiBzb3J0aW5nT3JkZXIsCiAgICAgICAgICAgICAgICAgICAgcG9zdGVyX3JhdGluZzogcG9zdGVyUmF0aW5nLAogICAgICAgICAgICAgICAgICAgIHRtZGJfYXBpX2tleTogdG1kYkFwaUtleSB8fCB1bmRlZmluZWQsCiAgICAgICAgICAgICAgICAgICAgc2lta2xfYXBpX2tleTogc2lta2xBcGlLZXksCiAgICAgICAgICAgICAgICAgICAgZ2VtaW5pX2FwaV9rZXk6IGdlbWluaUFwaUtleSwKICAgICAgICAgICAgICAgICAgICBleGNsdWRlZF9tb3ZpZV9nZW5yZXM6IGV4Y2x1ZGVkTW92aWVHZW5yZXMsCiAgICAgICAgICAgICAgICAgICAgZXhjbHVkZWRfc2VyaWVzX2dlbnJlczogZXhjbHVkZWRTZXJpZXNHZW5yZXMKICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goZW5kcG9pbnQsIHsKICAgICAgICAgICAgICAgIG1ldGhvZDogIlBPU1QiLAogICAgICAgICAgICAgICAgaGVhZGVyczogeyAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iIH0sCiAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKQogICAgICAgICAgICB9KTsKCiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHsKICAgICAgICAgICAgICAgIGNvbnN0IGVycm9yRGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihlcnJvckRhdGEuZGV0YWlsIHx8ICJGYWlsZWQgdG8gZ2VuZXJhdGUgbWFuaWZlc3QgVVJMIik7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTsKICAgICAgICAgICAgc2hvd1N1Y2Nlc3MoZGF0YS5tYW5pZmVzdFVybCk7CiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgY29uc29sZS5lcnJvcigiRXJyb3I6IiwgZXJyb3IpOwogICAgICAgICAgICBzaG93RXJyb3IoImdlbmVyYWxFcnJvciIsIGVycm9yLm1lc3NhZ2UpOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHNldExvYWRpbmcoZmFsc2UpOwogICAgICAgIH0KICAgIH0pOwp9CgovLyBVSSBIZWxwZXJzICYgR2VucmUgTGlzdHMKZnVuY3Rpb24gaW5pdGlhbGl6ZUdlbnJlTGlzdHMoKSB7CiAgICByZW5kZXJHZW5yZUxpc3QobW92aWVHZW5yZUxpc3QsIE1PVklFX0dFTlJFUywgJ21vdmllLWdlbnJlJyk7CiAgICByZW5kZXJHZW5yZUxpc3Qoc2VyaWVzR2VucmVMaXN0LCBTRVJJRVNfR0VOUkVTLCAnc2VyaWVzLWdlbnJlJyk7Cn0KCmZ1bmN0aW9uIHJlbmRlckdlbnJlTGlzdChjb250YWluZXIsIGdlbnJlcywgbmFtZVByZWZpeCkgewogICAgaWYgKCFjb250YWluZXIpIHJldHVybjsKICAgIGNvbnRhaW5lci5pbm5lckhUTUwgPSBnZW5yZXMubWFwKGdlbnJlID0+IGAKICAgICAgICA8bGFiZWwgY2xhc3M9ImZsZXggaXRlbXMtY2VudGVyIGdhcC0zIHAtMiByb3VuZGVkLWxnIGhvdmVyOmJnLXdoaXRlLzUgY3Vyc29yLXBvaW50ZXIgdHJhbnNpdGlvbiBncm91cCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9InJlbGF0aXZlIGZsZXggaXRlbXMtY2VudGVyIj4KICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJjaGVja2JveCIgbmFtZT0iJHtuYW1lUHJlZml4fSIgdmFsdWU9IiR7Z2VucmUuaWR9IgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJwZWVyIGFwcGVhcmFuY2Utbm9uZSB3LTUgaC01IGJvcmRlci0yIGJvcmRlci1zbGF0ZS02MDAgcm91bmRlZCBiZy1uZXV0cmFsLTkwMCBjaGVja2VkOmJnLXdoaXRlIGNoZWNrZWQ6Ym9yZGVyLXdoaXRlIHRyYW5zaXRpb24tY29sb3JzIj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9ImFic29sdXRlIHctMy41IGgtMy41IHRleHQtYmxhY2sgbGVmdC0xLzIgdG9wLTEvMiAtdHJhbnNsYXRlLXgtMS8yIC10cmFuc2xhdGUteS0xLzIgb3BhY2l0eS0wIHBlZXItY2hlY2tlZDpvcGFjaXR5LTEwMCBwb2ludGVyLWV2ZW50cy1ub25lIHRyYW5zaXRpb24tb3BhY2l0eSIKICAgICAgICAgICAgICAgICAgICBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgZD0iTTUgMTNsNCA0TDE5IDciPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvc3ZnPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPHNwYW4gY2xhc3M9InRleHQtc20gdGV4dC1zbGF0ZS0zMDAgZ3JvdXAtaG92ZXI6dGV4dC13aGl0ZSB0cmFuc2l0aW9uLWNvbG9ycyBzZWxlY3Qtbm9uZSI+JHtnZW5yZS5uYW1lfTwvc3Bhbj4KICAgICAgICA8L2xhYmVsPgogICAgYCkuam9pbignJyk7Cn0KCmZ1bmN0aW9uIGluaXRpYWxpemVMYW5ndWFnZVNlbGVjdCgpIHsKICAgIGlmICghbGFuZ3VhZ2VTZWxlY3QpIHJldHVybjsKfQoKLy8gUG9zdGVyIFJhdGluZyBQcm92aWRlcgpmdW5jdGlvbiBpbml0aWFsaXplUG9zdGVyUmF0aW5nUHJvdmlkZXIoKSB7CiAgICBjb25zdCBwcm92aWRlclNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdQcm92aWRlciIpOwogICAgY29uc3QgYXBpS2V5Q29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0FwaUtleUNvbnRhaW5lciIpOwogICAgY29uc3QgYXBpS2V5SW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicG9zdGVyUmF0aW5nQXBpS2V5Iik7CiAgICBjb25zdCBoZWxwQ29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0hlbHAiKTsKICAgIGNvbnN0IGhlbHBUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0hlbHBUZXh0Iik7CiAgICBjb25zdCB2YWxpZGF0ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdBcGlLZXlWYWxpZGF0ZSIpOwogICAgY29uc3QgdG9nZ2xlQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0FwaUtleVRvZ2dsZSIpOwogICAgY29uc3QgZXllSWNvbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdBcGlLZXlFeWUiKTsKICAgIGNvbnN0IGV5ZU9mZkljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicG9zdGVyUmF0aW5nQXBpS2V5RXllT2ZmIik7CiAgICBjb25zdCB2YWxpZGF0aW9uTWVzc2FnZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdWYWxpZGF0aW9uTWVzc2FnZSIpOwoKICAgIGlmICghcHJvdmlkZXJTZWxlY3QgfHwgIWFwaUtleUNvbnRhaW5lciB8fCAhYXBpS2V5SW5wdXQgfHwgIWhlbHBDb250YWluZXIgfHwgIWhlbHBUZXh0KSByZXR1cm47CgogICAgY29uc3QgcHJvdmlkZXJJbmZvID0gewogICAgICAgICJycGRiIjogewogICAgICAgICAgICBuYW1lOiAiUlBEQiAoUmF0aW5nUG9zdGVyREIpIiwKICAgICAgICAgICAgdXJsOiAiaHR0cHM6Ly9yYXRpbmdwb3N0ZXJkYi5jb20iLAogICAgICAgICAgICBkZXNjcmlwdGlvbjogIkVuYWJsZSByYXRpbmdzIG9uIHBvc3RlcnMgdmlhIFJhdGluZ1Bvc3RlckRCIgogICAgICAgIH0sCiAgICAgICAgInRvcF9wb3N0ZXJzIjogewogICAgICAgICAgICBuYW1lOiAiVG9wIFBvc3RlcnMiLAogICAgICAgICAgICB1cmw6ICJodHRwczovL2FwaS50b3Atc3RyZWFtaW5nLnN0cmVhbS8iLAogICAgICAgICAgICBkZXNjcmlwdGlvbjogIkVuYWJsZSByYXRpbmdzIG9uIHBvc3RlcnMgdmlhIFRvcCBQb3N0ZXJzIgogICAgICAgIH0KICAgIH07CgogICAgbGV0IGlzVmFsaWRhdGVkID0gZmFsc2U7CgogICAgLy8gRXllIHRvZ2dsZSBmdW5jdGlvbmFsaXR5CiAgICBpZiAodG9nZ2xlQnRuICYmIGV5ZUljb24gJiYgZXllT2ZmSWNvbikgewogICAgICAgIHRvZ2dsZUJ0bi5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsICgpID0+IHsKICAgICAgICAgICAgY29uc3QgaXNQYXNzd29yZCA9IGFwaUtleUlucHV0LnR5cGUgPT09ICJwYXNzd29yZCI7CiAgICAgICAgICAgIGFwaUtleUlucHV0LnR5cGUgPSBpc1Bhc3N3b3JkID8gInRleHQiIDogInBhc3N3b3JkIjsKICAgICAgICAgICAgZXllSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCAhaXNQYXNzd29yZCk7CiAgICAgICAgICAgIGV5ZU9mZkljb24uY2xhc3NMaXN0LnRvZ2dsZSgiaGlkZGVuIiwgaXNQYXNzd29yZCk7CiAgICAgICAgfSk7CiAgICB9CgogICAgLy8gVmFsaWRhdGlvbiBmdW5jdGlvbgogICAgYXN5bmMgZnVuY3Rpb24gdmFsaWRhdGVBcGlLZXkoKSB7CiAgICAgICAgY29uc3Qgc2VsZWN0ZWRQcm92aWRlciA9IHByb3ZpZGVyU2VsZWN0LnZhbHVlOwogICAgICAgIGNvbnN0IGFwaUtleSA9IGFwaUtleUlucHV0LnZhbHVlLnRyaW0oKTsKCiAgICAgICAgaWYgKCFzZWxlY3RlZFByb3ZpZGVyIHx8ICFhcGlLZXkpIHsKICAgICAgICAgICAgc2hvd1ZhbGlkYXRpb25NZXNzYWdlKCJQbGVhc2Ugc2VsZWN0IGEgcHJvdmlkZXIgYW5kIGVudGVyIGFuIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgaWYgKCF2YWxpZGF0ZUJ0bikgcmV0dXJuIGZhbHNlOwoKICAgICAgICAvLyBTaG93IGxvYWRpbmcgc3RhdGUKICAgICAgICB2YWxpZGF0ZUJ0bi5kaXNhYmxlZCA9IHRydWU7CiAgICAgICAgdmFsaWRhdGVCdG4uY2xhc3NMaXN0LmFkZCgib3BhY2l0eS01MCIsICJjdXJzb3Itbm90LWFsbG93ZWQiKTsKICAgICAgICBjb25zdCBvcmlnaW5hbEhUTUwgPSB2YWxpZGF0ZUJ0bi5pbm5lckhUTUw7CiAgICAgICAgdmFsaWRhdGVCdG4uaW5uZXJIVE1MID0gJzxzdmcgY2xhc3M9InctNSBoLTUgYW5pbWF0ZS1zcGluIiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGNsYXNzPSJvcGFjaXR5LTI1IiBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjQiPjwvY2lyY2xlPjxwYXRoIGNsYXNzPSJvcGFjaXR5LTc1IiBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik00IDEyYTggOCAwIDAxOC04VjBDNS4zNzMgMCAwIDUuMzczIDAgMTJoNHptMiA1LjI5MUE3Ljk2MiA3Ljk2MiAwIDAxNCAxMkgwYzAgMy4wNDIgMS4xMzUgNS44MjQgMyA3LjkzOGwzLTIuNjQ3eiI+PC9wYXRoPjwvc3ZnPic7CgogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goIi9wb3N0ZXItcmF0aW5nL3ZhbGlkYXRlIiwgewogICAgICAgICAgICAgICAgbWV0aG9kOiAiUE9TVCIsCiAgICAgICAgICAgICAgICBoZWFkZXJzOiB7ICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIgfSwKICAgICAgICAgICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgcHJvdmlkZXI6IHNlbGVjdGVkUHJvdmlkZXIsIGFwaV9rZXk6IGFwaUtleSB9KQogICAgICAgICAgICB9KTsKCiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7CgogICAgICAgICAgICBpZiAoZGF0YS52YWxpZCkgewogICAgICAgICAgICAgICAgc2hvd1ZhbGlkYXRpb25NZXNzYWdlKCJBUEkga2V5IGlzIHZhbGlkIOKckyIsICJzdWNjZXNzIik7CiAgICAgICAgICAgICAgICBpc1ZhbGlkYXRlZCA9IHRydWU7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIHNob3dWYWxpZGF0aW9uTWVzc2FnZShkYXRhLm1lc3NhZ2UgfHwgIkludmFsaWQgQVBJIGtleSIsICJlcnJvciIpOwogICAgICAgICAgICAgICAgYXBpS2V5SW5wdXQudmFsdWUgPSAiIjsgLy8gQ2xlYXIgaW52YWxpZCBrZXkKICAgICAgICAgICAgICAgIGlzVmFsaWRhdGVkID0gZmFsc2U7CiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgIH0KICAgICAgICB9IGNhdGNoIChlcnJvcikgewogICAgICAgICAgICBzaG93VmFsaWRhdGlvbk1lc3NhZ2UoIlZhbGlkYXRpb24gZmFpbGVkLiBQbGVhc2UgdHJ5IGFnYWluLiIsICJlcnJvciIpOwogICAgICAgICAgICBpc1ZhbGlkYXRlZCA9IGZhbHNlOwogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSBmYWxzZTsKICAgICAgICAgICAgdmFsaWRhdGVCdG4uY2xhc3NMaXN0LnJlbW92ZSgib3BhY2l0eS01MCIsICJjdXJzb3Itbm90LWFsbG93ZWQiKTsKICAgICAgICAgICAgdmFsaWRhdGVCdG4uaW5uZXJIVE1MID0gb3JpZ2luYWxIVE1MOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBTaG93IHZhbGlkYXRpb24gbWVzc2FnZQogICAgZnVuY3Rpb24gc2hvd1ZhbGlkYXRpb25NZXNzYWdlKG1lc3NhZ2UsIHR5cGUpIHsKICAgICAgICBpZiAoIXZhbGlkYXRpb25NZXNzYWdlKSByZXR1cm47CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UudGV4dENvbnRlbnQgPSBtZXNzYWdlOwogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTmFtZSA9IGBtdC0yIHRleHQteHMgJHt0eXBlID09PSAic3VjY2VzcyIgPyAidGV4dC1ncmVlbi00MDAiIDogInRleHQtcmVkLTQwMCJ9YDsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsKICAgIH0KCiAgICAvLyBDbGVhciB2YWxpZGF0aW9uIG1lc3NhZ2UKICAgIGZ1bmN0aW9uIGNsZWFyVmFsaWRhdGlvbk1lc3NhZ2UoKSB7CiAgICAgICAgaWYgKHZhbGlkYXRpb25NZXNzYWdlKSB7CiAgICAgICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBWYWxpZGF0ZSBidXR0b24gY2xpY2sKICAgIGlmICh2YWxpZGF0ZUJ0bikgewogICAgICAgIHZhbGlkYXRlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgdmFsaWRhdGVBcGlLZXkpOwogICAgfQoKICAgIC8vIENsZWFyIHZhbGlkYXRpb24gd2hlbiBBUEkga2V5IGNoYW5nZXMKICAgIGFwaUtleUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoImlucHV0IiwgKCkgPT4gewogICAgICAgIGlzVmFsaWRhdGVkID0gZmFsc2U7CiAgICAgICAgY2xlYXJWYWxpZGF0aW9uTWVzc2FnZSgpOwogICAgfSk7CgogICAgZnVuY3Rpb24gdXBkYXRlVUkoKSB7CiAgICAgICAgY29uc3Qgc2VsZWN0ZWRQcm92aWRlciA9IHByb3ZpZGVyU2VsZWN0LnZhbHVlOwoKICAgICAgICBpZiAoc2VsZWN0ZWRQcm92aWRlciAmJiBwcm92aWRlckluZm9bc2VsZWN0ZWRQcm92aWRlcl0pIHsKICAgICAgICAgICAgY29uc3QgaW5mbyA9IHByb3ZpZGVySW5mb1tzZWxlY3RlZFByb3ZpZGVyXTsKICAgICAgICAgICAgYXBpS2V5Q29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAiYmxvY2siOwogICAgICAgICAgICBoZWxwQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAiYmxvY2siOwogICAgICAgICAgICBoZWxwVGV4dC5pbm5lckhUTUwgPSBgJHtpbmZvLmRlc2NyaXB0aW9ufS4gR2V0IHlvdXIgQVBJIGtleSBmcm9tIDxhIGhyZWY9IiR7aW5mby51cmx9IiB0YXJnZXQ9Il9ibGFuayIgY2xhc3M9InRleHQtc2xhdGUtMzAwIGhvdmVyOnRleHQtd2hpdGUgdW5kZXJsaW5lIj4ke2luZm8ubmFtZX08L2E+LmA7CiAgICAgICAgICAgIC8vIERvbid0IGNsZWFyIHRoZSBBUEkga2V5IHdoZW4gc3dpdGNoaW5nIHByb3ZpZGVycyAtIGp1c3QgcmVzZXQgdmFsaWRhdGlvbgogICAgICAgICAgICBpc1ZhbGlkYXRlZCA9IGZhbHNlOwogICAgICAgICAgICBjbGVhclZhbGlkYXRpb25NZXNzYWdlKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgLy8gT25seSBjbGVhciB3aGVuIHByb3ZpZGVyIGlzIHNldCB0byAiTm9uZSIKICAgICAgICAgICAgYXBpS2V5Q29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAibm9uZSI7CiAgICAgICAgICAgIGhlbHBDb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICJub25lIjsKICAgICAgICAgICAgYXBpS2V5SW5wdXQudmFsdWUgPSAiIjsKICAgICAgICAgICAgaXNWYWxpZGF0ZWQgPSBmYWxzZTsKICAgICAgICAgICAgY2xlYXJWYWxpZGF0aW9uTWVzc2FnZSgpOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBIYW5kbGUgcHJvdmlkZXIgY2hhbmdlIC0gcHJlc2VydmUgQVBJIGtleSB2YWx1ZSwganVzdCByZXNldCB2YWxpZGF0aW9uCiAgICBwcm92aWRlclNlbGVjdC5hZGRFdmVudExpc3RlbmVyKCJjaGFuZ2UiLCAoKSA9PiB7CiAgICAgICAgaXNWYWxpZGF0ZWQgPSBmYWxzZTsKICAgICAgICBjbGVhclZhbGlkYXRpb25NZXNzYWdlKCk7CiAgICAgICAgdXBkYXRlVUkoKTsKICAgIH0pOwoKICAgIHVwZGF0ZVVJKCk7IC8vIEluaXRpYWxpemUgb24gbG9hZAoKICAgIC8vIEV4cG9ydCB2YWxpZGF0ZSBmdW5jdGlvbiBmb3IgZm9ybSBzdWJtaXNzaW9uCiAgICB3aW5kb3cudmFsaWRhdGVQb3N0ZXJSYXRpbmdBcGlLZXkgPSB2YWxpZGF0ZUFwaUtleTsKfQoKLy8gVE1EQiBBUEkgS2V5IChSZXF1aXJlZCkKZnVuY3Rpb24gaW5pdGlhbGl6ZVRtZGIoKSB7CiAgICBjb25zdCBhcGlLZXlJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0bWRiQXBpS2V5Iik7CiAgICBjb25zdCB2YWxpZGF0ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0bWRiQXBpS2V5VmFsaWRhdGUiKTsKICAgIGNvbnN0IHRvZ2dsZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0bWRiQXBpS2V5VG9nZ2xlIik7CiAgICBjb25zdCBleWVJY29uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInRtZGJBcGlLZXlFeWUiKTsKICAgIGNvbnN0IGV5ZU9mZkljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYkFwaUtleUV5ZU9mZiIpOwogICAgY29uc3QgdmFsaWRhdGlvbk1lc3NhZ2UgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYlZhbGlkYXRpb25NZXNzYWdlIik7CgogICAgaWYgKCFhcGlLZXlJbnB1dCB8fCAhdmFsaWRhdGlvbk1lc3NhZ2UpIHJldHVybjsKCiAgICBpZiAodG9nZ2xlQnRuICYmIGV5ZUljb24gJiYgZXllT2ZmSWNvbikgewogICAgICAgIHRvZ2dsZUJ0bi5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsICgpID0+IHsKICAgICAgICAgICAgY29uc3QgaXNQYXNzd29yZCA9IGFwaUtleUlucHV0LnR5cGUgPT09ICJwYXNzd29yZCI7CiAgICAgICAgICAgIGFwaUtleUlucHV0LnR5cGUgPSBpc1Bhc3N3b3JkID8gInRleHQiIDogInBhc3N3b3JkIjsKICAgICAgICAgICAgZXllSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCAhaXNQYXNzd29yZCk7CiAgICAgICAgICAgIGV5ZU9mZkljb24uY2xhc3NMaXN0LnRvZ2dsZSgiaGlkZGVuIiwgaXNQYXNzd29yZCk7CiAgICAgICAgfSk7CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd1RtZGJWYWxpZGF0aW9uTWVzc2FnZShtZXNzYWdlLCB0eXBlKSB7CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UudGV4dENvbnRlbnQgPSBtZXNzYWdlOwogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTmFtZSA9IGBtdC0yIHRleHQteHMgJHt0eXBlID09PSAic3VjY2VzcyIgPyAidGV4dC1ncmVlbi00MDAiIDogInRleHQtcmVkLTQwMCJ9YDsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsKICAgIH0KCiAgICBpZiAodmFsaWRhdGVCdG4pIHsKICAgICAgICB2YWxpZGF0ZUJ0bi5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGFzeW5jICgpID0+IHsKICAgICAgICAgICAgY29uc3QgYXBpS2V5ID0gYXBpS2V5SW5wdXQudmFsdWUudHJpbSgpOwogICAgICAgICAgICBpZiAoIWFwaUtleSkgewogICAgICAgICAgICAgICAgc2hvd1RtZGJWYWxpZGF0aW9uTWVzc2FnZSgiUGxlYXNlIGVudGVyIGEgVE1EQiBBUEkga2V5IiwgImVycm9yIik7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgICAgICB2YWxpZGF0ZUJ0bi5jbGFzc0xpc3QuYWRkKCJvcGFjaXR5LTUwIiwgImN1cnNvci1ub3QtYWxsb3dlZCIpOwogICAgICAgICAgICBjb25zdCBvcmlnaW5hbEhUTUwgPSB2YWxpZGF0ZUJ0bi5pbm5lckhUTUw7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPSJ3LTUgaC01IGFuaW1hdGUtc3BpbiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjbGFzcz0ib3BhY2l0eS0yNSIgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT48cGF0aCBjbGFzcz0ib3BhY2l0eS03NSIgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD48L3N2Zz4nOwogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgiL3RtZGIvdmFsaWRhdGlvbiIsIHsKICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICJQT1NUIiwKICAgICAgICAgICAgICAgICAgICBoZWFkZXJzOiB7ICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIgfSwKICAgICAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGFwaV9rZXk6IGFwaUtleSB9KQogICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpOwogICAgICAgICAgICAgICAgaWYgKGRhdGEudmFsaWQpIHsKICAgICAgICAgICAgICAgICAgICBzaG93VG1kYlZhbGlkYXRpb25NZXNzYWdlKCJUTURCIEFQSSBrZXkgaXMgdmFsaWQg4pyTIiwgInN1Y2Nlc3MiKTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgc2hvd1RtZGJWYWxpZGF0aW9uTWVzc2FnZShkYXRhLm1lc3NhZ2UgfHwgIkludmFsaWQgVE1EQiBBUEkga2V5IiwgImVycm9yIik7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICAgICAgICBzaG93VG1kYlZhbGlkYXRpb25NZXNzYWdlKCJWYWxpZGF0aW9uIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCAiZXJyb3IiKTsKICAgICAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgICAgIHZhbGlkYXRlQnRuLmRpc2FibGVkID0gZmFsc2U7CiAgICAgICAgICAgICAgICB2YWxpZGF0ZUJ0bi5jbGFzc0xpc3QucmVtb3ZlKCJvcGFjaXR5LTUwIiwgImN1cnNvci1ub3QtYWxsb3dlZCIpOwogICAgICAgICAgICAgICAgdmFsaWRhdGVCdG4uaW5uZXJIVE1MID0gb3JpZ2luYWxIVE1MOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9CgogICAgYXBpS2V5SW5wdXQuYWRkRXZlbnRMaXN0ZW5lcigiaW5wdXQiLCAoKSA9PiB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QuYWRkKCJoaWRkZW4iKSk7Cn0KCi8vIFNpbWtsIEludGVncmF0aW9uCmZ1bmN0aW9uIGluaXRpYWxpemVTaW1rbCgpIHsKICAgIGNvbnN0IGFwaUtleUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNpbWtsQXBpS2V5Iik7CiAgICBjb25zdCB2YWxpZGF0ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJzaW1rbEFwaUtleVZhbGlkYXRlIik7CiAgICBjb25zdCB0b2dnbGVCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXlUb2dnbGUiKTsKICAgIGNvbnN0IGV5ZUljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXlFeWUiKTsKICAgIGNvbnN0IGV5ZU9mZkljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXlFeWVPZmYiKTsKICAgIGNvbnN0IHZhbGlkYXRpb25NZXNzYWdlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNpbWtsVmFsaWRhdGlvbk1lc3NhZ2UiKTsKCiAgICBpZiAoIWFwaUtleUlucHV0IHx8ICF2YWxpZGF0ZUJ0biB8fCAhdmFsaWRhdGlvbk1lc3NhZ2UpIHJldHVybjsKCiAgICAvLyBFeWUgdG9nZ2xlIGZ1bmN0aW9uYWxpdHkKICAgIGlmICh0b2dnbGVCdG4gJiYgZXllSWNvbiAmJiBleWVPZmZJY29uKSB7CiAgICAgICAgdG9nZ2xlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgKCkgPT4gewogICAgICAgICAgICBjb25zdCBpc1Bhc3N3b3JkID0gYXBpS2V5SW5wdXQudHlwZSA9PT0gInBhc3N3b3JkIjsKICAgICAgICAgICAgYXBpS2V5SW5wdXQudHlwZSA9IGlzUGFzc3dvcmQgPyAidGV4dCIgOiAicGFzc3dvcmQiOwogICAgICAgICAgICBleWVJY29uLmNsYXNzTGlzdC50b2dnbGUoImhpZGRlbiIsICFpc1Bhc3N3b3JkKTsKICAgICAgICAgICAgZXllT2ZmSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCBpc1Bhc3N3b3JkKTsKICAgICAgICB9KTsKICAgIH0KCiAgICAvLyBWYWxpZGF0aW9uIGZ1bmN0aW9uCiAgICBhc3luYyBmdW5jdGlvbiB2YWxpZGF0ZVNpbWtsS2V5KCkgewogICAgICAgIGNvbnN0IGFwaUtleSA9IGFwaUtleUlucHV0LnZhbHVlLnRyaW0oKTsKCiAgICAgICAgaWYgKCFhcGlLZXkpIHsKICAgICAgICAgICAgc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UoIlBsZWFzZSBlbnRlciBhIFNpbWtsIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgLy8gU2hvdyBsb2FkaW5nIHN0YXRlCiAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5hZGQoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgY29uc3Qgb3JpZ2luYWxIVE1MID0gdmFsaWRhdGVCdG4uaW5uZXJIVE1MOwogICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPSJ3LTUgaC01IGFuaW1hdGUtc3BpbiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjbGFzcz0ib3BhY2l0eS0yNSIgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT48cGF0aCBjbGFzcz0ib3BhY2l0eS03NSIgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD48L3N2Zz4nOwoKICAgICAgICB0cnkgewogICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKCIvc2lta2wvdmFsaWRhdGlvbiIsIHsKICAgICAgICAgICAgICAgIG1ldGhvZDogIlBPU1QiLAogICAgICAgICAgICAgICAgaGVhZGVyczogeyAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iIH0sCiAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGFwaV9rZXk6IGFwaUtleSB9KQogICAgICAgICAgICB9KTsKCiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7CgogICAgICAgICAgICBpZiAoZGF0YS52YWxpZCkgewogICAgICAgICAgICAgICAgc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UoIlNpbWtsIEFQSSBrZXkgaXMgdmFsaWQg4pyTIiwgInN1Y2Nlc3MiKTsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UoZGF0YS5tZXNzYWdlIHx8ICJJbnZhbGlkIFNpbWtsIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgICAgfQogICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICAgIHNob3dTaW1rbFZhbGlkYXRpb25NZXNzYWdlKCJWYWxpZGF0aW9uIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmRpc2FibGVkID0gZmFsc2U7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5yZW1vdmUoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9IG9yaWdpbmFsSFRNTDsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UobWVzc2FnZSwgdHlwZSkgewogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLnRleHRDb250ZW50ID0gbWVzc2FnZTsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc05hbWUgPSBgbXQtMiB0ZXh0LXhzICR7dHlwZSA9PT0gInN1Y2Nlc3MiID8gInRleHQtZ3JlZW4tNDAwIiA6ICJ0ZXh0LXJlZC00MDAifWA7CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7CiAgICB9CgogICAgdmFsaWRhdGVCdG4uYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCB2YWxpZGF0ZVNpbWtsS2V5KTsKCiAgICBhcGlLZXlJbnB1dC5hZGRFdmVudExpc3RlbmVyKCJpbnB1dCIsICgpID0+IHsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QuYWRkKCJoaWRkZW4iKTsKICAgIH0pOwp9CgovLyBHZW1pbmkgQUkgSW50ZWdyYXRpb24KZnVuY3Rpb24gaW5pdGlhbGl6ZUdlbWluaSgpIHsKICAgIGNvbnN0IGFwaUtleUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImdlbWluaUFwaUtleSIpOwogICAgY29uc3QgdmFsaWRhdGVCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiZ2VtaW5pQXBpS2V5VmFsaWRhdGUiKTsKICAgIGNvbnN0IHRvZ2dsZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJnZW1pbmlBcGlLZXlUb2dnbGUiKTsKICAgIGNvbnN0IGV5ZUljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiZ2VtaW5pQXBpS2V5RXllIik7CiAgICBjb25zdCBleWVPZmZJY29uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImdlbWluaUFwaUtleUV5ZU9mZiIpOwogICAgY29uc3QgdmFsaWRhdGlvbk1lc3NhZ2UgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiZ2VtaW5pVmFsaWRhdGlvbk1lc3NhZ2UiKTsKCiAgICBpZiAoIWFwaUtleUlucHV0IHx8ICF2YWxpZGF0ZUJ0biB8fCAhdmFsaWRhdGlvbk1lc3NhZ2UpIHJldHVybjsKCiAgICAvLyBFeWUgdG9nZ2xlIGZ1bmN0aW9uYWxpdHkKICAgIGlmICh0b2dnbGVCdG4gJiYgZXllSWNvbiAmJiBleWVPZmZJY29uKSB7CiAgICAgICAgdG9nZ2xlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgKCkgPT4gewogICAgICAgICAgICBjb25zdCBpc1Bhc3N3b3JkID0gYXBpS2V5SW5wdXQudHlwZSA9PT0gInBhc3N3b3JkIjsKICAgICAgICAgICAgYXBpS2V5SW5wdXQudHlwZSA9IGlzUGFzc3dvcmQgPyAidGV4dCIgOiAicGFzc3dvcmQiOwogICAgICAgICAgICBleWVJY29uLmNsYXNzTGlzdC50b2dnbGUoImhpZGRlbiIsICFpc1Bhc3N3b3JkKTsKICAgICAgICAgICAgZXllT2ZmSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCBpc1Bhc3N3b3JkKTsKICAgICAgICB9KTsKICAgIH0KCiAgICAvLyBWYWxpZGF0aW9uIGZ1bmN0aW9uCiAgICBhc3luYyBmdW5jdGlvbiB2YWxpZGF0ZUdlbWluaUtleSgpIHsKICAgICAgICBjb25zdCBhcGlLZXkgPSBhcGlLZXlJbnB1dC52YWx1ZS50cmltKCk7CgogICAgICAgIGlmICghYXBpS2V5KSB7CiAgICAgICAgICAgIHNob3dHZW1pbmlWYWxpZGF0aW9uTWVzc2FnZSgiUGxlYXNlIGVudGVyIGEgR2VtaW5pIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgLy8gU2hvdyBsb2FkaW5nIHN0YXRlCiAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5hZGQoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgY29uc3Qgb3JpZ2luYWxIVE1MID0gdmFsaWRhdGVCdG4uaW5uZXJIVE1MOwogICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPSJ3LTUgaC01IGFuaW1hdGUtc3BpbiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjbGFzcz0ib3BhY2l0eS0yNSIgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT48cGF0aCBjbGFzcz0ib3BhY2l0eS03NSIgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD48L3N2Zz4nOwoKICAgICAgICB0cnkgewogICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKCIvZ2VtaW5pL3ZhbGlkYXRpb24iLCB7CiAgICAgICAgICAgICAgICBtZXRob2Q6ICJQT1NUIiwKICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgIkNvbnRlbnQtVHlwZSI6ICJhcHBsaWNhdGlvbi9qc29uIiB9LAogICAgICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBhcGlfa2V5OiBhcGlLZXkgfSkKICAgICAgICAgICAgfSk7CgogICAgICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpOwoKICAgICAgICAgICAgaWYgKGRhdGEudmFsaWQpIHsKICAgICAgICAgICAgICAgIHNob3dHZW1pbmlWYWxpZGF0aW9uTWVzc2FnZSgiR2VtaW5pIEFQSSBrZXkgaXMgdmFsaWQg4pyTIiwgInN1Y2Nlc3MiKTsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgc2hvd0dlbWluaVZhbGlkYXRpb25NZXNzYWdlKGRhdGEubWVzc2FnZSB8fCAiSW52YWxpZCBHZW1pbmkgQVBJIGtleSIsICJlcnJvciIpOwogICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgICAgICB9CiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgc2hvd0dlbWluaVZhbGlkYXRpb25NZXNzYWdlKCJWYWxpZGF0aW9uIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmRpc2FibGVkID0gZmFsc2U7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5yZW1vdmUoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9IG9yaWdpbmFsSFRNTDsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd0dlbWluaVZhbGlkYXRpb25NZXNzYWdlKG1lc3NhZ2UsIHR5cGUpIHsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS50ZXh0Q29udGVudCA9IG1lc3NhZ2U7CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UuY2xhc3NOYW1lID0gYG10LTIgdGV4dC14cyAke3R5cGUgPT09ICJzdWNjZXNzIiA/ICJ0ZXh0LWdyZWVuLTQwMCIgOiAidGV4dC1yZWQtNDAwIn1gOwogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOwogICAgfQoKICAgIHZhbGlkYXRlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgdmFsaWRhdGVHZW1pbmlLZXkpOwoKICAgIGFwaUtleUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoImlucHV0IiwgKCkgPT4gewogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOwogICAgfSk7Cn0KCi8vIFBhc3N3b3JkIFRvZ2dsZXMKZnVuY3Rpb24gaW5pdGlhbGl6ZVBhc3N3b3JkVG9nZ2xlcygpIHsKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy50b2dnbGUtYnRuJykuZm9yRWFjaChidG4gPT4gewogICAgICAgIGJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsKICAgICAgICAgICAgY29uc3QgdGFyZ2V0SWQgPSBidG4uZ2V0QXR0cmlidXRlKCdkYXRhLXRhcmdldCcpOwogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRhcmdldElkKTsKICAgICAgICAgICAgaWYgKCFpbnB1dCkgcmV0dXJuOwogICAgICAgICAgICBjb25zdCBpc0hpZGRlbiA9IGlucHV0LnR5cGUgPT09ICdwYXNzd29yZCc7CiAgICAgICAgICAgIGlucHV0LnR5cGUgPSBpc0hpZGRlbiA/ICd0ZXh0JyA6ICdwYXNzd29yZCc7CiAgICAgICAgICAgIC8vIFN3YXAgaWNvbiBhbmQgbGFiZWxzCiAgICAgICAgICAgIGlmIChpc0hpZGRlbikgewogICAgICAgICAgICAgICAgLy8gTm93IHZpc2libGU6IHNob3cgZXllLW9mZiBpY29uCiAgICAgICAgICAgICAgICBidG4uc2V0QXR0cmlidXRlKCd0aXRsZScsICdIaWRlJyk7CiAgICAgICAgICAgICAgICBidG4uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgJ0hpZGUgcGFzc3dvcmQnKTsKICAgICAgICAgICAgICAgIGJ0bi5pbm5lckhUTUwgPSAnPHN2ZyBjbGFzcz0idy00IGgtNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xNy45NCAxNy45NEExMC45NCAxMC45NCAwIDAgMSAxMiAyMGMtNyAwLTExLTgtMTEtOGEyMS43NyAyMS43NyAwIDAgMSA1LjA2LTYuMTdNOS45IDQuMjRBMTAuOTQgMTAuOTQgMCAwIDEgMTIgNGM3IDAgMTEgOCAxMSA4YTIxLjggMjEuOCAwIDAgMS0zLjIyIDQuMzEiLz48cGF0aCBkPSJNMSAxbDIyIDIyIi8+PHBhdGggZD0iTTE0LjEyIDE0LjEyQTMgMyAwIDAgMSA5Ljg4IDkuODgiLz48L3N2Zz4nOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgLy8gTm93IGhpZGRlbjogc2hvdyBleWUgaWNvbgogICAgICAgICAgICAgICAgYnRuLnNldEF0dHJpYnV0ZSgndGl0bGUnLCAnU2hvdycpOwogICAgICAgICAgICAgICAgYnRuLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsICdTaG93IHBhc3N3b3JkJyk7CiAgICAgICAgICAgICAgICBidG4uaW5uZXJIVE1MID0gJzxzdmcgY2xhc3M9InctNCBoLTQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMSAxMnM0LTcgMTEtNyAxMSA3IDExIDctNCA3LTExIDctMTEtNy0xMS03eiIvPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiLz48L3N2Zz4nOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9KTsKfQoKLy8gRGVsZXRlICYgU3VjY2VzcyBIZWxwZXJzCmZ1bmN0aW9uIGluaXRpYWxpemVTdWNjZXNzQWN0aW9ucygpIHsKICAgIGNvbnN0IGNvcHlCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29weUJ0bicpOwogICAgaWYgKGNvcHlCdG4pIHsKICAgICAgICBjb3B5QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpOwogICAgICAgICAgICBjb25zdCB1cmxUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZG9uVXJsJykudGV4dENvbnRlbnQ7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBhd2FpdCBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dCh1cmxUZXh0KTsKICAgICAgICAgICAgICAgIGNvbnN0IG9yaWdpbmFsVGV4dCA9IGNvcHlCdG4uaW5uZXJIVE1MOwogICAgICAgICAgICAgICAgY29weUJ0bi5pbm5lckhUTUwgPSAnQ29waWVkISc7CiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHsgY29weUJ0bi5pbm5lckhUTUwgPSBvcmlnaW5hbFRleHQ7IH0sIDIwMDApOwogICAgICAgICAgICB9IGNhdGNoIChlcnIpIHsgfQogICAgICAgIH0pOwogICAgfQoKICAgIGNvbnN0IGluc3RhbGxEZXNrdG9wQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2luc3RhbGxEZXNrdG9wQnRuJyk7CiAgICBpZiAoaW5zdGFsbERlc2t0b3BCdG4pIHsKICAgICAgICBpbnN0YWxsRGVza3RvcEJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKSA9PiB7CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTsKICAgICAgICAgICAgY29uc3QgdXJsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZG9uVXJsJykudGV4dENvbnRlbnQ7CiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gYHN0cmVtaW86Ly8ke3VybC5yZXBsYWNlKC9eaHR0cHM/OlwvXC8vLCAnJyl9YDsKICAgICAgICB9KTsKICAgIH0KICAgIGNvbnN0IGluc3RhbGxXZWJCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW5zdGFsbFdlYkJ0bicpOwogICAgaWYgKGluc3RhbGxXZWJCdG4pIHsKICAgICAgICBpbnN0YWxsV2ViQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpOwogICAgICAgICAgICBjb25zdCB1cmwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYWRkb25VcmwnKS50ZXh0Q29udGVudDsKICAgICAgICAgICAgd2luZG93Lm9wZW4oYGh0dHBzOi8vd2ViLnN0cmVtaW8uY29tLyMvYWRkb25zP2FkZG9uPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHVybCl9YCwgJ19ibGFuaycpOwogICAgICAgIH0pOwogICAgfQoKICAgIGNvbnN0IGRlbGV0ZUFjY291bnRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZGVsZXRlQWNjb3VudEJ0bicpOwogICAgaWYgKGRlbGV0ZUFjY291bnRCdG4pIHsKICAgICAgICBkZWxldGVBY2NvdW50QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKCkgPT4gewogICAgICAgICAgICBjb25zdCBjb25maXJtZWQgPSBhd2FpdCBzaG93Q29uZmlybSgKICAgICAgICAgICAgICAgICdEZWxldGUgQWNjb3VudD8nLAogICAgICAgICAgICAgICAgJ0FyZSB5b3Ugc3VyZSB5b3Ugd2FudCB0byBkZWxldGUgeW91ciBzZXR0aW5ncz8gVGhpcyBhY3Rpb24gaXMgaXJyZXZlcnNpYmxlIGFuZCBhbGwgeW91ciBkYXRhIHdpbGwgYmUgcGVybWFuZW50bHkgcmVtb3ZlZC4nCiAgICAgICAgICAgICk7CgogICAgICAgICAgICBpZiAoIWNvbmZpcm1lZCkgcmV0dXJuOwoKICAgICAgICAgICAgY29uc3Qgc0F1dGhLZXkgPSAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImF1dGhLZXkiKS52YWx1ZSB8fCAnJykudHJpbSgpOwogICAgICAgICAgICBjb25zdCBlbWFpbCA9IGVtYWlsSW5wdXQ/LnZhbHVlLnRyaW0oKTsKICAgICAgICAgICAgY29uc3QgcGFzc3dvcmQgPSBwYXNzd29yZElucHV0Py52YWx1ZTsKCiAgICAgICAgICAgIGlmICghc0F1dGhLZXkgJiYgIShlbWFpbCAmJiBwYXNzd29yZCkpIHsKICAgICAgICAgICAgICAgIHNob3dFcnJvcignZ2VuZXJhbEVycm9yJywgIlByb3ZpZGUgU3RyZW1pbyBhdXRoIGtleSBvciBlbWFpbCAmIHBhc3N3b3JkIHRvIGRlbGV0ZSB5b3VyIGFjY291bnQuIik7CiAgICAgICAgICAgICAgICBzd2l0Y2hTZWN0aW9uKCdsb2dpbicpOwogICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICB9CgogICAgICAgICAgICBzZXRMb2FkaW5nKHRydWUpOwogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHsgYXV0aEtleTogc0F1dGhLZXkgfHwgdW5kZWZpbmVkLCBlbWFpbDogZW1haWwgfHwgdW5kZWZpbmVkLCBwYXNzd29yZDogcGFzc3dvcmQgfHwgdW5kZWZpbmVkIH07CiAgICAgICAgICAgICAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCgnL3Rva2Vucy8nLCB7IG1ldGhvZDogJ0RFTEVURScsIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LCBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9KTsKICAgICAgICAgICAgICAgIGlmICghcmVzLm9rKSB0aHJvdyBuZXcgRXJyb3IoKGF3YWl0IHJlcy5qc29uKCkpLmRldGFpbCB8fCAnRmFpbGVkIHRvIGRlbGV0ZScpOwogICAgICAgICAgICAgICAgc2hvd1RvYXN0KCdBY2NvdW50IGRlbGV0ZWQgc3VjY2Vzc2Z1bGx5LicsICdzdWNjZXNzJyk7CiAgICAgICAgICAgICAgICBpZiAocmVzZXRBcHApIHJlc2V0QXBwKCk7CiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgICAgIHNob3dFcnJvcignZ2VuZXJhbEVycm9yJywgZS5tZXNzYWdlKTsKICAgICAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgICAgIHNldExvYWRpbmcoZmFsc2UpOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9Cn0KCmZ1bmN0aW9uIHNldExvYWRpbmcobG9hZGluZykgewogICAgaWYgKCFzdWJtaXRCdG4pIHJldHVybjsKICAgIGNvbnN0IGJ0blRleHQgPSBzdWJtaXRCdG4ucXVlcnlTZWxlY3RvcignLmJ0bi10ZXh0Jyk7CiAgICBjb25zdCBsb2FkZXIgPSBzdWJtaXRCdG4ucXVlcnlTZWxlY3RvcignLmxvYWRlcicpOwogICAgc3VibWl0QnRuLmRpc2FibGVkID0gbG9hZGluZzsKICAgIGlmIChsb2FkaW5nKSB7CiAgICAgICAgaWYgKGJ0blRleHQpIGJ0blRleHQuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICAgICAgaWYgKGxvYWRlcikgbG9hZGVyLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgfSBlbHNlIHsKICAgICAgICBpZiAoYnRuVGV4dCkgYnRuVGV4dC5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgICAgICBpZiAobG9hZGVyKSBsb2FkZXIuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICB9Cn0KCmZ1bmN0aW9uIHNob3dFcnJvcih0YXJnZXQsIG1lc3NhZ2UpIHsKICAgIGlmICh0YXJnZXQgPT09ICdnZW5lcmFsRXJyb3InKSB7CiAgICAgICAgY29uc3QgZXJyRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZXJyb3JNZXNzYWdlJyk7CiAgICAgICAgaWYgKGVyckVsKSB7CiAgICAgICAgICAgIGVyckVsLnF1ZXJ5U2VsZWN0b3IoJy5tZXNzYWdlLWNvbnRlbnQnKS50ZXh0Q29udGVudCA9IG1lc3NhZ2U7CiAgICAgICAgICAgIGVyckVsLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgICAgIH0gZWxzZSB7IHNob3dUb2FzdChtZXNzYWdlLCAnZXJyb3InKTsgfQogICAgfSBlbHNlIGlmICh0YXJnZXQgPT09ICdzdHJlbWlvQXV0aFNlY3Rpb24nKSB7CiAgICAgICAgc2hvd1RvYXN0KG1lc3NhZ2UsICdlcnJvcicpOwogICAgfSBlbHNlIHsKICAgICAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRhcmdldCk7CiAgICAgICAgaWYgKGVsKSB7CiAgICAgICAgICAgIGVsLmNsYXNzTGlzdC5hZGQoJ2JvcmRlci1yZWQtNTAwJyk7CiAgICAgICAgICAgIGVsLmZvY3VzKCk7CiAgICAgICAgfQogICAgfQp9CgpleHBvcnQgZnVuY3Rpb24gY2xlYXJFcnJvcnMoKSB7CiAgICBjb25zdCBlcnJFbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlcnJvck1lc3NhZ2UnKTsKICAgIGlmIChlcnJFbCkgZXJyRWwuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuYm9yZGVyLXJlZC01MDAnKS5mb3JFYWNoKGUgPT4gZS5jbGFzc0xpc3QucmVtb3ZlKCdib3JkZXItcmVkLTUwMCcpKTsKfQoKZnVuY3Rpb24gc2hvd1N1Y2Nlc3ModXJsKSB7CiAgICAvLyBIaWRlIGZvcm0gZW50aXJlbHkgYnkgaGlkaW5nIHRoZSBhY3RpdmUgc2VjdGlvbgogICAgY29uc3Qgc2VjdGlvbnMgPSB7CiAgICAgICAgd2VsY29tZTogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3Qtd2VsY29tZScpLAogICAgICAgIGxvZ2luOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1sb2dpbicpLAogICAgICAgIGNvbmZpZzogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3QtY29uZmlnJyksCiAgICAgICAgY2F0YWxvZ3M6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWN0LWNhdGFsb2dzJyksCiAgICAgICAgaW5zdGFsbDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3QtaW5zdGFsbCcpLAogICAgICAgIHN1Y2Nlc3M6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWN0LXN1Y2Nlc3MnKQogICAgfTsKICAgIE9iamVjdC52YWx1ZXMoc2VjdGlvbnMpLmZvckVhY2gocyA9PiB7IGlmIChzKSBzLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpIH0pOwoKICAgIC8vIFNob3cgU3VjY2VzcyBTZWN0aW9uCiAgICBpZiAoc2VjdGlvbnMuc3VjY2VzcykgewogICAgICAgIHNlY3Rpb25zLnN1Y2Nlc3MuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZG9uVXJsJykudGV4dENvbnRlbnQgPSB1cmw7CiAgICB9Cn0KCi8vIFllYXIgU2xpZGVyIExvZ2ljCmZ1bmN0aW9uIGluaXRpYWxpemVZZWFyU2xpZGVyKCkgewogICAgY29uc3QgeWVhck1pbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd5ZWFyTWluJyk7CiAgICBjb25zdCB5ZWFyTWF4ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3llYXJNYXgnKTsKICAgIGNvbnN0IHllYXJNaW5MYWJlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd5ZWFyTWluTGFiZWwnKTsKICAgIGNvbnN0IHllYXJNYXhMYWJlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd5ZWFyTWF4TGFiZWwnKTsKICAgIGNvbnN0IHRyYWNrID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3llYXJTbGlkZXJUcmFjaycpOwoKICAgIGlmICgheWVhck1pbiB8fCAheWVhck1heCB8fCAheWVhck1pbkxhYmVsIHx8ICF5ZWFyTWF4TGFiZWwgfHwgIXRyYWNrKSByZXR1cm47CgogICAgZnVuY3Rpb24gdXBkYXRlU2xpZGVyKCkgewogICAgICAgIGNvbnN0IG1pblZhbCA9IHBhcnNlSW50KHllYXJNaW4udmFsdWUpOwogICAgICAgIGNvbnN0IG1heFZhbCA9IHBhcnNlSW50KHllYXJNYXgudmFsdWUpOwoKICAgICAgICBpZiAobWluVmFsID4gbWF4VmFsKSB7CiAgICAgICAgICAgIC8vIFByZXZlbnQgY3Jvc3Npbmc6IGlmIG1pbiA+IG1heCwgc25hcCB0aGVtCiAgICAgICAgICAgIC8vIFRoaXMgaXMgaGFuZGxlZCBieSBpbnB1dCBsaXN0ZW5lcnMgdG8gYXZvaWQgamVya3kgbW92ZW1lbnQKICAgICAgICB9CgogICAgICAgIHllYXJNaW5MYWJlbC50ZXh0Q29udGVudCA9IG1pblZhbDsKICAgICAgICB5ZWFyTWF4TGFiZWwudGV4dENvbnRlbnQgPSBtYXhWYWw7CgogICAgICAgIGNvbnN0IHJhbmdlID0geWVhck1pbi5tYXggLSB5ZWFyTWluLm1pbjsKICAgICAgICBjb25zdCBsZWZ0ID0gKChtaW5WYWwgLSB5ZWFyTWluLm1pbikgLyByYW5nZSkgKiAxMDA7CiAgICAgICAgY29uc3QgcmlnaHQgPSAoKHllYXJNaW4ubWF4IC0gbWF4VmFsKSAvIHJhbmdlKSAqIDEwMDsKCiAgICAgICAgdHJhY2suc3R5bGUubGVmdCA9IGxlZnQgKyAnJSc7CiAgICAgICAgdHJhY2suc3R5bGUucmlnaHQgPSByaWdodCArICclJzsKICAgIH0KCiAgICB5ZWFyTWluLmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgKCkgPT4gewogICAgICAgIGlmIChwYXJzZUludCh5ZWFyTWluLnZhbHVlKSA+IHBhcnNlSW50KHllYXJNYXgudmFsdWUpKSB7CiAgICAgICAgICAgIHllYXJNaW4udmFsdWUgPSB5ZWFyTWF4LnZhbHVlOwogICAgICAgIH0KICAgICAgICB5ZWFyTWluLmNsYXNzTGlzdC5hZGQoJ3llYXItc2xpZGVyLWFjdGl2ZScpOwogICAgICAgIHllYXJNYXguY2xhc3NMaXN0LnJlbW92ZSgneWVhci1zbGlkZXItYWN0aXZlJyk7CiAgICAgICAgdXBkYXRlU2xpZGVyKCk7CiAgICB9KTsKCiAgICB5ZWFyTWF4LmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgKCkgPT4gewogICAgICAgIGlmIChwYXJzZUludCh5ZWFyTWF4LnZhbHVlKSA8IHBhcnNlSW50KHllYXJNaW4udmFsdWUpKSB7CiAgICAgICAgICAgIHllYXJNYXgudmFsdWUgPSB5ZWFyTWluLnZhbHVlOwogICAgICAgIH0KICAgICAgICB5ZWFyTWF4LmNsYXNzTGlzdC5hZGQoJ3llYXItc2xpZGVyLWFjdGl2ZScpOwogICAgICAgIHllYXJNaW4uY2xhc3NMaXN0LnJlbW92ZSgneWVhci1zbGlkZXItYWN0aXZlJyk7CiAgICAgICAgdXBkYXRlU2xpZGVyKCk7CiAgICB9KTsKCiAgICAvLyBJbml0aWFsIHVwZGF0ZQogICAgdXBkYXRlU2xpZGVyKCk7CgogICAgLy8gRXhwb3J0IHVwZGF0ZSBmdW5jdGlvbiBmb3IgZXh0ZXJuYWwgcG9wdWxhdGlvbgogICAgd2luZG93LnVwZGF0ZVllYXJTbGlkZXIgPSB1cGRhdGVTbGlkZXI7Cn0K" | base64 --decode > "$REPO/app/static/js/modules/form.js" +echo " wrote app/static/js/modules/form.js" + +mkdir -p "$REPO/app/services" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBmYXN0YXBpIGltcG9ydCBIVFRQRXhjZXB0aW9uCmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKCmZyb20gYXBwLmNvcmUuY29uZmlnIGltcG9ydCBzZXR0aW5ncwpmcm9tIGFwcC5jb3JlLnNlY3VyaXR5IGltcG9ydCByZWRhY3RfdG9rZW4KZnJvbSBhcHAuY29yZS5zZXR0aW5ncyBpbXBvcnQgVXNlclNldHRpbmdzLCByZXNvbHZlX3RtZGJfYXBpX2tleQpmcm9tIGFwcC5jb3JlLnZlcnNpb24gaW1wb3J0IF9fdmVyc2lvbl9fCmZyb20gYXBwLnNlcnZpY2VzLmNhdGFsb2cgaW1wb3J0IER5bmFtaWNDYXRhbG9nU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy5wcm9maWxlLmludGVncmF0aW9uIGltcG9ydCBQcm9maWxlSW50ZWdyYXRpb24KZnJvbSBhcHAuc2VydmljZXMuc3RyZW1pby5zZXJ2aWNlIGltcG9ydCBTdHJlbWlvQnVuZGxlCmZyb20gYXBwLnNlcnZpY2VzLnRva2VuX3N0b3JlIGltcG9ydCB0b2tlbl9zdG9yZQpmcm9tIGFwcC5zZXJ2aWNlcy50cmFuc2xhdGlvbiBpbXBvcnQgYXBwbHlfY2F0YWxvZ190cmFuc2xhdGlvbgpmcm9tIGFwcC5zZXJ2aWNlcy51c2VyX2NhY2hlIGltcG9ydCB1c2VyX2NhY2hlCmZyb20gYXBwLnV0aWxzLmNhdGFsb2cgaW1wb3J0IGNhY2hlX3Byb2ZpbGVfYW5kX3dhdGNoZWRfc2V0cywgc29ydF9jYXRhbG9ncwoKCmNsYXNzIE1hbmlmZXN0U2VydmljZToKICAgICIiIlNlcnZpY2UgZm9yIGdlbmVyYXRpbmcgU3RyZW1pbyBtYW5pZmVzdCBmaWxlcy4iIiIKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgZ2V0X2Jhc2VfbWFuaWZlc3QoKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICAiIiJHZXQgdGhlIGJhc2UgbWFuaWZlc3Qgc3RydWN0dXJlLiIiIgogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJpZCI6IHNldHRpbmdzLkFERE9OX0lELAogICAgICAgICAgICAidmVyc2lvbiI6IF9fdmVyc2lvbl9fLAogICAgICAgICAgICAibmFtZSI6IHNldHRpbmdzLkFERE9OX05BTUUsCiAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJNb3ZpZSBhbmQgc2VyaWVzIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiB5b3VyIFN0cmVtaW8gbGlicmFyeS4iLAogICAgICAgICAgICAibG9nbyI6ICgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1RpbWlsc2luYUJpbWFsL1dhdGNobHkvcmVmcy9oZWFkcy9tYWluL2FwcC9zdGF0aWMvbG9nby5wbmciKSwKICAgICAgICAgICAgImJhY2tncm91bmQiOiAoCiAgICAgICAgICAgICAgICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1RpbWlsc2luYUJpbWFsL1dhdGNobHkvcmVmcy9oZWFkcy9tYWluL2FwcC9zdGF0aWMvY292ZXIucG5nIgogICAgICAgICAgICApLAogICAgICAgICAgICAicmVzb3VyY2VzIjogWyJjYXRhbG9nIl0sCiAgICAgICAgICAgICJ0eXBlcyI6IFsibW92aWUiLCAic2VyaWVzIl0sCiAgICAgICAgICAgICJpZFByZWZpeGVzIjogWyJ0dCJdLAogICAgICAgICAgICAiY2F0YWxvZ3MiOiBbXSwKICAgICAgICAgICAgImJlaGF2aW9ySGludHMiOiB7ImNvbmZpZ3VyYWJsZSI6IFRydWUsICJjb25maWd1cmF0aW9uUmVxdWlyZWQiOiBGYWxzZX0sCiAgICAgICAgICAgICJzdHJlbWlvQWRkb25zQ29uZmlnIjogewogICAgICAgICAgICAgICAgImlzc3VlciI6ICJodHRwczovL3N0cmVtaW8tYWRkb25zLm5ldCIsCiAgICAgICAgICAgICAgICAic2lnbmF0dXJlIjogKAogICAgICAgICAgICAgICAgICAgICJleUpoYkdjaU9pSmthWElpTENKbGJtTWlPaUpCTVRJNFEwSkRMVWhUTWpVMkluMC4uV1NyaHp6bGoxVHVEeWNENlFvVkx1QS5Eem14enI0eTgzdXFRRjE1cjR0QzFiQjktdnRaUmgxUnZ5NEJxZ0RZeHU5MWMyZXNpSnVvdjlLbm5JX2Nib1FDZ1pTN2hqd25JcVJTbFEtakV5R3dYSEhSZXJoOVFrbHlmZHhwWHFOVXlCZ1RXRnpET1ZkVnZEWUplTV90R01tUi5zZXpBQ2hsV0dWN2xOUy10OUhXQjZBIiAgIyBub3FhCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICB9LAogICAgICAgIH0KCiAgICBhc3luYyBkZWYgX3Jlc29sdmVfYXV0aF9rZXkoc2VsZiwgYnVuZGxlOiBTdHJlbWlvQnVuZGxlLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0sIHRva2VuOiBzdHIpIC0+IHN0ciB8IE5vbmU6CiAgICAgICAgIiIiUmVzb2x2ZSBhbmQgdmFsaWRhdGUgYXV0aCBrZXksIHJlZnJlc2hpbmcgaWYgbmVlZGVkLiIiIgogICAgICAgIGF1dGhfa2V5ID0gY3JlZGVudGlhbHMuZ2V0KCJhdXRoS2V5IikKICAgICAgICBlbWFpbCA9IGNyZWRlbnRpYWxzLmdldCgiZW1haWwiKQogICAgICAgIHBhc3N3b3JkID0gY3JlZGVudGlhbHMuZ2V0KCJwYXNzd29yZCIpCgogICAgICAgIGlzX3ZhbGlkID0gRmFsc2UKICAgICAgICBpZiBhdXRoX2tleToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgYXdhaXQgYnVuZGxlLmF1dGguZ2V0X3VzZXJfaW5mbyhhdXRoX2tleSkKICAgICAgICAgICAgICAgIGlzX3ZhbGlkID0gVHJ1ZQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJBdXRoIGtleSBjaGVjayBmYWlsZWQgZm9yIHtlbWFpbCBvciAndW5rbm93bid9OiB7ZX0iKQoKICAgICAgICBpZiBub3QgaXNfdmFsaWQgYW5kIGVtYWlsIGFuZCBwYXNzd29yZDoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgYXV0aF9rZXkgPSBhd2FpdCBidW5kbGUuYXV0aC5sb2dpbihlbWFpbCwgcGFzc3dvcmQpCiAgICAgICAgICAgICAgICAjIFVwZGF0ZSBzdG9yZQogICAgICAgICAgICAgICAgY3JlZGVudGlhbHNbImF1dGhLZXkiXSA9IGF1dGhfa2V5CiAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiRmFpbGVkIHRvIHJlZnJlc2ggYXV0aCBrZXkgZHVyaW5nIG1hbmlmZXN0IGZldGNoOiB7ZX0iKQogICAgICAgICAgICAgICAgcmV0dXJuIE5vbmUKCiAgICAgICAgcmV0dXJuIGF1dGhfa2V5CgogICAgYXN5bmMgZGVmIGNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzKAogICAgICAgIHNlbGYsIGJ1bmRsZTogU3RyZW1pb0J1bmRsZSwgYXV0aF9rZXk6IHN0ciwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzLCB0b2tlbjogc3RyCiAgICApIC0+IGRpY3Rbc3RyLCBBbnldOgogICAgICAgICIiIgogICAgICAgIEZldGNoIGFuZCBjYWNoZSBsaWJyYXJ5IGl0ZW1zIGFuZCBwcm9maWxlcyBmb3IgYSB1c2VyLgoKICAgICAgICBUaGlzIHNob3VsZCBiZSBjYWxsZWQgZHVyaW5nIHRva2VuIGNyZWF0aW9uIHRvIHByZS1jYWNoZSBkYXRhCiAgICAgICAgc28gbWFuaWZlc3QgZ2VuZXJhdGlvbiBpcyBmYXN0LgoKICAgICAgICBBcmdzOgogICAgICAgICAgICBidW5kbGU6IFN0cmVtaW9CdW5kbGUgaW5zdGFuY2UKICAgICAgICAgICAgYXV0aF9rZXk6IFN0cmVtaW8gYXV0aCBrZXkKICAgICAgICAgICAgdXNlcl9zZXR0aW5nczogVXNlciBzZXR0aW5ncwogICAgICAgICAgICB0b2tlbjogVXNlciB0b2tlbgoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBMaWJyYXJ5IGl0ZW1zIGRpY3Rpb25hcnkKICAgICAgICAiIiIKICAgICAgICAjIEZldGNoIGxpYnJhcnkgaXRlbXMKICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZldGNoaW5nIGxpYnJhcnkgaXRlbXMgZm9yIGNhY2hpbmciKQogICAgICAgIGxpYnJhcnlfaXRlbXMgPSBhd2FpdCBidW5kbGUubGlicmFyeS5nZXRfbGlicmFyeV9pdGVtcyhhdXRoX2tleSkKCiAgICAgICAgIyBDYWNoZSBsaWJyYXJ5IGl0ZW1zIHVzaW5nIGNlbnRyYWxpemVkIGNhY2hlIHNlcnZpY2UKICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQogICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBsaWJyYXJ5IGl0ZW1zIikKCiAgICAgICAgIyBCdWlsZCBhbmQgY2FjaGUgcHJvZmlsZXMgZm9yIGJvdGggbW92aWUgYW5kIHNlcmllcwogICAgICAgIGxhbmd1YWdlID0gdXNlcl9zZXR0aW5ncy5sYW5ndWFnZQogICAgICAgIHRtZGJfa2V5ID0gcmVzb2x2ZV90bWRiX2FwaV9rZXkodXNlcl9zZXR0aW5ncykKICAgICAgICBpbnRlZ3JhdGlvbl9zZXJ2aWNlID0gUHJvZmlsZUludGVncmF0aW9uKGxhbmd1YWdlPWxhbmd1YWdlLCB0bWRiX2FwaV9rZXk9dG1kYl9rZXkpCgogICAgICAgIGZvciBjb250ZW50X3R5cGUgaW4gWyJtb3ZpZSIsICJzZXJpZXMiXToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBCdWlsZGluZyBhbmQgY2FjaGluZyBwcm9maWxlIGZvciB7Y29udGVudF90eXBlfSIpCiAgICAgICAgICAgICAgICBfLCBfLCBfID0gYXdhaXQgY2FjaGVfcHJvZmlsZV9hbmRfd2F0Y2hlZF9zZXRzKAogICAgICAgICAgICAgICAgICAgIHRva2VuLCBjb250ZW50X3R5cGUsIGludGVncmF0aW9uX3NlcnZpY2UsIGxpYnJhcnlfaXRlbXMsIGJ1bmRsZSwgYXV0aF9rZXkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBwcm9maWxlIGFuZCB3YXRjaGVkIHNldHMgZm9yIHtjb250ZW50X3R5cGV9IikKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gYnVpbGQvY2FjaGUgcHJvZmlsZSBmb3Ige2NvbnRlbnRfdHlwZX06IHtlfSIpCgogICAgICAgIHJldHVybiBsaWJyYXJ5X2l0ZW1zCgogICAgYXN5bmMgZGVmIGNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzX2Zyb21faXRlbXMoCiAgICAgICAgc2VsZiwgbGlicmFyeV9pdGVtczogZGljdCwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzLCB0b2tlbjogc3RyCiAgICApIC0+IE5vbmU6CiAgICAgICAgIiIiCiAgICAgICAgQ2FjaGUgbGlicmFyeSBpdGVtcyBhbmQgYnVpbGQgcHJvZmlsZXMgZnJvbSBhbiBhbHJlYWR5LWZldGNoZWQgbGlicmFyeSBkaWN0LgogICAgICAgIFVzZWQgYnkgVHJha3QgKGFuZCBhbnkgZnV0dXJlIG5vbi1TdHJlbWlvIHByb3ZpZGVyKSB3aGVyZSBsaWJyYXJ5IGRhdGEgaXMKICAgICAgICBvYnRhaW5lZCBvdXRzaWRlIG9mIHRoZSBTdHJlbWlvIGJ1bmRsZS4KICAgICAgICAiIiIKICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQogICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBsaWJyYXJ5IGl0ZW1zIChwcm92aWRlci1hZ25vc3RpYykiKQoKICAgICAgICBsYW5ndWFnZSA9IHVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UKICAgICAgICB0bWRiX2tleSA9IHJlc29sdmVfdG1kYl9hcGlfa2V5KHVzZXJfc2V0dGluZ3MpCiAgICAgICAgaW50ZWdyYXRpb25fc2VydmljZSA9IFByb2ZpbGVJbnRlZ3JhdGlvbihsYW5ndWFnZT1sYW5ndWFnZSwgdG1kYl9hcGlfa2V5PXRtZGJfa2V5KQoKICAgICAgICBmb3IgY29udGVudF90eXBlIGluIFsibW92aWUiLCAic2VyaWVzIl06CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHByb2ZpbGUsIHdhdGNoZWRfdG1kYiwgd2F0Y2hlZF9pbWRiID0gYXdhaXQgaW50ZWdyYXRpb25fc2VydmljZS5idWlsZF9wcm9maWxlX2Zyb21fbGlicmFyeSgKICAgICAgICAgICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zLCBjb250ZW50X3R5cGUKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGF3YWl0IHVzZXJfY2FjaGUuc2V0X3Byb2ZpbGVfYW5kX3dhdGNoZWRfc2V0cyh0b2tlbiwgY29udGVudF90eXBlLCBwcm9maWxlLCB3YXRjaGVkX3RtZGIsIHdhdGNoZWRfaW1kYikKICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBwcm9maWxlIGZvciB7Y29udGVudF90eXBlfSIpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gRmFpbGVkIHRvIGNhY2hlIHByb2ZpbGUgZm9yIHtjb250ZW50X3R5cGV9OiB7ZX0iKQoKCiAgICBhc3luYyBkZWYgX2Vuc3VyZV9saWJyYXJ5X2FuZF9wcm9maWxlc19jYWNoZWQoCiAgICAgICAgc2VsZiwgYnVuZGxlOiBTdHJlbWlvQnVuZGxlLCBhdXRoX2tleTogc3RyLCB1c2VyX3NldHRpbmdzOiBVc2VyU2V0dGluZ3MsIHRva2VuOiBzdHIKICAgICkgLT4gZGljdFtzdHIsIEFueV06CiAgICAgICAgIiIiRW5zdXJlIGxpYnJhcnkgaXRlbXMgYW5kIHByb2ZpbGVzIGFyZSBjYWNoZWQsIGZldGNoaW5nIGFuZCBidWlsZGluZyBpZiBuZWVkZWQuIiIiCiAgICAgICAgIyBUcnkgdG8gZ2V0IGNhY2hlZCBsaWJyYXJ5IGl0ZW1zIGZpcnN0CiAgICAgICAgbGlicmFyeV9pdGVtcyA9IGF3YWl0IHVzZXJfY2FjaGUuZ2V0X2xpYnJhcnlfaXRlbXModG9rZW4pCgogICAgICAgIGlmIGxpYnJhcnlfaXRlbXM6CiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIFVzaW5nIGNhY2hlZCBsaWJyYXJ5IGl0ZW1zIGZvciBtYW5pZmVzdCIpCiAgICAgICAgICAgIHJldHVybiBsaWJyYXJ5X2l0ZW1zCgogICAgICAgICMgSWYgbm90IGNhY2hlZCwgZmV0Y2ggYW5kIGNhY2hlCiAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBMaWJyYXJ5IGl0ZW1zIG5vdCBjYWNoZWQsIGZldGNoaW5nIGZyb20gU3RyZW1pbyBmb3IgbWFuaWZlc3QiKQogICAgICAgIHJldHVybiBhd2FpdCBzZWxmLmNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzKGJ1bmRsZSwgYXV0aF9rZXksIHVzZXJfc2V0dGluZ3MsIHRva2VuKQoKICAgIGFzeW5jIGRlZiBfYnVpbGRfZHluYW1pY19jYXRhbG9ncygKICAgICAgICBzZWxmLCBidW5kbGU6IFN0cmVtaW9CdW5kbGUsIGF1dGhfa2V5OiBzdHIsIHVzZXJfc2V0dGluZ3M6IFVzZXJTZXR0aW5ncyB8IE5vbmUsIHRva2VuOiBzdHIKICAgICkgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiQnVpbGQgZHluYW1pYyBjYXRhbG9ncyBmb3IgdGhlIG1hbmlmZXN0LiIiIgogICAgICAgICMgY2hlY2sgaWYgY2FjaGVkLCBpZiBub3QsIGZldGNoIGFuZCBjYWNoZQogICAgICAgIGxpYnJhcnlfaXRlbXMgPSBhd2FpdCB1c2VyX2NhY2hlLmdldF9saWJyYXJ5X2l0ZW1zKHRva2VuKQogICAgICAgIGlmIG5vdCBsaWJyYXJ5X2l0ZW1zOgogICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgc2VsZi5fZW5zdXJlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzX2NhY2hlZChidW5kbGUsIGF1dGhfa2V5LCB1c2VyX3NldHRpbmdzLCB0b2tlbikKICAgICAgICAgICAgYXdhaXQgdXNlcl9jYWNoZS5zZXRfbGlicmFyeV9pdGVtcyh0b2tlbiwgbGlicmFyeV9pdGVtcykKCiAgICAgICAgdG1kYl9rZXkgPSByZXNvbHZlX3RtZGJfYXBpX2tleSh1c2VyX3NldHRpbmdzKQogICAgICAgIGR5bmFtaWNfY2F0YWxvZ19zZXJ2aWNlID0gRHluYW1pY0NhdGFsb2dTZXJ2aWNlKGxhbmd1YWdlPXVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UsIHRtZGJfYXBpX2tleT10bWRiX2tleSkKICAgICAgICByZXR1cm4gYXdhaXQgZHluYW1pY19jYXRhbG9nX3NlcnZpY2UuZ2V0X2R5bmFtaWNfY2F0YWxvZ3MobGlicmFyeV9pdGVtcywgdXNlcl9zZXR0aW5ncywgdG9rZW49dG9rZW4pCgogICAgYXN5bmMgZGVmIF90cmFuc2xhdGVfY2F0YWxvZ3Moc2VsZiwgY2F0YWxvZ3M6IGxpc3RbZGljdFtzdHIsIEFueV1dLCBsYW5ndWFnZTogc3RyIHwgTm9uZSkgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiVHJhbnNsYXRlIGNhdGFsb2cgbmFtZXMgdG8gdGFyZ2V0IGxhbmd1YWdlLiIiIgogICAgICAgIGlmIG5vdCBsYW5ndWFnZToKICAgICAgICAgICAgcmV0dXJuIGNhdGFsb2dzCgogICAgICAgIHRyYW5zbGF0ZWRfY2F0YWxvZ3MgPSBbXQogICAgICAgIGZvciBjYXQgaW4gY2F0YWxvZ3M6CiAgICAgICAgICAgIGF3YWl0IGFwcGx5X2NhdGFsb2dfdHJhbnNsYXRpb24oY2F0LCBsYW5ndWFnZSkKICAgICAgICAgICAgdHJhbnNsYXRlZF9jYXRhbG9ncy5hcHBlbmQoY2F0KQoKICAgICAgICByZXR1cm4gdHJhbnNsYXRlZF9jYXRhbG9ncwoKICAgIGRlZiBfc29ydF9jYXRhbG9ncygKICAgICAgICBzZWxmLCBjYXRhbG9nczogbGlzdFtkaWN0W3N0ciwgQW55XV0sIHVzZXJfc2V0dGluZ3M6IFVzZXJTZXR0aW5ncyB8IE5vbmUKICAgICkgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiU29ydCBjYXRhbG9ncyBhY2NvcmRpbmcgdG8gdXNlciBzZXR0aW5ncyBvcmRlci4iIiIKICAgICAgICBpZiBub3QgdXNlcl9zZXR0aW5nczoKICAgICAgICAgICAgcmV0dXJuIGNhdGFsb2dzCgogICAgICAgIHJldHVybiBzb3J0X2NhdGFsb2dzKGNhdGFsb2dzLCB1c2VyX3NldHRpbmdzKQoKICAgIGFzeW5jIGRlZiBfYnVpbGRfZHluYW1pY19jYXRhbG9nc190cmFrdCgKICAgICAgICBzZWxmLCBjcmVkczogZGljdCwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzIHwgTm9uZSwgdG9rZW46IHN0cgogICAgKSAtPiBsaXN0W2RpY3Rbc3RyLCBBbnldXToKICAgICAgICAiIiJCdWlsZCBkeW5hbWljIGNhdGFsb2dzIGZvciBhIFRyYWt0LWJhY2tlZCBhY2NvdW50LiIiIgogICAgICAgIGZyb20gYXBwLmNvcmUuY29uZmlnIGltcG9ydCBzZXR0aW5ncyBhcyBhcHBfc2V0dGluZ3MKICAgICAgICBmcm9tIGFwcC5zZXJ2aWNlcy50cmFrdC5zZXJ2aWNlIGltcG9ydCBUcmFrdEJ1bmRsZQoKICAgICAgICAjIFVzZSBjYWNoZWQgbGlicmFyeSBpZiBhdmFpbGFibGUKICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgdXNlcl9jYWNoZS5nZXRfbGlicmFyeV9pdGVtcyh0b2tlbikKICAgICAgICBpZiBub3QgbGlicmFyeV9pdGVtczoKICAgICAgICAgICAgYWNjZXNzX3Rva2VuID0gY3JlZHMuZ2V0KCJhdXRoS2V5IikgICMgc3RvcmVkIGFzIGF1dGhLZXkgYWZ0ZXIgZW5jcnlwdGlvbi9kZWNyeXB0aW9uCiAgICAgICAgICAgIGlmIG5vdCBhY2Nlc3NfdG9rZW4gb3Igbm90IGFwcF9zZXR0aW5ncy5UUkFLVF9DTElFTlRfSUQgb3Igbm90IGFwcF9zZXR0aW5ncy5UUkFLVF9DTElFTlRfU0VDUkVUOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBUcmFrdCBjcmVkZW50aWFscyBtaXNzaW5nLCBjYW5ub3QgZmV0Y2ggbGlicmFyeSIpCiAgICAgICAgICAgICAgICByZXR1cm4gW10KICAgICAgICAgICAgcmVkaXJlY3RfdXJpID0gZiJ7YXBwX3NldHRpbmdzLkhPU1RfTkFNRX0vdG9rZW5zL3RyYWt0L2NhbGxiYWNrIgogICAgICAgICAgICB0cmFrdF9idW5kbGUgPSBUcmFrdEJ1bmRsZSgKICAgICAgICAgICAgICAgIGNsaWVudF9pZD1hcHBfc2V0dGluZ3MuVFJBS1RfQ0xJRU5UX0lELAogICAgICAgICAgICAgICAgY2xpZW50X3NlY3JldD1hcHBfc2V0dGluZ3MuVFJBS1RfQ0xJRU5UX1NFQ1JFVCwKICAgICAgICAgICAgICAgIHJlZGlyZWN0X3VyaT1yZWRpcmVjdF91cmksCiAgICAgICAgICAgICAgICBhY2Nlc3NfdG9rZW49YWNjZXNzX3Rva2VuLAogICAgICAgICAgICApCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGxpYnJhcnlfaXRlbXMgPSBhd2FpdCB0cmFrdF9idW5kbGUubGlicmFyeS5nZXRfbGlicmFyeV9pdGVtcygpCiAgICAgICAgICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQogICAgICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICAgICAgYXdhaXQgdHJha3RfYnVuZGxlLmNsb3NlKCkKCiAgICAgICAgaWYgbm90IGxpYnJhcnlfaXRlbXM6CiAgICAgICAgICAgIHJldHVybiBbXQoKICAgICAgICB0bWRiX2tleSA9IHJlc29sdmVfdG1kYl9hcGlfa2V5KHVzZXJfc2V0dGluZ3MpCiAgICAgICAgZHluYW1pY19jYXRhbG9nX3NlcnZpY2UgPSBEeW5hbWljQ2F0YWxvZ1NlcnZpY2UoCiAgICAgICAgICAgIGxhbmd1YWdlPXVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UgaWYgdXNlcl9zZXR0aW5ncyBlbHNlICJlbi1VUyIsCiAgICAgICAgICAgIHRtZGJfYXBpX2tleT10bWRiX2tleSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIGF3YWl0IGR5bmFtaWNfY2F0YWxvZ19zZXJ2aWNlLmdldF9keW5hbWljX2NhdGFsb2dzKGxpYnJhcnlfaXRlbXMsIHVzZXJfc2V0dGluZ3MsIHRva2VuPXRva2VuKQoKICAgIGFzeW5jIGRlZiBnZXRfbWFuaWZlc3RfZm9yX3Rva2VuKHNlbGYsIHRva2VuOiBzdHIpIC0+IGRpY3Rbc3RyLCBBbnldOgogICAgICAgICIiIgogICAgICAgIEdlbmVyYXRlIG1hbmlmZXN0IGZvciBhIGdpdmVuIHRva2VuLgoKICAgICAgICBBcmdzOgogICAgICAgICAgICB0b2tlbjogVXNlciB0b2tlbgoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBDb21wbGV0ZSBtYW5pZmVzdCBkaWN0aW9uYXJ5CgogICAgICAgIFJhaXNlczoKICAgICAgICAgICAgSFRUUEV4Y2VwdGlvbjogSWYgdG9rZW4gaXMgaW52YWxpZCBvciBjcmVkZW50aWFscyBhcmUgbWlzc2luZwogICAgICAgICIiIgogICAgICAgIGlmIG5vdCB0b2tlbjoKICAgICAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDEsIGRldGFpbD0iTWlzc2luZyB0b2tlbi4gUGxlYXNlIHJlY29uZmlndXJlIHRoZSBhZGRvbi4iKQoKICAgICAgICAjIExvYWQgdXNlciBjcmVkZW50aWFscyBhbmQgc2V0dGluZ3MKICAgICAgICBjcmVkcyA9IGF3YWl0IHRva2VuX3N0b3JlLmdldF91c2VyX2RhdGEodG9rZW4pCiAgICAgICAgaWYgbm90IGNyZWRzOgogICAgICAgICAgICByYWlzZSBIVFRQRXhjZXB0aW9uKHN0YXR1c19jb2RlPTQwMSwgZGV0YWlsPSJUb2tlbiBub3QgZm91bmQuIFBsZWFzZSByZWNvbmZpZ3VyZSB0aGUgYWRkb24uIikKCiAgICAgICAgdXNlcl9zZXR0aW5ncyA9IE5vbmUKICAgICAgICB0cnk6CiAgICAgICAgICAgIGlmIGNyZWRzLmdldCgic2V0dGluZ3MiKToKICAgICAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBVc2VyU2V0dGluZ3MoKipjcmVkc1sic2V0dGluZ3MiXSkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci5lcnJvcihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEVycm9yIGxvYWRpbmcgdXNlciBkYXRhIGZyb20gdG9rZW4gc3RvcmU6IHtlfSIpCiAgICAgICAgICAgIHJhaXNlIEhUVFBFeGNlcHRpb24oc3RhdHVzX2NvZGU9NDAxLCBkZXRhaWw9IkludmFsaWQgdG9rZW4gc2Vzc2lvbi4gUGxlYXNlIHJlY29uZmlndXJlLiIpCgogICAgICAgIGJhc2VfbWFuaWZlc3QgPSBzZWxmLmdldF9iYXNlX21hbmlmZXN0KCkKCiAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IFtdCiAgICAgICAgdHJ5OgogICAgICAgICAgICBpZiBjcmVkcy5nZXQoImF1dGhfcHJvdmlkZXIiKSA9PSAidHJha3QiOgogICAgICAgICAgICAgICAgIyBUcmFrdC1iYWNrZWQgYWNjb3VudDogZmV0Y2ggbGlicmFyeSBmcm9tIFRyYWt0LCBieXBhc3MgU3RyZW1pbwogICAgICAgICAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IGF3YWl0IHNlbGYuX2J1aWxkX2R5bmFtaWNfY2F0YWxvZ3NfdHJha3QoY3JlZHMsIHVzZXJfc2V0dGluZ3MsIHRva2VuKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgYnVuZGxlID0gU3RyZW1pb0J1bmRsZSgpCiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgIyBSZXNvbHZlIGF1dGgga2V5CiAgICAgICAgICAgICAgICAgICAgYXV0aF9rZXkgPSBhd2FpdCBzZWxmLl9yZXNvbHZlX2F1dGhfa2V5KGJ1bmRsZSwgY3JlZHMsIHRva2VuKQoKICAgICAgICAgICAgICAgICAgICBpZiBhdXRoX2tleToKICAgICAgICAgICAgICAgICAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IGF3YWl0IHNlbGYuX2J1aWxkX2R5bmFtaWNfY2F0YWxvZ3MoYnVuZGxlLCBhdXRoX2tleSwgdXNlcl9zZXR0aW5ncywgdG9rZW4pCiAgICAgICAgICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gRHluYW1pYyBjYXRhbG9nIGJ1aWxkIGZhaWxlZDoge2V9IikKICAgICAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IFtdCgogICAgICAgICMgQ29tYmluZSBiYXNlIGNhdGFsb2dzIHdpdGggZmV0Y2hlZCBjYXRhbG9ncwogICAgICAgIGFsbF9jYXRhbG9ncyA9IFtjLmNvcHkoKSBmb3IgYyBpbiBiYXNlX21hbmlmZXN0WyJjYXRhbG9ncyJdXSArIFtjLmNvcHkoKSBmb3IgYyBpbiBmZXRjaGVkX2NhdGFsb2dzXQoKICAgICAgICAjIFRyYW5zbGF0ZSBjYXRhbG9ncwogICAgICAgIGxhbmd1YWdlID0gdXNlcl9zZXR0aW5ncy5sYW5ndWFnZSBpZiB1c2VyX3NldHRpbmdzIGVsc2UgTm9uZQogICAgICAgIHRyYW5zbGF0ZWRfY2F0YWxvZ3MgPSBhd2FpdCBzZWxmLl90cmFuc2xhdGVfY2F0YWxvZ3MoYWxsX2NhdGFsb2dzLCBsYW5ndWFnZSkKCiAgICAgICAgIyBTb3J0IGNhdGFsb2dzCiAgICAgICAgc29ydGVkX2NhdGFsb2dzID0gc2VsZi5fc29ydF9jYXRhbG9ncyh0cmFuc2xhdGVkX2NhdGFsb2dzLCB1c2VyX3NldHRpbmdzKQoKICAgICAgICBpZiBzb3J0ZWRfY2F0YWxvZ3M6CiAgICAgICAgICAgIGJhc2VfbWFuaWZlc3RbImNhdGFsb2dzIl0gPSBzb3J0ZWRfY2F0YWxvZ3MKCiAgICAgICAgcmV0dXJuIGJhc2VfbWFuaWZlc3QKCgptYW5pZmVzdF9zZXJ2aWNlID0gTWFuaWZlc3RTZXJ2aWNlKCkK" | base64 --decode > "$REPO/app/services/manifest.py" +echo " wrote app/services/manifest.py" + +mkdir -p "$REPO/app/services" +echo "aW1wb3J0IGFzeW5jaW8KZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUsIHRpbWV6b25lCmZyb20gdHlwaW5nIGltcG9ydCBBbnkKCmZyb20gZmFzdGFwaSBpbXBvcnQgSFRUUEV4Y2VwdGlvbgpmcm9tIGxvZ3VydSBpbXBvcnQgbG9nZ2VyCgpmcm9tIGFwcC5jb3JlLmNvbmZpZyBpbXBvcnQgc2V0dGluZ3MKZnJvbSBhcHAuY29yZS5zZWN1cml0eSBpbXBvcnQgcmVkYWN0X3Rva2VuCmZyb20gYXBwLmNvcmUuc2V0dGluZ3MgaW1wb3J0IFVzZXJTZXR0aW5ncwpmcm9tIGFwcC5zZXJ2aWNlcy5jYXRhbG9nIGltcG9ydCBEeW5hbWljQ2F0YWxvZ1NlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMubWFuaWZlc3QgaW1wb3J0IG1hbmlmZXN0X3NlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMuc3RyZW1pby5zZXJ2aWNlIGltcG9ydCBTdHJlbWlvQnVuZGxlCmZyb20gYXBwLnNlcnZpY2VzLnRva2VuX3N0b3JlIGltcG9ydCB0b2tlbl9zdG9yZQpmcm9tIGFwcC5zZXJ2aWNlcy50cmFuc2xhdGlvbiBpbXBvcnQgYXBwbHlfY2F0YWxvZ190cmFuc2xhdGlvbgpmcm9tIGFwcC51dGlscy5jYXRhbG9nIGltcG9ydCBzb3J0X2NhdGFsb2dzCgoKY2xhc3MgQ2F0YWxvZ1VwZGF0ZXI6CiAgICAiIiIKICAgIENhdGFsb2cgdXBkYXRlciB0aGF0IHRyaWdnZXJzIHVwZGF0ZXMgb24tZGVtYW5kIHdoZW4gdXNlcnMgcmVxdWVzdCBjYXRhbG9ncy4KICAgIFVzZXMgaW4tbWVtb3J5IGxvY2tpbmcgdG8gcHJldmVudCBkdXBsaWNhdGUgY29uY3VycmVudCB1cGRhdGVzLgogICAgIiIiCgogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgICMgSW4tbWVtb3J5IGxvY2sgdG8gcHJldmVudCBkdXBsaWNhdGUgdXBkYXRlcyBmb3IgdGhlIHNhbWUgdG9rZW4KICAgICAgICBzZWxmLl91cGRhdGluZ190b2tlbnM6IHNldFtzdHJdID0gc2V0KCkKCiAgICBkZWYgX25lZWRzX3VwZGF0ZShzZWxmLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0pIC0+IGJvb2w6CiAgICAgICAgIiIiQ2hlY2sgaWYgY2F0YWxvZyB1cGRhdGUgaXMgbmVlZGVkIGJhc2VkIG9uIGxhc3RfdXBkYXRlZCB0aW1lc3RhbXAuIiIiCiAgICAgICAgaWYgbm90IGNyZWRlbnRpYWxzOgogICAgICAgICAgICByZXR1cm4gRmFsc2UKCiAgICAgICAgbGFzdF91cGRhdGVkID0gY3JlZGVudGlhbHMuZ2V0KCJsYXN0X3VwZGF0ZWQiKQogICAgICAgIGlmIG5vdCBsYXN0X3VwZGF0ZWQ6CiAgICAgICAgICAgICMgTm8gdGltZXN0YW1wIG1lYW5zIG5ldmVyIHVwZGF0ZWQsIG5lZWRzIHVwZGF0ZQogICAgICAgICAgICByZXR1cm4gVHJ1ZQoKICAgICAgICB0cnk6CiAgICAgICAgICAgICMgUGFyc2UgSVNPIGZvcm1hdCB0aW1lc3RhbXAKICAgICAgICAgICAgaWYgaXNpbnN0YW5jZShsYXN0X3VwZGF0ZWQsIHN0cik6CiAgICAgICAgICAgICAgICBsYXN0X3VwZGF0ZV90aW1lID0gZGF0ZXRpbWUuZnJvbWlzb2Zvcm1hdChsYXN0X3VwZGF0ZWQucmVwbGFjZSgiWiIsICIrMDA6MDAiKSkKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGxhc3RfdXBkYXRlX3RpbWUgPSBsYXN0X3VwZGF0ZWQKCiAgICAgICAgICAgICMgQ2hlY2sgaWYgbW9yZSB0aGFuIDExIGhvdXJzIGhhdmUgcGFzc2VkICh1cGRhdGUgaWYgbGVzcyB0aGFuIDEgaG91ciByZW1haW5pbmcpCiAgICAgICAgICAgIG5vdyA9IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpCiAgICAgICAgICAgIGlmIGxhc3RfdXBkYXRlX3RpbWUudHppbmZvIGlzIE5vbmU6CiAgICAgICAgICAgICAgICBsYXN0X3VwZGF0ZV90aW1lID0gbGFzdF91cGRhdGVfdGltZS5yZXBsYWNlKHR6aW5mbz10aW1lem9uZS51dGMpCgogICAgICAgICAgICB0aW1lX3NpbmNlX3VwZGF0ZSA9IChub3cgLSBsYXN0X3VwZGF0ZV90aW1lKS50b3RhbF9zZWNvbmRzKCkKICAgICAgICAgICAgIyBVcGRhdGUgaWYgbGVzcyB0aGFuIDEgaG91ciByZW1haW5pbmcgdW50aWwgbmV4dCB1cGRhdGUKICAgICAgICAgICAgcmV0dXJuIHRpbWVfc2luY2VfdXBkYXRlID49IChzZXR0aW5ncy5DQVRBTE9HX1JFRlJFU0hfSU5URVJWQUxfU0VDT05EUyAtIDM2MDApCiAgICAgICAgZXhjZXB0IChWYWx1ZUVycm9yLCBUeXBlRXJyb3IsIEF0dHJpYnV0ZUVycm9yKSBhcyBlOgogICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBwYXJzZSBsYXN0X3VwZGF0ZWQgdGltZXN0YW1wOiB7ZX0uIFRyZWF0aW5nIGFzIG5lZWRzIHVwZGF0ZS4iKQogICAgICAgICAgICByZXR1cm4gVHJ1ZQoKICAgIGFzeW5jIGRlZiByZWZyZXNoX2NhdGFsb2dzX2Zvcl9jcmVkZW50aWFscygKICAgICAgICBzZWxmLCB0b2tlbjogc3RyLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0sIHVwZGF0ZV90aW1lc3RhbXA6IGJvb2wgPSBUcnVlCiAgICApIC0+IGJvb2w6CiAgICAgICAgIiIiCiAgICAgICAgUmVmcmVzaCBjYXRhbG9ncyBmb3IgYSB1c2VyJ3MgY3JlZGVudGlhbHMuCgogICAgICAgIEFyZ3M6CiAgICAgICAgICAgIHRva2VuOiBVc2VyIHRva2VuCiAgICAgICAgICAgIGNyZWRlbnRpYWxzOiBVc2VyIGNyZWRlbnRpYWxzIGRpY3QKICAgICAgICAgICAgdXBkYXRlX3RpbWVzdGFtcDogV2hldGhlciB0byB1cGRhdGUgbGFzdF91cGRhdGVkIHRpbWVzdGFtcCBvbiBzdWNjZXNzCgogICAgICAgIFJldHVybnM6CiAgICAgICAgICAgIFRydWUgaWYgdXBkYXRlIHdhcyBzdWNjZXNzZnVsLCBGYWxzZSBvdGhlcndpc2UKICAgICAgICAiIiIKICAgICAgICBpZiBub3QgY3JlZGVudGlhbHM6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gQXR0ZW1wdGVkIHRvIHJlZnJlc2ggY2F0YWxvZ3Mgd2l0aCBubyBjcmVkZW50aWFscy4iKQogICAgICAgICAgICByYWlzZSBIVFRQRXhjZXB0aW9uKHN0YXR1c19jb2RlPTQwMSwgZGV0YWlsPSJJbnZhbGlkIG9yIGV4cGlyZWQgdG9rZW4uIFBsZWFzZSByZWNvbmZpZ3VyZSB0aGUgYWRkb24uIikKCiAgICAgICAgIyBUcmFrdC1iYWNrZWQgYWNjb3VudHMgdXNlIGEgZGlmZmVyZW50IHJlZnJlc2ggcGF0aAogICAgICAgIGlmIGNyZWRlbnRpYWxzLmdldCgiYXV0aF9wcm92aWRlciIpID09ICJ0cmFrdCI6CiAgICAgICAgICAgIHJldHVybiBhd2FpdCBzZWxmLl9yZWZyZXNoX3RyYWt0X2NhdGFsb2dzKHRva2VuLCBjcmVkZW50aWFscywgdXBkYXRlX3RpbWVzdGFtcCkKCiAgICAgICAgYXV0aF9rZXkgPSBjcmVkZW50aWFscy5nZXQoImF1dGhLZXkiKQogICAgICAgICMgY2hlY2sgaWYgYXV0aCBrZXkgaXMgdmFsaWQKICAgICAgICBidW5kbGUgPSBTdHJlbWlvQnVuZGxlKCkKICAgICAgICB0cnk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5hdXRoLmdldF91c2VyX2luZm8oYXV0aF9rZXkpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBJbnZhbGlkIGF1dGgga2V5LiBGYWxsaW5nIGJhY2sgdG8gbG9naW46IHtlfSIpCiAgICAgICAgICAgICAgICBlbWFpbCA9IGNyZWRlbnRpYWxzLmdldCgiZW1haWwiKQogICAgICAgICAgICAgICAgcGFzc3dvcmQgPSBjcmVkZW50aWFscy5nZXQoInBhc3N3b3JkIikKICAgICAgICAgICAgICAgIGlmIGVtYWlsIGFuZCBwYXNzd29yZDoKICAgICAgICAgICAgICAgICAgICBhdXRoX2tleSA9IGF3YWl0IGJ1bmRsZS5hdXRoLmxvZ2luKGVtYWlsLCBwYXNzd29yZCkKICAgICAgICAgICAgICAgICAgICBjcmVkZW50aWFsc1siYXV0aEtleSJdID0gYXV0aF9rZXkKICAgICAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRydWUgICMgdHJ1ZSBzaW5jZSB3ZSB3b24ndCBiZSBhYmxlIHRvIHVwZGF0ZSBpdCBhZ2Fpbi4gc28gbm8gbmVlZCB0byB0cnkgYWdhaW4uCgogICAgICAgICAgICAjIDEuIENoZWNrIGlmIGFkZG9uIGlzIHN0aWxsIGluc3RhbGxlZAogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBhZGRvbl9pbnN0YWxsZWQgPSBhd2FpdCBidW5kbGUuYWRkb25zLmlzX2FkZG9uX2luc3RhbGxlZChhdXRoX2tleSkKICAgICAgICAgICAgICAgIGlmIG5vdCBhZGRvbl9pbnN0YWxsZWQ6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBVc2VyIGhhcyBub3QgaW5zdGFsbGVkIGFkZG9uLiBSZW1vdmluZyB0b2tlbiBmcm9tIHJlZGlzIikKICAgICAgICAgICAgICAgICAgICByZXR1cm4gVHJ1ZQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gRmFpbGVkIHRvIGNoZWNrIGlmIGFkZG9uIGlzIGluc3RhbGxlZDoge2V9IikKICAgICAgICAgICAgICAgIHJldHVybiBGYWxzZQoKICAgICAgICAgICAgIyAyLiBFeHRyYWN0IHNldHRpbmdzIGFuZCByZWZyZXNoCiAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBOb25lCiAgICAgICAgICAgIGlmIGNyZWRlbnRpYWxzLmdldCgic2V0dGluZ3MiKToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICB1c2VyX3NldHRpbmdzID0gVXNlclNldHRpbmdzKCoqY3JlZGVudGlhbHNbInNldHRpbmdzIl0pCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmV4Y2VwdGlvbihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byBwYXJzZSB1c2VyIHNldHRpbmdzOiB7ZX0iKQogICAgICAgICAgICAgICAgICAgICMgaWYgdXNlciBkb2Vzbid0IGhhdmUgc2V0dGluZywgd2UgY2FuJ3QgdXBkYXRlIHRoZSBjYXRhbG9ncy4KICAgICAgICAgICAgICAgICAgICAjIHNvIG5vIG5lZWQgdG8gdHJ5IGFnYWluLgogICAgICAgICAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgbWFuaWZlc3Rfc2VydmljZS5jYWNoZV9saWJyYXJ5X2FuZF9wcm9maWxlcyhidW5kbGUsIGF1dGhfa2V5LCB1c2VyX3NldHRpbmdzLCB0b2tlbikKICAgICAgICAgICAgbGFuZ3VhZ2UgPSB1c2VyX3NldHRpbmdzLmxhbmd1YWdlIGlmIHVzZXJfc2V0dGluZ3MgZWxzZSAiZW4tVVMiCgogICAgICAgICAgICBmcm9tIGFwcC5jb3JlLnNldHRpbmdzIGltcG9ydCByZXNvbHZlX3RtZGJfYXBpX2tleQoKICAgICAgICAgICAgdG1kYl9rZXkgPSByZXNvbHZlX3RtZGJfYXBpX2tleSh1c2VyX3NldHRpbmdzKQogICAgICAgICAgICBkeW5hbWljX2NhdGFsb2dfc2VydmljZSA9IER5bmFtaWNDYXRhbG9nU2VydmljZSgKICAgICAgICAgICAgICAgIGxhbmd1YWdlPWxhbmd1YWdlLAogICAgICAgICAgICAgICAgdG1kYl9hcGlfa2V5PXRtZGJfa2V5LAogICAgICAgICAgICApCgogICAgICAgICAgICBjYXRhbG9ncyA9IGF3YWl0IGR5bmFtaWNfY2F0YWxvZ19zZXJ2aWNlLmdldF9keW5hbWljX2NhdGFsb2dzKAogICAgICAgICAgICAgICAgbGlicmFyeV9pdGVtcz1saWJyYXJ5X2l0ZW1zLCB1c2VyX3NldHRpbmdzPXVzZXJfc2V0dGluZ3MsIHRva2VuPXRva2VuCiAgICAgICAgICAgICkKCiAgICAgICAgICAgIGxhbmcgPSB1c2VyX3NldHRpbmdzLmxhbmd1YWdlIGlmIHVzZXJfc2V0dGluZ3MgZWxzZSBOb25lCiAgICAgICAgICAgIGZvciBjYXQgaW4gY2F0YWxvZ3M6CiAgICAgICAgICAgICAgICBhd2FpdCBhcHBseV9jYXRhbG9nX3RyYW5zbGF0aW9uKGNhdCwgbGFuZykKCiAgICAgICAgICAgICMgc29ydCBjYXRhbG9ncyBieSBvcmRlciBpbiB1c2VyIHNldHRpbmdzCiAgICAgICAgICAgIGlmIHVzZXJfc2V0dGluZ3M6CiAgICAgICAgICAgICAgICBjYXRhbG9ncyA9IHNvcnRfY2F0YWxvZ3MoY2F0YWxvZ3MsIHVzZXJfc2V0dGluZ3MpCgogICAgICAgICAgICBzdWNjZXNzID0gYXdhaXQgYnVuZGxlLmFkZG9ucy51cGRhdGVfY2F0YWxvZ3MoYXV0aF9rZXksIGNhdGFsb2dzKQoKICAgICAgICAgICAgIyBVcGRhdGUgdGltZXN0YW1wIGFuZCBpbnZhbGlkYXRlIGNhY2hlIG9ubHkgb24gc3VjY2VzcwogICAgICAgICAgICBpZiBzdWNjZXNzIGFuZCB1cGRhdGVfdGltZXN0YW1wOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICMgVXBkYXRlIGxhc3RfdXBkYXRlZCB0aW1lc3RhbXAgdG8gY3VycmVudCB0aW1lCiAgICAgICAgICAgICAgICAgICAgIyBUaGlzIHJlcHJlc2VudHMgd2hlbiB0aGUgdXBkYXRlIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHkKICAgICAgICAgICAgICAgICAgICBub3cgPSBkYXRldGltZS5ub3codGltZXpvbmUudXRjKQogICAgICAgICAgICAgICAgICAgIGxhc3RfdXBkYXRlZF9zdHIgPSBub3cucmVwbGFjZShtaWNyb3NlY29uZD0wKS5pc29mb3JtYXQoKQogICAgICAgICAgICAgICAgICAgIGNyZWRlbnRpYWxzWyJsYXN0X3VwZGF0ZWQiXSA9IGxhc3RfdXBkYXRlZF9zdHIKICAgICAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBVcGRhdGVkIGxhc3RfdXBkYXRlZCB0aW1lc3RhbXAgdG8ge2xhc3RfdXBkYXRlZF9zdHJ9IikKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byB1cGRhdGUgbGFzdF91cGRhdGVkIHRpbWVzdGFtcDoge2V9IikKCiAgICAgICAgICAgIHJldHVybiBzdWNjZXNzCgogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgbG9nZ2VyLmV4Y2VwdGlvbihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byB1cGRhdGUgY2F0YWxvZ3MgaW4gYmFja2dyb3VuZDoge2V9IikKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZXJyb3JfbXNnID0gZiJGYWlsZWQgdG8gdXBkYXRlIGNhdGFsb2dzOiB7c3RyKGUpfSIKICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gKAogICAgICAgICAgICAgICAgICAgIGYiTW92aWUgYW5kIHNlcmllcyByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24geW91ciBTdHJlbWlvIGxpYnJhcnkuXG5cbuKaoO+4jyBTdGF0dXM6IEVycm9yXG57ZXJyb3JfbXNnfSIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5hZGRvbnMudXBkYXRlX2Rlc2NyaXB0aW9uKGF1dGhfa2V5LCBkZXNjcmlwdGlvbikKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyB1cGRhdGVfZXJyOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gdXBkYXRlIGFkZG9uIGRlc2NyaXB0aW9uIHdpdGggZXJyb3I6IHt1cGRhdGVfZXJyfSIpCiAgICAgICAgICAgIHJldHVybiBGYWxzZQogICAgICAgIGZpbmFsbHk6CiAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCgogICAgYXN5bmMgZGVmIF9yZWZyZXNoX3RyYWt0X2NhdGFsb2dzKAogICAgICAgIHNlbGYsIHRva2VuOiBzdHIsIGNyZWRlbnRpYWxzOiBkaWN0W3N0ciwgQW55XSwgdXBkYXRlX3RpbWVzdGFtcDogYm9vbCA9IFRydWUKICAgICkgLT4gYm9vbDoKICAgICAgICAiIiJSZWZyZXNoIGNhdGFsb2dzIGZvciBhIFRyYWt0LWJhY2tlZCBhY2NvdW50LiIiIgogICAgICAgIGZyb20gYXBwLmNvcmUuc2V0dGluZ3MgaW1wb3J0IHJlc29sdmVfdG1kYl9hcGlfa2V5CiAgICAgICAgZnJvbSBhcHAuc2VydmljZXMudHJha3Quc2VydmljZSBpbXBvcnQgVHJha3RCdW5kbGUKCiAgICAgICAgdXNlcl9zZXR0aW5ncyA9IE5vbmUKICAgICAgICBpZiBjcmVkZW50aWFscy5nZXQoInNldHRpbmdzIik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBVc2VyU2V0dGluZ3MoKipjcmVkZW50aWFsc1sic2V0dGluZ3MiXSkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gcGFyc2UgVHJha3QgdXNlciBzZXR0aW5nczoge2V9IikKICAgICAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgIGFjY2Vzc190b2tlbiA9IGNyZWRlbnRpYWxzLmdldCgiYXV0aEtleSIpICAjIHN0b3JlZCBhcyBhdXRoS2V5IGFmdGVyIGRlY3J5cHQKICAgICAgICBpZiBub3QgYWNjZXNzX3Rva2VuIG9yIG5vdCBzZXR0aW5ncy5UUkFLVF9DTElFTlRfSUQgb3Igbm90IHNldHRpbmdzLlRSQUtUX0NMSUVOVF9TRUNSRVQ6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVHJha3QgY3JlZGVudGlhbHMgbWlzc2luZywgc2tpcHBpbmcgcmVmcmVzaCIpCiAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgIHJlZGlyZWN0X3VyaSA9IGYie3NldHRpbmdzLkhPU1RfTkFNRX0vdG9rZW5zL3RyYWt0L2NhbGxiYWNrIgogICAgICAgIHRyYWt0X2J1bmRsZSA9IFRyYWt0QnVuZGxlKAogICAgICAgICAgICBjbGllbnRfaWQ9c2V0dGluZ3MuVFJBS1RfQ0xJRU5UX0lELAogICAgICAgICAgICBjbGllbnRfc2VjcmV0PXNldHRpbmdzLlRSQUtUX0NMSUVOVF9TRUNSRVQsCiAgICAgICAgICAgIHJlZGlyZWN0X3VyaT1yZWRpcmVjdF91cmksCiAgICAgICAgICAgIGFjY2Vzc190b2tlbj1hY2Nlc3NfdG9rZW4sCiAgICAgICAgKQogICAgICAgIHRyeToKICAgICAgICAgICAgbGlicmFyeV9pdGVtcyA9IGF3YWl0IHRyYWt0X2J1bmRsZS5saWJyYXJ5LmdldF9saWJyYXJ5X2l0ZW1zKCkKICAgICAgICAgICAgYXdhaXQgbWFuaWZlc3Rfc2VydmljZS5jYWNoZV9saWJyYXJ5X2FuZF9wcm9maWxlc19mcm9tX2l0ZW1zKGxpYnJhcnlfaXRlbXMsIHVzZXJfc2V0dGluZ3MsIHRva2VuKQoKICAgICAgICAgICAgaWYgdXBkYXRlX3RpbWVzdGFtcDoKICAgICAgICAgICAgICAgIG5vdyA9IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpCiAgICAgICAgICAgICAgICBjcmVkZW50aWFsc1sibGFzdF91cGRhdGVkIl0gPSBub3cucmVwbGFjZShtaWNyb3NlY29uZD0wKS5pc29mb3JtYXQoKQogICAgICAgICAgICAgICAgYXdhaXQgdG9rZW5fc3RvcmUudXBkYXRlX3VzZXJfZGF0YSh0b2tlbiwgY3JlZGVudGlhbHMpCgogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIFRyYWt0IGNhdGFsb2cgcmVmcmVzaCBjb21wbGV0ZSIpCiAgICAgICAgICAgIHJldHVybiBUcnVlCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVHJha3QgY2F0YWxvZyByZWZyZXNoIGZhaWxlZDoge2V9IikKICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgYXdhaXQgdHJha3RfYnVuZGxlLmNsb3NlKCkKCiAgICBhc3luYyBkZWYgdHJpZ2dlcl91cGRhdGUoc2VsZiwgdG9rZW46IHN0ciwgY3JlZGVudGlhbHM6IGRpY3Rbc3RyLCBBbnldKSAtPiBOb25lOgogICAgICAgICIiIgogICAgICAgIFRyaWdnZXIgYSBjYXRhbG9nIHVwZGF0ZSBpZiBuZWVkZWQuCiAgICAgICAgVGhpcyBmdW5jdGlvbiBjaGVja3MgaWYgdXBkYXRlIGlzIG5lZWRlZCBhbmQgZmlyZXMgYSBiYWNrZ3JvdW5kIHRhc2suCiAgICAgICAgVXNlcyBpbi1tZW1vcnkgbG9jayB0byBwcmV2ZW50IGR1cGxpY2F0ZSB1cGRhdGVzLgogICAgICAgICIiIgogICAgICAgICMgQ2hlY2sgaWYgYWxyZWFkeSB1cGRhdGluZwogICAgICAgIGlmIHRva2VuIGluIHNlbGYuX3VwZGF0aW5nX3Rva2VuczoKICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVXBkYXRlIGFscmVhZHkgaW4gcHJvZ3Jlc3MsIHNraXBwaW5nIikKICAgICAgICAgICAgcmV0dXJuCgogICAgICAgICMgQ2hlY2sgaWYgdXBkYXRlIGlzIG5lZWRlZAogICAgICAgIGlmIG5vdCBzZWxmLl9uZWVkc191cGRhdGUoY3JlZGVudGlhbHMpOgogICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBDYXRhbG9nIHVwZGF0ZSBub3QgbmVlZGVkIHlldCIpCiAgICAgICAgICAgIHJldHVybgoKICAgICAgICAjIEFkZCB0byBsb2NrIGFuZCBmaXJlIGJhY2tncm91bmQgdXBkYXRlCiAgICAgICAgc2VsZi5fdXBkYXRpbmdfdG9rZW5zLmFkZCh0b2tlbikKICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIFRyaWdnZXJpbmcgY2F0YWxvZyB1cGRhdGUiKQoKICAgICAgICAjIEZpcmUgYW5kIGZvcmdldCBiYWNrZ3JvdW5kIHRhc2sKICAgICAgICBhc3luY2lvLmNyZWF0ZV90YXNrKHNlbGYuX3VwZGF0ZV90YXNrKHRva2VuLCBjcmVkZW50aWFscykpCgogICAgYXN5bmMgZGVmIF91cGRhdGVfdGFzayhzZWxmLCB0b2tlbjogc3RyLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0pIC0+IE5vbmU6CiAgICAgICAgIiIiQmFja2dyb3VuZCB0YXNrIHRoYXQgcGVyZm9ybXMgdGhlIGFjdHVhbCBjYXRhbG9nIHVwZGF0ZS4iIiIKICAgICAgICB0cnk6CiAgICAgICAgICAgIHN1Y2Nlc3MgPSBhd2FpdCBzZWxmLnJlZnJlc2hfY2F0YWxvZ3NfZm9yX2NyZWRlbnRpYWxzKHRva2VuLCBjcmVkZW50aWFscywgdXBkYXRlX3RpbWVzdGFtcD1UcnVlKQogICAgICAgICAgICBpZiBzdWNjZXNzOgogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBDYXRhbG9nIHVwZGF0ZSBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5IikKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gQ2F0YWxvZyB1cGRhdGUgY29tcGxldGVkIHdpdGggZmFpbHVyZSIpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gQ2F0YWxvZyB1cGRhdGUgdGFzayBmYWlsZWQ6IHtlfSIpCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgIyBBbHdheXMgcmVtb3ZlIGZyb20gbG9jawogICAgICAgICAgICBzZWxmLl91cGRhdGluZ190b2tlbnMuZGlzY2FyZCh0b2tlbikKCgpsb2dnZXIuaW5mbyhmIkNhdGFsb2cgdXBkYXRlciBpbml0aWFsaXplZCB3aXRoIHJlZnJlc2ggaW50ZXJ2YWwgb2Yge3NldHRpbmdzLkNBVEFMT0dfUkVGUkVTSF9JTlRFUlZBTF9TRUNPTkRTfSBzZWNvbmRzIikKY2F0YWxvZ191cGRhdGVyID0gQ2F0YWxvZ1VwZGF0ZXIoKQo=" | base64 --decode > "$REPO/app/services/catalog_updater.py" +echo " wrote app/services/catalog_updater.py" + +mkdir -p "$REPO/app/services" +echo "aW1wb3J0IGJhc2U2NAppbXBvcnQganNvbgpmcm9tIHR5cGluZyBpbXBvcnQgQW55CgppbXBvcnQgcmVkaXMuYXN5bmNpbyBhcyByZWRpcwpmcm9tIGFzeW5jX2xydSBpbXBvcnQgYWxydV9jYWNoZQpmcm9tIGNyeXB0b2dyYXBoeS5mZXJuZXQgaW1wb3J0IEZlcm5ldApmcm9tIGNyeXB0b2dyYXBoeS5oYXptYXQucHJpbWl0aXZlcyBpbXBvcnQgaGFzaGVzCmZyb20gY3J5cHRvZ3JhcGh5Lmhhem1hdC5wcmltaXRpdmVzLmtkZi5wYmtkZjIgaW1wb3J0IFBCS0RGMkhNQUMKZnJvbSBsb2d1cnUgaW1wb3J0IGxvZ2dlcgoKZnJvbSBhcHAuY29yZS5jb25maWcgaW1wb3J0IHNldHRpbmdzCmZyb20gYXBwLmNvcmUuc2VjdXJpdHkgaW1wb3J0IHJlZGFjdF90b2tlbgpmcm9tIGFwcC5zZXJ2aWNlcy5yZWRpc19zZXJ2aWNlIGltcG9ydCByZWRpc19zZXJ2aWNlCmZyb20gYXBwLnNlcnZpY2VzLnVzZXJfY2FjaGUgaW1wb3J0IHVzZXJfY2FjaGUKCgpjbGFzcyBUb2tlblN0b3JlOgogICAgIiIiUmVkaXMtYmFja2VkIHN0b3JlIGZvciB1c2VyIGNyZWRlbnRpYWxzIGFuZCBhdXRoIHRva2Vucy4iIiIKCiAgICBLRVlfUFJFRklYID0gc2V0dGluZ3MuUkVESVNfVE9LRU5fS0VZCgogICAgZGVmIF9faW5pdF9fKHNlbGYpIC0+IE5vbmU6CiAgICAgICAgaWYgbm90IHNldHRpbmdzLlRPS0VOX1NBTFQgb3Igc2V0dGluZ3MuVE9LRU5fU0FMVCA9PSAiY2hhbmdlLW1lIjoKICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoCiAgICAgICAgICAgICAgICAiVE9LRU5fU0FMVCBpcyBtaXNzaW5nIG9yIHVzaW5nIHRoZSBkZWZhdWx0IHBsYWNlaG9sZGVyLiBTZXQgYSBzdHJvbmcgdmFsdWUgdG8gc2VjdXJlIHRva2Vucy4iCiAgICAgICAgICAgICkKCiAgICBkZWYgX2Vuc3VyZV9zZWN1cmVfc2FsdChzZWxmKSAtPiBOb25lOgogICAgICAgIGlmIG5vdCBzZXR0aW5ncy5UT0tFTl9TQUxUIG9yIHNldHRpbmdzLlRPS0VOX1NBTFQgPT0gImNoYW5nZS1tZSI6CiAgICAgICAgICAgIGxvZ2dlci5lcnJvcigiVE9LRU5fU0FMVCBpcyB1bnNldCBvciB1c2luZyB0aGUgaW5zZWN1cmUgZGVmYXVsdC4iKQogICAgICAgICAgICByYWlzZSBSdW50aW1lRXJyb3IoIlRPS0VOX1NBTFQgbXVzdCBiZSBzZXQgdG8gYSBub24tZGVmYXVsdCB2YWx1ZSBiZWZvcmUgc3RvcmluZyBjcmVkZW50aWFscy4iKQoKICAgIGRlZiBfZ2V0X2NpcGhlcihzZWxmKSAtPiBGZXJuZXQ6CiAgICAgICAgc2FsdCA9IGIieDdGRGY5a3lwelExTG1SMzJiOGhXdjQ5c0txMlBkOFQiCiAgICAgICAga2RmID0gUEJLREYySE1BQygKICAgICAgICAgICAgYWxnb3JpdGhtPWhhc2hlcy5TSEEyNTYoKSwKICAgICAgICAgICAgbGVuZ3RoPTMyLAogICAgICAgICAgICBzYWx0PXNhbHQsCiAgICAgICAgICAgIGl0ZXJhdGlvbnM9MjAwXzAwMCwKICAgICAgICApCgogICAgICAgIGtleSA9IGJhc2U2NC51cmxzYWZlX2I2NGVuY29kZShrZGYuZGVyaXZlKHNldHRpbmdzLlRPS0VOX1NBTFQuZW5jb2RlKCJ1dGYtOCIpKSkKICAgICAgICByZXR1cm4gRmVybmV0KGtleSkKCiAgICBkZWYgZW5jcnlwdF90b2tlbihzZWxmLCB0b2tlbjogc3RyKSAtPiBzdHI6CiAgICAgICAgY2lwaGVyID0gc2VsZi5fZ2V0X2NpcGhlcigpCiAgICAgICAgcmV0dXJuIGNpcGhlci5lbmNyeXB0KHRva2VuLmVuY29kZSgidXRmLTgiKSkuZGVjb2RlKCJ1dGYtOCIpCgogICAgZGVmIGRlY3J5cHRfdG9rZW4oc2VsZiwgZW5jOiBzdHIpIC0+IHN0cjoKICAgICAgICBjaXBoZXIgPSBzZWxmLl9nZXRfY2lwaGVyKCkKICAgICAgICByZXR1cm4gY2lwaGVyLmRlY3J5cHQoZW5jLmVuY29kZSgidXRmLTgiKSkuZGVjb2RlKCJ1dGYtOCIpCgogICAgZGVmIF9mb3JtYXRfa2V5KHNlbGYsIHRva2VuOiBzdHIpIC0+IHN0cjoKICAgICAgICAiIiJGb3JtYXQgUmVkaXMga2V5IGZyb20gdG9rZW4uIiIiCiAgICAgICAgcmV0dXJuIGYie3NlbGYuS0VZX1BSRUZJWH17dG9rZW59IgoKICAgIGRlZiBnZXRfdG9rZW5fZnJvbV91c2VyX2lkKHNlbGYsIHVzZXJfaWQ6IHN0cikgLT4gc3RyOgogICAgICAgIHJldHVybiB1c2VyX2lkLnN0cmlwKCkKCiAgICBkZWYgZ2V0X3VzZXJfaWRfZnJvbV90b2tlbihzZWxmLCB0b2tlbjogc3RyKSAtPiBzdHI6CiAgICAgICAgcmV0dXJuIHRva2VuLnN0cmlwKCkgaWYgdG9rZW4gZWxzZSAiIgoKICAgIGFzeW5jIGRlZiBzdG9yZV91c2VyX2RhdGEoc2VsZiwgdXNlcl9pZDogc3RyLCBwYXlsb2FkOiBkaWN0W3N0ciwgQW55XSkgLT4gc3RyOgogICAgICAgIHNlbGYuX2Vuc3VyZV9zZWN1cmVfc2FsdCgpCiAgICAgICAgdG9rZW4gPSBzZWxmLmdldF90b2tlbl9mcm9tX3VzZXJfaWQodXNlcl9pZCkKICAgICAgICBrZXkgPSBzZWxmLl9mb3JtYXRfa2V5KHRva2VuKQoKICAgICAgICAjIFByZXBhcmUgZGF0YSBmb3Igc3RvcmFnZSAoUGxhaW4gSlNPTiwgbm8gZW5jcnlwdGlvbiBuZWVkZWQpCiAgICAgICAgc3RvcmFnZV9kYXRhID0gcGF5bG9hZC5jb3B5KCkKCiAgICAgICAgIyBTdG9yZSB1c2VyX2lkIGluIHBheWxvYWQgZm9yIGNvbnZlbmllbmNlCiAgICAgICAgc3RvcmFnZV9kYXRhWyJ1c2VyX2lkIl0gPSB1c2VyX2lkCgogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoImF1dGhLZXkiKToKICAgICAgICAgICAgc3RvcmFnZV9kYXRhWyJhdXRoS2V5Il0gPSBzZWxmLmVuY3J5cHRfdG9rZW4oc3RvcmFnZV9kYXRhWyJhdXRoS2V5Il0pCgogICAgICAgICMgRW5jcnlwdCBUcmFrdCByZWZyZXNoIHRva2VuIGlmIHByZXNlbnQKICAgICAgICBpZiBzdG9yYWdlX2RhdGEuZ2V0KCJ0cmFrdF9yZWZyZXNoX3Rva2VuIik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGlmIG5vdCBzdG9yYWdlX2RhdGFbInRyYWt0X3JlZnJlc2hfdG9rZW4iXS5zdGFydHN3aXRoKCJnQUFBQUFCIik6CiAgICAgICAgICAgICAgICAgICAgc3RvcmFnZV9kYXRhWyJ0cmFrdF9yZWZyZXNoX3Rva2VuIl0gPSBzZWxmLmVuY3J5cHRfdG9rZW4oc3RvcmFnZV9kYXRhWyJ0cmFrdF9yZWZyZXNoX3Rva2VuIl0pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJGYWlsZWQgdG8gZW5jcnlwdCB0cmFrdF9yZWZyZXNoX3Rva2VuOiB7ZXhjfSIpCgogICAgICAgICMgU2VjdXJlbHkgc3RvcmUgcGFzc3dvcmQgaWYgcHJvdmlkZWQgKHByaW1hcnkgbG9naW4gbW9kZSkKICAgICAgICBpZiBzdG9yYWdlX2RhdGEuZ2V0KCJwYXNzd29yZCIpOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBzdG9yYWdlX2RhdGFbInBhc3N3b3JkIl0gPSBzZWxmLmVuY3J5cHRfdG9rZW4oc3RvcmFnZV9kYXRhWyJwYXNzd29yZCJdKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcihmIlBhc3N3b3JkIGVuY3J5cHRpb24gZmFpbGVkIGZvciB7cmVkYWN0X3Rva2VuKHVzZXJfaWQpfToge2V4Y30iKQogICAgICAgICAgICAgICAgIyBEbyBub3Qgc3RvcmUgcGxhaW50ZXh0IHBhc3N3b3JkcwogICAgICAgICAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKCJQQVNTV09SRF9FTkNSWVBUX0ZBSUxFRCIpCgogICAgICAgICMgRW5jcnlwdCBwb3N0ZXJfcmF0aW5nIEFQSSBrZXkgaWYgcHJlc2VudAogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoInNldHRpbmdzIikgYW5kIGlzaW5zdGFuY2Uoc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLCBkaWN0KToKICAgICAgICAgICAgcG9zdGVyX3JhdGluZyA9IHN0b3JhZ2VfZGF0YVsic2V0dGluZ3MiXS5nZXQoInBvc3Rlcl9yYXRpbmciKQogICAgICAgICAgICBpZiBwb3N0ZXJfcmF0aW5nIGFuZCBpc2luc3RhbmNlKHBvc3Rlcl9yYXRpbmcsIGRpY3QpIGFuZCBwb3N0ZXJfcmF0aW5nLmdldCgiYXBpX2tleSIpOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICMgT25seSBlbmNyeXB0IGlmIGl0J3Mgbm90IGFscmVhZHkgZW5jcnlwdGVkIChjaGVjayBpZiBpdCdzIGEgdmFsaWQgZW5jcnlwdGVkIHN0cmluZykKICAgICAgICAgICAgICAgICAgICBhcGlfa2V5ID0gcG9zdGVyX3JhdGluZ1siYXBpX2tleSJdCiAgICAgICAgICAgICAgICAgICAgIyBTaW1wbGUgY2hlY2s6IGVuY3J5cHRlZCB0b2tlbnMgYXJlIGJhc2U2NC1saWtlIGFuZCBsb25nZXIKICAgICAgICAgICAgICAgICAgICAjIElmIGl0IGxvb2tzIGxpa2UgcGxhaW50ZXh0LCBlbmNyeXB0IGl0CiAgICAgICAgICAgICAgICAgICAgIyBGZXJuZXQgZW5jcnlwdGVkIHRva2VucyBzdGFydCB3aXRoICJnQUFBQUFCIgogICAgICAgICAgICAgICAgICAgIGlmIG5vdCBhcGlfa2V5LnN0YXJ0c3dpdGgoImdBQUFBQUIiKToKICAgICAgICAgICAgICAgICAgICAgICAgcG9zdGVyX3JhdGluZ1siYXBpX2tleSJdID0gc2VsZi5lbmNyeXB0X3Rva2VuKGFwaV9rZXkpCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBlbmNyeXB0IHBvc3Rlcl9yYXRpbmcgYXBpX2tleSBmb3Ige3JlZGFjdF90b2tlbih1c2VyX2lkKX06IHtleGN9IikKCiAgICAgICAgIyBFbmNyeXB0IHNpbWtsX2FwaV9rZXkgaWYgcHJlc2VudAogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoInNldHRpbmdzIikgYW5kIGlzaW5zdGFuY2Uoc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLCBkaWN0KToKICAgICAgICAgICAgc2lta2xfYXBpX2tleSA9IHN0b3JhZ2VfZGF0YVsic2V0dGluZ3MiXS5nZXQoInNpbWtsX2FwaV9rZXkiKQogICAgICAgICAgICBpZiBzaW1rbF9hcGlfa2V5OgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGlmIG5vdCBzaW1rbF9hcGlfa2V5LnN0YXJ0c3dpdGgoImdBQUFBQUIiKToKICAgICAgICAgICAgICAgICAgICAgICAgc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdWyJzaW1rbF9hcGlfa2V5Il0gPSBzZWxmLmVuY3J5cHRfdG9rZW4oc2lta2xfYXBpX2tleSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGVuY3J5cHQgc2lta2xfYXBpX2tleSBmb3Ige3JlZGFjdF90b2tlbih1c2VyX2lkKX06IHtleGN9IikKCiAgICAgICAgIyBFbmNyeXB0IGdlbWluaV9hcGlfa2V5IGlmIHByZXNlbnQKICAgICAgICBpZiBzdG9yYWdlX2RhdGEuZ2V0KCJzZXR0aW5ncyIpIGFuZCBpc2luc3RhbmNlKHN0b3JhZ2VfZGF0YVsic2V0dGluZ3MiXSwgZGljdCk6CiAgICAgICAgICAgIGdlbWluaV9hcGlfa2V5ID0gc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLmdldCgiZ2VtaW5pX2FwaV9rZXkiKQogICAgICAgICAgICBpZiBnZW1pbmlfYXBpX2tleToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBpZiBub3QgZ2VtaW5pX2FwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBQiIpOgogICAgICAgICAgICAgICAgICAgICAgICBzdG9yYWdlX2RhdGFbInNldHRpbmdzIl1bImdlbWluaV9hcGlfa2V5Il0gPSBzZWxmLmVuY3J5cHRfdG9rZW4oZ2VtaW5pX2FwaV9rZXkpCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBlbmNyeXB0IGdlbWluaV9hcGlfa2V5IGZvciB7cmVkYWN0X3Rva2VuKHVzZXJfaWQpfToge2V4Y30iKQoKICAgICAgICAjIEVuY3J5cHQgdG1kYl9hcGlfa2V5IGlmIHByZXNlbnQKICAgICAgICBpZiBzdG9yYWdlX2RhdGEuZ2V0KCJzZXR0aW5ncyIpIGFuZCBpc2luc3RhbmNlKHN0b3JhZ2VfZGF0YVsic2V0dGluZ3MiXSwgZGljdCk6CiAgICAgICAgICAgIHRtZGJfYXBpX2tleSA9IHN0b3JhZ2VfZGF0YVsic2V0dGluZ3MiXS5nZXQoInRtZGJfYXBpX2tleSIpCiAgICAgICAgICAgIGlmIHRtZGJfYXBpX2tleToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBpZiBub3QgdG1kYl9hcGlfa2V5LnN0YXJ0c3dpdGgoImdBQUFBQUIiKToKICAgICAgICAgICAgICAgICAgICAgICAgc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdWyJ0bWRiX2FwaV9rZXkiXSA9IHNlbGYuZW5jcnlwdF90b2tlbih0bWRiX2FwaV9rZXkpCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBlbmNyeXB0IHRtZGJfYXBpX2tleSBmb3Ige3JlZGFjdF90b2tlbih1c2VyX2lkKX06IHtleGN9IikKICAgICAgICBqc29uX3N0ciA9IGpzb24uZHVtcHMoc3RvcmFnZV9kYXRhKQoKICAgICAgICBpZiBzZXR0aW5ncy5UT0tFTl9UVExfU0VDT05EUyBhbmQgc2V0dGluZ3MuVE9LRU5fVFRMX1NFQ09ORFMgPiAwOgogICAgICAgICAgICBhd2FpdCByZWRpc19zZXJ2aWNlLnNldChrZXksIGpzb25fc3RyLCBzZXR0aW5ncy5UT0tFTl9UVExfU0VDT05EUykKICAgICAgICBlbHNlOgogICAgICAgICAgICBhd2FpdCByZWRpc19zZXJ2aWNlLnNldChrZXksIGpzb25fc3RyKQoKICAgICAgICAjIEludmFsaWRhdGUgYXN5bmMgTFJVIGNhY2hlIGZvciBmcmVzaCByZWFkcyBvbiBzdWJzZXF1ZW50IHJlcXVlc3RzCiAgICAgICAgdHJ5OgogICAgICAgICAgICBzZWxmLl9nZXRfdXNlcl9kYXRhX2NhY2hlZC5jYWNoZV9pbnZhbGlkYXRlKHRva2VuKQogICAgICAgIGV4Y2VwdCBLZXlFcnJvcjoKICAgICAgICAgICAgcGFzcwogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJUYXJnZXRlZCBjYWNoZSBpbnZhbGlkYXRpb24gZmFpbGVkOiB7ZX0uIEZhbGxpbmcgYmFjayB0byBjbGVhcmluZyBjYWNoZS4iKQogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBzZWxmLl9nZXRfdXNlcl9kYXRhX2NhY2hlZC5jYWNoZV9jbGVhcigpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZV9jbGVhcjoKICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcihmIkVycm9yIHdoaWxlIGNsZWFyaW5nIGNhY2hlOiB7ZV9jbGVhcn0iKQoKICAgICAgICByZXR1cm4gdG9rZW4KCiAgICBhc3luYyBkZWYgdXBkYXRlX3VzZXJfZGF0YShzZWxmLCB0b2tlbjogc3RyLCBwYXlsb2FkOiBkaWN0W3N0ciwgQW55XSkgLT4gc3RyOgogICAgICAgICIiIlVwZGF0ZSB1c2VyIGRhdGEgYnkgdG9rZW4uIFRoaXMgaXMgYSBjb252ZW5pZW5jZSB3cmFwcGVyIGFyb3VuZCBzdG9yZV91c2VyX2RhdGEuIiIiCiAgICAgICAgdXNlcl9pZCA9IHNlbGYuZ2V0X3VzZXJfaWRfZnJvbV90b2tlbih0b2tlbikKICAgICAgICByZXR1cm4gYXdhaXQgc2VsZi5zdG9yZV91c2VyX2RhdGEodXNlcl9pZCwgcGF5bG9hZCkKCiAgICBhc3luYyBkZWYgX21pZ3JhdGVfcG9zdGVyX3JhdGluZ19mb3JtYXRfcmF3KHNlbGYsIHRva2VuOiBzdHIsIHJlZGlzX2tleTogc3RyLCBkYXRhOiBkaWN0KSAtPiBkaWN0IHwgTm9uZToKICAgICAgICAiIiJNaWdyYXRlIG9sZCBycGRiX2tleSBmb3JtYXQgdG8gbmV3IHBvc3Rlcl9yYXRpbmcgZm9ybWF0IGluIHJhdyBSZWRpcyBkYXRhIGlmIG5lZWRlZC4iIiIKICAgICAgICBpZiBub3QgZGF0YToKICAgICAgICAgICAgcmV0dXJuIE5vbmUKCiAgICAgICAgc2V0dGluZ3NfZGljdCA9IGRhdGEuZ2V0KCJzZXR0aW5ncyIpCiAgICAgICAgaWYgbm90IHNldHRpbmdzX2RpY3Qgb3Igbm90IGlzaW5zdGFuY2Uoc2V0dGluZ3NfZGljdCwgZGljdCk6CiAgICAgICAgICAgIHJldHVybiBOb25lCgogICAgICAgIHJwZGJfa2V5ID0gc2V0dGluZ3NfZGljdC5nZXQoInJwZGJfa2V5IikKICAgICAgICBwb3N0ZXJfcmF0aW5nID0gc2V0dGluZ3NfZGljdC5nZXQoInBvc3Rlcl9yYXRpbmciKQogICAgICAgIG5lZWRzX3NhdmUgPSBGYWxzZQoKICAgICAgICAjIENhc2UgMTogTWlncmF0ZSBycGRiX2tleSB0byBwb3N0ZXJfcmF0aW5nIGlmIHJwZGJfa2V5IGV4aXN0cyBhbmQgcG9zdGVyX3JhdGluZyBkb2Vzbid0CiAgICAgICAgaWYgcnBkYl9rZXkgYW5kIG5vdCBwb3N0ZXJfcmF0aW5nOgogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIltNSUdSQVRJT05dIE1pZ3JhdGluZyBycGRiX2tleSB0byBwb3N0ZXJfcmF0aW5nIGZvcm1hdCBmb3Ige3JlZGFjdF90b2tlbih0b2tlbil9IikKICAgICAgICAgICAgc2V0dGluZ3NfZGljdFsicG9zdGVyX3JhdGluZyJdID0gewogICAgICAgICAgICAgICAgInByb3ZpZGVyIjogInJwZGIiLAogICAgICAgICAgICAgICAgImFwaV9rZXkiOiBzZWxmLmVuY3J5cHRfdG9rZW4ocnBkYl9rZXkpLCAgIyBFbmNyeXB0IHRoZSBBUEkga2V5CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbmVlZHNfc2F2ZSA9IFRydWUKCiAgICAgICAgIyBDYXNlIDI6IENsZWFuIHVwIGRlcHJlY2F0ZWQgcnBkYl9rZXkgZmllbGQgaWYgaXQgZXhpc3RzIChldmVuIGlmIGVtcHR5L251bGwpCiAgICAgICAgIyBSZW1vdmUgaXQgc2luY2Ugd2UndmUgbWlncmF0ZWQgdG8gcG9zdGVyX3JhdGluZyBvciBpdCdzIG5vIGxvbmdlciBuZWVkZWQKICAgICAgICBpZiAicnBkYl9rZXkiIGluIHNldHRpbmdzX2RpY3Q6CiAgICAgICAgICAgIHNldHRpbmdzX2RpY3QucG9wKCJycGRiX2tleSIpCiAgICAgICAgICAgICMga2VlcCBlbXB0eSBwb3N0ZXJfcmF0aW5nIGZpZWxkIGZvciBub3cKICAgICAgICAgICAgc2V0dGluZ3NfZGljdFsicG9zdGVyX3JhdGluZyJdID0gewogICAgICAgICAgICAgICAgInByb3ZpZGVyIjogInJwZGIiLAogICAgICAgICAgICAgICAgImFwaV9rZXkiOiBOb25lLAogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIG5vdCBuZWVkc19zYXZlOiAgIyBPbmx5IGxvZyBpZiB3ZSBkaWRuJ3QgYWxyZWFkeSBsb2cgbWlncmF0aW9uCiAgICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhmIltNSUdSQVRJT05dIFJlbW92aW5nIGRlcHJlY2F0ZWQgcnBkYl9rZXkgZmllbGQgZm9yIHtyZWRhY3RfdG9rZW4odG9rZW4pfSIpCiAgICAgICAgICAgIG5lZWRzX3NhdmUgPSBUcnVlCgogICAgICAgICMgU2F2ZSBiYWNrIHRvIHJlZGlzIGlmIGFueSBjaGFuZ2VzIHdlcmUgbWFkZQogICAgICAgIGlmIG5lZWRzX3NhdmU6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGlmIHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTIGFuZCBzZXR0aW5ncy5UT0tFTl9UVExfU0VDT05EUyA+IDA6CiAgICAgICAgICAgICAgICAgICAgYXdhaXQgcmVkaXNfc2VydmljZS5zZXQocmVkaXNfa2V5LCBqc29uLmR1bXBzKGRhdGEpLCBzZXR0aW5ncy5UT0tFTl9UVExfU0VDT05EUykKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgYXdhaXQgcmVkaXNfc2VydmljZS5zZXQocmVkaXNfa2V5LCBqc29uLmR1bXBzKGRhdGEpKQoKICAgICAgICAgICAgICAgICMgSW52YWxpZGF0ZSBjYWNoZSBzbyBuZXh0IHJlYWQgZ2V0cyB0aGUgbWlncmF0ZWQgZGF0YQogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIHNlbGYuZ2V0X3VzZXJfZGF0YS5jYWNoZV9pbnZhbGlkYXRlKHRva2VuKQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgICAgICBwYXNzCgogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oCiAgICAgICAgICAgICAgICAgICAgIltNSUdSQVRJT05dIFN1Y2Nlc3NmdWxseSBtaWdyYXRlZCBhbmQgZW5jcnlwdGVkIHBvc3Rlcl9yYXRpbmcgIiBmImZvcm1hdCBmb3Ige3JlZGFjdF90b2tlbih0b2tlbil9IgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgcmV0dXJuIGRhdGEKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbTUlHUkFUSU9OXSBGYWlsZWQgdG8gc2F2ZSBtaWdyYXRlZCBkYXRhIGZvciB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCiAgICAgICAgICAgICAgICByZXR1cm4gTm9uZQoKICAgICAgICByZXR1cm4gTm9uZQoKICAgIGFzeW5jIGRlZiBnZXRfdXNlcl9kYXRhKHNlbGYsIHRva2VuOiBzdHIpIC0+IGRpY3Rbc3RyLCBBbnldIHwgTm9uZToKICAgICAgICBkYXRhID0gYXdhaXQgc2VsZi5fZ2V0X3VzZXJfZGF0YV9jYWNoZWQodG9rZW4pCiAgICAgICAgaWYgZGF0YSBpcyBOb25lOgogICAgICAgICAgICAjIERvbid0IGxldCBhIG1pc3NpbmctdG9rZW4gcmVzdWx0IGdldCBwaW5uZWQgaW4gdGhlIHBlci1wcm9jZXNzIGNhY2hlOwogICAgICAgICAgICAjIG90aGVyd2lzZSBhIHRva2VuIGNyZWF0ZWQgb24gYW5vdGhlciB3b3JrZXIgd291bGQgNDAxIGhlcmUgZm9yIGhvdXJzLgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBzZWxmLl9nZXRfdXNlcl9kYXRhX2NhY2hlZC5jYWNoZV9pbnZhbGlkYXRlKHRva2VuKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICAgICAgcGFzcwogICAgICAgIHJldHVybiBkYXRhCgogICAgQGFscnVfY2FjaGUobWF4c2l6ZT0yMDAwLCB0dGw9NDMyMDApCiAgICBhc3luYyBkZWYgX2dldF91c2VyX2RhdGFfY2FjaGVkKHNlbGYsIHRva2VuOiBzdHIpIC0+IGRpY3Rbc3RyLCBBbnldIHwgTm9uZToKICAgICAgICBsb2dnZXIuZGVidWcoZiJbUkVESVNdIENhY2hlIG1pc3MuIEZldGNoaW5nIGRhdGEgZnJvbSByZWRpcyBmb3Ige3Rva2VufSIpCiAgICAgICAga2V5ID0gc2VsZi5fZm9ybWF0X2tleSh0b2tlbikKICAgICAgICBkYXRhX3JhdyA9IGF3YWl0IHJlZGlzX3NlcnZpY2UuZ2V0KGtleSkKCiAgICAgICAgaWYgbm90IGRhdGFfcmF3OgogICAgICAgICAgICByZXR1cm4gTm9uZQoKICAgICAgICB0cnk6CiAgICAgICAgICAgIGRhdGEgPSBqc29uLmxvYWRzKGRhdGFfcmF3KQogICAgICAgIGV4Y2VwdCBqc29uLkpTT05EZWNvZGVFcnJvcjoKICAgICAgICAgICAgcmV0dXJuIE5vbmUKCiAgICAgICAgdXBkYXRlZF9kYXRhID0gYXdhaXQgc2VsZi5fbWlncmF0ZV9wb3N0ZXJfcmF0aW5nX2Zvcm1hdF9yYXcodG9rZW4sIGtleSwgZGF0YSkKICAgICAgICBpZiB1cGRhdGVkX2RhdGE6CiAgICAgICAgICAgIGRhdGEgPSB1cGRhdGVkX2RhdGEKCiAgICAgICAgIyBEZWNyeXB0IGZpZWxkcyBpbmRpdmlkdWFsbHk7IGRvIG5vdCBmYWlsIGVudGlyZSByZWNvcmQgb24gZGVjcnlwdGlvbiBlcnJvcnMKICAgICAgICBpZiBkYXRhLmdldCgiYXV0aEtleSIpOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBkYXRhWyJhdXRoS2V5Il0gPSBzZWxmLmRlY3J5cHRfdG9rZW4oZGF0YVsiYXV0aEtleSJdKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkRlY3J5cHRpb24gZmFpbGVkIGZvciBhdXRoS2V5IGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCiAgICAgICAgICAgICAgICAjIExlYXZlIGFzLWlzIChsZWdhY3kgcGxhaW50ZXh0IG9yIHByZXZpb3VzIGZhaWx1cmUpCiAgICAgICAgICAgICAgICBwYXNzCgogICAgICAgIGlmIGRhdGEuZ2V0KCJ0cmFrdF9yZWZyZXNoX3Rva2VuIik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGlmIGRhdGFbInRyYWt0X3JlZnJlc2hfdG9rZW4iXS5zdGFydHN3aXRoKCJnQUFBQUEiKToKICAgICAgICAgICAgICAgICAgICBkYXRhWyJ0cmFrdF9yZWZyZXNoX3Rva2VuIl0gPSBzZWxmLmRlY3J5cHRfdG9rZW4oZGF0YVsidHJha3RfcmVmcmVzaF90b2tlbiJdKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJEZWNyeXB0aW9uIGZhaWxlZCBmb3IgdHJha3RfcmVmcmVzaF90b2tlbiBhc3NvY2lhdGVkIHdpdGgge3JlZGFjdF90b2tlbih0b2tlbil9OiB7ZX0iKQogICAgICAgIGlmIGRhdGEuZ2V0KCJwYXNzd29yZCIpOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBkYXRhWyJwYXNzd29yZCJdID0gc2VsZi5kZWNyeXB0X3Rva2VuKGRhdGFbInBhc3N3b3JkIl0pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRGVjcnlwdGlvbiBmYWlsZWQgZm9yIHBhc3N3b3JkIGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCiAgICAgICAgICAgICAgICAjIHJlcXVpcmUgcmUtbG9naW4gcGF0aCB3aGVuIG5lZWRlZAogICAgICAgICAgICAgICAgZGF0YVsicGFzc3dvcmQiXSA9IE5vbmUKCiAgICAgICAgIyBEZWNyeXB0IHBvc3Rlcl9yYXRpbmcgQVBJIGtleSBpZiBwcmVzZW50CiAgICAgICAgaWYgZGF0YS5nZXQoInNldHRpbmdzIikgYW5kIGlzaW5zdGFuY2UoZGF0YVsic2V0dGluZ3MiXSwgZGljdCk6CiAgICAgICAgICAgIHBvc3Rlcl9yYXRpbmcgPSBkYXRhWyJzZXR0aW5ncyJdLmdldCgicG9zdGVyX3JhdGluZyIpCiAgICAgICAgICAgIGlmIHBvc3Rlcl9yYXRpbmcgYW5kIGlzaW5zdGFuY2UocG9zdGVyX3JhdGluZywgZGljdCkgYW5kIHBvc3Rlcl9yYXRpbmcuZ2V0KCJhcGlfa2V5Iik6CiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgaWYgcG9zdGVyX3JhdGluZ1siYXBpX2tleSJdLnN0YXJ0c3dpdGgoImdBQUFBQSIpOgogICAgICAgICAgICAgICAgICAgICAgICBwb3N0ZXJfcmF0aW5nWyJhcGlfa2V5Il0gPSBzZWxmLmRlY3J5cHRfdG9rZW4ocG9zdGVyX3JhdGluZ1siYXBpX2tleSJdKQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZygKICAgICAgICAgICAgICAgICAgICAgICAgZiJEZWNyeXB0aW9uIGZhaWxlZCBmb3IgcG9zdGVyX3JhdGluZyBhcGlfa2V5IGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIKICAgICAgICAgICAgICAgICAgICApCgogICAgICAgICAgICBzaW1rbF9hcGlfa2V5ID0gZGF0YVsic2V0dGluZ3MiXS5nZXQoInNpbWtsX2FwaV9rZXkiKQogICAgICAgICAgICBpZiBzaW1rbF9hcGlfa2V5OgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGlmIHNpbWtsX2FwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBIik6CiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFbInNldHRpbmdzIl1bInNpbWtsX2FwaV9rZXkiXSA9IHNlbGYuZGVjcnlwdF90b2tlbihzaW1rbF9hcGlfa2V5KQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIkRlY3J5cHRpb24gZmFpbGVkIGZvciBzaW1rbF9hcGlfa2V5IGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCgogICAgICAgICAgICBnZW1pbmlfYXBpX2tleSA9IGRhdGFbInNldHRpbmdzIl0uZ2V0KCJnZW1pbmlfYXBpX2tleSIpCiAgICAgICAgICAgIGlmIGdlbWluaV9hcGlfa2V5OgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGlmIGdlbWluaV9hcGlfa2V5LnN0YXJ0c3dpdGgoImdBQUFBQSIpOgogICAgICAgICAgICAgICAgICAgICAgICBkYXRhWyJzZXR0aW5ncyJdWyJnZW1pbmlfYXBpX2tleSJdID0gc2VsZi5kZWNyeXB0X3Rva2VuKGdlbWluaV9hcGlfa2V5KQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIkRlY3J5cHRpb24gZmFpbGVkIGZvciBnZW1pbmlfYXBpX2tleSBhc3NvY2lhdGVkIHdpdGgge3JlZGFjdF90b2tlbih0b2tlbil9OiB7ZX0iKQoKICAgICAgICAgICAgdG1kYl9hcGlfa2V5ID0gZGF0YVsic2V0dGluZ3MiXS5nZXQoInRtZGJfYXBpX2tleSIpCiAgICAgICAgICAgIGlmIHRtZGJfYXBpX2tleToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBpZiB0bWRiX2FwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBIik6CiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFbInNldHRpbmdzIl1bInRtZGJfYXBpX2tleSJdID0gc2VsZi5kZWNyeXB0X3Rva2VuKHRtZGJfYXBpX2tleSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJEZWNyeXB0aW9uIGZhaWxlZCBmb3IgdG1kYl9hcGlfa2V5IGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCgogICAgICAgIHJldHVybiBkYXRhCgogICAgYXN5bmMgZGVmIGRlbGV0ZV90b2tlbihzZWxmLCB0b2tlbjogc3RyID0gTm9uZSwga2V5OiBzdHIgPSBOb25lKSAtPiBOb25lOgogICAgICAgIGlmIG5vdCB0b2tlbiBhbmQgbm90IGtleToKICAgICAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigiRWl0aGVyIHRva2VuIG9yIGtleSBtdXN0IGJlIHByb3ZpZGVkIikKICAgICAgICBpZiB0b2tlbjoKICAgICAgICAgICAga2V5ID0gc2VsZi5fZm9ybWF0X2tleSh0b2tlbikKCiAgICAgICAgYXdhaXQgcmVkaXNfc2VydmljZS5kZWxldGUoa2V5KQogICAgICAgICMgd2UgYWxzbyBuZWVkIHRvIGRlbGV0ZSB0aGUgY2FjaGVkIGxpYnJhcnkgaXRlbXMsIHByb2ZpbGVzIGFuZCB3YXRjaGVkIHNldHMKICAgICAgICBpZiB0b2tlbjoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgYXdhaXQgdXNlcl9jYWNoZS5pbnZhbGlkYXRlX2FsbF91c2VyX2RhdGEodG9rZW4pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGludmFsaWRhdGUgYWxsIHVzZXIgZGF0YSBmb3Ige3JlZGFjdF90b2tlbih0b2tlbil9OiB7ZX0iKQoKICAgICAgICAjIEludmFsaWRhdGUgYXN5bmMgTFJVIGNhY2hlIHNvIGZ1dHVyZSByZWFkcyByZWZsZWN0IGRlbGV0aW9uCiAgICAgICAgdHJ5OgogICAgICAgICAgICBpZiB0b2tlbjoKICAgICAgICAgICAgICAgIHNlbGYuX2dldF91c2VyX2RhdGFfY2FjaGVkLmNhY2hlX2ludmFsaWRhdGUodG9rZW4pCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAjIElmIG9ubHkga2V5IGlzIHByb3ZpZGVkLCBjbGVhciBjYWNoZSBlbnRpcmVseSB0byBiZSBzYWZlCiAgICAgICAgICAgICAgICBzZWxmLl9nZXRfdXNlcl9kYXRhX2NhY2hlZC5jYWNoZV9jbGVhcigpCiAgICAgICAgZXhjZXB0IEtleUVycm9yOgogICAgICAgICAgICBwYXNzCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBpbnZhbGlkYXRlIHVzZXIgZGF0YSBjYWNoZSBkdXJpbmcgdG9rZW4gZGVsZXRpb246IHtlfSIpCgogICAgYXN5bmMgZGVmIGNvdW50X3VzZXJzKHNlbGYpIC0+IGludDoKICAgICAgICAiIiJDb3VudCB0b3RhbCB1c2VycyBieSBzY2FubmluZyBSZWRpcyBrZXlzIHdpdGggdGhlIGNvbmZpZ3VyZWQgcHJlZml4LgoKICAgICAgICBDYWNoZWQgZm9yIDEyIGhvdXJzIHRvIGF2b2lkIGZyZXF1ZW50IFJlZGlzIHNjYW5zLgogICAgICAgICIiIgogICAgICAgIHRyeToKICAgICAgICAgICAgY2xpZW50ID0gYXdhaXQgcmVkaXNfc2VydmljZS5nZXRfY2xpZW50KCkKICAgICAgICBleGNlcHQgKHJlZGlzLlJlZGlzRXJyb3IsIE9TRXJyb3IpIGFzIGV4YzoKICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJDYW5ub3QgY291bnQgdXNlcnM7IFJlZGlzIHVuYXZhaWxhYmxlOiB7ZXhjfSIpCiAgICAgICAgICAgIHJldHVybiAwCgogICAgICAgIHBhdHRlcm4gPSBmIntzZWxmLktFWV9QUkVGSVh9KiIKICAgICAgICB0b3RhbCA9IDAKICAgICAgICB0cnk6CiAgICAgICAgICAgIGFzeW5jIGZvciBfIGluIGNsaWVudC5zY2FuX2l0ZXIobWF0Y2g9cGF0dGVybiwgY291bnQ9NTAwKToKICAgICAgICAgICAgICAgIHRvdGFsICs9IDEKICAgICAgICBleGNlcHQgKHJlZGlzLlJlZGlzRXJyb3IsIE9TRXJyb3IpIGFzIGV4YzoKICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJGYWlsZWQgdG8gc2NhbiBmb3IgdXNlciBjb3VudDoge2V4Y30iKQogICAgICAgICAgICByZXR1cm4gMAogICAgICAgIHJldHVybiB0b3RhbAoKCnRva2VuX3N0b3JlID0gVG9rZW5TdG9yZSgpCg==" | base64 --decode > "$REPO/app/services/token_store.py" +echo " wrote app/services/token_store.py" + +cat >> "$REPO/.env.example" << 'ENVEOF' + +# Trakt OAuth (optional - enables Trakt login on the configure page) +# Register your app at https://trakt.tv/oauth/applications/new +# Set the redirect URI to: http://localhost:8000/tokens/trakt/callback +TRAKT_CLIENT_ID= +TRAKT_CLIENT_SECRET= +ENVEOF + +echo "" +echo "Patch applied successfully! All files written." \ No newline at end of file diff --git a/apply_trakt_patch_final.sh b/apply_trakt_patch_final.sh new file mode 100644 index 0000000..961180e --- /dev/null +++ b/apply_trakt_patch_final.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -e + +REPO="$(pwd)" +if [ ! -f "$REPO/main.py" ] || [ ! -d "$REPO/app" ]; then + echo "ERROR: Run this script from inside your watchly-trakt directory." + exit 1 +fi +echo "Applying Trakt integration patch (final)..." +mkdir -p "$REPO/app/services/trakt" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSBhcHAuc2VydmljZXMudHJha3Quc2VydmljZSBpbXBvcnQgVHJha3RCdW5kbGUKCl9fYWxsX18gPSBbIlRyYWt0QnVuZGxlIl0K" | base64 --decode > "$REPO/app/services/trakt/__init__.py" +echo " wrote app/services/trakt/__init__.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSBhcHAuY29yZS5iYXNlX2NsaWVudCBpbXBvcnQgQmFzZUNsaWVudAoKCmNsYXNzIFRyYWt0Q2xpZW50KEJhc2VDbGllbnQpOgogICAgIiIiCiAgICBDbGllbnQgZm9yIGludGVyYWN0aW5nIHdpdGggdGhlIFRyYWt0IEFQSS4KICAgICIiIgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjbGllbnRfaWQ6IHN0ciwgYWNjZXNzX3Rva2VuOiBzdHIgfCBOb25lID0gTm9uZSwgdGltZW91dDogZmxvYXQgPSAxMC4wLCBtYXhfcmV0cmllczogaW50ID0gMyk6CiAgICAgICAgaGVhZGVycyA9IHsKICAgICAgICAgICAgIkNvbnRlbnQtVHlwZSI6ICJhcHBsaWNhdGlvbi9qc29uIiwKICAgICAgICAgICAgInRyYWt0LWFwaS12ZXJzaW9uIjogIjIiLAogICAgICAgICAgICAidHJha3QtYXBpLWtleSI6IGNsaWVudF9pZCwKICAgICAgICB9CiAgICAgICAgaWYgYWNjZXNzX3Rva2VuOgogICAgICAgICAgICBoZWFkZXJzWyJBdXRob3JpemF0aW9uIl0gPSBmIkJlYXJlciB7YWNjZXNzX3Rva2VufSIKCiAgICAgICAgc3VwZXIoKS5fX2luaXRfXyhiYXNlX3VybD0iaHR0cHM6Ly9hcGkudHJha3QudHYiLCB0aW1lb3V0PXRpbWVvdXQsIG1heF9yZXRyaWVzPW1heF9yZXRyaWVzLCBoZWFkZXJzPWhlYWRlcnMpCg==" | base64 --decode > "$REPO/app/services/trakt/client.py" +echo " wrote app/services/trakt/client.py" + +mkdir -p "$REPO/app/services/trakt" +echo "aW1wb3J0IHNlY3JldHMKZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKaW1wb3J0IGh0dHB4CmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKCgpjbGFzcyBUcmFrdEF1dGhTZXJ2aWNlOgogICAgIiIiCiAgICBIYW5kbGVzIFRyYWt0IE9BdXRoMiBhdXRoZW50aWNhdGlvbiAoRGV2aWNlIENvZGUgLyBBdXRob3JpemF0aW9uIENvZGUgZmxvd3MpLgogICAgIiIiCgogICAgVE9LRU5fVVJMID0gImh0dHBzOi8vYXBpLnRyYWt0LnR2L29hdXRoL3Rva2VuIgogICAgQVVUSE9SSVpFX1VSTCA9ICJodHRwczovL3RyYWt0LnR2L29hdXRoL2F1dGhvcml6ZSIKICAgIERFVklDRV9DT0RFX1VSTCA9ICJodHRwczovL2FwaS50cmFrdC50di9vYXV0aC9kZXZpY2UvY29kZSIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgY2xpZW50X2lkOiBzdHIsIGNsaWVudF9zZWNyZXQ6IHN0ciwgcmVkaXJlY3RfdXJpOiBzdHIpOgogICAgICAgIHNlbGYuY2xpZW50X2lkID0gY2xpZW50X2lkCiAgICAgICAgc2VsZi5jbGllbnRfc2VjcmV0ID0gY2xpZW50X3NlY3JldAogICAgICAgIHNlbGYucmVkaXJlY3RfdXJpID0gcmVkaXJlY3RfdXJpCgogICAgZGVmIGdldF9hdXRob3JpemVfdXJsKHNlbGYsIHN0YXRlOiBzdHIgfCBOb25lID0gTm9uZSkgLT4gdHVwbGVbc3RyLCBzdHJdOgogICAgICAgICIiIgogICAgICAgIEJ1aWxkIHRoZSBPQXV0aDIgYXV0aG9yaXphdGlvbiBVUkwgYW5kIHJldHVybiBpdCBhbG9uZyB3aXRoIHRoZSBzdGF0ZSB2YWx1ZS4KICAgICAgICAiIiIKICAgICAgICBpZiBub3Qgc3RhdGU6CiAgICAgICAgICAgIHN0YXRlID0gc2VjcmV0cy50b2tlbl91cmxzYWZlKDE2KQoKICAgICAgICBwYXJhbXMgPSB7CiAgICAgICAgICAgICJyZXNwb25zZV90eXBlIjogImNvZGUiLAogICAgICAgICAgICAiY2xpZW50X2lkIjogc2VsZi5jbGllbnRfaWQsCiAgICAgICAgICAgICJyZWRpcmVjdF91cmkiOiBzZWxmLnJlZGlyZWN0X3VyaSwKICAgICAgICAgICAgInN0YXRlIjogc3RhdGUsCiAgICAgICAgfQogICAgICAgIHF1ZXJ5ID0gIiYiLmpvaW4oZiJ7a309e3Z9IiBmb3IgaywgdiBpbiBwYXJhbXMuaXRlbXMoKSkKICAgICAgICByZXR1cm4gZiJ7c2VsZi5BVVRIT1JJWkVfVVJMfT97cXVlcnl9Iiwgc3RhdGUKCiAgICBhc3luYyBkZWYgZXhjaGFuZ2VfY29kZShzZWxmLCBjb2RlOiBzdHIpIC0+IGRpY3Rbc3RyLCBBbnldOgogICAgICAgICIiIgogICAgICAgIEV4Y2hhbmdlIGFuIGF1dGhvcml6YXRpb24gY29kZSBmb3IgdG9rZW5zLgogICAgICAgIFJldHVybnMgZGljdCB3aXRoIGFjY2Vzc190b2tlbiwgcmVmcmVzaF90b2tlbiwgZXhwaXJlc19pbiwgZXRjLgogICAgICAgICIiIgogICAgICAgIHBheWxvYWQgPSB7CiAgICAgICAgICAgICJjb2RlIjogY29kZSwKICAgICAgICAgICAgImNsaWVudF9pZCI6IHNlbGYuY2xpZW50X2lkLAogICAgICAgICAgICAiY2xpZW50X3NlY3JldCI6IHNlbGYuY2xpZW50X3NlY3JldCwKICAgICAgICAgICAgInJlZGlyZWN0X3VyaSI6IHNlbGYucmVkaXJlY3RfdXJpLAogICAgICAgICAgICAiZ3JhbnRfdHlwZSI6ICJhdXRob3JpemF0aW9uX2NvZGUiLAogICAgICAgIH0KICAgICAgICB0cnk6CiAgICAgICAgICAgIGFzeW5jIHdpdGggaHR0cHguQXN5bmNDbGllbnQodGltZW91dD0xNS4wKSBhcyBjbGllbnQ6CiAgICAgICAgICAgICAgICByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5wb3N0KHNlbGYuVE9LRU5fVVJMLCBqc29uPXBheWxvYWQpCiAgICAgICAgICAgICAgICByZXNwb25zZS5yYWlzZV9mb3Jfc3RhdHVzKCkKICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZS5qc29uKCkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJUcmFrdCB0b2tlbiBleGNoYW5nZSBmYWlsZWQ6IHtlfSIpCiAgICAgICAgICAgIHJhaXNlCgogICAgYXN5bmMgZGVmIHJlZnJlc2hfdG9rZW4oc2VsZiwgcmVmcmVzaF90b2tlbl92YWx1ZTogc3RyKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICAiIiJSZWZyZXNoIGFuIGV4cGlyZWQgYWNjZXNzIHRva2VuLiIiIgogICAgICAgIHBheWxvYWQgPSB7CiAgICAgICAgICAgICJyZWZyZXNoX3Rva2VuIjogcmVmcmVzaF90b2tlbl92YWx1ZSwKICAgICAgICAgICAgImNsaWVudF9pZCI6IHNlbGYuY2xpZW50X2lkLAogICAgICAgICAgICAiY2xpZW50X3NlY3JldCI6IHNlbGYuY2xpZW50X3NlY3JldCwKICAgICAgICAgICAgInJlZGlyZWN0X3VyaSI6IHNlbGYucmVkaXJlY3RfdXJpLAogICAgICAgICAgICAiZ3JhbnRfdHlwZSI6ICJyZWZyZXNoX3Rva2VuIiwKICAgICAgICB9CiAgICAgICAgdHJ5OgogICAgICAgICAgICBhc3luYyB3aXRoIGh0dHB4LkFzeW5jQ2xpZW50KHRpbWVvdXQ9MTUuMCkgYXMgY2xpZW50OgogICAgICAgICAgICAgICAgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQucG9zdChzZWxmLlRPS0VOX1VSTCwganNvbj1wYXlsb2FkKQogICAgICAgICAgICAgICAgcmVzcG9uc2UucmFpc2VfZm9yX3N0YXR1cygpCiAgICAgICAgICAgICAgICByZXR1cm4gcmVzcG9uc2UuanNvbigpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiVHJha3QgdG9rZW4gcmVmcmVzaCBmYWlsZWQ6IHtlfSIpCiAgICAgICAgICAgIHJhaXNlCg==" | base64 --decode > "$REPO/app/services/trakt/auth.py" +echo " wrote app/services/trakt/auth.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBsb2d1cnUgaW1wb3J0IGxvZ2dlcgoKZnJvbSBhcHAuc2VydmljZXMudHJha3QuY2xpZW50IGltcG9ydCBUcmFrdENsaWVudAoKCmNsYXNzIFRyYWt0VXNlclNlcnZpY2U6CiAgICAiIiIKICAgIEZldGNoZXMgdGhlIGF1dGhlbnRpY2F0ZWQgVHJha3QgdXNlcidzIHByb2ZpbGUuCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgY2xpZW50OiBUcmFrdENsaWVudCk6CiAgICAgICAgc2VsZi5jbGllbnQgPSBjbGllbnQKCiAgICBhc3luYyBkZWYgZ2V0X3VzZXJfaW5mbyhzZWxmKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICAiIiJSZXR1cm4gdGhlIGF1dGhlbnRpY2F0ZWQgdXNlcidzIHByb2ZpbGUgKHVzZXJuYW1lLCBuYW1lLCBldGMuKS4iIiIKICAgICAgICB0cnk6CiAgICAgICAgICAgIGRhdGEgPSBhd2FpdCBzZWxmLmNsaWVudC5nZXQoIi91c2Vycy9tZT9leHRlbmRlZD1mdWxsIikKICAgICAgICAgICAgcmV0dXJuIGRhdGEKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJGYWlsZWQgdG8gZmV0Y2ggVHJha3QgdXNlciBpbmZvOiB7ZX0iKQogICAgICAgICAgICByYWlzZQo=" | base64 --decode > "$REPO/app/services/trakt/user.py" +echo " wrote app/services/trakt/user.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBsb2d1cnUgaW1wb3J0IGxvZ2dlcgoKZnJvbSBhcHAuc2VydmljZXMudHJha3QuY2xpZW50IGltcG9ydCBUcmFrdENsaWVudAoKCmNsYXNzIFRyYWt0TGlicmFyeVNlcnZpY2U6CiAgICAiIiIKICAgIEZldGNoZXMgYW5kIG5vcm1hbGlzZXMgd2F0Y2ggaGlzdG9yeSBmcm9tIFRyYWt0IGludG8gdGhlIHNhbWUgc2hhcGUKICAgIHRoYXQgdGhlIFN0cmVtaW8gbGlicmFyeSBzZXJ2aWNlIHByb2R1Y2VzLCBzbyB0aGUgcmVzdCBvZiB0aGUgYXBwCiAgICBuZWVkcyBubyBjaGFuZ2VzLgogICAgIiIiCgogICAgIyBIb3cgbWFueSBwYWdlcyBvZiBoaXN0b3J5IHRvIHB1bGwgKDEgMDAwIGl0ZW1zIC8gcGFnZSkKICAgIE1BWF9QQUdFUyA9IDEwCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGNsaWVudDogVHJha3RDbGllbnQpOgogICAgICAgIHNlbGYuY2xpZW50ID0gY2xpZW50CgogICAgIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICAgICMgUHVibGljIGhlbHBlcnMKICAgICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgogICAgYXN5bmMgZGVmIGdldF9saWJyYXJ5X2l0ZW1zKHNlbGYpIC0+IGRpY3Rbc3RyLCBsaXN0W2RpY3Rbc3RyLCBBbnldXV06CiAgICAgICAgIiIiCiAgICAgICAgUmV0dXJuIGxpYnJhcnkgaXRlbXMgaW4gdGhlIHNhbWUgc2hhcGUgYXMgU3RyZW1pb0xpYnJhcnlTZXJ2aWNlOgogICAgICAgIHsgIndhdGNoZWQiOiBbLi4uXSwgImxvdmVkIjogW10sICJsaWtlZCI6IFtdLCAiYWRkZWQiOiBbXSwgInJlbW92ZWQiOiBbXSB9CgogICAgICAgIEVhY2ggaXRlbSBjb250YWlucyBhdCBtaW5pbXVtOgogICAgICAgICAgX2lkICAgICAgIOKAkyB0dC4uLiBvciB0bWRiOi4uLiBzdHJpbmcKICAgICAgICAgIHR5cGUgICAgICDigJMgIm1vdmllIiB8ICJzZXJpZXMiCiAgICAgICAgICBuYW1lICAgICAg4oCTIHRpdGxlCiAgICAgICAgIiIiCiAgICAgICAgdHJ5OgogICAgICAgICAgICBtb3ZpZXMgPSBhd2FpdCBzZWxmLl9nZXRfaGlzdG9yeSgibW92aWVzIikKICAgICAgICAgICAgc2hvd3MgPSBhd2FpdCBzZWxmLl9nZXRfaGlzdG9yeSgic2hvd3MiKQoKICAgICAgICAgICAgd2F0Y2hlZDogbGlzdFtkaWN0W3N0ciwgQW55XV0gPSBbXQogICAgICAgICAgICBzZWVuX2lkczogc2V0W3N0cl0gPSBzZXQoKQoKICAgICAgICAgICAgZm9yIHJhdyBpbiBtb3ZpZXM6CiAgICAgICAgICAgICAgICBpdGVtID0gc2VsZi5fbm9ybWFsaXNlX21vdmllKHJhdykKICAgICAgICAgICAgICAgIGlmIGl0ZW0gYW5kIGl0ZW1bIl9pZCJdIG5vdCBpbiBzZWVuX2lkczoKICAgICAgICAgICAgICAgICAgICBzZWVuX2lkcy5hZGQoaXRlbVsiX2lkIl0pCiAgICAgICAgICAgICAgICAgICAgd2F0Y2hlZC5hcHBlbmQoaXRlbSkKCiAgICAgICAgICAgIGZvciByYXcgaW4gc2hvd3M6CiAgICAgICAgICAgICAgICBpdGVtID0gc2VsZi5fbm9ybWFsaXNlX3Nob3cocmF3KQogICAgICAgICAgICAgICAgaWYgaXRlbSBhbmQgaXRlbVsiX2lkIl0gbm90IGluIHNlZW5faWRzOgogICAgICAgICAgICAgICAgICAgIHNlZW5faWRzLmFkZChpdGVtWyJfaWQiXSkKICAgICAgICAgICAgICAgICAgICB3YXRjaGVkLmFwcGVuZChpdGVtKQoKICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbVHJha3RdIGxpYnJhcnk6IHtsZW4od2F0Y2hlZCl9IHdhdGNoZWQgaXRlbXMgKHtsZW4obW92aWVzKX0gbW92aWVzLCB7bGVuKHNob3dzKX0gc2hvd3MpIikKCiAgICAgICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICAgICAid2F0Y2hlZCI6IHdhdGNoZWQsCiAgICAgICAgICAgICAgICAibG92ZWQiOiBbXSwKICAgICAgICAgICAgICAgICJsaWtlZCI6IFtdLAogICAgICAgICAgICAgICAgImFkZGVkIjogW10sCiAgICAgICAgICAgICAgICAicmVtb3ZlZCI6IFtdLAogICAgICAgICAgICB9CiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW1RyYWt0XSBGYWlsZWQgdG8gZ2V0IGxpYnJhcnkgaXRlbXM6IHtlfSIpCiAgICAgICAgICAgIHJldHVybiB7IndhdGNoZWQiOiBbXSwgImxvdmVkIjogW10sICJsaWtlZCI6IFtdLCAiYWRkZWQiOiBbXSwgInJlbW92ZWQiOiBbXX0KCiAgICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICAgIyBQcml2YXRlIGhlbHBlcnMKICAgICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgogICAgYXN5bmMgZGVmIF9nZXRfaGlzdG9yeShzZWxmLCBtZWRpYV90eXBlOiBzdHIpIC0+IGxpc3RbZGljdFtzdHIsIEFueV1dOgogICAgICAgICIiIgogICAgICAgIFB1bGwgd2F0Y2hlZCBoaXN0b3J5IGZvciAqbWVkaWFfdHlwZSogKCJtb3ZpZXMiIHwgInNob3dzIikuCiAgICAgICAgVXNlcyB0aGUgL3VzZXJzL21lL3dhdGNoZWQvOnR5cGUgZW5kcG9pbnQgd2hpY2ggcmV0dXJucyBhIGRlZHVwbGljYXRlZAogICAgICAgIGxpc3Qgb2YgZXZlcnl0aGluZyB0aGUgdXNlciBoYXMgZXZlciBwbGF5ZWQgKG5vIHBhZ2luZyBuZWVkZWQpLgogICAgICAgICIiIgogICAgICAgIHRyeToKICAgICAgICAgICAgZGF0YSA9IGF3YWl0IHNlbGYuY2xpZW50LmdldChmIi91c2Vycy9tZS93YXRjaGVkL3ttZWRpYV90eXBlfSIpCiAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoZGF0YSwgbGlzdCk6CiAgICAgICAgICAgICAgICByZXR1cm4gZGF0YQogICAgICAgICAgICByZXR1cm4gW10KICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW1RyYWt0XSBGYWlsZWQgdG8gZmV0Y2gge21lZGlhX3R5cGV9IGhpc3Rvcnk6IHtlfSIpCiAgICAgICAgICAgIHJldHVybiBbXQoKICAgIGRlZiBfZ2V0X2lkKHNlbGYsIGlkczogZGljdFtzdHIsIEFueV0pIC0+IHN0ciB8IE5vbmU6CiAgICAgICAgIiIiUmV0dXJuIHRoZSBiZXN0IGF2YWlsYWJsZSBjYW5vbmljYWwgSUQgKHByZWZlciBJTURiLCBmYWxsIGJhY2sgdG8gVE1EQikuIiIiCiAgICAgICAgaW1kYiA9IGlkcy5nZXQoImltZGIiKQogICAgICAgIGlmIGltZGI6CiAgICAgICAgICAgIHJldHVybiBpbWRiICAjIGUuZy4gInR0MTIzNDU2NyIKICAgICAgICB0bWRiID0gaWRzLmdldCgidG1kYiIpCiAgICAgICAgaWYgdG1kYjoKICAgICAgICAgICAgcmV0dXJuIGYidG1kYjp7dG1kYn0iCiAgICAgICAgcmV0dXJuIE5vbmUKCiAgICBkZWYgX25vcm1hbGlzZV9tb3ZpZShzZWxmLCByYXc6IGRpY3Rbc3RyLCBBbnldKSAtPiBkaWN0W3N0ciwgQW55XSB8IE5vbmU6CiAgICAgICAgbW92aWUgPSByYXcuZ2V0KCJtb3ZpZSIsIHt9KQogICAgICAgIGlkcyA9IG1vdmllLmdldCgiaWRzIiwge30pCiAgICAgICAgY2Fub25pY2FsX2lkID0gc2VsZi5fZ2V0X2lkKGlkcykKICAgICAgICBpZiBub3QgY2Fub25pY2FsX2lkOgogICAgICAgICAgICByZXR1cm4gTm9uZQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJfaWQiOiBjYW5vbmljYWxfaWQsCiAgICAgICAgICAgICJ0eXBlIjogIm1vdmllIiwKICAgICAgICAgICAgIm5hbWUiOiBtb3ZpZS5nZXQoInRpdGxlIiwgIiIpLAogICAgICAgICAgICAieWVhciI6IG1vdmllLmdldCgieWVhciIpLAogICAgICAgICAgICAic3RhdGUiOiB7CiAgICAgICAgICAgICAgICAidGltZXNXYXRjaGVkIjogcmF3LmdldCgicGxheXMiLCAxKSwKICAgICAgICAgICAgICAgICJmbGFnZ2VkV2F0Y2hlZCI6IDEsCiAgICAgICAgICAgICAgICAibGFzdFdhdGNoZWQiOiByYXcuZ2V0KCJsYXN0X3dhdGNoZWRfYXQiLCAiIiksCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJ0ZW1wIjogRmFsc2UsCiAgICAgICAgICAgICJyZW1vdmVkIjogRmFsc2UsCiAgICAgICAgICAgICJfc291cmNlIjogInRyYWt0IiwKICAgICAgICB9CgogICAgZGVmIF9ub3JtYWxpc2Vfc2hvdyhzZWxmLCByYXc6IGRpY3Rbc3RyLCBBbnldKSAtPiBkaWN0W3N0ciwgQW55XSB8IE5vbmU6CiAgICAgICAgc2hvdyA9IHJhdy5nZXQoInNob3ciLCB7fSkKICAgICAgICBpZHMgPSBzaG93LmdldCgiaWRzIiwge30pCiAgICAgICAgY2Fub25pY2FsX2lkID0gc2VsZi5fZ2V0X2lkKGlkcykKICAgICAgICBpZiBub3QgY2Fub25pY2FsX2lkOgogICAgICAgICAgICByZXR1cm4gTm9uZQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJfaWQiOiBjYW5vbmljYWxfaWQsCiAgICAgICAgICAgICJ0eXBlIjogInNlcmllcyIsCiAgICAgICAgICAgICJuYW1lIjogc2hvdy5nZXQoInRpdGxlIiwgIiIpLAogICAgICAgICAgICAieWVhciI6IHNob3cuZ2V0KCJ5ZWFyIiksCiAgICAgICAgICAgICJzdGF0ZSI6IHsKICAgICAgICAgICAgICAgICJ0aW1lc1dhdGNoZWQiOiByYXcuZ2V0KCJwbGF5cyIsIDEpLAogICAgICAgICAgICAgICAgImZsYWdnZWRXYXRjaGVkIjogMSwKICAgICAgICAgICAgICAgICJsYXN0V2F0Y2hlZCI6IHJhdy5nZXQoImxhc3Rfd2F0Y2hlZF9hdCIsICIiKSwKICAgICAgICAgICAgfSwKICAgICAgICAgICAgInRlbXAiOiBGYWxzZSwKICAgICAgICAgICAgInJlbW92ZWQiOiBGYWxzZSwKICAgICAgICAgICAgIl9zb3VyY2UiOiAidHJha3QiLAogICAgICAgIH0K" | base64 --decode > "$REPO/app/services/trakt/library.py" +echo " wrote app/services/trakt/library.py" + +mkdir -p "$REPO/app/services/trakt" +echo "ZnJvbSBhcHAuc2VydmljZXMudHJha3QuYXV0aCBpbXBvcnQgVHJha3RBdXRoU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy50cmFrdC5jbGllbnQgaW1wb3J0IFRyYWt0Q2xpZW50CmZyb20gYXBwLnNlcnZpY2VzLnRyYWt0LmxpYnJhcnkgaW1wb3J0IFRyYWt0TGlicmFyeVNlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMudHJha3QudXNlciBpbXBvcnQgVHJha3RVc2VyU2VydmljZQoKCmNsYXNzIFRyYWt0QnVuZGxlOgogICAgIiIiCiAgICBVbmlmaWVkIGJ1bmRsZSBmb3IgYWxsIFRyYWt0LXJlbGF0ZWQgc2VydmljZXMuCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgY2xpZW50X2lkOiBzdHIsIGNsaWVudF9zZWNyZXQ6IHN0ciwgcmVkaXJlY3RfdXJpOiBzdHIsIGFjY2Vzc190b2tlbjogc3RyIHwgTm9uZSA9IE5vbmUpOgogICAgICAgIHNlbGYuYXV0aCA9IFRyYWt0QXV0aFNlcnZpY2UoCiAgICAgICAgICAgIGNsaWVudF9pZD1jbGllbnRfaWQsCiAgICAgICAgICAgIGNsaWVudF9zZWNyZXQ9Y2xpZW50X3NlY3JldCwKICAgICAgICAgICAgcmVkaXJlY3RfdXJpPXJlZGlyZWN0X3VyaSwKICAgICAgICApCiAgICAgICAgc2VsZi5fY2xpZW50ID0gVHJha3RDbGllbnQoY2xpZW50X2lkPWNsaWVudF9pZCwgYWNjZXNzX3Rva2VuPWFjY2Vzc190b2tlbikKICAgICAgICBzZWxmLnVzZXIgPSBUcmFrdFVzZXJTZXJ2aWNlKHNlbGYuX2NsaWVudCkKICAgICAgICBzZWxmLmxpYnJhcnkgPSBUcmFrdExpYnJhcnlTZXJ2aWNlKHNlbGYuX2NsaWVudCkKCiAgICBhc3luYyBkZWYgY2xvc2Uoc2VsZik6CiAgICAgICAgYXdhaXQgc2VsZi5fY2xpZW50LmNsb3NlKCkK" | base64 --decode > "$REPO/app/services/trakt/service.py" +echo " wrote app/services/trakt/service.py" + +mkdir -p "$REPO/app/api/endpoints" +echo "IiIiClRyYWt0IE9BdXRoIGFuZCB0b2tlbi1jcmVhdGlvbiBlbmRwb2ludHMuCgpGbG93OgoxLiBHRVQgIC90b2tlbnMvdHJha3QvY29uZmlnICAgICAgICDihpIgcmV0dXJucyBjbGllbnRfaWQgKyB3aGV0aGVyIFRyYWt0IGlzIGNvbmZpZ3VyZWQKMi4gR0VUICAvdG9rZW5zL3RyYWt0L2F1dGhvcml6ZSAgICAg4oaSIHJldHVybnMgdGhlIFRyYWt0IE9BdXRoMiBVUkwgZm9yIHRoZSBwb3B1cAozLiBHRVQgIC90b2tlbnMvdHJha3QvY2FsbGJhY2sgICAgICDihpIgVHJha3QgcmVkaXJlY3RzIGhlcmUgYWZ0ZXIgdXNlciBhcHByb3ZlczsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjaGFuZ2VzIGNvZGUgZm9yIHRva2VucywgdGhlbiBwb3N0cyBhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgdG8gdGhlIG9wZW5lciB3aW5kb3cgYW5kIGNsb3Nlcy4KNC4gUE9TVCAvdG9rZW5zL3RyYWt0ICAgICAgICAgICAgICAg4oaSIENyZWF0ZXMvdXBkYXRlcyBhIFdhdGNobHkgYWNjb3VudCB1c2luZyBhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYWt0IGFjY2Vzc190b2tlbiAoc2FtZSBib2R5IHNoYXBlIGFzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZSBtYWluIC90b2tlbnMvIGVuZHBvaW50IHBsdXMgdHJha3RfYWNjZXNzX3Rva2VuKS4KNS4gUE9TVCAvdG9rZW5zL3RyYWt0L2lkZW50aXR5ICAgICAg4oaSIExpZ2h0d2VpZ2h0IGlkZW50aXR5LWNoZWNrIChyZXR1cm5zIHVzZXIgaW5mbwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICArIGV4aXN0aW5nIHNldHRpbmdzIGlmIGFjY291bnQgZXhpc3RzKS4KIiIiCgppbXBvcnQgc2VjcmV0cwpmcm9tIGRhdGV0aW1lIGltcG9ydCBkYXRldGltZSwgdGltZXpvbmUKZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBmYXN0YXBpIGltcG9ydCBBUElSb3V0ZXIsIEhUVFBFeGNlcHRpb24sIFJlcXVlc3QKZnJvbSBmYXN0YXBpLnJlc3BvbnNlcyBpbXBvcnQgSFRNTFJlc3BvbnNlCmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKZnJvbSBweWRhbnRpYyBpbXBvcnQgQmFzZU1vZGVsLCBGaWVsZAoKZnJvbSBhcHAuY29yZS5jb25maWcgaW1wb3J0IHNldHRpbmdzCmZyb20gYXBwLmNvcmUuc2VjdXJpdHkgaW1wb3J0IHJlZGFjdF90b2tlbgpmcm9tIGFwcC5jb3JlLnNldHRpbmdzIGltcG9ydCBDYXRhbG9nQ29uZmlnLCBQb3N0ZXJSYXRpbmdDb25maWcsIFVzZXJTZXR0aW5ncywgZ2V0X2RlZmF1bHRfc2V0dGluZ3MKZnJvbSBhcHAuc2VydmljZXMubWFuaWZlc3QgaW1wb3J0IG1hbmlmZXN0X3NlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMudG9rZW5fc3RvcmUgaW1wb3J0IHRva2VuX3N0b3JlCmZyb20gYXBwLnNlcnZpY2VzLnRyYWt0LnNlcnZpY2UgaW1wb3J0IFRyYWt0QnVuZGxlCgpyb3V0ZXIgPSBBUElSb3V0ZXIocHJlZml4PSIvdG9rZW5zL3RyYWt0IiwgdGFncz1bInRyYWt0Il0pCgojIEluLW1lbW9yeSBzdG9yZSBmb3Igc2hvcnQtbGl2ZWQgT0F1dGggc3RhdGUgdmFsdWVzLgojIFRoaXMgaXMgcGVyLXByb2Nlc3Mgb25seTsgZm9yIG11bHRpLXdvcmtlciBkZXBsb3ltZW50cyBSZWRpcyB3b3VsZCBiZSBiZXR0ZXIsCiMgYnV0IENTUkYgcHJvdGVjdGlvbiBpcyBzdGlsbCBpbXByb3ZlZCB2ZXJzdXMgbm90aGluZy4KX29hdXRoX3N0YXRlczogZGljdFtzdHIsIHN0cl0gPSB7fQoKCmRlZiBfZ2V0X2J1bmRsZShhY2Nlc3NfdG9rZW46IHN0ciB8IE5vbmUgPSBOb25lKSAtPiBUcmFrdEJ1bmRsZToKICAgIGNsaWVudF9pZCA9IHNldHRpbmdzLlRSQUtUX0NMSUVOVF9JRAogICAgY2xpZW50X3NlY3JldCA9IHNldHRpbmdzLlRSQUtUX0NMSUVOVF9TRUNSRVQKICAgIGlmIG5vdCBjbGllbnRfaWQgb3Igbm90IGNsaWVudF9zZWNyZXQ6CiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbigKICAgICAgICAgICAgc3RhdHVzX2NvZGU9NTAzLAogICAgICAgICAgICBkZXRhaWw9IlRyYWt0IGludGVncmF0aW9uIGlzIG5vdCBjb25maWd1cmVkIG9uIHRoaXMgc2VydmVyLiBTZXQgVFJBS1RfQ0xJRU5UX0lEIGFuZCBUUkFLVF9DTElFTlRfU0VDUkVULiIsCiAgICAgICAgKQogICAgcmVkaXJlY3RfdXJpID0gZiJ7c2V0dGluZ3MuSE9TVF9OQU1FfS90b2tlbnMvdHJha3QvY2FsbGJhY2siCiAgICByZXR1cm4gVHJha3RCdW5kbGUoCiAgICAgICAgY2xpZW50X2lkPWNsaWVudF9pZCwKICAgICAgICBjbGllbnRfc2VjcmV0PWNsaWVudF9zZWNyZXQsCiAgICAgICAgcmVkaXJlY3RfdXJpPXJlZGlyZWN0X3VyaSwKICAgICAgICBhY2Nlc3NfdG9rZW49YWNjZXNzX3Rva2VuLAogICAgKQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgUmVxdWVzdCAvIFJlc3BvbnNlIG1vZGVscwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmNsYXNzIFRyYWt0VG9rZW5SZXF1ZXN0KEJhc2VNb2RlbCk6CiAgICB0cmFrdF9hY2Nlc3NfdG9rZW46IHN0ciA9IEZpZWxkKC4uLiwgZGVzY3JpcHRpb249IlRyYWt0IE9BdXRoMiBhY2Nlc3MgdG9rZW4iKQogICAgdHJha3RfcmVmcmVzaF90b2tlbjogc3RyIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSwgZGVzY3JpcHRpb249IlRyYWt0IE9BdXRoMiByZWZyZXNoIHRva2VuIikKICAgIHRyYWt0X2V4cGlyZXNfYXQ6IGludCB8IE5vbmUgPSBGaWVsZChkZWZhdWx0PU5vbmUsIGRlc2NyaXB0aW9uPSJVbml4IHRpbWVzdGFtcCB3aGVuIGFjY2VzcyB0b2tlbiBleHBpcmVzIikKICAgIGNhdGFsb2dzOiBsaXN0W0NhdGFsb2dDb25maWddIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSkKICAgIGxhbmd1YWdlOiBzdHIgPSBGaWVsZChkZWZhdWx0PSJlbi1VUyIpCiAgICBwb3N0ZXJfcmF0aW5nOiBQb3N0ZXJSYXRpbmdDb25maWcgfCBOb25lID0gRmllbGQoZGVmYXVsdD1Ob25lKQogICAgZXhjbHVkZWRfbW92aWVfZ2VucmVzOiBsaXN0W3N0cl0gPSBGaWVsZChkZWZhdWx0X2ZhY3Rvcnk9bGlzdCkKICAgIGV4Y2x1ZGVkX3Nlcmllc19nZW5yZXM6IGxpc3Rbc3RyXSA9IEZpZWxkKGRlZmF1bHRfZmFjdG9yeT1saXN0KQogICAgcG9wdWxhcml0eTogc3RyID0gRmllbGQoZGVmYXVsdD0iYmFsYW5jZWQiKQogICAgeWVhcl9taW46IGludCA9IEZpZWxkKGRlZmF1bHQ9MjAxMCkKICAgIHllYXJfbWF4OiBpbnQgPSBGaWVsZChkZWZhdWx0PTIwMjUpCiAgICBzb3J0aW5nX29yZGVyOiBzdHIgPSBGaWVsZChkZWZhdWx0PSJkZWZhdWx0IikKICAgIHNpbWtsX2FwaV9rZXk6IHN0ciB8IE5vbmUgPSBGaWVsZChkZWZhdWx0PU5vbmUpCiAgICBnZW1pbmlfYXBpX2tleTogc3RyIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSkKICAgIHRtZGJfYXBpX2tleTogc3RyIHwgTm9uZSA9IEZpZWxkKGRlZmF1bHQ9Tm9uZSkKCgpjbGFzcyBUcmFrdFRva2VuUmVzcG9uc2UoQmFzZU1vZGVsKToKICAgIHRva2VuOiBzdHIKICAgIG1hbmlmZXN0VXJsOiBzdHIKICAgIGV4cGlyZXNJblNlY29uZHM6IGludCB8IE5vbmUgPSBOb25lCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBFbmRwb2ludHMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpAcm91dGVyLmdldCgiL2NvbmZpZyIpCmFzeW5jIGRlZiB0cmFrdF9jb25maWcoKToKICAgICIiIlJldHVybiB3aGV0aGVyIFRyYWt0IGlzIGNvbmZpZ3VyZWQgYW5kIHRoZSBjbGllbnRfaWQgKG5lZWRlZCBmb3IgdGhlIHBvcHVwKS4iIiIKICAgIGNvbmZpZ3VyZWQgPSBib29sKHNldHRpbmdzLlRSQUtUX0NMSUVOVF9JRCBhbmQgc2V0dGluZ3MuVFJBS1RfQ0xJRU5UX1NFQ1JFVCkKICAgIHJldHVybiB7CiAgICAgICAgImNvbmZpZ3VyZWQiOiBjb25maWd1cmVkLAogICAgICAgICJjbGllbnRfaWQiOiBzZXR0aW5ncy5UUkFLVF9DTElFTlRfSUQgaWYgY29uZmlndXJlZCBlbHNlIE5vbmUsCiAgICB9CgoKQHJvdXRlci5nZXQoIi9hdXRob3JpemUiKQphc3luYyBkZWYgdHJha3RfYXV0aG9yaXplKCk6CiAgICAiIiJSZXR1cm4gdGhlIFRyYWt0IE9BdXRoMiBhdXRob3JpemF0aW9uIFVSTCBmb3IgdGhlIGZyb250ZW5kIHBvcHVwLiIiIgogICAgYnVuZGxlID0gX2dldF9idW5kbGUoKQogICAgc3RhdGUgPSBzZWNyZXRzLnRva2VuX3VybHNhZmUoMTYpCiAgICBfb2F1dGhfc3RhdGVzW3N0YXRlXSA9IHN0YXRlICAjIG1pbmltYWwgYW50aS1DU1JGCiAgICB1cmwsIF8gPSBidW5kbGUuYXV0aC5nZXRfYXV0aG9yaXplX3VybChzdGF0ZT1zdGF0ZSkKICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCiAgICByZXR1cm4geyJ1cmwiOiB1cmwsICJzdGF0ZSI6IHN0YXRlfQoKCkByb3V0ZXIuZ2V0KCIvY2FsbGJhY2siLCByZXNwb25zZV9jbGFzcz1IVE1MUmVzcG9uc2UpCmFzeW5jIGRlZiB0cmFrdF9jYWxsYmFjayhyZXF1ZXN0OiBSZXF1ZXN0LCBjb2RlOiBzdHIgfCBOb25lID0gTm9uZSwgc3RhdGU6IHN0ciB8IE5vbmUgPSBOb25lLCBlcnJvcjogc3RyIHwgTm9uZSA9IE5vbmUpOgogICAgIiIiCiAgICBUcmFrdCByZWRpcmVjdHMgaGVyZSBhZnRlciB0aGUgdXNlciBhcHByb3ZlcyAob3IgZGVuaWVzKSB0aGUgYXBwLgogICAgV2UgZXhjaGFuZ2UgdGhlIGNvZGUgZm9yIHRva2VucyBhbmQgcG9zdCBhIG1lc3NhZ2UgYmFjayB0byB0aGUgb3BlbmVyCiAgICB3aW5kb3csIHRoZW4gY2xvc2UgdGhlIHBvcHVwLgogICAgIiIiCiAgICAjIC0tLSBlcnJvciBmcm9tIFRyYWt0IC0tLQogICAgaWYgZXJyb3Igb3Igbm90IGNvZGU6CiAgICAgICAgcmV0dXJuIEhUTUxSZXNwb25zZShfcG9wdXBfY2xvc2VfaHRtbChzdWNjZXNzPUZhbHNlLCBlcnJvcj1lcnJvciBvciAiQXV0aG9yaXphdGlvbiBjYW5jZWxsZWQiKSkKCiAgICAjIC0tLSB2ZXJpZnkgc3RhdGUgdG8gcHJldmVudCBDU1JGIC0tLQogICAgaWYgbm90IHN0YXRlIG9yIHN0YXRlIG5vdCBpbiBfb2F1dGhfc3RhdGVzOgogICAgICAgIHJldHVybiBIVE1MUmVzcG9uc2UoX3BvcHVwX2Nsb3NlX2h0bWwoc3VjY2Vzcz1GYWxzZSwgZXJyb3I9IkludmFsaWQgT0F1dGggc3RhdGUuIFBsZWFzZSB0cnkgYWdhaW4uIikpCiAgICBkZWwgX29hdXRoX3N0YXRlc1tzdGF0ZV0KCiAgICAjIC0tLSBleGNoYW5nZSBjb2RlIGZvciB0b2tlbnMgLS0tCiAgICB0cnk6CiAgICAgICAgYnVuZGxlID0gX2dldF9idW5kbGUoKQogICAgICAgIHRva2VuX2RhdGEgPSBhd2FpdCBidW5kbGUuYXV0aC5leGNoYW5nZV9jb2RlKGNvZGUpCiAgICAgICAgYXdhaXQgYnVuZGxlLmNsb3NlKCkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgIGxvZ2dlci5lcnJvcihmIlRyYWt0IGNhbGxiYWNrOiB0b2tlbiBleGNoYW5nZSBmYWlsZWQ6IHtleGN9IikKICAgICAgICByZXR1cm4gSFRNTFJlc3BvbnNlKF9wb3B1cF9jbG9zZV9odG1sKHN1Y2Nlc3M9RmFsc2UsIGVycm9yPSJUb2tlbiBleGNoYW5nZSBmYWlsZWQiKSkKCiAgICBhY2Nlc3NfdG9rZW4gPSB0b2tlbl9kYXRhLmdldCgiYWNjZXNzX3Rva2VuIikKICAgIHJlZnJlc2hfdG9rZW4gPSB0b2tlbl9kYXRhLmdldCgicmVmcmVzaF90b2tlbiIpCiAgICBleHBpcmVzX2luID0gdG9rZW5fZGF0YS5nZXQoImV4cGlyZXNfaW4iLCA3Nzc2MDAwKSAgIyBkZWZhdWx0IDkwIGRheXMKCiAgICBpZiBub3QgYWNjZXNzX3Rva2VuOgogICAgICAgIHJldHVybiBIVE1MUmVzcG9uc2UoX3BvcHVwX2Nsb3NlX2h0bWwoc3VjY2Vzcz1GYWxzZSwgZXJyb3I9Ik5vIGFjY2VzcyB0b2tlbiByZWNlaXZlZCIpKQoKICAgICMgQ2FsY3VsYXRlIGV4cGlyeSB1bml4IHRpbWVzdGFtcAogICAgaW1wb3J0IHRpbWUKICAgIGV4cGlyZXNfYXQgPSBpbnQodGltZS50aW1lKCkpICsgZXhwaXJlc19pbgoKICAgIHJldHVybiBIVE1MUmVzcG9uc2UoCiAgICAgICAgX3BvcHVwX2Nsb3NlX2h0bWwoCiAgICAgICAgICAgIHN1Y2Nlc3M9VHJ1ZSwKICAgICAgICAgICAgYWNjZXNzX3Rva2VuPWFjY2Vzc190b2tlbiwKICAgICAgICAgICAgcmVmcmVzaF90b2tlbj1yZWZyZXNoX3Rva2VuLAogICAgICAgICAgICBleHBpcmVzX2F0PWV4cGlyZXNfYXQsCiAgICAgICAgKQogICAgKQoKCkByb3V0ZXIucG9zdCgiL2lkZW50aXR5Iiwgc3RhdHVzX2NvZGU9MjAwKQphc3luYyBkZWYgdHJha3RfaWRlbnRpdHkocGF5bG9hZDogVHJha3RUb2tlblJlcXVlc3QpOgogICAgIiIiCiAgICBWYWxpZGF0ZSB0aGUgVHJha3QgYWNjZXNzIHRva2VuLCByZXR1cm4gdXNlciBpbmZvIGFuZCBleGlzdGluZyBzZXR0aW5ncy4KICAgICIiIgogICAgYnVuZGxlID0gX2dldF9idW5kbGUoYWNjZXNzX3Rva2VuPXBheWxvYWQudHJha3RfYWNjZXNzX3Rva2VuKQogICAgdHJ5OgogICAgICAgIHVzZXJfaW5mbyA9IGF3YWl0IGJ1bmRsZS51c2VyLmdldF91c2VyX2luZm8oKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDAsIGRldGFpbD1mIkludmFsaWQgVHJha3QgYWNjZXNzIHRva2VuOiB7ZXhjfSIpIGZyb20gZXhjCiAgICBmaW5hbGx5OgogICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCgogICAgdXNlcm5hbWUgPSB1c2VyX2luZm8uZ2V0KCJ1c2VybmFtZSIpIG9yIHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciAidHJha3RfdXNlciIKICAgICMgVXNlIGEgVHJha3QtbmFtZXNwYWNlZCB1c2VyX2lkIHNvIGl0IGNhbid0IGNsYXNoIHdpdGggU3RyZW1pbyBJRHMKICAgIHVzZXJfaWQgPSBmInRyYWt0Ont1c2VybmFtZX0iCgogICAgdG9rZW4gPSB0b2tlbl9zdG9yZS5nZXRfdG9rZW5fZnJvbV91c2VyX2lkKHVzZXJfaWQpCiAgICB1c2VyX2RhdGEgPSBhd2FpdCB0b2tlbl9zdG9yZS5nZXRfdXNlcl9kYXRhKHRva2VuKQogICAgZXhpc3RzID0gYm9vbCh1c2VyX2RhdGEpCgogICAgcmVzcG9uc2U6IGRpY3Rbc3RyLCBBbnldID0gewogICAgICAgICJ1c2VyX2lkIjogdXNlcl9pZCwKICAgICAgICAidXNlcm5hbWUiOiB1c2VybmFtZSwKICAgICAgICAiZGlzcGxheSI6IHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciB1c2VybmFtZSwKICAgICAgICAiZXhpc3RzIjogZXhpc3RzLAogICAgfQogICAgaWYgZXhpc3RzIGFuZCB1c2VyX2RhdGE6CiAgICAgICAgcmF3X3NldHRpbmdzID0gdXNlcl9kYXRhLmdldCgic2V0dGluZ3MiLCB7fSkKICAgICAgICB0cnk6CiAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBVc2VyU2V0dGluZ3MoKipyYXdfc2V0dGluZ3MpCiAgICAgICAgICAgIHJlc3BvbnNlWyJzZXR0aW5ncyJdID0gdXNlcl9zZXR0aW5ncy5tb2RlbF9kdW1wKCkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIG5vcm1hbGlzZSBzZXR0aW5ncyBmb3Ige3VzZXJfaWR9OiB7ZX0iKQogICAgICAgICAgICByZXNwb25zZVsic2V0dGluZ3MiXSA9IHJhd19zZXR0aW5ncwoKICAgIHJldHVybiByZXNwb25zZQoKCkByb3V0ZXIucG9zdCgiLyIsIHJlc3BvbnNlX21vZGVsPVRyYWt0VG9rZW5SZXNwb25zZSkKYXN5bmMgZGVmIGNyZWF0ZV90cmFrdF90b2tlbihwYXlsb2FkOiBUcmFrdFRva2VuUmVxdWVzdCwgcmVxdWVzdDogUmVxdWVzdCk6CiAgICAiIiIKICAgIENyZWF0ZSBvciB1cGRhdGUgYSBXYXRjaGx5IGFjY291bnQgYmFja2VkIGJ5IGEgVHJha3QgYWNjZXNzIHRva2VuLgogICAgVGhlIGxpYnJhcnkgaXMgZmV0Y2hlZCBmcm9tIFRyYWt0OyBldmVyeXRoaW5nIGVsc2UgYmVoYXZlcyBsaWtlIHRoZQogICAgU3RyZW1pby1iYXNlZCB0b2tlbiBlbmRwb2ludC4KICAgICIiIgogICAgYnVuZGxlID0gX2dldF9idW5kbGUoYWNjZXNzX3Rva2VuPXBheWxvYWQudHJha3RfYWNjZXNzX3Rva2VuKQoKICAgIHRyeToKICAgICAgICB1c2VyX2luZm8gPSBhd2FpdCBidW5kbGUudXNlci5nZXRfdXNlcl9pbmZvKCkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDAsIGRldGFpbD1mIkludmFsaWQgVHJha3QgYWNjZXNzIHRva2VuOiB7ZXhjfSIpIGZyb20gZXhjCgogICAgdXNlcm5hbWUgPSB1c2VyX2luZm8uZ2V0KCJ1c2VybmFtZSIpIG9yIHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciAidHJha3RfdXNlciIKICAgIHVzZXJfaWQgPSBmInRyYWt0Ont1c2VybmFtZX0iCgogICAgdG9rZW4gPSB0b2tlbl9zdG9yZS5nZXRfdG9rZW5fZnJvbV91c2VyX2lkKHVzZXJfaWQpCiAgICBleGlzdGluZ19kYXRhID0gYXdhaXQgdG9rZW5fc3RvcmUuZ2V0X3VzZXJfZGF0YSh0b2tlbikKCiAgICBkZWZhdWx0X3NldHRpbmdzID0gZ2V0X2RlZmF1bHRfc2V0dGluZ3MoKQogICAgdXNlcl9zZXR0aW5ncyA9IFVzZXJTZXR0aW5ncygKICAgICAgICBsYW5ndWFnZT1wYXlsb2FkLmxhbmd1YWdlIG9yIGRlZmF1bHRfc2V0dGluZ3MubGFuZ3VhZ2UsCiAgICAgICAgY2F0YWxvZ3M9cGF5bG9hZC5jYXRhbG9ncyBpZiBwYXlsb2FkLmNhdGFsb2dzIGVsc2UgZGVmYXVsdF9zZXR0aW5ncy5jYXRhbG9ncywKICAgICAgICBwb3N0ZXJfcmF0aW5nPXBheWxvYWQucG9zdGVyX3JhdGluZywKICAgICAgICBleGNsdWRlZF9tb3ZpZV9nZW5yZXM9cGF5bG9hZC5leGNsdWRlZF9tb3ZpZV9nZW5yZXMsCiAgICAgICAgZXhjbHVkZWRfc2VyaWVzX2dlbnJlcz1wYXlsb2FkLmV4Y2x1ZGVkX3Nlcmllc19nZW5yZXMsCiAgICAgICAgeWVhcl9taW49cGF5bG9hZC55ZWFyX21pbiwKICAgICAgICB5ZWFyX21heD1wYXlsb2FkLnllYXJfbWF4LAogICAgICAgIHBvcHVsYXJpdHk9cGF5bG9hZC5wb3B1bGFyaXR5LAogICAgICAgIHNvcnRpbmdfb3JkZXI9cGF5bG9hZC5zb3J0aW5nX29yZGVyLAogICAgICAgIHNpbWtsX2FwaV9rZXk9cGF5bG9hZC5zaW1rbF9hcGlfa2V5LAogICAgICAgIGdlbWluaV9hcGlfa2V5PXBheWxvYWQuZ2VtaW5pX2FwaV9rZXksCiAgICAgICAgdG1kYl9hcGlfa2V5PXBheWxvYWQudG1kYl9hcGlfa2V5LAogICAgKQoKICAgIHBheWxvYWRfdG9fc3RvcmU6IGRpY3Rbc3RyLCBBbnldID0gewogICAgICAgICJhdXRoX3Byb3ZpZGVyIjogInRyYWt0IiwKICAgICAgICAjIGF1dGhLZXkgc3RvcmVzIHRoZSBUcmFrdCBhY2Nlc3MgdG9rZW4gKGVuY3J5cHRlZCBieSB0b2tlbl9zdG9yZSkKICAgICAgICAiYXV0aEtleSI6IHBheWxvYWQudHJha3RfYWNjZXNzX3Rva2VuLAogICAgICAgICJ0cmFrdF9yZWZyZXNoX3Rva2VuIjogcGF5bG9hZC50cmFrdF9yZWZyZXNoX3Rva2VuLAogICAgICAgICJ0cmFrdF9leHBpcmVzX2F0IjogcGF5bG9hZC50cmFrdF9leHBpcmVzX2F0LAogICAgICAgICJ0cmFrdF91c2VybmFtZSI6IHVzZXJuYW1lLAogICAgICAgICJlbWFpbCI6IHVzZXJfaW5mby5nZXQoIm5hbWUiKSBvciB1c2VybmFtZSwKICAgICAgICAic2V0dGluZ3MiOiB1c2VyX3NldHRpbmdzLm1vZGVsX2R1bXAoKSwKICAgICAgICAibGFzdF91cGRhdGVkIjogZXhpc3RpbmdfZGF0YS5nZXQoImxhc3RfdXBkYXRlZCIpIGlmIGV4aXN0aW5nX2RhdGEgZWxzZSBkYXRldGltZS5ub3codGltZXpvbmUudXRjKS5pc29mb3JtYXQoKSwKICAgIH0KCiAgICB0b2tlbiA9IGF3YWl0IHRva2VuX3N0b3JlLnN0b3JlX3VzZXJfZGF0YSh1c2VyX2lkLCBwYXlsb2FkX3RvX3N0b3JlKQogICAgYWNjb3VudF9zdGF0dXMgPSAidXBkYXRlZCIgaWYgZXhpc3RpbmdfZGF0YSBlbHNlICJjcmVhdGVkIgogICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBUcmFrdCBhY2NvdW50IHthY2NvdW50X3N0YXR1c30gZm9yIHVzZXIge3VzZXJfaWR9IikKCiAgICAjIFByZS1jYWNoZSBsaWJyYXJ5IHVzaW5nIFRyYWt0IGRhdGEKICAgIHRyeToKICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgYnVuZGxlLmxpYnJhcnkuZ2V0X2xpYnJhcnlfaXRlbXMoKQogICAgICAgICMgVXNlIHRoZSBzaGFyZWQgbWFuaWZlc3QgY2FjaGluZyBwYXRod2F5LCBwYXNzaW5nIGxpYnJhcnlfaXRlbXMgZGlyZWN0bHkKICAgICAgICBhd2FpdCBtYW5pZmVzdF9zZXJ2aWNlLmNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzX2Zyb21faXRlbXMobGlicmFyeV9pdGVtcywgdXNlcl9zZXR0aW5ncywgdG9rZW4pCiAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBUcmFrdCBsaWJyYXJ5IGNhY2hlZCAoe2xlbihsaWJyYXJ5X2l0ZW1zLmdldCgnd2F0Y2hlZCcsIFtdKSl9IGl0ZW1zKSIpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICBsb2dnZXIud2FybmluZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byBwcmUtY2FjaGUgVHJha3QgbGlicmFyeToge2V4Y30uIFdpbGwgY2FjaGUgb24gZGVtYW5kLiIpCiAgICBmaW5hbGx5OgogICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCgogICAgYmFzZV91cmwgPSBzZXR0aW5ncy5IT1NUX05BTUUKICAgIG1hbmlmZXN0X3VybCA9IGYie2Jhc2VfdXJsfS97dG9rZW59L21hbmlmZXN0Lmpzb24iCiAgICBleHBpcmVzX2luID0gc2V0dGluZ3MuVE9LRU5fVFRMX1NFQ09ORFMgaWYgc2V0dGluZ3MuVE9LRU5fVFRMX1NFQ09ORFMgPiAwIGVsc2UgTm9uZQoKICAgIHJldHVybiBUcmFrdFRva2VuUmVzcG9uc2UodG9rZW49dG9rZW4sIG1hbmlmZXN0VXJsPW1hbmlmZXN0X3VybCwgZXhwaXJlc0luU2Vjb25kcz1leHBpcmVzX2luKQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgSGVscGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmRlZiBfcG9wdXBfY2xvc2VfaHRtbCgKICAgIHN1Y2Nlc3M6IGJvb2wsCiAgICBlcnJvcjogc3RyIHwgTm9uZSA9IE5vbmUsCiAgICBhY2Nlc3NfdG9rZW46IHN0ciB8IE5vbmUgPSBOb25lLAogICAgcmVmcmVzaF90b2tlbjogc3RyIHwgTm9uZSA9IE5vbmUsCiAgICBleHBpcmVzX2F0OiBpbnQgfCBOb25lID0gTm9uZSwKKSAtPiBzdHI6CiAgICAiIiIKICAgIE1pbmltYWwgSFRNTCBwYWdlIHRoYXQgcG9zdHMgYSBtZXNzYWdlIHRvIHRoZSBvcGVuZXIgYW5kIGNsb3NlcyBpdHNlbGYuCiAgICBUaGUgZnJvbnRlbmQgbGlzdGVucyBmb3IgdGhpcyBtZXNzYWdlIHZpYSB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIC4uLikuCiAgICAiIiIKICAgIGlmIHN1Y2Nlc3M6CiAgICAgICAgcGF5bG9hZF9qcyA9ICgKICAgICAgICAgICAgZiJ7eyB0eXBlOiAndHJha3RfYXV0aF9zdWNjZXNzJywgIgogICAgICAgICAgICBmImFjY2Vzc190b2tlbjoge19qc19zdHIoYWNjZXNzX3Rva2VuKX0sICIKICAgICAgICAgICAgZiJyZWZyZXNoX3Rva2VuOiB7X2pzX3N0cihyZWZyZXNoX3Rva2VuKX0sICIKICAgICAgICAgICAgZiJleHBpcmVzX2F0OiB7ZXhwaXJlc19hdCBvciAnbnVsbCd9IH19IgogICAgICAgICkKICAgIGVsc2U6CiAgICAgICAgcGF5bG9hZF9qcyA9IGYie3sgdHlwZTogJ3RyYWt0X2F1dGhfZXJyb3InLCBlcnJvcjoge19qc19zdHIoZXJyb3Igb3IgJ1Vua25vd24gZXJyb3InKX0gfX0iCgogICAgcmV0dXJuIGYiIiI8IURPQ1RZUEUgaHRtbD4KPGh0bWw+CjxoZWFkPjx0aXRsZT5UcmFrdCBBdXRob3JpemF0aW9uPC90aXRsZT48L2hlYWQ+Cjxib2R5Pgo8cCBzdHlsZT0iZm9udC1mYW1pbHk6c2Fucy1zZXJpZjt0ZXh0LWFsaWduOmNlbnRlcjttYXJnaW4tdG9wOjNyZW0iPgogIHsnQXV0aG9yaXphdGlvbiBzdWNjZXNzZnVsISBZb3UgY2FuIGNsb3NlIHRoaXMgd2luZG93LicgaWYgc3VjY2VzcyBlbHNlIGYnQXV0aG9yaXphdGlvbiBmYWlsZWQ6IHtlcnJvcn0nfQo8L3A+CjxzY3JpcHQ+CiAgdHJ5IHt7CiAgICBpZiAod2luZG93Lm9wZW5lcikge3sKICAgICAgd2luZG93Lm9wZW5lci5wb3N0TWVzc2FnZSh7cGF5bG9hZF9qc30sIHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4pOwogICAgfX0KICB9fSBjYXRjaChlKSB7e319CiAgc2V0VGltZW91dChmdW5jdGlvbigpIHt7IHdpbmRvdy5jbG9zZSgpOyB9fSwgMTAwMCk7Cjwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4iIiIKCgpkZWYgX2pzX3N0cih2YWx1ZTogc3RyIHwgTm9uZSkgLT4gc3RyOgogICAgaWYgdmFsdWUgaXMgTm9uZToKICAgICAgICByZXR1cm4gIm51bGwiCiAgICBlc2NhcGVkID0gdmFsdWUucmVwbGFjZSgiXFwiLCAiXFxcXCIpLnJlcGxhY2UoJyInLCAnXFwiJykucmVwbGFjZSgiXG4iLCAiXFxuIikKICAgIHJldHVybiBmJyJ7ZXNjYXBlZH0iJwo=" | base64 --decode > "$REPO/app/api/endpoints/trakt.py" +echo " wrote app/api/endpoints/trakt.py" + +mkdir -p "$REPO/app/api/endpoints" +echo "ZnJvbSBmYXN0YXBpIGltcG9ydCBBUElSb3V0ZXIsIEhUVFBFeGNlcHRpb24sIFJlc3BvbnNlCmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKCmZyb20gYXBwLmNvcmUuc2VjdXJpdHkgaW1wb3J0IHJlZGFjdF90b2tlbgpmcm9tIGFwcC5zZXJ2aWNlcy5yZWNvbW1lbmRhdGlvbi5jYXRhbG9nX3NlcnZpY2UgaW1wb3J0IGNhdGFsb2dfc2VydmljZQoKcm91dGVyID0gQVBJUm91dGVyKCkKCgpAcm91dGVyLmdldCgiL3t0b2tlbn0vY2F0YWxvZy97dHlwZX0ve2lkfS5qc29uIikKQHJvdXRlci5nZXQoIi97dG9rZW59L2NhdGFsb2cve3R5cGV9L3tpZH0ve2V4dHJhfS5qc29uIikKYXN5bmMgZGVmIGdldF9jYXRhbG9nKHJlc3BvbnNlOiBSZXNwb25zZSwgdHlwZTogc3RyLCBpZDogc3RyLCB0b2tlbjogc3RyLCBleHRyYTogc3RyIHwgTm9uZSA9IE5vbmUpIC0+IGRpY3Q6CiAgICBpZiB0eXBlIG5vdCBpbiAoIm1vdmllIiwgInNlcmllcyIpOgogICAgICAgIHJhaXNlIEhUVFBFeGNlcHRpb24oc3RhdHVzX2NvZGU9NDAwLCBkZXRhaWw9IkludmFsaWQgY29udGVudCB0eXBlLiBNdXN0IGJlICdtb3ZpZScgb3IgJ3NlcmllcycuIikKCiAgICBpZiBsZW4odG9rZW4pID4gODA6ICAjIFN0cmVtaW8gdG9rZW5zIGFyZSB+MjQgY2hhcnM7IFRyYWt0IGhhc2hlZCB0b2tlbnMgYXJlIDQwIGNoYXJzLiA4MCBpcyBhIHNhZmUgdXBwZXIgYm91bmQuCiAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDAsIGRldGFpbD0iSW52YWxpZCB0b2tlbi4iKQoKICAgIHRyeToKICAgICAgICAjIERlbGVnYXRlIHRvIGNhdGFsb2cgc2VydmljZSBmYWNhZGUKICAgICAgICByZWNvbW1lbmRhdGlvbnMsIGhlYWRlcnMgPSBhd2FpdCBjYXRhbG9nX3NlcnZpY2UuZ2V0X2NhdGFsb2codG9rZW4sIHR5cGUsIGlkKQoKICAgICAgICAjIFNldCByZXNwb25zZSBoZWFkZXJzCiAgICAgICAgZm9yIGtleSwgdmFsdWUgaW4gaGVhZGVycy5pdGVtcygpOgogICAgICAgICAgICByZXNwb25zZS5oZWFkZXJzW2tleV0gPSB2YWx1ZQoKICAgICAgICAjIGlmIHJlY29tbWVuZGF0aW9ucyBhcmUgbm9uZSBvciBlbXB0eSwgdGhlbiBzZXQgY2FjaGUgaGVhZGVyIHRvIG5vLWNhY2hlCiAgICAgICAgaWYgcmVjb21tZW5kYXRpb25zIGFuZCBub3QgcmVjb21tZW5kYXRpb25zLmdldCgibWV0YSIpOgogICAgICAgICAgICByZXNwb25zZS5oZWFkZXJzWyJDYWNoZS1Db250cm9sIl0gPSAibm8tY2FjaGUiCgogICAgICAgIHJldHVybiByZWNvbW1lbmRhdGlvbnMKCiAgICBleGNlcHQgSFRUUEV4Y2VwdGlvbjoKICAgICAgICByYWlzZQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBFcnJvciBmZXRjaGluZyBjYXRhbG9nIGZvciB7dHlwZX0ve2lkfToge2V9IikKICAgICAgICByYWlzZSBIVFRQRXhjZXB0aW9uKHN0YXR1c19jb2RlPTUwMCwgZGV0YWlsPWYiU29tZXRoaW5nIHdlbnQgd3JvbmcuIFBsZWFzZSB0cnkgYWdhaW4uIEVycm9yOiB7ZX0iKQo=" | base64 --decode > "$REPO/app/api/endpoints/catalogs.py" +echo " wrote app/api/endpoints/catalogs.py" + +mkdir -p "$REPO/app/static/js/modules" +echo "Ly8gVHJha3QgQXV0aGVudGljYXRpb24gTW9kdWxlCgppbXBvcnQgeyBzaG93VG9hc3QgfSBmcm9tICcuL3VpLmpzJzsKaW1wb3J0IHsgc3dpdGNoU2VjdGlvbiwgdW5sb2NrTmF2aWdhdGlvbiB9IGZyb20gJy4vbmF2aWdhdGlvbi5qcyc7CgovLyBMb2NhbFN0b3JhZ2Uga2V5cyBmb3IgVHJha3QKY29uc3QgVFJBS1RfU1RPUkFHRV9LRVkgPSAnd2F0Y2hseV90cmFrdF9hdXRoJzsKY29uc3QgRVhQSVJZX0RBWVMgPSA4NTsgLy8gVHJha3QgdG9rZW5zIGxhc3QgOTAgZGF5czsgcmVmcmVzaCBhIGJpdCBlYXJseQoKbGV0IGxhbmd1YWdlU2VsZWN0ID0gbnVsbDsKbGV0IGdldENhdGFsb2dzID0gbnVsbDsKbGV0IHJlbmRlckNhdGFsb2dMaXN0ID0gbnVsbDsKbGV0IHJlc2V0QXBwID0gbnVsbDsKCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIFB1YmxpYyBBUEkKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplVHJha3QoZG9tRWxlbWVudHMsIGNhdGFsb2dTdGF0ZSkgewogICAgbGFuZ3VhZ2VTZWxlY3QgPSBkb21FbGVtZW50cy5sYW5ndWFnZVNlbGVjdDsKICAgIGdldENhdGFsb2dzID0gY2F0YWxvZ1N0YXRlLmdldENhdGFsb2dzOwogICAgcmVuZGVyQ2F0YWxvZ0xpc3QgPSBjYXRhbG9nU3RhdGUucmVuZGVyQ2F0YWxvZ0xpc3Q7CiAgICByZXNldEFwcCA9IGNhdGFsb2dTdGF0ZS5yZXNldEFwcDsKCiAgICBpbml0aWFsaXplVHJha3RDb25uZWN0QnV0dG9uKCk7CiAgICBpbml0aWFsaXplVHJha3RMb2dvdXRCdXR0b24oKTsKICAgIGF0dGVtcHRUcmFrdEF1dG9Mb2dpbigpOwp9CgpleHBvcnQgZnVuY3Rpb24gc2V0VHJha3RMb2dnZWRPdXRTdGF0ZSgpIHsKICAgIGNsZWFyVHJha3RGcm9tU3RvcmFnZSgpOwogICAgaGlkZVRyYWt0U3RhdHVzKCk7CgogICAgY29uc3QgdHJha3RDb25uZWN0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0Q29ubmVjdEJ0bicpOwogICAgaWYgKHRyYWt0Q29ubmVjdEJ0bikgewogICAgICAgIHRyYWt0Q29ubmVjdEJ0bi5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgIH0KfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gU3RvcmFnZSBoZWxwZXJzCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpmdW5jdGlvbiBzYXZlVHJha3RUb1N0b3JhZ2UoYXV0aERhdGEpIHsKICAgIHRyeSB7CiAgICAgICAgY29uc3QgZXhwaXJ5RGF0ZSA9IG5ldyBEYXRlKCk7CiAgICAgICAgZXhwaXJ5RGF0ZS5zZXREYXRlKGV4cGlyeURhdGUuZ2V0RGF0ZSgpICsgRVhQSVJZX0RBWVMpOwogICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFRSQUtUX1NUT1JBR0VfS0VZLCBKU09OLnN0cmluZ2lmeSh7IC4uLmF1dGhEYXRhLCBleHBpcmVzQXQ6IGV4cGlyeURhdGUuZ2V0VGltZSgpIH0pKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICBjb25zb2xlLndhcm4oJ0ZhaWxlZCB0byBzYXZlIFRyYWt0IGF1dGg6JywgZSk7CiAgICB9Cn0KCmZ1bmN0aW9uIGdldFRyYWt0RnJvbVN0b3JhZ2UoKSB7CiAgICB0cnkgewogICAgICAgIGNvbnN0IHN0b3JlZCA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKFRSQUtUX1NUT1JBR0VfS0VZKTsKICAgICAgICBpZiAoIXN0b3JlZCkgcmV0dXJuIG51bGw7CiAgICAgICAgY29uc3QgZGF0YSA9IEpTT04ucGFyc2Uoc3RvcmVkKTsKICAgICAgICBpZiAoZGF0YS5leHBpcmVzQXQgJiYgZGF0YS5leHBpcmVzQXQgPCBEYXRlLm5vdygpKSB7CiAgICAgICAgICAgIGNsZWFyVHJha3RGcm9tU3RvcmFnZSgpOwogICAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIGRhdGE7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgY2xlYXJUcmFrdEZyb21TdG9yYWdlKCk7CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICB9Cn0KCmZ1bmN0aW9uIGNsZWFyVHJha3RGcm9tU3RvcmFnZSgpIHsKICAgIHRyeSB7IGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKFRSQUtUX1NUT1JBR0VfS0VZKTsgfSBjYXRjaCAoZSkgeyAvKiBub29wICovIH0KfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gT0F1dGggcG9wdXAgZmxvdwovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZnVuY3Rpb24gaW5pdGlhbGl6ZVRyYWt0Q29ubmVjdEJ1dHRvbigpIHsKICAgIGNvbnN0IGJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0cmFrdENvbm5lY3RCdG4nKTsKICAgIGlmICghYnRuKSByZXR1cm47CgogICAgYnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKCkgPT4gewogICAgICAgIHNldFRyYWt0Q29ubmVjdGluZyh0cnVlKTsKICAgICAgICB0cnkgewogICAgICAgICAgICAvLyAxLiBGZXRjaCB0aGUgYXV0aG9yaXphdGlvbiBVUkwgZnJvbSBiYWNrZW5kCiAgICAgICAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKCcvdG9rZW5zL3RyYWt0L2F1dGhvcml6ZScpOwogICAgICAgICAgICBpZiAoIXJlcy5vaykgewogICAgICAgICAgICAgICAgY29uc3QgZXJyID0gYXdhaXQgcmVzLmpzb24oKS5jYXRjaCgoKSA9PiAoe30pKTsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihlcnIuZGV0YWlsIHx8ICdGYWlsZWQgdG8gc3RhcnQgVHJha3QgYXV0aG9yaXphdGlvbicpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnN0IHsgdXJsIH0gPSBhd2FpdCByZXMuanNvbigpOwoKICAgICAgICAgICAgLy8gMi4gT3BlbiBPQXV0aCBwb3B1cAogICAgICAgICAgICBjb25zdCB0b2tlbnMgPSBhd2FpdCBvcGVuVHJha3RQb3B1cCh1cmwpOwoKICAgICAgICAgICAgLy8gMy4gQ2FsbCBpZGVudGl0eSBjaGVjayB0byBnZXQgdXNlciBpbmZvICsgZXhpc3Rpbmcgc2V0dGluZ3MKICAgICAgICAgICAgYXdhaXQgZmV0Y2hUcmFrdElkZW50aXR5KHRva2Vucyk7CgogICAgICAgICAgICAvLyA0LiBTYXZlIHRvIHN0b3JhZ2UKICAgICAgICAgICAgc2F2ZVRyYWt0VG9TdG9yYWdlKHRva2Vucyk7CgogICAgICAgICAgICB1bmxvY2tOYXZpZ2F0aW9uKCk7CiAgICAgICAgICAgIHN3aXRjaFNlY3Rpb24oJ2NvbmZpZycpOwogICAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgICAgICBzaG93VG9hc3QoZXJyLm1lc3NhZ2UgfHwgJ1RyYWt0IGxvZ2luIGZhaWxlZCcsICdlcnJvcicpOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHNldFRyYWt0Q29ubmVjdGluZyhmYWxzZSk7CiAgICAgICAgfQogICAgfSk7Cn0KCmZ1bmN0aW9uIGluaXRpYWxpemVUcmFrdExvZ291dEJ1dHRvbigpIHsKICAgIGNvbnN0IGJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0cmFrdExvZ291dEJ0bicpOwogICAgaWYgKCFidG4pIHJldHVybjsKICAgIGJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsKICAgICAgICBpZiAocmVzZXRBcHApIHJlc2V0QXBwKCk7CiAgICB9KTsKfQoKLyoqCiAqIE9wZW4gYSBwb3B1cCB3aW5kb3cgZm9yIFRyYWt0IE9BdXRoIGFuZCByZXNvbHZlIHdoZW4gdGhlIGNhbGxiYWNrIHBhZ2UKICogcG9zdHMgYSBtZXNzYWdlIGJhY2sgdG8gdXMuCiAqLwpmdW5jdGlvbiBvcGVuVHJha3RQb3B1cCh1cmwpIHsKICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgY29uc3Qgd2lkdGggPSA2MDA7CiAgICAgICAgY29uc3QgaGVpZ2h0ID0gNzAwOwogICAgICAgIGNvbnN0IGxlZnQgPSBNYXRoLnJvdW5kKHdpbmRvdy5zY3JlZW5YICsgKHdpbmRvdy5vdXRlcldpZHRoIC0gd2lkdGgpIC8gMik7CiAgICAgICAgY29uc3QgdG9wID0gTWF0aC5yb3VuZCh3aW5kb3cuc2NyZWVuWSArICh3aW5kb3cub3V0ZXJIZWlnaHQgLSBoZWlnaHQpIC8gMik7CgogICAgICAgIGNvbnN0IHBvcHVwID0gd2luZG93Lm9wZW4oCiAgICAgICAgICAgIHVybCwKICAgICAgICAgICAgJ3RyYWt0X29hdXRoJywKICAgICAgICAgICAgYHdpZHRoPSR7d2lkdGh9LGhlaWdodD0ke2hlaWdodH0sbGVmdD0ke2xlZnR9LHRvcD0ke3RvcH0scmVzaXphYmxlPXllcyxzY3JvbGxiYXJzPXllc2AKICAgICAgICApOwoKICAgICAgICBpZiAoIXBvcHVwKSB7CiAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ0NvdWxkIG5vdCBvcGVuIHRoZSBhdXRob3JpemF0aW9uIHBvcHVwLiBQbGVhc2UgYWxsb3cgcG9wdXBzIGZvciB0aGlzIHNpdGUuJykpOwogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQoKICAgICAgICBsZXQgc2V0dGxlZCA9IGZhbHNlOwoKICAgICAgICBmdW5jdGlvbiBvbk1lc3NhZ2UoZXZlbnQpIHsKICAgICAgICAgICAgLy8gT25seSBhY2NlcHQgbWVzc2FnZXMgZnJvbSBvdXIgb3duIG9yaWdpbgogICAgICAgICAgICBpZiAoZXZlbnQub3JpZ2luICE9PSB3aW5kb3cubG9jYXRpb24ub3JpZ2luKSByZXR1cm47CiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBldmVudC5kYXRhOwogICAgICAgICAgICBpZiAoIWRhdGEgfHwgdHlwZW9mIGRhdGEgIT09ICdvYmplY3QnKSByZXR1cm47CgogICAgICAgICAgICBpZiAoZGF0YS50eXBlID09PSAndHJha3RfYXV0aF9zdWNjZXNzJykgewogICAgICAgICAgICAgICAgaWYgKCFzZXR0bGVkKSB7CiAgICAgICAgICAgICAgICAgICAgc2V0dGxlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgY2xlYW51cCgpOwogICAgICAgICAgICAgICAgICAgIHJlc29sdmUoewogICAgICAgICAgICAgICAgICAgICAgICBhY2Nlc3NfdG9rZW46IGRhdGEuYWNjZXNzX3Rva2VuLAogICAgICAgICAgICAgICAgICAgICAgICByZWZyZXNoX3Rva2VuOiBkYXRhLnJlZnJlc2hfdG9rZW4sCiAgICAgICAgICAgICAgICAgICAgICAgIGV4cGlyZXNfYXQ6IGRhdGEuZXhwaXJlc19hdCwKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT09ICd0cmFrdF9hdXRoX2Vycm9yJykgewogICAgICAgICAgICAgICAgaWYgKCFzZXR0bGVkKSB7CiAgICAgICAgICAgICAgICAgICAgc2V0dGxlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgY2xlYW51cCgpOwogICAgICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoZGF0YS5lcnJvciB8fCAnVHJha3QgYXV0aG9yaXphdGlvbiBmYWlsZWQnKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vIEFsc28gZGV0ZWN0IGlmIHRoZSB1c2VyIGNsb3NlcyB0aGUgcG9wdXAgbWFudWFsbHkKICAgICAgICBjb25zdCBwb2xsVGltZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7CiAgICAgICAgICAgIGlmIChwb3B1cC5jbG9zZWQgJiYgIXNldHRsZWQpIHsKICAgICAgICAgICAgICAgIHNldHRsZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgY2xlYW51cCgpOwogICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignQXV0aG9yaXphdGlvbiB3aW5kb3cgd2FzIGNsb3NlZCcpKTsKICAgICAgICAgICAgfQogICAgICAgIH0sIDUwMCk7CgogICAgICAgIGZ1bmN0aW9uIGNsZWFudXAoKSB7CiAgICAgICAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgb25NZXNzYWdlKTsKICAgICAgICAgICAgY2xlYXJJbnRlcnZhbChwb2xsVGltZXIpOwogICAgICAgIH0KCiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBvbk1lc3NhZ2UpOwogICAgfSk7Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIElkZW50aXR5IGZldGNoICsgc2V0dGluZ3MgcG9wdWxhdGlvbgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYXN5bmMgZnVuY3Rpb24gZmV0Y2hUcmFrdElkZW50aXR5KHRva2VucykgewogICAgY29uc3QgcGF5bG9hZCA9IHsKICAgICAgICB0cmFrdF9hY2Nlc3NfdG9rZW46IHRva2Vucy5hY2Nlc3NfdG9rZW4sCiAgICAgICAgdHJha3RfcmVmcmVzaF90b2tlbjogdG9rZW5zLnJlZnJlc2hfdG9rZW4gfHwgbnVsbCwKICAgICAgICB0cmFrdF9leHBpcmVzX2F0OiB0b2tlbnMuZXhwaXJlc19hdCB8fCBudWxsLAogICAgfTsKCiAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCgnL3Rva2Vucy90cmFrdC9pZGVudGl0eScsIHsKICAgICAgICBtZXRob2Q6ICdQT1NUJywKICAgICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSwKICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSwKICAgIH0pOwoKICAgIGlmICghcmVzLm9rKSB7CiAgICAgICAgY29uc3QgZXJyID0gYXdhaXQgcmVzLmpzb24oKS5jYXRjaCgoKSA9PiAoe30pKTsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoZXJyLmRldGFpbCB8fCAnRmFpbGVkIHRvIHZlcmlmeSBUcmFrdCBpZGVudGl0eScpOwogICAgfQoKICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXMuanNvbigpOwogICAgY29uc3QgZGlzcGxheSA9IGRhdGEuZGlzcGxheSB8fCBkYXRhLnVzZXJuYW1lIHx8ICdUcmFrdCBVc2VyJzsKCiAgICBzaG93VHJha3RTdGF0dXMoZGlzcGxheSk7CgogICAgaWYgKGRhdGEuZXhpc3RzICYmIGRhdGEuc2V0dGluZ3MpIHsKICAgICAgICBzaG93VG9hc3QoYFdlbGNvbWUgYmFjaywgJHtkaXNwbGF5fSEgTG9hZGluZyB5b3VyIHNldHRpbmdz4oCmYCwgJ2luZm8nLCA1MDAwKTsKICAgICAgICBwb3B1bGF0ZVNldHRpbmdzKGRhdGEuc2V0dGluZ3MpOwoKICAgICAgICBjb25zdCBpbnN0YWxsSGVhZGVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3NlY3QtaW5zdGFsbCBoMicpOwogICAgICAgIGNvbnN0IGluc3RhbGxEZXNjID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3NlY3QtaW5zdGFsbCBwJyk7CiAgICAgICAgaWYgKGluc3RhbGxIZWFkZXIpIGluc3RhbGxIZWFkZXIudGV4dENvbnRlbnQgPSAnVXBkYXRlIFNldHRpbmdzJzsKICAgICAgICBpZiAoaW5zdGFsbERlc2MpIGluc3RhbGxEZXNjLnRleHRDb250ZW50ID0gJ1VwZGF0ZSB5b3VyIHByZWZlcmVuY2VzIGFuZCByZS1pbnN0YWxsLic7CgogICAgICAgIGNvbnN0IGJ0blRleHQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjc3VibWl0QnRuIC5idG4tdGV4dCcpOwogICAgICAgIGlmIChidG5UZXh0KSBidG5UZXh0LnRleHRDb250ZW50ID0gJ1VwZGF0ZSAmIFJlLUluc3RhbGwnOwogICAgfSBlbHNlIHsKICAgICAgICBzaG93VG9hc3QoYFdlbGNvbWUsICR7ZGlzcGxheX0hIFNldHRpbmcgdXAgeW91ciBhY2NvdW504oCmYCwgJ3N1Y2Nlc3MnLCA1MDAwKTsKICAgIH0KCiAgICAvLyBTdG9yZSBkaXNwbGF5IG5hbWUgc28gdGhlIGZvcm0gc3VibWl0IGNhbiB1c2UgaXQKICAgIGNvbnN0IHRyYWt0RGlzcGxheUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0RGlzcGxheU5hbWUnKTsKICAgIGlmICh0cmFrdERpc3BsYXlJbnB1dCkgdHJha3REaXNwbGF5SW5wdXQudmFsdWUgPSBkaXNwbGF5Owp9CgpmdW5jdGlvbiBwb3B1bGF0ZVNldHRpbmdzKHMpIHsKICAgIGlmIChzLmxhbmd1YWdlICYmIGxhbmd1YWdlU2VsZWN0KSBsYW5ndWFnZVNlbGVjdC52YWx1ZSA9IHMubGFuZ3VhZ2U7CgogICAgY29uc3QgcG9wdWxhcml0eVNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwb3B1bGFyaXR5U2VsZWN0Jyk7CiAgICBjb25zdCB5ZWFyTWluSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgneWVhck1pbicpOwogICAgY29uc3QgeWVhck1heElucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3llYXJNYXgnKTsKICAgIGNvbnN0IHNvcnRpbmdPcmRlclNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzb3J0aW5nT3JkZXJTZWxlY3QnKTsKCiAgICBpZiAocy5wb3B1bGFyaXR5ICYmIHBvcHVsYXJpdHlTZWxlY3QpIHBvcHVsYXJpdHlTZWxlY3QudmFsdWUgPSBzLnBvcHVsYXJpdHk7CiAgICBpZiAocy55ZWFyX21pbiAmJiB5ZWFyTWluSW5wdXQpIHllYXJNaW5JbnB1dC52YWx1ZSA9IHMueWVhcl9taW47CiAgICBpZiAocy55ZWFyX21heCAmJiB5ZWFyTWF4SW5wdXQpIHllYXJNYXhJbnB1dC52YWx1ZSA9IHMueWVhcl9tYXg7CiAgICBpZiAod2luZG93LnVwZGF0ZVllYXJTbGlkZXIpIHdpbmRvdy51cGRhdGVZZWFyU2xpZGVyKCk7CiAgICBpZiAocy5zb3J0aW5nX29yZGVyICYmIHNvcnRpbmdPcmRlclNlbGVjdCkgc29ydGluZ09yZGVyU2VsZWN0LnZhbHVlID0gcy5zb3J0aW5nX29yZGVyOwoKICAgIGNvbnN0IHBvc3RlclJhdGluZ1Byb3ZpZGVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bvc3RlclJhdGluZ1Byb3ZpZGVyJyk7CiAgICBjb25zdCBwb3N0ZXJSYXRpbmdBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncG9zdGVyUmF0aW5nQXBpS2V5Jyk7CiAgICBpZiAocG9zdGVyUmF0aW5nUHJvdmlkZXIgJiYgcG9zdGVyUmF0aW5nQXBpS2V5ICYmIHMucG9zdGVyX3JhdGluZz8ucHJvdmlkZXIgJiYgcy5wb3N0ZXJfcmF0aW5nPy5hcGlfa2V5KSB7CiAgICAgICAgcG9zdGVyUmF0aW5nUHJvdmlkZXIudmFsdWUgPSBzLnBvc3Rlcl9yYXRpbmcucHJvdmlkZXI7CiAgICAgICAgcG9zdGVyUmF0aW5nQXBpS2V5LnZhbHVlID0gcy5wb3N0ZXJfcmF0aW5nLmFwaV9rZXk7CiAgICAgICAgcG9zdGVyUmF0aW5nUHJvdmlkZXIuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScpKTsKICAgIH0KCiAgICBjb25zdCB0bWRiQXBpS2V5SW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndG1kYkFwaUtleScpOwogICAgaWYgKHMudG1kYl9hcGlfa2V5ICYmIHRtZGJBcGlLZXlJbnB1dCkgdG1kYkFwaUtleUlucHV0LnZhbHVlID0gcy50bWRiX2FwaV9rZXk7CgogICAgY29uc3Qgc2lta2xBcGlLZXlJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzaW1rbEFwaUtleScpOwogICAgaWYgKHMuc2lta2xfYXBpX2tleSAmJiBzaW1rbEFwaUtleUlucHV0KSBzaW1rbEFwaUtleUlucHV0LnZhbHVlID0gcy5zaW1rbF9hcGlfa2V5OwoKICAgIGNvbnN0IGdlbWluaUFwaUtleUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2dlbWluaUFwaUtleScpOwogICAgaWYgKHMuZ2VtaW5pX2FwaV9rZXkgJiYgZ2VtaW5pQXBpS2V5SW5wdXQpIGdlbWluaUFwaUtleUlucHV0LnZhbHVlID0gcy5nZW1pbmlfYXBpX2tleTsKCiAgICAvLyBHZW5yZXMKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJ2lucHV0W25hbWU9Im1vdmllLWdlbnJlIl0nKS5mb3JFYWNoKGNiID0+IGNiLmNoZWNrZWQgPSBmYWxzZSk7CiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dFtuYW1lPSJzZXJpZXMtZ2VucmUiXScpLmZvckVhY2goY2IgPT4gY2IuY2hlY2tlZCA9IGZhbHNlKTsKICAgIGlmIChzLmV4Y2x1ZGVkX21vdmllX2dlbnJlcykgcy5leGNsdWRlZF9tb3ZpZV9nZW5yZXMuZm9yRWFjaChpZCA9PiB7CiAgICAgICAgY29uc3QgY2IgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBpbnB1dFtuYW1lPSJtb3ZpZS1nZW5yZSJdW3ZhbHVlPSIke2lkfSJdYCk7CiAgICAgICAgaWYgKGNiKSBjYi5jaGVja2VkID0gdHJ1ZTsKICAgIH0pOwogICAgaWYgKHMuZXhjbHVkZWRfc2VyaWVzX2dlbnJlcykgcy5leGNsdWRlZF9zZXJpZXNfZ2VucmVzLmZvckVhY2goaWQgPT4gewogICAgICAgIGNvbnN0IGNiID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihgaW5wdXRbbmFtZT0ic2VyaWVzLWdlbnJlIl1bdmFsdWU9IiR7aWR9Il1gKTsKICAgICAgICBpZiAoY2IpIGNiLmNoZWNrZWQgPSB0cnVlOwogICAgfSk7CgogICAgLy8gQ2F0YWxvZ3MKICAgIGlmIChzLmNhdGFsb2dzICYmIEFycmF5LmlzQXJyYXkocy5jYXRhbG9ncykpIHsKICAgICAgICBjb25zdCBjYXRhbG9ncyA9IGdldENhdGFsb2dzID8gZ2V0Q2F0YWxvZ3MoKSA6IFtdOwogICAgICAgIHMuY2F0YWxvZ3MuZm9yRWFjaChyZW1vdGUgPT4gewogICAgICAgICAgICBjb25zdCBsb2NhbCA9IGNhdGFsb2dzLmZpbmQoYyA9PiBjLmlkID09PSByZW1vdGUuaWQpOwogICAgICAgICAgICBpZiAobG9jYWwpIHsKICAgICAgICAgICAgICAgIGxvY2FsLmVuYWJsZWQgPSByZW1vdGUuZW5hYmxlZDsKICAgICAgICAgICAgICAgIGlmIChyZW1vdGUubmFtZSkgbG9jYWwubmFtZSA9IHJlbW90ZS5uYW1lOwogICAgICAgICAgICAgICAgaWYgKHR5cGVvZiByZW1vdGUuZW5hYmxlZF9tb3ZpZSA9PT0gJ2Jvb2xlYW4nKSBsb2NhbC5lbmFibGVkTW92aWUgPSByZW1vdGUuZW5hYmxlZF9tb3ZpZTsKICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgcmVtb3RlLmVuYWJsZWRfc2VyaWVzID09PSAnYm9vbGVhbicpIGxvY2FsLmVuYWJsZWRTZXJpZXMgPSByZW1vdGUuZW5hYmxlZF9zZXJpZXM7CiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHJlbW90ZS5kaXNwbGF5X2F0X2hvbWUgPT09ICdib29sZWFuJykgbG9jYWwuZGlzcGxheV9hdF9ob21lID0gcmVtb3RlLmRpc3BsYXlfYXRfaG9tZTsKICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgcmVtb3RlLnNodWZmbGUgPT09ICdib29sZWFuJykgbG9jYWwuc2h1ZmZsZSA9IHJlbW90ZS5zaHVmZmxlOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgaWYgKHJlbmRlckNhdGFsb2dMaXN0KSByZW5kZXJDYXRhbG9nTGlzdCgpOwogICAgfQp9CgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBBdXRvLWxvZ2luCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgphc3luYyBmdW5jdGlvbiBhdHRlbXB0VHJha3RBdXRvTG9naW4oKSB7CiAgICBjb25zdCBzdG9yZWQgPSBnZXRUcmFrdEZyb21TdG9yYWdlKCk7CiAgICBpZiAoIXN0b3JlZD8uYWNjZXNzX3Rva2VuKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgICBhd2FpdCBmZXRjaFRyYWt0SWRlbnRpdHkoc3RvcmVkKTsKICAgICAgICB1bmxvY2tOYXZpZ2F0aW9uKCk7CiAgICAgICAgc3dpdGNoU2VjdGlvbignY29uZmlnJyk7CiAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLndhcm4oJ1RyYWt0IGF1dG8tbG9naW4gZmFpbGVkOicsIGVycik7CiAgICAgICAgY2xlYXJUcmFrdEZyb21TdG9yYWdlKCk7CiAgICB9Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIFVJIGhlbHBlcnMKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmZ1bmN0aW9uIHNldFRyYWt0Q29ubmVjdGluZyhsb2FkaW5nKSB7CiAgICBjb25zdCBidG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RDb25uZWN0QnRuJyk7CiAgICBpZiAoIWJ0bikgcmV0dXJuOwogICAgY29uc3QgdGV4dCA9IGJ0bi5xdWVyeVNlbGVjdG9yKCcuYnRuLXRleHQnKTsKICAgIGNvbnN0IGxvYWRlciA9IGJ0bi5xdWVyeVNlbGVjdG9yKCcubG9hZGVyJyk7CiAgICBidG4uZGlzYWJsZWQgPSBsb2FkaW5nOwogICAgaWYgKHRleHQpIHRleHQuY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgbG9hZGluZyk7CiAgICBpZiAobG9hZGVyKSBsb2FkZXIuY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgIWxvYWRpbmcpOwp9CgpleHBvcnQgZnVuY3Rpb24gc2hvd1RyYWt0U3RhdHVzKGRpc3BsYXlOYW1lKSB7CiAgICBjb25zdCBzdGF0dXNTZWN0aW9uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0U3RhdHVzU2VjdGlvbicpOwogICAgY29uc3QgZGlzcGxheUVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0U3RhdHVzRGlzcGxheScpOwogICAgY29uc3QgYXZhdGFyRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RTdGF0dXNBdmF0YXInKTsKICAgIGNvbnN0IGNvbm5lY3RCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RDb25uZWN0QnRuJyk7CgogICAgaWYgKGRpc3BsYXlFbCkgZGlzcGxheUVsLnRleHRDb250ZW50ID0gZGlzcGxheU5hbWU7CiAgICBpZiAoYXZhdGFyRWwpIGF2YXRhckVsLnRleHRDb250ZW50ID0gZ2V0SW5pdGlhbHMoZGlzcGxheU5hbWUpOwogICAgaWYgKHN0YXR1c1NlY3Rpb24pIHN0YXR1c1NlY3Rpb24uY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICBpZiAoY29ubmVjdEJ0bikgY29ubmVjdEJ0bi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKCiAgICAvLyBTaWRlYmFyIHByb2ZpbGUKICAgIGNvbnN0IHVzZXJQcm9maWxlV3JhcHBlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1c2VyLXByb2ZpbGUtZHJvcGRvd24td3JhcHBlcicpOwogICAgY29uc3QgdXNlckVtYWlsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VzZXItZW1haWwnKTsKICAgIGNvbnN0IHVzZXJBdmF0YXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndXNlci1hdmF0YXInKTsKICAgIGNvbnN0IGxvZ2luRm9ybUNhcmQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9naW5Gb3JtQ2FyZCcpOwoKICAgIGlmICh1c2VyRW1haWwpIHVzZXJFbWFpbC50ZXh0Q29udGVudCA9IGRpc3BsYXlOYW1lOwogICAgaWYgKHVzZXJBdmF0YXIpIHVzZXJBdmF0YXIudGV4dENvbnRlbnQgPSBnZXRJbml0aWFscyhkaXNwbGF5TmFtZSk7CiAgICBpZiAodXNlclByb2ZpbGVXcmFwcGVyKSB1c2VyUHJvZmlsZVdyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICAvLyBLZWVwIGxvZ2luRm9ybUNhcmQgdmlzaWJsZSBzbyB1c2VycyBjYW4gbmF2aWdhdGUgYmFjayBhbmQgc2VlIHRoZSBUcmFrdCB0YWIKICAgIC8vIEluc3RlYWQsIHN3aXRjaCB0byBUcmFrdCB0YWIgc28gaXQncyBjbGVhciB3aGljaCBwcm92aWRlciBpcyBhY3RpdmUKICAgIHRyeSB7CiAgICAgICAgY29uc3Qgc2F2ZWQgPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnd2F0Y2hseV9sb2dpbl90YWInKTsKICAgICAgICBpZiAoIXNhdmVkIHx8IHNhdmVkID09PSAndHJha3QnKSB7CiAgICAgICAgICAgIGNvbnN0IHRyYWt0VGFiID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RhYlRyYWt0Jyk7CiAgICAgICAgICAgIGlmICh0cmFrdFRhYikgdHJha3RUYWIuY2xpY2soKTsKICAgICAgICB9CiAgICB9IGNhdGNoKGUpIHt9Cn0KCmZ1bmN0aW9uIGhpZGVUcmFrdFN0YXR1cygpIHsKICAgIGNvbnN0IHN0YXR1c1NlY3Rpb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndHJha3RTdGF0dXNTZWN0aW9uJyk7CiAgICBjb25zdCBjb25uZWN0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RyYWt0Q29ubmVjdEJ0bicpOwogICAgaWYgKHN0YXR1c1NlY3Rpb24pIHN0YXR1c1NlY3Rpb24uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICBpZiAoY29ubmVjdEJ0bikgY29ubmVjdEJ0bi5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKCiAgICBjb25zdCB1c2VyUHJvZmlsZVdyYXBwZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndXNlci1wcm9maWxlLWRyb3Bkb3duLXdyYXBwZXInKTsKICAgIGlmICh1c2VyUHJvZmlsZVdyYXBwZXIpIHVzZXJQcm9maWxlV3JhcHBlci5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKfQoKZnVuY3Rpb24gZ2V0SW5pdGlhbHMobmFtZSkgewogICAgaWYgKCFuYW1lKSByZXR1cm4gJz8nOwogICAgY29uc3QgcGFydHMgPSBuYW1lLnRyaW0oKS5zcGxpdCgvW1xzLl8tXSsvKTsKICAgIGlmIChwYXJ0cy5sZW5ndGggPj0gMikgcmV0dXJuIChwYXJ0c1swXVswXSArIHBhcnRzWzFdWzBdKS50b1VwcGVyQ2FzZSgpOwogICAgcmV0dXJuIG5hbWUuc3Vic3RyaW5nKDAsIDIpLnRvVXBwZXJDYXNlKCk7Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIEV4cG9ydGVkIGhlbHBlcnMgZm9yIGZvcm0uanMKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmV4cG9ydCBmdW5jdGlvbiBnZXRUcmFrdFRva2Vuc0Zyb21TdG9yYWdlKCkgewogICAgcmV0dXJuIGdldFRyYWt0RnJvbVN0b3JhZ2UoKTsKfQo=" | base64 --decode > "$REPO/app/static/js/modules/trakt.js" +echo " wrote app/static/js/modules/trakt.js" + +mkdir -p "$REPO/app/templates/components" +echo "PCEtLSBTRUNUSU9OIDE6IExPR0lOIC0tPgo8c2VjdGlvbiBpZD0ic2VjdC1sb2dpbiIgY2xhc3M9InNwYWNlLXktNiBoaWRkZW4gYW5pbWF0ZS1mYWRlLWluIj4KICAgIDxkaXYgY2xhc3M9Im1iLTgiPgogICAgICAgIDxoMiBjbGFzcz0idGV4dC0zeGwgZm9udC1ib2xkIHRleHQtd2hpdGUgbWItMiI+Q29ubmVjdCBZb3VyIExpYnJhcnk8L2gyPgogICAgICAgIDxwIGNsYXNzPSJ0ZXh0LXNsYXRlLTQwMCI+TG9nIGluIHdpdGggU3RyZW1pbyBvciBUcmFrdCBzbyBXYXRjaGx5IGNhbiByZWFkIHlvdXIgd2F0Y2ggaGlzdG9yeS48L3A+CiAgICA8L2Rpdj4KCiAgICA8IS0tIExvZ2dlZCBJbiBTdGF0dXMgKHNob3duIGFmdGVyIGxvZ2luIC0gc2hhcmVkIGFjcm9zcyBib3RoIHByb3ZpZGVycykgLS0+CiAgICA8ZGl2IGlkPSJsb2dpblN0YXR1c1NlY3Rpb24iIGNsYXNzPSJoaWRkZW4gYmctbmV1dHJhbC05MDAvNjAgYm9yZGVyIGJvcmRlci13aGl0ZS8xMCByb3VuZGVkLTJ4bCBwLTYgbWQ6cC04IGJhY2tkcm9wLWJsdXItc20gc2hhZG93LXhsIHNoYWRvdy1ibGFjay8yMCI+CiAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIGdhcC00Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idy0xMCBoLTEwIHJvdW5kZWQtZnVsbCBiZy13aGl0ZSB0ZXh0LWJsYWNrIHJpbmctMSByaW5nLXdoaXRlLzEwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGZvbnQtYm9sZCB0ZXh0LXNtIGZsZXgtc2hyaW5rLTAiCiAgICAgICAgICAgICAgICAgICAgaWQ9ImxvZ2luU3RhdHVzQXZhdGFyIj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9InRleHQteHMgdGV4dC1zbGF0ZS01MDAgbWItMC41Ij5Mb2dnZWQgaW4gYXM8L2Rpdj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ0ZXh0LXNtIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gdHJ1bmNhdGUiIGlkPSJsb2dpblN0YXR1c0VtYWlsIj48L2Rpdj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJsb2dpblN0YXR1c0xvZ291dEJ0biIKICAgICAgICAgICAgICAgIGNsYXNzPSJmbGV4LXNocmluay0wIGJnLXJlZC02MDAgaG92ZXI6YmctcmVkLTcwMCB0ZXh0LXdoaXRlIGZvbnQtbWVkaXVtIHB5LTIuNSBweC00IHJvdW5kZWQteGwgdHJhbnNpdGlvbiBib3JkZXIgYm9yZGVyLXJlZC03MDAgc2hhZG93LWxnIHNoYWRvdy1yZWQtOTAwLzIwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGdhcC0yIj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNCBoLTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIgogICAgICAgICAgICAgICAgICAgICAgICBkPSJNMTcgMTZsNC00bTAgMGwtNC00bTQgNEg3bTYgNHYxYTMgMyAwIDAxLTMgM0g2YTMgMyAwIDAxLTMtM1Y3YTMgMyAwIDAxMy0zaDRhMyAzIDAgMDEzIDN2MSI+CiAgICAgICAgICAgICAgICAgICAgPC9wYXRoPgogICAgICAgICAgICAgICAgPC9zdmc+CiAgICAgICAgICAgICAgICA8c3Bhbj5Mb2dvdXQ8L3NwYW4+CiAgICAgICAgICAgIDwvYnV0dG9uPgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CgogICAgPGRpdiBpZD0ibG9naW5Gb3JtQ2FyZCIgY2xhc3M9ImJnLW5ldXRyYWwtOTAwLzYwIGJvcmRlciBib3JkZXItd2hpdGUvMTAgcm91bmRlZC0yeGwgcC02IG1kOnAtOCBiYWNrZHJvcC1ibHVyLXNtIHNoYWRvdy14bCBzaGFkb3ctYmxhY2svMjAiPgoKICAgICAgICA8IS0tIFByb3ZpZGVyIFRhYnMgLS0+CiAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBnYXAtMSBtYi02IGJnLW5ldXRyYWwtODAwLzYwIHAtMSByb3VuZGVkLXhsIj4KICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJ0YWJTdHJlbWlvIgogICAgICAgICAgICAgICAgY2xhc3M9ImxvZ2luLXRhYiBmbGV4LTEgcHktMi41IHB4LTQgcm91bmRlZC1sZyB0ZXh0LXNtIGZvbnQtbWVkaXVtIHRyYW5zaXRpb24tYWxsIGJnLXdoaXRlIHRleHQtYmxhY2sgc2hhZG93IgogICAgICAgICAgICAgICAgZGF0YS10YWI9InN0cmVtaW8iPgogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGdhcC0yIj4KICAgICAgICAgICAgICAgICAgICA8aW1nIHNyYz0iaHR0cHM6Ly9zdHJlbWlvLmNvbS93ZWJzaXRlL3N0cmVtaW8tbG9nby1zbWFsbC5wbmciIGNsYXNzPSJ3LTQgaC00IiBhbHQ9IiI+CiAgICAgICAgICAgICAgICAgICAgU3RyZW1pbwogICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJ0YWJUcmFrdCIKICAgICAgICAgICAgICAgIGNsYXNzPSJsb2dpbi10YWIgZmxleC0xIHB5LTIuNSBweC00IHJvdW5kZWQtbGcgdGV4dC1zbSBmb250LW1lZGl1bSB0cmFuc2l0aW9uLWFsbCB0ZXh0LXNsYXRlLTQwMCBob3Zlcjp0ZXh0LXdoaXRlIgogICAgICAgICAgICAgICAgZGF0YS10YWI9InRyYWt0Ij4KICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBnYXAtMiI+CiAgICAgICAgICAgICAgICAgICAgPCEtLSBUcmFrdCBpY29uIChzaW1wbGUgInQiIGJhZGdlKSAtLT4KICAgICAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPSJ3LTQgaC00IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9ImN1cnJlbnRDb2xvciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMiAyQzYuNDc3IDIgMiA2LjQ3NyAyIDEyczQuNDc3IDEwIDEwIDEwIDEwLTQuNDc3IDEwLTEwUzE3LjUyMyAyIDEyIDJ6bS0uNSA1aDEuNXY2aDN2MS41aC00LjVWN3oiLz4KICAgICAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgICAgICAgICBUcmFrdAogICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPCEtLSA9PT09PSBTVFJFTUlPIFBBTkVMID09PT09IC0tPgogICAgICAgIDxkaXYgaWQ9InBhbmVsU3RyZW1pbyI+CiAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0ic3RyZW1pb0xvZ2luQnRuIgogICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBiZy1zdHJlbWlvIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcHktNCByb3VuZGVkLXhsIHRyYW5zaXRpb24gZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTMgYm9yZGVyIGJvcmRlci1zdHJlbWlvLWJvcmRlciBzaGFkb3ctbGcgc2hhZG93LXN0cmVtaW8vMjAgZ3JvdXAgaG92ZXI6Ymctd2hpdGUgaG92ZXI6dGV4dC1ibGFjayBob3Zlcjpib3JkZXItd2hpdGUvMTAiPgogICAgICAgICAgICAgICAgPGltZyBzcmM9Imh0dHBzOi8vc3RyZW1pby5jb20vd2Vic2l0ZS9zdHJlbWlvLWxvZ28tc21hbGwucG5nIgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ3LTYgaC02IGdyb3VwLWhvdmVyOnNjYWxlLTExMCB0cmFuc2l0aW9uLXRyYW5zZm9ybSIgYWx0PSJTdHJlbWlvIj4KICAgICAgICAgICAgICAgIDxzcGFuIGlkPSJzdHJlbWlvTG9naW5UZXh0IiBjbGFzcz0idGV4dC1sZyI+TG9naW4gd2l0aCBTdHJlbWlvPC9zcGFuPgogICAgICAgICAgICA8L2J1dHRvbj4KCiAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJoaWRkZW4iIGlkPSJhdXRoS2V5Ij4KCiAgICAgICAgICAgIDwhLS0gRGl2aWRlciAtLT4KICAgICAgICAgICAgPGRpdiBpZD0iZW1haWxQd2REaXZpZGVyIiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgbXktNiI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJoLXB4IGJnLXdoaXRlLzEwIHctZnVsbCI+PC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ0ZXh0LXhzIHRleHQtc2xhdGUtNTAwIj5vcjwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iaC1weCBiZy13aGl0ZS8xMCB3LWZ1bGwiPjwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDwhLS0gRW1haWwvUGFzc3dvcmQgTG9naW4gLS0+CiAgICAgICAgICAgIDxkaXYgaWQ9ImVtYWlsUHdkU2VjdGlvbiIgY2xhc3M9ImdyaWQgZ2FwLTMiPgogICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzPSJ0ZXh0LXhzIHRleHQtc2xhdGUtNDAwIj5FbWFpbDwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgaWQ9ImVtYWlsSW5wdXQiIHR5cGU9ImVtYWlsIiBhdXRvY29tcGxldGU9ImVtYWlsIiBpbnB1dG1vZGU9ImVtYWlsIgogICAgICAgICAgICAgICAgICAgIHNwZWxsY2hlY2s9ImZhbHNlIiByZXF1aXJlZCBwbGFjZWhvbGRlcj0ieW91QGV4YW1wbGUuY29tIgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ3LWZ1bGwgYmctbmV1dHJhbC05MDAgYm9yZGVyIGJvcmRlci1zbGF0ZS03MDAgcm91bmRlZC14bCBweC00IHB5LTMuNSB0ZXh0LXdoaXRlIHBsYWNlaG9sZGVyLXNsYXRlLTUwMCBmb2N1czpyaW5nLTIgZm9jdXM6cmluZy13aGl0ZS8yMCBmb2N1czpib3JkZXItd2hpdGUvMzAgb3V0bGluZS1ub25lIHRyYW5zaXRpb24tYWxsIj4KICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzcz0idGV4dC14cyB0ZXh0LXNsYXRlLTQwMCI+UGFzc3dvcmQ8L2xhYmVsPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0icmVsYXRpdmUiPgogICAgICAgICAgICAgICAgICAgIDxpbnB1dCBpZD0icGFzc3dvcmRJbnB1dCIgdHlwZT0icGFzc3dvcmQiIGF1dG9jb21wbGV0ZT0iY3VycmVudC1wYXNzd29yZCIKICAgICAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9IllvdXIgU3RyZW1pbyBwYXNzd29yZCIKICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBiZy1uZXV0cmFsLTkwMCBib3JkZXIgYm9yZGVyLXNsYXRlLTcwMCByb3VuZGVkLXhsIHBsLTQgcHItMTIgcHktMy41IHRleHQtd2hpdGUgcGxhY2Vob2xkZXItc2xhdGUtNTAwIGZvY3VzOnJpbmctMiBmb2N1czpyaW5nLXdoaXRlLzIwIGZvY3VzOmJvcmRlci13aGl0ZS8zMCBvdXRsaW5lLW5vbmUgdHJhbnNpdGlvbi1hbGwiPgogICAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIgogICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz0idG9nZ2xlLWJ0biBhYnNvbHV0ZSByaWdodC0yIHRvcC0xLzIgLXRyYW5zbGF0ZS15LTEvMiBiZy13aGl0ZS8xMCBob3ZlcjpiZy13aGl0ZS8yMCB0ZXh0LXdoaXRlIHAtMiByb3VuZGVkLWxnIgogICAgICAgICAgICAgICAgICAgICAgICBhcmlhLWxhYmVsPSJTaG93IHBhc3N3b3JkIiB0aXRsZT0iU2hvdyIgZGF0YS10YXJnZXQ9InBhc3N3b3JkSW5wdXQiPgogICAgICAgICAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPSJ3LTQgaC00IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEgMTJzNC03IDExLTcgMTEgNyAxMSA3LTQgNy0xMSA3LTExLTctMTEtN3oiIC8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIzIiAvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJlbWFpbFB3ZENvbnRpbnVlQnRuIgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJtdC0yIHctZnVsbCBiZy13aGl0ZSB0ZXh0LWJsYWNrIGhvdmVyOmJnLXdoaXRlLzkwIGZvbnQtbWVkaXVtIHB5LTMgcm91bmRlZC14bCB0cmFuc2l0aW9uIGJvcmRlciBib3JkZXItd2hpdGUvMTAgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTIiPgogICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJidG4tdGV4dCI+Q29udGludWUgd2l0aCBFbWFpbDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJsb2FkZXIgaGlkZGVuIHctNSBoLTUgYm9yZGVyLTIgYm9yZGVyLWJsYWNrLzMwIGJvcmRlci10LWJsYWNrIHJvdW5kZWQtZnVsbCBhbmltYXRlLXNwaW4iPgogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPC9idXR0b24+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgPCEtLSBJbmxpbmUgZXJyb3IgZm9yIGVtYWlsL3Bhc3N3b3JkIGxvZ2luIC0tPgogICAgICAgICAgICA8ZGl2IGlkPSJlbWFpbFB3ZEVycm9yIiBjbGFzcz0iaGlkZGVuIG10LTMgcC0zIGJnLXJlZC01MDAvMTAgYm9yZGVyIGJvcmRlci1yZWQtNTAwLzIwIHJvdW5kZWQteGwgdGV4dC1yZWQtMjAwIHRleHQtc20iPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDwhLS0gRGlzY2xhaW1lciAtLT4KICAgICAgICAgICAgPGRpdiBpZD0iZW1haWxQd2REaXNjbGFpbWVyIgogICAgICAgICAgICAgICAgY2xhc3M9Im10LTQgdGV4dC14cyBsZWFkaW5nLXJlbGF4ZWQgYmcteWVsbG93LTUwMC8xMCBib3JkZXIgYm9yZGVyLXllbGxvdy01MDAvMzAgdGV4dC15ZWxsb3ctMjAwIHJvdW5kZWQteGwgcC0zIj4KICAgICAgICAgICAgICAgIDxzdHJvbmcgY2xhc3M9InRleHQteWVsbG93LTMwMCI+V2h5IGVtYWlsICZhbXA7IHBhc3N3b3JkPzwvc3Ryb25nPgogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImJsb2NrIG10LTEiPldlIHN0b3JlIHlvdXIgY3JlZGVudGlhbHMgc2VjdXJlbHkgdG8gZ2VuZXJhdGUgYSBmcmVzaCBTdHJlbWlvIGF1dGgKICAgICAgICAgICAgICAgICAgICBrZXkgYXV0b21hdGljYWxseSB3aGVuIG5lZWRlZC4gVGhpcyBhdm9pZHMgZXhwaXJlZCBrZXlzIGFuZCBrZWVwcyB5b3VyIGFkZG9uIHdvcmtpbmcKICAgICAgICAgICAgICAgICAgICB3aXRob3V0IG1hbnVhbCByZS1sb2dpbi48L3NwYW4+CiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYmxvY2sgbXQtMiI+UHJlZmVyIG5vdCB0byBzaGFyZSB5b3VyIHBhc3N3b3JkPyBVc2UgdGhlIFN0cmVtaW8gbG9naW4gYWJvdmUgdG8KICAgICAgICAgICAgICAgICAgICBzdXBwbHkgYW4gYXV0aCBrZXkuIE5vdGU6IGF1dGgga2V5cyBjYW4gZXhwaXJlIGFuZCBtYXkgcmVxdWlyZSBwZXJpb2RpYwogICAgICAgICAgICAgICAgICAgIHJlLWF1dGhlbnRpY2F0aW9uLjwvc3Bhbj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICAgICAgPCEtLSBlbmQgI3BhbmVsU3RyZW1pbyAtLT4KCiAgICAgICAgPCEtLSA9PT09PSBUUkFLVCBQQU5FTCA9PT09PSAtLT4KICAgICAgICA8ZGl2IGlkPSJwYW5lbFRyYWt0IiBjbGFzcz0iaGlkZGVuIj4KCiAgICAgICAgICAgIDwhLS0gTG9nZ2VkLWluIHN0YXRlIC0tPgogICAgICAgICAgICA8ZGl2IGlkPSJ0cmFrdFN0YXR1c1NlY3Rpb24iIGNsYXNzPSJoaWRkZW4gbWItNCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWJldHdlZW4gZ2FwLTQgcC00IGJnLW5ldXRyYWwtODAwLzYwIHJvdW5kZWQteGwgYm9yZGVyIGJvcmRlci13aGl0ZS8xMCI+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ3LTEwIGgtMTAgcm91bmRlZC1mdWxsIGJnLVsjZWQxYzI0XSB0ZXh0LXdoaXRlIHJpbmctMSByaW5nLXdoaXRlLzEwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGZvbnQtYm9sZCB0ZXh0LXNtIGZsZXgtc2hyaW5rLTAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0idHJha3RTdGF0dXNBdmF0YXIiPlQ8L2Rpdj4KICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iZmxleC1ncm93IG1pbi13LTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC14cyB0ZXh0LXNsYXRlLTUwMCBtYi0wLjUiPkNvbm5lY3RlZCBhczwvZGl2PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC1zbSB0ZXh0LXdoaXRlIGZvbnQtbWVkaXVtIHRydW5jYXRlIiBpZD0idHJha3RTdGF0dXNEaXNwbGF5Ij48L2Rpdj4KICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJ0cmFrdExvZ291dEJ0biIKICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9ImZsZXgtc2hyaW5rLTAgYmctcmVkLTYwMCBob3ZlcjpiZy1yZWQtNzAwIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcHktMiBweC0zIHJvdW5kZWQteGwgdHJhbnNpdGlvbiBib3JkZXIgYm9yZGVyLXJlZC03MDAgdGV4dC1zbSI+CiAgICAgICAgICAgICAgICAgICAgICAgIERpc2Nvbm5lY3QKICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDwhLS0gQ29ubmVjdCBidXR0b24gLS0+CiAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0idHJha3RDb25uZWN0QnRuIgogICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBiZy1bI2VkMWMyNF0gaG92ZXI6YmctWyNjNDEwMTldIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcHktNCByb3VuZGVkLXhsIHRyYW5zaXRpb24gZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTMgYm9yZGVyIGJvcmRlci1bI2IwMGUxNV0gc2hhZG93LWxnIHNoYWRvdy1yZWQtOTAwLzIwIj4KICAgICAgICAgICAgICAgIDwhLS0gVHJha3QgIlQiIGxvZ28gYXMgc2ltcGxlIFNWRyAtLT4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNiBoLTYgZmxleC1zaHJpbmstMCIgdmlld0JveD0iMCAwIDMyIDMyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgY3g9IjE2IiBjeT0iMTYiIHI9IjE2IiBmaWxsPSJ3aGl0ZSIvPgogICAgICAgICAgICAgICAgICAgIDx0ZXh0IHg9IjE2IiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9ImJvbGQiIGZpbGw9IiNlZDFjMjQiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIj50PC90ZXh0PgogICAgICAgICAgICAgICAgPC9zdmc+CiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYnRuLXRleHQgdGV4dC1sZyI+Q29ubmVjdCB3aXRoIFRyYWt0PC9zcGFuPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0ibG9hZGVyIGhpZGRlbiB3LTUgaC01IGJvcmRlci0yIGJvcmRlci13aGl0ZS8zMCBib3JkZXItdC13aGl0ZSByb3VuZGVkLWZ1bGwgYW5pbWF0ZS1zcGluIj48L2Rpdj4KICAgICAgICAgICAgPC9idXR0b24+CgogICAgICAgICAgICA8IS0tIEhpZGRlbiBmaWVsZCB0byBjYXJyeSBUcmFrdCBkaXNwbGF5IG5hbWUgdGhyb3VnaCB0byBmb3JtIHN1Ym1pc3Npb24gLS0+CiAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJoaWRkZW4iIGlkPSJ0cmFrdERpc3BsYXlOYW1lIj4KCiAgICAgICAgICAgIDxkaXYgY2xhc3M9Im10LTQgdGV4dC14cyBsZWFkaW5nLXJlbGF4ZWQgYmctYmx1ZS01MDAvMTAgYm9yZGVyIGJvcmRlci1ibHVlLTUwMC8zMCB0ZXh0LWJsdWUtMjAwIHJvdW5kZWQteGwgcC0zIj4KICAgICAgICAgICAgICAgIDxzdHJvbmcgY2xhc3M9InRleHQtYmx1ZS0zMDAiPkhvdyBpdCB3b3Jrczwvc3Ryb25nPgogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImJsb2NrIG10LTEiPkNsaWNraW5nICJDb25uZWN0IHdpdGggVHJha3QiIG9wZW5zIGEgc2VjdXJlIFRyYWt0IGF1dGhvcml6YXRpb24KICAgICAgICAgICAgICAgICAgICB3aW5kb3cuIEFmdGVyIHlvdSBhcHByb3ZlIGFjY2VzcywgV2F0Y2hseSB1c2VzIHlvdXIgVHJha3Qgd2F0Y2ggaGlzdG9yeSB0byBnZW5lcmF0ZQogICAgICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyDigJQgbm8gU3RyZW1pbyBhY2NvdW50IG5lZWRlZC48L3NwYW4+CiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYmxvY2sgbXQtMiI+WW91ciBUcmFrdCBhY2Nlc3MgdG9rZW4gaXMgc3RvcmVkIGVuY3J5cHRlZCBvbiB0aGUgc2VydmVyIGFuZAogICAgICAgICAgICAgICAgICAgIGlzIG5ldmVyIHNoYXJlZCBvciBleHBvc2VkIGluIFVSTHMuPC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgICAgICA8IS0tIGVuZCAjcGFuZWxUcmFrdCAtLT4KCiAgICA8L2Rpdj4KICAgIDwhLS0gZW5kICNsb2dpbkZvcm1DYXJkIC0tPgo8L3NlY3Rpb24+Cgo8c2NyaXB0PgovLyBUYWIgc3dpdGNoaW5nIGxvZ2ljIChpbmxpbmUgdG8gYXZvaWQgbW9kdWxlLWxvYWRpbmcgb3JkZXIgaXNzdWVzKQooZnVuY3Rpb24gKCkgewogICAgZnVuY3Rpb24gc3dpdGNoTG9naW5UYWIodGFiKSB7CiAgICAgICAgY29uc3QgdGFicyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5sb2dpbi10YWInKTsKICAgICAgICBjb25zdCBwYW5lbFN0cmVtaW8gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncGFuZWxTdHJlbWlvJyk7CiAgICAgICAgY29uc3QgcGFuZWxUcmFrdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYW5lbFRyYWt0Jyk7CgogICAgICAgIHRhYnMuZm9yRWFjaCh0ID0+IHsKICAgICAgICAgICAgY29uc3QgYWN0aXZlID0gdC5kYXRhc2V0LnRhYiA9PT0gdGFiOwogICAgICAgICAgICB0LmNsYXNzTGlzdC50b2dnbGUoJ2JnLXdoaXRlJywgYWN0aXZlKTsKICAgICAgICAgICAgdC5jbGFzc0xpc3QudG9nZ2xlKCd0ZXh0LWJsYWNrJywgYWN0aXZlKTsKICAgICAgICAgICAgdC5jbGFzc0xpc3QudG9nZ2xlKCdzaGFkb3cnLCBhY3RpdmUpOwogICAgICAgICAgICB0LmNsYXNzTGlzdC50b2dnbGUoJ3RleHQtc2xhdGUtNDAwJywgIWFjdGl2ZSk7CiAgICAgICAgICAgIHQuY2xhc3NMaXN0LnRvZ2dsZSgnaG92ZXI6dGV4dC13aGl0ZScsICFhY3RpdmUpOwogICAgICAgIH0pOwoKICAgICAgICBpZiAocGFuZWxTdHJlbWlvKSBwYW5lbFN0cmVtaW8uY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgdGFiICE9PSAnc3RyZW1pbycpOwogICAgICAgIGlmIChwYW5lbFRyYWt0KSBwYW5lbFRyYWt0LmNsYXNzTGlzdC50b2dnbGUoJ2hpZGRlbicsIHRhYiAhPT0gJ3RyYWt0Jyk7CgogICAgICAgIC8vIFBlcnNpc3QgY2hvaWNlCiAgICAgICAgdHJ5IHsgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ3dhdGNobHlfbG9naW5fdGFiJywgdGFiKTsgfSBjYXRjaCAoZSkgeyB9CiAgICB9CgogICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGZ1bmN0aW9uICgpIHsKICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcubG9naW4tdGFiJykuZm9yRWFjaChidG4gPT4gewogICAgICAgICAgICBidG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiBzd2l0Y2hMb2dpblRhYihidG4uZGF0YXNldC50YWIpKTsKICAgICAgICB9KTsKCiAgICAgICAgLy8gUmVzdG9yZSBsYXN0IHRhYgogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGNvbnN0IHNhdmVkID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oJ3dhdGNobHlfbG9naW5fdGFiJyk7CiAgICAgICAgICAgIGlmIChzYXZlZCA9PT0gJ3RyYWt0Jykgc3dpdGNoTG9naW5UYWIoJ3RyYWt0Jyk7CiAgICAgICAgfSBjYXRjaCAoZSkgeyB9CiAgICB9KTsKfSkoKTsKPC9zY3JpcHQ+Cgo8c2NyaXB0PgovLyBIaWRlIHRoZSBUcmFrdCB0YWIgaWYgVHJha3QgaXMgbm90IGNvbmZpZ3VyZWQgb24gdGhpcyBzZXJ2ZXIKKGZ1bmN0aW9uICgpIHsKICAgIGZldGNoKCcvdG9rZW5zL3RyYWt0L2NvbmZpZycpCiAgICAgICAgLnRoZW4ociA9PiByLmpzb24oKSkKICAgICAgICAudGhlbihkYXRhID0+IHsKICAgICAgICAgICAgaWYgKCFkYXRhLmNvbmZpZ3VyZWQpIHsKICAgICAgICAgICAgICAgIGNvbnN0IHRyYWt0VGFiID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RhYlRyYWt0Jyk7CiAgICAgICAgICAgICAgICBpZiAodHJha3RUYWIpIHRyYWt0VGFiLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgICAgICAgIH0KICAgICAgICB9KQogICAgICAgIC5jYXRjaCgoKSA9PiB7CiAgICAgICAgICAgIC8vIElmIHRoZSBlbmRwb2ludCBmYWlscyAodW5saWtlbHkpLCBsZWF2ZSB0YWJzIGFzLWlzCiAgICAgICAgfSk7Cn0pKCk7Cjwvc2NyaXB0PgoK" | base64 --decode > "$REPO/app/templates/components/section_login.html" +echo " wrote app/templates/components/section_login.html" + +mkdir -p "$REPO/app/templates/components" +echo "PCEtLSBNb2JpbGUgSGVhZGVyIC0tPgo8ZGl2IGlkPSJtb2JpbGVIZWFkZXIiCiAgICBjbGFzcz0ibWQ6aGlkZGVuIGZpeGVkIHRvcC0wIGxlZnQtMCByaWdodC0wIHotNTAgcHgtNCBweS0zIGJnLW5ldXRyYWwtOTUwIGJvcmRlci1iIGJvcmRlci1zbGF0ZS04MDAgZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTQiPgogICAgPGJ1dHRvbiBpZD0ibW9iaWxlTmF2VG9nZ2xlIiBhcmlhLWxhYmVsPSJPcGVuIG5hdmlnYXRpb24iIGFyaWEtZXhwYW5kZWQ9ImZhbHNlIgogICAgICAgIGNsYXNzPSJoYW1idXJnZXIgcC0yIHJvdW5kZWQtbWQgaG92ZXI6YmctbmV1dHJhbC04MDAvNTAiPgogICAgICAgIDxzcGFuIGNsYXNzPSJiYXIgdG9wIj48L3NwYW4+CiAgICAgICAgPHNwYW4gY2xhc3M9ImJhciBtaWRkbGUiPjwvc3Bhbj4KICAgICAgICA8c3BhbiBjbGFzcz0iYmFyIGJvdHRvbSI+PC9zcGFuPgogICAgPC9idXR0b24+CiAgICA8aW1nIHNyYz0iL2FwcC9zdGF0aWMvbG9nby5wbmciIGFsdD0iV2F0Y2hseSIgY2xhc3M9InctOCBoLTggcm91bmRlZC1sZyByaW5nLTEgcmluZy13aGl0ZS8xNSBiZy1ibGFjayI+CiAgICA8aDEgY2xhc3M9ImZvbnQtYm9sZCB0ZXh0LXhsIHRleHQtd2hpdGUiPldhdGNobHk8L2gxPgo8L2Rpdj4KCjwhLS0gTW9iaWxlIE5hdiBCYWNrZHJvcCAtLT4KPGRpdiBpZD0ibW9iaWxlTmF2QmFja2Ryb3AiIGNsYXNzPSJmaXhlZCBpbnNldC0wIGJnLWJsYWNrLzUwIHotMzAgaGlkZGVuIG1kOmhpZGRlbiI+PC9kaXY+Cgo8IS0tIFNpZGViYXIgTmF2aWdhdGlvbiAtLT4KPGFzaWRlIGlkPSJtYWluU2lkZWJhciIKICAgIGNsYXNzPSJmaXhlZCBpbnNldC15LTAgbGVmdC0wIHotNDAgdy03MiB0cmFuc2Zvcm0gLXRyYW5zbGF0ZS14LWZ1bGwgdHJhbnNpdGlvbi10cmFuc2Zvcm0gZHVyYXRpb24tMzAwIGVhc2Utb3V0IGJnLW5ldXRyYWwtOTUwLzUwIGJhY2tkcm9wLWJsdXIteGwgYm9yZGVyLWIgbWQ6Ym9yZGVyLWItMCBtZDpib3JkZXItciBib3JkZXItc2xhdGUtODAwIGZsZXggZmxleC1jb2wgZmxleC1zaHJpbmstMCBtZDp0cmFuc2xhdGUteC0wIHB0LVtlbnYoc2FmZS1hcmVhLWluc2V0LXRvcCldIHBiLVtlbnYoc2FmZS1hcmVhLWluc2V0LWJvdHRvbSldIj4KICAgIDxkaXYgY2xhc3M9InAtNiBtZDpwLTggZmxleC1jb2wgaXRlbXMtc3RhcnQgZ2FwLTQgaGlkZGVuIG1kOmZsZXgiPgogICAgICAgIDxkaXYgY2xhc3M9ImZsZXggaXRlbXMtY2VudGVyIGdhcC0zIj4KICAgICAgICAgICAgPGltZyBzcmM9Ii9hcHAvc3RhdGljL2xvZ28ucG5nIiBhbHQ9IldhdGNobHkiCiAgICAgICAgICAgICAgICBjbGFzcz0idy0xMCBoLTEwIHJvdW5kZWQteGwgc2hhZG93LWxnIHNoYWRvdy13aGl0ZS8xMCByaW5nLTEgcmluZy13aGl0ZS8xMCBiZy1ibGFjayI+CiAgICAgICAgICAgIDxoMSBjbGFzcz0iZm9udC1ib2xkIHRleHQtMnhsIHRleHQtdHJhbnNwYXJlbnQgYmctY2xpcC10ZXh0IGJnLWdyYWRpZW50LXRvLXIgZnJvbS13aGl0ZSB0by1zbGF0ZS0zMDAiPgogICAgICAgICAgICAgICAgV2F0Y2hseTwvaDE+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj4KCiAgICA8IS0tIFVzZXIgUHJvZmlsZSBEcm9wZG93biAoSGlkZGVuIGJ5IGRlZmF1bHQsIHNob3duIGFmdGVyIGxvZ2luKSAtLT4KICAgIDxkaXYgaWQ9InVzZXItcHJvZmlsZS1kcm9wZG93bi13cmFwcGVyIiBjbGFzcz0iaGlkZGVuIHB4LTQgbWQ6cHgtNiBwdC00IHBiLTIiPgogICAgICAgIDxkaXYgY2xhc3M9InJlbGF0aXZlIj4KICAgICAgICAgICAgPGJ1dHRvbiBpZD0idXNlci1wcm9maWxlLXRyaWdnZXIiIHR5cGU9ImJ1dHRvbiIKICAgICAgICAgICAgICAgIGNsYXNzPSJ3LWZ1bGwgZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgcC0zIGJnLW5ldXRyYWwtODAwLzUwIGJvcmRlciBib3JkZXItc2xhdGUtNzAwLzUwIHJvdW5kZWQteGwgaG92ZXI6YmctbmV1dHJhbC04MDAvNzAgdHJhbnNpdGlvbi1hbGwgZ3JvdXAiPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0idy0xMCBoLTEwIHJvdW5kZWQtZnVsbCBiZy13aGl0ZSB0ZXh0LWJsYWNrIHJpbmctMSByaW5nLXdoaXRlLzEwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGZvbnQtYm9sZCB0ZXh0LXNtIGZsZXgtc2hyaW5rLTAiCiAgICAgICAgICAgICAgICAgICAgaWQ9InVzZXItYXZhdGFyIj4KICAgICAgICAgICAgICAgICAgICA8IS0tIEF2YXRhciBpbml0aWFscyB3aWxsIGJlIGdlbmVyYXRlZCBmcm9tIGVtYWlsIC0tPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJmbGV4LWdyb3cgbWluLXctMCB0ZXh0LWxlZnQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9InRleHQteHMgdGV4dC1zbGF0ZS01MDAgbWItMC41Ij5Mb2dnZWQgaW4gYXM8L2Rpdj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ0ZXh0LXNtIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gdHJ1bmNhdGUiIGlkPSJ1c2VyLWVtYWlsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPCEtLSBFbWFpbCB3aWxsIGJlIGluc2VydGVkIGhlcmUgLS0+CiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNCBoLTQgdGV4dC1zbGF0ZS00MDAgZ3JvdXAtaG92ZXI6dGV4dC13aGl0ZSB0cmFuc2l0aW9uLWNvbG9ycyBmbGV4LXNocmluay0wIiBmaWxsPSJub25lIgogICAgICAgICAgICAgICAgICAgIHN0cm9rZT0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGlkPSJ1c2VyLXByb2ZpbGUtY2hldnJvbiIgc3R5bGU9InRyYW5zaXRpb246IHRyYW5zZm9ybSAyMDBtcyBlYXNlOyI+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjIiIGQ9Ik0xOSA5bC03IDctNy03Ij48L3BhdGg+CiAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgPC9idXR0b24+CgogICAgICAgICAgICA8IS0tIERyb3Bkb3duIE1lbnUgLS0+CiAgICAgICAgICAgIDxkaXYgaWQ9InVzZXItcHJvZmlsZS1kcm9wZG93biIgY2xhc3M9ImhpZGRlbiBhYnNvbHV0ZSB0b3AtZnVsbCBsZWZ0LTAgcmlnaHQtMCBtdC0yIGJnLW5ldXRyYWwtOTAwIGJvcmRlciBib3JkZXItc2xhdGUtNzAwLzUwIHJvdW5kZWQteGwgc2hhZG93LXhsIG92ZXJmbG93LWhpZGRlbiB6LTUwIj4KICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0idXNlci1wcm9maWxlLWxvZ291dC1idG4iCiAgICAgICAgICAgICAgICAgICAgY2xhc3M9InctZnVsbCBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMyBweC00IHB5LTMgdGV4dC1zbSBmb250LW1lZGl1bSB0ZXh0LXJlZC00MDAgaG92ZXI6YmctcmVkLTUwMC8xMCB0cmFuc2l0aW9uLWNvbG9ycyB0ZXh0LWxlZnQiPgogICAgICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNCBoLTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgogICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGQ9Ik0xNyAxNmw0LTRtMCAwbC00LTRtNCA0SDdtNiA0djFhMyAzIDAgMDEtMyAzSDZhMyAzIDAgMDEtMy0zVjdhMyAzIDAgMDEzLTNoNGEzIDMgMCAwMTMgM3YxIj4KICAgICAgICAgICAgICAgICAgICAgICAgPC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDwvc3ZnPgogICAgICAgICAgICAgICAgICAgIDxzcGFuPkxvZ291dDwvc3Bhbj4KICAgICAgICAgICAgICAgIDwvYnV0dG9uPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgoKICAgIDxuYXYKICAgICAgICBjbGFzcz0iZmxleC1ncm93IHB4LTQgbWQ6cHgtNiBweS00IHNwYWNlLXktMSBtZDpvdmVyZmxvdy12aXNpYmxlIGZsZXggZmxleC1jb2wgbWQ6YmxvY2sgd2hpdGVzcGFjZS1ub3JtYWwgZ2FwLTIiPgoKICAgICAgICA8YnV0dG9uIGlkPSJuYXYtd2VsY29tZSIKICAgICAgICAgICAgY2xhc3M9Im5hdi1pdGVtIGdyb3VwIHctZnVsbCBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMyBweC00IHB5LTMgdGV4dC1zbSBmb250LW1lZGl1bSByb3VuZGVkLXhsIHRyYW5zaXRpb24tYWxsIHRleHQtc2xhdGUtNDAwIGhvdmVyOnRleHQtd2hpdGUgaG92ZXI6YmctbmV1dHJhbC04MDAvNTAgYWN0aXZlIHRleHQtbGVmdCI+CiAgICAgICAgICAgIDxkaXYKICAgICAgICAgICAgICAgIGNsYXNzPSJ3LTggaC04IHJvdW5kZWQtbGcgYmctYmx1ZS01MDAvMTAgdGV4dC1ibHVlLTQwMCBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBib3JkZXIgYm9yZGVyLWJsdWUtNDAwLzIwIGZsZXgtc2hyaW5rLTAgdHJhbnNpdGlvbi1hbGwgZ3JvdXAtaG92ZXI6c2NhbGUtMTA1IGdyb3VwLWhvdmVyOmJnLWJsdWUtNTAwLzE1IGdyb3VwLWhvdmVyOmJvcmRlci1ibHVlLTQwMC8zMCBncm91cC1ob3ZlcjpzaGFkb3ctbGcgZ3JvdXAtaG92ZXI6c2hhZG93LWJsdWUtOTAwLzEwIj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNCBoLTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIgogICAgICAgICAgICAgICAgICAgICAgICBkPSJNMyAxMmwyLTJtMCAwbDctNyA3IDdNNSAxMHYxMGExIDEgMCAwMDEgMWgzbTEwLTExbDIgMm0tMi0ydjEwYTEgMSAwIDAxLTEgMWgtM20tNiAwYTEgMSAwIDAwMS0xdi00YTEgMSAwIDAxMS0xaDJhMSAxIDAgMDExIDF2NGExIDEgMCAwMDEgMW0tNiAwaDYiPgogICAgICAgICAgICAgICAgICAgIDwvcGF0aD4KICAgICAgICAgICAgICAgIDwvc3ZnPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPHNwYW4+R2V0IFN0YXJ0ZWQ8L3NwYW4+CiAgICAgICAgPC9idXR0b24+CgogICAgICAgIDxidXR0b24gaWQ9Im5hdi1sb2dpbiIKICAgICAgICAgICAgY2xhc3M9Im5hdi1pdGVtIGdyb3VwIHctZnVsbCBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMyBweC00IHB5LTMgdGV4dC1zbSBmb250LW1lZGl1bSByb3VuZGVkLXhsIHRyYW5zaXRpb24tYWxsIHRleHQtc2xhdGUtNDAwIGhvdmVyOnRleHQtd2hpdGUgaG92ZXI6YmctbmV1dHJhbC04MDAvNTAgdGV4dC1sZWZ0IGRpc2FibGVkIj4KICAgICAgICAgICAgPGRpdgogICAgICAgICAgICAgICAgY2xhc3M9InctOCBoLTggcm91bmRlZC1sZyBiZy1jeWFuLTUwMC8xMCB0ZXh0LWN5YW4tNDAwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGJvcmRlciBib3JkZXItY3lhbi00MDAvMjAgZmxleC1zaHJpbmstMCB0cmFuc2l0aW9uLWFsbCBncm91cC1ob3ZlcjpzY2FsZS0xMDUgZ3JvdXAtaG92ZXI6YmctY3lhbi01MDAvMTUgZ3JvdXAtaG92ZXI6Ym9yZGVyLWN5YW4tNDAwLzMwIGdyb3VwLWhvdmVyOnNoYWRvdy1sZyBncm91cC1ob3ZlcjpzaGFkb3ctY3lhbi05MDAvMTAiPgogICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz0idy00IGgtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjIiCiAgICAgICAgICAgICAgICAgICAgICAgIGQ9Ik0xMSAxNmwtNC00bTAgMGw0LTRtLTQgNGgxNG0tNSA0djFhMyAzIDAgMDEtMyAzSDZhMyAzIDAgMDEtMy0zVjdhMyAzIDAgMDEzLTNoN2EzIDMgMCAwMTMgM3YxIj4KICAgICAgICAgICAgICAgICAgICA8L3BhdGg+CiAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxzcGFuPkNvbm5lY3QgTGlicmFyeTwvc3Bhbj4KICAgICAgICA8L2J1dHRvbj4KCiAgICAgICAgPGJ1dHRvbiBpZD0ibmF2LWNvbmZpZyIKICAgICAgICAgICAgY2xhc3M9Im5hdi1pdGVtIGdyb3VwIHctZnVsbCBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMyBweC00IHB5LTMgdGV4dC1zbSBmb250LW1lZGl1bSByb3VuZGVkLXhsIHRyYW5zaXRpb24tYWxsIHRleHQtc2xhdGUtNDAwIGhvdmVyOnRleHQtd2hpdGUgaG92ZXI6YmctbmV1dHJhbC04MDAvNTAgdGV4dC1sZWZ0IGRpc2FibGVkIj4KICAgICAgICAgICAgPGRpdgogICAgICAgICAgICAgICAgY2xhc3M9InctOCBoLTggcm91bmRlZC1sZyBiZy1wdXJwbGUtNTAwLzEwIHRleHQtcHVycGxlLTQwMCBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBib3JkZXIgYm9yZGVyLXB1cnBsZS00MDAvMjAgZmxleC1zaHJpbmstMCB0cmFuc2l0aW9uLWFsbCBncm91cC1ob3ZlcjpzY2FsZS0xMDUgZ3JvdXAtaG92ZXI6YmctcHVycGxlLTUwMC8xNSBncm91cC1ob3Zlcjpib3JkZXItcHVycGxlLTQwMC8zMCBncm91cC1ob3ZlcjpzaGFkb3ctbGcgZ3JvdXAtaG92ZXI6c2hhZG93LXB1cnBsZS05MDAvMTAiPgogICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz0idy00IGgtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjIiCiAgICAgICAgICAgICAgICAgICAgICAgIGQ9Ik0xMiA2VjRtMCAyYTIgMiAwIDEwMCA0bTAtNGEyIDIgMCAxMTAgNG0tNiA4YTIgMiAwIDEwMC00bTAgNGEyIDIgMCAxMTAtNG0wIDR2Mm0wLTZWNG02IDZ2MTBtNi0yYTIgMiAwIDEwMC00bTAgNGEyIDIgMCAxMTAtNG0wIDR2Mm0wLTZWNCI+CiAgICAgICAgICAgICAgICAgICAgPC9wYXRoPgogICAgICAgICAgICAgICAgPC9zdmc+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8c3Bhbj5Db25maWd1cmUgT3B0aW9uczwvc3Bhbj4KICAgICAgICA8L2J1dHRvbj4KCiAgICAgICAgPGJ1dHRvbiBpZD0ibmF2LWNhdGFsb2dzIgogICAgICAgICAgICBjbGFzcz0ibmF2LWl0ZW0gZ3JvdXAgdy1mdWxsIGZsZXggaXRlbXMtY2VudGVyIGdhcC0zIHB4LTQgcHktMyB0ZXh0LXNtIGZvbnQtbWVkaXVtIHJvdW5kZWQteGwgdHJhbnNpdGlvbi1hbGwgdGV4dC1zbGF0ZS00MDAgaG92ZXI6dGV4dC13aGl0ZSBob3ZlcjpiZy1uZXV0cmFsLTgwMC81MCB0ZXh0LWxlZnQgZGlzYWJsZWQiPgogICAgICAgICAgICA8ZGl2CiAgICAgICAgICAgICAgICBjbGFzcz0idy04IGgtOCByb3VuZGVkLWxnIGJnLW9yYW5nZS01MDAvMTAgdGV4dC1vcmFuZ2UtNDAwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGJvcmRlciBib3JkZXItb3JhbmdlLTQwMC8yMCBmbGV4LXNocmluay0wIHRyYW5zaXRpb24tYWxsIGdyb3VwLWhvdmVyOnNjYWxlLTEwNSBncm91cC1ob3ZlcjpiZy1vcmFuZ2UtNTAwLzE1IGdyb3VwLWhvdmVyOmJvcmRlci1vcmFuZ2UtNDAwLzMwIGdyb3VwLWhvdmVyOnNoYWRvdy1sZyBncm91cC1ob3ZlcjpzaGFkb3ctb3JhbmdlLTkwMC8xMCI+CiAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPSJ3LTQgaC00IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIKICAgICAgICAgICAgICAgICAgICAgICAgZD0iTTQgNmgxNk00IDEwaDE2TTQgMTRoMTZNNCAxOGgxNiI+CiAgICAgICAgICAgICAgICAgICAgPC9wYXRoPgogICAgICAgICAgICAgICAgPC9zdmc+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8c3Bhbj5DYXRhbG9nczwvc3Bhbj4KICAgICAgICA8L2J1dHRvbj4KCiAgICAgICAgPGJ1dHRvbiBpZD0ibmF2LWluc3RhbGwiCiAgICAgICAgICAgIGNsYXNzPSJuYXYtaXRlbSBncm91cCB3LWZ1bGwgZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTMgcHgtNCBweS0zIHRleHQtc20gZm9udC1tZWRpdW0gcm91bmRlZC14bCB0cmFuc2l0aW9uLWFsbCB0ZXh0LXNsYXRlLTQwMCBob3Zlcjp0ZXh0LXdoaXRlIGhvdmVyOmJnLW5ldXRyYWwtODAwLzUwIHRleHQtbGVmdCBkaXNhYmxlZCI+CiAgICAgICAgICAgIDxkaXYKICAgICAgICAgICAgICAgIGNsYXNzPSJ3LTggaC04IHJvdW5kZWQtbGcgYmctZ3JlZW4tNTAwLzEwIHRleHQtZ3JlZW4tNDAwIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGJvcmRlciBib3JkZXItZ3JlZW4tNDAwLzIwIGZsZXgtc2hyaW5rLTAgdHJhbnNpdGlvbi1hbGwgZ3JvdXAtaG92ZXI6c2NhbGUtMTA1IGdyb3VwLWhvdmVyOmJnLWdyZWVuLTUwMC8xNSBncm91cC1ob3Zlcjpib3JkZXItZ3JlZW4tNDAwLzMwIGdyb3VwLWhvdmVyOnNoYWRvdy1sZyBncm91cC1ob3ZlcjpzaGFkb3ctZ3JlZW4tOTAwLzEwIj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9InctNCBoLTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIgogICAgICAgICAgICAgICAgICAgICAgICBkPSJNOCA3SDVhMiAyIDAgMDAtMiAydjlhMiAyIDAgMDAyIDJoMTRhMiAyIDAgMDAyLTJWOWEyIDIgMCAwMC0yLTJoLTNtLTEgNGwtMyAzbTAgMGwtMy0zbTMgM1Y0Ij4KICAgICAgICAgICAgICAgICAgICA8L3BhdGg+CiAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxzcGFuPlNhdmUgJiBJbnN0YWxsPC9zcGFuPgogICAgICAgIDwvYnV0dG9uPgoKICAgIDwvbmF2PgoKICAgIDxkaXYgY2xhc3M9InAtNCBtZDpwLTYgbXQtYXV0byBzcGFjZS15LTQiPgogICAgICAgIDwhLS0gU3VwcG9ydCBCdXR0b24gLS0+CiAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJrb2ZpQnRuIgogICAgICAgICAgICBjbGFzcz0iYmxvY2sgdy1mdWxsIGJnLXdoaXRlIHRleHQtYmxhY2sgaG92ZXI6Ymctd2hpdGUvOTAgZm9udC1tZWRpdW0gcHktMyBweC00IHJvdW5kZWQteGwgdHJhbnNpdGlvbi1hbGwgc2hhZG93LWxnIHNoYWRvdy1ibGFjay8xMCBob3ZlcjpzaGFkb3cteGwgaG92ZXI6LXRyYW5zbGF0ZS15LTAuNSBob3ZlcjpyaW5nLTIgaG92ZXI6cmluZy1ibGFjay8xMCBmb2N1czpvdXRsaW5lLW5vbmUgZm9jdXM6cmluZy0yIGZvY3VzOnJpbmctYmxhY2svMjAgYWN0aXZlOnRyYW5zbGF0ZS15LTAgYm9yZGVyIGJvcmRlci13aGl0ZS8xMCBncm91cCB0ZXh0LWxlZnQiCiAgICAgICAgICAgIGFyaWEtbGFiZWw9IlN1cHBvcnQgbWUiPgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBnYXAtMyI+CiAgICAgICAgICAgICAgICA8IS0tIEhlYXJ0IGljb24gLS0+CiAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPSJ3LTUgaC01IGdyb3VwLWhvdmVyOnNjYWxlLTExMCB0cmFuc2l0aW9uLXRyYW5zZm9ybSB0ZXh0LWJsYWNrIiBmaWxsPSJjdXJyZW50Q29sb3IiCiAgICAgICAgICAgICAgICAgICAgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgICAgICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgICAgICAgICBkPSJNNC4zMTggNi4zMThhNC41IDQuNSAwIDAwMCA2LjM2NEwxMiAyMC4zNjRsNy42ODItNy42ODJhNC41IDQuNSAwIDAwLTYuMzY0LTYuMzY0TDEyIDcuNjM2bC0xLjMxOC0xLjMxOGE0LjUgNC41IDAgMDAtNi4zNjQgMHoiIC8+CiAgICAgICAgICAgICAgICA8L3N2Zz4KICAgICAgICAgICAgICAgIDxzcGFuPlN1cHBvcnQgbWU8L3NwYW4+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvYnV0dG9uPgoKICAgICAgICA8IS0tIENvcHlyaWdodCAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJ0ZXh0LXhzIHRleHQtc2xhdGUtNjAwIHRleHQtY2VudGVyIGhpZGRlbiBtZDpibG9jayI+CiAgICAgICAgICAgICZjb3B5OyA8c3BhbiBpZD0iY3VycmVudFllYXIiPjIwMjQ8L3NwYW4+IFdhdGNobHkKICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L2FzaWRlPgo=" | base64 --decode > "$REPO/app/templates/components/sidebar.html" +echo " wrote app/templates/components/sidebar.html" + +mkdir -p "$REPO/app/api" +echo "ZnJvbSBmYXN0YXBpIGltcG9ydCBBUElSb3V0ZXIKCmZyb20gLmVuZHBvaW50cy5hbm5vdW5jZW1lbnQgaW1wb3J0IHJvdXRlciBhcyBhbm5vdW5jZW1lbnRfcm91dGVyCmZyb20gLmVuZHBvaW50cy5jYXRhbG9ncyBpbXBvcnQgcm91dGVyIGFzIGNhdGFsb2dzX3JvdXRlcgpmcm9tIC5lbmRwb2ludHMuaGVhbHRoIGltcG9ydCByb3V0ZXIgYXMgaGVhbHRoX3JvdXRlcgpmcm9tIC5lbmRwb2ludHMubWFuaWZlc3QgaW1wb3J0IHJvdXRlciBhcyBtYW5pZmVzdF9yb3V0ZXIKZnJvbSAuZW5kcG9pbnRzLm1ldGEgaW1wb3J0IHJvdXRlciBhcyBtZXRhX3JvdXRlcgpmcm9tIC5lbmRwb2ludHMuc3RhdHMgaW1wb3J0IHJvdXRlciBhcyBzdGF0c19yb3V0ZXIKZnJvbSAuZW5kcG9pbnRzLnRva2VucyBpbXBvcnQgcm91dGVyIGFzIHRva2Vuc19yb3V0ZXIKZnJvbSAuZW5kcG9pbnRzLnRyYWt0IGltcG9ydCByb3V0ZXIgYXMgdHJha3Rfcm91dGVyCmZyb20gLmVuZHBvaW50cy52YWxpZGF0aW9uIGltcG9ydCByb3V0ZXIgYXMgdmFsaWRhdGlvbl9yb3V0ZXIKCmFwaV9yb3V0ZXIgPSBBUElSb3V0ZXIoKQoKCkBhcGlfcm91dGVyLmdldCgiLyIpCmFzeW5jIGRlZiByb290KCk6CiAgICByZXR1cm4geyJtZXNzYWdlIjogIldhdGNobHkgQVBJIGlzIHJ1bm5pbmcifQoKCmFwaV9yb3V0ZXIuaW5jbHVkZV9yb3V0ZXIobWFuaWZlc3Rfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKGNhdGFsb2dzX3JvdXRlcikKYXBpX3JvdXRlci5pbmNsdWRlX3JvdXRlcih0b2tlbnNfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKHRyYWt0X3JvdXRlcikKYXBpX3JvdXRlci5pbmNsdWRlX3JvdXRlcihoZWFsdGhfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKG1ldGFfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKGFubm91bmNlbWVudF9yb3V0ZXIpCmFwaV9yb3V0ZXIuaW5jbHVkZV9yb3V0ZXIoc3RhdHNfcm91dGVyKQphcGlfcm91dGVyLmluY2x1ZGVfcm91dGVyKHZhbGlkYXRpb25fcm91dGVyKQo=" | base64 --decode > "$REPO/app/api/router.py" +echo " wrote app/api/router.py" + +mkdir -p "$REPO/app/core" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IExpdGVyYWwKCmZyb20gcHlkYW50aWNfc2V0dGluZ3MgaW1wb3J0IEJhc2VTZXR0aW5ncywgU2V0dGluZ3NDb25maWdEaWN0Cgpmcm9tIGFwcC5jb3JlLnZlcnNpb24gaW1wb3J0IF9fdmVyc2lvbl9fCgoKY2xhc3MgU2V0dGluZ3MoQmFzZVNldHRpbmdzKToKICAgICIiIkFwcGxpY2F0aW9uIHNldHRpbmdzIGxvYWRlZCBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcy4iIiIKCiAgICBtb2RlbF9jb25maWcgPSBTZXR0aW5nc0NvbmZpZ0RpY3QoCiAgICAgICAgZW52X2ZpbGU9Ii5lbnYiLAogICAgICAgIGVudl9maWxlX2VuY29kaW5nPSJ1dGYtOCIsCiAgICAgICAgY2FzZV9zZW5zaXRpdmU9RmFsc2UsCiAgICAgICAgZXh0cmE9ImFsbG93IiwKICAgICkKCiAgICBUTURCX0FQSV9LRVk6IHN0ciB8IE5vbmUgPSBOb25lCiAgICBQT1JUOiBpbnQgPSA4MDAwCiAgICBBRERPTl9JRDogc3RyID0gImNvbS5iaW1hbC53YXRjaGx5IgogICAgQURET05fTkFNRTogc3RyID0gIldhdGNobHkiCiAgICBSRURJU19VUkw6IHN0ciA9ICJyZWRpczovL3JlZGlzOjYzNzkvMCIKICAgICMgTWF4aW11bSBudW1iZXIgb2YgY29ubmVjdGlvbnMgUmVkaXMgY2xpZW50IHdpbGwgb3BlbiBwZXIgcHJvY2VzcwogICAgIyBTZXQgY29uc2VydmF0aXZlbHkgdG8gYXZvaWQgdW5ib3VuZGVkIGNvbm5lY3Rpb24gZ3Jvd3RoIHVuZGVyIGhpZ2ggY29uY3VycmVuY3kKICAgIFJFRElTX01BWF9DT05ORUNUSU9OUzogaW50ID0gMjAKICAgICMgSWYgdG90YWwgY29ubmVjdGVkIGNsaWVudHMgcmVwb3J0ZWQgYnkgUmVkaXMgZXhjZWVkcyB0aGlzLCBiYWNrZ3JvdW5kCiAgICAjIFJlZGlzLWhlYXZ5IGpvYnMgd2lsbCBiYWNrIG9mZi4gVHVuZSBhY2NvcmRpbmcgdG8geW91ciBSZWRpcyBjYXBhY2l0eS4KICAgIFJFRElTX0NPTk5FQ1RJT05TX1RIUkVTSE9MRDogaW50ID0gMTAwCiAgICBSRURJU19UT0tFTl9LRVk6IHN0ciA9ICJ3YXRjaGx5OnRva2VuOiIKICAgIFRPS0VOX1NBTFQ6IHN0ciA9ICJjaGFuZ2UtbWUiCiAgICBUT0tFTl9UVExfU0VDT05EUzogaW50ID0gMCAgIyAwID0gbmV2ZXIgZXhwaXJlCiAgICBBTk5PVU5DRU1FTlRfSFRNTDogc3RyID0gIiIKICAgIEFVVE9fVVBEQVRFX0NBVEFMT0dTOiBib29sID0gVHJ1ZQogICAgQ0FUQUxPR19SRUZSRVNIX0lOVEVSVkFMX1NFQ09ORFM6IGludCA9IDg2NDAwICAjIDI0IGhvdXJzCiAgICBBUFBfRU5WOiBMaXRlcmFsWyJkZXZlbG9wbWVudCIsICJwcm9kdWN0aW9uIiwgInZlcmNlbCJdID0gInByb2R1Y3Rpb24iCiAgICBIT1NUX05BTUU6IHN0ciA9ICJodHRwczovLzFjY2VhNDMwMTU4Ny13YXRjaGx5LmJhYnktYmVhbXVwLmNsdWIiCgogICAgUkVDT01NRU5EQVRJT05fU09VUkNFX0lURU1TX0xJTUlUOiBpbnQgPSAxMAogICAgTElCUkFSWV9JVEVNU19MSU1JVDogaW50ID0gMjAKCiAgICBDQVRBTE9HX0NBQ0hFX1RUTDogaW50ID0gNDMyMDAgICMgMTIgaG91cnMKICAgIENBVEFMT0dfU1RBTEVfVFRMOiBpbnQgPSA2MDQ4MDAgICMgNyBkYXlzIChzb2Z0IGV4cGlyYXRpb24gZmFsbGJhY2spCgogICAgIyBBSQogICAgREVGQVVMVF9HRU1JTklfTU9ERUw6IHN0ciA9ICJnZW1tYS0zLTI3Yi1pdCIKICAgIEdFTUlOSV9BUElfS0VZOiBzdHIgfCBOb25lID0gTm9uZQoKICAgICMgVHJha3QgT0F1dGgKICAgIFRSQUtUX0NMSUVOVF9JRDogc3RyIHwgTm9uZSA9IE5vbmUKICAgIFRSQUtUX0NMSUVOVF9TRUNSRVQ6IHN0ciB8IE5vbmUgPSBOb25lCgoKc2V0dGluZ3MgPSBTZXR0aW5ncygpCgpBUFBfVkVSU0lPTiA9IF9fdmVyc2lvbl9fCg==" | base64 --decode > "$REPO/app/core/config.py" +echo " wrote app/core/config.py" + +mkdir -p "$REPO/app/static/js" +echo "Ly8gTWFpbiBlbnRyeSBwb2ludCAtIGluaXRpYWxpemVzIGFsbCBtb2R1bGVzCgppbXBvcnQgeyBkZWZhdWx0Q2F0YWxvZ3MgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7CmltcG9ydCB7IHNob3dUb2FzdCwgaW5pdGlhbGl6ZUZvb3RlciwgaW5pdGlhbGl6ZUtvZmkgfSBmcm9tICcuL21vZHVsZXMvdWkuanMnOwppbXBvcnQgeyBpbml0aWFsaXplTmF2aWdhdGlvbiwgc3dpdGNoU2VjdGlvbiwgbG9ja05hdmlnYXRpb25Gb3JMb2dnZWRPdXQsIGluaXRpYWxpemVNb2JpbGVOYXYsIHVwZGF0ZU1vYmlsZUxheW91dCwgdW5sb2NrTmF2aWdhdGlvbiB9IGZyb20gJy4vbW9kdWxlcy9uYXZpZ2F0aW9uLmpzJzsKaW1wb3J0IHsgaW5pdGlhbGl6ZUF1dGgsIHNldFN0cmVtaW9Mb2dnZWRPdXRTdGF0ZSB9IGZyb20gJy4vbW9kdWxlcy9hdXRoLmpzJzsKaW1wb3J0IHsgaW5pdGlhbGl6ZVRyYWt0LCBzZXRUcmFrdExvZ2dlZE91dFN0YXRlIH0gZnJvbSAnLi9tb2R1bGVzL3RyYWt0LmpzJzsKaW1wb3J0IHsgaW5pdGlhbGl6ZUNhdGFsb2dMaXN0LCByZW5kZXJDYXRhbG9nTGlzdCwgZ2V0Q2F0YWxvZ3MsIHNldENhdGFsb2dzIH0gZnJvbSAnLi9tb2R1bGVzL2NhdGFsb2cuanMnOwppbXBvcnQgeyBpbml0aWFsaXplRm9ybSwgY2xlYXJFcnJvcnMgfSBmcm9tICcuL21vZHVsZXMvZm9ybS5qcyc7CgovLyBJbml0aWFsaXplIGNhdGFsb2dzIHN0YXRlCmxldCBjYXRhbG9nc1N0YXRlID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShkZWZhdWx0Q2F0YWxvZ3MpKTsKCi8vIERPTSBFbGVtZW50cwpjb25zdCBjb25maWdGb3JtID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpZ0Zvcm0nKTsKY29uc3QgY2F0YWxvZ0xpc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2F0YWxvZ0xpc3QnKTsKY29uc3QgbW92aWVHZW5yZUxpc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbW92aWVHZW5yZUxpc3QnKTsKY29uc3Qgc2VyaWVzR2VucmVMaXN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Nlcmllc0dlbnJlTGlzdCcpOwpjb25zdCBzdWJtaXRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc3VibWl0QnRuJyk7CmNvbnN0IHN0cmVtaW9Mb2dpbkJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdHJlbWlvTG9naW5CdG4nKTsKY29uc3Qgc3RyZW1pb0xvZ2luVGV4dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdHJlbWlvTG9naW5UZXh0Jyk7CmNvbnN0IGVtYWlsSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZW1haWxJbnB1dCcpOwpjb25zdCBwYXNzd29yZElucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bhc3N3b3JkSW5wdXQnKTsKY29uc3QgZW1haWxQd2RDb250aW51ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlbWFpbFB3ZENvbnRpbnVlQnRuJyk7CmNvbnN0IGxhbmd1YWdlU2VsZWN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhbmd1YWdlU2VsZWN0Jyk7CmNvbnN0IGNvbmZpZ05leHRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlnTmV4dEJ0bicpOwpjb25zdCBjYXRhbG9nc05leHRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2F0YWxvZ3NOZXh0QnRuJyk7CmNvbnN0IHN1Y2Nlc3NSZXNldEJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdWNjZXNzUmVzZXRCdG4nKTsKY29uc3QgYnRuR2V0U3RhcnRlZCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdidG4tZ2V0LXN0YXJ0ZWQnKTsKCmNvbnN0IG5hdkl0ZW1zID0gewogICAgd2VsY29tZTogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25hdi13ZWxjb21lJyksCiAgICBsb2dpbjogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25hdi1sb2dpbicpLAogICAgY29uZmlnOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmF2LWNvbmZpZycpLAogICAgY2F0YWxvZ3M6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduYXYtY2F0YWxvZ3MnKSwKICAgIGluc3RhbGw6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduYXYtaW5zdGFsbCcpCn07Cgpjb25zdCBzZWN0aW9ucyA9IHsKICAgIHdlbGNvbWU6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWN0LXdlbGNvbWUnKSwKICAgIGxvZ2luOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1sb2dpbicpLAogICAgY29uZmlnOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1jb25maWcnKSwKICAgIGNhdGFsb2dzOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1jYXRhbG9ncycpLAogICAgaW5zdGFsbDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3QtaW5zdGFsbCcpLAogICAgc3VjY2VzczogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3Qtc3VjY2VzcycpCn07CgovLyBNYWluIHNjcm9sbCBjb250YWluZXIKY29uc3QgbWFpbkVsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignbWFpbicpOwoKLy8gUmVzZXQgQXBwIEZ1bmN0aW9uCmZ1bmN0aW9uIHJlc2V0QXBwKCkgewogICAgaWYgKGNvbmZpZ0Zvcm0pIGNvbmZpZ0Zvcm0ucmVzZXQoKTsKICAgIGNsZWFyRXJyb3JzKCk7CgogICAgLy8gUmVzZXQgTmF2aWdhdGlvbiBpcyBub3cgQmFjayB0byBXZWxjb21lCiAgICBzd2l0Y2hTZWN0aW9uKCd3ZWxjb21lJyk7CgogICAgLy8gTG9jayBOYXZzCiAgICBPYmplY3Qua2V5cyhuYXZJdGVtcykuZm9yRWFjaChrZXkgPT4gewogICAgICAgIGlmIChrZXkgIT09ICdsb2dpbicgJiYga2V5ICE9PSAnd2VsY29tZScpIHsKICAgICAgICAgICAgaWYgKG5hdkl0ZW1zW2tleV0pIG5hdkl0ZW1zW2tleV0uY2xhc3NMaXN0LmFkZCgnZGlzYWJsZWQnKTsKICAgICAgICB9CiAgICB9KTsKCiAgICAvLyBSZXNldCBTdHJlbWlvIFN0YXRlCiAgICBzZXRTdHJlbWlvTG9nZ2VkT3V0U3RhdGUoKTsKCiAgICAvLyBSZXNldCBUcmFrdCBTdGF0ZQogICAgc2V0VHJha3RMb2dnZWRPdXRTdGF0ZSgpOwoKICAgIC8vIFJlc2V0IGNhdGFsb2dzCiAgICBjYXRhbG9nc1N0YXRlID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShkZWZhdWx0Q2F0YWxvZ3MpKTsKICAgIHNldENhdGFsb2dzKGNhdGFsb2dzU3RhdGUpOwogICAgcmVuZGVyQ2F0YWxvZ0xpc3QoKTsKCiAgICAvLyBTaG93IEZvcm0KICAgIGlmIChjb25maWdGb3JtKSBjb25maWdGb3JtLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgaWYgKHNlY3Rpb25zLnN1Y2Nlc3MpIHNlY3Rpb25zLnN1Y2Nlc3MuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7Cn0KCi8vIFdlbGNvbWUgRmxvdyBMb2dpYwpmdW5jdGlvbiBpbml0aWFsaXplV2VsY29tZUZsb3coKSB7CiAgICBpZiAoIWJ0bkdldFN0YXJ0ZWQpIHJldHVybjsKCiAgICAvLyBTdXBwb3J0IG1vYmlsZSB0YXBzIHJlbGlhYmx5IHdoaWxlIGF2b2lkaW5nIGRvdWJsZS1maXJlICh0b3VjaCAtPiBjbGljaykKICAgIGxldCB0b3VjaGVkID0gZmFsc2U7CiAgICBjb25zdCBoYW5kbGVHZXRTdGFydGVkID0gKGUpID0+IHsKICAgICAgICBpZiAoZS50eXBlID09PSAnY2xpY2snICYmIHRvdWNoZWQpIHJldHVybjsKICAgICAgICBpZiAoZS50eXBlID09PSAndG91Y2hzdGFydCcpIHRvdWNoZWQgPSB0cnVlOwogICAgICAgIGlmIChuYXZJdGVtcy5sb2dpbikgbmF2SXRlbXMubG9naW4uY2xhc3NMaXN0LnJlbW92ZSgnZGlzYWJsZWQnKTsKICAgICAgICBzd2l0Y2hTZWN0aW9uKCdsb2dpbicpOwogICAgfTsKCiAgICBidG5HZXRTdGFydGVkLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgaGFuZGxlR2V0U3RhcnRlZCk7CiAgICBidG5HZXRTdGFydGVkLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBoYW5kbGVHZXRTdGFydGVkLCB7IHBhc3NpdmU6IHRydWUgfSk7Cn0KCi8vIEluaXRpYWxpemUgZXZlcnl0aGluZwpkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4gewogICAgLy8gU3RhcnQgYXQgV2VsY29tZQogICAgc3dpdGNoU2VjdGlvbignd2VsY29tZScpOwogICAgaW5pdGlhbGl6ZVdlbGNvbWVGbG93KCk7CgogICAgLy8gSW5pdGlhbGl6ZSBhbGwgbW9kdWxlcwogICAgaW5pdGlhbGl6ZU5hdmlnYXRpb24oewogICAgICAgIG5hdkl0ZW1zLAogICAgICAgIHNlY3Rpb25zLAogICAgICAgIG1haW5FbAogICAgfSk7CgogICAgLy8gQnkgZGVmYXVsdCwgZW5zdXJlIGxvZ2dlZC1vdXQgdXNlcnMgc2VlIG9ubHkgV2VsY29tZS9Mb2dpbgogICAgbG9ja05hdmlnYXRpb25Gb3JMb2dnZWRPdXQoKTsKCiAgICAvLyBJbml0aWFsaXplIGNhdGFsb2cgbWFuYWdlbWVudCAtIHNldCBjYXRhbG9ncyBmaXJzdAogICAgc2V0Q2F0YWxvZ3MoY2F0YWxvZ3NTdGF0ZSk7CiAgICBpbml0aWFsaXplQ2F0YWxvZ0xpc3QoCiAgICAgICAgeyBjYXRhbG9nTGlzdCB9LAogICAgICAgIHsKICAgICAgICAgICAgY2F0YWxvZ3M6IGNhdGFsb2dzU3RhdGUsCiAgICAgICAgICAgIHJlbmRlckNhdGFsb2dMaXN0CiAgICAgICAgfQogICAgKTsKCiAgICAvLyBJbml0aWFsaXplIGF1dGhlbnRpY2F0aW9uIChTdHJlbWlvKQogICAgaW5pdGlhbGl6ZUF1dGgoCiAgICAgICAgewogICAgICAgICAgICBzdHJlbWlvTG9naW5CdG4sCiAgICAgICAgICAgIHN0cmVtaW9Mb2dpblRleHQsCiAgICAgICAgICAgIGVtYWlsSW5wdXQsCiAgICAgICAgICAgIHBhc3N3b3JkSW5wdXQsCiAgICAgICAgICAgIGVtYWlsUHdkQ29udGludWVCdG4sCiAgICAgICAgICAgIGxhbmd1YWdlU2VsZWN0CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAgIGdldENhdGFsb2dzLAogICAgICAgICAgICByZW5kZXJDYXRhbG9nTGlzdCwKICAgICAgICAgICAgcmVzZXRBcHAKICAgICAgICB9CiAgICApOwoKICAgIC8vIEluaXRpYWxpemUgVHJha3QgYXV0aGVudGljYXRpb24KICAgIGluaXRpYWxpemVUcmFrdCgKICAgICAgICB7IGxhbmd1YWdlU2VsZWN0IH0sCiAgICAgICAgewogICAgICAgICAgICBnZXRDYXRhbG9ncywKICAgICAgICAgICAgcmVuZGVyQ2F0YWxvZ0xpc3QsCiAgICAgICAgICAgIHJlc2V0QXBwCiAgICAgICAgfQogICAgKTsKCiAgICAvLyBJbml0aWFsaXplIGZvcm0gaGFuZGxpbmcKICAgIGluaXRpYWxpemVGb3JtKAogICAgICAgIHsKICAgICAgICAgICAgY29uZmlnRm9ybSwKICAgICAgICAgICAgc3VibWl0QnRuLAogICAgICAgICAgICBlbWFpbElucHV0LAogICAgICAgICAgICBwYXNzd29yZElucHV0LAogICAgICAgICAgICBsYW5ndWFnZVNlbGVjdCwKICAgICAgICAgICAgbW92aWVHZW5yZUxpc3QsCiAgICAgICAgICAgIHNlcmllc0dlbnJlTGlzdAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgICBnZXRDYXRhbG9ncywKICAgICAgICAgICAgcmVzZXRBcHAKICAgICAgICB9CiAgICApOwoKICAgIC8vIEluaXRpYWxpemUgbW9iaWxlIG5hdmlnYXRpb24KICAgIGluaXRpYWxpemVNb2JpbGVOYXYoKTsKCiAgICAvLyBJbml0aWFsaXplIFVJIGNvbXBvbmVudHMKICAgIGluaXRpYWxpemVGb290ZXIoKTsKICAgIGluaXRpYWxpemVLb2ZpKCk7CgogICAgLy8gTGF5b3V0IGFkanVzdG1lbnRzIGZvciBmaXhlZCBtb2JpbGUgaGVhZGVyCiAgICB1cGRhdGVNb2JpbGVMYXlvdXQoKTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCB1cGRhdGVNb2JpbGVMYXlvdXQpOwogICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ29yaWVudGF0aW9uY2hhbmdlJywgdXBkYXRlTW9iaWxlTGF5b3V0KTsKCiAgICAvLyBOZXh0IEJ1dHRvbnMKICAgIGlmIChjb25maWdOZXh0QnRuKSBjb25maWdOZXh0QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4gc3dpdGNoU2VjdGlvbignY2F0YWxvZ3MnKSk7CiAgICBpZiAoY2F0YWxvZ3NOZXh0QnRuKSBjYXRhbG9nc05leHRCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiBzd2l0Y2hTZWN0aW9uKCdpbnN0YWxsJykpOwoKICAgIC8vIFJlc2V0IEJ1dHRvbnMKICAgIGNvbnN0IHJlc2V0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Jlc2V0QnRuJyk7CiAgICBpZiAocmVzZXRCdG4pIHJlc2V0QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcmVzZXRBcHApOwogICAgaWYgKHN1Y2Nlc3NSZXNldEJ0bikgc3VjY2Vzc1Jlc2V0QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgcmVzZXRBcHApOwp9KTsKCi8vIE1ha2UgcmVzZXRBcHAgYXZhaWxhYmxlIGdsb2JhbGx5IGZvciBhdXRoIG1vZHVsZQp3aW5kb3cucmVzZXRBcHAgPSByZXNldEFwcDsKd2luZG93LnN3aXRjaFNlY3Rpb24gPSBzd2l0Y2hTZWN0aW9uOwp3aW5kb3cudW5sb2NrTmF2aWdhdGlvbiA9IHVubG9ja05hdmlnYXRpb247Cg==" | base64 --decode > "$REPO/app/static/js/main.js" +echo " wrote app/static/js/main.js" + +mkdir -p "$REPO/app/static/js/modules" +echo "Ly8gRm9ybSBTdWJtaXNzaW9uIGFuZCBVSSBIZWxwZXJzCgppbXBvcnQgeyBzaG93VG9hc3QsIHNob3dDb25maXJtLCBlc2NhcGVIdG1sIH0gZnJvbSAnLi91aS5qcyc7CmltcG9ydCB7IGdldFRyYWt0VG9rZW5zRnJvbVN0b3JhZ2UgfSBmcm9tICcuL3RyYWt0LmpzJzsKaW1wb3J0IHsgc3dpdGNoU2VjdGlvbiB9IGZyb20gJy4vbmF2aWdhdGlvbi5qcyc7CmltcG9ydCB7IE1PVklFX0dFTlJFUywgU0VSSUVTX0dFTlJFUyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7CgovLyBET00gRWxlbWVudHMgLSB3aWxsIGJlIGluaXRpYWxpemVkCmxldCBjb25maWdGb3JtID0gbnVsbDsKbGV0IHN1Ym1pdEJ0biA9IG51bGw7CmxldCBlbWFpbElucHV0ID0gbnVsbDsKbGV0IHBhc3N3b3JkSW5wdXQgPSBudWxsOwpsZXQgbGFuZ3VhZ2VTZWxlY3QgPSBudWxsOwpsZXQgbW92aWVHZW5yZUxpc3QgPSBudWxsOwpsZXQgc2VyaWVzR2VucmVMaXN0ID0gbnVsbDsKbGV0IGdldENhdGFsb2dzID0gbnVsbDsKbGV0IHJlc2V0QXBwID0gbnVsbDsKCmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplRm9ybShkb21FbGVtZW50cywgY2F0YWxvZ1N0YXRlKSB7CiAgICBjb25maWdGb3JtID0gZG9tRWxlbWVudHMuY29uZmlnRm9ybTsKICAgIHN1Ym1pdEJ0biA9IGRvbUVsZW1lbnRzLnN1Ym1pdEJ0bjsKICAgIGVtYWlsSW5wdXQgPSBkb21FbGVtZW50cy5lbWFpbElucHV0OwogICAgcGFzc3dvcmRJbnB1dCA9IGRvbUVsZW1lbnRzLnBhc3N3b3JkSW5wdXQ7CiAgICBsYW5ndWFnZVNlbGVjdCA9IGRvbUVsZW1lbnRzLmxhbmd1YWdlU2VsZWN0OwogICAgbW92aWVHZW5yZUxpc3QgPSBkb21FbGVtZW50cy5tb3ZpZUdlbnJlTGlzdDsKICAgIHNlcmllc0dlbnJlTGlzdCA9IGRvbUVsZW1lbnRzLnNlcmllc0dlbnJlTGlzdDsKICAgIGdldENhdGFsb2dzID0gY2F0YWxvZ1N0YXRlLmdldENhdGFsb2dzOwogICAgcmVzZXRBcHAgPSBjYXRhbG9nU3RhdGUucmVzZXRBcHA7CgogICAgaW5pdGlhbGl6ZUZvcm1TdWJtaXNzaW9uKCk7CiAgICBpbml0aWFsaXplR2VucmVMaXN0cygpOwogICAgaW5pdGlhbGl6ZUxhbmd1YWdlU2VsZWN0KCk7CiAgICBpbml0aWFsaXplUGFzc3dvcmRUb2dnbGVzKCk7CiAgICBpbml0aWFsaXplU3VjY2Vzc0FjdGlvbnMoKTsKICAgIGluaXRpYWxpemVQb3N0ZXJSYXRpbmdQcm92aWRlcigpOwogICAgaW5pdGlhbGl6ZVRtZGIoKTsKICAgIGluaXRpYWxpemVTaW1rbCgpOwogICAgaW5pdGlhbGl6ZUdlbWluaSgpOwogICAgaW5pdGlhbGl6ZVllYXJTbGlkZXIoKTsKfQoKLy8gRm9ybSBTdWJtaXNzaW9uCmFzeW5jIGZ1bmN0aW9uIGluaXRpYWxpemVGb3JtU3VibWlzc2lvbigpIHsKICAgIGlmICghc3VibWl0QnRuKSByZXR1cm47CgogICAgc3VibWl0QnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgYXN5bmMgKGUpID0+IHsKICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7CiAgICAgICAgY2xlYXJFcnJvcnMoKTsKCiAgICAgICAgY29uc3Qgc0F1dGhLZXkgPSAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImF1dGhLZXkiKS52YWx1ZSB8fCAnJykudHJpbSgpOwogICAgICAgIGNvbnN0IGVtYWlsID0gZW1haWxJbnB1dD8udmFsdWUudHJpbSgpOwogICAgICAgIGNvbnN0IHBhc3N3b3JkID0gcGFzc3dvcmRJbnB1dD8udmFsdWU7CiAgICAgICAgY29uc3QgbGFuZ3VhZ2UgPSBsYW5ndWFnZVNlbGVjdC52YWx1ZTsKICAgICAgICBjb25zdCBwb3B1bGFyaXR5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvcHVsYXJpdHlTZWxlY3QiKT8udmFsdWUgfHwgImJhbGFuY2VkIjsKICAgICAgICBjb25zdCB5ZWFyTWluID0gcGFyc2VJbnQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInllYXJNaW4iKT8udmFsdWUgfHwgIjE5ODAiKTsKICAgICAgICBjb25zdCB5ZWFyTWF4ID0gcGFyc2VJbnQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInllYXJNYXgiKT8udmFsdWUgfHwgIjIwMjYiKTsKICAgICAgICBjb25zdCBzb3J0aW5nT3JkZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic29ydGluZ09yZGVyU2VsZWN0Iik/LnZhbHVlIHx8ICJkZWZhdWx0IjsKICAgICAgICBjb25zdCBwb3N0ZXJSYXRpbmdQcm92aWRlciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdQcm92aWRlciIpPy52YWx1ZSB8fCAiIjsKICAgICAgICBjb25zdCBwb3N0ZXJSYXRpbmdBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicG9zdGVyUmF0aW5nQXBpS2V5Iik/LnZhbHVlLnRyaW0oKSB8fCAiIjsKICAgICAgICBjb25zdCBleGNsdWRlZE1vdmllR2VucmVzID0gQXJyYXkuZnJvbShkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dFtuYW1lPSJtb3ZpZS1nZW5yZSJdOmNoZWNrZWQnKSkubWFwKGNiID0+IGNiLnZhbHVlKTsKICAgICAgICBjb25zdCBleGNsdWRlZFNlcmllc0dlbnJlcyA9IEFycmF5LmZyb20oZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnaW5wdXRbbmFtZT0ic2VyaWVzLWdlbnJlIl06Y2hlY2tlZCcpKS5tYXAoY2IgPT4gY2IudmFsdWUpOwogICAgICAgIGNvbnN0IHRtZGJBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYkFwaUtleSIpPy52YWx1ZS50cmltKCkgfHwgIiI7CiAgICAgICAgY29uc3Qgc2lta2xBcGlLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXkiKT8udmFsdWUudHJpbSgpIHx8ICIiOwogICAgICAgIGNvbnN0IGdlbWluaUFwaUtleSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJnZW1pbmlBcGlLZXkiKT8udmFsdWUudHJpbSgpIHx8ICIiOwoKICAgICAgICBjb25zdCBjYXRhbG9nc1RvU2VuZCA9IFtdOwogICAgICAgIGNvbnN0IGNhdGFsb2dzID0gZ2V0Q2F0YWxvZ3MgPyBnZXRDYXRhbG9ncygpIDogW107CiAgICAgICAgLy8gR2V0IGVuYWJsZWQgc3RhdGUgZnJvbSBjYXRhbG9nIG9iamVjdHMgKHVwZGF0ZWQgYnkgdmlzaWJpbGl0eSBidXR0b24pCiAgICAgICAgY2F0YWxvZ3MuZm9yRWFjaChvcmlnaW5hbENhdGFsb2cgPT4gewogICAgICAgICAgICBjb25zdCBjYXRhbG9nSWQgPSBvcmlnaW5hbENhdGFsb2cuaWQ7CiAgICAgICAgICAgIGNvbnN0IGVuYWJsZWQgPSBvcmlnaW5hbENhdGFsb2cuZW5hYmxlZCAhPT0gZmFsc2U7CgogICAgICAgICAgICAvLyBHZXQgZW5hYmxlZF9tb3ZpZSBhbmQgZW5hYmxlZF9zZXJpZXMgZnJvbSB0b2dnbGUgYnV0dG9ucwogICAgICAgICAgICBjb25zdCBhY3RpdmVCdG4gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGAuY2F0YWxvZy10eXBlLWJ0bltkYXRhLWNhdGFsb2ctaWQ9IiR7Y2F0YWxvZ0lkfSJdLmJnLXdoaXRlYCk7CiAgICAgICAgICAgIGxldCBlbmFibGVkTW92aWUgPSB0cnVlOwogICAgICAgICAgICBsZXQgZW5hYmxlZFNlcmllcyA9IHRydWU7CgogICAgICAgICAgICBpZiAoYWN0aXZlQnRuKSB7CiAgICAgICAgICAgICAgICBjb25zdCBtb2RlID0gYWN0aXZlQnRuLmRhdGFzZXQubW9kZTsKICAgICAgICAgICAgICAgIGlmIChtb2RlID09PSAnbW92aWUnKSB7CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZE1vdmllID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICBlbmFibGVkU2VyaWVzID0gZmFsc2U7CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG1vZGUgPT09ICdzZXJpZXMnKSB7CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZE1vdmllID0gZmFsc2U7CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZFNlcmllcyA9IHRydWU7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIC8vICdib3RoJyBvciBkZWZhdWx0CiAgICAgICAgICAgICAgICAgICAgZW5hYmxlZE1vdmllID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICBlbmFibGVkU2VyaWVzID0gdHJ1ZTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIC8vIEZhbGxiYWNrIHRvIGNhdGFsb2cgc3RhdGUKICAgICAgICAgICAgICAgIGVuYWJsZWRNb3ZpZSA9IG9yaWdpbmFsQ2F0YWxvZy5lbmFibGVkTW92aWUgIT09IGZhbHNlOwogICAgICAgICAgICAgICAgZW5hYmxlZFNlcmllcyA9IG9yaWdpbmFsQ2F0YWxvZy5lbmFibGVkU2VyaWVzICE9PSBmYWxzZTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgY2F0YWxvZ3NUb1NlbmQucHVzaCh7CiAgICAgICAgICAgICAgICBpZDogY2F0YWxvZ0lkLAogICAgICAgICAgICAgICAgbmFtZTogb3JpZ2luYWxDYXRhbG9nLm5hbWUsCiAgICAgICAgICAgICAgICBlbmFibGVkOiBlbmFibGVkLAogICAgICAgICAgICAgICAgZW5hYmxlZF9tb3ZpZTogZW5hYmxlZE1vdmllLAogICAgICAgICAgICAgICAgZW5hYmxlZF9zZXJpZXM6IGVuYWJsZWRTZXJpZXMsCiAgICAgICAgICAgICAgICBkaXNwbGF5X2F0X2hvbWU6IG9yaWdpbmFsQ2F0YWxvZy5kaXNwbGF5X2F0X2hvbWUgIT09IGZhbHNlLCAvLyBEZWZhdWx0IHRvIHRydWUgaWYgbm90IHNldAogICAgICAgICAgICAgICAgc2h1ZmZsZTogb3JpZ2luYWxDYXRhbG9nLnNodWZmbGUgPT09IHRydWUsIC8vIERlZmF1bHQgdG8gZmFsc2UgaWYgbm90IHNldAogICAgICAgICAgICB9KTsKICAgICAgICB9KTsKCiAgICAgICAgLy8gRGV0ZXJtaW5lIGFjdGl2ZSBwcm92aWRlcgogICAgICAgIGNvbnN0IHRyYWt0VG9rZW5zID0gZ2V0VHJha3RUb2tlbnNGcm9tU3RvcmFnZSgpOwogICAgICAgIGNvbnN0IGlzVHJha3RMb2dpbiA9ICEhKHRyYWt0VG9rZW5zICYmIHRyYWt0VG9rZW5zLmFjY2Vzc190b2tlbiAmJiAhc0F1dGhLZXkgJiYgIWVtYWlsKTsKCiAgICAgICAgLy8gVmFsaWRhdGlvbgogICAgICAgIGlmICghc0F1dGhLZXkgJiYgIShlbWFpbCAmJiBwYXNzd29yZCkgJiYgIWlzVHJha3RMb2dpbikgewogICAgICAgICAgICBzaG93RXJyb3IoImdlbmVyYWxFcnJvciIsICJQbGVhc2UgbG9naW4gd2l0aCBTdHJlbWlvIG9yIFRyYWt0IHRvIGNvbnRpbnVlLiIpOwogICAgICAgICAgICBzd2l0Y2hTZWN0aW9uKCdsb2dpbicpOwogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQoKICAgICAgICBpZiAoIXRtZGJBcGlLZXkpIHsKICAgICAgICAgICAgc2hvd0Vycm9yKCJnZW5lcmFsRXJyb3IiLCAiVE1EQiBBUEkga2V5IGlzIHJlcXVpcmVkLiIpOwogICAgICAgICAgICBjb25zdCB0bWRiSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYkFwaUtleSIpOwogICAgICAgICAgICBpZiAodG1kYklucHV0KSB7CiAgICAgICAgICAgICAgICB0bWRiSW5wdXQuZm9jdXMoKTsKICAgICAgICAgICAgICAgIHRtZGJJbnB1dC5zY3JvbGxJbnRvVmlldyh7IGJlaGF2aW9yOiAic21vb3RoIiwgYmxvY2s6ICJjZW50ZXIiIH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybjsKICAgICAgICB9CgogICAgICAgIC8vIFZhbGlkYXRlIHBvc3RlciByYXRpbmcgQVBJIGtleSBpZiBwcm92aWRlZAogICAgICAgIGlmIChwb3N0ZXJSYXRpbmdQcm92aWRlciAmJiBwb3N0ZXJSYXRpbmdBcGlLZXkpIHsKICAgICAgICAgICAgaWYgKHdpbmRvdy52YWxpZGF0ZVBvc3RlclJhdGluZ0FwaUtleSkgewogICAgICAgICAgICAgICAgY29uc3QgaXNWYWxpZCA9IGF3YWl0IHdpbmRvdy52YWxpZGF0ZVBvc3RlclJhdGluZ0FwaUtleSgpOwogICAgICAgICAgICAgICAgaWYgKCFpc1ZhbGlkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBzZXRMb2FkaW5nKHRydWUpOwoKICAgICAgICB0cnkgewogICAgICAgICAgICAvLyBCdWlsZCBwb3N0ZXJfcmF0aW5nIHBheWxvYWQKICAgICAgICAgICAgbGV0IHBvc3RlclJhdGluZyA9IG51bGw7CiAgICAgICAgICAgIGlmIChwb3N0ZXJSYXRpbmdQcm92aWRlciAmJiBwb3N0ZXJSYXRpbmdBcGlLZXkpIHsKICAgICAgICAgICAgICAgIHBvc3RlclJhdGluZyA9IHsKICAgICAgICAgICAgICAgICAgICBwcm92aWRlcjogcG9zdGVyUmF0aW5nUHJvdmlkZXIsCiAgICAgICAgICAgICAgICAgICAgYXBpX2tleTogcG9zdGVyUmF0aW5nQXBpS2V5CiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9CgogICAgICAgICAgICBsZXQgZW5kcG9pbnQsIHBheWxvYWQ7CgogICAgICAgICAgICBpZiAoaXNUcmFrdExvZ2luKSB7CiAgICAgICAgICAgICAgICAvLyAtLS0tIFRyYWt0IHN1Ym1pc3Npb24gLS0tLQogICAgICAgICAgICAgICAgZW5kcG9pbnQgPSAiL3Rva2Vucy90cmFrdC8iOwogICAgICAgICAgICAgICAgcGF5bG9hZCA9IHsKICAgICAgICAgICAgICAgICAgICB0cmFrdF9hY2Nlc3NfdG9rZW46IHRyYWt0VG9rZW5zLmFjY2Vzc190b2tlbiwKICAgICAgICAgICAgICAgICAgICB0cmFrdF9yZWZyZXNoX3Rva2VuOiB0cmFrdFRva2Vucy5yZWZyZXNoX3Rva2VuIHx8IHVuZGVmaW5lZCwKICAgICAgICAgICAgICAgICAgICB0cmFrdF9leHBpcmVzX2F0OiB0cmFrdFRva2Vucy5leHBpcmVzX2F0IHx8IHVuZGVmaW5lZCwKICAgICAgICAgICAgICAgICAgICBjYXRhbG9nczogY2F0YWxvZ3NUb1NlbmQsCiAgICAgICAgICAgICAgICAgICAgbGFuZ3VhZ2U6IGxhbmd1YWdlLAogICAgICAgICAgICAgICAgICAgIHllYXJfbWluOiB5ZWFyTWluLAogICAgICAgICAgICAgICAgICAgIHllYXJfbWF4OiB5ZWFyTWF4LAogICAgICAgICAgICAgICAgICAgIHBvcHVsYXJpdHk6IHBvcHVsYXJpdHksCiAgICAgICAgICAgICAgICAgICAgc29ydGluZ19vcmRlcjogc29ydGluZ09yZGVyLAogICAgICAgICAgICAgICAgICAgIHBvc3Rlcl9yYXRpbmc6IHBvc3RlclJhdGluZywKICAgICAgICAgICAgICAgICAgICB0bWRiX2FwaV9rZXk6IHRtZGJBcGlLZXkgfHwgdW5kZWZpbmVkLAogICAgICAgICAgICAgICAgICAgIHNpbWtsX2FwaV9rZXk6IHNpbWtsQXBpS2V5LAogICAgICAgICAgICAgICAgICAgIGdlbWluaV9hcGlfa2V5OiBnZW1pbmlBcGlLZXksCiAgICAgICAgICAgICAgICAgICAgZXhjbHVkZWRfbW92aWVfZ2VucmVzOiBleGNsdWRlZE1vdmllR2VucmVzLAogICAgICAgICAgICAgICAgICAgIGV4Y2x1ZGVkX3Nlcmllc19nZW5yZXM6IGV4Y2x1ZGVkU2VyaWVzR2VucmVzCiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgLy8gLS0tLSBTdHJlbWlvIHN1Ym1pc3Npb24gLS0tLQogICAgICAgICAgICAgICAgZW5kcG9pbnQgPSAiL3Rva2Vucy8iOwogICAgICAgICAgICAgICAgcGF5bG9hZCA9IHsKICAgICAgICAgICAgICAgICAgICBhdXRoS2V5OiBzQXV0aEtleSB8fCB1bmRlZmluZWQsCiAgICAgICAgICAgICAgICAgICAgZW1haWw6IGVtYWlsIHx8IHVuZGVmaW5lZCwKICAgICAgICAgICAgICAgICAgICBwYXNzd29yZDogcGFzc3dvcmQgfHwgdW5kZWZpbmVkLAogICAgICAgICAgICAgICAgICAgIGNhdGFsb2dzOiBjYXRhbG9nc1RvU2VuZCwKICAgICAgICAgICAgICAgICAgICBsYW5ndWFnZTogbGFuZ3VhZ2UsCiAgICAgICAgICAgICAgICAgICAgeWVhcl9taW46IHllYXJNaW4sCiAgICAgICAgICAgICAgICAgICAgeWVhcl9tYXg6IHllYXJNYXgsCiAgICAgICAgICAgICAgICAgICAgcG9wdWxhcml0eTogcG9wdWxhcml0eSwKICAgICAgICAgICAgICAgICAgICBzb3J0aW5nX29yZGVyOiBzb3J0aW5nT3JkZXIsCiAgICAgICAgICAgICAgICAgICAgcG9zdGVyX3JhdGluZzogcG9zdGVyUmF0aW5nLAogICAgICAgICAgICAgICAgICAgIHRtZGJfYXBpX2tleTogdG1kYkFwaUtleSB8fCB1bmRlZmluZWQsCiAgICAgICAgICAgICAgICAgICAgc2lta2xfYXBpX2tleTogc2lta2xBcGlLZXksCiAgICAgICAgICAgICAgICAgICAgZ2VtaW5pX2FwaV9rZXk6IGdlbWluaUFwaUtleSwKICAgICAgICAgICAgICAgICAgICBleGNsdWRlZF9tb3ZpZV9nZW5yZXM6IGV4Y2x1ZGVkTW92aWVHZW5yZXMsCiAgICAgICAgICAgICAgICAgICAgZXhjbHVkZWRfc2VyaWVzX2dlbnJlczogZXhjbHVkZWRTZXJpZXNHZW5yZXMKICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goZW5kcG9pbnQsIHsKICAgICAgICAgICAgICAgIG1ldGhvZDogIlBPU1QiLAogICAgICAgICAgICAgICAgaGVhZGVyczogeyAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iIH0sCiAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKQogICAgICAgICAgICB9KTsKCiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHsKICAgICAgICAgICAgICAgIGNvbnN0IGVycm9yRGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihlcnJvckRhdGEuZGV0YWlsIHx8ICJGYWlsZWQgdG8gZ2VuZXJhdGUgbWFuaWZlc3QgVVJMIik7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTsKICAgICAgICAgICAgc2hvd1N1Y2Nlc3MoZGF0YS5tYW5pZmVzdFVybCk7CiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgY29uc29sZS5lcnJvcigiRXJyb3I6IiwgZXJyb3IpOwogICAgICAgICAgICBzaG93RXJyb3IoImdlbmVyYWxFcnJvciIsIGVycm9yLm1lc3NhZ2UpOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHNldExvYWRpbmcoZmFsc2UpOwogICAgICAgIH0KICAgIH0pOwp9CgovLyBVSSBIZWxwZXJzICYgR2VucmUgTGlzdHMKZnVuY3Rpb24gaW5pdGlhbGl6ZUdlbnJlTGlzdHMoKSB7CiAgICByZW5kZXJHZW5yZUxpc3QobW92aWVHZW5yZUxpc3QsIE1PVklFX0dFTlJFUywgJ21vdmllLWdlbnJlJyk7CiAgICByZW5kZXJHZW5yZUxpc3Qoc2VyaWVzR2VucmVMaXN0LCBTRVJJRVNfR0VOUkVTLCAnc2VyaWVzLWdlbnJlJyk7Cn0KCmZ1bmN0aW9uIHJlbmRlckdlbnJlTGlzdChjb250YWluZXIsIGdlbnJlcywgbmFtZVByZWZpeCkgewogICAgaWYgKCFjb250YWluZXIpIHJldHVybjsKICAgIGNvbnRhaW5lci5pbm5lckhUTUwgPSBnZW5yZXMubWFwKGdlbnJlID0+IGAKICAgICAgICA8bGFiZWwgY2xhc3M9ImZsZXggaXRlbXMtY2VudGVyIGdhcC0zIHAtMiByb3VuZGVkLWxnIGhvdmVyOmJnLXdoaXRlLzUgY3Vyc29yLXBvaW50ZXIgdHJhbnNpdGlvbiBncm91cCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9InJlbGF0aXZlIGZsZXggaXRlbXMtY2VudGVyIj4KICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJjaGVja2JveCIgbmFtZT0iJHtuYW1lUHJlZml4fSIgdmFsdWU9IiR7Z2VucmUuaWR9IgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJwZWVyIGFwcGVhcmFuY2Utbm9uZSB3LTUgaC01IGJvcmRlci0yIGJvcmRlci1zbGF0ZS02MDAgcm91bmRlZCBiZy1uZXV0cmFsLTkwMCBjaGVja2VkOmJnLXdoaXRlIGNoZWNrZWQ6Ym9yZGVyLXdoaXRlIHRyYW5zaXRpb24tY29sb3JzIj4KICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9ImFic29sdXRlIHctMy41IGgtMy41IHRleHQtYmxhY2sgbGVmdC0xLzIgdG9wLTEvMiAtdHJhbnNsYXRlLXgtMS8yIC10cmFuc2xhdGUteS0xLzIgb3BhY2l0eS0wIHBlZXItY2hlY2tlZDpvcGFjaXR5LTEwMCBwb2ludGVyLWV2ZW50cy1ub25lIHRyYW5zaXRpb24tb3BhY2l0eSIKICAgICAgICAgICAgICAgICAgICBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgZD0iTTUgMTNsNCA0TDE5IDciPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvc3ZnPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPHNwYW4gY2xhc3M9InRleHQtc20gdGV4dC1zbGF0ZS0zMDAgZ3JvdXAtaG92ZXI6dGV4dC13aGl0ZSB0cmFuc2l0aW9uLWNvbG9ycyBzZWxlY3Qtbm9uZSI+JHtnZW5yZS5uYW1lfTwvc3Bhbj4KICAgICAgICA8L2xhYmVsPgogICAgYCkuam9pbignJyk7Cn0KCmZ1bmN0aW9uIGluaXRpYWxpemVMYW5ndWFnZVNlbGVjdCgpIHsKICAgIGlmICghbGFuZ3VhZ2VTZWxlY3QpIHJldHVybjsKfQoKLy8gUG9zdGVyIFJhdGluZyBQcm92aWRlcgpmdW5jdGlvbiBpbml0aWFsaXplUG9zdGVyUmF0aW5nUHJvdmlkZXIoKSB7CiAgICBjb25zdCBwcm92aWRlclNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdQcm92aWRlciIpOwogICAgY29uc3QgYXBpS2V5Q29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0FwaUtleUNvbnRhaW5lciIpOwogICAgY29uc3QgYXBpS2V5SW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicG9zdGVyUmF0aW5nQXBpS2V5Iik7CiAgICBjb25zdCBoZWxwQ29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0hlbHAiKTsKICAgIGNvbnN0IGhlbHBUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0hlbHBUZXh0Iik7CiAgICBjb25zdCB2YWxpZGF0ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdBcGlLZXlWYWxpZGF0ZSIpOwogICAgY29uc3QgdG9nZ2xlQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBvc3RlclJhdGluZ0FwaUtleVRvZ2dsZSIpOwogICAgY29uc3QgZXllSWNvbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdBcGlLZXlFeWUiKTsKICAgIGNvbnN0IGV5ZU9mZkljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicG9zdGVyUmF0aW5nQXBpS2V5RXllT2ZmIik7CiAgICBjb25zdCB2YWxpZGF0aW9uTWVzc2FnZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwb3N0ZXJSYXRpbmdWYWxpZGF0aW9uTWVzc2FnZSIpOwoKICAgIGlmICghcHJvdmlkZXJTZWxlY3QgfHwgIWFwaUtleUNvbnRhaW5lciB8fCAhYXBpS2V5SW5wdXQgfHwgIWhlbHBDb250YWluZXIgfHwgIWhlbHBUZXh0KSByZXR1cm47CgogICAgY29uc3QgcHJvdmlkZXJJbmZvID0gewogICAgICAgICJycGRiIjogewogICAgICAgICAgICBuYW1lOiAiUlBEQiAoUmF0aW5nUG9zdGVyREIpIiwKICAgICAgICAgICAgdXJsOiAiaHR0cHM6Ly9yYXRpbmdwb3N0ZXJkYi5jb20iLAogICAgICAgICAgICBkZXNjcmlwdGlvbjogIkVuYWJsZSByYXRpbmdzIG9uIHBvc3RlcnMgdmlhIFJhdGluZ1Bvc3RlckRCIgogICAgICAgIH0sCiAgICAgICAgInRvcF9wb3N0ZXJzIjogewogICAgICAgICAgICBuYW1lOiAiVG9wIFBvc3RlcnMiLAogICAgICAgICAgICB1cmw6ICJodHRwczovL2FwaS50b3Atc3RyZWFtaW5nLnN0cmVhbS8iLAogICAgICAgICAgICBkZXNjcmlwdGlvbjogIkVuYWJsZSByYXRpbmdzIG9uIHBvc3RlcnMgdmlhIFRvcCBQb3N0ZXJzIgogICAgICAgIH0KICAgIH07CgogICAgbGV0IGlzVmFsaWRhdGVkID0gZmFsc2U7CgogICAgLy8gRXllIHRvZ2dsZSBmdW5jdGlvbmFsaXR5CiAgICBpZiAodG9nZ2xlQnRuICYmIGV5ZUljb24gJiYgZXllT2ZmSWNvbikgewogICAgICAgIHRvZ2dsZUJ0bi5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsICgpID0+IHsKICAgICAgICAgICAgY29uc3QgaXNQYXNzd29yZCA9IGFwaUtleUlucHV0LnR5cGUgPT09ICJwYXNzd29yZCI7CiAgICAgICAgICAgIGFwaUtleUlucHV0LnR5cGUgPSBpc1Bhc3N3b3JkID8gInRleHQiIDogInBhc3N3b3JkIjsKICAgICAgICAgICAgZXllSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCAhaXNQYXNzd29yZCk7CiAgICAgICAgICAgIGV5ZU9mZkljb24uY2xhc3NMaXN0LnRvZ2dsZSgiaGlkZGVuIiwgaXNQYXNzd29yZCk7CiAgICAgICAgfSk7CiAgICB9CgogICAgLy8gVmFsaWRhdGlvbiBmdW5jdGlvbgogICAgYXN5bmMgZnVuY3Rpb24gdmFsaWRhdGVBcGlLZXkoKSB7CiAgICAgICAgY29uc3Qgc2VsZWN0ZWRQcm92aWRlciA9IHByb3ZpZGVyU2VsZWN0LnZhbHVlOwogICAgICAgIGNvbnN0IGFwaUtleSA9IGFwaUtleUlucHV0LnZhbHVlLnRyaW0oKTsKCiAgICAgICAgaWYgKCFzZWxlY3RlZFByb3ZpZGVyIHx8ICFhcGlLZXkpIHsKICAgICAgICAgICAgc2hvd1ZhbGlkYXRpb25NZXNzYWdlKCJQbGVhc2Ugc2VsZWN0IGEgcHJvdmlkZXIgYW5kIGVudGVyIGFuIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgaWYgKCF2YWxpZGF0ZUJ0bikgcmV0dXJuIGZhbHNlOwoKICAgICAgICAvLyBTaG93IGxvYWRpbmcgc3RhdGUKICAgICAgICB2YWxpZGF0ZUJ0bi5kaXNhYmxlZCA9IHRydWU7CiAgICAgICAgdmFsaWRhdGVCdG4uY2xhc3NMaXN0LmFkZCgib3BhY2l0eS01MCIsICJjdXJzb3Itbm90LWFsbG93ZWQiKTsKICAgICAgICBjb25zdCBvcmlnaW5hbEhUTUwgPSB2YWxpZGF0ZUJ0bi5pbm5lckhUTUw7CiAgICAgICAgdmFsaWRhdGVCdG4uaW5uZXJIVE1MID0gJzxzdmcgY2xhc3M9InctNSBoLTUgYW5pbWF0ZS1zcGluIiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGNsYXNzPSJvcGFjaXR5LTI1IiBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjQiPjwvY2lyY2xlPjxwYXRoIGNsYXNzPSJvcGFjaXR5LTc1IiBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik00IDEyYTggOCAwIDAxOC04VjBDNS4zNzMgMCAwIDUuMzczIDAgMTJoNHptMiA1LjI5MUE3Ljk2MiA3Ljk2MiAwIDAxNCAxMkgwYzAgMy4wNDIgMS4xMzUgNS44MjQgMyA3LjkzOGwzLTIuNjQ3eiI+PC9wYXRoPjwvc3ZnPic7CgogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goIi9wb3N0ZXItcmF0aW5nL3ZhbGlkYXRlIiwgewogICAgICAgICAgICAgICAgbWV0aG9kOiAiUE9TVCIsCiAgICAgICAgICAgICAgICBoZWFkZXJzOiB7ICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIgfSwKICAgICAgICAgICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgcHJvdmlkZXI6IHNlbGVjdGVkUHJvdmlkZXIsIGFwaV9rZXk6IGFwaUtleSB9KQogICAgICAgICAgICB9KTsKCiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7CgogICAgICAgICAgICBpZiAoZGF0YS52YWxpZCkgewogICAgICAgICAgICAgICAgc2hvd1ZhbGlkYXRpb25NZXNzYWdlKCJBUEkga2V5IGlzIHZhbGlkIOKckyIsICJzdWNjZXNzIik7CiAgICAgICAgICAgICAgICBpc1ZhbGlkYXRlZCA9IHRydWU7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIHNob3dWYWxpZGF0aW9uTWVzc2FnZShkYXRhLm1lc3NhZ2UgfHwgIkludmFsaWQgQVBJIGtleSIsICJlcnJvciIpOwogICAgICAgICAgICAgICAgYXBpS2V5SW5wdXQudmFsdWUgPSAiIjsgLy8gQ2xlYXIgaW52YWxpZCBrZXkKICAgICAgICAgICAgICAgIGlzVmFsaWRhdGVkID0gZmFsc2U7CiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgIH0KICAgICAgICB9IGNhdGNoIChlcnJvcikgewogICAgICAgICAgICBzaG93VmFsaWRhdGlvbk1lc3NhZ2UoIlZhbGlkYXRpb24gZmFpbGVkLiBQbGVhc2UgdHJ5IGFnYWluLiIsICJlcnJvciIpOwogICAgICAgICAgICBpc1ZhbGlkYXRlZCA9IGZhbHNlOwogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSBmYWxzZTsKICAgICAgICAgICAgdmFsaWRhdGVCdG4uY2xhc3NMaXN0LnJlbW92ZSgib3BhY2l0eS01MCIsICJjdXJzb3Itbm90LWFsbG93ZWQiKTsKICAgICAgICAgICAgdmFsaWRhdGVCdG4uaW5uZXJIVE1MID0gb3JpZ2luYWxIVE1MOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBTaG93IHZhbGlkYXRpb24gbWVzc2FnZQogICAgZnVuY3Rpb24gc2hvd1ZhbGlkYXRpb25NZXNzYWdlKG1lc3NhZ2UsIHR5cGUpIHsKICAgICAgICBpZiAoIXZhbGlkYXRpb25NZXNzYWdlKSByZXR1cm47CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UudGV4dENvbnRlbnQgPSBtZXNzYWdlOwogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTmFtZSA9IGBtdC0yIHRleHQteHMgJHt0eXBlID09PSAic3VjY2VzcyIgPyAidGV4dC1ncmVlbi00MDAiIDogInRleHQtcmVkLTQwMCJ9YDsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsKICAgIH0KCiAgICAvLyBDbGVhciB2YWxpZGF0aW9uIG1lc3NhZ2UKICAgIGZ1bmN0aW9uIGNsZWFyVmFsaWRhdGlvbk1lc3NhZ2UoKSB7CiAgICAgICAgaWYgKHZhbGlkYXRpb25NZXNzYWdlKSB7CiAgICAgICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBWYWxpZGF0ZSBidXR0b24gY2xpY2sKICAgIGlmICh2YWxpZGF0ZUJ0bikgewogICAgICAgIHZhbGlkYXRlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgdmFsaWRhdGVBcGlLZXkpOwogICAgfQoKICAgIC8vIENsZWFyIHZhbGlkYXRpb24gd2hlbiBBUEkga2V5IGNoYW5nZXMKICAgIGFwaUtleUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoImlucHV0IiwgKCkgPT4gewogICAgICAgIGlzVmFsaWRhdGVkID0gZmFsc2U7CiAgICAgICAgY2xlYXJWYWxpZGF0aW9uTWVzc2FnZSgpOwogICAgfSk7CgogICAgZnVuY3Rpb24gdXBkYXRlVUkoKSB7CiAgICAgICAgY29uc3Qgc2VsZWN0ZWRQcm92aWRlciA9IHByb3ZpZGVyU2VsZWN0LnZhbHVlOwoKICAgICAgICBpZiAoc2VsZWN0ZWRQcm92aWRlciAmJiBwcm92aWRlckluZm9bc2VsZWN0ZWRQcm92aWRlcl0pIHsKICAgICAgICAgICAgY29uc3QgaW5mbyA9IHByb3ZpZGVySW5mb1tzZWxlY3RlZFByb3ZpZGVyXTsKICAgICAgICAgICAgYXBpS2V5Q29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAiYmxvY2siOwogICAgICAgICAgICBoZWxwQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAiYmxvY2siOwogICAgICAgICAgICBoZWxwVGV4dC5pbm5lckhUTUwgPSBgJHtpbmZvLmRlc2NyaXB0aW9ufS4gR2V0IHlvdXIgQVBJIGtleSBmcm9tIDxhIGhyZWY9IiR7aW5mby51cmx9IiB0YXJnZXQ9Il9ibGFuayIgY2xhc3M9InRleHQtc2xhdGUtMzAwIGhvdmVyOnRleHQtd2hpdGUgdW5kZXJsaW5lIj4ke2luZm8ubmFtZX08L2E+LmA7CiAgICAgICAgICAgIC8vIERvbid0IGNsZWFyIHRoZSBBUEkga2V5IHdoZW4gc3dpdGNoaW5nIHByb3ZpZGVycyAtIGp1c3QgcmVzZXQgdmFsaWRhdGlvbgogICAgICAgICAgICBpc1ZhbGlkYXRlZCA9IGZhbHNlOwogICAgICAgICAgICBjbGVhclZhbGlkYXRpb25NZXNzYWdlKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgLy8gT25seSBjbGVhciB3aGVuIHByb3ZpZGVyIGlzIHNldCB0byAiTm9uZSIKICAgICAgICAgICAgYXBpS2V5Q29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAibm9uZSI7CiAgICAgICAgICAgIGhlbHBDb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICJub25lIjsKICAgICAgICAgICAgYXBpS2V5SW5wdXQudmFsdWUgPSAiIjsKICAgICAgICAgICAgaXNWYWxpZGF0ZWQgPSBmYWxzZTsKICAgICAgICAgICAgY2xlYXJWYWxpZGF0aW9uTWVzc2FnZSgpOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBIYW5kbGUgcHJvdmlkZXIgY2hhbmdlIC0gcHJlc2VydmUgQVBJIGtleSB2YWx1ZSwganVzdCByZXNldCB2YWxpZGF0aW9uCiAgICBwcm92aWRlclNlbGVjdC5hZGRFdmVudExpc3RlbmVyKCJjaGFuZ2UiLCAoKSA9PiB7CiAgICAgICAgaXNWYWxpZGF0ZWQgPSBmYWxzZTsKICAgICAgICBjbGVhclZhbGlkYXRpb25NZXNzYWdlKCk7CiAgICAgICAgdXBkYXRlVUkoKTsKICAgIH0pOwoKICAgIHVwZGF0ZVVJKCk7IC8vIEluaXRpYWxpemUgb24gbG9hZAoKICAgIC8vIEV4cG9ydCB2YWxpZGF0ZSBmdW5jdGlvbiBmb3IgZm9ybSBzdWJtaXNzaW9uCiAgICB3aW5kb3cudmFsaWRhdGVQb3N0ZXJSYXRpbmdBcGlLZXkgPSB2YWxpZGF0ZUFwaUtleTsKfQoKLy8gVE1EQiBBUEkgS2V5IChSZXF1aXJlZCkKZnVuY3Rpb24gaW5pdGlhbGl6ZVRtZGIoKSB7CiAgICBjb25zdCBhcGlLZXlJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0bWRiQXBpS2V5Iik7CiAgICBjb25zdCB2YWxpZGF0ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0bWRiQXBpS2V5VmFsaWRhdGUiKTsKICAgIGNvbnN0IHRvZ2dsZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ0bWRiQXBpS2V5VG9nZ2xlIik7CiAgICBjb25zdCBleWVJY29uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInRtZGJBcGlLZXlFeWUiKTsKICAgIGNvbnN0IGV5ZU9mZkljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYkFwaUtleUV5ZU9mZiIpOwogICAgY29uc3QgdmFsaWRhdGlvbk1lc3NhZ2UgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidG1kYlZhbGlkYXRpb25NZXNzYWdlIik7CgogICAgaWYgKCFhcGlLZXlJbnB1dCB8fCAhdmFsaWRhdGlvbk1lc3NhZ2UpIHJldHVybjsKCiAgICBpZiAodG9nZ2xlQnRuICYmIGV5ZUljb24gJiYgZXllT2ZmSWNvbikgewogICAgICAgIHRvZ2dsZUJ0bi5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsICgpID0+IHsKICAgICAgICAgICAgY29uc3QgaXNQYXNzd29yZCA9IGFwaUtleUlucHV0LnR5cGUgPT09ICJwYXNzd29yZCI7CiAgICAgICAgICAgIGFwaUtleUlucHV0LnR5cGUgPSBpc1Bhc3N3b3JkID8gInRleHQiIDogInBhc3N3b3JkIjsKICAgICAgICAgICAgZXllSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCAhaXNQYXNzd29yZCk7CiAgICAgICAgICAgIGV5ZU9mZkljb24uY2xhc3NMaXN0LnRvZ2dsZSgiaGlkZGVuIiwgaXNQYXNzd29yZCk7CiAgICAgICAgfSk7CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd1RtZGJWYWxpZGF0aW9uTWVzc2FnZShtZXNzYWdlLCB0eXBlKSB7CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UudGV4dENvbnRlbnQgPSBtZXNzYWdlOwogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTmFtZSA9IGBtdC0yIHRleHQteHMgJHt0eXBlID09PSAic3VjY2VzcyIgPyAidGV4dC1ncmVlbi00MDAiIDogInRleHQtcmVkLTQwMCJ9YDsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsKICAgIH0KCiAgICBpZiAodmFsaWRhdGVCdG4pIHsKICAgICAgICB2YWxpZGF0ZUJ0bi5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGFzeW5jICgpID0+IHsKICAgICAgICAgICAgY29uc3QgYXBpS2V5ID0gYXBpS2V5SW5wdXQudmFsdWUudHJpbSgpOwogICAgICAgICAgICBpZiAoIWFwaUtleSkgewogICAgICAgICAgICAgICAgc2hvd1RtZGJWYWxpZGF0aW9uTWVzc2FnZSgiUGxlYXNlIGVudGVyIGEgVE1EQiBBUEkga2V5IiwgImVycm9yIik7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgICAgICB2YWxpZGF0ZUJ0bi5jbGFzc0xpc3QuYWRkKCJvcGFjaXR5LTUwIiwgImN1cnNvci1ub3QtYWxsb3dlZCIpOwogICAgICAgICAgICBjb25zdCBvcmlnaW5hbEhUTUwgPSB2YWxpZGF0ZUJ0bi5pbm5lckhUTUw7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPSJ3LTUgaC01IGFuaW1hdGUtc3BpbiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjbGFzcz0ib3BhY2l0eS0yNSIgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT48cGF0aCBjbGFzcz0ib3BhY2l0eS03NSIgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD48L3N2Zz4nOwogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgiL3RtZGIvdmFsaWRhdGlvbiIsIHsKICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICJQT1NUIiwKICAgICAgICAgICAgICAgICAgICBoZWFkZXJzOiB7ICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIgfSwKICAgICAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGFwaV9rZXk6IGFwaUtleSB9KQogICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpOwogICAgICAgICAgICAgICAgaWYgKGRhdGEudmFsaWQpIHsKICAgICAgICAgICAgICAgICAgICBzaG93VG1kYlZhbGlkYXRpb25NZXNzYWdlKCJUTURCIEFQSSBrZXkgaXMgdmFsaWQg4pyTIiwgInN1Y2Nlc3MiKTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgc2hvd1RtZGJWYWxpZGF0aW9uTWVzc2FnZShkYXRhLm1lc3NhZ2UgfHwgIkludmFsaWQgVE1EQiBBUEkga2V5IiwgImVycm9yIik7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICAgICAgICBzaG93VG1kYlZhbGlkYXRpb25NZXNzYWdlKCJWYWxpZGF0aW9uIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCAiZXJyb3IiKTsKICAgICAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgICAgIHZhbGlkYXRlQnRuLmRpc2FibGVkID0gZmFsc2U7CiAgICAgICAgICAgICAgICB2YWxpZGF0ZUJ0bi5jbGFzc0xpc3QucmVtb3ZlKCJvcGFjaXR5LTUwIiwgImN1cnNvci1ub3QtYWxsb3dlZCIpOwogICAgICAgICAgICAgICAgdmFsaWRhdGVCdG4uaW5uZXJIVE1MID0gb3JpZ2luYWxIVE1MOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9CgogICAgYXBpS2V5SW5wdXQuYWRkRXZlbnRMaXN0ZW5lcigiaW5wdXQiLCAoKSA9PiB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QuYWRkKCJoaWRkZW4iKSk7Cn0KCi8vIFNpbWtsIEludGVncmF0aW9uCmZ1bmN0aW9uIGluaXRpYWxpemVTaW1rbCgpIHsKICAgIGNvbnN0IGFwaUtleUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNpbWtsQXBpS2V5Iik7CiAgICBjb25zdCB2YWxpZGF0ZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJzaW1rbEFwaUtleVZhbGlkYXRlIik7CiAgICBjb25zdCB0b2dnbGVCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXlUb2dnbGUiKTsKICAgIGNvbnN0IGV5ZUljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXlFeWUiKTsKICAgIGNvbnN0IGV5ZU9mZkljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2lta2xBcGlLZXlFeWVPZmYiKTsKICAgIGNvbnN0IHZhbGlkYXRpb25NZXNzYWdlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNpbWtsVmFsaWRhdGlvbk1lc3NhZ2UiKTsKCiAgICBpZiAoIWFwaUtleUlucHV0IHx8ICF2YWxpZGF0ZUJ0biB8fCAhdmFsaWRhdGlvbk1lc3NhZ2UpIHJldHVybjsKCiAgICAvLyBFeWUgdG9nZ2xlIGZ1bmN0aW9uYWxpdHkKICAgIGlmICh0b2dnbGVCdG4gJiYgZXllSWNvbiAmJiBleWVPZmZJY29uKSB7CiAgICAgICAgdG9nZ2xlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgKCkgPT4gewogICAgICAgICAgICBjb25zdCBpc1Bhc3N3b3JkID0gYXBpS2V5SW5wdXQudHlwZSA9PT0gInBhc3N3b3JkIjsKICAgICAgICAgICAgYXBpS2V5SW5wdXQudHlwZSA9IGlzUGFzc3dvcmQgPyAidGV4dCIgOiAicGFzc3dvcmQiOwogICAgICAgICAgICBleWVJY29uLmNsYXNzTGlzdC50b2dnbGUoImhpZGRlbiIsICFpc1Bhc3N3b3JkKTsKICAgICAgICAgICAgZXllT2ZmSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCBpc1Bhc3N3b3JkKTsKICAgICAgICB9KTsKICAgIH0KCiAgICAvLyBWYWxpZGF0aW9uIGZ1bmN0aW9uCiAgICBhc3luYyBmdW5jdGlvbiB2YWxpZGF0ZVNpbWtsS2V5KCkgewogICAgICAgIGNvbnN0IGFwaUtleSA9IGFwaUtleUlucHV0LnZhbHVlLnRyaW0oKTsKCiAgICAgICAgaWYgKCFhcGlLZXkpIHsKICAgICAgICAgICAgc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UoIlBsZWFzZSBlbnRlciBhIFNpbWtsIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgLy8gU2hvdyBsb2FkaW5nIHN0YXRlCiAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5hZGQoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgY29uc3Qgb3JpZ2luYWxIVE1MID0gdmFsaWRhdGVCdG4uaW5uZXJIVE1MOwogICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPSJ3LTUgaC01IGFuaW1hdGUtc3BpbiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjbGFzcz0ib3BhY2l0eS0yNSIgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT48cGF0aCBjbGFzcz0ib3BhY2l0eS03NSIgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD48L3N2Zz4nOwoKICAgICAgICB0cnkgewogICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKCIvc2lta2wvdmFsaWRhdGlvbiIsIHsKICAgICAgICAgICAgICAgIG1ldGhvZDogIlBPU1QiLAogICAgICAgICAgICAgICAgaGVhZGVyczogeyAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iIH0sCiAgICAgICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGFwaV9rZXk6IGFwaUtleSB9KQogICAgICAgICAgICB9KTsKCiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7CgogICAgICAgICAgICBpZiAoZGF0YS52YWxpZCkgewogICAgICAgICAgICAgICAgc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UoIlNpbWtsIEFQSSBrZXkgaXMgdmFsaWQg4pyTIiwgInN1Y2Nlc3MiKTsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UoZGF0YS5tZXNzYWdlIHx8ICJJbnZhbGlkIFNpbWtsIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgICAgfQogICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICAgIHNob3dTaW1rbFZhbGlkYXRpb25NZXNzYWdlKCJWYWxpZGF0aW9uIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmRpc2FibGVkID0gZmFsc2U7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5yZW1vdmUoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9IG9yaWdpbmFsSFRNTDsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd1NpbWtsVmFsaWRhdGlvbk1lc3NhZ2UobWVzc2FnZSwgdHlwZSkgewogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLnRleHRDb250ZW50ID0gbWVzc2FnZTsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc05hbWUgPSBgbXQtMiB0ZXh0LXhzICR7dHlwZSA9PT0gInN1Y2Nlc3MiID8gInRleHQtZ3JlZW4tNDAwIiA6ICJ0ZXh0LXJlZC00MDAifWA7CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7CiAgICB9CgogICAgdmFsaWRhdGVCdG4uYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCB2YWxpZGF0ZVNpbWtsS2V5KTsKCiAgICBhcGlLZXlJbnB1dC5hZGRFdmVudExpc3RlbmVyKCJpbnB1dCIsICgpID0+IHsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS5jbGFzc0xpc3QuYWRkKCJoaWRkZW4iKTsKICAgIH0pOwp9CgovLyBHZW1pbmkgQUkgSW50ZWdyYXRpb24KZnVuY3Rpb24gaW5pdGlhbGl6ZUdlbWluaSgpIHsKICAgIGNvbnN0IGFwaUtleUlucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImdlbWluaUFwaUtleSIpOwogICAgY29uc3QgdmFsaWRhdGVCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiZ2VtaW5pQXBpS2V5VmFsaWRhdGUiKTsKICAgIGNvbnN0IHRvZ2dsZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJnZW1pbmlBcGlLZXlUb2dnbGUiKTsKICAgIGNvbnN0IGV5ZUljb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiZ2VtaW5pQXBpS2V5RXllIik7CiAgICBjb25zdCBleWVPZmZJY29uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImdlbWluaUFwaUtleUV5ZU9mZiIpOwogICAgY29uc3QgdmFsaWRhdGlvbk1lc3NhZ2UgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiZ2VtaW5pVmFsaWRhdGlvbk1lc3NhZ2UiKTsKCiAgICBpZiAoIWFwaUtleUlucHV0IHx8ICF2YWxpZGF0ZUJ0biB8fCAhdmFsaWRhdGlvbk1lc3NhZ2UpIHJldHVybjsKCiAgICAvLyBFeWUgdG9nZ2xlIGZ1bmN0aW9uYWxpdHkKICAgIGlmICh0b2dnbGVCdG4gJiYgZXllSWNvbiAmJiBleWVPZmZJY29uKSB7CiAgICAgICAgdG9nZ2xlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgKCkgPT4gewogICAgICAgICAgICBjb25zdCBpc1Bhc3N3b3JkID0gYXBpS2V5SW5wdXQudHlwZSA9PT0gInBhc3N3b3JkIjsKICAgICAgICAgICAgYXBpS2V5SW5wdXQudHlwZSA9IGlzUGFzc3dvcmQgPyAidGV4dCIgOiAicGFzc3dvcmQiOwogICAgICAgICAgICBleWVJY29uLmNsYXNzTGlzdC50b2dnbGUoImhpZGRlbiIsICFpc1Bhc3N3b3JkKTsKICAgICAgICAgICAgZXllT2ZmSWNvbi5jbGFzc0xpc3QudG9nZ2xlKCJoaWRkZW4iLCBpc1Bhc3N3b3JkKTsKICAgICAgICB9KTsKICAgIH0KCiAgICAvLyBWYWxpZGF0aW9uIGZ1bmN0aW9uCiAgICBhc3luYyBmdW5jdGlvbiB2YWxpZGF0ZUdlbWluaUtleSgpIHsKICAgICAgICBjb25zdCBhcGlLZXkgPSBhcGlLZXlJbnB1dC52YWx1ZS50cmltKCk7CgogICAgICAgIGlmICghYXBpS2V5KSB7CiAgICAgICAgICAgIHNob3dHZW1pbmlWYWxpZGF0aW9uTWVzc2FnZSgiUGxlYXNlIGVudGVyIGEgR2VtaW5pIEFQSSBrZXkiLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgLy8gU2hvdyBsb2FkaW5nIHN0YXRlCiAgICAgICAgdmFsaWRhdGVCdG4uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5hZGQoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgY29uc3Qgb3JpZ2luYWxIVE1MID0gdmFsaWRhdGVCdG4uaW5uZXJIVE1MOwogICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPSJ3LTUgaC01IGFuaW1hdGUtc3BpbiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjbGFzcz0ib3BhY2l0eS0yNSIgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT48cGF0aCBjbGFzcz0ib3BhY2l0eS03NSIgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD48L3N2Zz4nOwoKICAgICAgICB0cnkgewogICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKCIvZ2VtaW5pL3ZhbGlkYXRpb24iLCB7CiAgICAgICAgICAgICAgICBtZXRob2Q6ICJQT1NUIiwKICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgIkNvbnRlbnQtVHlwZSI6ICJhcHBsaWNhdGlvbi9qc29uIiB9LAogICAgICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBhcGlfa2V5OiBhcGlLZXkgfSkKICAgICAgICAgICAgfSk7CgogICAgICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpOwoKICAgICAgICAgICAgaWYgKGRhdGEudmFsaWQpIHsKICAgICAgICAgICAgICAgIHNob3dHZW1pbmlWYWxpZGF0aW9uTWVzc2FnZSgiR2VtaW5pIEFQSSBrZXkgaXMgdmFsaWQg4pyTIiwgInN1Y2Nlc3MiKTsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgc2hvd0dlbWluaVZhbGlkYXRpb25NZXNzYWdlKGRhdGEubWVzc2FnZSB8fCAiSW52YWxpZCBHZW1pbmkgQVBJIGtleSIsICJlcnJvciIpOwogICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgICAgICB9CiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgc2hvd0dlbWluaVZhbGlkYXRpb25NZXNzYWdlKCJWYWxpZGF0aW9uIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCAiZXJyb3IiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmRpc2FibGVkID0gZmFsc2U7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmNsYXNzTGlzdC5yZW1vdmUoIm9wYWNpdHktNTAiLCAiY3Vyc29yLW5vdC1hbGxvd2VkIik7CiAgICAgICAgICAgIHZhbGlkYXRlQnRuLmlubmVySFRNTCA9IG9yaWdpbmFsSFRNTDsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd0dlbWluaVZhbGlkYXRpb25NZXNzYWdlKG1lc3NhZ2UsIHR5cGUpIHsKICAgICAgICB2YWxpZGF0aW9uTWVzc2FnZS50ZXh0Q29udGVudCA9IG1lc3NhZ2U7CiAgICAgICAgdmFsaWRhdGlvbk1lc3NhZ2UuY2xhc3NOYW1lID0gYG10LTIgdGV4dC14cyAke3R5cGUgPT09ICJzdWNjZXNzIiA/ICJ0ZXh0LWdyZWVuLTQwMCIgOiAidGV4dC1yZWQtNDAwIn1gOwogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOwogICAgfQoKICAgIHZhbGlkYXRlQnRuLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgdmFsaWRhdGVHZW1pbmlLZXkpOwoKICAgIGFwaUtleUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoImlucHV0IiwgKCkgPT4gewogICAgICAgIHZhbGlkYXRpb25NZXNzYWdlLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOwogICAgfSk7Cn0KCi8vIFBhc3N3b3JkIFRvZ2dsZXMKZnVuY3Rpb24gaW5pdGlhbGl6ZVBhc3N3b3JkVG9nZ2xlcygpIHsKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy50b2dnbGUtYnRuJykuZm9yRWFjaChidG4gPT4gewogICAgICAgIGJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsKICAgICAgICAgICAgY29uc3QgdGFyZ2V0SWQgPSBidG4uZ2V0QXR0cmlidXRlKCdkYXRhLXRhcmdldCcpOwogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRhcmdldElkKTsKICAgICAgICAgICAgaWYgKCFpbnB1dCkgcmV0dXJuOwogICAgICAgICAgICBjb25zdCBpc0hpZGRlbiA9IGlucHV0LnR5cGUgPT09ICdwYXNzd29yZCc7CiAgICAgICAgICAgIGlucHV0LnR5cGUgPSBpc0hpZGRlbiA/ICd0ZXh0JyA6ICdwYXNzd29yZCc7CiAgICAgICAgICAgIC8vIFN3YXAgaWNvbiBhbmQgbGFiZWxzCiAgICAgICAgICAgIGlmIChpc0hpZGRlbikgewogICAgICAgICAgICAgICAgLy8gTm93IHZpc2libGU6IHNob3cgZXllLW9mZiBpY29uCiAgICAgICAgICAgICAgICBidG4uc2V0QXR0cmlidXRlKCd0aXRsZScsICdIaWRlJyk7CiAgICAgICAgICAgICAgICBidG4uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgJ0hpZGUgcGFzc3dvcmQnKTsKICAgICAgICAgICAgICAgIGJ0bi5pbm5lckhUTUwgPSAnPHN2ZyBjbGFzcz0idy00IGgtNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xNy45NCAxNy45NEExMC45NCAxMC45NCAwIDAgMSAxMiAyMGMtNyAwLTExLTgtMTEtOGEyMS43NyAyMS43NyAwIDAgMSA1LjA2LTYuMTdNOS45IDQuMjRBMTAuOTQgMTAuOTQgMCAwIDEgMTIgNGM3IDAgMTEgOCAxMSA4YTIxLjggMjEuOCAwIDAgMS0zLjIyIDQuMzEiLz48cGF0aCBkPSJNMSAxbDIyIDIyIi8+PHBhdGggZD0iTTE0LjEyIDE0LjEyQTMgMyAwIDAgMSA5Ljg4IDkuODgiLz48L3N2Zz4nOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgLy8gTm93IGhpZGRlbjogc2hvdyBleWUgaWNvbgogICAgICAgICAgICAgICAgYnRuLnNldEF0dHJpYnV0ZSgndGl0bGUnLCAnU2hvdycpOwogICAgICAgICAgICAgICAgYnRuLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsICdTaG93IHBhc3N3b3JkJyk7CiAgICAgICAgICAgICAgICBidG4uaW5uZXJIVE1MID0gJzxzdmcgY2xhc3M9InctNCBoLTQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMSAxMnM0LTcgMTEtNyAxMSA3IDExIDctNCA3LTExIDctMTEtNy0xMS03eiIvPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiLz48L3N2Zz4nOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9KTsKfQoKLy8gRGVsZXRlICYgU3VjY2VzcyBIZWxwZXJzCmZ1bmN0aW9uIGluaXRpYWxpemVTdWNjZXNzQWN0aW9ucygpIHsKICAgIGNvbnN0IGNvcHlCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29weUJ0bicpOwogICAgaWYgKGNvcHlCdG4pIHsKICAgICAgICBjb3B5QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpOwogICAgICAgICAgICBjb25zdCB1cmxUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZG9uVXJsJykudGV4dENvbnRlbnQ7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICBhd2FpdCBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dCh1cmxUZXh0KTsKICAgICAgICAgICAgICAgIGNvbnN0IG9yaWdpbmFsVGV4dCA9IGNvcHlCdG4uaW5uZXJIVE1MOwogICAgICAgICAgICAgICAgY29weUJ0bi5pbm5lckhUTUwgPSAnQ29waWVkISc7CiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHsgY29weUJ0bi5pbm5lckhUTUwgPSBvcmlnaW5hbFRleHQ7IH0sIDIwMDApOwogICAgICAgICAgICB9IGNhdGNoIChlcnIpIHsgfQogICAgICAgIH0pOwogICAgfQoKICAgIGNvbnN0IGluc3RhbGxEZXNrdG9wQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2luc3RhbGxEZXNrdG9wQnRuJyk7CiAgICBpZiAoaW5zdGFsbERlc2t0b3BCdG4pIHsKICAgICAgICBpbnN0YWxsRGVza3RvcEJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKSA9PiB7CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTsKICAgICAgICAgICAgY29uc3QgdXJsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZG9uVXJsJykudGV4dENvbnRlbnQ7CiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gYHN0cmVtaW86Ly8ke3VybC5yZXBsYWNlKC9eaHR0cHM/OlwvXC8vLCAnJyl9YDsKICAgICAgICB9KTsKICAgIH0KICAgIGNvbnN0IGluc3RhbGxXZWJCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW5zdGFsbFdlYkJ0bicpOwogICAgaWYgKGluc3RhbGxXZWJCdG4pIHsKICAgICAgICBpbnN0YWxsV2ViQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpOwogICAgICAgICAgICBjb25zdCB1cmwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYWRkb25VcmwnKS50ZXh0Q29udGVudDsKICAgICAgICAgICAgd2luZG93Lm9wZW4oYGh0dHBzOi8vd2ViLnN0cmVtaW8uY29tLyMvYWRkb25zP2FkZG9uPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHVybCl9YCwgJ19ibGFuaycpOwogICAgICAgIH0pOwogICAgfQoKICAgIGNvbnN0IGRlbGV0ZUFjY291bnRCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZGVsZXRlQWNjb3VudEJ0bicpOwogICAgaWYgKGRlbGV0ZUFjY291bnRCdG4pIHsKICAgICAgICBkZWxldGVBY2NvdW50QnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKCkgPT4gewogICAgICAgICAgICBjb25zdCBjb25maXJtZWQgPSBhd2FpdCBzaG93Q29uZmlybSgKICAgICAgICAgICAgICAgICdEZWxldGUgQWNjb3VudD8nLAogICAgICAgICAgICAgICAgJ0FyZSB5b3Ugc3VyZSB5b3Ugd2FudCB0byBkZWxldGUgeW91ciBzZXR0aW5ncz8gVGhpcyBhY3Rpb24gaXMgaXJyZXZlcnNpYmxlIGFuZCBhbGwgeW91ciBkYXRhIHdpbGwgYmUgcGVybWFuZW50bHkgcmVtb3ZlZC4nCiAgICAgICAgICAgICk7CgogICAgICAgICAgICBpZiAoIWNvbmZpcm1lZCkgcmV0dXJuOwoKICAgICAgICAgICAgY29uc3Qgc0F1dGhLZXkgPSAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImF1dGhLZXkiKS52YWx1ZSB8fCAnJykudHJpbSgpOwogICAgICAgICAgICBjb25zdCBlbWFpbCA9IGVtYWlsSW5wdXQ/LnZhbHVlLnRyaW0oKTsKICAgICAgICAgICAgY29uc3QgcGFzc3dvcmQgPSBwYXNzd29yZElucHV0Py52YWx1ZTsKCiAgICAgICAgICAgIGlmICghc0F1dGhLZXkgJiYgIShlbWFpbCAmJiBwYXNzd29yZCkpIHsKICAgICAgICAgICAgICAgIHNob3dFcnJvcignZ2VuZXJhbEVycm9yJywgIlByb3ZpZGUgU3RyZW1pbyBhdXRoIGtleSBvciBlbWFpbCAmIHBhc3N3b3JkIHRvIGRlbGV0ZSB5b3VyIGFjY291bnQuIik7CiAgICAgICAgICAgICAgICBzd2l0Y2hTZWN0aW9uKCdsb2dpbicpOwogICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICB9CgogICAgICAgICAgICBzZXRMb2FkaW5nKHRydWUpOwogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHsgYXV0aEtleTogc0F1dGhLZXkgfHwgdW5kZWZpbmVkLCBlbWFpbDogZW1haWwgfHwgdW5kZWZpbmVkLCBwYXNzd29yZDogcGFzc3dvcmQgfHwgdW5kZWZpbmVkIH07CiAgICAgICAgICAgICAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCgnL3Rva2Vucy8nLCB7IG1ldGhvZDogJ0RFTEVURScsIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LCBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9KTsKICAgICAgICAgICAgICAgIGlmICghcmVzLm9rKSB0aHJvdyBuZXcgRXJyb3IoKGF3YWl0IHJlcy5qc29uKCkpLmRldGFpbCB8fCAnRmFpbGVkIHRvIGRlbGV0ZScpOwogICAgICAgICAgICAgICAgc2hvd1RvYXN0KCdBY2NvdW50IGRlbGV0ZWQgc3VjY2Vzc2Z1bGx5LicsICdzdWNjZXNzJyk7CiAgICAgICAgICAgICAgICBpZiAocmVzZXRBcHApIHJlc2V0QXBwKCk7CiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgICAgIHNob3dFcnJvcignZ2VuZXJhbEVycm9yJywgZS5tZXNzYWdlKTsKICAgICAgICAgICAgfSBmaW5hbGx5IHsKICAgICAgICAgICAgICAgIHNldExvYWRpbmcoZmFsc2UpOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9Cn0KCmZ1bmN0aW9uIHNldExvYWRpbmcobG9hZGluZykgewogICAgaWYgKCFzdWJtaXRCdG4pIHJldHVybjsKICAgIGNvbnN0IGJ0blRleHQgPSBzdWJtaXRCdG4ucXVlcnlTZWxlY3RvcignLmJ0bi10ZXh0Jyk7CiAgICBjb25zdCBsb2FkZXIgPSBzdWJtaXRCdG4ucXVlcnlTZWxlY3RvcignLmxvYWRlcicpOwogICAgc3VibWl0QnRuLmRpc2FibGVkID0gbG9hZGluZzsKICAgIGlmIChsb2FkaW5nKSB7CiAgICAgICAgaWYgKGJ0blRleHQpIGJ0blRleHQuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICAgICAgaWYgKGxvYWRlcikgbG9hZGVyLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgfSBlbHNlIHsKICAgICAgICBpZiAoYnRuVGV4dCkgYnRuVGV4dC5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgICAgICBpZiAobG9hZGVyKSBsb2FkZXIuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICB9Cn0KCmZ1bmN0aW9uIHNob3dFcnJvcih0YXJnZXQsIG1lc3NhZ2UpIHsKICAgIGlmICh0YXJnZXQgPT09ICdnZW5lcmFsRXJyb3InKSB7CiAgICAgICAgY29uc3QgZXJyRWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZXJyb3JNZXNzYWdlJyk7CiAgICAgICAgaWYgKGVyckVsKSB7CiAgICAgICAgICAgIGVyckVsLnF1ZXJ5U2VsZWN0b3IoJy5tZXNzYWdlLWNvbnRlbnQnKS50ZXh0Q29udGVudCA9IG1lc3NhZ2U7CiAgICAgICAgICAgIGVyckVsLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgICAgIH0gZWxzZSB7IHNob3dUb2FzdChtZXNzYWdlLCAnZXJyb3InKTsgfQogICAgfSBlbHNlIGlmICh0YXJnZXQgPT09ICdzdHJlbWlvQXV0aFNlY3Rpb24nKSB7CiAgICAgICAgc2hvd1RvYXN0KG1lc3NhZ2UsICdlcnJvcicpOwogICAgfSBlbHNlIHsKICAgICAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRhcmdldCk7CiAgICAgICAgaWYgKGVsKSB7CiAgICAgICAgICAgIGVsLmNsYXNzTGlzdC5hZGQoJ2JvcmRlci1yZWQtNTAwJyk7CiAgICAgICAgICAgIGVsLmZvY3VzKCk7CiAgICAgICAgfQogICAgfQp9CgpleHBvcnQgZnVuY3Rpb24gY2xlYXJFcnJvcnMoKSB7CiAgICBjb25zdCBlcnJFbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlcnJvck1lc3NhZ2UnKTsKICAgIGlmIChlcnJFbCkgZXJyRWwuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuYm9yZGVyLXJlZC01MDAnKS5mb3JFYWNoKGUgPT4gZS5jbGFzc0xpc3QucmVtb3ZlKCdib3JkZXItcmVkLTUwMCcpKTsKfQoKZnVuY3Rpb24gc2hvd1N1Y2Nlc3ModXJsKSB7CiAgICAvLyBIaWRlIGZvcm0gZW50aXJlbHkgYnkgaGlkaW5nIHRoZSBhY3RpdmUgc2VjdGlvbgogICAgY29uc3Qgc2VjdGlvbnMgPSB7CiAgICAgICAgd2VsY29tZTogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3Qtd2VsY29tZScpLAogICAgICAgIGxvZ2luOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2VjdC1sb2dpbicpLAogICAgICAgIGNvbmZpZzogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3QtY29uZmlnJyksCiAgICAgICAgY2F0YWxvZ3M6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWN0LWNhdGFsb2dzJyksCiAgICAgICAgaW5zdGFsbDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NlY3QtaW5zdGFsbCcpLAogICAgICAgIHN1Y2Nlc3M6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWN0LXN1Y2Nlc3MnKQogICAgfTsKICAgIE9iamVjdC52YWx1ZXMoc2VjdGlvbnMpLmZvckVhY2gocyA9PiB7IGlmIChzKSBzLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpIH0pOwoKICAgIC8vIFNob3cgU3VjY2VzcyBTZWN0aW9uCiAgICBpZiAoc2VjdGlvbnMuc3VjY2VzcykgewogICAgICAgIHNlY3Rpb25zLnN1Y2Nlc3MuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2FkZG9uVXJsJykudGV4dENvbnRlbnQgPSB1cmw7CiAgICB9Cn0KCi8vIFllYXIgU2xpZGVyIExvZ2ljCmZ1bmN0aW9uIGluaXRpYWxpemVZZWFyU2xpZGVyKCkgewogICAgY29uc3QgeWVhck1pbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd5ZWFyTWluJyk7CiAgICBjb25zdCB5ZWFyTWF4ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3llYXJNYXgnKTsKICAgIGNvbnN0IHllYXJNaW5MYWJlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd5ZWFyTWluTGFiZWwnKTsKICAgIGNvbnN0IHllYXJNYXhMYWJlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd5ZWFyTWF4TGFiZWwnKTsKICAgIGNvbnN0IHRyYWNrID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3llYXJTbGlkZXJUcmFjaycpOwoKICAgIGlmICgheWVhck1pbiB8fCAheWVhck1heCB8fCAheWVhck1pbkxhYmVsIHx8ICF5ZWFyTWF4TGFiZWwgfHwgIXRyYWNrKSByZXR1cm47CgogICAgZnVuY3Rpb24gdXBkYXRlU2xpZGVyKCkgewogICAgICAgIGNvbnN0IG1pblZhbCA9IHBhcnNlSW50KHllYXJNaW4udmFsdWUpOwogICAgICAgIGNvbnN0IG1heFZhbCA9IHBhcnNlSW50KHllYXJNYXgudmFsdWUpOwoKICAgICAgICBpZiAobWluVmFsID4gbWF4VmFsKSB7CiAgICAgICAgICAgIC8vIFByZXZlbnQgY3Jvc3Npbmc6IGlmIG1pbiA+IG1heCwgc25hcCB0aGVtCiAgICAgICAgICAgIC8vIFRoaXMgaXMgaGFuZGxlZCBieSBpbnB1dCBsaXN0ZW5lcnMgdG8gYXZvaWQgamVya3kgbW92ZW1lbnQKICAgICAgICB9CgogICAgICAgIHllYXJNaW5MYWJlbC50ZXh0Q29udGVudCA9IG1pblZhbDsKICAgICAgICB5ZWFyTWF4TGFiZWwudGV4dENvbnRlbnQgPSBtYXhWYWw7CgogICAgICAgIGNvbnN0IHJhbmdlID0geWVhck1pbi5tYXggLSB5ZWFyTWluLm1pbjsKICAgICAgICBjb25zdCBsZWZ0ID0gKChtaW5WYWwgLSB5ZWFyTWluLm1pbikgLyByYW5nZSkgKiAxMDA7CiAgICAgICAgY29uc3QgcmlnaHQgPSAoKHllYXJNaW4ubWF4IC0gbWF4VmFsKSAvIHJhbmdlKSAqIDEwMDsKCiAgICAgICAgdHJhY2suc3R5bGUubGVmdCA9IGxlZnQgKyAnJSc7CiAgICAgICAgdHJhY2suc3R5bGUucmlnaHQgPSByaWdodCArICclJzsKICAgIH0KCiAgICB5ZWFyTWluLmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgKCkgPT4gewogICAgICAgIGlmIChwYXJzZUludCh5ZWFyTWluLnZhbHVlKSA+IHBhcnNlSW50KHllYXJNYXgudmFsdWUpKSB7CiAgICAgICAgICAgIHllYXJNaW4udmFsdWUgPSB5ZWFyTWF4LnZhbHVlOwogICAgICAgIH0KICAgICAgICB5ZWFyTWluLmNsYXNzTGlzdC5hZGQoJ3llYXItc2xpZGVyLWFjdGl2ZScpOwogICAgICAgIHllYXJNYXguY2xhc3NMaXN0LnJlbW92ZSgneWVhci1zbGlkZXItYWN0aXZlJyk7CiAgICAgICAgdXBkYXRlU2xpZGVyKCk7CiAgICB9KTsKCiAgICB5ZWFyTWF4LmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgKCkgPT4gewogICAgICAgIGlmIChwYXJzZUludCh5ZWFyTWF4LnZhbHVlKSA8IHBhcnNlSW50KHllYXJNaW4udmFsdWUpKSB7CiAgICAgICAgICAgIHllYXJNYXgudmFsdWUgPSB5ZWFyTWluLnZhbHVlOwogICAgICAgIH0KICAgICAgICB5ZWFyTWF4LmNsYXNzTGlzdC5hZGQoJ3llYXItc2xpZGVyLWFjdGl2ZScpOwogICAgICAgIHllYXJNaW4uY2xhc3NMaXN0LnJlbW92ZSgneWVhci1zbGlkZXItYWN0aXZlJyk7CiAgICAgICAgdXBkYXRlU2xpZGVyKCk7CiAgICB9KTsKCiAgICAvLyBJbml0aWFsIHVwZGF0ZQogICAgdXBkYXRlU2xpZGVyKCk7CgogICAgLy8gRXhwb3J0IHVwZGF0ZSBmdW5jdGlvbiBmb3IgZXh0ZXJuYWwgcG9wdWxhdGlvbgogICAgd2luZG93LnVwZGF0ZVllYXJTbGlkZXIgPSB1cGRhdGVTbGlkZXI7Cn0K" | base64 --decode > "$REPO/app/static/js/modules/form.js" +echo " wrote app/static/js/modules/form.js" + +mkdir -p "$REPO/app/services" +echo "ZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBmYXN0YXBpIGltcG9ydCBIVFRQRXhjZXB0aW9uCmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKCmZyb20gYXBwLmNvcmUuY29uZmlnIGltcG9ydCBzZXR0aW5ncwpmcm9tIGFwcC5jb3JlLnNlY3VyaXR5IGltcG9ydCByZWRhY3RfdG9rZW4KZnJvbSBhcHAuY29yZS5zZXR0aW5ncyBpbXBvcnQgVXNlclNldHRpbmdzLCByZXNvbHZlX3RtZGJfYXBpX2tleQpmcm9tIGFwcC5jb3JlLnZlcnNpb24gaW1wb3J0IF9fdmVyc2lvbl9fCmZyb20gYXBwLnNlcnZpY2VzLmNhdGFsb2cgaW1wb3J0IER5bmFtaWNDYXRhbG9nU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy5wcm9maWxlLmludGVncmF0aW9uIGltcG9ydCBQcm9maWxlSW50ZWdyYXRpb24KZnJvbSBhcHAuc2VydmljZXMuc3RyZW1pby5zZXJ2aWNlIGltcG9ydCBTdHJlbWlvQnVuZGxlCmZyb20gYXBwLnNlcnZpY2VzLnRva2VuX3N0b3JlIGltcG9ydCB0b2tlbl9zdG9yZQpmcm9tIGFwcC5zZXJ2aWNlcy50cmFuc2xhdGlvbiBpbXBvcnQgYXBwbHlfY2F0YWxvZ190cmFuc2xhdGlvbgpmcm9tIGFwcC5zZXJ2aWNlcy51c2VyX2NhY2hlIGltcG9ydCB1c2VyX2NhY2hlCmZyb20gYXBwLnV0aWxzLmNhdGFsb2cgaW1wb3J0IGNhY2hlX3Byb2ZpbGVfYW5kX3dhdGNoZWRfc2V0cywgc29ydF9jYXRhbG9ncwoKCmNsYXNzIE1hbmlmZXN0U2VydmljZToKICAgICIiIlNlcnZpY2UgZm9yIGdlbmVyYXRpbmcgU3RyZW1pbyBtYW5pZmVzdCBmaWxlcy4iIiIKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgZ2V0X2Jhc2VfbWFuaWZlc3QoKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICAiIiJHZXQgdGhlIGJhc2UgbWFuaWZlc3Qgc3RydWN0dXJlLiIiIgogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJpZCI6IHNldHRpbmdzLkFERE9OX0lELAogICAgICAgICAgICAidmVyc2lvbiI6IF9fdmVyc2lvbl9fLAogICAgICAgICAgICAibmFtZSI6IHNldHRpbmdzLkFERE9OX05BTUUsCiAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJNb3ZpZSBhbmQgc2VyaWVzIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiB5b3VyIFN0cmVtaW8gbGlicmFyeS4iLAogICAgICAgICAgICAibG9nbyI6ICgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1RpbWlsc2luYUJpbWFsL1dhdGNobHkvcmVmcy9oZWFkcy9tYWluL2FwcC9zdGF0aWMvbG9nby5wbmciKSwKICAgICAgICAgICAgImJhY2tncm91bmQiOiAoCiAgICAgICAgICAgICAgICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1RpbWlsc2luYUJpbWFsL1dhdGNobHkvcmVmcy9oZWFkcy9tYWluL2FwcC9zdGF0aWMvY292ZXIucG5nIgogICAgICAgICAgICApLAogICAgICAgICAgICAicmVzb3VyY2VzIjogWyJjYXRhbG9nIl0sCiAgICAgICAgICAgICJ0eXBlcyI6IFsibW92aWUiLCAic2VyaWVzIl0sCiAgICAgICAgICAgICJpZFByZWZpeGVzIjogWyJ0dCJdLAogICAgICAgICAgICAiY2F0YWxvZ3MiOiBbXSwKICAgICAgICAgICAgImJlaGF2aW9ySGludHMiOiB7ImNvbmZpZ3VyYWJsZSI6IFRydWUsICJjb25maWd1cmF0aW9uUmVxdWlyZWQiOiBGYWxzZX0sCiAgICAgICAgICAgICJzdHJlbWlvQWRkb25zQ29uZmlnIjogewogICAgICAgICAgICAgICAgImlzc3VlciI6ICJodHRwczovL3N0cmVtaW8tYWRkb25zLm5ldCIsCiAgICAgICAgICAgICAgICAic2lnbmF0dXJlIjogKAogICAgICAgICAgICAgICAgICAgICJleUpoYkdjaU9pSmthWElpTENKbGJtTWlPaUpCTVRJNFEwSkRMVWhUTWpVMkluMC4uV1NyaHp6bGoxVHVEeWNENlFvVkx1QS5Eem14enI0eTgzdXFRRjE1cjR0QzFiQjktdnRaUmgxUnZ5NEJxZ0RZeHU5MWMyZXNpSnVvdjlLbm5JX2Nib1FDZ1pTN2hqd25JcVJTbFEtakV5R3dYSEhSZXJoOVFrbHlmZHhwWHFOVXlCZ1RXRnpET1ZkVnZEWUplTV90R01tUi5zZXpBQ2hsV0dWN2xOUy10OUhXQjZBIiAgIyBub3FhCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICB9LAogICAgICAgIH0KCiAgICBhc3luYyBkZWYgX3Jlc29sdmVfYXV0aF9rZXkoc2VsZiwgYnVuZGxlOiBTdHJlbWlvQnVuZGxlLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0sIHRva2VuOiBzdHIpIC0+IHN0ciB8IE5vbmU6CiAgICAgICAgIiIiUmVzb2x2ZSBhbmQgdmFsaWRhdGUgYXV0aCBrZXksIHJlZnJlc2hpbmcgaWYgbmVlZGVkLiIiIgogICAgICAgIGF1dGhfa2V5ID0gY3JlZGVudGlhbHMuZ2V0KCJhdXRoS2V5IikKICAgICAgICBlbWFpbCA9IGNyZWRlbnRpYWxzLmdldCgiZW1haWwiKQogICAgICAgIHBhc3N3b3JkID0gY3JlZGVudGlhbHMuZ2V0KCJwYXNzd29yZCIpCgogICAgICAgIGlzX3ZhbGlkID0gRmFsc2UKICAgICAgICBpZiBhdXRoX2tleToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgYXdhaXQgYnVuZGxlLmF1dGguZ2V0X3VzZXJfaW5mbyhhdXRoX2tleSkKICAgICAgICAgICAgICAgIGlzX3ZhbGlkID0gVHJ1ZQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJBdXRoIGtleSBjaGVjayBmYWlsZWQgZm9yIHtlbWFpbCBvciAndW5rbm93bid9OiB7ZX0iKQoKICAgICAgICBpZiBub3QgaXNfdmFsaWQgYW5kIGVtYWlsIGFuZCBwYXNzd29yZDoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgYXV0aF9rZXkgPSBhd2FpdCBidW5kbGUuYXV0aC5sb2dpbihlbWFpbCwgcGFzc3dvcmQpCiAgICAgICAgICAgICAgICAjIFVwZGF0ZSBzdG9yZQogICAgICAgICAgICAgICAgY3JlZGVudGlhbHNbImF1dGhLZXkiXSA9IGF1dGhfa2V5CiAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiRmFpbGVkIHRvIHJlZnJlc2ggYXV0aCBrZXkgZHVyaW5nIG1hbmlmZXN0IGZldGNoOiB7ZX0iKQogICAgICAgICAgICAgICAgcmV0dXJuIE5vbmUKCiAgICAgICAgcmV0dXJuIGF1dGhfa2V5CgogICAgYXN5bmMgZGVmIGNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzKAogICAgICAgIHNlbGYsIGJ1bmRsZTogU3RyZW1pb0J1bmRsZSwgYXV0aF9rZXk6IHN0ciwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzLCB0b2tlbjogc3RyCiAgICApIC0+IGRpY3Rbc3RyLCBBbnldOgogICAgICAgICIiIgogICAgICAgIEZldGNoIGFuZCBjYWNoZSBsaWJyYXJ5IGl0ZW1zIGFuZCBwcm9maWxlcyBmb3IgYSB1c2VyLgoKICAgICAgICBUaGlzIHNob3VsZCBiZSBjYWxsZWQgZHVyaW5nIHRva2VuIGNyZWF0aW9uIHRvIHByZS1jYWNoZSBkYXRhCiAgICAgICAgc28gbWFuaWZlc3QgZ2VuZXJhdGlvbiBpcyBmYXN0LgoKICAgICAgICBBcmdzOgogICAgICAgICAgICBidW5kbGU6IFN0cmVtaW9CdW5kbGUgaW5zdGFuY2UKICAgICAgICAgICAgYXV0aF9rZXk6IFN0cmVtaW8gYXV0aCBrZXkKICAgICAgICAgICAgdXNlcl9zZXR0aW5nczogVXNlciBzZXR0aW5ncwogICAgICAgICAgICB0b2tlbjogVXNlciB0b2tlbgoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBMaWJyYXJ5IGl0ZW1zIGRpY3Rpb25hcnkKICAgICAgICAiIiIKICAgICAgICAjIEZldGNoIGxpYnJhcnkgaXRlbXMKICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZldGNoaW5nIGxpYnJhcnkgaXRlbXMgZm9yIGNhY2hpbmciKQogICAgICAgIGxpYnJhcnlfaXRlbXMgPSBhd2FpdCBidW5kbGUubGlicmFyeS5nZXRfbGlicmFyeV9pdGVtcyhhdXRoX2tleSkKCiAgICAgICAgIyBDYWNoZSBsaWJyYXJ5IGl0ZW1zIHVzaW5nIGNlbnRyYWxpemVkIGNhY2hlIHNlcnZpY2UKICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQogICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBsaWJyYXJ5IGl0ZW1zIikKCiAgICAgICAgIyBCdWlsZCBhbmQgY2FjaGUgcHJvZmlsZXMgZm9yIGJvdGggbW92aWUgYW5kIHNlcmllcwogICAgICAgIGxhbmd1YWdlID0gdXNlcl9zZXR0aW5ncy5sYW5ndWFnZQogICAgICAgIHRtZGJfa2V5ID0gcmVzb2x2ZV90bWRiX2FwaV9rZXkodXNlcl9zZXR0aW5ncykKICAgICAgICBpbnRlZ3JhdGlvbl9zZXJ2aWNlID0gUHJvZmlsZUludGVncmF0aW9uKGxhbmd1YWdlPWxhbmd1YWdlLCB0bWRiX2FwaV9rZXk9dG1kYl9rZXkpCgogICAgICAgIGZvciBjb250ZW50X3R5cGUgaW4gWyJtb3ZpZSIsICJzZXJpZXMiXToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBCdWlsZGluZyBhbmQgY2FjaGluZyBwcm9maWxlIGZvciB7Y29udGVudF90eXBlfSIpCiAgICAgICAgICAgICAgICBfLCBfLCBfID0gYXdhaXQgY2FjaGVfcHJvZmlsZV9hbmRfd2F0Y2hlZF9zZXRzKAogICAgICAgICAgICAgICAgICAgIHRva2VuLCBjb250ZW50X3R5cGUsIGludGVncmF0aW9uX3NlcnZpY2UsIGxpYnJhcnlfaXRlbXMsIGJ1bmRsZSwgYXV0aF9rZXkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBwcm9maWxlIGFuZCB3YXRjaGVkIHNldHMgZm9yIHtjb250ZW50X3R5cGV9IikKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gYnVpbGQvY2FjaGUgcHJvZmlsZSBmb3Ige2NvbnRlbnRfdHlwZX06IHtlfSIpCgogICAgICAgIHJldHVybiBsaWJyYXJ5X2l0ZW1zCgogICAgYXN5bmMgZGVmIGNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzX2Zyb21faXRlbXMoCiAgICAgICAgc2VsZiwgbGlicmFyeV9pdGVtczogZGljdCwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzLCB0b2tlbjogc3RyCiAgICApIC0+IE5vbmU6CiAgICAgICAgIiIiCiAgICAgICAgQ2FjaGUgbGlicmFyeSBpdGVtcyBhbmQgYnVpbGQgcHJvZmlsZXMgZnJvbSBhbiBhbHJlYWR5LWZldGNoZWQgbGlicmFyeSBkaWN0LgogICAgICAgIFVzZWQgYnkgVHJha3QgKGFuZCBhbnkgZnV0dXJlIG5vbi1TdHJlbWlvIHByb3ZpZGVyKSB3aGVyZSBsaWJyYXJ5IGRhdGEgaXMKICAgICAgICBvYnRhaW5lZCBvdXRzaWRlIG9mIHRoZSBTdHJlbWlvIGJ1bmRsZS4KICAgICAgICAiIiIKICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQogICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBsaWJyYXJ5IGl0ZW1zIChwcm92aWRlci1hZ25vc3RpYykiKQoKICAgICAgICBsYW5ndWFnZSA9IHVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UKICAgICAgICB0bWRiX2tleSA9IHJlc29sdmVfdG1kYl9hcGlfa2V5KHVzZXJfc2V0dGluZ3MpCiAgICAgICAgaW50ZWdyYXRpb25fc2VydmljZSA9IFByb2ZpbGVJbnRlZ3JhdGlvbihsYW5ndWFnZT1sYW5ndWFnZSwgdG1kYl9hcGlfa2V5PXRtZGJfa2V5KQoKICAgICAgICBmb3IgY29udGVudF90eXBlIGluIFsibW92aWUiLCAic2VyaWVzIl06CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHByb2ZpbGUsIHdhdGNoZWRfdG1kYiwgd2F0Y2hlZF9pbWRiID0gYXdhaXQgaW50ZWdyYXRpb25fc2VydmljZS5idWlsZF9wcm9maWxlX2Zyb21fbGlicmFyeSgKICAgICAgICAgICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zLCBjb250ZW50X3R5cGUKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGF3YWl0IHVzZXJfY2FjaGUuc2V0X3Byb2ZpbGVfYW5kX3dhdGNoZWRfc2V0cyh0b2tlbiwgY29udGVudF90eXBlLCBwcm9maWxlLCB3YXRjaGVkX3RtZGIsIHdhdGNoZWRfaW1kYikKICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIENhY2hlZCBwcm9maWxlIGZvciB7Y29udGVudF90eXBlfSIpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gRmFpbGVkIHRvIGNhY2hlIHByb2ZpbGUgZm9yIHtjb250ZW50X3R5cGV9OiB7ZX0iKQoKCiAgICBhc3luYyBkZWYgX2Vuc3VyZV9saWJyYXJ5X2FuZF9wcm9maWxlc19jYWNoZWQoCiAgICAgICAgc2VsZiwgYnVuZGxlOiBTdHJlbWlvQnVuZGxlLCBhdXRoX2tleTogc3RyLCB1c2VyX3NldHRpbmdzOiBVc2VyU2V0dGluZ3MsIHRva2VuOiBzdHIKICAgICkgLT4gZGljdFtzdHIsIEFueV06CiAgICAgICAgIiIiRW5zdXJlIGxpYnJhcnkgaXRlbXMgYW5kIHByb2ZpbGVzIGFyZSBjYWNoZWQsIGZldGNoaW5nIGFuZCBidWlsZGluZyBpZiBuZWVkZWQuIiIiCiAgICAgICAgIyBUcnkgdG8gZ2V0IGNhY2hlZCBsaWJyYXJ5IGl0ZW1zIGZpcnN0CiAgICAgICAgbGlicmFyeV9pdGVtcyA9IGF3YWl0IHVzZXJfY2FjaGUuZ2V0X2xpYnJhcnlfaXRlbXModG9rZW4pCgogICAgICAgIGlmIGxpYnJhcnlfaXRlbXM6CiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIFVzaW5nIGNhY2hlZCBsaWJyYXJ5IGl0ZW1zIGZvciBtYW5pZmVzdCIpCiAgICAgICAgICAgIHJldHVybiBsaWJyYXJ5X2l0ZW1zCgogICAgICAgICMgSWYgbm90IGNhY2hlZCwgZmV0Y2ggYW5kIGNhY2hlCiAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBMaWJyYXJ5IGl0ZW1zIG5vdCBjYWNoZWQsIGZldGNoaW5nIGZyb20gU3RyZW1pbyBmb3IgbWFuaWZlc3QiKQogICAgICAgIHJldHVybiBhd2FpdCBzZWxmLmNhY2hlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzKGJ1bmRsZSwgYXV0aF9rZXksIHVzZXJfc2V0dGluZ3MsIHRva2VuKQoKICAgIGFzeW5jIGRlZiBfYnVpbGRfZHluYW1pY19jYXRhbG9ncygKICAgICAgICBzZWxmLCBidW5kbGU6IFN0cmVtaW9CdW5kbGUsIGF1dGhfa2V5OiBzdHIsIHVzZXJfc2V0dGluZ3M6IFVzZXJTZXR0aW5ncyB8IE5vbmUsIHRva2VuOiBzdHIKICAgICkgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiQnVpbGQgZHluYW1pYyBjYXRhbG9ncyBmb3IgdGhlIG1hbmlmZXN0LiIiIgogICAgICAgICMgY2hlY2sgaWYgY2FjaGVkLCBpZiBub3QsIGZldGNoIGFuZCBjYWNoZQogICAgICAgIGxpYnJhcnlfaXRlbXMgPSBhd2FpdCB1c2VyX2NhY2hlLmdldF9saWJyYXJ5X2l0ZW1zKHRva2VuKQogICAgICAgIGlmIG5vdCBsaWJyYXJ5X2l0ZW1zOgogICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgc2VsZi5fZW5zdXJlX2xpYnJhcnlfYW5kX3Byb2ZpbGVzX2NhY2hlZChidW5kbGUsIGF1dGhfa2V5LCB1c2VyX3NldHRpbmdzLCB0b2tlbikKICAgICAgICAgICAgYXdhaXQgdXNlcl9jYWNoZS5zZXRfbGlicmFyeV9pdGVtcyh0b2tlbiwgbGlicmFyeV9pdGVtcykKCiAgICAgICAgdG1kYl9rZXkgPSByZXNvbHZlX3RtZGJfYXBpX2tleSh1c2VyX3NldHRpbmdzKQogICAgICAgIGR5bmFtaWNfY2F0YWxvZ19zZXJ2aWNlID0gRHluYW1pY0NhdGFsb2dTZXJ2aWNlKGxhbmd1YWdlPXVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UsIHRtZGJfYXBpX2tleT10bWRiX2tleSkKICAgICAgICByZXR1cm4gYXdhaXQgZHluYW1pY19jYXRhbG9nX3NlcnZpY2UuZ2V0X2R5bmFtaWNfY2F0YWxvZ3MobGlicmFyeV9pdGVtcywgdXNlcl9zZXR0aW5ncywgdG9rZW49dG9rZW4pCgogICAgYXN5bmMgZGVmIF90cmFuc2xhdGVfY2F0YWxvZ3Moc2VsZiwgY2F0YWxvZ3M6IGxpc3RbZGljdFtzdHIsIEFueV1dLCBsYW5ndWFnZTogc3RyIHwgTm9uZSkgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiVHJhbnNsYXRlIGNhdGFsb2cgbmFtZXMgdG8gdGFyZ2V0IGxhbmd1YWdlLiIiIgogICAgICAgIGlmIG5vdCBsYW5ndWFnZToKICAgICAgICAgICAgcmV0dXJuIGNhdGFsb2dzCgogICAgICAgIHRyYW5zbGF0ZWRfY2F0YWxvZ3MgPSBbXQogICAgICAgIGZvciBjYXQgaW4gY2F0YWxvZ3M6CiAgICAgICAgICAgIGF3YWl0IGFwcGx5X2NhdGFsb2dfdHJhbnNsYXRpb24oY2F0LCBsYW5ndWFnZSkKICAgICAgICAgICAgdHJhbnNsYXRlZF9jYXRhbG9ncy5hcHBlbmQoY2F0KQoKICAgICAgICByZXR1cm4gdHJhbnNsYXRlZF9jYXRhbG9ncwoKICAgIGRlZiBfc29ydF9jYXRhbG9ncygKICAgICAgICBzZWxmLCBjYXRhbG9nczogbGlzdFtkaWN0W3N0ciwgQW55XV0sIHVzZXJfc2V0dGluZ3M6IFVzZXJTZXR0aW5ncyB8IE5vbmUKICAgICkgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiU29ydCBjYXRhbG9ncyBhY2NvcmRpbmcgdG8gdXNlciBzZXR0aW5ncyBvcmRlci4iIiIKICAgICAgICBpZiBub3QgdXNlcl9zZXR0aW5nczoKICAgICAgICAgICAgcmV0dXJuIGNhdGFsb2dzCgogICAgICAgIHJldHVybiBzb3J0X2NhdGFsb2dzKGNhdGFsb2dzLCB1c2VyX3NldHRpbmdzKQoKICAgIGFzeW5jIGRlZiBfYnVpbGRfZHluYW1pY19jYXRhbG9nc190cmFrdCgKICAgICAgICBzZWxmLCBjcmVkczogZGljdCwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzIHwgTm9uZSwgdG9rZW46IHN0cgogICAgKSAtPiBsaXN0W2RpY3Rbc3RyLCBBbnldXToKICAgICAgICAiIiJCdWlsZCBkeW5hbWljIGNhdGFsb2dzIGZvciBhIFRyYWt0LWJhY2tlZCBhY2NvdW50LiIiIgogICAgICAgIGZyb20gYXBwLmNvcmUuY29uZmlnIGltcG9ydCBzZXR0aW5ncyBhcyBhcHBfc2V0dGluZ3MKICAgICAgICBmcm9tIGFwcC5zZXJ2aWNlcy50cmFrdC5zZXJ2aWNlIGltcG9ydCBUcmFrdEJ1bmRsZQoKICAgICAgICAjIFVzZSBjYWNoZWQgbGlicmFyeSBpZiBhdmFpbGFibGUKICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgdXNlcl9jYWNoZS5nZXRfbGlicmFyeV9pdGVtcyh0b2tlbikKICAgICAgICBpZiBub3QgbGlicmFyeV9pdGVtczoKICAgICAgICAgICAgYWNjZXNzX3Rva2VuID0gY3JlZHMuZ2V0KCJhdXRoS2V5IikgICMgc3RvcmVkIGFzIGF1dGhLZXkgYWZ0ZXIgZW5jcnlwdGlvbi9kZWNyeXB0aW9uCiAgICAgICAgICAgIGlmIG5vdCBhY2Nlc3NfdG9rZW4gb3Igbm90IGFwcF9zZXR0aW5ncy5UUkFLVF9DTElFTlRfSUQgb3Igbm90IGFwcF9zZXR0aW5ncy5UUkFLVF9DTElFTlRfU0VDUkVUOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBUcmFrdCBjcmVkZW50aWFscyBtaXNzaW5nLCBjYW5ub3QgZmV0Y2ggbGlicmFyeSIpCiAgICAgICAgICAgICAgICByZXR1cm4gW10KICAgICAgICAgICAgcmVkaXJlY3RfdXJpID0gZiJ7YXBwX3NldHRpbmdzLkhPU1RfTkFNRX0vdG9rZW5zL3RyYWt0L2NhbGxiYWNrIgogICAgICAgICAgICB0cmFrdF9idW5kbGUgPSBUcmFrdEJ1bmRsZSgKICAgICAgICAgICAgICAgIGNsaWVudF9pZD1hcHBfc2V0dGluZ3MuVFJBS1RfQ0xJRU5UX0lELAogICAgICAgICAgICAgICAgY2xpZW50X3NlY3JldD1hcHBfc2V0dGluZ3MuVFJBS1RfQ0xJRU5UX1NFQ1JFVCwKICAgICAgICAgICAgICAgIHJlZGlyZWN0X3VyaT1yZWRpcmVjdF91cmksCiAgICAgICAgICAgICAgICBhY2Nlc3NfdG9rZW49YWNjZXNzX3Rva2VuLAogICAgICAgICAgICApCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGxpYnJhcnlfaXRlbXMgPSBhd2FpdCB0cmFrdF9idW5kbGUubGlicmFyeS5nZXRfbGlicmFyeV9pdGVtcygpCiAgICAgICAgICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQogICAgICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICAgICAgYXdhaXQgdHJha3RfYnVuZGxlLmNsb3NlKCkKCiAgICAgICAgaWYgbm90IGxpYnJhcnlfaXRlbXM6CiAgICAgICAgICAgIHJldHVybiBbXQoKICAgICAgICB0bWRiX2tleSA9IHJlc29sdmVfdG1kYl9hcGlfa2V5KHVzZXJfc2V0dGluZ3MpCiAgICAgICAgZHluYW1pY19jYXRhbG9nX3NlcnZpY2UgPSBEeW5hbWljQ2F0YWxvZ1NlcnZpY2UoCiAgICAgICAgICAgIGxhbmd1YWdlPXVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UgaWYgdXNlcl9zZXR0aW5ncyBlbHNlICJlbi1VUyIsCiAgICAgICAgICAgIHRtZGJfYXBpX2tleT10bWRiX2tleSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIGF3YWl0IGR5bmFtaWNfY2F0YWxvZ19zZXJ2aWNlLmdldF9keW5hbWljX2NhdGFsb2dzKGxpYnJhcnlfaXRlbXMsIHVzZXJfc2V0dGluZ3MsIHRva2VuPXRva2VuKQoKICAgIGFzeW5jIGRlZiBnZXRfbWFuaWZlc3RfZm9yX3Rva2VuKHNlbGYsIHRva2VuOiBzdHIpIC0+IGRpY3Rbc3RyLCBBbnldOgogICAgICAgICIiIgogICAgICAgIEdlbmVyYXRlIG1hbmlmZXN0IGZvciBhIGdpdmVuIHRva2VuLgoKICAgICAgICBBcmdzOgogICAgICAgICAgICB0b2tlbjogVXNlciB0b2tlbgoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBDb21wbGV0ZSBtYW5pZmVzdCBkaWN0aW9uYXJ5CgogICAgICAgIFJhaXNlczoKICAgICAgICAgICAgSFRUUEV4Y2VwdGlvbjogSWYgdG9rZW4gaXMgaW52YWxpZCBvciBjcmVkZW50aWFscyBhcmUgbWlzc2luZwogICAgICAgICIiIgogICAgICAgIGlmIG5vdCB0b2tlbjoKICAgICAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDEsIGRldGFpbD0iTWlzc2luZyB0b2tlbi4gUGxlYXNlIHJlY29uZmlndXJlIHRoZSBhZGRvbi4iKQoKICAgICAgICAjIExvYWQgdXNlciBjcmVkZW50aWFscyBhbmQgc2V0dGluZ3MKICAgICAgICBjcmVkcyA9IGF3YWl0IHRva2VuX3N0b3JlLmdldF91c2VyX2RhdGEodG9rZW4pCiAgICAgICAgaWYgbm90IGNyZWRzOgogICAgICAgICAgICByYWlzZSBIVFRQRXhjZXB0aW9uKHN0YXR1c19jb2RlPTQwMSwgZGV0YWlsPSJUb2tlbiBub3QgZm91bmQuIFBsZWFzZSByZWNvbmZpZ3VyZSB0aGUgYWRkb24uIikKCiAgICAgICAgdXNlcl9zZXR0aW5ncyA9IE5vbmUKICAgICAgICB0cnk6CiAgICAgICAgICAgIGlmIGNyZWRzLmdldCgic2V0dGluZ3MiKToKICAgICAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBVc2VyU2V0dGluZ3MoKipjcmVkc1sic2V0dGluZ3MiXSkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci5lcnJvcihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEVycm9yIGxvYWRpbmcgdXNlciBkYXRhIGZyb20gdG9rZW4gc3RvcmU6IHtlfSIpCiAgICAgICAgICAgIHJhaXNlIEhUVFBFeGNlcHRpb24oc3RhdHVzX2NvZGU9NDAxLCBkZXRhaWw9IkludmFsaWQgdG9rZW4gc2Vzc2lvbi4gUGxlYXNlIHJlY29uZmlndXJlLiIpCgogICAgICAgIGJhc2VfbWFuaWZlc3QgPSBzZWxmLmdldF9iYXNlX21hbmlmZXN0KCkKCiAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IFtdCiAgICAgICAgdHJ5OgogICAgICAgICAgICBpZiBjcmVkcy5nZXQoImF1dGhfcHJvdmlkZXIiKSA9PSAidHJha3QiOgogICAgICAgICAgICAgICAgIyBUcmFrdC1iYWNrZWQgYWNjb3VudDogZmV0Y2ggbGlicmFyeSBmcm9tIFRyYWt0LCBieXBhc3MgU3RyZW1pbwogICAgICAgICAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IGF3YWl0IHNlbGYuX2J1aWxkX2R5bmFtaWNfY2F0YWxvZ3NfdHJha3QoY3JlZHMsIHVzZXJfc2V0dGluZ3MsIHRva2VuKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgYnVuZGxlID0gU3RyZW1pb0J1bmRsZSgpCiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgIyBSZXNvbHZlIGF1dGgga2V5CiAgICAgICAgICAgICAgICAgICAgYXV0aF9rZXkgPSBhd2FpdCBzZWxmLl9yZXNvbHZlX2F1dGhfa2V5KGJ1bmRsZSwgY3JlZHMsIHRva2VuKQoKICAgICAgICAgICAgICAgICAgICBpZiBhdXRoX2tleToKICAgICAgICAgICAgICAgICAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IGF3YWl0IHNlbGYuX2J1aWxkX2R5bmFtaWNfY2F0YWxvZ3MoYnVuZGxlLCBhdXRoX2tleSwgdXNlcl9zZXR0aW5ncywgdG9rZW4pCiAgICAgICAgICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gRHluYW1pYyBjYXRhbG9nIGJ1aWxkIGZhaWxlZDoge2V9IikKICAgICAgICAgICAgZmV0Y2hlZF9jYXRhbG9ncyA9IFtdCgogICAgICAgICMgQ29tYmluZSBiYXNlIGNhdGFsb2dzIHdpdGggZmV0Y2hlZCBjYXRhbG9ncwogICAgICAgIGFsbF9jYXRhbG9ncyA9IFtjLmNvcHkoKSBmb3IgYyBpbiBiYXNlX21hbmlmZXN0WyJjYXRhbG9ncyJdXSArIFtjLmNvcHkoKSBmb3IgYyBpbiBmZXRjaGVkX2NhdGFsb2dzXQoKICAgICAgICAjIFRyYW5zbGF0ZSBjYXRhbG9ncwogICAgICAgIGxhbmd1YWdlID0gdXNlcl9zZXR0aW5ncy5sYW5ndWFnZSBpZiB1c2VyX3NldHRpbmdzIGVsc2UgTm9uZQogICAgICAgIHRyYW5zbGF0ZWRfY2F0YWxvZ3MgPSBhd2FpdCBzZWxmLl90cmFuc2xhdGVfY2F0YWxvZ3MoYWxsX2NhdGFsb2dzLCBsYW5ndWFnZSkKCiAgICAgICAgIyBTb3J0IGNhdGFsb2dzCiAgICAgICAgc29ydGVkX2NhdGFsb2dzID0gc2VsZi5fc29ydF9jYXRhbG9ncyh0cmFuc2xhdGVkX2NhdGFsb2dzLCB1c2VyX3NldHRpbmdzKQoKICAgICAgICBpZiBzb3J0ZWRfY2F0YWxvZ3M6CiAgICAgICAgICAgIGJhc2VfbWFuaWZlc3RbImNhdGFsb2dzIl0gPSBzb3J0ZWRfY2F0YWxvZ3MKCiAgICAgICAgcmV0dXJuIGJhc2VfbWFuaWZlc3QKCgptYW5pZmVzdF9zZXJ2aWNlID0gTWFuaWZlc3RTZXJ2aWNlKCkK" | base64 --decode > "$REPO/app/services/manifest.py" +echo " wrote app/services/manifest.py" + +mkdir -p "$REPO/app/services" +echo "aW1wb3J0IGFzeW5jaW8KZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUsIHRpbWV6b25lCmZyb20gdHlwaW5nIGltcG9ydCBBbnkKCmZyb20gZmFzdGFwaSBpbXBvcnQgSFRUUEV4Y2VwdGlvbgpmcm9tIGxvZ3VydSBpbXBvcnQgbG9nZ2VyCgpmcm9tIGFwcC5jb3JlLmNvbmZpZyBpbXBvcnQgc2V0dGluZ3MKZnJvbSBhcHAuY29yZS5zZWN1cml0eSBpbXBvcnQgcmVkYWN0X3Rva2VuCmZyb20gYXBwLmNvcmUuc2V0dGluZ3MgaW1wb3J0IFVzZXJTZXR0aW5ncwpmcm9tIGFwcC5zZXJ2aWNlcy5jYXRhbG9nIGltcG9ydCBEeW5hbWljQ2F0YWxvZ1NlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMubWFuaWZlc3QgaW1wb3J0IG1hbmlmZXN0X3NlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMuc3RyZW1pby5zZXJ2aWNlIGltcG9ydCBTdHJlbWlvQnVuZGxlCmZyb20gYXBwLnNlcnZpY2VzLnRva2VuX3N0b3JlIGltcG9ydCB0b2tlbl9zdG9yZQpmcm9tIGFwcC5zZXJ2aWNlcy50cmFuc2xhdGlvbiBpbXBvcnQgYXBwbHlfY2F0YWxvZ190cmFuc2xhdGlvbgpmcm9tIGFwcC51dGlscy5jYXRhbG9nIGltcG9ydCBzb3J0X2NhdGFsb2dzCgoKY2xhc3MgQ2F0YWxvZ1VwZGF0ZXI6CiAgICAiIiIKICAgIENhdGFsb2cgdXBkYXRlciB0aGF0IHRyaWdnZXJzIHVwZGF0ZXMgb24tZGVtYW5kIHdoZW4gdXNlcnMgcmVxdWVzdCBjYXRhbG9ncy4KICAgIFVzZXMgaW4tbWVtb3J5IGxvY2tpbmcgdG8gcHJldmVudCBkdXBsaWNhdGUgY29uY3VycmVudCB1cGRhdGVzLgogICAgIiIiCgogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgICMgSW4tbWVtb3J5IGxvY2sgdG8gcHJldmVudCBkdXBsaWNhdGUgdXBkYXRlcyBmb3IgdGhlIHNhbWUgdG9rZW4KICAgICAgICBzZWxmLl91cGRhdGluZ190b2tlbnM6IHNldFtzdHJdID0gc2V0KCkKCiAgICBkZWYgX25lZWRzX3VwZGF0ZShzZWxmLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0pIC0+IGJvb2w6CiAgICAgICAgIiIiQ2hlY2sgaWYgY2F0YWxvZyB1cGRhdGUgaXMgbmVlZGVkIGJhc2VkIG9uIGxhc3RfdXBkYXRlZCB0aW1lc3RhbXAuIiIiCiAgICAgICAgaWYgbm90IGNyZWRlbnRpYWxzOgogICAgICAgICAgICByZXR1cm4gRmFsc2UKCiAgICAgICAgbGFzdF91cGRhdGVkID0gY3JlZGVudGlhbHMuZ2V0KCJsYXN0X3VwZGF0ZWQiKQogICAgICAgIGlmIG5vdCBsYXN0X3VwZGF0ZWQ6CiAgICAgICAgICAgICMgTm8gdGltZXN0YW1wIG1lYW5zIG5ldmVyIHVwZGF0ZWQsIG5lZWRzIHVwZGF0ZQogICAgICAgICAgICByZXR1cm4gVHJ1ZQoKICAgICAgICB0cnk6CiAgICAgICAgICAgICMgUGFyc2UgSVNPIGZvcm1hdCB0aW1lc3RhbXAKICAgICAgICAgICAgaWYgaXNpbnN0YW5jZShsYXN0X3VwZGF0ZWQsIHN0cik6CiAgICAgICAgICAgICAgICBsYXN0X3VwZGF0ZV90aW1lID0gZGF0ZXRpbWUuZnJvbWlzb2Zvcm1hdChsYXN0X3VwZGF0ZWQucmVwbGFjZSgiWiIsICIrMDA6MDAiKSkKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGxhc3RfdXBkYXRlX3RpbWUgPSBsYXN0X3VwZGF0ZWQKCiAgICAgICAgICAgICMgQ2hlY2sgaWYgbW9yZSB0aGFuIDExIGhvdXJzIGhhdmUgcGFzc2VkICh1cGRhdGUgaWYgbGVzcyB0aGFuIDEgaG91ciByZW1haW5pbmcpCiAgICAgICAgICAgIG5vdyA9IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpCiAgICAgICAgICAgIGlmIGxhc3RfdXBkYXRlX3RpbWUudHppbmZvIGlzIE5vbmU6CiAgICAgICAgICAgICAgICBsYXN0X3VwZGF0ZV90aW1lID0gbGFzdF91cGRhdGVfdGltZS5yZXBsYWNlKHR6aW5mbz10aW1lem9uZS51dGMpCgogICAgICAgICAgICB0aW1lX3NpbmNlX3VwZGF0ZSA9IChub3cgLSBsYXN0X3VwZGF0ZV90aW1lKS50b3RhbF9zZWNvbmRzKCkKICAgICAgICAgICAgIyBVcGRhdGUgaWYgbGVzcyB0aGFuIDEgaG91ciByZW1haW5pbmcgdW50aWwgbmV4dCB1cGRhdGUKICAgICAgICAgICAgcmV0dXJuIHRpbWVfc2luY2VfdXBkYXRlID49IChzZXR0aW5ncy5DQVRBTE9HX1JFRlJFU0hfSU5URVJWQUxfU0VDT05EUyAtIDM2MDApCiAgICAgICAgZXhjZXB0IChWYWx1ZUVycm9yLCBUeXBlRXJyb3IsIEF0dHJpYnV0ZUVycm9yKSBhcyBlOgogICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBwYXJzZSBsYXN0X3VwZGF0ZWQgdGltZXN0YW1wOiB7ZX0uIFRyZWF0aW5nIGFzIG5lZWRzIHVwZGF0ZS4iKQogICAgICAgICAgICByZXR1cm4gVHJ1ZQoKICAgIGFzeW5jIGRlZiByZWZyZXNoX2NhdGFsb2dzX2Zvcl9jcmVkZW50aWFscygKICAgICAgICBzZWxmLCB0b2tlbjogc3RyLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0sIHVwZGF0ZV90aW1lc3RhbXA6IGJvb2wgPSBUcnVlCiAgICApIC0+IGJvb2w6CiAgICAgICAgIiIiCiAgICAgICAgUmVmcmVzaCBjYXRhbG9ncyBmb3IgYSB1c2VyJ3MgY3JlZGVudGlhbHMuCgogICAgICAgIEFyZ3M6CiAgICAgICAgICAgIHRva2VuOiBVc2VyIHRva2VuCiAgICAgICAgICAgIGNyZWRlbnRpYWxzOiBVc2VyIGNyZWRlbnRpYWxzIGRpY3QKICAgICAgICAgICAgdXBkYXRlX3RpbWVzdGFtcDogV2hldGhlciB0byB1cGRhdGUgbGFzdF91cGRhdGVkIHRpbWVzdGFtcCBvbiBzdWNjZXNzCgogICAgICAgIFJldHVybnM6CiAgICAgICAgICAgIFRydWUgaWYgdXBkYXRlIHdhcyBzdWNjZXNzZnVsLCBGYWxzZSBvdGhlcndpc2UKICAgICAgICAiIiIKICAgICAgICBpZiBub3QgY3JlZGVudGlhbHM6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gQXR0ZW1wdGVkIHRvIHJlZnJlc2ggY2F0YWxvZ3Mgd2l0aCBubyBjcmVkZW50aWFscy4iKQogICAgICAgICAgICByYWlzZSBIVFRQRXhjZXB0aW9uKHN0YXR1c19jb2RlPTQwMSwgZGV0YWlsPSJJbnZhbGlkIG9yIGV4cGlyZWQgdG9rZW4uIFBsZWFzZSByZWNvbmZpZ3VyZSB0aGUgYWRkb24uIikKCiAgICAgICAgIyBUcmFrdC1iYWNrZWQgYWNjb3VudHMgdXNlIGEgZGlmZmVyZW50IHJlZnJlc2ggcGF0aAogICAgICAgIGlmIGNyZWRlbnRpYWxzLmdldCgiYXV0aF9wcm92aWRlciIpID09ICJ0cmFrdCI6CiAgICAgICAgICAgIHJldHVybiBhd2FpdCBzZWxmLl9yZWZyZXNoX3RyYWt0X2NhdGFsb2dzKHRva2VuLCBjcmVkZW50aWFscywgdXBkYXRlX3RpbWVzdGFtcCkKCiAgICAgICAgYXV0aF9rZXkgPSBjcmVkZW50aWFscy5nZXQoImF1dGhLZXkiKQogICAgICAgICMgY2hlY2sgaWYgYXV0aCBrZXkgaXMgdmFsaWQKICAgICAgICBidW5kbGUgPSBTdHJlbWlvQnVuZGxlKCkKICAgICAgICB0cnk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5hdXRoLmdldF91c2VyX2luZm8oYXV0aF9rZXkpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci5leGNlcHRpb24oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBJbnZhbGlkIGF1dGgga2V5LiBGYWxsaW5nIGJhY2sgdG8gbG9naW46IHtlfSIpCiAgICAgICAgICAgICAgICBlbWFpbCA9IGNyZWRlbnRpYWxzLmdldCgiZW1haWwiKQogICAgICAgICAgICAgICAgcGFzc3dvcmQgPSBjcmVkZW50aWFscy5nZXQoInBhc3N3b3JkIikKICAgICAgICAgICAgICAgIGlmIGVtYWlsIGFuZCBwYXNzd29yZDoKICAgICAgICAgICAgICAgICAgICBhdXRoX2tleSA9IGF3YWl0IGJ1bmRsZS5hdXRoLmxvZ2luKGVtYWlsLCBwYXNzd29yZCkKICAgICAgICAgICAgICAgICAgICBjcmVkZW50aWFsc1siYXV0aEtleSJdID0gYXV0aF9rZXkKICAgICAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRydWUgICMgdHJ1ZSBzaW5jZSB3ZSB3b24ndCBiZSBhYmxlIHRvIHVwZGF0ZSBpdCBhZ2Fpbi4gc28gbm8gbmVlZCB0byB0cnkgYWdhaW4uCgogICAgICAgICAgICAjIDEuIENoZWNrIGlmIGFkZG9uIGlzIHN0aWxsIGluc3RhbGxlZAogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBhZGRvbl9pbnN0YWxsZWQgPSBhd2FpdCBidW5kbGUuYWRkb25zLmlzX2FkZG9uX2luc3RhbGxlZChhdXRoX2tleSkKICAgICAgICAgICAgICAgIGlmIG5vdCBhZGRvbl9pbnN0YWxsZWQ6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBVc2VyIGhhcyBub3QgaW5zdGFsbGVkIGFkZG9uLiBSZW1vdmluZyB0b2tlbiBmcm9tIHJlZGlzIikKICAgICAgICAgICAgICAgICAgICByZXR1cm4gVHJ1ZQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gRmFpbGVkIHRvIGNoZWNrIGlmIGFkZG9uIGlzIGluc3RhbGxlZDoge2V9IikKICAgICAgICAgICAgICAgIHJldHVybiBGYWxzZQoKICAgICAgICAgICAgIyAyLiBFeHRyYWN0IHNldHRpbmdzIGFuZCByZWZyZXNoCiAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBOb25lCiAgICAgICAgICAgIGlmIGNyZWRlbnRpYWxzLmdldCgic2V0dGluZ3MiKToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICB1c2VyX3NldHRpbmdzID0gVXNlclNldHRpbmdzKCoqY3JlZGVudGlhbHNbInNldHRpbmdzIl0pCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmV4Y2VwdGlvbihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byBwYXJzZSB1c2VyIHNldHRpbmdzOiB7ZX0iKQogICAgICAgICAgICAgICAgICAgICMgaWYgdXNlciBkb2Vzbid0IGhhdmUgc2V0dGluZywgd2UgY2FuJ3QgdXBkYXRlIHRoZSBjYXRhbG9ncy4KICAgICAgICAgICAgICAgICAgICAjIHNvIG5vIG5lZWQgdG8gdHJ5IGFnYWluLgogICAgICAgICAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgbWFuaWZlc3Rfc2VydmljZS5jYWNoZV9saWJyYXJ5X2FuZF9wcm9maWxlcyhidW5kbGUsIGF1dGhfa2V5LCB1c2VyX3NldHRpbmdzLCB0b2tlbikKICAgICAgICAgICAgbGFuZ3VhZ2UgPSB1c2VyX3NldHRpbmdzLmxhbmd1YWdlIGlmIHVzZXJfc2V0dGluZ3MgZWxzZSAiZW4tVVMiCgogICAgICAgICAgICBmcm9tIGFwcC5jb3JlLnNldHRpbmdzIGltcG9ydCByZXNvbHZlX3RtZGJfYXBpX2tleQoKICAgICAgICAgICAgdG1kYl9rZXkgPSByZXNvbHZlX3RtZGJfYXBpX2tleSh1c2VyX3NldHRpbmdzKQogICAgICAgICAgICBkeW5hbWljX2NhdGFsb2dfc2VydmljZSA9IER5bmFtaWNDYXRhbG9nU2VydmljZSgKICAgICAgICAgICAgICAgIGxhbmd1YWdlPWxhbmd1YWdlLAogICAgICAgICAgICAgICAgdG1kYl9hcGlfa2V5PXRtZGJfa2V5LAogICAgICAgICAgICApCgogICAgICAgICAgICBjYXRhbG9ncyA9IGF3YWl0IGR5bmFtaWNfY2F0YWxvZ19zZXJ2aWNlLmdldF9keW5hbWljX2NhdGFsb2dzKAogICAgICAgICAgICAgICAgbGlicmFyeV9pdGVtcz1saWJyYXJ5X2l0ZW1zLCB1c2VyX3NldHRpbmdzPXVzZXJfc2V0dGluZ3MsIHRva2VuPXRva2VuCiAgICAgICAgICAgICkKCiAgICAgICAgICAgIGxhbmcgPSB1c2VyX3NldHRpbmdzLmxhbmd1YWdlIGlmIHVzZXJfc2V0dGluZ3MgZWxzZSBOb25lCiAgICAgICAgICAgIGZvciBjYXQgaW4gY2F0YWxvZ3M6CiAgICAgICAgICAgICAgICBhd2FpdCBhcHBseV9jYXRhbG9nX3RyYW5zbGF0aW9uKGNhdCwgbGFuZykKCiAgICAgICAgICAgICMgc29ydCBjYXRhbG9ncyBieSBvcmRlciBpbiB1c2VyIHNldHRpbmdzCiAgICAgICAgICAgIGlmIHVzZXJfc2V0dGluZ3M6CiAgICAgICAgICAgICAgICBjYXRhbG9ncyA9IHNvcnRfY2F0YWxvZ3MoY2F0YWxvZ3MsIHVzZXJfc2V0dGluZ3MpCgogICAgICAgICAgICBzdWNjZXNzID0gYXdhaXQgYnVuZGxlLmFkZG9ucy51cGRhdGVfY2F0YWxvZ3MoYXV0aF9rZXksIGNhdGFsb2dzKQoKICAgICAgICAgICAgIyBVcGRhdGUgdGltZXN0YW1wIGFuZCBpbnZhbGlkYXRlIGNhY2hlIG9ubHkgb24gc3VjY2VzcwogICAgICAgICAgICBpZiBzdWNjZXNzIGFuZCB1cGRhdGVfdGltZXN0YW1wOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICMgVXBkYXRlIGxhc3RfdXBkYXRlZCB0aW1lc3RhbXAgdG8gY3VycmVudCB0aW1lCiAgICAgICAgICAgICAgICAgICAgIyBUaGlzIHJlcHJlc2VudHMgd2hlbiB0aGUgdXBkYXRlIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHkKICAgICAgICAgICAgICAgICAgICBub3cgPSBkYXRldGltZS5ub3codGltZXpvbmUudXRjKQogICAgICAgICAgICAgICAgICAgIGxhc3RfdXBkYXRlZF9zdHIgPSBub3cucmVwbGFjZShtaWNyb3NlY29uZD0wKS5pc29mb3JtYXQoKQogICAgICAgICAgICAgICAgICAgIGNyZWRlbnRpYWxzWyJsYXN0X3VwZGF0ZWQiXSA9IGxhc3RfdXBkYXRlZF9zdHIKICAgICAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBVcGRhdGVkIGxhc3RfdXBkYXRlZCB0aW1lc3RhbXAgdG8ge2xhc3RfdXBkYXRlZF9zdHJ9IikKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byB1cGRhdGUgbGFzdF91cGRhdGVkIHRpbWVzdGFtcDoge2V9IikKCiAgICAgICAgICAgIHJldHVybiBzdWNjZXNzCgogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgbG9nZ2VyLmV4Y2VwdGlvbihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIEZhaWxlZCB0byB1cGRhdGUgY2F0YWxvZ3MgaW4gYmFja2dyb3VuZDoge2V9IikKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZXJyb3JfbXNnID0gZiJGYWlsZWQgdG8gdXBkYXRlIGNhdGFsb2dzOiB7c3RyKGUpfSIKICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gKAogICAgICAgICAgICAgICAgICAgIGYiTW92aWUgYW5kIHNlcmllcyByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24geW91ciBTdHJlbWlvIGxpYnJhcnkuXG5cbuKaoO+4jyBTdGF0dXM6IEVycm9yXG57ZXJyb3JfbXNnfSIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5hZGRvbnMudXBkYXRlX2Rlc2NyaXB0aW9uKGF1dGhfa2V5LCBkZXNjcmlwdGlvbikKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyB1cGRhdGVfZXJyOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gdXBkYXRlIGFkZG9uIGRlc2NyaXB0aW9uIHdpdGggZXJyb3I6IHt1cGRhdGVfZXJyfSIpCiAgICAgICAgICAgIHJldHVybiBGYWxzZQogICAgICAgIGZpbmFsbHk6CiAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5jbG9zZSgpCgogICAgYXN5bmMgZGVmIF9yZWZyZXNoX3RyYWt0X2NhdGFsb2dzKAogICAgICAgIHNlbGYsIHRva2VuOiBzdHIsIGNyZWRlbnRpYWxzOiBkaWN0W3N0ciwgQW55XSwgdXBkYXRlX3RpbWVzdGFtcDogYm9vbCA9IFRydWUKICAgICkgLT4gYm9vbDoKICAgICAgICAiIiJSZWZyZXNoIGNhdGFsb2dzIGZvciBhIFRyYWt0LWJhY2tlZCBhY2NvdW50LiIiIgogICAgICAgIGZyb20gYXBwLmNvcmUuc2V0dGluZ3MgaW1wb3J0IHJlc29sdmVfdG1kYl9hcGlfa2V5CiAgICAgICAgZnJvbSBhcHAuc2VydmljZXMudHJha3Quc2VydmljZSBpbXBvcnQgVHJha3RCdW5kbGUKCiAgICAgICAgdXNlcl9zZXR0aW5ncyA9IE5vbmUKICAgICAgICBpZiBjcmVkZW50aWFscy5nZXQoInNldHRpbmdzIik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBVc2VyU2V0dGluZ3MoKipjcmVkZW50aWFsc1sic2V0dGluZ3MiXSkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBGYWlsZWQgdG8gcGFyc2UgVHJha3QgdXNlciBzZXR0aW5nczoge2V9IikKICAgICAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgIGFjY2Vzc190b2tlbiA9IGNyZWRlbnRpYWxzLmdldCgiYXV0aEtleSIpICAjIHN0b3JlZCBhcyBhdXRoS2V5IGFmdGVyIGRlY3J5cHQKICAgICAgICBpZiBub3QgYWNjZXNzX3Rva2VuIG9yIG5vdCBzZXR0aW5ncy5UUkFLVF9DTElFTlRfSUQgb3Igbm90IHNldHRpbmdzLlRSQUtUX0NMSUVOVF9TRUNSRVQ6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVHJha3QgY3JlZGVudGlhbHMgbWlzc2luZywgc2tpcHBpbmcgcmVmcmVzaCIpCiAgICAgICAgICAgIHJldHVybiBUcnVlCgogICAgICAgIHJlZGlyZWN0X3VyaSA9IGYie3NldHRpbmdzLkhPU1RfTkFNRX0vdG9rZW5zL3RyYWt0L2NhbGxiYWNrIgogICAgICAgIHRyYWt0X2J1bmRsZSA9IFRyYWt0QnVuZGxlKAogICAgICAgICAgICBjbGllbnRfaWQ9c2V0dGluZ3MuVFJBS1RfQ0xJRU5UX0lELAogICAgICAgICAgICBjbGllbnRfc2VjcmV0PXNldHRpbmdzLlRSQUtUX0NMSUVOVF9TRUNSRVQsCiAgICAgICAgICAgIHJlZGlyZWN0X3VyaT1yZWRpcmVjdF91cmksCiAgICAgICAgICAgIGFjY2Vzc190b2tlbj1hY2Nlc3NfdG9rZW4sCiAgICAgICAgKQogICAgICAgIHRyeToKICAgICAgICAgICAgbGlicmFyeV9pdGVtcyA9IGF3YWl0IHRyYWt0X2J1bmRsZS5saWJyYXJ5LmdldF9saWJyYXJ5X2l0ZW1zKCkKICAgICAgICAgICAgYXdhaXQgbWFuaWZlc3Rfc2VydmljZS5jYWNoZV9saWJyYXJ5X2FuZF9wcm9maWxlc19mcm9tX2l0ZW1zKGxpYnJhcnlfaXRlbXMsIHVzZXJfc2V0dGluZ3MsIHRva2VuKQoKICAgICAgICAgICAgaWYgdXBkYXRlX3RpbWVzdGFtcDoKICAgICAgICAgICAgICAgIG5vdyA9IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpCiAgICAgICAgICAgICAgICBjcmVkZW50aWFsc1sibGFzdF91cGRhdGVkIl0gPSBub3cucmVwbGFjZShtaWNyb3NlY29uZD0wKS5pc29mb3JtYXQoKQogICAgICAgICAgICAgICAgYXdhaXQgdG9rZW5fc3RvcmUudXBkYXRlX3VzZXJfZGF0YSh0b2tlbiwgY3JlZGVudGlhbHMpCgogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIFRyYWt0IGNhdGFsb2cgcmVmcmVzaCBjb21wbGV0ZSIpCiAgICAgICAgICAgIHJldHVybiBUcnVlCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVHJha3QgY2F0YWxvZyByZWZyZXNoIGZhaWxlZDoge2V9IikKICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgYXdhaXQgdHJha3RfYnVuZGxlLmNsb3NlKCkKCiAgICBhc3luYyBkZWYgdHJpZ2dlcl91cGRhdGUoc2VsZiwgdG9rZW46IHN0ciwgY3JlZGVudGlhbHM6IGRpY3Rbc3RyLCBBbnldKSAtPiBOb25lOgogICAgICAgICIiIgogICAgICAgIFRyaWdnZXIgYSBjYXRhbG9nIHVwZGF0ZSBpZiBuZWVkZWQuCiAgICAgICAgVGhpcyBmdW5jdGlvbiBjaGVja3MgaWYgdXBkYXRlIGlzIG5lZWRlZCBhbmQgZmlyZXMgYSBiYWNrZ3JvdW5kIHRhc2suCiAgICAgICAgVXNlcyBpbi1tZW1vcnkgbG9jayB0byBwcmV2ZW50IGR1cGxpY2F0ZSB1cGRhdGVzLgogICAgICAgICIiIgogICAgICAgICMgQ2hlY2sgaWYgYWxyZWFkeSB1cGRhdGluZwogICAgICAgIGlmIHRva2VuIGluIHNlbGYuX3VwZGF0aW5nX3Rva2VuczoKICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gVXBkYXRlIGFscmVhZHkgaW4gcHJvZ3Jlc3MsIHNraXBwaW5nIikKICAgICAgICAgICAgcmV0dXJuCgogICAgICAgICMgQ2hlY2sgaWYgdXBkYXRlIGlzIG5lZWRlZAogICAgICAgIGlmIG5vdCBzZWxmLl9uZWVkc191cGRhdGUoY3JlZGVudGlhbHMpOgogICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBDYXRhbG9nIHVwZGF0ZSBub3QgbmVlZGVkIHlldCIpCiAgICAgICAgICAgIHJldHVybgoKICAgICAgICAjIEFkZCB0byBsb2NrIGFuZCBmaXJlIGJhY2tncm91bmQgdXBkYXRlCiAgICAgICAgc2VsZi5fdXBkYXRpbmdfdG9rZW5zLmFkZCh0b2tlbikKICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX1dIFRyaWdnZXJpbmcgY2F0YWxvZyB1cGRhdGUiKQoKICAgICAgICAjIEZpcmUgYW5kIGZvcmdldCBiYWNrZ3JvdW5kIHRhc2sKICAgICAgICBhc3luY2lvLmNyZWF0ZV90YXNrKHNlbGYuX3VwZGF0ZV90YXNrKHRva2VuLCBjcmVkZW50aWFscykpCgogICAgYXN5bmMgZGVmIF91cGRhdGVfdGFzayhzZWxmLCB0b2tlbjogc3RyLCBjcmVkZW50aWFsczogZGljdFtzdHIsIEFueV0pIC0+IE5vbmU6CiAgICAgICAgIiIiQmFja2dyb3VuZCB0YXNrIHRoYXQgcGVyZm9ybXMgdGhlIGFjdHVhbCBjYXRhbG9nIHVwZGF0ZS4iIiIKICAgICAgICB0cnk6CiAgICAgICAgICAgIHN1Y2Nlc3MgPSBhd2FpdCBzZWxmLnJlZnJlc2hfY2F0YWxvZ3NfZm9yX2NyZWRlbnRpYWxzKHRva2VuLCBjcmVkZW50aWFscywgdXBkYXRlX3RpbWVzdGFtcD1UcnVlKQogICAgICAgICAgICBpZiBzdWNjZXNzOgogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9XSBDYXRhbG9nIHVwZGF0ZSBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5IikKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gQ2F0YWxvZyB1cGRhdGUgY29tcGxldGVkIHdpdGggZmFpbHVyZSIpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIuZXhjZXB0aW9uKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfV0gQ2F0YWxvZyB1cGRhdGUgdGFzayBmYWlsZWQ6IHtlfSIpCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgIyBBbHdheXMgcmVtb3ZlIGZyb20gbG9jawogICAgICAgICAgICBzZWxmLl91cGRhdGluZ190b2tlbnMuZGlzY2FyZCh0b2tlbikKCgpsb2dnZXIuaW5mbyhmIkNhdGFsb2cgdXBkYXRlciBpbml0aWFsaXplZCB3aXRoIHJlZnJlc2ggaW50ZXJ2YWwgb2Yge3NldHRpbmdzLkNBVEFMT0dfUkVGUkVTSF9JTlRFUlZBTF9TRUNPTkRTfSBzZWNvbmRzIikKY2F0YWxvZ191cGRhdGVyID0gQ2F0YWxvZ1VwZGF0ZXIoKQo=" | base64 --decode > "$REPO/app/services/catalog_updater.py" +echo " wrote app/services/catalog_updater.py" + +mkdir -p "$REPO/app/services" +echo "aW1wb3J0IGJhc2U2NAppbXBvcnQganNvbgpmcm9tIHR5cGluZyBpbXBvcnQgQW55CgppbXBvcnQgcmVkaXMuYXN5bmNpbyBhcyByZWRpcwpmcm9tIGFzeW5jX2xydSBpbXBvcnQgYWxydV9jYWNoZQpmcm9tIGNyeXB0b2dyYXBoeS5mZXJuZXQgaW1wb3J0IEZlcm5ldApmcm9tIGNyeXB0b2dyYXBoeS5oYXptYXQucHJpbWl0aXZlcyBpbXBvcnQgaGFzaGVzCmZyb20gY3J5cHRvZ3JhcGh5Lmhhem1hdC5wcmltaXRpdmVzLmtkZi5wYmtkZjIgaW1wb3J0IFBCS0RGMkhNQUMKZnJvbSBsb2d1cnUgaW1wb3J0IGxvZ2dlcgoKZnJvbSBhcHAuY29yZS5jb25maWcgaW1wb3J0IHNldHRpbmdzCmZyb20gYXBwLmNvcmUuc2VjdXJpdHkgaW1wb3J0IHJlZGFjdF90b2tlbgpmcm9tIGFwcC5zZXJ2aWNlcy5yZWRpc19zZXJ2aWNlIGltcG9ydCByZWRpc19zZXJ2aWNlCmZyb20gYXBwLnNlcnZpY2VzLnVzZXJfY2FjaGUgaW1wb3J0IHVzZXJfY2FjaGUKCgpjbGFzcyBUb2tlblN0b3JlOgogICAgIiIiUmVkaXMtYmFja2VkIHN0b3JlIGZvciB1c2VyIGNyZWRlbnRpYWxzIGFuZCBhdXRoIHRva2Vucy4iIiIKCiAgICBLRVlfUFJFRklYID0gc2V0dGluZ3MuUkVESVNfVE9LRU5fS0VZCgogICAgZGVmIF9faW5pdF9fKHNlbGYpIC0+IE5vbmU6CiAgICAgICAgaWYgbm90IHNldHRpbmdzLlRPS0VOX1NBTFQgb3Igc2V0dGluZ3MuVE9LRU5fU0FMVCA9PSAiY2hhbmdlLW1lIjoKICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoCiAgICAgICAgICAgICAgICAiVE9LRU5fU0FMVCBpcyBtaXNzaW5nIG9yIHVzaW5nIHRoZSBkZWZhdWx0IHBsYWNlaG9sZGVyLiBTZXQgYSBzdHJvbmcgdmFsdWUgdG8gc2VjdXJlIHRva2Vucy4iCiAgICAgICAgICAgICkKCiAgICBkZWYgX2Vuc3VyZV9zZWN1cmVfc2FsdChzZWxmKSAtPiBOb25lOgogICAgICAgIGlmIG5vdCBzZXR0aW5ncy5UT0tFTl9TQUxUIG9yIHNldHRpbmdzLlRPS0VOX1NBTFQgPT0gImNoYW5nZS1tZSI6CiAgICAgICAgICAgIGxvZ2dlci5lcnJvcigiVE9LRU5fU0FMVCBpcyB1bnNldCBvciB1c2luZyB0aGUgaW5zZWN1cmUgZGVmYXVsdC4iKQogICAgICAgICAgICByYWlzZSBSdW50aW1lRXJyb3IoIlRPS0VOX1NBTFQgbXVzdCBiZSBzZXQgdG8gYSBub24tZGVmYXVsdCB2YWx1ZSBiZWZvcmUgc3RvcmluZyBjcmVkZW50aWFscy4iKQoKICAgIGRlZiBfZ2V0X2NpcGhlcihzZWxmKSAtPiBGZXJuZXQ6CiAgICAgICAgc2FsdCA9IGIieDdGRGY5a3lwelExTG1SMzJiOGhXdjQ5c0txMlBkOFQiCiAgICAgICAga2RmID0gUEJLREYySE1BQygKICAgICAgICAgICAgYWxnb3JpdGhtPWhhc2hlcy5TSEEyNTYoKSwKICAgICAgICAgICAgbGVuZ3RoPTMyLAogICAgICAgICAgICBzYWx0PXNhbHQsCiAgICAgICAgICAgIGl0ZXJhdGlvbnM9MjAwXzAwMCwKICAgICAgICApCgogICAgICAgIGtleSA9IGJhc2U2NC51cmxzYWZlX2I2NGVuY29kZShrZGYuZGVyaXZlKHNldHRpbmdzLlRPS0VOX1NBTFQuZW5jb2RlKCJ1dGYtOCIpKSkKICAgICAgICByZXR1cm4gRmVybmV0KGtleSkKCiAgICBkZWYgZW5jcnlwdF90b2tlbihzZWxmLCB0b2tlbjogc3RyKSAtPiBzdHI6CiAgICAgICAgY2lwaGVyID0gc2VsZi5fZ2V0X2NpcGhlcigpCiAgICAgICAgcmV0dXJuIGNpcGhlci5lbmNyeXB0KHRva2VuLmVuY29kZSgidXRmLTgiKSkuZGVjb2RlKCJ1dGYtOCIpCgogICAgZGVmIGRlY3J5cHRfdG9rZW4oc2VsZiwgZW5jOiBzdHIpIC0+IHN0cjoKICAgICAgICBjaXBoZXIgPSBzZWxmLl9nZXRfY2lwaGVyKCkKICAgICAgICByZXR1cm4gY2lwaGVyLmRlY3J5cHQoZW5jLmVuY29kZSgidXRmLTgiKSkuZGVjb2RlKCJ1dGYtOCIpCgogICAgZGVmIF9mb3JtYXRfa2V5KHNlbGYsIHRva2VuOiBzdHIpIC0+IHN0cjoKICAgICAgICAiIiJGb3JtYXQgUmVkaXMga2V5IGZyb20gdG9rZW4uIiIiCiAgICAgICAgcmV0dXJuIGYie3NlbGYuS0VZX1BSRUZJWH17dG9rZW59IgoKICAgIGRlZiBnZXRfdG9rZW5fZnJvbV91c2VyX2lkKHNlbGYsIHVzZXJfaWQ6IHN0cikgLT4gc3RyOgogICAgICAgICIiIgogICAgICAgIEZvciBUcmFrdCB1c2VycywgZ2VuZXJhdGUgYSBzdGFibGUgb3BhcXVlIHRva2VuIGZyb20gdGhlIHVzZXJfaWQKICAgICAgICBzbyB0aGUgdXNlcm5hbWUgaXMgbm90IGV4cG9zZWQgaW4gdGhlIG1hbmlmZXN0IFVSTC4KICAgICAgICBTdHJlbWlvIHVzZXJzIGtlZXAgdGhlaXIgZXhpc3RpbmcgYmVoYXZpb3VyICh1c2VyX2lkID09IHRva2VuKS4KICAgICAgICAiIiIKICAgICAgICBpZiB1c2VyX2lkLnN0YXJ0c3dpdGgoInRyYWt0OiIpOgogICAgICAgICAgICBpbXBvcnQgaGFzaGxpYgogICAgICAgICAgICBzYWx0ID0gc2V0dGluZ3MuVE9LRU5fU0FMVCBvciAid2F0Y2hseSIKICAgICAgICAgICAgZGlnZXN0ID0gaGFzaGxpYi5zaGEyNTYoZiJ7c2FsdH06e3VzZXJfaWR9Ii5lbmNvZGUoKSkuaGV4ZGlnZXN0KCkKICAgICAgICAgICAgcmV0dXJuIGRpZ2VzdFs6NDBdCiAgICAgICAgcmV0dXJuIHVzZXJfaWQuc3RyaXAoKQoKICAgIGRlZiBnZXRfdXNlcl9pZF9mcm9tX3Rva2VuKHNlbGYsIHRva2VuOiBzdHIpIC0+IHN0cjoKICAgICAgICByZXR1cm4gdG9rZW4uc3RyaXAoKSBpZiB0b2tlbiBlbHNlICIiCgogICAgYXN5bmMgZGVmIHN0b3JlX3VzZXJfZGF0YShzZWxmLCB1c2VyX2lkOiBzdHIsIHBheWxvYWQ6IGRpY3Rbc3RyLCBBbnldKSAtPiBzdHI6CiAgICAgICAgc2VsZi5fZW5zdXJlX3NlY3VyZV9zYWx0KCkKICAgICAgICB0b2tlbiA9IHNlbGYuZ2V0X3Rva2VuX2Zyb21fdXNlcl9pZCh1c2VyX2lkKQogICAgICAgIGtleSA9IHNlbGYuX2Zvcm1hdF9rZXkodG9rZW4pCgogICAgICAgICMgUHJlcGFyZSBkYXRhIGZvciBzdG9yYWdlIChQbGFpbiBKU09OLCBubyBlbmNyeXB0aW9uIG5lZWRlZCkKICAgICAgICBzdG9yYWdlX2RhdGEgPSBwYXlsb2FkLmNvcHkoKQoKICAgICAgICAjIFN0b3JlIHVzZXJfaWQgaW4gcGF5bG9hZCBmb3IgY29udmVuaWVuY2UKICAgICAgICBzdG9yYWdlX2RhdGFbInVzZXJfaWQiXSA9IHVzZXJfaWQKCiAgICAgICAgaWYgc3RvcmFnZV9kYXRhLmdldCgiYXV0aEtleSIpOgogICAgICAgICAgICBzdG9yYWdlX2RhdGFbImF1dGhLZXkiXSA9IHNlbGYuZW5jcnlwdF90b2tlbihzdG9yYWdlX2RhdGFbImF1dGhLZXkiXSkKCiAgICAgICAgIyBFbmNyeXB0IFRyYWt0IHJlZnJlc2ggdG9rZW4gaWYgcHJlc2VudAogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoInRyYWt0X3JlZnJlc2hfdG9rZW4iKToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaWYgbm90IHN0b3JhZ2VfZGF0YVsidHJha3RfcmVmcmVzaF90b2tlbiJdLnN0YXJ0c3dpdGgoImdBQUFBQUIiKToKICAgICAgICAgICAgICAgICAgICBzdG9yYWdlX2RhdGFbInRyYWt0X3JlZnJlc2hfdG9rZW4iXSA9IHNlbGYuZW5jcnlwdF90b2tlbihzdG9yYWdlX2RhdGFbInRyYWt0X3JlZnJlc2hfdG9rZW4iXSkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBlbmNyeXB0IHRyYWt0X3JlZnJlc2hfdG9rZW46IHtleGN9IikKCiAgICAgICAgIyBTZWN1cmVseSBzdG9yZSBwYXNzd29yZCBpZiBwcm92aWRlZCAocHJpbWFyeSBsb2dpbiBtb2RlKQogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoInBhc3N3b3JkIik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHN0b3JhZ2VfZGF0YVsicGFzc3dvcmQiXSA9IHNlbGYuZW5jcnlwdF90b2tlbihzdG9yYWdlX2RhdGFbInBhc3N3b3JkIl0pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiUGFzc3dvcmQgZW5jcnlwdGlvbiBmYWlsZWQgZm9yIHtyZWRhY3RfdG9rZW4odXNlcl9pZCl9OiB7ZXhjfSIpCiAgICAgICAgICAgICAgICAjIERvIG5vdCBzdG9yZSBwbGFpbnRleHQgcGFzc3dvcmRzCiAgICAgICAgICAgICAgICByYWlzZSBSdW50aW1lRXJyb3IoIlBBU1NXT1JEX0VOQ1JZUFRfRkFJTEVEIikKCiAgICAgICAgIyBFbmNyeXB0IHBvc3Rlcl9yYXRpbmcgQVBJIGtleSBpZiBwcmVzZW50CiAgICAgICAgaWYgc3RvcmFnZV9kYXRhLmdldCgic2V0dGluZ3MiKSBhbmQgaXNpbnN0YW5jZShzdG9yYWdlX2RhdGFbInNldHRpbmdzIl0sIGRpY3QpOgogICAgICAgICAgICBwb3N0ZXJfcmF0aW5nID0gc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLmdldCgicG9zdGVyX3JhdGluZyIpCiAgICAgICAgICAgIGlmIHBvc3Rlcl9yYXRpbmcgYW5kIGlzaW5zdGFuY2UocG9zdGVyX3JhdGluZywgZGljdCkgYW5kIHBvc3Rlcl9yYXRpbmcuZ2V0KCJhcGlfa2V5Iik6CiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgIyBPbmx5IGVuY3J5cHQgaWYgaXQncyBub3QgYWxyZWFkeSBlbmNyeXB0ZWQgKGNoZWNrIGlmIGl0J3MgYSB2YWxpZCBlbmNyeXB0ZWQgc3RyaW5nKQogICAgICAgICAgICAgICAgICAgIGFwaV9rZXkgPSBwb3N0ZXJfcmF0aW5nWyJhcGlfa2V5Il0KICAgICAgICAgICAgICAgICAgICAjIFNpbXBsZSBjaGVjazogZW5jcnlwdGVkIHRva2VucyBhcmUgYmFzZTY0LWxpa2UgYW5kIGxvbmdlcgogICAgICAgICAgICAgICAgICAgICMgSWYgaXQgbG9va3MgbGlrZSBwbGFpbnRleHQsIGVuY3J5cHQgaXQKICAgICAgICAgICAgICAgICAgICAjIEZlcm5ldCBlbmNyeXB0ZWQgdG9rZW5zIHN0YXJ0IHdpdGggImdBQUFBQUIiCiAgICAgICAgICAgICAgICAgICAgaWYgbm90IGFwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBQiIpOgogICAgICAgICAgICAgICAgICAgICAgICBwb3N0ZXJfcmF0aW5nWyJhcGlfa2V5Il0gPSBzZWxmLmVuY3J5cHRfdG9rZW4oYXBpX2tleSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGVuY3J5cHQgcG9zdGVyX3JhdGluZyBhcGlfa2V5IGZvciB7cmVkYWN0X3Rva2VuKHVzZXJfaWQpfToge2V4Y30iKQoKICAgICAgICAjIEVuY3J5cHQgc2lta2xfYXBpX2tleSBpZiBwcmVzZW50CiAgICAgICAgaWYgc3RvcmFnZV9kYXRhLmdldCgic2V0dGluZ3MiKSBhbmQgaXNpbnN0YW5jZShzdG9yYWdlX2RhdGFbInNldHRpbmdzIl0sIGRpY3QpOgogICAgICAgICAgICBzaW1rbF9hcGlfa2V5ID0gc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLmdldCgic2lta2xfYXBpX2tleSIpCiAgICAgICAgICAgIGlmIHNpbWtsX2FwaV9rZXk6CiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgaWYgbm90IHNpbWtsX2FwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBQiIpOgogICAgICAgICAgICAgICAgICAgICAgICBzdG9yYWdlX2RhdGFbInNldHRpbmdzIl1bInNpbWtsX2FwaV9rZXkiXSA9IHNlbGYuZW5jcnlwdF90b2tlbihzaW1rbF9hcGlfa2V5KQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJGYWlsZWQgdG8gZW5jcnlwdCBzaW1rbF9hcGlfa2V5IGZvciB7cmVkYWN0X3Rva2VuKHVzZXJfaWQpfToge2V4Y30iKQoKICAgICAgICAjIEVuY3J5cHQgZ2VtaW5pX2FwaV9rZXkgaWYgcHJlc2VudAogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoInNldHRpbmdzIikgYW5kIGlzaW5zdGFuY2Uoc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLCBkaWN0KToKICAgICAgICAgICAgZ2VtaW5pX2FwaV9rZXkgPSBzdG9yYWdlX2RhdGFbInNldHRpbmdzIl0uZ2V0KCJnZW1pbmlfYXBpX2tleSIpCiAgICAgICAgICAgIGlmIGdlbWluaV9hcGlfa2V5OgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGlmIG5vdCBnZW1pbmlfYXBpX2tleS5zdGFydHN3aXRoKCJnQUFBQUFCIik6CiAgICAgICAgICAgICAgICAgICAgICAgIHN0b3JhZ2VfZGF0YVsic2V0dGluZ3MiXVsiZ2VtaW5pX2FwaV9rZXkiXSA9IHNlbGYuZW5jcnlwdF90b2tlbihnZW1pbmlfYXBpX2tleSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGVuY3J5cHQgZ2VtaW5pX2FwaV9rZXkgZm9yIHtyZWRhY3RfdG9rZW4odXNlcl9pZCl9OiB7ZXhjfSIpCgogICAgICAgICMgRW5jcnlwdCB0bWRiX2FwaV9rZXkgaWYgcHJlc2VudAogICAgICAgIGlmIHN0b3JhZ2VfZGF0YS5nZXQoInNldHRpbmdzIikgYW5kIGlzaW5zdGFuY2Uoc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLCBkaWN0KToKICAgICAgICAgICAgdG1kYl9hcGlfa2V5ID0gc3RvcmFnZV9kYXRhWyJzZXR0aW5ncyJdLmdldCgidG1kYl9hcGlfa2V5IikKICAgICAgICAgICAgaWYgdG1kYl9hcGlfa2V5OgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGlmIG5vdCB0bWRiX2FwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBQiIpOgogICAgICAgICAgICAgICAgICAgICAgICBzdG9yYWdlX2RhdGFbInNldHRpbmdzIl1bInRtZGJfYXBpX2tleSJdID0gc2VsZi5lbmNyeXB0X3Rva2VuKHRtZGJfYXBpX2tleSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGVuY3J5cHQgdG1kYl9hcGlfa2V5IGZvciB7cmVkYWN0X3Rva2VuKHVzZXJfaWQpfToge2V4Y30iKQogICAgICAgIGpzb25fc3RyID0ganNvbi5kdW1wcyhzdG9yYWdlX2RhdGEpCgogICAgICAgIGlmIHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTIGFuZCBzZXR0aW5ncy5UT0tFTl9UVExfU0VDT05EUyA+IDA6CiAgICAgICAgICAgIGF3YWl0IHJlZGlzX3NlcnZpY2Uuc2V0KGtleSwganNvbl9zdHIsIHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGF3YWl0IHJlZGlzX3NlcnZpY2Uuc2V0KGtleSwganNvbl9zdHIpCgogICAgICAgICMgSW52YWxpZGF0ZSBhc3luYyBMUlUgY2FjaGUgZm9yIGZyZXNoIHJlYWRzIG9uIHN1YnNlcXVlbnQgcmVxdWVzdHMKICAgICAgICB0cnk6CiAgICAgICAgICAgIHNlbGYuX2dldF91c2VyX2RhdGFfY2FjaGVkLmNhY2hlX2ludmFsaWRhdGUodG9rZW4pCiAgICAgICAgZXhjZXB0IEtleUVycm9yOgogICAgICAgICAgICBwYXNzCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIlRhcmdldGVkIGNhY2hlIGludmFsaWRhdGlvbiBmYWlsZWQ6IHtlfS4gRmFsbGluZyBiYWNrIHRvIGNsZWFyaW5nIGNhY2hlLiIpCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHNlbGYuX2dldF91c2VyX2RhdGFfY2FjaGVkLmNhY2hlX2NsZWFyKCkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlX2NsZWFyOgogICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiRXJyb3Igd2hpbGUgY2xlYXJpbmcgY2FjaGU6IHtlX2NsZWFyfSIpCgogICAgICAgIHJldHVybiB0b2tlbgoKICAgIGFzeW5jIGRlZiB1cGRhdGVfdXNlcl9kYXRhKHNlbGYsIHRva2VuOiBzdHIsIHBheWxvYWQ6IGRpY3Rbc3RyLCBBbnldKSAtPiBzdHI6CiAgICAgICAgIiIiVXBkYXRlIHVzZXIgZGF0YSBieSB0b2tlbi4gVGhpcyBpcyBhIGNvbnZlbmllbmNlIHdyYXBwZXIgYXJvdW5kIHN0b3JlX3VzZXJfZGF0YS4iIiIKICAgICAgICB1c2VyX2lkID0gc2VsZi5nZXRfdXNlcl9pZF9mcm9tX3Rva2VuKHRva2VuKQogICAgICAgIHJldHVybiBhd2FpdCBzZWxmLnN0b3JlX3VzZXJfZGF0YSh1c2VyX2lkLCBwYXlsb2FkKQoKICAgIGFzeW5jIGRlZiBfbWlncmF0ZV9wb3N0ZXJfcmF0aW5nX2Zvcm1hdF9yYXcoc2VsZiwgdG9rZW46IHN0ciwgcmVkaXNfa2V5OiBzdHIsIGRhdGE6IGRpY3QpIC0+IGRpY3QgfCBOb25lOgogICAgICAgICIiIk1pZ3JhdGUgb2xkIHJwZGJfa2V5IGZvcm1hdCB0byBuZXcgcG9zdGVyX3JhdGluZyBmb3JtYXQgaW4gcmF3IFJlZGlzIGRhdGEgaWYgbmVlZGVkLiIiIgogICAgICAgIGlmIG5vdCBkYXRhOgogICAgICAgICAgICByZXR1cm4gTm9uZQoKICAgICAgICBzZXR0aW5nc19kaWN0ID0gZGF0YS5nZXQoInNldHRpbmdzIikKICAgICAgICBpZiBub3Qgc2V0dGluZ3NfZGljdCBvciBub3QgaXNpbnN0YW5jZShzZXR0aW5nc19kaWN0LCBkaWN0KToKICAgICAgICAgICAgcmV0dXJuIE5vbmUKCiAgICAgICAgcnBkYl9rZXkgPSBzZXR0aW5nc19kaWN0LmdldCgicnBkYl9rZXkiKQogICAgICAgIHBvc3Rlcl9yYXRpbmcgPSBzZXR0aW5nc19kaWN0LmdldCgicG9zdGVyX3JhdGluZyIpCiAgICAgICAgbmVlZHNfc2F2ZSA9IEZhbHNlCgogICAgICAgICMgQ2FzZSAxOiBNaWdyYXRlIHJwZGJfa2V5IHRvIHBvc3Rlcl9yYXRpbmcgaWYgcnBkYl9rZXkgZXhpc3RzIGFuZCBwb3N0ZXJfcmF0aW5nIGRvZXNuJ3QKICAgICAgICBpZiBycGRiX2tleSBhbmQgbm90IHBvc3Rlcl9yYXRpbmc6CiAgICAgICAgICAgIGxvZ2dlci5pbmZvKGYiW01JR1JBVElPTl0gTWlncmF0aW5nIHJwZGJfa2V5IHRvIHBvc3Rlcl9yYXRpbmcgZm9ybWF0IGZvciB7cmVkYWN0X3Rva2VuKHRva2VuKX0iKQogICAgICAgICAgICBzZXR0aW5nc19kaWN0WyJwb3N0ZXJfcmF0aW5nIl0gPSB7CiAgICAgICAgICAgICAgICAicHJvdmlkZXIiOiAicnBkYiIsCiAgICAgICAgICAgICAgICAiYXBpX2tleSI6IHNlbGYuZW5jcnlwdF90b2tlbihycGRiX2tleSksICAjIEVuY3J5cHQgdGhlIEFQSSBrZXkKICAgICAgICAgICAgfQogICAgICAgICAgICBuZWVkc19zYXZlID0gVHJ1ZQoKICAgICAgICAjIENhc2UgMjogQ2xlYW4gdXAgZGVwcmVjYXRlZCBycGRiX2tleSBmaWVsZCBpZiBpdCBleGlzdHMgKGV2ZW4gaWYgZW1wdHkvbnVsbCkKICAgICAgICAjIFJlbW92ZSBpdCBzaW5jZSB3ZSd2ZSBtaWdyYXRlZCB0byBwb3N0ZXJfcmF0aW5nIG9yIGl0J3Mgbm8gbG9uZ2VyIG5lZWRlZAogICAgICAgIGlmICJycGRiX2tleSIgaW4gc2V0dGluZ3NfZGljdDoKICAgICAgICAgICAgc2V0dGluZ3NfZGljdC5wb3AoInJwZGJfa2V5IikKICAgICAgICAgICAgIyBrZWVwIGVtcHR5IHBvc3Rlcl9yYXRpbmcgZmllbGQgZm9yIG5vdwogICAgICAgICAgICBzZXR0aW5nc19kaWN0WyJwb3N0ZXJfcmF0aW5nIl0gPSB7CiAgICAgICAgICAgICAgICAicHJvdmlkZXIiOiAicnBkYiIsCiAgICAgICAgICAgICAgICAiYXBpX2tleSI6IE5vbmUsCiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgbm90IG5lZWRzX3NhdmU6ICAjIE9ubHkgbG9nIGlmIHdlIGRpZG4ndCBhbHJlYWR5IGxvZyBtaWdyYXRpb24KICAgICAgICAgICAgICAgIGxvZ2dlci5pbmZvKGYiW01JR1JBVElPTl0gUmVtb3ZpbmcgZGVwcmVjYXRlZCBycGRiX2tleSBmaWVsZCBmb3Ige3JlZGFjdF90b2tlbih0b2tlbil9IikKICAgICAgICAgICAgbmVlZHNfc2F2ZSA9IFRydWUKCiAgICAgICAgIyBTYXZlIGJhY2sgdG8gcmVkaXMgaWYgYW55IGNoYW5nZXMgd2VyZSBtYWRlCiAgICAgICAgaWYgbmVlZHNfc2F2ZToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaWYgc2V0dGluZ3MuVE9LRU5fVFRMX1NFQ09ORFMgYW5kIHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTID4gMDoKICAgICAgICAgICAgICAgICAgICBhd2FpdCByZWRpc19zZXJ2aWNlLnNldChyZWRpc19rZXksIGpzb24uZHVtcHMoZGF0YSksIHNldHRpbmdzLlRPS0VOX1RUTF9TRUNPTkRTKQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBhd2FpdCByZWRpc19zZXJ2aWNlLnNldChyZWRpc19rZXksIGpzb24uZHVtcHMoZGF0YSkpCgogICAgICAgICAgICAgICAgIyBJbnZhbGlkYXRlIGNhY2hlIHNvIG5leHQgcmVhZCBnZXRzIHRoZSBtaWdyYXRlZCBkYXRhCiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgc2VsZi5nZXRfdXNlcl9kYXRhLmNhY2hlX2ludmFsaWRhdGUodG9rZW4pCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICAgICAgICAgIHBhc3MKCiAgICAgICAgICAgICAgICBsb2dnZXIuaW5mbygKICAgICAgICAgICAgICAgICAgICAiW01JR1JBVElPTl0gU3VjY2Vzc2Z1bGx5IG1pZ3JhdGVkIGFuZCBlbmNyeXB0ZWQgcG9zdGVyX3JhdGluZyAiIGYiZm9ybWF0IGZvciB7cmVkYWN0X3Rva2VuKHRva2VuKX0iCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICByZXR1cm4gZGF0YQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIltNSUdSQVRJT05dIEZhaWxlZCB0byBzYXZlIG1pZ3JhdGVkIGRhdGEgZm9yIHtyZWRhY3RfdG9rZW4odG9rZW4pfToge2V9IikKICAgICAgICAgICAgICAgIHJldHVybiBOb25lCgogICAgICAgIHJldHVybiBOb25lCgogICAgYXN5bmMgZGVmIGdldF91c2VyX2RhdGEoc2VsZiwgdG9rZW46IHN0cikgLT4gZGljdFtzdHIsIEFueV0gfCBOb25lOgogICAgICAgIGRhdGEgPSBhd2FpdCBzZWxmLl9nZXRfdXNlcl9kYXRhX2NhY2hlZCh0b2tlbikKICAgICAgICBpZiBkYXRhIGlzIE5vbmU6CiAgICAgICAgICAgICMgRG9uJ3QgbGV0IGEgbWlzc2luZy10b2tlbiByZXN1bHQgZ2V0IHBpbm5lZCBpbiB0aGUgcGVyLXByb2Nlc3MgY2FjaGU7CiAgICAgICAgICAgICMgb3RoZXJ3aXNlIGEgdG9rZW4gY3JlYXRlZCBvbiBhbm90aGVyIHdvcmtlciB3b3VsZCA0MDEgaGVyZSBmb3IgaG91cnMuCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHNlbGYuX2dldF91c2VyX2RhdGFfY2FjaGVkLmNhY2hlX2ludmFsaWRhdGUodG9rZW4pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgcmV0dXJuIGRhdGEKCiAgICBAYWxydV9jYWNoZShtYXhzaXplPTIwMDAsIHR0bD00MzIwMCkKICAgIGFzeW5jIGRlZiBfZ2V0X3VzZXJfZGF0YV9jYWNoZWQoc2VsZiwgdG9rZW46IHN0cikgLT4gZGljdFtzdHIsIEFueV0gfCBOb25lOgogICAgICAgIGxvZ2dlci5kZWJ1ZyhmIltSRURJU10gQ2FjaGUgbWlzcy4gRmV0Y2hpbmcgZGF0YSBmcm9tIHJlZGlzIGZvciB7dG9rZW59IikKICAgICAgICBrZXkgPSBzZWxmLl9mb3JtYXRfa2V5KHRva2VuKQogICAgICAgIGRhdGFfcmF3ID0gYXdhaXQgcmVkaXNfc2VydmljZS5nZXQoa2V5KQoKICAgICAgICBpZiBub3QgZGF0YV9yYXc6CiAgICAgICAgICAgIHJldHVybiBOb25lCgogICAgICAgIHRyeToKICAgICAgICAgICAgZGF0YSA9IGpzb24ubG9hZHMoZGF0YV9yYXcpCiAgICAgICAgZXhjZXB0IGpzb24uSlNPTkRlY29kZUVycm9yOgogICAgICAgICAgICByZXR1cm4gTm9uZQoKICAgICAgICB1cGRhdGVkX2RhdGEgPSBhd2FpdCBzZWxmLl9taWdyYXRlX3Bvc3Rlcl9yYXRpbmdfZm9ybWF0X3Jhdyh0b2tlbiwga2V5LCBkYXRhKQogICAgICAgIGlmIHVwZGF0ZWRfZGF0YToKICAgICAgICAgICAgZGF0YSA9IHVwZGF0ZWRfZGF0YQoKICAgICAgICAjIERlY3J5cHQgZmllbGRzIGluZGl2aWR1YWxseTsgZG8gbm90IGZhaWwgZW50aXJlIHJlY29yZCBvbiBkZWNyeXB0aW9uIGVycm9ycwogICAgICAgIGlmIGRhdGEuZ2V0KCJhdXRoS2V5Iik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGRhdGFbImF1dGhLZXkiXSA9IHNlbGYuZGVjcnlwdF90b2tlbihkYXRhWyJhdXRoS2V5Il0pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRGVjcnlwdGlvbiBmYWlsZWQgZm9yIGF1dGhLZXkgYXNzb2NpYXRlZCB3aXRoIHtyZWRhY3RfdG9rZW4odG9rZW4pfToge2V9IikKICAgICAgICAgICAgICAgICMgTGVhdmUgYXMtaXMgKGxlZ2FjeSBwbGFpbnRleHQgb3IgcHJldmlvdXMgZmFpbHVyZSkKICAgICAgICAgICAgICAgIHBhc3MKCiAgICAgICAgaWYgZGF0YS5nZXQoInRyYWt0X3JlZnJlc2hfdG9rZW4iKToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaWYgZGF0YVsidHJha3RfcmVmcmVzaF90b2tlbiJdLnN0YXJ0c3dpdGgoImdBQUFBQSIpOgogICAgICAgICAgICAgICAgICAgIGRhdGFbInRyYWt0X3JlZnJlc2hfdG9rZW4iXSA9IHNlbGYuZGVjcnlwdF90b2tlbihkYXRhWyJ0cmFrdF9yZWZyZXNoX3Rva2VuIl0pCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIkRlY3J5cHRpb24gZmFpbGVkIGZvciB0cmFrdF9yZWZyZXNoX3Rva2VuIGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCiAgICAgICAgaWYgZGF0YS5nZXQoInBhc3N3b3JkIik6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGRhdGFbInBhc3N3b3JkIl0gPSBzZWxmLmRlY3J5cHRfdG9rZW4oZGF0YVsicGFzc3dvcmQiXSkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJEZWNyeXB0aW9uIGZhaWxlZCBmb3IgcGFzc3dvcmQgYXNzb2NpYXRlZCB3aXRoIHtyZWRhY3RfdG9rZW4odG9rZW4pfToge2V9IikKICAgICAgICAgICAgICAgICMgcmVxdWlyZSByZS1sb2dpbiBwYXRoIHdoZW4gbmVlZGVkCiAgICAgICAgICAgICAgICBkYXRhWyJwYXNzd29yZCJdID0gTm9uZQoKICAgICAgICAjIERlY3J5cHQgcG9zdGVyX3JhdGluZyBBUEkga2V5IGlmIHByZXNlbnQKICAgICAgICBpZiBkYXRhLmdldCgic2V0dGluZ3MiKSBhbmQgaXNpbnN0YW5jZShkYXRhWyJzZXR0aW5ncyJdLCBkaWN0KToKICAgICAgICAgICAgcG9zdGVyX3JhdGluZyA9IGRhdGFbInNldHRpbmdzIl0uZ2V0KCJwb3N0ZXJfcmF0aW5nIikKICAgICAgICAgICAgaWYgcG9zdGVyX3JhdGluZyBhbmQgaXNpbnN0YW5jZShwb3N0ZXJfcmF0aW5nLCBkaWN0KSBhbmQgcG9zdGVyX3JhdGluZy5nZXQoImFwaV9rZXkiKToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBpZiBwb3N0ZXJfcmF0aW5nWyJhcGlfa2V5Il0uc3RhcnRzd2l0aCgiZ0FBQUFBIik6CiAgICAgICAgICAgICAgICAgICAgICAgIHBvc3Rlcl9yYXRpbmdbImFwaV9rZXkiXSA9IHNlbGYuZGVjcnlwdF90b2tlbihwb3N0ZXJfcmF0aW5nWyJhcGlfa2V5Il0pCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKAogICAgICAgICAgICAgICAgICAgICAgICBmIkRlY3J5cHRpb24gZmFpbGVkIGZvciBwb3N0ZXJfcmF0aW5nIGFwaV9rZXkgYXNzb2NpYXRlZCB3aXRoIHtyZWRhY3RfdG9rZW4odG9rZW4pfToge2V9IgogICAgICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgIHNpbWtsX2FwaV9rZXkgPSBkYXRhWyJzZXR0aW5ncyJdLmdldCgic2lta2xfYXBpX2tleSIpCiAgICAgICAgICAgIGlmIHNpbWtsX2FwaV9rZXk6CiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgaWYgc2lta2xfYXBpX2tleS5zdGFydHN3aXRoKCJnQUFBQUEiKToKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YVsic2V0dGluZ3MiXVsic2lta2xfYXBpX2tleSJdID0gc2VsZi5kZWNyeXB0X3Rva2VuKHNpbWtsX2FwaV9rZXkpCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGYiRGVjcnlwdGlvbiBmYWlsZWQgZm9yIHNpbWtsX2FwaV9rZXkgYXNzb2NpYXRlZCB3aXRoIHtyZWRhY3RfdG9rZW4odG9rZW4pfToge2V9IikKCiAgICAgICAgICAgIGdlbWluaV9hcGlfa2V5ID0gZGF0YVsic2V0dGluZ3MiXS5nZXQoImdlbWluaV9hcGlfa2V5IikKICAgICAgICAgICAgaWYgZ2VtaW5pX2FwaV9rZXk6CiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgaWYgZ2VtaW5pX2FwaV9rZXkuc3RhcnRzd2l0aCgiZ0FBQUFBIik6CiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFbInNldHRpbmdzIl1bImdlbWluaV9hcGlfa2V5Il0gPSBzZWxmLmRlY3J5cHRfdG9rZW4oZ2VtaW5pX2FwaV9rZXkpCiAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGYiRGVjcnlwdGlvbiBmYWlsZWQgZm9yIGdlbWluaV9hcGlfa2V5IGFzc29jaWF0ZWQgd2l0aCB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCgogICAgICAgICAgICB0bWRiX2FwaV9rZXkgPSBkYXRhWyJzZXR0aW5ncyJdLmdldCgidG1kYl9hcGlfa2V5IikKICAgICAgICAgICAgaWYgdG1kYl9hcGlfa2V5OgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGlmIHRtZGJfYXBpX2tleS5zdGFydHN3aXRoKCJnQUFBQUEiKToKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YVsic2V0dGluZ3MiXVsidG1kYl9hcGlfa2V5Il0gPSBzZWxmLmRlY3J5cHRfdG9rZW4odG1kYl9hcGlfa2V5KQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhmIkRlY3J5cHRpb24gZmFpbGVkIGZvciB0bWRiX2FwaV9rZXkgYXNzb2NpYXRlZCB3aXRoIHtyZWRhY3RfdG9rZW4odG9rZW4pfToge2V9IikKCiAgICAgICAgcmV0dXJuIGRhdGEKCiAgICBhc3luYyBkZWYgZGVsZXRlX3Rva2VuKHNlbGYsIHRva2VuOiBzdHIgPSBOb25lLCBrZXk6IHN0ciA9IE5vbmUpIC0+IE5vbmU6CiAgICAgICAgaWYgbm90IHRva2VuIGFuZCBub3Qga2V5OgogICAgICAgICAgICByYWlzZSBWYWx1ZUVycm9yKCJFaXRoZXIgdG9rZW4gb3Iga2V5IG11c3QgYmUgcHJvdmlkZWQiKQogICAgICAgIGlmIHRva2VuOgogICAgICAgICAgICBrZXkgPSBzZWxmLl9mb3JtYXRfa2V5KHRva2VuKQoKICAgICAgICBhd2FpdCByZWRpc19zZXJ2aWNlLmRlbGV0ZShrZXkpCiAgICAgICAgIyB3ZSBhbHNvIG5lZWQgdG8gZGVsZXRlIHRoZSBjYWNoZWQgbGlicmFyeSBpdGVtcywgcHJvZmlsZXMgYW5kIHdhdGNoZWQgc2V0cwogICAgICAgIGlmIHRva2VuOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLmludmFsaWRhdGVfYWxsX3VzZXJfZGF0YSh0b2tlbikKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJGYWlsZWQgdG8gaW52YWxpZGF0ZSBhbGwgdXNlciBkYXRhIGZvciB7cmVkYWN0X3Rva2VuKHRva2VuKX06IHtlfSIpCgogICAgICAgICMgSW52YWxpZGF0ZSBhc3luYyBMUlUgY2FjaGUgc28gZnV0dXJlIHJlYWRzIHJlZmxlY3QgZGVsZXRpb24KICAgICAgICB0cnk6CiAgICAgICAgICAgIGlmIHRva2VuOgogICAgICAgICAgICAgICAgc2VsZi5fZ2V0X3VzZXJfZGF0YV9jYWNoZWQuY2FjaGVfaW52YWxpZGF0ZSh0b2tlbikKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICMgSWYgb25seSBrZXkgaXMgcHJvdmlkZWQsIGNsZWFyIGNhY2hlIGVudGlyZWx5IHRvIGJlIHNhZmUKICAgICAgICAgICAgICAgIHNlbGYuX2dldF91c2VyX2RhdGFfY2FjaGVkLmNhY2hlX2NsZWFyKCkKICAgICAgICBleGNlcHQgS2V5RXJyb3I6CiAgICAgICAgICAgIHBhc3MKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGludmFsaWRhdGUgdXNlciBkYXRhIGNhY2hlIGR1cmluZyB0b2tlbiBkZWxldGlvbjoge2V9IikKCiAgICBhc3luYyBkZWYgY291bnRfdXNlcnMoc2VsZikgLT4gaW50OgogICAgICAgICIiIkNvdW50IHRvdGFsIHVzZXJzIGJ5IHNjYW5uaW5nIFJlZGlzIGtleXMgd2l0aCB0aGUgY29uZmlndXJlZCBwcmVmaXguCgogICAgICAgIENhY2hlZCBmb3IgMTIgaG91cnMgdG8gYXZvaWQgZnJlcXVlbnQgUmVkaXMgc2NhbnMuCiAgICAgICAgIiIiCiAgICAgICAgdHJ5OgogICAgICAgICAgICBjbGllbnQgPSBhd2FpdCByZWRpc19zZXJ2aWNlLmdldF9jbGllbnQoKQogICAgICAgIGV4Y2VwdCAocmVkaXMuUmVkaXNFcnJvciwgT1NFcnJvcikgYXMgZXhjOgogICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkNhbm5vdCBjb3VudCB1c2VyczsgUmVkaXMgdW5hdmFpbGFibGU6IHtleGN9IikKICAgICAgICAgICAgcmV0dXJuIDAKCiAgICAgICAgcGF0dGVybiA9IGYie3NlbGYuS0VZX1BSRUZJWH0qIgogICAgICAgIHRvdGFsID0gMAogICAgICAgIHRyeToKICAgICAgICAgICAgYXN5bmMgZm9yIF8gaW4gY2xpZW50LnNjYW5faXRlcihtYXRjaD1wYXR0ZXJuLCBjb3VudD01MDApOgogICAgICAgICAgICAgICAgdG90YWwgKz0gMQogICAgICAgIGV4Y2VwdCAocmVkaXMuUmVkaXNFcnJvciwgT1NFcnJvcikgYXMgZXhjOgogICAgICAgICAgICBsb2dnZXIud2FybmluZyhmIkZhaWxlZCB0byBzY2FuIGZvciB1c2VyIGNvdW50OiB7ZXhjfSIpCiAgICAgICAgICAgIHJldHVybiAwCiAgICAgICAgcmV0dXJuIHRvdGFsCgoKdG9rZW5fc3RvcmUgPSBUb2tlblN0b3JlKCkK" | base64 --decode > "$REPO/app/services/token_store.py" +echo " wrote app/services/token_store.py" + +mkdir -p "$REPO/app/services/recommendation" +echo "aW1wb3J0IHJhbmRvbQppbXBvcnQgcmUKaW1wb3J0IHRpbWUKZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKZnJvbSBmYXN0YXBpIGltcG9ydCBIVFRQRXhjZXB0aW9uCmZyb20gbG9ndXJ1IGltcG9ydCBsb2dnZXIKCmZyb20gYXBwLmNvcmUuY29uZmlnIGltcG9ydCBzZXR0aW5ncwpmcm9tIGFwcC5jb3JlLmNvbnN0YW50cyBpbXBvcnQgREVGQVVMVF9DQVRBTE9HX0xJTUlULCBERUZBVUxUX01JTl9JVEVNUwpmcm9tIGFwcC5jb3JlLnNlY3VyaXR5IGltcG9ydCByZWRhY3RfdG9rZW4KZnJvbSBhcHAuY29yZS5zZXR0aW5ncyBpbXBvcnQgVXNlclNldHRpbmdzLCBnZXRfZGVmYXVsdF9zZXR0aW5ncywgcmVzb2x2ZV90bWRiX2FwaV9rZXkKZnJvbSBhcHAubW9kZWxzLnRhc3RlX3Byb2ZpbGUgaW1wb3J0IFRhc3RlUHJvZmlsZQpmcm9tIGFwcC5zZXJ2aWNlcy5jYXRhbG9nX3VwZGF0ZXIgaW1wb3J0IGNhdGFsb2dfdXBkYXRlcgpmcm9tIGFwcC5zZXJ2aWNlcy5wcm9maWxlLmludGVncmF0aW9uIGltcG9ydCBQcm9maWxlSW50ZWdyYXRpb24KZnJvbSBhcHAuc2VydmljZXMucmVjb21tZW5kYXRpb24uYWxsX2Jhc2VkIGltcG9ydCBBbGxCYXNlZFNlcnZpY2UKZnJvbSBhcHAuc2VydmljZXMucmVjb21tZW5kYXRpb24uY3JlYXRvcnMgaW1wb3J0IENyZWF0b3JzU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy5yZWNvbW1lbmRhdGlvbi5pdGVtX2Jhc2VkIGltcG9ydCBJdGVtQmFzZWRTZXJ2aWNlCmZyb20gYXBwLnNlcnZpY2VzLnJlY29tbWVuZGF0aW9uLnRoZW1lX2Jhc2VkIGltcG9ydCBUaGVtZUJhc2VkU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy5yZWNvbW1lbmRhdGlvbi50b3BfcGlja3MgaW1wb3J0IFRvcFBpY2tzU2VydmljZQpmcm9tIGFwcC5zZXJ2aWNlcy5yZWNvbW1lbmRhdGlvbi51dGlscyBpbXBvcnQgcGFkX3RvX21pbgpmcm9tIGFwcC5zZXJ2aWNlcy5zdHJlbWlvLnNlcnZpY2UgaW1wb3J0IFN0cmVtaW9CdW5kbGUKZnJvbSBhcHAuc2VydmljZXMudG1kYi5zZXJ2aWNlIGltcG9ydCBnZXRfdG1kYl9zZXJ2aWNlCmZyb20gYXBwLnNlcnZpY2VzLnRva2VuX3N0b3JlIGltcG9ydCB0b2tlbl9zdG9yZQpmcm9tIGFwcC5zZXJ2aWNlcy51c2VyX2NhY2hlIGltcG9ydCB1c2VyX2NhY2hlCmZyb20gYXBwLnV0aWxzLmNhdGFsb2cgaW1wb3J0IGNhY2hlX3Byb2ZpbGVfYW5kX3dhdGNoZWRfc2V0cwoKCmRlZiBzaG91bGRfc2h1ZmZsZSh1c2VyX3NldHRpbmdzOiBVc2VyU2V0dGluZ3MsIGNhdGFsb2dfaWQ6IHN0cikgLT4gYm9vbDoKICAgIGNvbmZpZyA9IG5leHQoKGMgZm9yIGMgaW4gdXNlcl9zZXR0aW5ncy5jYXRhbG9ncyBpZiBjLmlkID09IGNhdGFsb2dfaWQpLCBOb25lKQogICAgcmV0dXJuIGdldGF0dHIoY29uZmlnLCAic2h1ZmZsZSIsIEZhbHNlKSBpZiBjb25maWcgZWxzZSBGYWxzZQoKCmRlZiBzaHVmZmxlX2RhdGFfaWZfbmVlZGVkKAogICAgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzLCBjYXRhbG9nX2lkOiBzdHIsIGRhdGE6IGxpc3RbZGljdFtzdHIsIEFueV1dCikgLT4gbGlzdFtkaWN0W3N0ciwgQW55XV06CiAgICBpZiBzaG91bGRfc2h1ZmZsZSh1c2VyX3NldHRpbmdzLCBjYXRhbG9nX2lkKToKICAgICAgICByYW5kb20uc2h1ZmZsZShkYXRhKQogICAgcmV0dXJuIGRhdGEKCgpkZWYgX2NsZWFuX21ldGEobWV0YTogZGljdCkgLT4gZGljdCB8IE5vbmU6CiAgICAiIiJSZXR1cm4gYSBzYW5pdGl6ZWQgU3RyZW1pbyBtZXRhIG9iamVjdCB3aXRob3V0IGludGVybmFsIGZpZWxkcy4KCiAgICBLZWVwcyBvbmx5IHB1YmxpYyBrZXlzIGFuZCBkcm9wcyBpbnRlcm5hbCBzY29yaW5nL0lEcy9rZXl3b3Jkcy9jYXN0LCBldGMuCiAgICAiIiIKICAgIGFsbG93ZWQgPSB7CiAgICAgICAgImlkIiwKICAgICAgICAidHlwZSIsCiAgICAgICAgIm5hbWUiLAogICAgICAgICJwb3N0ZXIiLAogICAgICAgICJsb2dvIiwKICAgICAgICAiYmFja2dyb3VuZCIsCiAgICAgICAgImRlc2NyaXB0aW9uIiwKICAgICAgICAicmVsZWFzZUluZm8iLAogICAgICAgICJpbWRiUmF0aW5nIiwKICAgICAgICAiZ2VucmVzIiwKICAgICAgICAicnVudGltZSIsCiAgICB9CiAgICBjbGVhbmVkID0ge2s6IHYgZm9yIGssIHYgaW4gbWV0YS5pdGVtcygpIGlmIGsgaW4gYWxsb3dlZH0KICAgICMgRHJvcCBlbXB0eSB2YWx1ZXMKICAgIGNsZWFuZWQgPSB7azogdiBmb3IgaywgdiBpbiBjbGVhbmVkLml0ZW1zKCkgaWYgdiBub3QgaW4gKE5vbmUsICIiLCBbXSwge30sICgpKX0KCiAgICAjIE5vcm1hbGl6ZSBJTURiIHJhdGluZyB0byBhIHN0cmluZyB3aXRoIDEgZGVjaW1hbCBwbGFjZQogICAgcmF0aW5nID0gY2xlYW5lZC5nZXQoImltZGJSYXRpbmciKQogICAgaWYgcmF0aW5nIG5vdCBpbiAoTm9uZSwgIiIpOgogICAgICAgIHRyeToKICAgICAgICAgICAgY2xlYW5lZFsiaW1kYlJhdGluZyJdID0gZiJ7ZmxvYXQocmF0aW5nKTouMWZ9IgogICAgICAgIGV4Y2VwdCAoVHlwZUVycm9yLCBWYWx1ZUVycm9yKToKICAgICAgICAgICAgIyBLZWVwIG9yaWdpbmFsIHZhbHVlIGlmIGl0IGNhbm5vdCBiZSBwYXJzZWQKICAgICAgICAgICAgcGFzcwoKICAgIGltZGJfaWQgPSBjbGVhbmVkLmdldCgiaWQiLCAiIikKICAgICMgaWYgaWQgZG9lcyBub3Qgc3RhcnQgd2l0aCB0dCwgcmV0dXJuIE5vbmUKICAgIGlmIG5vdCBpbWRiX2lkLnN0YXJ0c3dpdGgoInR0Iik6CiAgICAgICAgcmV0dXJuIE5vbmUKICAgICMgVXNlIE1ldGFodWIgbG9nbyBvbmx5IHdoZW4gbm8gbGFuZ3VhZ2UtYXdhcmUgbG9nbyB3YXMgc2V0IChlLmcuIGZyb20gVE1EQikKICAgIGlmIG5vdCBjbGVhbmVkLmdldCgibG9nbyIpOgogICAgICAgIGNsZWFuZWRbImxvZ28iXSA9IGYiaHR0cHM6Ly9saXZlLm1ldGFodWIuc3BhY2UvbG9nby9tZWRpdW0ve2ltZGJfaWR9L2ltZyIKICAgIHJldHVybiBjbGVhbmVkCgoKY2xhc3MgQ2F0YWxvZ1NlcnZpY2U6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgcGFzcwoKICAgIGFzeW5jIGRlZiBnZXRfY2F0YWxvZygKICAgICAgICBzZWxmLCB0b2tlbjogc3RyLCBjb250ZW50X3R5cGU6IHN0ciwgY2F0YWxvZ19pZDogc3RyCiAgICApIC0+IHR1cGxlW2RpY3Rbc3RyLCBBbnldLCBkaWN0W3N0ciwgQW55XV06CiAgICAgICAgIiIiCiAgICAgICAgR2V0IGNhdGFsb2cgcmVjb21tZW5kYXRpb25zLgoKICAgICAgICBBcmdzOgogICAgICAgICAgICB0b2tlbjogVXNlciB0b2tlbgogICAgICAgICAgICBjb250ZW50X3R5cGU6IENvbnRlbnQgdHlwZSAobW92aWUvc2VyaWVzKQogICAgICAgICAgICBjYXRhbG9nX2lkOiBDYXRhbG9nIElEICh3YXRjaGx5LnJlYywgd2F0Y2hseS5jcmVhdG9ycywgd2F0Y2hseS50aGVtZS4qLCBldGMuKQoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBUdXBsZSBvZiAocmVjb21tZW5kYXRpb25zIGRpY3QsIHJlc3BvbnNlIGhlYWRlcnMgZGljdCkKICAgICAgICAiIiIKICAgICAgICAjIFZhbGlkYXRlIGlucHV0cwogICAgICAgIHNlbGYuX3ZhbGlkYXRlX2lucHV0cyh0b2tlbiwgY29udGVudF90eXBlLCBjYXRhbG9nX2lkKQoKICAgICAgICAjIFByZXBhcmUgcmVzcG9uc2UgaGVhZGVycwoKICAgICAgICBoZWFkZXJzOiBkaWN0W3N0ciwgQW55XSA9IHsKICAgICAgICAgICAgIkFjY2Vzcy1Db250cm9sLUFsbG93LU9yaWdpbiI6ICIqIiwKICAgICAgICAgICAgIkFjY2Vzcy1Db250cm9sLUFsbG93LUhlYWRlcnMiOiAiKiIsCiAgICAgICAgICAgICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIsCiAgICAgICAgICAgICJDYWNoZS1Db250cm9sIjogKAogICAgICAgICAgICAgICAgZiJwdWJsaWMsIG1heC1hZ2U9e3NldHRpbmdzLkNBVEFMT0dfQ0FDSEVfVFRMfSwiICJzdGFsZS13aGlsZS1yZXZhbGlkYXRlPTM2MDAsIHN0YWxlLWlmLWVycm9yPTE4MDAiCiAgICAgICAgICAgICksCiAgICAgICAgfQoKICAgICAgICBsb2dnZXIuaW5mbyhmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX0uLi5dIEZldGNoaW5nIGNhdGFsb2cgZm9yIHtjb250ZW50X3R5cGV9IHdpdGggaWQge2NhdGFsb2dfaWR9IikKCiAgICAgICAgIyBHZXQgY3JlZGVudGlhbHMKICAgICAgICBjcmVkZW50aWFscyA9IGF3YWl0IHRva2VuX3N0b3JlLmdldF91c2VyX2RhdGEodG9rZW4pCiAgICAgICAgaWYgbm90IGNyZWRlbnRpYWxzOgogICAgICAgICAgICBsb2dnZXIuZXJyb3IoIk5vIGNyZWRlbnRpYWxzIGZvdW5kIGZvciB0b2tlbiIpCiAgICAgICAgICAgIHJhaXNlIEhUVFBFeGNlcHRpb24oCiAgICAgICAgICAgICAgICBzdGF0dXNfY29kZT00MDEsCiAgICAgICAgICAgICAgICBkZXRhaWw9IkludmFsaWQgb3IgZXhwaXJlZCB0b2tlbi4gUGxlYXNlIHJlY29uZmlndXJlIHRoZSBhZGRvbi4iLAogICAgICAgICAgICApCgogICAgICAgICMgVHJpZ2dlciBsYXp5IHVwZGF0ZSBpZiBuZWVkZWQKICAgICAgICBpZiBzZXR0aW5ncy5BVVRPX1VQREFURV9DQVRBTE9HUzoKICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9Li4uXSBUcmlnZ2VyaW5nIGF1dG8gdXBkYXRlIGZvciB0b2tlbiIpCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGF3YWl0IGNhdGFsb2dfdXBkYXRlci50cmlnZ2VyX3VwZGF0ZSh0b2tlbiwgY3JlZGVudGlhbHMpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcihmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX0uLi5dIEZhaWxlZCB0byB0cmlnZ2VyIGF1dG8gdXBkYXRlOiB7ZX0iKQogICAgICAgICAgICAgICAgIyBjb250aW51ZSB3aXRoIHRoZSByZXF1ZXN0IGV2ZW4gaWYgdGhlIGF1dG8gdXBkYXRlIGZhaWxzCiAgICAgICAgICAgICAgICBwYXNzCgogICAgICAgIGJ1bmRsZSA9IFN0cmVtaW9CdW5kbGUoKQogICAgICAgIHVzZXJfc2V0dGluZ3MgPSBOb25lCiAgICAgICAgc3RhbGVfZGF0YSA9IE5vbmUKCiAgICAgICAgdHJ5OgogICAgICAgICAgICAjIGdldCBjYWNoZWQgY2F0YWxvZwogICAgICAgICAgICBjYWNoZWRfcmVzdWx0ID0gYXdhaXQgdXNlcl9jYWNoZS5nZXRfY2F0YWxvZyh0b2tlbiwgY29udGVudF90eXBlLCBjYXRhbG9nX2lkKQoKICAgICAgICAgICAgaWYgY2FjaGVkX3Jlc3VsdDoKICAgICAgICAgICAgICAgIGRhdGEsIGNyZWF0ZWRfYXQgPSBjYWNoZWRfcmVzdWx0CiAgICAgICAgICAgICAgICBhZ2UgPSBpbnQodGltZS50aW1lKCkpIC0gY3JlYXRlZF9hdAoKICAgICAgICAgICAgICAgICMgSWYgZGF0YSBpcyBmcmVzaCBlbm91Z2ggKHdpdGhpbiByZWZyZXNoIGludGVydmFsKSwgcmV0dXJuIGl0CiAgICAgICAgICAgICAgICBpZiBhZ2UgPCBzZXR0aW5ncy5DQVRBTE9HX1JFRlJFU0hfSU5URVJWQUxfU0VDT05EUzoKICAgICAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9Li4uXSBVc2luZyBjYWNoZWQgY2F0YWxvZyBmb3Ige2NvbnRlbnRfdHlwZX0ve2NhdGFsb2dfaWR9IikKICAgICAgICAgICAgICAgICAgICAjIFRyeSB0byBleHRyYWN0IHNldHRpbmdzIGZyb20gY3JlZGVudGlhbHMgZm9yIHNodWZmbGluZywgZXZlbiBvbiBjYWNoZWQgcGF0aAogICAgICAgICAgICAgICAgICAgIHVzZXJfc2V0dGluZ3MgPSBzZWxmLl9leHRyYWN0X3NldHRpbmdzKGNyZWRlbnRpYWxzKQogICAgICAgICAgICAgICAgICAgIG1ldGFfZGF0YSA9IGRhdGFbIm1ldGFzIl0KICAgICAgICAgICAgICAgICAgICBtZXRhX2RhdGEgPSBzaHVmZmxlX2RhdGFfaWZfbmVlZGVkKHVzZXJfc2V0dGluZ3MsIGNhdGFsb2dfaWQsIG1ldGFfZGF0YSkKICAgICAgICAgICAgICAgICAgICBkYXRhWyJtZXRhcyJdID0gbWV0YV9kYXRhCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGRhdGEsIGhlYWRlcnMKCiAgICAgICAgICAgICAgICAjIElmIGRhdGEgaXMgc3RhbGUsIGtlZXAgaXQgZm9yIGZhbGxiYWNrCiAgICAgICAgICAgICAgICBzdGFsZV9kYXRhID0gZGF0YQogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oCiAgICAgICAgICAgICAgICAgICAgZiJbe3JlZGFjdF90b2tlbih0b2tlbil9Li4uXSBDYXRhbG9nIGlzIHN0YWxlIChhZ2U6IHthZ2V9cykgZm9yIHtjb250ZW50X3R5cGV9L3tjYXRhbG9nX2lkfSwiCiAgICAgICAgICAgICAgICAgICAgInJlZnJlc2hpbmcuLi4iCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBsb2dnZXIuaW5mbygKICAgICAgICAgICAgICAgICAgICBmIlt7cmVkYWN0X3Rva2VuKHRva2VuKX0uLi5dIENhdGFsb2cgbm90IGNhY2hlZCBmb3Ige2NvbnRlbnRfdHlwZX0ve2NhdGFsb2dfaWR9LCBidWlsZGluZyBmcm9tIgogICAgICAgICAgICAgICAgICAgICIgc2NyYXRjaCIKICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICMgUmVzb2x2ZSBhdXRoIGFuZCBzZXR0aW5ncwogICAgICAgICAgICBhdXRoX2tleSA9IGF3YWl0IHNlbGYuX3Jlc29sdmVfYXV0aChidW5kbGUsIGNyZWRlbnRpYWxzLCB0b2tlbikKICAgICAgICAgICAgdXNlcl9zZXR0aW5ncyA9IHNlbGYuX2V4dHJhY3Rfc2V0dGluZ3MoY3JlZGVudGlhbHMpCgogICAgICAgICAgICBsYW5ndWFnZSA9IHVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UgaWYgdXNlcl9zZXR0aW5ncyBlbHNlICJlbi1VUyIKCiAgICAgICAgICAgICMgVHJ5IHRvIGdldCBjYWNoZWQgbGlicmFyeSBpdGVtcyBmaXJzdAogICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgdXNlcl9jYWNoZS5nZXRfbGlicmFyeV9pdGVtcyh0b2tlbikKCiAgICAgICAgICAgIGlmIGxpYnJhcnlfaXRlbXM6CiAgICAgICAgICAgICAgICBsb2dnZXIuZGVidWcoZiJbe3JlZGFjdF90b2tlbih0b2tlbil9Li4uXSBVc2luZyBjYWNoZWQgbGlicmFyeSBpdGVtcyIpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAjIEZldGNoIGxpYnJhcnkgaWYgbm90IGNhY2hlZAogICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJbe3JlZGFjdF90b2tlbih0b2tlbil9Li4uXSBMaWJyYXJ5IGl0ZW1zIG5vdCBjYWNoZWQsIGZldGNoaW5nIGZyb20gU3RyZW1pbyIpCiAgICAgICAgICAgICAgICBsaWJyYXJ5X2l0ZW1zID0gYXdhaXQgYnVuZGxlLmxpYnJhcnkuZ2V0X2xpYnJhcnlfaXRlbXMoYXV0aF9rZXkpCiAgICAgICAgICAgICAgICAjIENhY2hlIGl0IGZvciBmdXR1cmUgdXNlCiAgICAgICAgICAgICAgICBhd2FpdCB1c2VyX2NhY2hlLnNldF9saWJyYXJ5X2l0ZW1zKHRva2VuLCBsaWJyYXJ5X2l0ZW1zKQoKICAgICAgICAgICAgc2VydmljZXMgPSBzZWxmLl9pbml0aWFsaXplX3NlcnZpY2VzKGxhbmd1YWdlLCB1c2VyX3NldHRpbmdzKQogICAgICAgICAgICBpbnRlZ3JhdGlvbl9zZXJ2aWNlOiBQcm9maWxlSW50ZWdyYXRpb24gPSBzZXJ2aWNlc1siaW50ZWdyYXRpb24iXQoKICAgICAgICAgICAgIyBUcnkgdG8gZ2V0IGNhY2hlZCBwcm9maWxlIGFuZCB3YXRjaGVkIHNldHMKICAgICAgICAgICAgY2FjaGVkX2RhdGEgPSBhd2FpdCB1c2VyX2NhY2hlLmdldF9wcm9maWxlX2FuZF93YXRjaGVkX3NldHModG9rZW4sIGNvbnRlbnRfdHlwZSkKCiAgICAgICAgICAgIGlmIGNhY2hlZF9kYXRhOgogICAgICAgICAgICAgICAgIyBVc2UgY2FjaGVkIHByb2ZpbGUgYW5kIHdhdGNoZWQgc2V0cwogICAgICAgICAgICAgICAgcHJvZmlsZSwgd2F0Y2hlZF90bWRiLCB3YXRjaGVkX2ltZGIgPSBjYWNoZWRfZGF0YQogICAgICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfS4uLl0gVXNpbmcgY2FjaGVkIHByb2ZpbGUgYW5kIHdhdGNoZWQgc2V0cyBmb3Ige2NvbnRlbnRfdHlwZX0iKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgIyBCdWlsZCBwcm9maWxlIGlmIG5vdCBjYWNoZWQKICAgICAgICAgICAgICAgIGxvZ2dlci5pbmZvKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfS4uLl0gUHJvZmlsZSBub3QgY2FjaGVkIGZvciB7Y29udGVudF90eXBlfSwgYnVpbGRpbmcgZnJvbSBsaWJyYXJ5IikKICAgICAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICAgICBwcm9maWxlLAogICAgICAgICAgICAgICAgICAgIHdhdGNoZWRfdG1kYiwKICAgICAgICAgICAgICAgICAgICB3YXRjaGVkX2ltZGIsCiAgICAgICAgICAgICAgICApID0gYXdhaXQgY2FjaGVfcHJvZmlsZV9hbmRfd2F0Y2hlZF9zZXRzKAogICAgICAgICAgICAgICAgICAgIHRva2VuLAogICAgICAgICAgICAgICAgICAgIGNvbnRlbnRfdHlwZSwKICAgICAgICAgICAgICAgICAgICBpbnRlZ3JhdGlvbl9zZXJ2aWNlLAogICAgICAgICAgICAgICAgICAgIGxpYnJhcnlfaXRlbXMsCiAgICAgICAgICAgICAgICAgICAgYnVuZGxlLAogICAgICAgICAgICAgICAgICAgIGF1dGhfa2V5LAogICAgICAgICAgICAgICAgKQoKICAgICAgICAgICAgd2hpdGVsaXN0ID0gYXdhaXQgaW50ZWdyYXRpb25fc2VydmljZS5nZXRfZ2VucmVfd2hpdGVsaXN0KHByb2ZpbGUsIGNvbnRlbnRfdHlwZSkgaWYgcHJvZmlsZSBlbHNlIHNldCgpCgogICAgICAgICAgICAjIFJvdXRlIHRvIGFwcHJvcHJpYXRlIHJlY29tbWVuZGF0aW9uIHNlcnZpY2UKICAgICAgICAgICAgcmVjb21tZW5kYXRpb25zID0gYXdhaXQgc2VsZi5fZ2V0X3JlY29tbWVuZGF0aW9ucygKICAgICAgICAgICAgICAgIGNhdGFsb2dfaWQ9Y2F0YWxvZ19pZCwKICAgICAgICAgICAgICAgIGNvbnRlbnRfdHlwZT1jb250ZW50X3R5cGUsCiAgICAgICAgICAgICAgICBzZXJ2aWNlcz1zZXJ2aWNlcywKICAgICAgICAgICAgICAgIHByb2ZpbGU9cHJvZmlsZSwKICAgICAgICAgICAgICAgIHdhdGNoZWRfdG1kYj13YXRjaGVkX3RtZGIsCiAgICAgICAgICAgICAgICB3YXRjaGVkX2ltZGI9d2F0Y2hlZF9pbWRiLAogICAgICAgICAgICAgICAgd2hpdGVsaXN0PXdoaXRlbGlzdCwKICAgICAgICAgICAgICAgIGxpYnJhcnlfaXRlbXM9bGlicmFyeV9pdGVtcywKICAgICAgICAgICAgICAgIGxpbWl0PURFRkFVTFRfQ0FUQUxPR19MSU1JVCwKICAgICAgICAgICAgICAgIHVzZXJfc2V0dGluZ3M9dXNlcl9zZXR0aW5ncywKICAgICAgICAgICAgKQoKICAgICAgICAgICAgIyBQYWQgaWYgbmVlZGVkIHRvIG1lZXQgbWluaW11bSBvZiA4IGl0ZW1zCiAgICAgICAgICAgICMgIyBUT0RPOiBUaGlzIGlzIHJpc2t5IGJlY2F1c2UgaXQgY2FuIGZldGNoIHRvbyBtYW55IHVucmVsYXRlZCBpdGVtcy4KICAgICAgICAgICAgaWYgcmVjb21tZW5kYXRpb25zIGFuZCBsZW4ocmVjb21tZW5kYXRpb25zKSA8IERFRkFVTFRfTUlOX0lURU1TOgogICAgICAgICAgICAgICAgcmVjb21tZW5kYXRpb25zID0gYXdhaXQgcGFkX3RvX21pbigKICAgICAgICAgICAgICAgICAgICBjb250ZW50X3R5cGUsCiAgICAgICAgICAgICAgICAgICAgcmVjb21tZW5kYXRpb25zLAogICAgICAgICAgICAgICAgICAgIERFRkFVTFRfTUlOX0lURU1TLAogICAgICAgICAgICAgICAgICAgIHNlcnZpY2VzWyJ0bWRiIl0sCiAgICAgICAgICAgICAgICAgICAgdXNlcl9zZXR0aW5ncywKICAgICAgICAgICAgICAgICAgICB3YXRjaGVkX3RtZGIsCiAgICAgICAgICAgICAgICAgICAgd2F0Y2hlZF9pbWRiLAogICAgICAgICAgICAgICAgKQoKICAgICAgICAgICAgbG9nZ2VyLmluZm8oZiJSZXR1cm5pbmcge2xlbihyZWNvbW1lbmRhdGlvbnMpfSBpdGVtcyBmb3Ige2NvbnRlbnRfdHlwZX0iKQoKICAgICAgICAgICAgIyBDbGVhbiBhbmQgZm9ybWF0IG1ldGFkYXRhCiAgICAgICAgICAgIGNsZWFuZWQgPSBbX2NsZWFuX21ldGEobSkgZm9yIG0gaW4gcmVjb21tZW5kYXRpb25zXQogICAgICAgICAgICBjbGVhbmVkID0gW20gZm9yIG0gaW4gY2xlYW5lZCBpZiBtIGlzIG5vdCBOb25lXQoKICAgICAgICAgICAgY2xlYW5lZCA9IHNodWZmbGVfZGF0YV9pZl9uZWVkZWQodXNlcl9zZXR0aW5ncywgY2F0YWxvZ19pZCwgY2xlYW5lZCkKCiAgICAgICAgICAgIGRhdGEgPSB7Im1ldGFzIjogY2xlYW5lZH0KICAgICAgICAgICAgIyBpZiBjYXRhbG9nIGRhdGEgaXMgbm90IGVtcHR5LCBzZXQgdGhlIGNhY2hlIHdpdGggU1RBTEVfVFRMICg3IGRheXMpCiAgICAgICAgICAgICMgVGhpcyBlbnN1cmVzIHdlIGhhdmUgZmFsbGJhY2sgZGF0YSBhdmFpbGFibGUgaWYgdGhlIG5leHQgcmVmcmVzaCBmYWlscwogICAgICAgICAgICBpZiBjbGVhbmVkOgogICAgICAgICAgICAgICAgYXdhaXQgdXNlcl9jYWNoZS5zZXRfY2F0YWxvZyh0b2tlbiwgY29udGVudF90eXBlLCBjYXRhbG9nX2lkLCBkYXRhLCBzZXR0aW5ncy5DQVRBTE9HX1NUQUxFX1RUTCkKCiAgICAgICAgICAgIHJldHVybiBkYXRhLCBoZWFkZXJzCgogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiW3tyZWRhY3RfdG9rZW4odG9rZW4pfS4uLl0gRmFpbGVkIHRvIGdlbmVyYXRlIGNhdGFsb2c6IHtlfSIpCgogICAgICAgICAgICAjIEZhbGxiYWNrIDE6IFJldHVybiBTdGFsZSBEYXRhIGlmIGF2YWlsYWJsZQogICAgICAgICAgICBpZiBzdGFsZV9kYXRhOgogICAgICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoCiAgICAgICAgICAgICAgICAgICAgZiJbe3JlZGFjdF90b2tlbih0b2tlbil9Li4uXSBTZXJ2aW5nIHN0YWxlIGNvbnRlbnQgZm9yIHtjb250ZW50X3R5cGV9L3tjYXRhbG9nX2lkfSBkdWUgdG8gZXJyb3IiCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAjIFNodWZmbGUgc3RhbGUgZGF0YSB0b28gaWYgbmVlZGVkCiAgICAgICAgICAgICAgICB1c2VyX3NldHRpbmdzID0gdXNlcl9zZXR0aW5ncyBvciBzZWxmLl9leHRyYWN0X3NldHRpbmdzKGNyZWRlbnRpYWxzKQogICAgICAgICAgICAgICAgbWV0YV9kYXRhID0gc3RhbGVfZGF0YS5nZXQoIm1ldGFzIiwgW10pCiAgICAgICAgICAgICAgICBtZXRhX2RhdGEgPSBzaHVmZmxlX2RhdGFfaWZfbmVlZGVkKHVzZXJfc2V0dGluZ3MsIGNhdGFsb2dfaWQsIG1ldGFfZGF0YSkKICAgICAgICAgICAgICAgIHN0YWxlX2RhdGFbIm1ldGFzIl0gPSBtZXRhX2RhdGEKICAgICAgICAgICAgICAgIHJldHVybiBzdGFsZV9kYXRhLCBoZWFkZXJzCgogICAgICAgICAgICAjIEZhbGxiYWNrIDI6IFJldHVybiBFbXB0eSAocHJldmVudHMgNTAwIGVycm9yKQogICAgICAgICAgICByZXR1cm4geyJtZXRhcyI6IFtdfSwgaGVhZGVycwoKICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICBhd2FpdCBidW5kbGUuY2xvc2UoKQoKICAgIGRlZiBfdmFsaWRhdGVfaW5wdXRzKHNlbGYsIHRva2VuOiBzdHIsIGNvbnRlbnRfdHlwZTogc3RyLCBjYXRhbG9nX2lkOiBzdHIpIC0+IE5vbmU6CiAgICAgICAgaWYgbm90IHRva2VuOgogICAgICAgICAgICByYWlzZSBIVFRQRXhjZXB0aW9uKAogICAgICAgICAgICAgICAgc3RhdHVzX2NvZGU9NDAwLAogICAgICAgICAgICAgICAgZGV0YWlsPSJNaXNzaW5nIGNyZWRlbnRpYWxzIHRva2VuLiBQbGVhc2Ugb3BlbiBXYXRjaGx5IGZyb20gYSBjb25maWd1cmVkIG1hbmlmZXN0IFVSTC4iLAogICAgICAgICAgICApCgogICAgICAgIGlmIGNvbnRlbnRfdHlwZSBub3QgaW4gWyJtb3ZpZSIsICJzZXJpZXMiXToKICAgICAgICAgICAgbG9nZ2VyLndhcm5pbmcoZiJJbnZhbGlkIHR5cGU6IHtjb250ZW50X3R5cGV9IikKICAgICAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbihzdGF0dXNfY29kZT00MDAsIGRldGFpbD0iSW52YWxpZCB0eXBlLiBVc2UgJ21vdmllJyBvciAnc2VyaWVzJyIpCgogICAgICAgICMgU3VwcG9ydGVkIElEcwogICAgICAgIHN1cHBvcnRlZF9iYXNlID0gWwogICAgICAgICAgICAid2F0Y2hseS5yZWMiLAogICAgICAgICAgICAid2F0Y2hseS5jcmVhdG9ycyIsCiAgICAgICAgICAgICJ3YXRjaGx5LmFsbC5sb3ZlZCIsCiAgICAgICAgICAgICJ3YXRjaGx5Lmxpa2VkLmFsbCIsCiAgICAgICAgXQogICAgICAgIHN1cHBvcnRlZF9wcmVmaXhlcyA9ICgid2F0Y2hseS50aGVtZS4iLCAid2F0Y2hseS5sb3ZlZC4iLCAid2F0Y2hseS53YXRjaGVkLiIpCiAgICAgICAgaWYgY2F0YWxvZ19pZCBub3QgaW4gc3VwcG9ydGVkX2Jhc2UgYW5kIG5vdCBhbnkoY2F0YWxvZ19pZC5zdGFydHN3aXRoKHApIGZvciBwIGluIHN1cHBvcnRlZF9wcmVmaXhlcyk6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiSW52YWxpZCBpZDoge2NhdGFsb2dfaWR9IikKICAgICAgICAgICAgcmFpc2UgSFRUUEV4Y2VwdGlvbigKICAgICAgICAgICAgICAgIHN0YXR1c19jb2RlPTQwMCwKICAgICAgICAgICAgICAgIGRldGFpbD0oCiAgICAgICAgICAgICAgICAgICAgIkludmFsaWQgaWQuIFN1cHBvcnRlZDogJ3dhdGNobHkucmVjJywgJ3dhdGNobHkuY3JlYXRvcnMnLCAiCiAgICAgICAgICAgICAgICAgICAgIid3YXRjaGx5LnRoZW1lLjxwYXJhbXM+JywgJ3dhdGNobHkuYWxsLmxvdmVkJywgJ3dhdGNobHkubGlrZWQuYWxsJyIKICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICkKCiAgICBhc3luYyBkZWYgX3Jlc29sdmVfYXV0aChzZWxmLCBidW5kbGU6IFN0cmVtaW9CdW5kbGUsIGNyZWRlbnRpYWxzOiBkaWN0LCB0b2tlbjogc3RyKSAtPiBzdHI6CiAgICAgICAgYXV0aF9rZXkgPSBjcmVkZW50aWFscy5nZXQoImF1dGhLZXkiKQoKICAgICAgICAjIFRyYWt0IGFjY291bnRzIHVzZSBhIFRyYWt0IGFjY2VzcyB0b2tlbiBzdG9yZWQgYXMgYXV0aEtleS4KICAgICAgICAjIFNraXAgU3RyZW1pbyBzZXNzaW9uIHZhbGlkYXRpb24gZW50aXJlbHkgZm9yIHRoZXNlIGFjY291bnRzLgogICAgICAgIGlmIGNyZWRlbnRpYWxzLmdldCgiYXV0aF9wcm92aWRlciIpID09ICJ0cmFrdCI6CiAgICAgICAgICAgIGlmIG5vdCBhdXRoX2tleToKICAgICAgICAgICAgICAgIHJhaXNlIEhUVFBFeGNlcHRpb24oc3RhdHVzX2NvZGU9NDAxLCBkZXRhaWw9IlRyYWt0IHNlc3Npb24gZXhwaXJlZC4gUGxlYXNlIHJlY29uZmlndXJlLiIpCiAgICAgICAgICAgIHJldHVybiBhdXRoX2tleQoKICAgICAgICBlbWFpbCA9IGNyZWRlbnRpYWxzLmdldCgiZW1haWwiKQogICAgICAgIHBhc3N3b3JkID0gY3JlZGVudGlhbHMuZ2V0KCJwYXNzd29yZCIpCgogICAgICAgICMgVmFsaWRhdGUgZXhpc3RpbmcgYXV0aCBrZXkKICAgICAgICBpc192YWxpZCA9IEZhbHNlCiAgICAgICAgaWYgYXV0aF9rZXk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGF3YWl0IGJ1bmRsZS5hdXRoLmdldF91c2VyX2luZm8oYXV0aF9rZXkpCiAgICAgICAgICAgICAgICBpc192YWxpZCA9IFRydWUKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiRmFpbGVkIHRvIHZhbGlkYXRlIGF1dGgga2V5IGR1cmluZyBjYXRhbG9nIGZldGNoOiB7ZX0iKQogICAgICAgICAgICAgICAgcGFzcwoKICAgICAgICAjIFRyeSB0byByZWZyZXNoIGlmIGludmFsaWQKICAgICAgICBpZiBub3QgaXNfdmFsaWQgYW5kIGVtYWlsIGFuZCBwYXNzd29yZDoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgYXV0aF9rZXkgPSBhd2FpdCBidW5kbGUuYXV0aC5sb2dpbihlbWFpbCwgcGFzc3dvcmQpCiAgICAgICAgICAgICAgICBjcmVkZW50aWFsc1siYXV0aEtleSJdID0gYXV0aF9rZXkKICAgICAgICAgICAgICAgICMgVXBkYXRlIHRva2VuIHN0b3JlIHdpdGggcmVmcmVzaGVkIGNyZWRlbnRpYWxzCiAgICAgICAgICAgICAgICBhd2FpdCB0b2tlbl9zdG9yZS51cGRhdGVfdXNlcl9kYXRhKHRva2VuLCBjcmVkZW50aWFscykKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGYiRmFpbGVkIHRvIHJlZnJlc2ggYXV0aCBrZXkgZHVyaW5nIGNhdGFsb2cgZmV0Y2g6IHtlfSIpCgogICAgICAgIGlmIG5vdCBhdXRoX2tleToKICAgICAgICAgICAgbG9nZ2VyLmVycm9yKCJObyBhdXRoIGtleSBmb3VuZCBkdXJpbmcgY2F0YWxvZyBmZXRjaCIpCiAgICAgICAgICAgIHJhaXNlIEhUVFBFeGNlcHRpb24oc3RhdHVzX2NvZGU9NDAxLCBkZXRhaWw9IlN0cmVtaW8gc2Vzc2lvbiBleHBpcmVkLiBQbGVhc2UgcmVjb25maWd1cmUuIikKCiAgICAgICAgcmV0dXJuIGF1dGhfa2V5CgogICAgZGVmIF9leHRyYWN0X3NldHRpbmdzKHNlbGYsIGNyZWRlbnRpYWxzOiBkaWN0KSAtPiBVc2VyU2V0dGluZ3M6CiAgICAgICAgc2V0dGluZ3NfZGljdCA9IGNyZWRlbnRpYWxzLmdldCgic2V0dGluZ3MiLCB7fSkKICAgICAgICByZXR1cm4gVXNlclNldHRpbmdzKCoqc2V0dGluZ3NfZGljdCkgaWYgc2V0dGluZ3NfZGljdCBlbHNlIGdldF9kZWZhdWx0X3NldHRpbmdzKCkKCiAgICBhc3luYyBkZWYgX2dldF90cmVuZGluZ19mYWxsYmFjaygKICAgICAgICBzZWxmLCBjb250ZW50X3R5cGU6IHN0ciwgbGltaXQ6IGludCA9IDIwLCB1c2VyX3NldHRpbmdzOiBVc2VyU2V0dGluZ3MgfCBOb25lID0gTm9uZQogICAgKSAtPiBsaXN0W2RpY3Rbc3RyLCBBbnldXToKICAgICAgICAiIiJHZXQgdHJlbmRpbmcgaXRlbXMgZm9yIG5ldyB1c2VycyB3aXRob3V0IHByb2ZpbGVzLiIiIgogICAgICAgIGZyb20gYXBwLnNlcnZpY2VzLnJlY29tbWVuZGF0aW9uLnV0aWxzIGltcG9ydCBjb250ZW50X3R5cGVfdG9fbXR5cGUKCiAgICAgICAgbXR5cGUgPSBjb250ZW50X3R5cGVfdG9fbXR5cGUoY29udGVudF90eXBlKQogICAgICAgIHRtZGJfa2V5ID0gcmVzb2x2ZV90bWRiX2FwaV9rZXkodXNlcl9zZXR0aW5ncykKICAgICAgICBsYW5ndWFnZSA9IHVzZXJfc2V0dGluZ3MubGFuZ3VhZ2UgaWYgdXNlcl9zZXR0aW5ncyBlbHNlICJlbi1VUyIKICAgICAgICB0bWRiX3NlcnZpY2UgPSBnZXRfdG1kYl9zZXJ2aWNlKGxhbmd1YWdlPWxhbmd1YWdlLCBhcGlfa2V5PXRtZGJfa2V5KQoKICAgICAgICB0cnk6CiAgICAgICAgICAgICMgRmV0Y2ggdHJlbmRpbmcgd2VlawogICAgICAgICAgICB0cmVuZGluZyA9IGF3YWl0IHRtZGJfc2VydmljZS5nZXRfdHJlbmRpbmcobXR5cGUsICJ3ZWVrIikKICAgICAgICAgICAgaXRlbXMgPSB0cmVuZGluZy5nZXQoInJlc3VsdHMiLCBbXSkKCiAgICAgICAgICAgICMgRW5yaWNoIG1ldGFkYXRhCiAgICAgICAgICAgIGZyb20gYXBwLnNlcnZpY2VzLnJlY29tbWVuZGF0aW9uLm1ldGFkYXRhIGltcG9ydCBSZWNvbW1lbmRhdGlvbk1ldGFkYXRhCgogICAgICAgICAgICByZXR1cm4gYXdhaXQgUmVjb21tZW5kYXRpb25NZXRhZGF0YS5mZXRjaF9iYXRjaCh0bWRiX3NlcnZpY2UsIGl0ZW1zLCBjb250ZW50X3R5cGUsIHVzZXJfc2V0dGluZ3M9Tm9uZSkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiRmFpbGVkIHRvIGZldGNoIHRyZW5kaW5nIGl0ZW1zOiB7ZX0iKQogICAgICAgICAgICByZXR1cm4gW10KCiAgICBkZWYgX2luaXRpYWxpemVfc2VydmljZXMoc2VsZiwgbGFuZ3VhZ2U6IHN0ciwgdXNlcl9zZXR0aW5nczogVXNlclNldHRpbmdzKSAtPiBkaWN0W3N0ciwgQW55XToKICAgICAgICB0bWRiX2tleSA9IHJlc29sdmVfdG1kYl9hcGlfa2V5KHVzZXJfc2V0dGluZ3MpCiAgICAgICAgdG1kYl9zZXJ2aWNlID0gZ2V0X3RtZGJfc2VydmljZShsYW5ndWFnZT1sYW5ndWFnZSwgYXBpX2tleT10bWRiX2tleSkKICAgICAgICByZXR1cm4gewogICAgICAgICAgICAidG1kYiI6IHRtZGJfc2VydmljZSwKICAgICAgICAgICAgImludGVncmF0aW9uIjogUHJvZmlsZUludGVncmF0aW9uKGxhbmd1YWdlPWxhbmd1YWdlLCB0bWRiX2FwaV9rZXk9dG1kYl9rZXkpLAogICAgICAgICAgICAiaXRlbSI6IEl0ZW1CYXNlZFNlcnZpY2UodG1kYl9zZXJ2aWNlLCB1c2VyX3NldHRpbmdzKSwKICAgICAgICAgICAgInRoZW1lIjogVGhlbWVCYXNlZFNlcnZpY2UodG1kYl9zZXJ2aWNlLCB1c2VyX3NldHRpbmdzKSwKICAgICAgICAgICAgInRvcF9waWNrcyI6IFRvcFBpY2tzU2VydmljZSh0bWRiX3NlcnZpY2UsIHVzZXJfc2V0dGluZ3MpLAogICAgICAgICAgICAiY3JlYXRvcnMiOiBDcmVhdG9yc1NlcnZpY2UodG1kYl9zZXJ2aWNlLCB1c2VyX3NldHRpbmdzKSwKICAgICAgICAgICAgImFsbF9iYXNlZCI6IEFsbEJhc2VkU2VydmljZSh0bWRiX3NlcnZpY2UsIHVzZXJfc2V0dGluZ3MpLAogICAgICAgIH0KCiAgICBhc3luYyBkZWYgX2dldF9yZWNvbW1lbmRhdGlvbnMoCiAgICAgICAgc2VsZiwKICAgICAgICBjYXRhbG9nX2lkOiBzdHIsCiAgICAgICAgY29udGVudF90eXBlOiBzdHIsCiAgICAgICAgc2VydmljZXM6IGRpY3Rbc3RyLCBBbnldLAogICAgICAgIHByb2ZpbGU6IFRhc3RlUHJvZmlsZSB8IE5vbmUsCiAgICAgICAgd2F0Y2hlZF90bWRiOiBzZXRbaW50XSwKICAgICAgICB3YXRjaGVkX2ltZGI6IHNldFtzdHJdLAogICAgICAgIHdoaXRlbGlzdDogc2V0W2ludF0sCiAgICAgICAgbGlicmFyeV9pdGVtczogZGljdCwKICAgICAgICBsaW1pdDogaW50LAogICAgICAgIHVzZXJfc2V0dGluZ3M6IFVzZXJTZXR0aW5ncyB8IE5vbmUgPSBOb25lLAogICAgKSAtPiBsaXN0W2RpY3Rbc3RyLCBBbnldXToKICAgICAgICAiIiJSb3V0ZSB0byBhcHByb3ByaWF0ZSByZWNvbW1lbmRhdGlvbiBzZXJ2aWNlIGJhc2VkIG9uIGNhdGFsb2cgSUQuIiIiCiAgICAgICAgIyBJdGVtLWJhc2VkIHJlY29tbWVuZGF0aW9ucwogICAgICAgIGlmIGFueSgKICAgICAgICAgICAgY2F0YWxvZ19pZC5zdGFydHN3aXRoKHApCiAgICAgICAgICAgIGZvciBwIGluICgKICAgICAgICAgICAgICAgICJ3YXRjaGx5LmxvdmVkLiIsCiAgICAgICAgICAgICAgICAid2F0Y2hseS53YXRjaGVkLiIsCiAgICAgICAgICAgICkKICAgICAgICApOgogICAgICAgICAgICAjIEV4dHJhY3QgaXRlbSBJRAogICAgICAgICAgICBpdGVtX2lkID0gcmUuc3ViKHIiXndhdGNobHlcLihsb3ZlZHx3YXRjaGVkKVwuIiwgIiIsIGNhdGFsb2dfaWQpCgogICAgICAgICAgICBpdGVtX3NlcnZpY2U6IEl0ZW1CYXNlZFNlcnZpY2UgPSBzZXJ2aWNlc1siaXRlbSJdCgogICAgICAgICAgICByZWNvbW1lbmRhdGlvbnMgPSBhd2FpdCBpdGVtX3NlcnZpY2UuZ2V0X3JlY29tbWVuZGF0aW9uc19mb3JfaXRlbSgKICAgICAgICAgICAgICAgIGl0ZW1faWQ9aXRlbV9pZCwKICAgICAgICAgICAgICAgIGNvbnRlbnRfdHlwZT1jb250ZW50X3R5cGUsCiAgICAgICAgICAgICAgICB3YXRjaGVkX3RtZGI9d2F0Y2hlZF90bWRiLAogICAgICAgICAgICAgICAgd2F0Y2hlZF9pbWRiPXdhdGNoZWRfaW1kYiwKICAgICAgICAgICAgICAgIGxpbWl0PWxpbWl0LAogICAgICAgICAgICAgICAgd2hpdGVsaXN0PXdoaXRlbGlzdCwKICAgICAgICAgICAgKQogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIkZvdW5kIHtsZW4ocmVjb21tZW5kYXRpb25zKX0gcmVjb21tZW5kYXRpb25zIGZvciBpdGVtIHtpdGVtX2lkfSIpCgogICAgICAgICMgVGhlbWUtYmFzZWQgcmVjb21tZW5kYXRpb25zCiAgICAgICAgZWxpZiBjYXRhbG9nX2lkLnN0YXJ0c3dpdGgoIndhdGNobHkudGhlbWUuIik6CiAgICAgICAgICAgIHRoZW1lX3NlcnZpY2U6IFRoZW1lQmFzZWRTZXJ2aWNlID0gc2VydmljZXNbInRoZW1lIl0KCiAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyA9IGF3YWl0IHRoZW1lX3NlcnZpY2UuZ2V0X3JlY29tbWVuZGF0aW9uc19mb3JfdGhlbWUoCiAgICAgICAgICAgICAgICB0aGVtZV9pZD1jYXRhbG9nX2lkLAogICAgICAgICAgICAgICAgY29udGVudF90eXBlPWNvbnRlbnRfdHlwZSwKICAgICAgICAgICAgICAgIHByb2ZpbGU9cHJvZmlsZSwKICAgICAgICAgICAgICAgIHdhdGNoZWRfdG1kYj13YXRjaGVkX3RtZGIsCiAgICAgICAgICAgICAgICB3YXRjaGVkX2ltZGI9d2F0Y2hlZF9pbWRiLAogICAgICAgICAgICAgICAgbGltaXQ9bGltaXQsCiAgICAgICAgICAgICAgICB3aGl0ZWxpc3Q9d2hpdGVsaXN0LAogICAgICAgICAgICApCiAgICAgICAgICAgIGxvZ2dlci5pbmZvKGYiRm91bmQge2xlbihyZWNvbW1lbmRhdGlvbnMpfSByZWNvbW1lbmRhdGlvbnMgZm9yIHRoZW1lIHtjYXRhbG9nX2lkfSIpCgogICAgICAgICMgQ3JlYXRvcnMtYmFzZWQgcmVjb21tZW5kYXRpb25zCiAgICAgICAgZWxpZiBjYXRhbG9nX2lkID09ICJ3YXRjaGx5LmNyZWF0b3JzIjoKICAgICAgICAgICAgY3JlYXRvcnNfc2VydmljZTogQ3JlYXRvcnNTZXJ2aWNlID0gc2VydmljZXNbImNyZWF0b3JzIl0KCiAgICAgICAgICAgIGlmIHByb2ZpbGU6CiAgICAgICAgICAgICAgICByZWNvbW1lbmRhdGlvbnMgPSBhd2FpdCBjcmVhdG9yc19zZXJ2aWNlLmdldF9yZWNvbW1lbmRhdGlvbnNfZnJvbV9jcmVhdG9ycygKICAgICAgICAgICAgICAgICAgICBwcm9maWxlPXByb2ZpbGUsCiAgICAgICAgICAgICAgICAgICAgY29udGVudF90eXBlPWNvbnRlbnRfdHlwZSwKICAgICAgICAgICAgICAgICAgICB3YXRjaGVkX3RtZGI9d2F0Y2hlZF90bWRiLAogICAgICAgICAgICAgICAgICAgIHdhdGNoZWRfaW1kYj13YXRjaGVkX2ltZGIsCiAgICAgICAgICAgICAgICAgICAgbGltaXQ9bGltaXQsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhmIk5vIHByb2ZpbGUgZm9yIGNyZWF0b3JzLCBzaG93aW5nIHRyZW5kaW5nIHtjb250ZW50X3R5cGV9IikKICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyA9IGF3YWl0IHNlbGYuX2dldF90cmVuZGluZ19mYWxsYmFjayhjb250ZW50X3R5cGUsIGxpbWl0LCB1c2VyX3NldHRpbmdzKQogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIkZvdW5kIHtsZW4ocmVjb21tZW5kYXRpb25zKX0gcmVjb21tZW5kYXRpb25zIGZyb20gY3JlYXRvcnMiKQoKICAgICAgICAjIFRvcCBwaWNrcwogICAgICAgIGVsaWYgY2F0YWxvZ19pZCA9PSAid2F0Y2hseS5yZWMiOgogICAgICAgICAgICBpZiBwcm9maWxlOgogICAgICAgICAgICAgICAgdG9wX3BpY2tzX3NlcnZpY2U6IFRvcFBpY2tzU2VydmljZSA9IHNlcnZpY2VzWyJ0b3BfcGlja3MiXQoKICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyA9IGF3YWl0IHRvcF9waWNrc19zZXJ2aWNlLmdldF90b3BfcGlja3MoCiAgICAgICAgICAgICAgICAgICAgcHJvZmlsZT1wcm9maWxlLAogICAgICAgICAgICAgICAgICAgIGNvbnRlbnRfdHlwZT1jb250ZW50X3R5cGUsCiAgICAgICAgICAgICAgICAgICAgbGlicmFyeV9pdGVtcz1saWJyYXJ5X2l0ZW1zLAogICAgICAgICAgICAgICAgICAgIHdhdGNoZWRfdG1kYj13YXRjaGVkX3RtZGIsCiAgICAgICAgICAgICAgICAgICAgd2F0Y2hlZF9pbWRiPXdhdGNoZWRfaW1kYiwKICAgICAgICAgICAgICAgICAgICBsaW1pdD1saW1pdCwKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGxvZ2dlci5pbmZvKGYiTm8gcHJvZmlsZSBmb3IgdG9wIHBpY2tzLCBzaG93aW5nIHRyZW5kaW5nIHtjb250ZW50X3R5cGV9IikKICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyA9IGF3YWl0IHNlbGYuX2dldF90cmVuZGluZ19mYWxsYmFjayhjb250ZW50X3R5cGUsIGxpbWl0LCB1c2VyX3NldHRpbmdzKQogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIkZvdW5kIHtsZW4ocmVjb21tZW5kYXRpb25zKX0gdG9wIHBpY2tzIGZvciB7Y29udGVudF90eXBlfSIpCgogICAgICAgICMgQmFzZWQgb24gd2hhdCB5b3UgbG92ZWQKICAgICAgICBlbGlmIGNhdGFsb2dfaWQgaW4gKCJ3YXRjaGx5LmFsbC5sb3ZlZCIsICJ3YXRjaGx5Lmxpa2VkLmFsbCIpOgogICAgICAgICAgICBpdGVtX3R5cGUgPSAibG92ZWQiIGlmIGNhdGFsb2dfaWQgPT0gIndhdGNobHkuYWxsLmxvdmVkIiBlbHNlICJsaWtlZCIKICAgICAgICAgICAgYWxsX2Jhc2VkX3NlcnZpY2U6IEFsbEJhc2VkU2VydmljZSA9IHNlcnZpY2VzWyJhbGxfYmFzZWQiXQogICAgICAgICAgICByZWNvbW1lbmRhdGlvbnMgPSBhd2FpdCBhbGxfYmFzZWRfc2VydmljZS5nZXRfcmVjb21tZW5kYXRpb25zX2Zyb21fYWxsX2l0ZW1zKAogICAgICAgICAgICAgICAgbGlicmFyeV9pdGVtcz1saWJyYXJ5X2l0ZW1zLAogICAgICAgICAgICAgICAgY29udGVudF90eXBlPWNvbnRlbnRfdHlwZSwKICAgICAgICAgICAgICAgIHdhdGNoZWRfdG1kYj13YXRjaGVkX3RtZGIsCiAgICAgICAgICAgICAgICB3YXRjaGVkX2ltZGI9d2F0Y2hlZF9pbWRiLAogICAgICAgICAgICAgICAgd2hpdGVsaXN0PXdoaXRlbGlzdCwKICAgICAgICAgICAgICAgIGxpbWl0PWxpbWl0LAogICAgICAgICAgICAgICAgaXRlbV90eXBlPWl0ZW1fdHlwZSwKICAgICAgICAgICAgICAgIHByb2ZpbGU9cHJvZmlsZSwKICAgICAgICAgICAgKQogICAgICAgICAgICBsb2dnZXIuaW5mbyhmIkZvdW5kIHtsZW4ocmVjb21tZW5kYXRpb25zKX0gcmVjb21tZW5kYXRpb25zIGJhc2VkIG9uIGFsbCB7aXRlbV90eXBlfSBpdGVtcyIpCgogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGxvZ2dlci53YXJuaW5nKGYiVW5rbm93biBjYXRhbG9nIElEOiB7Y2F0YWxvZ19pZH0iKQogICAgICAgICAgICByZWNvbW1lbmRhdGlvbnMgPSBbXQoKICAgICAgICByZXR1cm4gcmVjb21tZW5kYXRpb25zCgoKY2F0YWxvZ19zZXJ2aWNlID0gQ2F0YWxvZ1NlcnZpY2UoKQo=" | base64 --decode > "$REPO/app/services/recommendation/catalog_service.py" +echo " wrote app/services/recommendation/catalog_service.py" + +echo "VE1EQl9BUElfS0VZPTx5b3VyX3RtZGJfYXBpX2tleT4KUkVESVNfVVJMPXJlZGlzOi8vcmVkaXM6NjM3OS8wClRPS0VOX1NBTFQ9Y2hhbmdlLW1lICMgZ2VuZXJhdGUgc2VjdXJlIHNhbHQuLi4KCiMgYXBwIHNldHVwCkFQUF9FTlY9ImRldmVsb3BtZW50IiAgIyBhdmFpbGFibGUgdmFsdWVzIGFyZSBbImRldmVsb3BtZW50IiwgInByb2R1Y3Rpb24iXQpIT1NUX05BTUU9PHlvdXJfYWRkb25fdXJsPgpQT1JUPTgwMDAKCiMgcmVkaXMKUkVESVNfTUFYX0NPTk5FQ1RJT05TPTIwClJFRElTX0NPTk5FQ1RJT05TX1RIUkVTSE9MRD0xMDAKCiMgVVBEQVRFUgpBVVRPX1VQREFURV9DQVRBTE9HUz1UcnVlCkNBVEFMT0dfUkVGUkVTSF9JTlRFUlZBTF9TRUNPTkRTPTIxNjAwICMgNio2MCo2MCBldmVyeSB+NiBob3VycwoKIyBBSSBDYXRhbG9nIG5hbWUgZ2VuZXJhdGlvbgpHRU1JTklfQVBJX0tFWT08eW91cl9nZW1pbmlfYXBpX2tleT4gICAjIE9wdGlvbmFsCkRFRkFVTFRfR0VNSU5JX01PREVMPSJnZW1tYS0zLTI3Yi1pdCIKCiMgVHJha3QgT0F1dGggKG9wdGlvbmFsIC0gZW5hYmxlcyBUcmFrdCBsb2dpbiBvbiB0aGUgY29uZmlndXJlIHBhZ2UpCiMgUmVnaXN0ZXIgeW91ciBhcHAgYXQgaHR0cHM6Ly90cmFrdC50di9vYXV0aC9hcHBsaWNhdGlvbnMKVFJBS1RfQ0xJRU5UX0lEPTx5b3VyX3RyYWt0X2NsaWVudF9pZD4KVFJBS1RfQ0xJRU5UX1NFQ1JFVD08eW91cl90cmFrdF9jbGllbnRfc2VjcmV0Pgo=" | base64 --decode > "$REPO/.env.example" +echo " wrote .env.example" + +echo "IyBXYXRjaGx5Cgo8ZGl2IGFsaWduPSJjZW50ZXIiPgoKPCEtLSBQcmVtaXVtIEJhZGdlIENvbGxlY3Rpb24gLS0+ClshW1ZlcnNpb25dKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vZ2l0aHViL3YvcmVsZWFzZS90aW1pbHNpbmFiaW1hbC93YXRjaGx5P3N0eWxlPWZvci10aGUtYmFkZ2UmbG9nbz1zZW12ZXImY29sb3I9NjM2NmYxKV0oaHR0cHM6Ly9naXRodWIuY29tL3RpbWlsc2luYWJpbWFsL3dhdGNobHkvcmVsZWFzZXMpClshW0xpY2Vuc2VdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvTGljZW5zZS1NSVQtMjJjNTVlP3N0eWxlPWZvci10aGUtYmFkZ2UmbG9nbz1vcGVuc291cmNlaW5pdGlhdGl2ZSZsb2dvQ29sb3I9d2hpdGUpXShMSUNFTlNFKQpbIVtHaXRIdWIgU3RhcnNdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vZ2l0aHViL3N0YXJzL3RpbWlsc2luYWJpbWFsL3dhdGNobHk/c3R5bGU9Zm9yLXRoZS1iYWRnZSZjb2xvcj1mNTllMGImbG9nbz1naXRodWIpXShodHRwczovL2dpdGh1Yi5jb20vdGltaWxzaW5hYmltYWwvd2F0Y2hseS9zdGFyZ2F6ZXJzKQpbIVtCdXkgbWUgIENvZmZlZV0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9Lby0tZmktU3VwcG9ydC1GMTYwNjE/c3R5bGU9Zm9yLXRoZS1iYWRnZSZsb2dvPWtvZmkmbG9nb0NvbG9yPXdoaXRlKV0oaHR0cHM6Ly9rby1maS5jb20vdGltaWxzaW5hYmltYWwpCgo8L2Rpdj4KPGJyLz4KCioqV2F0Y2hseSoqIGlzIGEgU3RyZW1pbyBjYXRhbG9nIGFkZG9uIHRoYXQgcHJvdmlkZXMgcGVyc29uYWxpemVkIG1vdmllIGFuZCBzZXJpZXMgcmVjb21tZW5kYXRpb25zIGJhc2VkIG9uIHlvdXIgU3RyZW1pbyBsaWJyYXJ5LiBJdCB1c2VzIFRoZSBNb3ZpZSBEYXRhYmFzZSAoVE1EQikgQVBJIHRvIGdlbmVyYXRlIGludGVsbGlnZW50IHJlY29tbWVuZGF0aW9ucyBmcm9tIHRoZSBjb250ZW50IHlvdSd2ZSB3YXRjaGVkIGFuZCBsb3ZlZC4KCiMjIEZlYXR1cmVzCgotICoqUGVyc29uYWxpemVkIFJlY29tbWVuZGF0aW9ucyoqOiBBbmFseXplcyB5b3VyIFN0cmVtaW8gb3IgVHJha3QgbGlicmFyeSB0byB1bmRlcnN0YW5kIHlvdXIgdmlld2luZyBwcmVmZXJlbmNlcy4KLSAqKlRyYWt0IEludGVncmF0aW9uKio6IExvZyBpbiB3aXRoIFRyYWt0IGluc3RlYWQgb2YgU3RyZW1pbyDigJQgbm8gU3RyZW1pbyBhY2NvdW50IHJlcXVpcmVkLgotICoqU21hcnQgRmlsdGVyaW5nKio6IEF1dG9tYXRpY2FsbHkgZXhjbHVkZXMgY29udGVudCB5b3UndmUgYWxyZWFkeSB3YXRjaGVkLgotICoqQWR2YW5jZWQgU2NvcmluZyoqOiBSZWNvbW1lbmRhdGlvbnMgYXJlIGludGVsbGlnZW50bHkgd2VpZ2h0ZWQgYnkgcmVjZW5jeSBhbmQgcmVsZXZhbmNlLgotICoqR2VucmUtQmFzZWQgRGlzY292ZXJ5Kio6IE9mZmVycyBnZW5yZS1zcGVjaWZpYyBjYXRhbG9ncyBiYXNlZCBvbiB5b3VyIHZpZXdpbmcgaGlzdG9yeS4KLSAqKlNpbWlsYXIgQ29udGVudCoqOiBEaXNjb3ZlciBjb250ZW50IHNpbWlsYXIgdG8gc3BlY2lmaWMgdGl0bGVzIGluIHlvdXIgbGlicmFyeS4KLSAqKldlYiBDb25maWd1cmF0aW9uKio6IEVhc3ktdG8tdXNlIHdlYiBpbnRlcmZhY2UgZm9yIHNlY3VyZSBzZXR1cC4KLSAqKlNlY3VyZSBBcmNoaXRlY3R1cmUqKjogQ3JlZGVudGlhbHMgYXJlIHN0b3JlZCBzZWN1cmVseSBhbmQgbmV2ZXIgZXhwb3NlZCBpbiBVUkxzLgotICoqQmFja2dyb3VuZCBTeW5jKio6IEtlZXBzIHlvdXIgY2F0YWxvZ3MgdXBkYXRlZCBhdXRvbWF0aWNhbGx5IGluIHRoZSBiYWNrZ3JvdW5kLgotICoqUGVyZm9ybWFuY2UgT3B0aW1pemVkKio6IEludGVsbGlnZW50IGNhY2hpbmcgZm9yIGZhc3QgYW5kIHJlbGlhYmxlIHJlc3BvbnNlcy4KCiMjIyBTY3JlZW5zaG90CjxpbWcgc3JjPSIuL2FwcC9zdGF0aWMvc2NyZWVuc2hvdHMvaG9tZXBhZ2UucG5nIiBhbHQ9IlRvcCBQaWNrcyIgd2lkdGg9IjgwMCIvPgoKCkZpbmQgbW9yZSBzY3JlZW5zaG90cyBbaGVyZV0oLi9hcHAvc3RhdGljL3NjcmVlbnNob3RzLykKIyMgSW5zdGFsbGF0aW9uCgojIyMgVXNpbmcgRG9ja2VyIChSZWNvbW1lbmRlZCkKCllvdSBjYW4gcHVsbCB0aGUgbGF0ZXN0IGltYWdlIGZyb20gdGhlIEdpdEh1YiBDb250YWluZXIgUmVnaXN0cnkuCgoxLiAgKipDcmVhdGUgYSBgZG9ja2VyLWNvbXBvc2UueW1sYCBmaWxlOioqCgogICAgYGBgeWFtbAogICAgc2VydmljZXM6CiAgICAgIHJlZGlzOgogICAgICAgIGltYWdlOiByZWRpczo3LWFscGluZQogICAgICAgIGNvbnRhaW5lcl9uYW1lOiB3YXRjaGx5LXJlZGlzCiAgICAgICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgICAgICB2b2x1bWVzOgogICAgICAgICAgLSByZWRpc19kYXRhOi9kYXRhCgogICAgICB3YXRjaGx5OgogICAgICAgIGltYWdlOiBnaGNyLmlvL3RpbWlsc2luYWJpbWFsL3dhdGNobHk6bGF0ZXN0CiAgICAgICAgY29udGFpbmVyX25hbWU6IHdhdGNobHkKICAgICAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgICAgIHBvcnRzOgogICAgICAgICAgLSAiODAwMDo4MDAwIgogICAgICAgIGVudl9maWxlOgogICAgICAgICAgLSAuZW52CiAgICAgICAgZGVwZW5kc19vbjoKICAgICAgICAgIC0gcmVkaXMKCiAgICB2b2x1bWVzOgogICAgICByZWRpc19kYXRhOgogICAgYGBgCgoyLiAgKipDcmVhdGUgYSBgLmVudmAgZmlsZToqKgoKICAgIGBgYGVudgogICAgIyBSZXF1aXJlZAogICAgVE1EQl9BUElfS0VZPXlvdXJfdG1kYl9hcGlfa2V5X2hlcmUKICAgIFRPS0VOX1NBTFQ9Z2VuZXJhdGVfYV9yYW5kb21fc2VjdXJlX3N0cmluZ19oZXJlICAjIHB5dGhvbjMgLWMgImltcG9ydCBzZWNyZXRzOyBwcmludChzZWNyZXRzLnRva2VuX2hleCgzMikpIgogICAgSE9TVF9OQU1FPXlvdXJfYWRkb25fdXJsCgogICAgIyBPcHRpb25hbAogICAgUE9SVD04MDAwCiAgICBSRURJU19VUkw9cmVkaXM6Ly9yZWRpczo2Mzc5LzAKICAgIEFERE9OX0lEPWNvbS5iaW1hbC53YXRjaGx5CiAgICBBRERPTl9OQU1FPVdhdGNobHkKICAgIFRPS0VOX1RUTF9TRUNPTkRTPTAKICAgIEFVVE9fVVBEQVRFX0NBVEFMT0dTPXRydWUKCiAgICAjIFRyYWt0IEludGVncmF0aW9uIChvcHRpb25hbCDigJQgZW5hYmxlcyBUcmFrdCBsb2dpbiBvbiB0aGUgY29uZmlndXJlIHBhZ2UpCiAgICAjIFJlZ2lzdGVyIHlvdXIgYXBwIGF0IGh0dHBzOi8vdHJha3QudHYvb2F1dGgvYXBwbGljYXRpb25zL25ldwogICAgIyBTZXQgdGhlIHJlZGlyZWN0IFVSSSB0bzogaHR0cHM6Ly95b3VyX2FkZG9uX3VybC90b2tlbnMvdHJha3QvY2FsbGJhY2sKICAgIFRSQUtUX0NMSUVOVF9JRD15b3VyX3RyYWt0X2NsaWVudF9pZAogICAgVFJBS1RfQ0xJRU5UX1NFQ1JFVD15b3VyX3RyYWt0X2NsaWVudF9zZWNyZXQKICAgIGBgYAoKMy4gICoqU3RhcnQgdGhlIGFwcGxpY2F0aW9uOioqCgogICAgYGBgYmFzaAogICAgZG9ja2VyLWNvbXBvc2UgdXAgLWQKICAgIGBgYAoKNC4gICoqQ29uZmlndXJlIHRoZSBhZGRvbjoqKgogICAgT3BlbiBgaHR0cDovL2xvY2FsaG9zdDo4MDAwL2NvbmZpZ3VyZWAgaW4geW91ciBicm93c2VyLiBMb2cgaW4gd2l0aCBlaXRoZXIgeW91ciAqKlN0cmVtaW8qKiBjcmVkZW50aWFscyBvciB5b3VyICoqVHJha3QqKiBhY2NvdW50LCB0aGVuIGNvbmZpZ3VyZSB5b3VyIHByZWZlcmVuY2VzIGFuZCBpbnN0YWxsIHRoZSBhZGRvbi4KCiMjIERldmVsb3BtZW50CgpUbyBydW4gdGhlIHByb2plY3QgbG9jYWxseToKCjEuICAqKkNsb25lIHRoZSByZXBvc2l0b3J5OioqCiAgICBgYGBiYXNoCiAgICBnaXQgY2xvbmUgaHR0cHM6Ly9naXRodWIuY29tL1RpbWlsc2luYUJpbWFsL1dhdGNobHkuZ2l0CiAgICBjZCBXYXRjaGx5CiAgICBgYGAKCjIuICAqKkluc3RhbGwgZGVwZW5kZW5jaWVzOioqCiAgICBJIHJlY29tbWVuZCB1c2luZyBbdXZdKGh0dHBzOi8vZ2l0aHViLmNvbS9hc3RyYWwtc2gvdXYpIGZvciBmYXN0IGRlcGVuZGVuY3kgbWFuYWdlbWVudC4KICAgIGBgYGJhc2gKICAgIHV2IHN5bmMKICAgIGBgYAoKMy4gICoqUnVuIHRoZSBhcHBsaWNhdGlvbjoqKgogICAgYGBgYmFzaAogICAgdXYgcnVuIG1haW4ucHkgLS1kZXYKICAgIGBgYAoKIyMgQ29udHJpYnV0aW5nCgpJIHdlbGNvbWUgY29udHJpYnV0aW9ucyBvZiBhbGwgc2l6ZXMhCgotICoqU21hbGwgQnVnIEZpeGVzICYgSW1wcm92ZW1lbnRzKio6IEZlZWwgZnJlZSB0byBvcGVuIGEgUHVsbCBSZXF1ZXN0IGRpcmVjdGx5LgotICoqTWFqb3IgRmVhdHVyZXMgJiBSZWZhY3RvcnMqKjogUGxlYXNlICoqW29wZW4gYW4gaXNzdWVdKGh0dHBzOi8vZ2l0aHViLmNvbS9UaW1pbHNpbmFCaW1hbC9XYXRjaGx5L2lzc3VlcykqKiB0byBkaXNjdXNzIHlvdXIgcHJvcG9zZWQgY2hhbmdlcy4gVGhpcyBoZWxwcyBlbnN1cmUgeW91ciB3b3JrIGFsaWducyB3aXRoIHRoZSBwcm9qZWN0J3MgZGlyZWN0aW9uIGFuZCBzYXZlcyB5b3UgdGltZS4KCiMjIEZ1bmRpbmcgJiBTdXBwb3J0CgpJZiB5b3UgZmluZCBXYXRjaGx5IHVzZWZ1bCwgcGxlYXNlIGNvbnNpZGVyIHN1cHBvcnRpbmcgdGhlIHByb2plY3Q6Ci0gW0J1eSBtZSBNbzpNb10oaHR0cHM6Ly9idXltZW1vbW8uY29tL3RpbWlsc2luYWJpbWFsKQotIFtTdXBwb3J0IG9uIEtvLWZpXShodHRwczovL2tvLWZpLmNvbS9JMkk4MU9WSkVIKQotIFtEb25hdGUgdmlhIFBheVBhbF0oaHR0cHM6Ly93d3cucGF5cGFsLmNvbS9kb25hdGUvP2hvc3RlZF9idXR0b25faWQ9S1JRTVZTMzRGQzVLQykKCiMjIEJ1ZyBSZXBvcnRzCgpGb3VuZCBhIGJ1ZyBvciBoYXZlIGEgZmVhdHVyZSByZXF1ZXN0PyBQbGVhc2UgW29wZW4gYW4gaXNzdWVdKGh0dHBzOi8vZ2l0aHViLmNvbS9UaW1pbHNpbmFCaW1hbC9XYXRjaGx5L2lzc3Vlcykgb24gR2l0SHViLgoKIyMgQ29udHJpYnV0b3JzCgpUaGFuayB5b3UgdG8gZXZlcnlvbmUgd2hvIGhhcyBjb250cmlidXRlZCB0byB0aGUgcHJvamVjdCEKCjxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9UaW1pbHNpbmFCaW1hbC93YXRjaGx5L2dyYXBocy9jb250cmlidXRvcnMiPgogIDxpbWcgc3JjPSJodHRwczovL2NvbnRyaWIucm9ja3MvaW1hZ2U/cmVwbz1UaW1pbHNpbmFCaW1hbC93YXRjaGx5IiAvPgo8L2E+CgojIyBBY2tub3dsZWRnZW1lbnRzCgpTcGVjaWFsIHRoYW5rcyB0byAqKltUaGUgTW92aWUgRGF0YWJhc2UgKFRNREIpXShodHRwczovL3d3dy50aGVtb3ZpZWRiLm9yZy8pKiogZm9yIHByb3ZpZGluZyB0aGUgcmljaCBtZXRhZGF0YSB0aGF0IHBvd2VycyBXYXRjaGx5J3MgcmVjb21tZW5kYXRpb25zLgo=" | base64 --decode > "$REPO/README.md" +echo " wrote README.md" + +echo "" +echo "All done! Restart the server with: uv run main.py" \ No newline at end of file