├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── bin ├── bugzilla2gitlab └── clone_users.py ├── bugzilla2gitlab ├── __init__.py ├── config.py ├── migrator.py ├── models.py └── utils.py ├── pypirc ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── setup.cfg ├── snapshot.png └── tests ├── test_bugzilla2gitlab.py └── test_data ├── bug-103.xml ├── bug-5933.xml └── config ├── component_mappings.yml ├── defaults.yml └── user_mappings.yml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "13:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, workflow_dispatch] 4 | 5 | jobs: 6 | format: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - run: pip install black 11 | - run: black --check bugzilla2gitlab 12 | test: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: ['3.7', '3.8', '3.9', '3.10'] 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup python 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - run: pip install -r requirements-dev.txt 24 | - run: pip install . 25 | - run: pytest 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Create PyPI Release 3 | 4 | on: 5 | push: 6 | tags: 7 | - '*' 8 | jobs: 9 | create_release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout source 13 | uses: actions/checkout@v2 14 | - name: Install release tooling 15 | run: pip install build twine 16 | - name: Create .pypirc file 17 | env: 18 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 19 | run: envsubst < pypirc > .pypirc 20 | - name: Build the thing 21 | run: python -m build --sdist --wheel 22 | - name: Upload to PyPI 23 | run: twine upload --config-file .pypirc -r pypi dist/* 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # pytest 7 | .pytest_cache/ 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Sphinx documentation 52 | docs/_build/ 53 | 54 | # PyBuilder 55 | target/ 56 | 57 | # pyenv 58 | .python-version 59 | 60 | # dotenv 61 | .env 62 | 63 | # virtualenv 64 | venv/ 65 | ENV/ 66 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project adheres to the [Contributor Covenant Code of Conduct](http://contributor-covenant.org/version/1/4/). By participating, you are expected to honor this code. 4 | 5 | ## Getting started 6 | 7 | The best way to start developing this project is to set up a virtualenv and install the requirements. 8 | 9 | git clone 10 | cd bugzilla2gitlab 11 | virtualenv venv 12 | source venv/bin/activate 13 | pip install -r requirements-dev.txt 14 | pip install . 15 | 16 | Run tests to confirm that everything is set up properly. 17 | 18 | pytest 19 | 20 | ## Submitting a pull request 21 | 22 | 1. Fork this repository 23 | 2. Create a branch: `git checkout -b my_feature` 24 | 3. Make changes 25 | 4. Run the commands in the [build file](.travis.yml) locally to ensure a passing build 26 | 5. Commit: `git commit -am "Great new feature that closes #3"`. Reference any related issues in the first line of the commit message. 27 | 6. Push: `git push origin my_feature` 28 | 7. Open a pull request in Github 29 | 8. Pat yourself on the back for making an open source contribution :) 30 | 31 | ## Other considerations 32 | 33 | - Please review the open issues before opening a PR. 34 | - Significant changes or new features should be documented in [`README.md`](https://github.com/xmunoz/bugzilla2gitlab/blob/master/README.md). 35 | - Writing tests is never a bad idea. Make sure all tests are passing before opening a PR. 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Cristina Muñoz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include requirements.txt 4 | include snapshot.png 5 | recursive-include tests * 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPI version](https://badge.fury.io/py/bugzilla2gitlab.svg)](http://badge.fury.io/py/bugzilla2gitlab) [![Build Status](https://travis-ci.com/xmunoz/bugzilla2gitlab.svg?branch=master)](https://travis-ci.com/xmunoz/bugzilla2gitlab) 2 | 3 | # bugzilla2gitlab 4 | 5 | - [Introduction](#introduction) 6 | - [Installation](#installation) 7 | - [Usage](#usage) 8 | - [How it works](#how-it-works) 9 | - [Caveats](#caveats) 10 | - [Demo](#demo) 11 | - [Contributing](#contributing) 12 | 13 | ## Introduction 14 | 15 | This is a tool for developers or admins who want to migrate the issue management for their software project from Bugzilla to Gitlab Issues. Here is a screenshoot of an issue created by bugzilla2gitlab: 16 | ![bugzilla2gitlab created issue](snapshot.png) 17 | 18 | bugzilla2gitlab copies over bugs, bug comments and attachments. 19 | 20 | ## Installation 21 | 22 | This library is very much under development. That said, if you like to feel the wind in your hair, simply `pip install bugzilla2gitlab`. 23 | 24 | More than likely, you will need to roll up your sleaves and hack on the package to achieve a migration that you are happy with. In this case: 25 | 26 | ``` 27 | git clone git@github.com:xmunoz/bugzilla2gitlab.git 28 | cd bugzilla2gitlab 29 | python3 -m venv venv 30 | source venv/bin/activate 31 | pip install -r requirements.txt 32 | # installs this package locally 33 | pip install . 34 | ``` 35 | 36 | bugzilla2gitlab is compatible with python 3.7, 3.8, 3.9, and 3.10. 37 | 38 | ## Usage 39 | 40 | bugzilla2gitlab synchronously migrates a user-defined list of bugzilla bugs to a single GitLab project. There are two interfaces for this library. The command line usage: 41 | 42 | ``` 43 | $ bin/bugzilla2gitlab -h 44 | usage: bugzilla2gitlab [-h] [FILE] [CONFIG_DIRECTORY] 45 | 46 | Migrate bugs from Bugzilla to GitLab Issues. 47 | 48 | positional arguments: 49 | [FILE] A file containing a list of Bugzilla bug numbers to 50 | migrate, one per line. 51 | [CONFIG_DIRECTORY] The directory containing the required configuration 52 | files. 53 | 54 | optional arguments: 55 | -h, --help show this help message and exit 56 | ``` 57 | 58 | This package can also be used as a python module. 59 | 60 | ``` 61 | from bugzilla2gitlab import Migrator 62 | 63 | client = Migrator(config_path="/path/to/config") 64 | bugs_list = [1,2,3,4,5,6,7] 65 | client.migrate(bugs_list) 66 | ``` 67 | 68 | ## Configuration 69 | 70 | To begin using bugzilla2gitlab, the following list of configuration files is required in the specified `config` directory: 71 | 72 | - `defaults.yml`: Core default values used throughout the modules. 73 | - `user_mappings.yml`: key, value pairs of Bugzilla usernames to GitLab users 74 | - `component_mappings.yml`: key, value pairs of Bugzilla components to Gitlab labels 75 | 76 | Samples of all of these files with documentation for each configuration variable can be found in [tests/test_data/config](tests/test_data/config). 77 | 78 | bugzilla2gitlab creates issues and comments in GitLab with the user accounts specified in `user_mappings.yml`, perserving the integrity of the original Bugzilla commenter. This, however, may not always be possible. In [tests/test_data/config/user_mappings.yml](tests/test_data/config/user_mappings.yml), users with the designation "bugzilla" may have left the organization and therefore not have current GitLab accounts, or might simply be machine users. Comments for such users will be left under a generic "bugzilla" account. bugzilla2gitlab doesn't create any new user accounts. All of the accounts specified in `user_mappings.yml` must already exist in your GitLab installation. 79 | 80 | The default table created in the issue description by bugzilla2gitlab looks like this: 81 | 82 | | | | 83 | | --- | --- | 84 | | Bugzilla Link | [570755](https://bugzilla.mozilla.org/show_bug.cgi?id=570755) | 85 | | Created on | Jun 08, 2010 10:25 | 86 | | Version | unspecified | 87 | | OS | All | 88 | | Architecture | All | 89 | | Attachments | [a_PHP_play_script_to_demonstrate_how_the_browser_requests_videos](/uploads/e521dd042dc4cfd3d49151d87dee8058/a_PHP_play_script_to_demonstrate_how_the_browser_requests_videos) | 90 | | Reporter | mozilla | 91 | 92 | To modify this table, take a look at `create_description` in [models.py](/bugzilla2gitlab/models.py#L92). 93 | 94 | ## How it works 95 | 96 | ### GitLab 97 | 98 | Gitlab has a comprehensive and extensively documented API. Here are the main endpoints that this library makes use of. 99 | 100 | - [Creating new issues](http://doc.gitlab.com/ce/api/issues.html#new-issue) 101 | - [Adding comments to issues](http://doc.gitlab.com/ce/api/notes.html) 102 | - [Uploading files](http://doc.gitlab.com/ce/api/projects.html#upload-a-file) 103 | - [Changing an issue status](http://doc.gitlab.com/ce/api/issues.html#edit-issue) 104 | - [Getting user ids](http://doc.gitlab.com/ce/api/users.html#for-admins) 105 | 106 | Calls to the Gitlab API must be made with an administrator private token in order to [impersonate other users](http://doc.gitlab.com/ce/api/#sudo). 107 | 108 | ### Bugzilla 109 | 110 | This program relies on being able to fetch bug data by simply appending `&ctype=xml` to the end of the bugzilla bug url, and then parsing the resultant xml. If this trick doesn't work on your bugzilla installation, then bugzilla2gitlab probably won't work for you. 111 | 112 | ## Caveats 113 | 114 | Every comment or mention in GitLab typically sends a notification. This is true even for comments/issues created programatically. To avoid users inboxes being flooded with meaningless email notifications and avoid overwhelming your SMTP servers, GitLab users should disable all email notifications (global and group-specific) just prior to the running of this script. This can be done through the [Gitlab UI](https://gitlab.com/profile/notifications). 115 | 116 | Further, this tools requires Gitlab [sudo](https://docs.gitlab.com/ce/api/#sudo). If you need a tool that works without sudo, take a look at [FreeDesktop's migration tool](https://gitlab.freedesktop.org/freedesktop/bztogl). 117 | 118 | ## Demo 119 | 120 | If you want to test out this library on a non-production GitLab instance, I recommend starting up a [one-click GitLab droplet from DigitalOcean](https://www.digitalocean.com/features/one-click-apps/gitlab/). From there, you can create a code repository, add some user accounts, and take bugzilla2gitlab for a spin. 121 | 122 | ## Contributing 123 | 124 | Check out [CONTRIBUTING.md](CONTRIBUTING.md). 125 | -------------------------------------------------------------------------------- /bin/bugzilla2gitlab: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Command-line interface for bugzilla2gitlab. 5 | """ 6 | 7 | import argparse 8 | from bugzilla2gitlab import Migrator 9 | 10 | def main(): 11 | parser = argparse.ArgumentParser(description='Migrate bugs from Bugzilla to GitLab Issues.') 12 | parser.add_argument('bug_list', metavar="BUGLIST", 13 | help="A file containing a list of Bugzilla bug numbers to migrate," 14 | " one per line.") 15 | parser.add_argument("conf_dir", metavar='CONFIG_DIRECTORY', 16 | help="The directory containing the required configuration files.") 17 | args = parser.parse_args() 18 | 19 | with open(args.bug_list, "r") as f: 20 | bugs = f.read().splitlines() 21 | 22 | client = Migrator(config_path=args.conf_dir) 23 | client.migrate(bugs) 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /bin/clone_users.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | # SCIP and integer group variables 4 | GITLAB_GROUP_ID = 7 5 | GITLAB_PROJECT_ID = 57 6 | TESTGL_GROUP_ID = 4 7 | TESTGL_PROJECT_ID = 2 8 | 9 | class Gitlab(object): 10 | def __init__(self, url, token) 11 | self.base = url 12 | self.headers = {"private-token": token} 13 | 14 | def get_user(self, username): 15 | url = "{}{}?{}={}".format(self.base, "users", "username", username) 16 | result = _perform_request(url, "get", headers=self.headers) 17 | if result and isinstance(result, list): 18 | return result[0] 19 | else: 20 | None 21 | 22 | def get_users_from_group(self, group): 23 | url = "{}{}/{}/{}".format(self.base, "groups", group, "members") 24 | return _perform_request(url, "get", headers=self.headers) 25 | 26 | def get_users_from_project(self, project): 27 | url = "{}{}/{}/{}".format(self.base, "projects", project, "members") 28 | return _perform_request(url, "get", headers=self.headers) 29 | 30 | def get_my_groups(self): 31 | url = "{}{}".format(self.base, "groups") 32 | return _perform_request(url, "get", headers=self.headers) 33 | 34 | def create_user(self, user): 35 | print u"Creating {} in test gitlab".format(user["name"]) 36 | user_data = { 37 | "email": "cm.alephone+git{}@gmail.com".format(user["username"]), 38 | "username": user["username"], 39 | "name": user["name"], 40 | "password": "{} fish {} yaks".format(user["username"], user["username"]), 41 | "confirm": False, 42 | } 43 | url = "{}{}".format(self.base, "users") 44 | new_user = _perform_request(url, "post", params=user_data, headers=self.headers, json=True) 45 | return new_user 46 | 47 | def add_user_to_group(self, group, user, access_level=30): 48 | user_to_group = { 49 | "user_id": user["id"], 50 | "access_level": access_level, 51 | } 52 | url = "{}{}/{}/{}".format(self.base, "groups", group, "members") 53 | add_user = _perform_request(url, "post", params=user_to_group, headers=self.headers, json=True) 54 | print u"Added {} to group integer".format(user["username"]) 55 | 56 | return add_user 57 | 58 | def add_user_to_project(self, project, user, access_level=30): 59 | user_to_project = { 60 | "user_id": user["id"], 61 | "access_level": access_level, 62 | } 63 | url = "{}{}/{}/{}".format(self.base, "projects", project, "members") 64 | add_user = _perform_request(url, "post", params=user_to_project, headers=self.headers, paginated=False) 65 | print u"Added {} to project SCIP".format(user["username"]) 66 | 67 | return add_user 68 | 69 | def create_users_in_group(self, group, users): 70 | for user in users: 71 | current_user = self.get_user(user["username"]) 72 | if not current_user: 73 | current_user = self.create_user(user) 74 | if "id" not in current_user: 75 | print "error" 76 | return 77 | print "Created {} with id {}".format(current_user["username"], current_user["id"]) 78 | 79 | result = self.add_user_to_group(group, current_user, access_level=user["access_level"]) 80 | if "id" not in result: 81 | print "error" 82 | return 83 | 84 | def create_users_in_project(self, project, users): 85 | for user in users: 86 | current_user = self.get_user(user["username"]) 87 | if not current_user: 88 | current_user = self.create_user(user) 89 | if "id" not in current_user: 90 | print "error" 91 | return 92 | print "Created {} with id {}".format(current_user["username"], current_user["id"]) 93 | 94 | result = self.add_user_to_project(project, current_user, access_level=user["access_level"]) 95 | if "id" not in result: 96 | print "error" 97 | return 98 | 99 | 100 | def _perform_request(url, method, params={}, headers={}, paginated=True): 101 | ''' 102 | Perform an HTTP request. 103 | ''' 104 | func = getattr(requests, method) 105 | 106 | # perform paginated requests 107 | params.update({"per_page": 100}) 108 | result = func(url, params=params, headers=headers) 109 | 110 | if not paginated: 111 | if result.status_code in [200, 201]: 112 | return result.json() 113 | else: 114 | raise Exception("Failed request {}".format(result.status_code)) 115 | 116 | # paginated request 117 | final_results = [] 118 | final_results.extend(result.json()) 119 | while "link" in result.headers: 120 | url_parts = result.headers["link"].split() 121 | if url_parts[1] != 'rel="next",': 122 | break 123 | else: 124 | next_url = url_parts[0].strip("<").rstrip(">;") 125 | result = func(next_url, headers=headers) 126 | final_results.extend(result.json()) 127 | 128 | return final_results 129 | 130 | def migrate_users_from_group(client_from, client_to): 131 | # integer group 132 | users = client_from.get_users_from_group(GITLAB_GROUP_ID) 133 | client_to.create_users_in_group(TESTGL_GROUP_ID, users) 134 | 135 | def migrate_users_from_project(client_from, client_to): 136 | # scip project 137 | users = client_from.get_users_from_project(GITLAB_PROJECT_ID) 138 | client_to.create_users_in_project(TESTGL_PROJECT_ID, users) 139 | 140 | 141 | def main(): 142 | from_private_token = "XXX" 143 | from_url = "https://git.test.com/api/v3/" 144 | to_private_token = "YYY" 145 | to_url = "http://git.example.com/api/v3/" 146 | 147 | # clients 148 | client_from = Gitlab(from_url, from_private_token) 149 | client_to = Gitlab(to_url, to_private_token) 150 | 151 | migrate_users_from_project(client_from, client_to) 152 | 153 | if __name__ == "__main__": 154 | main() 155 | -------------------------------------------------------------------------------- /bugzilla2gitlab/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The main interface of bugzilla2gitlab. 3 | """ 4 | from .migrator import Migrator # noqa 5 | -------------------------------------------------------------------------------- /bugzilla2gitlab/config.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import os 3 | 4 | import yaml 5 | 6 | from .utils import _perform_request 7 | 8 | Config = namedtuple( 9 | "Config", 10 | [ 11 | "gitlab_base_url", 12 | "gitlab_project_id", 13 | "bugzilla_base_url", 14 | "bugzilla_user", 15 | "bugzilla_password", 16 | "bugzilla_auto_reporter", 17 | "bugzilla_closed_states", 18 | "default_headers", 19 | "component_mappings", 20 | "bugzilla_users", 21 | "gitlab_users", 22 | "gitlab_misc_user", 23 | "default_gitlab_labels", 24 | "datetime_format_string", 25 | "map_operating_system", 26 | "map_keywords", 27 | "keywords_to_skip", 28 | "map_milestones", 29 | "milestones_to_skip", 30 | "gitlab_milestones", 31 | "dry_run", 32 | "include_bugzilla_link", 33 | "use_bugzilla_id", 34 | "verify", 35 | ], 36 | ) 37 | 38 | 39 | def get_config(path): 40 | configuration = {} 41 | configuration.update(_load_defaults(path)) 42 | configuration.update( 43 | _load_user_id_cache( 44 | path, 45 | configuration["gitlab_base_url"], 46 | configuration["default_headers"], 47 | configuration["verify"], 48 | ) 49 | ) 50 | if configuration["map_milestones"]: 51 | configuration.update( 52 | _load_milestone_id_cache( 53 | configuration["gitlab_project_id"], 54 | configuration["gitlab_base_url"], 55 | configuration["default_headers"], 56 | configuration["verify"], 57 | ) 58 | ) 59 | configuration.update(_load_component_mappings(path)) 60 | return Config(**configuration) 61 | 62 | 63 | def _load_defaults(path): 64 | with open(os.path.join(path, "defaults.yml")) as f: 65 | config = yaml.safe_load(f) 66 | 67 | defaults = {} 68 | 69 | for key in config: 70 | if key == "gitlab_private_token": 71 | defaults["default_headers"] = {"private-token": config[key]} 72 | else: 73 | defaults[key] = config[key] 74 | 75 | return defaults 76 | 77 | 78 | def _load_user_id_cache(path, gitlab_url, gitlab_headers, verify): 79 | """ 80 | Load cache of GitLab usernames and ids 81 | """ 82 | print("Loading user cache...") 83 | with open(os.path.join(path, "user_mappings.yml")) as f: 84 | bugzilla_mapping = yaml.safe_load(f) 85 | 86 | gitlab_users = {} 87 | for user in bugzilla_mapping: 88 | gitlab_username = bugzilla_mapping[user] 89 | uid = _get_user_id(gitlab_username, gitlab_url, gitlab_headers, verify=verify) 90 | gitlab_users[gitlab_username] = str(uid) 91 | 92 | mappings = {} 93 | # bugzilla_username: gitlab_username 94 | mappings["bugzilla_users"] = bugzilla_mapping 95 | 96 | # gitlab_username: gitlab_userid 97 | mappings["gitlab_users"] = gitlab_users 98 | 99 | return mappings 100 | 101 | 102 | def _load_milestone_id_cache(project_id, gitlab_url, gitlab_headers, verify): 103 | """ 104 | Load cache of GitLab milestones and ids 105 | """ 106 | print("Loading milestone cache...") 107 | 108 | gitlab_milestones = {} 109 | url = "{}/projects/{}/milestones".format(gitlab_url, project_id) 110 | result = _perform_request(url, "get", headers=gitlab_headers, verify=verify) 111 | if result and isinstance(result, list): 112 | for milestone in result: 113 | gitlab_milestones[milestone["title"]] = milestone["id"] 114 | 115 | return {"gitlab_milestones": gitlab_milestones} 116 | 117 | 118 | def _get_user_id(username, gitlab_url, headers, verify): 119 | url = "{}/users?username={}".format(gitlab_url, username) 120 | result = _perform_request(url, "get", headers=headers, verify=verify) 121 | if result and isinstance(result, list): 122 | return result[0]["id"] 123 | raise Exception("No gitlab account found for user {}".format(username)) 124 | 125 | 126 | def _load_component_mappings(path): 127 | with open(os.path.join(path, "component_mappings.yml")) as f: 128 | component_mappings = yaml.safe_load(f) 129 | 130 | return {"component_mappings": component_mappings} 131 | -------------------------------------------------------------------------------- /bugzilla2gitlab/migrator.py: -------------------------------------------------------------------------------- 1 | from .config import get_config 2 | from .models import IssueThread 3 | from .utils import bugzilla_login, get_bugzilla_bug, validate_list 4 | 5 | 6 | class Migrator: 7 | def __init__(self, config_path): 8 | self.conf = get_config(config_path) 9 | 10 | def migrate(self, bug_list): 11 | """ 12 | Migrate a list of bug ids from Bugzilla to GitLab. 13 | """ 14 | validate_list(bug_list) 15 | if self.conf.bugzilla_user: 16 | bugzilla_login( 17 | self.conf.bugzilla_base_url, 18 | self.conf.bugzilla_user, 19 | self.conf.bugzilla_password, 20 | ) 21 | for bug in bug_list: 22 | self.migrate_one(bug) 23 | 24 | def migrate_one(self, bugzilla_bug_id): 25 | """ 26 | Migrate a single bug from Bugzilla to GitLab. 27 | """ 28 | print("Migrating bug {}".format(bugzilla_bug_id)) 29 | fields = get_bugzilla_bug(self.conf.bugzilla_base_url, bugzilla_bug_id) 30 | issue_thread = IssueThread(self.conf, fields) 31 | issue_thread.save() 32 | -------------------------------------------------------------------------------- /bugzilla2gitlab/models.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from .utils import _perform_request, format_datetime, format_utc, markdown_table_row 4 | 5 | CONF = None 6 | 7 | 8 | class IssueThread: 9 | """ 10 | Everything related to an issue in GitLab, e.g. the issue itself and subsequent comments. 11 | """ 12 | 13 | def __init__(self, config, fields): 14 | global CONF 15 | CONF = config 16 | self.load_objects(fields) 17 | 18 | def load_objects(self, fields): 19 | """ 20 | Load the issue object and the comment objects. 21 | If CONF.dry_run=False, then Attachments are created in GitLab in this step. 22 | """ 23 | self.issue = Issue(fields) 24 | self.comments = [] 25 | """ 26 | fields["long_desc"] gets peared down in Issue creation (above). This is because bugzilla 27 | lacks the concept of an issue description, so the first comment is harvested for 28 | the issue description, as well as any subsequent comments that are simply attachments 29 | from the original reporter. What remains below should be a list of genuine comments. 30 | """ 31 | 32 | for comment_fields in fields["long_desc"]: 33 | if comment_fields.get("thetext"): 34 | self.comments.append(Comment(comment_fields)) 35 | 36 | def save(self): 37 | """ 38 | Save the issue and all of the comments to GitLab. 39 | If CONF.dry_run=True, then only the HTTP request that would be made is printed. 40 | """ 41 | self.issue.save() 42 | 43 | for comment in self.comments: 44 | comment.issue_id = self.issue.id 45 | comment.save() 46 | 47 | # close the issue in GitLab, if it is resolved in Bugzilla 48 | if self.issue.status in CONF.bugzilla_closed_states: 49 | self.issue.close() 50 | 51 | 52 | class Issue: 53 | """ 54 | The issue model 55 | """ 56 | 57 | required_fields = ["sudo", "title", "description"] 58 | data_fields = [ 59 | "sudo", 60 | "created_at", 61 | "title", 62 | "description", 63 | "assignee_ids", 64 | "milestone_id", 65 | "labels", 66 | ] 67 | 68 | def __init__(self, bugzilla_fields): 69 | self.headers = CONF.default_headers 70 | validate_user(bugzilla_fields["reporter"]) 71 | validate_user(bugzilla_fields["assigned_to"]) 72 | self.load_fields(bugzilla_fields) 73 | 74 | def load_fields(self, fields): 75 | self.title = fields["short_desc"] 76 | self.sudo = CONF.gitlab_users[CONF.bugzilla_users[fields["reporter"]]] 77 | self.assignee_ids = [ 78 | CONF.gitlab_users[CONF.bugzilla_users[fields["assigned_to"]]] 79 | ] 80 | self.created_at = format_utc(fields["creation_ts"]) 81 | self.status = fields["bug_status"] 82 | self.create_labels( 83 | fields["component"], fields.get("op_sys"), fields.get("keywords") 84 | ) 85 | self.bug_id = fields["bug_id"] 86 | milestone = fields["target_milestone"] 87 | if CONF.map_milestones and milestone not in CONF.milestones_to_skip: 88 | self.create_milestone(milestone) 89 | self.create_description(fields) 90 | 91 | def create_labels(self, component, operating_system, keywords): 92 | """ 93 | Creates 4 types of labels: default labels listed in the configuration, component labels, 94 | operating system labels, and keyword labels. 95 | """ 96 | labels = [] 97 | if CONF.default_gitlab_labels: 98 | labels.extend(CONF.default_gitlab_labels) 99 | 100 | component_label = CONF.component_mappings.get(component) 101 | if component_label: 102 | labels.append(component_label) 103 | 104 | # Do not create a label if the OS is other. That is a meaningless label. 105 | if ( 106 | CONF.map_operating_system 107 | and operating_system 108 | and operating_system != "Other" 109 | ): 110 | labels.append(operating_system) 111 | 112 | if CONF.map_keywords and keywords: 113 | # Input: payload of XML element like this: SECURITY, SUPPORT 114 | # Bugzilla restriction: You may not use commas or whitespace in a keyword name. 115 | for k in keywords.replace(" ", "").split(","): 116 | if not (CONF.keywords_to_skip and k in CONF.keywords_to_skip): 117 | labels.append(k) 118 | 119 | self.labels = ",".join(labels) 120 | 121 | def create_milestone(self, milestone): 122 | """ 123 | Looks up milestone id given its title or creates a new one. 124 | """ 125 | if milestone not in CONF.gitlab_milestones: 126 | print("Create milestone: {}".format(milestone)) 127 | url = "{}/projects/{}/milestones".format( 128 | CONF.gitlab_base_url, CONF.gitlab_project_id 129 | ) 130 | response = _perform_request( 131 | url, 132 | "post", 133 | headers=self.headers, 134 | data={"title": milestone}, 135 | verify=CONF.verify, 136 | ) 137 | CONF.gitlab_milestones[milestone] = response["id"] 138 | 139 | self.milestone_id = CONF.gitlab_milestones[milestone] 140 | 141 | def create_description(self, fields): 142 | """ 143 | An opinionated description body creator. 144 | """ 145 | ext_description = "" 146 | 147 | # markdown table header 148 | self.description = markdown_table_row("", "") 149 | self.description += markdown_table_row("---", "---") 150 | 151 | if CONF.include_bugzilla_link: 152 | bug_id = fields["bug_id"] 153 | link = "{}/show_bug.cgi?id={}".format(CONF.bugzilla_base_url, bug_id) 154 | self.description += markdown_table_row( 155 | "Bugzilla Link", "[{}]({})".format(bug_id, link) 156 | ) 157 | 158 | formatted_dt = format_datetime( 159 | fields["creation_ts"], CONF.datetime_format_string 160 | ) 161 | self.description += markdown_table_row("Created on", formatted_dt) 162 | 163 | if fields.get("resolution"): 164 | self.description += markdown_table_row("Resolution", fields["resolution"]) 165 | self.description += markdown_table_row( 166 | "Resolved on", 167 | format_datetime(fields["delta_ts"], CONF.datetime_format_string), 168 | ) 169 | 170 | self.description += markdown_table_row("Version", fields.get("version")) 171 | self.description += markdown_table_row("OS", fields.get("op_sys")) 172 | self.description += markdown_table_row( 173 | "Architecture", fields.get("rep_platform") 174 | ) 175 | 176 | deplist = "" 177 | blocklist = "" 178 | if fields.get("dependson"): 179 | for depends in fields.get("dependson"): 180 | deplist += "[{}]({}) ".format(depends, link) 181 | self.description += markdown_table_row("Depends On", deplist) 182 | if fields.get("blocked"): 183 | for blocked in fields.get("blocked"): 184 | blocklist += "[{}]({}) ".format(blocked, link) 185 | self.description += markdown_table_row("Blocked by", blocklist) 186 | 187 | # add first comment to the issue description 188 | attachments = [] 189 | to_delete = [] 190 | comment0 = fields["long_desc"][0] 191 | if fields["reporter"] == comment0["who"] and comment0["thetext"]: 192 | ext_description += "\n## Extended Description \n" 193 | ext_description += "\n\n".join(re.split("\n+", comment0["thetext"])) 194 | self.update_attachments(fields["reporter"], comment0, attachments) 195 | del fields["long_desc"][0] 196 | 197 | for i in range(0, len(fields["long_desc"])): 198 | comment = fields["long_desc"][i] 199 | if self.update_attachments(fields["reporter"], comment, attachments): 200 | to_delete.append(i) 201 | 202 | # delete comments that have already added to the issue description 203 | for i in reversed(to_delete): 204 | del fields["long_desc"][i] 205 | 206 | if attachments: 207 | self.description += markdown_table_row( 208 | "Attachments", ", ".join(attachments) 209 | ) 210 | 211 | if ext_description: 212 | # for situations where the reporter is a generic or old user, specify the original 213 | # reporter in the description body 214 | if fields["reporter"] == CONF.bugzilla_auto_reporter: 215 | # try to get reporter email from the body 216 | _, part, user_data = ext_description.rpartition("Submitter was ") 217 | # partition found matching string 218 | if part: 219 | regex = r"^(\S*)\s?.*$" 220 | email = re.match(regex, user_data, flags=re.M).group(1) 221 | self.description += markdown_table_row("Reporter", email) 222 | # Add original reporter to the markdown table 223 | elif CONF.bugzilla_users[fields["reporter"]] == CONF.gitlab_misc_user: 224 | self.description += markdown_table_row("Reporter", fields["reporter"]) 225 | 226 | self.description += ext_description 227 | 228 | def update_attachments(self, reporter, comment, attachments): 229 | """ 230 | Fetches attachments from comment if authored by reporter. 231 | """ 232 | if comment.get("attachid") and comment.get("who") == reporter: 233 | filename = Attachment.parse_file_description(comment.get("thetext")) 234 | attachment_markdown = Attachment(comment.get("attachid"), filename).save() 235 | attachments.append(attachment_markdown) 236 | return True 237 | return False 238 | 239 | def validate(self): 240 | for field in self.required_fields: 241 | value = getattr(self, field) 242 | if not value: 243 | raise Exception("Missing value for required field: {}".format(field)) 244 | return True 245 | 246 | def save(self): 247 | self.validate() 248 | url = "{}/projects/{}/issues".format( 249 | CONF.gitlab_base_url, CONF.gitlab_project_id 250 | ) 251 | data = {k: v for k, v in self.__dict__.items() if k in self.data_fields} 252 | 253 | if CONF.use_bugzilla_id is True: 254 | print("Using original issue id") 255 | data["iid"] = self.bug_id 256 | 257 | self.headers["sudo"] = self.sudo 258 | 259 | response = _perform_request( 260 | url, 261 | "post", 262 | headers=self.headers, 263 | data=data, 264 | json=True, 265 | dry_run=CONF.dry_run, 266 | verify=CONF.verify, 267 | ) 268 | 269 | if CONF.dry_run: 270 | # assign a random number so that program can continue 271 | self.id = 5 272 | return 273 | 274 | self.id = response["iid"] 275 | print("Created issue with id: {}".format(self.id)) 276 | 277 | def close(self): 278 | url = "{}/projects/{}/issues/{}".format( 279 | CONF.gitlab_base_url, CONF.gitlab_project_id, self.id 280 | ) 281 | data = { 282 | "state_event": "close", 283 | } 284 | self.headers["sudo"] = self.sudo 285 | 286 | _perform_request( 287 | url, 288 | "put", 289 | headers=self.headers, 290 | data=data, 291 | dry_run=CONF.dry_run, 292 | verify=CONF.verify, 293 | ) 294 | 295 | 296 | class Comment: 297 | """ 298 | The comment model 299 | """ 300 | 301 | required_fields = ["sudo", "body", "issue_id"] 302 | data_fields = ["created_at", "body"] 303 | 304 | def __init__(self, bugzilla_fields): 305 | self.headers = CONF.default_headers 306 | validate_user(bugzilla_fields["who"]) 307 | self.load_fields(bugzilla_fields) 308 | 309 | def load_fields(self, fields): 310 | self.sudo = CONF.gitlab_users[CONF.bugzilla_users[fields["who"]]] 311 | # if unable to comment as the original user, put username in comment body 312 | self.created_at = format_utc(fields["bug_when"]) 313 | if CONF.bugzilla_users[fields["who"]] == CONF.gitlab_misc_user: 314 | self.body = "By {} on {}\n\n".format( 315 | fields["who"], 316 | format_datetime(fields["bug_when"], CONF.datetime_format_string), 317 | ) 318 | else: 319 | self.body = format_datetime(fields["bug_when"], CONF.datetime_format_string) 320 | self.body += "\n\n" 321 | 322 | # if this comment is actually an attachment, upload the attachment and add the 323 | # markdown to the comment body 324 | if fields.get("attachid"): 325 | filename = Attachment.parse_file_description(fields["thetext"]) 326 | attachment_markdown = Attachment(fields["attachid"], filename).save() 327 | self.body += attachment_markdown 328 | else: 329 | self.body += fields["thetext"] 330 | 331 | def validate(self): 332 | for field in self.required_fields: 333 | value = getattr(self, field) 334 | if not value: 335 | raise Exception("Missing value for required field: {}".format(field)) 336 | 337 | def save(self): 338 | self.validate() 339 | self.headers["sudo"] = self.sudo 340 | url = "{}/projects/{}/issues/{}/notes".format( 341 | CONF.gitlab_base_url, CONF.gitlab_project_id, self.issue_id 342 | ) 343 | data = {k: v for k, v in self.__dict__.items() if k in self.data_fields} 344 | 345 | _perform_request( 346 | url, 347 | "post", 348 | headers=self.headers, 349 | data=data, 350 | json=True, 351 | dry_run=CONF.dry_run, 352 | verify=CONF.verify, 353 | ) 354 | 355 | 356 | class Attachment: 357 | """ 358 | The attachment model 359 | """ 360 | 361 | def __init__(self, bugzilla_attachment_id, file_description): 362 | self.id = bugzilla_attachment_id 363 | self.file_description = file_description 364 | self.headers = CONF.default_headers 365 | 366 | @classmethod 367 | def parse_file_description(cls, comment): 368 | regex = r"^Created attachment (\d*)\s?(.*)$" 369 | matches = re.match(regex, comment, flags=re.M) 370 | if not matches: 371 | raise Exception("Failed to match comment string: {}".format(comment)) 372 | return matches.group(2) 373 | 374 | def parse_file_name(self, headers): 375 | # Use real filename to store attachment but descriptive name for issue text 376 | if "Content-disposition" not in headers: 377 | raise Exception( 378 | "No file name returned for attachment {}".format(self.file_description) 379 | ) 380 | # Content-disposition: application/zip; filename="mail_route.zip" 381 | regex = r"^.*; filename=\"(.*)\"$" 382 | matches = re.match(regex, headers["Content-disposition"], flags=re.M) 383 | if not matches: 384 | raise Exception( 385 | "Failed to match file name for string: {}".format( 386 | headers["Content-disposition"] 387 | ) 388 | ) 389 | return matches.group(1) 390 | 391 | def parse_upload_link(self, attachment): 392 | if not (attachment and attachment["markdown"]): 393 | raise Exception( 394 | "No markdown returned for upload of attachment {}".format( 395 | self.file_description 396 | ) 397 | ) 398 | # ![mail_route.zip](/uploads/e943e69eb2478529f2f1c7c7ea00fb46/mail_route.zip) 399 | regex = r"^!?\[.*\]\((.*)\)$" 400 | matches = re.match(regex, attachment["markdown"], flags=re.M) 401 | if not matches: 402 | raise Exception( 403 | "Failed to match upload link for string: {}".format( 404 | attachment["markdown"] 405 | ) 406 | ) 407 | return matches.group(1) 408 | 409 | def save(self): 410 | url = "{}/attachment.cgi?id={}".format(CONF.bugzilla_base_url, self.id) 411 | result = _perform_request(url, "get", json=False, verify=CONF.verify) 412 | filename = self.parse_file_name(result.headers) 413 | 414 | url = "{}/projects/{}/uploads".format( 415 | CONF.gitlab_base_url, CONF.gitlab_project_id 416 | ) 417 | f = {"file": (filename, result.content)} 418 | attachment = _perform_request( 419 | url, 420 | "post", 421 | headers=self.headers, 422 | files=f, 423 | json=True, 424 | dry_run=CONF.dry_run, 425 | verify=CONF.verify, 426 | ) 427 | # For dry run, nothing is uploaded, so upload link is faked just to let the process continue 428 | upload_link = ( 429 | self.file_description 430 | if CONF.dry_run 431 | else self.parse_upload_link(attachment) 432 | ) 433 | 434 | return "[{}]({})".format(self.file_description, upload_link) 435 | 436 | 437 | def validate_user(bugzilla_user): 438 | if bugzilla_user not in CONF.bugzilla_users: 439 | raise Exception( 440 | "Bugzilla user `{}` not found in user_mappings.yml. " 441 | "Please add them before continuing.".format(bugzilla_user) 442 | ) 443 | -------------------------------------------------------------------------------- /bugzilla2gitlab/utils.py: -------------------------------------------------------------------------------- 1 | from getpass import getpass 2 | 3 | import dateutil.parser 4 | from defusedxml import ElementTree 5 | import pytz 6 | import requests 7 | 8 | SESSION = None 9 | 10 | 11 | def _perform_request( 12 | url, 13 | method, 14 | data={}, 15 | params={}, 16 | headers={}, 17 | files={}, 18 | json=True, 19 | dry_run=False, 20 | verify=True, 21 | ): 22 | """ 23 | Utility method to perform an HTTP request. 24 | """ 25 | if dry_run and method != "get": 26 | msg = "{} {} dry_run".format(url, method) 27 | print(msg) 28 | return 0 29 | 30 | global SESSION 31 | if not SESSION: 32 | SESSION = requests.Session() 33 | 34 | func = getattr(SESSION, method) 35 | 36 | if files: 37 | result = func(url, files=files, headers=headers, verify=verify) 38 | else: 39 | result = func(url, params=params, data=data, headers=headers, verify=verify) 40 | 41 | if result.status_code in [200, 201]: 42 | if json: 43 | return result.json() 44 | return result 45 | 46 | raise Exception( 47 | "{} failed requests: [{}] Response: [{}] Request data: [{}] Url: [{}] Headers: [{}]".format( 48 | result.status_code, result.reason, result.content, data, url, headers 49 | ) 50 | ) 51 | 52 | 53 | def markdown_table_row(key, value): 54 | """ 55 | Create a row in a markdown table. 56 | """ 57 | return "| {} | {} |\n".format(key, value) 58 | 59 | 60 | def format_datetime(datestr, formatting): 61 | """ 62 | Apply a dateime format to a string, according to the formatting string. 63 | """ 64 | parsed_dt = dateutil.parser.parse(datestr) 65 | return parsed_dt.strftime(formatting) 66 | 67 | 68 | def format_utc(datestr): 69 | """ 70 | Convert dateime string to UTC format recognized by gitlab. 71 | """ 72 | parsed_dt = dateutil.parser.parse(datestr) 73 | utc_dt = parsed_dt.astimezone(pytz.utc) 74 | return utc_dt.strftime("%Y-%m-%dT%H:%M:%SZ") 75 | 76 | 77 | def get_bugzilla_bug(bugzilla_url, bug_id): 78 | """ 79 | Read bug XML, return all fields and values in a dictionary. 80 | """ 81 | bug_xml = _fetch_bug_content(bugzilla_url, bug_id) 82 | tree = ElementTree.fromstring(bug_xml) 83 | 84 | bug_fields = { 85 | "long_desc": [], 86 | "attachment": [], 87 | "cc": [], 88 | "dependson": [], 89 | "blocked": [], 90 | } 91 | for bug in tree: 92 | for field in bug: 93 | if field.tag in ("long_desc", "attachment"): 94 | new = {} 95 | for data in field: 96 | new[data.tag] = data.text 97 | bug_fields[field.tag].append(new) 98 | elif field.tag == "cc": 99 | bug_fields[field.tag].append(field.text) 100 | elif field.tag == "dependson": 101 | bug_fields[field.tag].append(field.text) 102 | elif field.tag == "blocked": 103 | bug_fields[field.tag].append(field.text) 104 | else: 105 | bug_fields[field.tag] = field.text 106 | 107 | return bug_fields 108 | 109 | 110 | def _fetch_bug_content(url, bug_id): 111 | url = "{}/show_bug.cgi?ctype=xml&id={}".format(url, bug_id) 112 | response = _perform_request(url, "get", json=False) 113 | return response.content 114 | 115 | 116 | def bugzilla_login(url, user, password): 117 | """ 118 | Log in to Bugzilla as user, asking for password for a few times / untill success. 119 | """ 120 | max_login_attempts = 3 121 | login_url = "{}/index.cgi".format(url) 122 | # CSRF protection bypass: GET, then POST 123 | _perform_request(login_url, "get", json=False) 124 | for attempt in range(max_login_attempts): 125 | if password is None: 126 | bugzilla_password = getpass("Bugzilla password for {}: ".format(user)) 127 | else: 128 | bugzilla_password = password 129 | 130 | response = _perform_request( 131 | login_url, 132 | "post", 133 | headers={"Referer": login_url}, 134 | data={ 135 | "Bugzilla_login": user, 136 | "Bugzilla_password": bugzilla_password, 137 | }, 138 | json=False, 139 | ) 140 | if response.cookies: 141 | break 142 | print("Failed to log in (attempt {})".format(attempt + 1)) 143 | else: 144 | raise Exception("Failed to log in after {} attempts".format(max_login_attempts)) 145 | 146 | 147 | def validate_list(integer_list): 148 | """ 149 | Ensure that the user-supplied input is a list of integers, or a list of strings 150 | that can be parsed as integers. 151 | """ 152 | if not integer_list: 153 | raise Exception("No bugs to migrate! Call `migrate` with a list of bug ids.") 154 | 155 | if not isinstance(integer_list, list): 156 | raise Exception( 157 | "Expected a list of integers. Instead recieved " 158 | "a(n) {}".format(type(integer_list)) 159 | ) 160 | 161 | for i in integer_list: 162 | try: 163 | int(i) 164 | except ValueError: 165 | raise Exception( 166 | "{} is not able to be parsed as an integer, " 167 | "and is therefore an invalid bug id.".format(i) 168 | ) from ValueError 169 | -------------------------------------------------------------------------------- /pypirc: -------------------------------------------------------------------------------- 1 | [distutils] 2 | index-servers = 3 | pypi 4 | 5 | [pypi] 6 | username = __token__ 7 | password = ${PYPI_TOKEN} 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | flake8>=3.5.0 3 | flake8-import-order>=0.16 4 | pytest>=3.3.0 5 | black 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | defusedxml>=0.5.0 2 | pyaml>=17.12.1 3 | python-dateutil>=2.7.2 4 | pytz>=2018.4 5 | requests==2.31.0 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = bugzilla2gitlab 3 | version = 2.0.1 4 | description = An opinionated Bugzilla to Gitlab Issues bug migration tool 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | keywords = bugzilla, gitlab, bugtracking, workflow 8 | license = MIT 9 | author = Cristina Muñoz 10 | author_email = hi@xmunoz.com 11 | maintainer = Cristina Muñoz 12 | maintainer_email = hi@xmunoz.com 13 | url = https://github.com/xmunoz/bugzilla2gitlab 14 | download_url = https://github.com/xmunoz/bugzilla2gitlab/archive/master.tar.gz 15 | classifiers = 16 | Programming Language :: Python 17 | Topic :: Software Development 18 | Topic :: Software Development :: Libraries 19 | Topic :: Software Development :: Libraries :: Python Modules 20 | Topic :: Software Development :: Bug Tracking 21 | 22 | [options] 23 | packages = bugzilla2gitlab 24 | include_package_data = True 25 | scripts = 26 | bin/bugzilla2gitlab 27 | bin/clone_users.py 28 | 29 | [flake8] 30 | max-line-length = 100 31 | import-order-style = google 32 | application-import-names = bugzilla2gitlab 33 | [bdist_wheel] 34 | universal=1 35 | -------------------------------------------------------------------------------- /snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmunoz/bugzilla2gitlab/93fcfba35156adb2585094f6b4de0b89e12c2204/snapshot.png -------------------------------------------------------------------------------- /tests/test_bugzilla2gitlab.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import random 3 | 4 | from bugzilla2gitlab import Migrator 5 | import bugzilla2gitlab.config 6 | import bugzilla2gitlab.utils 7 | 8 | TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), "test_data") 9 | 10 | 11 | def test_config(monkeypatch): 12 | 13 | def mockreturn(username, gitlab_url, headers, verify): 14 | return random.randint(0, 100) 15 | 16 | def mockmilestones(project_id, gitlab_url, headers, verify): 17 | return {"gitlab_milestones": {"Foo": 1}} 18 | 19 | # monkeypatch config method that performs API calls to return a random int instead 20 | monkeypatch.setattr(bugzilla2gitlab.config, '_get_user_id', mockreturn) 21 | monkeypatch.setattr(bugzilla2gitlab.config, '_load_milestone_id_cache', mockmilestones) 22 | conf = bugzilla2gitlab.config.get_config(os.path.join(TEST_DATA_PATH, "config")) 23 | assert isinstance(conf, bugzilla2gitlab.config.Config) 24 | ''' 25 | test all config keys to ensure the correct type and value 26 | ''' 27 | # conf.dry_run is a boolean value 28 | assert isinstance(conf.dry_run, bool) 29 | 30 | # conf.bugzilla users is dictionary of bugzilla username => gitlab username 31 | assert isinstance(conf.bugzilla_users, dict) 32 | 33 | # conf.gitlab_users is a dictionary of gitlab username => gitlab user id 34 | assert isinstance(conf.gitlab_users, dict) 35 | 36 | # sanity check 37 | assert sorted(conf.bugzilla_users.values()) == sorted(conf.gitlab_users.keys()) 38 | 39 | # conf.default_gitlab_labels is a list 40 | assert isinstance(conf.default_gitlab_labels, list) 41 | 42 | # conf.default_headers are the default headers to use when making gitlab api calls 43 | assert isinstance(conf.default_headers, dict) 44 | assert "private-token" in conf.default_headers 45 | 46 | # conf.compnent mappings is a dictionary 47 | assert isinstance(conf.component_mappings, dict) 48 | 49 | # conf.map_operating_system is a boolean value 50 | assert isinstance(conf.map_operating_system, bool) 51 | 52 | # conf.map_keywords is a boolean value 53 | assert isinstance(conf.map_keywords, bool) 54 | 55 | # conf.keywords_to_skip is a dictionary 56 | assert isinstance(conf.keywords_to_skip, list) 57 | 58 | # conf.map_milestones is a boolean value 59 | assert isinstance(conf.map_milestones, bool) 60 | 61 | # conf.milestones_to_skip is a list 62 | assert isinstance(conf.milestones_to_skip, list) 63 | 64 | # conf.gitlab_milestones is a dictionary 65 | assert isinstance(conf.gitlab_milestones, dict) 66 | 67 | 68 | def test_Migrator(monkeypatch): 69 | bug_id = 103 70 | 71 | def mock_getuserid(username, gitlab_url, headers, verify): 72 | return random.randint(0, 100) 73 | 74 | def mock_loadmilestoneidcache(project_id, gitlab_url, headers, verify): 75 | return {"gitlab_milestones": {"Foo": 1}} 76 | 77 | def mock_fetchbugcontent(url, bug_id): 78 | bug_file = "bug-{}.xml".format(bug_id) 79 | with open(os.path.join(TEST_DATA_PATH, bug_file), "r") as f: 80 | content = f.read() 81 | return content 82 | 83 | # monkeypatch config method that performs API calls to return a random int instead 84 | monkeypatch.setattr(bugzilla2gitlab.config, '_get_user_id', mock_getuserid) 85 | monkeypatch.setattr(bugzilla2gitlab.config, '_load_milestone_id_cache', 86 | mock_loadmilestoneidcache) 87 | monkeypatch.setattr(bugzilla2gitlab.utils, '_fetch_bug_content', mock_fetchbugcontent) 88 | 89 | # just test that it works without throwing any exceptions 90 | client = Migrator(os.path.join(TEST_DATA_PATH, "config")) 91 | client.migrate([bug_id]) 92 | -------------------------------------------------------------------------------- /tests/test_data/bug-103.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 103 12 | 13 | 2000-09-18 02:47:33 -0700 14 | What are the Spice Girls doing in my spice dispenser? 15 | 2014-06-01 07:57:32 -0700 16 | 1 17 | 1 18 | 1 19 | Unclassified 20 | FoodReplicator 21 | SaltSprinkler 22 | 1.0 23 | Other 24 | Other 25 | RESOLVED 26 | DUPLICATE 27 | 20 28 | 29 | 30 | 31 | 32 | P2 33 | normal 34 | --- 35 | 22803 36 | 23 37 | 1 38 | matt 39 | cyeh 40 | christyloanle 41 | 42 | vinay.chalamcharla 43 | 44 | 45 | 46 | 47 | 48 | --- 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | oldest_to_newest 59 | 60 | 202 61 | 0 62 | matt 63 | 2000-09-18 02:47:33 -0700 64 | Upon openning the spice dispenser in order to add the required raw materials, 65 | I found minature versions of the Spice Girls inside of it. Having had no 66 | food or air for the months the product sat on the shelf, they were all 67 | dead an mumified, their sightless eyes staring at me from shriveled, leering 68 | faces. But that's besides the point. I don't want anything so artificial 69 | as the Spice Girls going into my food! 70 | 71 | 12126 72 | 1 73 | christyloanle 74 | 2008-05-21 16:21:59 -0700 75 | 76 | 77 | *** This bug has been marked as a duplicate of bug 20 *** 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /tests/test_data/bug-5933.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 5933 12 | 13 | 2007-10-18 05:56:11 -0700 14 | this is my summary 15 | 2016-05-15 11:31:10 -0700 16 | 1 17 | 1 18 | 1 19 | Unclassified 20 | FoodReplicator 21 | renamed component 22 | 1.0 23 | DEC 24 | All 25 | RESOLVED 26 | FIXED 27 | 28 | 29 | 30 | 31 | 32 | P2 33 | normal 34 | --- 35 | 36 | 37 | 0 38 | bmc 39 | bmc 40 | cm449922 41 | 42 | 43 | 44 | 45 | Option 2 46 | Show Option 2 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | oldest_to_newest 57 | 58 | 10624 59 | 0 60 | bmc 61 | 2007-10-18 05:56:11 -0700 62 | this is my description. 63 | there are many like it, but this one is mine. 64 | 65 | 10625 66 | 1 67 | 894 68 | bmc 69 | 2007-10-18 05:56:31 -0700 70 | Created attachment 894 71 | Attaching the GPL, since everyone needs a copy of the GPL! 72 | 73 | 10626 74 | 2 75 | 895 76 | bmc 77 | 2007-10-18 05:56:40 -0700 78 | Created attachment 895 79 | Attaching the GPL, but as a big file! 80 | 81 | 82 | 87 | 894 88 | 2007-10-18 05:56:31 -0700 89 | 2007-10-18 05:57:15 -0700 90 | Attaching the GPL, since everyone needs a copy of the GPL! 91 | GPL 92 | text/plain 93 | 18007 94 | bmc 95 | 96 | CQkgICAgR05VIEdFTkVSQUwgUFVCTElDIExJQ0VOU0UKCQkgICAgICAgVmVyc2lvbiAyLCBKdW5l 97 | IDE5OTEKCiBDb3B5cmlnaHQgKEMpIDE5ODksIDE5OTEgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9u 98 | LCBJbmMuCiAgICAgICAgICAgICAgICAgICAgICAgNTkgVGVtcGxlIFBsYWNlLCBTdWl0ZSAzMzAs 99 | IEJvc3RvbiwgTUEgIDAyMTExLTEzMDcgIFVTQQogRXZlcnlvbmUgaXMgcGVybWl0dGVkIHRvIGNv 100 | cHkgYW5kIGRpc3RyaWJ1dGUgdmVyYmF0aW0gY29waWVzCiBvZiB0aGlzIGxpY2Vuc2UgZG9jdW1l 101 | bnQsIGJ1dCBjaGFuZ2luZyBpdCBpcyBub3QgYWxsb3dlZC4KCgkJCSAgICBQcmVhbWJsZQoKICBU 102 | aGUgbGljZW5zZXMgZm9yIG1vc3Qgc29mdHdhcmUgYXJlIGRlc2lnbmVkIHRvIHRha2UgYXdheSB5 103 | b3VyCmZyZWVkb20gdG8gc2hhcmUgYW5kIGNoYW5nZSBpdC4gIEJ5IGNvbnRyYXN0LCB0aGUgR05V 104 | IEdlbmVyYWwgUHVibGljCkxpY2Vuc2UgaXMgaW50ZW5kZWQgdG8gZ3VhcmFudGVlIHlvdXIgZnJl 105 | ZWRvbSB0byBzaGFyZSBhbmQgY2hhbmdlIGZyZWUKc29mdHdhcmUtLXRvIG1ha2Ugc3VyZSB0aGUg 106 | c29mdHdhcmUgaXMgZnJlZSBmb3IgYWxsIGl0cyB1c2Vycy4gIFRoaXMKR2VuZXJhbCBQdWJsaWMg 107 | TGljZW5zZSBhcHBsaWVzIHRvIG1vc3Qgb2YgdGhlIEZyZWUgU29mdHdhcmUKRm91bmRhdGlvbidz 108 | IHNvZnR3YXJlIGFuZCB0byBhbnkgb3RoZXIgcHJvZ3JhbSB3aG9zZSBhdXRob3JzIGNvbW1pdCB0 109 | bwp1c2luZyBpdC4gIChTb21lIG90aGVyIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiBzb2Z0d2Fy 110 | ZSBpcyBjb3ZlcmVkIGJ5CnRoZSBHTlUgTGlicmFyeSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGlu 111 | c3RlYWQuKSAgWW91IGNhbiBhcHBseSBpdCB0bwp5b3VyIHByb2dyYW1zLCB0b28uCgogIFdoZW4g 112 | d2Ugc3BlYWsgb2YgZnJlZSBzb2Z0d2FyZSwgd2UgYXJlIHJlZmVycmluZyB0byBmcmVlZG9tLCBu 113 | b3QKcHJpY2UuICBPdXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZXMgYXJlIGRlc2lnbmVkIHRvIG1h 114 | a2Ugc3VyZSB0aGF0IHlvdQpoYXZlIHRoZSBmcmVlZG9tIHRvIGRpc3RyaWJ1dGUgY29waWVzIG9m 115 | IGZyZWUgc29mdHdhcmUgKGFuZCBjaGFyZ2UgZm9yCnRoaXMgc2VydmljZSBpZiB5b3Ugd2lzaCks 116 | IHRoYXQgeW91IHJlY2VpdmUgc291cmNlIGNvZGUgb3IgY2FuIGdldCBpdAppZiB5b3Ugd2FudCBp 117 | dCwgdGhhdCB5b3UgY2FuIGNoYW5nZSB0aGUgc29mdHdhcmUgb3IgdXNlIHBpZWNlcyBvZiBpdApp 118 | biBuZXcgZnJlZSBwcm9ncmFtczsgYW5kIHRoYXQgeW91IGtub3cgeW91IGNhbiBkbyB0aGVzZSB0 119 | aGluZ3MuCgogIFRvIHByb3RlY3QgeW91ciByaWdodHMsIHdlIG5lZWQgdG8gbWFrZSByZXN0cmlj 120 | dGlvbnMgdGhhdCBmb3JiaWQKYW55b25lIHRvIGRlbnkgeW91IHRoZXNlIHJpZ2h0cyBvciB0byBh 121 | c2sgeW91IHRvIHN1cnJlbmRlciB0aGUgcmlnaHRzLgpUaGVzZSByZXN0cmljdGlvbnMgdHJhbnNs 122 | YXRlIHRvIGNlcnRhaW4gcmVzcG9uc2liaWxpdGllcyBmb3IgeW91IGlmIHlvdQpkaXN0cmlidXRl 123 | IGNvcGllcyBvZiB0aGUgc29mdHdhcmUsIG9yIGlmIHlvdSBtb2RpZnkgaXQuCgogIEZvciBleGFt 124 | cGxlLCBpZiB5b3UgZGlzdHJpYnV0ZSBjb3BpZXMgb2Ygc3VjaCBhIHByb2dyYW0sIHdoZXRoZXIK 125 | Z3JhdGlzIG9yIGZvciBhIGZlZSwgeW91IG11c3QgZ2l2ZSB0aGUgcmVjaXBpZW50cyBhbGwgdGhl 126 | IHJpZ2h0cyB0aGF0CnlvdSBoYXZlLiAgWW91IG11c3QgbWFrZSBzdXJlIHRoYXQgdGhleSwgdG9v 127 | LCByZWNlaXZlIG9yIGNhbiBnZXQgdGhlCnNvdXJjZSBjb2RlLiAgQW5kIHlvdSBtdXN0IHNob3cg 128 | dGhlbSB0aGVzZSB0ZXJtcyBzbyB0aGV5IGtub3cgdGhlaXIKcmlnaHRzLgoKICBXZSBwcm90ZWN0 129 | IHlvdXIgcmlnaHRzIHdpdGggdHdvIHN0ZXBzOiAoMSkgY29weXJpZ2h0IHRoZSBzb2Z0d2FyZSwg 130 | YW5kCigyKSBvZmZlciB5b3UgdGhpcyBsaWNlbnNlIHdoaWNoIGdpdmVzIHlvdSBsZWdhbCBwZXJt 131 | aXNzaW9uIHRvIGNvcHksCmRpc3RyaWJ1dGUgYW5kL29yIG1vZGlmeSB0aGUgc29mdHdhcmUuCgog 132 | IEFsc28sIGZvciBlYWNoIGF1dGhvcidzIHByb3RlY3Rpb24gYW5kIG91cnMsIHdlIHdhbnQgdG8g 133 | bWFrZSBjZXJ0YWluCnRoYXQgZXZlcnlvbmUgdW5kZXJzdGFuZHMgdGhhdCB0aGVyZSBpcyBubyB3 134 | YXJyYW50eSBmb3IgdGhpcyBmcmVlCnNvZnR3YXJlLiAgSWYgdGhlIHNvZnR3YXJlIGlzIG1vZGlm 135 | aWVkIGJ5IHNvbWVvbmUgZWxzZSBhbmQgcGFzc2VkIG9uLCB3ZQp3YW50IGl0cyByZWNpcGllbnRz 136 | IHRvIGtub3cgdGhhdCB3aGF0IHRoZXkgaGF2ZSBpcyBub3QgdGhlIG9yaWdpbmFsLCBzbwp0aGF0 137 | IGFueSBwcm9ibGVtcyBpbnRyb2R1Y2VkIGJ5IG90aGVycyB3aWxsIG5vdCByZWZsZWN0IG9uIHRo 138 | ZSBvcmlnaW5hbAphdXRob3JzJyByZXB1dGF0aW9ucy4KCiAgRmluYWxseSwgYW55IGZyZWUgcHJv 139 | Z3JhbSBpcyB0aHJlYXRlbmVkIGNvbnN0YW50bHkgYnkgc29mdHdhcmUKcGF0ZW50cy4gIFdlIHdp 140 | c2ggdG8gYXZvaWQgdGhlIGRhbmdlciB0aGF0IHJlZGlzdHJpYnV0b3JzIG9mIGEgZnJlZQpwcm9n 141 | cmFtIHdpbGwgaW5kaXZpZHVhbGx5IG9idGFpbiBwYXRlbnQgbGljZW5zZXMsIGluIGVmZmVjdCBt 142 | YWtpbmcgdGhlCnByb2dyYW0gcHJvcHJpZXRhcnkuICBUbyBwcmV2ZW50IHRoaXMsIHdlIGhhdmUg 143 | bWFkZSBpdCBjbGVhciB0aGF0IGFueQpwYXRlbnQgbXVzdCBiZSBsaWNlbnNlZCBmb3IgZXZlcnlv 144 | bmUncyBmcmVlIHVzZSBvciBub3QgbGljZW5zZWQgYXQgYWxsLgoKICBUaGUgcHJlY2lzZSB0ZXJt 145 | cyBhbmQgY29uZGl0aW9ucyBmb3IgY29weWluZywgZGlzdHJpYnV0aW9uIGFuZAptb2RpZmljYXRp 146 | b24gZm9sbG93LgoMCgkJICAgIEdOVSBHRU5FUkFMIFBVQkxJQyBMSUNFTlNFCiAgIFRFUk1TIEFO 147 | RCBDT05ESVRJT05TIEZPUiBDT1BZSU5HLCBESVNUUklCVVRJT04gQU5EIE1PRElGSUNBVElPTgoK 148 | ICAwLiBUaGlzIExpY2Vuc2UgYXBwbGllcyB0byBhbnkgcHJvZ3JhbSBvciBvdGhlciB3b3JrIHdo 149 | aWNoIGNvbnRhaW5zCmEgbm90aWNlIHBsYWNlZCBieSB0aGUgY29weXJpZ2h0IGhvbGRlciBzYXlp 150 | bmcgaXQgbWF5IGJlIGRpc3RyaWJ1dGVkCnVuZGVyIHRoZSB0ZXJtcyBvZiB0aGlzIEdlbmVyYWwg 151 | UHVibGljIExpY2Vuc2UuICBUaGUgIlByb2dyYW0iLCBiZWxvdywKcmVmZXJzIHRvIGFueSBzdWNo 152 | IHByb2dyYW0gb3Igd29yaywgYW5kIGEgIndvcmsgYmFzZWQgb24gdGhlIFByb2dyYW0iCm1lYW5z 153 | IGVpdGhlciB0aGUgUHJvZ3JhbSBvciBhbnkgZGVyaXZhdGl2ZSB3b3JrIHVuZGVyIGNvcHlyaWdo 154 | dCBsYXc6CnRoYXQgaXMgdG8gc2F5LCBhIHdvcmsgY29udGFpbmluZyB0aGUgUHJvZ3JhbSBvciBh 155 | IHBvcnRpb24gb2YgaXQsCmVpdGhlciB2ZXJiYXRpbSBvciB3aXRoIG1vZGlmaWNhdGlvbnMgYW5k 156 | L29yIHRyYW5zbGF0ZWQgaW50byBhbm90aGVyCmxhbmd1YWdlLiAgKEhlcmVpbmFmdGVyLCB0cmFu 157 | c2xhdGlvbiBpcyBpbmNsdWRlZCB3aXRob3V0IGxpbWl0YXRpb24gaW4KdGhlIHRlcm0gIm1vZGlm 158 | aWNhdGlvbiIuKSAgRWFjaCBsaWNlbnNlZSBpcyBhZGRyZXNzZWQgYXMgInlvdSIuCgpBY3Rpdml0 159 | aWVzIG90aGVyIHRoYW4gY29weWluZywgZGlzdHJpYnV0aW9uIGFuZCBtb2RpZmljYXRpb24gYXJl 160 | IG5vdApjb3ZlcmVkIGJ5IHRoaXMgTGljZW5zZTsgdGhleSBhcmUgb3V0c2lkZSBpdHMgc2NvcGUu 161 | ICBUaGUgYWN0IG9mCnJ1bm5pbmcgdGhlIFByb2dyYW0gaXMgbm90IHJlc3RyaWN0ZWQsIGFuZCB0 162 | aGUgb3V0cHV0IGZyb20gdGhlIFByb2dyYW0KaXMgY292ZXJlZCBvbmx5IGlmIGl0cyBjb250ZW50 163 | cyBjb25zdGl0dXRlIGEgd29yayBiYXNlZCBvbiB0aGUKUHJvZ3JhbSAoaW5kZXBlbmRlbnQgb2Yg 164 | aGF2aW5nIGJlZW4gbWFkZSBieSBydW5uaW5nIHRoZSBQcm9ncmFtKS4KV2hldGhlciB0aGF0IGlz 165 | IHRydWUgZGVwZW5kcyBvbiB3aGF0IHRoZSBQcm9ncmFtIGRvZXMuCgogIDEuIFlvdSBtYXkgY29w 166 | eSBhbmQgZGlzdHJpYnV0ZSB2ZXJiYXRpbSBjb3BpZXMgb2YgdGhlIFByb2dyYW0ncwpzb3VyY2Ug 167 | Y29kZSBhcyB5b3UgcmVjZWl2ZSBpdCwgaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhhdCB5b3UK 168 | Y29uc3BpY3VvdXNseSBhbmQgYXBwcm9wcmlhdGVseSBwdWJsaXNoIG9uIGVhY2ggY29weSBhbiBh 169 | cHByb3ByaWF0ZQpjb3B5cmlnaHQgbm90aWNlIGFuZCBkaXNjbGFpbWVyIG9mIHdhcnJhbnR5OyBr 170 | ZWVwIGludGFjdCBhbGwgdGhlCm5vdGljZXMgdGhhdCByZWZlciB0byB0aGlzIExpY2Vuc2UgYW5k 171 | IHRvIHRoZSBhYnNlbmNlIG9mIGFueSB3YXJyYW50eTsKYW5kIGdpdmUgYW55IG90aGVyIHJlY2lw 172 | aWVudHMgb2YgdGhlIFByb2dyYW0gYSBjb3B5IG9mIHRoaXMgTGljZW5zZQphbG9uZyB3aXRoIHRo 173 | ZSBQcm9ncmFtLgoKWW91IG1heSBjaGFyZ2UgYSBmZWUgZm9yIHRoZSBwaHlzaWNhbCBhY3Qgb2Yg 174 | dHJhbnNmZXJyaW5nIGEgY29weSwgYW5kCnlvdSBtYXkgYXQgeW91ciBvcHRpb24gb2ZmZXIgd2Fy 175 | cmFudHkgcHJvdGVjdGlvbiBpbiBleGNoYW5nZSBmb3IgYSBmZWUuCgogIDIuIFlvdSBtYXkgbW9k 176 | aWZ5IHlvdXIgY29weSBvciBjb3BpZXMgb2YgdGhlIFByb2dyYW0gb3IgYW55IHBvcnRpb24Kb2Yg 177 | aXQsIHRodXMgZm9ybWluZyBhIHdvcmsgYmFzZWQgb24gdGhlIFByb2dyYW0sIGFuZCBjb3B5IGFu 178 | ZApkaXN0cmlidXRlIHN1Y2ggbW9kaWZpY2F0aW9ucyBvciB3b3JrIHVuZGVyIHRoZSB0ZXJtcyBv 179 | ZiBTZWN0aW9uIDEKYWJvdmUsIHByb3ZpZGVkIHRoYXQgeW91IGFsc28gbWVldCBhbGwgb2YgdGhl 180 | c2UgY29uZGl0aW9uczoKCiAgICBhKSBZb3UgbXVzdCBjYXVzZSB0aGUgbW9kaWZpZWQgZmlsZXMg 181 | dG8gY2FycnkgcHJvbWluZW50IG5vdGljZXMKICAgIHN0YXRpbmcgdGhhdCB5b3UgY2hhbmdlZCB0 182 | aGUgZmlsZXMgYW5kIHRoZSBkYXRlIG9mIGFueSBjaGFuZ2UuCgogICAgYikgWW91IG11c3QgY2F1 183 | c2UgYW55IHdvcmsgdGhhdCB5b3UgZGlzdHJpYnV0ZSBvciBwdWJsaXNoLCB0aGF0IGluCiAgICB3 184 | aG9sZSBvciBpbiBwYXJ0IGNvbnRhaW5zIG9yIGlzIGRlcml2ZWQgZnJvbSB0aGUgUHJvZ3JhbSBv 185 | ciBhbnkKICAgIHBhcnQgdGhlcmVvZiwgdG8gYmUgbGljZW5zZWQgYXMgYSB3aG9sZSBhdCBubyBj 186 | aGFyZ2UgdG8gYWxsIHRoaXJkCiAgICBwYXJ0aWVzIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGlzIExp 187 | Y2Vuc2UuCgogICAgYykgSWYgdGhlIG1vZGlmaWVkIHByb2dyYW0gbm9ybWFsbHkgcmVhZHMgY29t 188 | bWFuZHMgaW50ZXJhY3RpdmVseQogICAgd2hlbiBydW4sIHlvdSBtdXN0IGNhdXNlIGl0LCB3aGVu 189 | IHN0YXJ0ZWQgcnVubmluZyBmb3Igc3VjaAogICAgaW50ZXJhY3RpdmUgdXNlIGluIHRoZSBtb3N0 190 | IG9yZGluYXJ5IHdheSwgdG8gcHJpbnQgb3IgZGlzcGxheSBhbgogICAgYW5ub3VuY2VtZW50IGlu 191 | Y2x1ZGluZyBhbiBhcHByb3ByaWF0ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCBhCiAgICBub3RpY2Ug 192 | dGhhdCB0aGVyZSBpcyBubyB3YXJyYW50eSAob3IgZWxzZSwgc2F5aW5nIHRoYXQgeW91IHByb3Zp 193 | ZGUKICAgIGEgd2FycmFudHkpIGFuZCB0aGF0IHVzZXJzIG1heSByZWRpc3RyaWJ1dGUgdGhlIHBy 194 | b2dyYW0gdW5kZXIKICAgIHRoZXNlIGNvbmRpdGlvbnMsIGFuZCB0ZWxsaW5nIHRoZSB1c2VyIGhv 195 | dyB0byB2aWV3IGEgY29weSBvZiB0aGlzCiAgICBMaWNlbnNlLiAgKEV4Y2VwdGlvbjogaWYgdGhl 196 | IFByb2dyYW0gaXRzZWxmIGlzIGludGVyYWN0aXZlIGJ1dAogICAgZG9lcyBub3Qgbm9ybWFsbHkg 197 | cHJpbnQgc3VjaCBhbiBhbm5vdW5jZW1lbnQsIHlvdXIgd29yayBiYXNlZCBvbgogICAgdGhlIFBy 198 | b2dyYW0gaXMgbm90IHJlcXVpcmVkIHRvIHByaW50IGFuIGFubm91bmNlbWVudC4pCgwKVGhlc2Ug 199 | cmVxdWlyZW1lbnRzIGFwcGx5IHRvIHRoZSBtb2RpZmllZCB3b3JrIGFzIGEgd2hvbGUuICBJZgpp 200 | ZGVudGlmaWFibGUgc2VjdGlvbnMgb2YgdGhhdCB3b3JrIGFyZSBub3QgZGVyaXZlZCBmcm9tIHRo 201 | ZSBQcm9ncmFtLAphbmQgY2FuIGJlIHJlYXNvbmFibHkgY29uc2lkZXJlZCBpbmRlcGVuZGVudCBh 202 | bmQgc2VwYXJhdGUgd29ya3MgaW4KdGhlbXNlbHZlcywgdGhlbiB0aGlzIExpY2Vuc2UsIGFuZCBp 203 | dHMgdGVybXMsIGRvIG5vdCBhcHBseSB0byB0aG9zZQpzZWN0aW9ucyB3aGVuIHlvdSBkaXN0cmli 204 | dXRlIHRoZW0gYXMgc2VwYXJhdGUgd29ya3MuICBCdXQgd2hlbiB5b3UKZGlzdHJpYnV0ZSB0aGUg 205 | c2FtZSBzZWN0aW9ucyBhcyBwYXJ0IG9mIGEgd2hvbGUgd2hpY2ggaXMgYSB3b3JrIGJhc2VkCm9u 206 | IHRoZSBQcm9ncmFtLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB3aG9sZSBtdXN0IGJlIG9uIHRo 207 | ZSB0ZXJtcyBvZgp0aGlzIExpY2Vuc2UsIHdob3NlIHBlcm1pc3Npb25zIGZvciBvdGhlciBsaWNl 208 | bnNlZXMgZXh0ZW5kIHRvIHRoZQplbnRpcmUgd2hvbGUsIGFuZCB0aHVzIHRvIGVhY2ggYW5kIGV2 209 | ZXJ5IHBhcnQgcmVnYXJkbGVzcyBvZiB3aG8gd3JvdGUgaXQuCgpUaHVzLCBpdCBpcyBub3QgdGhl 210 | IGludGVudCBvZiB0aGlzIHNlY3Rpb24gdG8gY2xhaW0gcmlnaHRzIG9yIGNvbnRlc3QKeW91ciBy 211 | aWdodHMgdG8gd29yayB3cml0dGVuIGVudGlyZWx5IGJ5IHlvdTsgcmF0aGVyLCB0aGUgaW50ZW50 212 | IGlzIHRvCmV4ZXJjaXNlIHRoZSByaWdodCB0byBjb250cm9sIHRoZSBkaXN0cmlidXRpb24gb2Yg 213 | ZGVyaXZhdGl2ZSBvcgpjb2xsZWN0aXZlIHdvcmtzIGJhc2VkIG9uIHRoZSBQcm9ncmFtLgoKSW4g 214 | YWRkaXRpb24sIG1lcmUgYWdncmVnYXRpb24gb2YgYW5vdGhlciB3b3JrIG5vdCBiYXNlZCBvbiB0 215 | aGUgUHJvZ3JhbQp3aXRoIHRoZSBQcm9ncmFtIChvciB3aXRoIGEgd29yayBiYXNlZCBvbiB0aGUg 216 | UHJvZ3JhbSkgb24gYSB2b2x1bWUgb2YKYSBzdG9yYWdlIG9yIGRpc3RyaWJ1dGlvbiBtZWRpdW0g 217 | ZG9lcyBub3QgYnJpbmcgdGhlIG90aGVyIHdvcmsgdW5kZXIKdGhlIHNjb3BlIG9mIHRoaXMgTGlj 218 | ZW5zZS4KCiAgMy4gWW91IG1heSBjb3B5IGFuZCBkaXN0cmlidXRlIHRoZSBQcm9ncmFtIChvciBh 219 | IHdvcmsgYmFzZWQgb24gaXQsCnVuZGVyIFNlY3Rpb24gMikgaW4gb2JqZWN0IGNvZGUgb3IgZXhl 220 | Y3V0YWJsZSBmb3JtIHVuZGVyIHRoZSB0ZXJtcyBvZgpTZWN0aW9ucyAxIGFuZCAyIGFib3ZlIHBy 221 | b3ZpZGVkIHRoYXQgeW91IGFsc28gZG8gb25lIG9mIHRoZSBmb2xsb3dpbmc6CgogICAgYSkgQWNj 222 | b21wYW55IGl0IHdpdGggdGhlIGNvbXBsZXRlIGNvcnJlc3BvbmRpbmcgbWFjaGluZS1yZWFkYWJs 223 | ZQogICAgc291cmNlIGNvZGUsIHdoaWNoIG11c3QgYmUgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRl 224 | cm1zIG9mIFNlY3Rpb25zCiAgICAxIGFuZCAyIGFib3ZlIG9uIGEgbWVkaXVtIGN1c3RvbWFyaWx5 225 | IHVzZWQgZm9yIHNvZnR3YXJlIGludGVyY2hhbmdlOyBvciwKCiAgICBiKSBBY2NvbXBhbnkgaXQg 226 | d2l0aCBhIHdyaXR0ZW4gb2ZmZXIsIHZhbGlkIGZvciBhdCBsZWFzdCB0aHJlZQogICAgeWVhcnMs 227 | IHRvIGdpdmUgYW55IHRoaXJkIHBhcnR5LCBmb3IgYSBjaGFyZ2Ugbm8gbW9yZSB0aGFuIHlvdXIK 228 | ICAgIGNvc3Qgb2YgcGh5c2ljYWxseSBwZXJmb3JtaW5nIHNvdXJjZSBkaXN0cmlidXRpb24sIGEg 229 | Y29tcGxldGUKICAgIG1hY2hpbmUtcmVhZGFibGUgY29weSBvZiB0aGUgY29ycmVzcG9uZGluZyBz 230 | b3VyY2UgY29kZSwgdG8gYmUKICAgIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiBTZWN0 231 | aW9ucyAxIGFuZCAyIGFib3ZlIG9uIGEgbWVkaXVtCiAgICBjdXN0b21hcmlseSB1c2VkIGZvciBz 232 | b2Z0d2FyZSBpbnRlcmNoYW5nZTsgb3IsCgogICAgYykgQWNjb21wYW55IGl0IHdpdGggdGhlIGlu 233 | Zm9ybWF0aW9uIHlvdSByZWNlaXZlZCBhcyB0byB0aGUgb2ZmZXIKICAgIHRvIGRpc3RyaWJ1dGUg 234 | Y29ycmVzcG9uZGluZyBzb3VyY2UgY29kZS4gIChUaGlzIGFsdGVybmF0aXZlIGlzCiAgICBhbGxv 235 | d2VkIG9ubHkgZm9yIG5vbmNvbW1lcmNpYWwgZGlzdHJpYnV0aW9uIGFuZCBvbmx5IGlmIHlvdQog 236 | ICAgcmVjZWl2ZWQgdGhlIHByb2dyYW0gaW4gb2JqZWN0IGNvZGUgb3IgZXhlY3V0YWJsZSBmb3Jt 237 | IHdpdGggc3VjaAogICAgYW4gb2ZmZXIsIGluIGFjY29yZCB3aXRoIFN1YnNlY3Rpb24gYiBhYm92 238 | ZS4pCgpUaGUgc291cmNlIGNvZGUgZm9yIGEgd29yayBtZWFucyB0aGUgcHJlZmVycmVkIGZvcm0g 239 | b2YgdGhlIHdvcmsgZm9yCm1ha2luZyBtb2RpZmljYXRpb25zIHRvIGl0LiAgRm9yIGFuIGV4ZWN1 240 | dGFibGUgd29yaywgY29tcGxldGUgc291cmNlCmNvZGUgbWVhbnMgYWxsIHRoZSBzb3VyY2UgY29k 241 | ZSBmb3IgYWxsIG1vZHVsZXMgaXQgY29udGFpbnMsIHBsdXMgYW55CmFzc29jaWF0ZWQgaW50ZXJm 242 | YWNlIGRlZmluaXRpb24gZmlsZXMsIHBsdXMgdGhlIHNjcmlwdHMgdXNlZCB0bwpjb250cm9sIGNv 243 | bXBpbGF0aW9uIGFuZCBpbnN0YWxsYXRpb24gb2YgdGhlIGV4ZWN1dGFibGUuICBIb3dldmVyLCBh 244 | cyBhCnNwZWNpYWwgZXhjZXB0aW9uLCB0aGUgc291cmNlIGNvZGUgZGlzdHJpYnV0ZWQgbmVlZCBu 245 | b3QgaW5jbHVkZQphbnl0aGluZyB0aGF0IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChpbiBlaXRo 246 | ZXIgc291cmNlIG9yIGJpbmFyeQpmb3JtKSB3aXRoIHRoZSBtYWpvciBjb21wb25lbnRzIChjb21w 247 | aWxlciwga2VybmVsLCBhbmQgc28gb24pIG9mIHRoZQpvcGVyYXRpbmcgc3lzdGVtIG9uIHdoaWNo 248 | IHRoZSBleGVjdXRhYmxlIHJ1bnMsIHVubGVzcyB0aGF0IGNvbXBvbmVudAppdHNlbGYgYWNjb21w 249 | YW5pZXMgdGhlIGV4ZWN1dGFibGUuCgpJZiBkaXN0cmlidXRpb24gb2YgZXhlY3V0YWJsZSBvciBv 250 | YmplY3QgY29kZSBpcyBtYWRlIGJ5IG9mZmVyaW5nCmFjY2VzcyB0byBjb3B5IGZyb20gYSBkZXNp 251 | Z25hdGVkIHBsYWNlLCB0aGVuIG9mZmVyaW5nIGVxdWl2YWxlbnQKYWNjZXNzIHRvIGNvcHkgdGhl 252 | IHNvdXJjZSBjb2RlIGZyb20gdGhlIHNhbWUgcGxhY2UgY291bnRzIGFzCmRpc3RyaWJ1dGlvbiBv 253 | ZiB0aGUgc291cmNlIGNvZGUsIGV2ZW4gdGhvdWdoIHRoaXJkIHBhcnRpZXMgYXJlIG5vdApjb21w 254 | ZWxsZWQgdG8gY29weSB0aGUgc291cmNlIGFsb25nIHdpdGggdGhlIG9iamVjdCBjb2RlLgoMCiAg 255 | NC4gWW91IG1heSBub3QgY29weSwgbW9kaWZ5LCBzdWJsaWNlbnNlLCBvciBkaXN0cmlidXRlIHRo 256 | ZSBQcm9ncmFtCmV4Y2VwdCBhcyBleHByZXNzbHkgcHJvdmlkZWQgdW5kZXIgdGhpcyBMaWNlbnNl 257 | LiAgQW55IGF0dGVtcHQKb3RoZXJ3aXNlIHRvIGNvcHksIG1vZGlmeSwgc3VibGljZW5zZSBvciBk 258 | aXN0cmlidXRlIHRoZSBQcm9ncmFtIGlzCnZvaWQsIGFuZCB3aWxsIGF1dG9tYXRpY2FsbHkgdGVy 259 | bWluYXRlIHlvdXIgcmlnaHRzIHVuZGVyIHRoaXMgTGljZW5zZS4KSG93ZXZlciwgcGFydGllcyB3 260 | aG8gaGF2ZSByZWNlaXZlZCBjb3BpZXMsIG9yIHJpZ2h0cywgZnJvbSB5b3UgdW5kZXIKdGhpcyBM 261 | aWNlbnNlIHdpbGwgbm90IGhhdmUgdGhlaXIgbGljZW5zZXMgdGVybWluYXRlZCBzbyBsb25nIGFz 262 | IHN1Y2gKcGFydGllcyByZW1haW4gaW4gZnVsbCBjb21wbGlhbmNlLgoKICA1LiBZb3UgYXJlIG5v 263 | dCByZXF1aXJlZCB0byBhY2NlcHQgdGhpcyBMaWNlbnNlLCBzaW5jZSB5b3UgaGF2ZSBub3QKc2ln 264 | bmVkIGl0LiAgSG93ZXZlciwgbm90aGluZyBlbHNlIGdyYW50cyB5b3UgcGVybWlzc2lvbiB0byBt 265 | b2RpZnkgb3IKZGlzdHJpYnV0ZSB0aGUgUHJvZ3JhbSBvciBpdHMgZGVyaXZhdGl2ZSB3b3Jrcy4g 266 | IFRoZXNlIGFjdGlvbnMgYXJlCnByb2hpYml0ZWQgYnkgbGF3IGlmIHlvdSBkbyBub3QgYWNjZXB0 267 | IHRoaXMgTGljZW5zZS4gIFRoZXJlZm9yZSwgYnkKbW9kaWZ5aW5nIG9yIGRpc3RyaWJ1dGluZyB0 268 | aGUgUHJvZ3JhbSAob3IgYW55IHdvcmsgYmFzZWQgb24gdGhlClByb2dyYW0pLCB5b3UgaW5kaWNh 269 | dGUgeW91ciBhY2NlcHRhbmNlIG9mIHRoaXMgTGljZW5zZSB0byBkbyBzbywgYW5kCmFsbCBpdHMg 270 | dGVybXMgYW5kIGNvbmRpdGlvbnMgZm9yIGNvcHlpbmcsIGRpc3RyaWJ1dGluZyBvciBtb2RpZnlp 271 | bmcKdGhlIFByb2dyYW0gb3Igd29ya3MgYmFzZWQgb24gaXQuCgogIDYuIEVhY2ggdGltZSB5b3Ug 272 | cmVkaXN0cmlidXRlIHRoZSBQcm9ncmFtIChvciBhbnkgd29yayBiYXNlZCBvbiB0aGUKUHJvZ3Jh 273 | bSksIHRoZSByZWNpcGllbnQgYXV0b21hdGljYWxseSByZWNlaXZlcyBhIGxpY2Vuc2UgZnJvbSB0 274 | aGUKb3JpZ2luYWwgbGljZW5zb3IgdG8gY29weSwgZGlzdHJpYnV0ZSBvciBtb2RpZnkgdGhlIFBy 275 | b2dyYW0gc3ViamVjdCB0bwp0aGVzZSB0ZXJtcyBhbmQgY29uZGl0aW9ucy4gIFlvdSBtYXkgbm90 276 | IGltcG9zZSBhbnkgZnVydGhlcgpyZXN0cmljdGlvbnMgb24gdGhlIHJlY2lwaWVudHMnIGV4ZXJj 277 | aXNlIG9mIHRoZSByaWdodHMgZ3JhbnRlZCBoZXJlaW4uCllvdSBhcmUgbm90IHJlc3BvbnNpYmxl 278 | IGZvciBlbmZvcmNpbmcgY29tcGxpYW5jZSBieSB0aGlyZCBwYXJ0aWVzIHRvCnRoaXMgTGljZW5z 279 | ZS4KCiAgNy4gSWYsIGFzIGEgY29uc2VxdWVuY2Ugb2YgYSBjb3VydCBqdWRnbWVudCBvciBhbGxl 280 | Z2F0aW9uIG9mIHBhdGVudAppbmZyaW5nZW1lbnQgb3IgZm9yIGFueSBvdGhlciByZWFzb24gKG5v 281 | dCBsaW1pdGVkIHRvIHBhdGVudCBpc3N1ZXMpLApjb25kaXRpb25zIGFyZSBpbXBvc2VkIG9uIHlv 282 | dSAod2hldGhlciBieSBjb3VydCBvcmRlciwgYWdyZWVtZW50IG9yCm90aGVyd2lzZSkgdGhhdCBj 283 | b250cmFkaWN0IHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZSwgdGhleSBkbyBub3QKZXhj 284 | dXNlIHlvdSBmcm9tIHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZS4gIElmIHlvdSBjYW5u 285 | b3QKZGlzdHJpYnV0ZSBzbyBhcyB0byBzYXRpc2Z5IHNpbXVsdGFuZW91c2x5IHlvdXIgb2JsaWdh 286 | dGlvbnMgdW5kZXIgdGhpcwpMaWNlbnNlIGFuZCBhbnkgb3RoZXIgcGVydGluZW50IG9ibGlnYXRp 287 | b25zLCB0aGVuIGFzIGEgY29uc2VxdWVuY2UgeW91Cm1heSBub3QgZGlzdHJpYnV0ZSB0aGUgUHJv 288 | Z3JhbSBhdCBhbGwuICBGb3IgZXhhbXBsZSwgaWYgYSBwYXRlbnQKbGljZW5zZSB3b3VsZCBub3Qg 289 | cGVybWl0IHJveWFsdHktZnJlZSByZWRpc3RyaWJ1dGlvbiBvZiB0aGUgUHJvZ3JhbSBieQphbGwg 290 | dGhvc2Ugd2hvIHJlY2VpdmUgY29waWVzIGRpcmVjdGx5IG9yIGluZGlyZWN0bHkgdGhyb3VnaCB5 291 | b3UsIHRoZW4KdGhlIG9ubHkgd2F5IHlvdSBjb3VsZCBzYXRpc2Z5IGJvdGggaXQgYW5kIHRoaXMg 292 | TGljZW5zZSB3b3VsZCBiZSB0bwpyZWZyYWluIGVudGlyZWx5IGZyb20gZGlzdHJpYnV0aW9uIG9m 293 | IHRoZSBQcm9ncmFtLgoKSWYgYW55IHBvcnRpb24gb2YgdGhpcyBzZWN0aW9uIGlzIGhlbGQgaW52 294 | YWxpZCBvciB1bmVuZm9yY2VhYmxlIHVuZGVyCmFueSBwYXJ0aWN1bGFyIGNpcmN1bXN0YW5jZSwg 295 | dGhlIGJhbGFuY2Ugb2YgdGhlIHNlY3Rpb24gaXMgaW50ZW5kZWQgdG8KYXBwbHkgYW5kIHRoZSBz 296 | ZWN0aW9uIGFzIGEgd2hvbGUgaXMgaW50ZW5kZWQgdG8gYXBwbHkgaW4gb3RoZXIKY2lyY3Vtc3Rh 297 | bmNlcy4KCkl0IGlzIG5vdCB0aGUgcHVycG9zZSBvZiB0aGlzIHNlY3Rpb24gdG8gaW5kdWNlIHlv 298 | dSB0byBpbmZyaW5nZSBhbnkKcGF0ZW50cyBvciBvdGhlciBwcm9wZXJ0eSByaWdodCBjbGFpbXMg 299 | b3IgdG8gY29udGVzdCB2YWxpZGl0eSBvZiBhbnkKc3VjaCBjbGFpbXM7IHRoaXMgc2VjdGlvbiBo 300 | YXMgdGhlIHNvbGUgcHVycG9zZSBvZiBwcm90ZWN0aW5nIHRoZQppbnRlZ3JpdHkgb2YgdGhlIGZy 301 | ZWUgc29mdHdhcmUgZGlzdHJpYnV0aW9uIHN5c3RlbSwgd2hpY2ggaXMKaW1wbGVtZW50ZWQgYnkg 302 | cHVibGljIGxpY2Vuc2UgcHJhY3RpY2VzLiAgTWFueSBwZW9wbGUgaGF2ZSBtYWRlCmdlbmVyb3Vz 303 | IGNvbnRyaWJ1dGlvbnMgdG8gdGhlIHdpZGUgcmFuZ2Ugb2Ygc29mdHdhcmUgZGlzdHJpYnV0ZWQK 304 | dGhyb3VnaCB0aGF0IHN5c3RlbSBpbiByZWxpYW5jZSBvbiBjb25zaXN0ZW50IGFwcGxpY2F0aW9u 305 | IG9mIHRoYXQKc3lzdGVtOyBpdCBpcyB1cCB0byB0aGUgYXV0aG9yL2Rvbm9yIHRvIGRlY2lkZSBp 306 | ZiBoZSBvciBzaGUgaXMgd2lsbGluZwp0byBkaXN0cmlidXRlIHNvZnR3YXJlIHRocm91Z2ggYW55 307 | IG90aGVyIHN5c3RlbSBhbmQgYSBsaWNlbnNlZSBjYW5ub3QKaW1wb3NlIHRoYXQgY2hvaWNlLgoK 308 | VGhpcyBzZWN0aW9uIGlzIGludGVuZGVkIHRvIG1ha2UgdGhvcm91Z2hseSBjbGVhciB3aGF0IGlz 309 | IGJlbGlldmVkIHRvCmJlIGEgY29uc2VxdWVuY2Ugb2YgdGhlIHJlc3Qgb2YgdGhpcyBMaWNlbnNl 310 | LgoMCiAgOC4gSWYgdGhlIGRpc3RyaWJ1dGlvbiBhbmQvb3IgdXNlIG9mIHRoZSBQcm9ncmFtIGlz 311 | IHJlc3RyaWN0ZWQgaW4KY2VydGFpbiBjb3VudHJpZXMgZWl0aGVyIGJ5IHBhdGVudHMgb3IgYnkg 312 | Y29weXJpZ2h0ZWQgaW50ZXJmYWNlcywgdGhlCm9yaWdpbmFsIGNvcHlyaWdodCBob2xkZXIgd2hv 313 | IHBsYWNlcyB0aGUgUHJvZ3JhbSB1bmRlciB0aGlzIExpY2Vuc2UKbWF5IGFkZCBhbiBleHBsaWNp 314 | dCBnZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9uIGxpbWl0YXRpb24gZXhjbHVkaW5nCnRob3NlIGNv 315 | dW50cmllcywgc28gdGhhdCBkaXN0cmlidXRpb24gaXMgcGVybWl0dGVkIG9ubHkgaW4gb3IgYW1v 316 | bmcKY291bnRyaWVzIG5vdCB0aHVzIGV4Y2x1ZGVkLiAgSW4gc3VjaCBjYXNlLCB0aGlzIExpY2Vu 317 | c2UgaW5jb3Jwb3JhdGVzCnRoZSBsaW1pdGF0aW9uIGFzIGlmIHdyaXR0ZW4gaW4gdGhlIGJvZHkg 318 | b2YgdGhpcyBMaWNlbnNlLgoKICA5LiBUaGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIG1heSBw 319 | dWJsaXNoIHJldmlzZWQgYW5kL29yIG5ldyB2ZXJzaW9ucwpvZiB0aGUgR2VuZXJhbCBQdWJsaWMg 320 | TGljZW5zZSBmcm9tIHRpbWUgdG8gdGltZS4gIFN1Y2ggbmV3IHZlcnNpb25zIHdpbGwKYmUgc2lt 321 | aWxhciBpbiBzcGlyaXQgdG8gdGhlIHByZXNlbnQgdmVyc2lvbiwgYnV0IG1heSBkaWZmZXIgaW4g 322 | ZGV0YWlsIHRvCmFkZHJlc3MgbmV3IHByb2JsZW1zIG9yIGNvbmNlcm5zLgoKRWFjaCB2ZXJzaW9u 323 | IGlzIGdpdmVuIGEgZGlzdGluZ3Vpc2hpbmcgdmVyc2lvbiBudW1iZXIuICBJZiB0aGUgUHJvZ3Jh 324 | bQpzcGVjaWZpZXMgYSB2ZXJzaW9uIG51bWJlciBvZiB0aGlzIExpY2Vuc2Ugd2hpY2ggYXBwbGll 325 | cyB0byBpdCBhbmQgImFueQpsYXRlciB2ZXJzaW9uIiwgeW91IGhhdmUgdGhlIG9wdGlvbiBvZiBm 326 | b2xsb3dpbmcgdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zCmVpdGhlciBvZiB0aGF0IHZlcnNpb24g 327 | b3Igb2YgYW55IGxhdGVyIHZlcnNpb24gcHVibGlzaGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZv 328 | dW5kYXRpb24uICBJZiB0aGUgUHJvZ3JhbSBkb2VzIG5vdCBzcGVjaWZ5IGEgdmVyc2lvbiBudW1i 329 | ZXIgb2YKdGhpcyBMaWNlbnNlLCB5b3UgbWF5IGNob29zZSBhbnkgdmVyc2lvbiBldmVyIHB1Ymxp 330 | c2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZQpGb3VuZGF0aW9uLgoKICAxMC4gSWYgeW91IHdpc2gg 331 | dG8gaW5jb3Jwb3JhdGUgcGFydHMgb2YgdGhlIFByb2dyYW0gaW50byBvdGhlciBmcmVlCnByb2dy 332 | YW1zIHdob3NlIGRpc3RyaWJ1dGlvbiBjb25kaXRpb25zIGFyZSBkaWZmZXJlbnQsIHdyaXRlIHRv 333 | IHRoZSBhdXRob3IKdG8gYXNrIGZvciBwZXJtaXNzaW9uLiAgRm9yIHNvZnR3YXJlIHdoaWNoIGlz 334 | IGNvcHlyaWdodGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZvdW5kYXRpb24sIHdyaXRlIHRvIHRo 335 | ZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHdlIHNvbWV0aW1lcwptYWtlIGV4Y2VwdGlvbnMg 336 | Zm9yIHRoaXMuICBPdXIgZGVjaXNpb24gd2lsbCBiZSBndWlkZWQgYnkgdGhlIHR3byBnb2Fscwpv 337 | ZiBwcmVzZXJ2aW5nIHRoZSBmcmVlIHN0YXR1cyBvZiBhbGwgZGVyaXZhdGl2ZXMgb2Ygb3VyIGZy 338 | ZWUgc29mdHdhcmUgYW5kCm9mIHByb21vdGluZyB0aGUgc2hhcmluZyBhbmQgcmV1c2Ugb2Ygc29m 339 | dHdhcmUgZ2VuZXJhbGx5LgoKCQkJICAgIE5PIFdBUlJBTlRZCgogIDExLiBCRUNBVVNFIFRIRSBQ 340 | Uk9HUkFNIElTIExJQ0VOU0VEIEZSRUUgT0YgQ0hBUkdFLCBUSEVSRSBJUyBOTyBXQVJSQU5UWQpG 341 | T1IgVEhFIFBST0dSQU0sIFRPIFRIRSBFWFRFTlQgUEVSTUlUVEVEIEJZIEFQUExJQ0FCTEUgTEFX 342 | LiAgRVhDRVBUIFdIRU4KT1RIRVJXSVNFIFNUQVRFRCBJTiBXUklUSU5HIFRIRSBDT1BZUklHSFQg 343 | SE9MREVSUyBBTkQvT1IgT1RIRVIgUEFSVElFUwpQUk9WSURFIFRIRSBQUk9HUkFNICJBUyBJUyIg 344 | V0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRUlUSEVSIEVYUFJFU1NFRApPUiBJTVBMSUVE 345 | LCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgVEhFIElNUExJRUQgV0FSUkFOVElFUyBP 346 | RgpNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAg 347 | VEhFIEVOVElSRSBSSVNLIEFTClRPIFRIRSBRVUFMSVRZIEFORCBQRVJGT1JNQU5DRSBPRiBUSEUg 348 | UFJPR1JBTSBJUyBXSVRIIFlPVS4gIFNIT1VMRCBUSEUKUFJPR1JBTSBQUk9WRSBERUZFQ1RJVkUs 349 | IFlPVSBBU1NVTUUgVEhFIENPU1QgT0YgQUxMIE5FQ0VTU0FSWSBTRVJWSUNJTkcsClJFUEFJUiBP 350 | UiBDT1JSRUNUSU9OLgoKICAxMi4gSU4gTk8gRVZFTlQgVU5MRVNTIFJFUVVJUkVEIEJZIEFQUExJ 351 | Q0FCTEUgTEFXIE9SIEFHUkVFRCBUTyBJTiBXUklUSU5HCldJTEwgQU5ZIENPUFlSSUdIVCBIT0xE 352 | RVIsIE9SIEFOWSBPVEhFUiBQQVJUWSBXSE8gTUFZIE1PRElGWSBBTkQvT1IKUkVESVNUUklCVVRF 353 | IFRIRSBQUk9HUkFNIEFTIFBFUk1JVFRFRCBBQk9WRSwgQkUgTElBQkxFIFRPIFlPVSBGT1IgREFN 354 | QUdFUywKSU5DTFVESU5HIEFOWSBHRU5FUkFMLCBTUEVDSUFMLCBJTkNJREVOVEFMIE9SIENPTlNF 355 | UVVFTlRJQUwgREFNQUdFUyBBUklTSU5HCk9VVCBPRiBUSEUgVVNFIE9SIElOQUJJTElUWSBUTyBV 356 | U0UgVEhFIFBST0dSQU0gKElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQKVE8gTE9TUyBPRiBEQVRB 357 | IE9SIERBVEEgQkVJTkcgUkVOREVSRUQgSU5BQ0NVUkFURSBPUiBMT1NTRVMgU1VTVEFJTkVEIEJZ 358 | CllPVSBPUiBUSElSRCBQQVJUSUVTIE9SIEEgRkFJTFVSRSBPRiBUSEUgUFJPR1JBTSBUTyBPUEVS 359 | QVRFIFdJVEggQU5ZIE9USEVSClBST0dSQU1TKSwgRVZFTiBJRiBTVUNIIEhPTERFUiBPUiBPVEhF 360 | UiBQQVJUWSBIQVMgQkVFTiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFH 361 | RVMuCgoJCSAgICAgRU5EIE9GIFRFUk1TIEFORCBDT05ESVRJT05TCgwKCSAgICBIb3cgdG8gQXBw 362 | bHkgVGhlc2UgVGVybXMgdG8gWW91ciBOZXcgUHJvZ3JhbXMKCiAgSWYgeW91IGRldmVsb3AgYSBu 363 | ZXcgcHJvZ3JhbSwgYW5kIHlvdSB3YW50IGl0IHRvIGJlIG9mIHRoZSBncmVhdGVzdApwb3NzaWJs 364 | ZSB1c2UgdG8gdGhlIHB1YmxpYywgdGhlIGJlc3Qgd2F5IHRvIGFjaGlldmUgdGhpcyBpcyB0byBt 365 | YWtlIGl0CmZyZWUgc29mdHdhcmUgd2hpY2ggZXZlcnlvbmUgY2FuIHJlZGlzdHJpYnV0ZSBhbmQg 366 | Y2hhbmdlIHVuZGVyIHRoZXNlIHRlcm1zLgoKICBUbyBkbyBzbywgYXR0YWNoIHRoZSBmb2xsb3dp 367 | bmcgbm90aWNlcyB0byB0aGUgcHJvZ3JhbS4gIEl0IGlzIHNhZmVzdAp0byBhdHRhY2ggdGhlbSB0 368 | byB0aGUgc3RhcnQgb2YgZWFjaCBzb3VyY2UgZmlsZSB0byBtb3N0IGVmZmVjdGl2ZWx5CmNvbnZl 369 | eSB0aGUgZXhjbHVzaW9uIG9mIHdhcnJhbnR5OyBhbmQgZWFjaCBmaWxlIHNob3VsZCBoYXZlIGF0 370 | IGxlYXN0CnRoZSAiY29weXJpZ2h0IiBsaW5lIGFuZCBhIHBvaW50ZXIgdG8gd2hlcmUgdGhlIGZ1 371 | bGwgbm90aWNlIGlzIGZvdW5kLgoKICAgIDxvbmUgbGluZSB0byBnaXZlIHRoZSBwcm9ncmFtJ3Mg 372 | bmFtZSBhbmQgYSBicmllZiBpZGVhIG9mIHdoYXQgaXQgZG9lcy4+CiAgICBDb3B5cmlnaHQgKEMp 373 | IDE5eXkgIDxuYW1lIG9mIGF1dGhvcj4KCiAgICBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2Fy 374 | ZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQogICAgaXQgdW5kZXIgdGhl 375 | IHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnkK 376 | ICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhl 377 | IExpY2Vuc2UsIG9yCiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLgoKICAg 378 | IFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUg 379 | dXNlZnVsLAogICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGlt 380 | cGxpZWQgd2FycmFudHkgb2YKICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBB 381 | UlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKICAgIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl 382 | IGZvciBtb3JlIGRldGFpbHMuCgogICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBv 383 | ZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKICAgIGFsb25nIHdpdGggdGhpcyBwcm9n 384 | cmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBJ 385 | bmMuLCA1OSBUZW1wbGUgUGxhY2UsIFN1aXRlIDMzMCwgQm9zdG9uLCBNQSAgMDIxMTEtMTMwNyAg 386 | VVNBCgoKQWxzbyBhZGQgaW5mb3JtYXRpb24gb24gaG93IHRvIGNvbnRhY3QgeW91IGJ5IGVsZWN0 387 | cm9uaWMgYW5kIHBhcGVyIG1haWwuCgpJZiB0aGUgcHJvZ3JhbSBpcyBpbnRlcmFjdGl2ZSwgbWFr 388 | ZSBpdCBvdXRwdXQgYSBzaG9ydCBub3RpY2UgbGlrZSB0aGlzCndoZW4gaXQgc3RhcnRzIGluIGFu 389 | IGludGVyYWN0aXZlIG1vZGU6CgogICAgR25vbW92aXNpb24gdmVyc2lvbiA2OSwgQ29weXJpZ2h0 390 | IChDKSAxOXl5IG5hbWUgb2YgYXV0aG9yCiAgICBHbm9tb3Zpc2lvbiBjb21lcyB3aXRoIEFCU09M 391 | VVRFTFkgTk8gV0FSUkFOVFk7IGZvciBkZXRhaWxzIHR5cGUgYHNob3cgdycuCiAgICBUaGlzIGlz 392 | IGZyZWUgc29mdHdhcmUsIGFuZCB5b3UgYXJlIHdlbGNvbWUgdG8gcmVkaXN0cmlidXRlIGl0CiAg 393 | ICB1bmRlciBjZXJ0YWluIGNvbmRpdGlvbnM7IHR5cGUgYHNob3cgYycgZm9yIGRldGFpbHMuCgpU 394 | aGUgaHlwb3RoZXRpY2FsIGNvbW1hbmRzIGBzaG93IHcnIGFuZCBgc2hvdyBjJyBzaG91bGQgc2hv 395 | dyB0aGUgYXBwcm9wcmlhdGUKcGFydHMgb2YgdGhlIEdlbmVyYWwgUHVibGljIExpY2Vuc2UuICBP 396 | ZiBjb3Vyc2UsIHRoZSBjb21tYW5kcyB5b3UgdXNlIG1heQpiZSBjYWxsZWQgc29tZXRoaW5nIG90 397 | aGVyIHRoYW4gYHNob3cgdycgYW5kIGBzaG93IGMnOyB0aGV5IGNvdWxkIGV2ZW4gYmUKbW91c2Ut 398 | Y2xpY2tzIG9yIG1lbnUgaXRlbXMtLXdoYXRldmVyIHN1aXRzIHlvdXIgcHJvZ3JhbS4KCllvdSBz 399 | aG91bGQgYWxzbyBnZXQgeW91ciBlbXBsb3llciAoaWYgeW91IHdvcmsgYXMgYSBwcm9ncmFtbWVy 400 | KSBvciB5b3VyCnNjaG9vbCwgaWYgYW55LCB0byBzaWduIGEgImNvcHlyaWdodCBkaXNjbGFpbWVy 401 | IiBmb3IgdGhlIHByb2dyYW0sIGlmCm5lY2Vzc2FyeS4gIEhlcmUgaXMgYSBzYW1wbGU7IGFsdGVy 402 | IHRoZSBuYW1lczoKCiAgWW95b2R5bmUsIEluYy4sIGhlcmVieSBkaXNjbGFpbXMgYWxsIGNvcHly 403 | aWdodCBpbnRlcmVzdCBpbiB0aGUgcHJvZ3JhbQogIGBHbm9tb3Zpc2lvbicgKHdoaWNoIG1ha2Vz 404 | IHBhc3NlcyBhdCBjb21waWxlcnMpIHdyaXR0ZW4gYnkgSmFtZXMgSGFja2VyLgoKICA8c2lnbmF0 405 | dXJlIG9mIFR5IENvb24+LCAxIEFwcmlsIDE5ODkKICBUeSBDb29uLCBQcmVzaWRlbnQgb2YgVmlj 406 | ZQoKVGhpcyBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGRvZXMgbm90IHBlcm1pdCBpbmNvcnBvcmF0 407 | aW5nIHlvdXIgcHJvZ3JhbSBpbnRvCnByb3ByaWV0YXJ5IHByb2dyYW1zLiAgSWYgeW91ciBwcm9n 408 | cmFtIGlzIGEgc3Vicm91dGluZSBsaWJyYXJ5LCB5b3UgbWF5CmNvbnNpZGVyIGl0IG1vcmUgdXNl 409 | ZnVsIHRvIHBlcm1pdCBsaW5raW5nIHByb3ByaWV0YXJ5IGFwcGxpY2F0aW9ucyB3aXRoIHRoZQps 410 | aWJyYXJ5LiAgSWYgdGhpcyBpcyB3aGF0IHlvdSB3YW50IHRvIGRvLCB1c2UgdGhlIEdOVSBMaWJy 411 | YXJ5IEdlbmVyYWwKUHVibGljIExpY2Vuc2UgaW5zdGVhZCBvZiB0aGlzIExpY2Vuc2UuCg== 412 | 413 | 414 | 415 | 420 | 895 421 | 2007-10-18 05:56:40 -0700 422 | 2007-10-18 05:56:40 -0700 423 | Attaching the GPL, but as a big file! 424 | GPL 425 | text/plain 426 | 18007 427 | bmc 428 | 429 | CQkgICAgR05VIEdFTkVSQUwgUFVCTElDIExJQ0VOU0UKCQkgICAgICAgVmVyc2lvbiAyLCBKdW5l 430 | IDE5OTEKCiBDb3B5cmlnaHQgKEMpIDE5ODksIDE5OTEgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9u 431 | LCBJbmMuCiAgICAgICAgICAgICAgICAgICAgICAgNTkgVGVtcGxlIFBsYWNlLCBTdWl0ZSAzMzAs 432 | IEJvc3RvbiwgTUEgIDAyMTExLTEzMDcgIFVTQQogRXZlcnlvbmUgaXMgcGVybWl0dGVkIHRvIGNv 433 | cHkgYW5kIGRpc3RyaWJ1dGUgdmVyYmF0aW0gY29waWVzCiBvZiB0aGlzIGxpY2Vuc2UgZG9jdW1l 434 | bnQsIGJ1dCBjaGFuZ2luZyBpdCBpcyBub3QgYWxsb3dlZC4KCgkJCSAgICBQcmVhbWJsZQoKICBU 435 | aGUgbGljZW5zZXMgZm9yIG1vc3Qgc29mdHdhcmUgYXJlIGRlc2lnbmVkIHRvIHRha2UgYXdheSB5 436 | b3VyCmZyZWVkb20gdG8gc2hhcmUgYW5kIGNoYW5nZSBpdC4gIEJ5IGNvbnRyYXN0LCB0aGUgR05V 437 | IEdlbmVyYWwgUHVibGljCkxpY2Vuc2UgaXMgaW50ZW5kZWQgdG8gZ3VhcmFudGVlIHlvdXIgZnJl 438 | ZWRvbSB0byBzaGFyZSBhbmQgY2hhbmdlIGZyZWUKc29mdHdhcmUtLXRvIG1ha2Ugc3VyZSB0aGUg 439 | c29mdHdhcmUgaXMgZnJlZSBmb3IgYWxsIGl0cyB1c2Vycy4gIFRoaXMKR2VuZXJhbCBQdWJsaWMg 440 | TGljZW5zZSBhcHBsaWVzIHRvIG1vc3Qgb2YgdGhlIEZyZWUgU29mdHdhcmUKRm91bmRhdGlvbidz 441 | IHNvZnR3YXJlIGFuZCB0byBhbnkgb3RoZXIgcHJvZ3JhbSB3aG9zZSBhdXRob3JzIGNvbW1pdCB0 442 | bwp1c2luZyBpdC4gIChTb21lIG90aGVyIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiBzb2Z0d2Fy 443 | ZSBpcyBjb3ZlcmVkIGJ5CnRoZSBHTlUgTGlicmFyeSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGlu 444 | c3RlYWQuKSAgWW91IGNhbiBhcHBseSBpdCB0bwp5b3VyIHByb2dyYW1zLCB0b28uCgogIFdoZW4g 445 | d2Ugc3BlYWsgb2YgZnJlZSBzb2Z0d2FyZSwgd2UgYXJlIHJlZmVycmluZyB0byBmcmVlZG9tLCBu 446 | b3QKcHJpY2UuICBPdXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZXMgYXJlIGRlc2lnbmVkIHRvIG1h 447 | a2Ugc3VyZSB0aGF0IHlvdQpoYXZlIHRoZSBmcmVlZG9tIHRvIGRpc3RyaWJ1dGUgY29waWVzIG9m 448 | IGZyZWUgc29mdHdhcmUgKGFuZCBjaGFyZ2UgZm9yCnRoaXMgc2VydmljZSBpZiB5b3Ugd2lzaCks 449 | IHRoYXQgeW91IHJlY2VpdmUgc291cmNlIGNvZGUgb3IgY2FuIGdldCBpdAppZiB5b3Ugd2FudCBp 450 | dCwgdGhhdCB5b3UgY2FuIGNoYW5nZSB0aGUgc29mdHdhcmUgb3IgdXNlIHBpZWNlcyBvZiBpdApp 451 | biBuZXcgZnJlZSBwcm9ncmFtczsgYW5kIHRoYXQgeW91IGtub3cgeW91IGNhbiBkbyB0aGVzZSB0 452 | aGluZ3MuCgogIFRvIHByb3RlY3QgeW91ciByaWdodHMsIHdlIG5lZWQgdG8gbWFrZSByZXN0cmlj 453 | dGlvbnMgdGhhdCBmb3JiaWQKYW55b25lIHRvIGRlbnkgeW91IHRoZXNlIHJpZ2h0cyBvciB0byBh 454 | c2sgeW91IHRvIHN1cnJlbmRlciB0aGUgcmlnaHRzLgpUaGVzZSByZXN0cmljdGlvbnMgdHJhbnNs 455 | YXRlIHRvIGNlcnRhaW4gcmVzcG9uc2liaWxpdGllcyBmb3IgeW91IGlmIHlvdQpkaXN0cmlidXRl 456 | IGNvcGllcyBvZiB0aGUgc29mdHdhcmUsIG9yIGlmIHlvdSBtb2RpZnkgaXQuCgogIEZvciBleGFt 457 | cGxlLCBpZiB5b3UgZGlzdHJpYnV0ZSBjb3BpZXMgb2Ygc3VjaCBhIHByb2dyYW0sIHdoZXRoZXIK 458 | Z3JhdGlzIG9yIGZvciBhIGZlZSwgeW91IG11c3QgZ2l2ZSB0aGUgcmVjaXBpZW50cyBhbGwgdGhl 459 | IHJpZ2h0cyB0aGF0CnlvdSBoYXZlLiAgWW91IG11c3QgbWFrZSBzdXJlIHRoYXQgdGhleSwgdG9v 460 | LCByZWNlaXZlIG9yIGNhbiBnZXQgdGhlCnNvdXJjZSBjb2RlLiAgQW5kIHlvdSBtdXN0IHNob3cg 461 | dGhlbSB0aGVzZSB0ZXJtcyBzbyB0aGV5IGtub3cgdGhlaXIKcmlnaHRzLgoKICBXZSBwcm90ZWN0 462 | IHlvdXIgcmlnaHRzIHdpdGggdHdvIHN0ZXBzOiAoMSkgY29weXJpZ2h0IHRoZSBzb2Z0d2FyZSwg 463 | YW5kCigyKSBvZmZlciB5b3UgdGhpcyBsaWNlbnNlIHdoaWNoIGdpdmVzIHlvdSBsZWdhbCBwZXJt 464 | aXNzaW9uIHRvIGNvcHksCmRpc3RyaWJ1dGUgYW5kL29yIG1vZGlmeSB0aGUgc29mdHdhcmUuCgog 465 | IEFsc28sIGZvciBlYWNoIGF1dGhvcidzIHByb3RlY3Rpb24gYW5kIG91cnMsIHdlIHdhbnQgdG8g 466 | bWFrZSBjZXJ0YWluCnRoYXQgZXZlcnlvbmUgdW5kZXJzdGFuZHMgdGhhdCB0aGVyZSBpcyBubyB3 467 | YXJyYW50eSBmb3IgdGhpcyBmcmVlCnNvZnR3YXJlLiAgSWYgdGhlIHNvZnR3YXJlIGlzIG1vZGlm 468 | aWVkIGJ5IHNvbWVvbmUgZWxzZSBhbmQgcGFzc2VkIG9uLCB3ZQp3YW50IGl0cyByZWNpcGllbnRz 469 | IHRvIGtub3cgdGhhdCB3aGF0IHRoZXkgaGF2ZSBpcyBub3QgdGhlIG9yaWdpbmFsLCBzbwp0aGF0 470 | IGFueSBwcm9ibGVtcyBpbnRyb2R1Y2VkIGJ5IG90aGVycyB3aWxsIG5vdCByZWZsZWN0IG9uIHRo 471 | ZSBvcmlnaW5hbAphdXRob3JzJyByZXB1dGF0aW9ucy4KCiAgRmluYWxseSwgYW55IGZyZWUgcHJv 472 | Z3JhbSBpcyB0aHJlYXRlbmVkIGNvbnN0YW50bHkgYnkgc29mdHdhcmUKcGF0ZW50cy4gIFdlIHdp 473 | c2ggdG8gYXZvaWQgdGhlIGRhbmdlciB0aGF0IHJlZGlzdHJpYnV0b3JzIG9mIGEgZnJlZQpwcm9n 474 | cmFtIHdpbGwgaW5kaXZpZHVhbGx5IG9idGFpbiBwYXRlbnQgbGljZW5zZXMsIGluIGVmZmVjdCBt 475 | YWtpbmcgdGhlCnByb2dyYW0gcHJvcHJpZXRhcnkuICBUbyBwcmV2ZW50IHRoaXMsIHdlIGhhdmUg 476 | bWFkZSBpdCBjbGVhciB0aGF0IGFueQpwYXRlbnQgbXVzdCBiZSBsaWNlbnNlZCBmb3IgZXZlcnlv 477 | bmUncyBmcmVlIHVzZSBvciBub3QgbGljZW5zZWQgYXQgYWxsLgoKICBUaGUgcHJlY2lzZSB0ZXJt 478 | cyBhbmQgY29uZGl0aW9ucyBmb3IgY29weWluZywgZGlzdHJpYnV0aW9uIGFuZAptb2RpZmljYXRp 479 | b24gZm9sbG93LgoMCgkJICAgIEdOVSBHRU5FUkFMIFBVQkxJQyBMSUNFTlNFCiAgIFRFUk1TIEFO 480 | RCBDT05ESVRJT05TIEZPUiBDT1BZSU5HLCBESVNUUklCVVRJT04gQU5EIE1PRElGSUNBVElPTgoK 481 | ICAwLiBUaGlzIExpY2Vuc2UgYXBwbGllcyB0byBhbnkgcHJvZ3JhbSBvciBvdGhlciB3b3JrIHdo 482 | aWNoIGNvbnRhaW5zCmEgbm90aWNlIHBsYWNlZCBieSB0aGUgY29weXJpZ2h0IGhvbGRlciBzYXlp 483 | bmcgaXQgbWF5IGJlIGRpc3RyaWJ1dGVkCnVuZGVyIHRoZSB0ZXJtcyBvZiB0aGlzIEdlbmVyYWwg 484 | UHVibGljIExpY2Vuc2UuICBUaGUgIlByb2dyYW0iLCBiZWxvdywKcmVmZXJzIHRvIGFueSBzdWNo 485 | IHByb2dyYW0gb3Igd29yaywgYW5kIGEgIndvcmsgYmFzZWQgb24gdGhlIFByb2dyYW0iCm1lYW5z 486 | IGVpdGhlciB0aGUgUHJvZ3JhbSBvciBhbnkgZGVyaXZhdGl2ZSB3b3JrIHVuZGVyIGNvcHlyaWdo 487 | dCBsYXc6CnRoYXQgaXMgdG8gc2F5LCBhIHdvcmsgY29udGFpbmluZyB0aGUgUHJvZ3JhbSBvciBh 488 | IHBvcnRpb24gb2YgaXQsCmVpdGhlciB2ZXJiYXRpbSBvciB3aXRoIG1vZGlmaWNhdGlvbnMgYW5k 489 | L29yIHRyYW5zbGF0ZWQgaW50byBhbm90aGVyCmxhbmd1YWdlLiAgKEhlcmVpbmFmdGVyLCB0cmFu 490 | c2xhdGlvbiBpcyBpbmNsdWRlZCB3aXRob3V0IGxpbWl0YXRpb24gaW4KdGhlIHRlcm0gIm1vZGlm 491 | aWNhdGlvbiIuKSAgRWFjaCBsaWNlbnNlZSBpcyBhZGRyZXNzZWQgYXMgInlvdSIuCgpBY3Rpdml0 492 | aWVzIG90aGVyIHRoYW4gY29weWluZywgZGlzdHJpYnV0aW9uIGFuZCBtb2RpZmljYXRpb24gYXJl 493 | IG5vdApjb3ZlcmVkIGJ5IHRoaXMgTGljZW5zZTsgdGhleSBhcmUgb3V0c2lkZSBpdHMgc2NvcGUu 494 | ICBUaGUgYWN0IG9mCnJ1bm5pbmcgdGhlIFByb2dyYW0gaXMgbm90IHJlc3RyaWN0ZWQsIGFuZCB0 495 | aGUgb3V0cHV0IGZyb20gdGhlIFByb2dyYW0KaXMgY292ZXJlZCBvbmx5IGlmIGl0cyBjb250ZW50 496 | cyBjb25zdGl0dXRlIGEgd29yayBiYXNlZCBvbiB0aGUKUHJvZ3JhbSAoaW5kZXBlbmRlbnQgb2Yg 497 | aGF2aW5nIGJlZW4gbWFkZSBieSBydW5uaW5nIHRoZSBQcm9ncmFtKS4KV2hldGhlciB0aGF0IGlz 498 | IHRydWUgZGVwZW5kcyBvbiB3aGF0IHRoZSBQcm9ncmFtIGRvZXMuCgogIDEuIFlvdSBtYXkgY29w 499 | eSBhbmQgZGlzdHJpYnV0ZSB2ZXJiYXRpbSBjb3BpZXMgb2YgdGhlIFByb2dyYW0ncwpzb3VyY2Ug 500 | Y29kZSBhcyB5b3UgcmVjZWl2ZSBpdCwgaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhhdCB5b3UK 501 | Y29uc3BpY3VvdXNseSBhbmQgYXBwcm9wcmlhdGVseSBwdWJsaXNoIG9uIGVhY2ggY29weSBhbiBh 502 | cHByb3ByaWF0ZQpjb3B5cmlnaHQgbm90aWNlIGFuZCBkaXNjbGFpbWVyIG9mIHdhcnJhbnR5OyBr 503 | ZWVwIGludGFjdCBhbGwgdGhlCm5vdGljZXMgdGhhdCByZWZlciB0byB0aGlzIExpY2Vuc2UgYW5k 504 | IHRvIHRoZSBhYnNlbmNlIG9mIGFueSB3YXJyYW50eTsKYW5kIGdpdmUgYW55IG90aGVyIHJlY2lw 505 | aWVudHMgb2YgdGhlIFByb2dyYW0gYSBjb3B5IG9mIHRoaXMgTGljZW5zZQphbG9uZyB3aXRoIHRo 506 | ZSBQcm9ncmFtLgoKWW91IG1heSBjaGFyZ2UgYSBmZWUgZm9yIHRoZSBwaHlzaWNhbCBhY3Qgb2Yg 507 | dHJhbnNmZXJyaW5nIGEgY29weSwgYW5kCnlvdSBtYXkgYXQgeW91ciBvcHRpb24gb2ZmZXIgd2Fy 508 | cmFudHkgcHJvdGVjdGlvbiBpbiBleGNoYW5nZSBmb3IgYSBmZWUuCgogIDIuIFlvdSBtYXkgbW9k 509 | aWZ5IHlvdXIgY29weSBvciBjb3BpZXMgb2YgdGhlIFByb2dyYW0gb3IgYW55IHBvcnRpb24Kb2Yg 510 | aXQsIHRodXMgZm9ybWluZyBhIHdvcmsgYmFzZWQgb24gdGhlIFByb2dyYW0sIGFuZCBjb3B5IGFu 511 | ZApkaXN0cmlidXRlIHN1Y2ggbW9kaWZpY2F0aW9ucyBvciB3b3JrIHVuZGVyIHRoZSB0ZXJtcyBv 512 | ZiBTZWN0aW9uIDEKYWJvdmUsIHByb3ZpZGVkIHRoYXQgeW91IGFsc28gbWVldCBhbGwgb2YgdGhl 513 | c2UgY29uZGl0aW9uczoKCiAgICBhKSBZb3UgbXVzdCBjYXVzZSB0aGUgbW9kaWZpZWQgZmlsZXMg 514 | dG8gY2FycnkgcHJvbWluZW50IG5vdGljZXMKICAgIHN0YXRpbmcgdGhhdCB5b3UgY2hhbmdlZCB0 515 | aGUgZmlsZXMgYW5kIHRoZSBkYXRlIG9mIGFueSBjaGFuZ2UuCgogICAgYikgWW91IG11c3QgY2F1 516 | c2UgYW55IHdvcmsgdGhhdCB5b3UgZGlzdHJpYnV0ZSBvciBwdWJsaXNoLCB0aGF0IGluCiAgICB3 517 | aG9sZSBvciBpbiBwYXJ0IGNvbnRhaW5zIG9yIGlzIGRlcml2ZWQgZnJvbSB0aGUgUHJvZ3JhbSBv 518 | ciBhbnkKICAgIHBhcnQgdGhlcmVvZiwgdG8gYmUgbGljZW5zZWQgYXMgYSB3aG9sZSBhdCBubyBj 519 | aGFyZ2UgdG8gYWxsIHRoaXJkCiAgICBwYXJ0aWVzIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGlzIExp 520 | Y2Vuc2UuCgogICAgYykgSWYgdGhlIG1vZGlmaWVkIHByb2dyYW0gbm9ybWFsbHkgcmVhZHMgY29t 521 | bWFuZHMgaW50ZXJhY3RpdmVseQogICAgd2hlbiBydW4sIHlvdSBtdXN0IGNhdXNlIGl0LCB3aGVu 522 | IHN0YXJ0ZWQgcnVubmluZyBmb3Igc3VjaAogICAgaW50ZXJhY3RpdmUgdXNlIGluIHRoZSBtb3N0 523 | IG9yZGluYXJ5IHdheSwgdG8gcHJpbnQgb3IgZGlzcGxheSBhbgogICAgYW5ub3VuY2VtZW50IGlu 524 | Y2x1ZGluZyBhbiBhcHByb3ByaWF0ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCBhCiAgICBub3RpY2Ug 525 | dGhhdCB0aGVyZSBpcyBubyB3YXJyYW50eSAob3IgZWxzZSwgc2F5aW5nIHRoYXQgeW91IHByb3Zp 526 | ZGUKICAgIGEgd2FycmFudHkpIGFuZCB0aGF0IHVzZXJzIG1heSByZWRpc3RyaWJ1dGUgdGhlIHBy 527 | b2dyYW0gdW5kZXIKICAgIHRoZXNlIGNvbmRpdGlvbnMsIGFuZCB0ZWxsaW5nIHRoZSB1c2VyIGhv 528 | dyB0byB2aWV3IGEgY29weSBvZiB0aGlzCiAgICBMaWNlbnNlLiAgKEV4Y2VwdGlvbjogaWYgdGhl 529 | IFByb2dyYW0gaXRzZWxmIGlzIGludGVyYWN0aXZlIGJ1dAogICAgZG9lcyBub3Qgbm9ybWFsbHkg 530 | cHJpbnQgc3VjaCBhbiBhbm5vdW5jZW1lbnQsIHlvdXIgd29yayBiYXNlZCBvbgogICAgdGhlIFBy 531 | b2dyYW0gaXMgbm90IHJlcXVpcmVkIHRvIHByaW50IGFuIGFubm91bmNlbWVudC4pCgwKVGhlc2Ug 532 | cmVxdWlyZW1lbnRzIGFwcGx5IHRvIHRoZSBtb2RpZmllZCB3b3JrIGFzIGEgd2hvbGUuICBJZgpp 533 | ZGVudGlmaWFibGUgc2VjdGlvbnMgb2YgdGhhdCB3b3JrIGFyZSBub3QgZGVyaXZlZCBmcm9tIHRo 534 | ZSBQcm9ncmFtLAphbmQgY2FuIGJlIHJlYXNvbmFibHkgY29uc2lkZXJlZCBpbmRlcGVuZGVudCBh 535 | bmQgc2VwYXJhdGUgd29ya3MgaW4KdGhlbXNlbHZlcywgdGhlbiB0aGlzIExpY2Vuc2UsIGFuZCBp 536 | dHMgdGVybXMsIGRvIG5vdCBhcHBseSB0byB0aG9zZQpzZWN0aW9ucyB3aGVuIHlvdSBkaXN0cmli 537 | dXRlIHRoZW0gYXMgc2VwYXJhdGUgd29ya3MuICBCdXQgd2hlbiB5b3UKZGlzdHJpYnV0ZSB0aGUg 538 | c2FtZSBzZWN0aW9ucyBhcyBwYXJ0IG9mIGEgd2hvbGUgd2hpY2ggaXMgYSB3b3JrIGJhc2VkCm9u 539 | IHRoZSBQcm9ncmFtLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB3aG9sZSBtdXN0IGJlIG9uIHRo 540 | ZSB0ZXJtcyBvZgp0aGlzIExpY2Vuc2UsIHdob3NlIHBlcm1pc3Npb25zIGZvciBvdGhlciBsaWNl 541 | bnNlZXMgZXh0ZW5kIHRvIHRoZQplbnRpcmUgd2hvbGUsIGFuZCB0aHVzIHRvIGVhY2ggYW5kIGV2 542 | ZXJ5IHBhcnQgcmVnYXJkbGVzcyBvZiB3aG8gd3JvdGUgaXQuCgpUaHVzLCBpdCBpcyBub3QgdGhl 543 | IGludGVudCBvZiB0aGlzIHNlY3Rpb24gdG8gY2xhaW0gcmlnaHRzIG9yIGNvbnRlc3QKeW91ciBy 544 | aWdodHMgdG8gd29yayB3cml0dGVuIGVudGlyZWx5IGJ5IHlvdTsgcmF0aGVyLCB0aGUgaW50ZW50 545 | IGlzIHRvCmV4ZXJjaXNlIHRoZSByaWdodCB0byBjb250cm9sIHRoZSBkaXN0cmlidXRpb24gb2Yg 546 | ZGVyaXZhdGl2ZSBvcgpjb2xsZWN0aXZlIHdvcmtzIGJhc2VkIG9uIHRoZSBQcm9ncmFtLgoKSW4g 547 | YWRkaXRpb24sIG1lcmUgYWdncmVnYXRpb24gb2YgYW5vdGhlciB3b3JrIG5vdCBiYXNlZCBvbiB0 548 | aGUgUHJvZ3JhbQp3aXRoIHRoZSBQcm9ncmFtIChvciB3aXRoIGEgd29yayBiYXNlZCBvbiB0aGUg 549 | UHJvZ3JhbSkgb24gYSB2b2x1bWUgb2YKYSBzdG9yYWdlIG9yIGRpc3RyaWJ1dGlvbiBtZWRpdW0g 550 | ZG9lcyBub3QgYnJpbmcgdGhlIG90aGVyIHdvcmsgdW5kZXIKdGhlIHNjb3BlIG9mIHRoaXMgTGlj 551 | ZW5zZS4KCiAgMy4gWW91IG1heSBjb3B5IGFuZCBkaXN0cmlidXRlIHRoZSBQcm9ncmFtIChvciBh 552 | IHdvcmsgYmFzZWQgb24gaXQsCnVuZGVyIFNlY3Rpb24gMikgaW4gb2JqZWN0IGNvZGUgb3IgZXhl 553 | Y3V0YWJsZSBmb3JtIHVuZGVyIHRoZSB0ZXJtcyBvZgpTZWN0aW9ucyAxIGFuZCAyIGFib3ZlIHBy 554 | b3ZpZGVkIHRoYXQgeW91IGFsc28gZG8gb25lIG9mIHRoZSBmb2xsb3dpbmc6CgogICAgYSkgQWNj 555 | b21wYW55IGl0IHdpdGggdGhlIGNvbXBsZXRlIGNvcnJlc3BvbmRpbmcgbWFjaGluZS1yZWFkYWJs 556 | ZQogICAgc291cmNlIGNvZGUsIHdoaWNoIG11c3QgYmUgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRl 557 | cm1zIG9mIFNlY3Rpb25zCiAgICAxIGFuZCAyIGFib3ZlIG9uIGEgbWVkaXVtIGN1c3RvbWFyaWx5 558 | IHVzZWQgZm9yIHNvZnR3YXJlIGludGVyY2hhbmdlOyBvciwKCiAgICBiKSBBY2NvbXBhbnkgaXQg 559 | d2l0aCBhIHdyaXR0ZW4gb2ZmZXIsIHZhbGlkIGZvciBhdCBsZWFzdCB0aHJlZQogICAgeWVhcnMs 560 | IHRvIGdpdmUgYW55IHRoaXJkIHBhcnR5LCBmb3IgYSBjaGFyZ2Ugbm8gbW9yZSB0aGFuIHlvdXIK 561 | ICAgIGNvc3Qgb2YgcGh5c2ljYWxseSBwZXJmb3JtaW5nIHNvdXJjZSBkaXN0cmlidXRpb24sIGEg 562 | Y29tcGxldGUKICAgIG1hY2hpbmUtcmVhZGFibGUgY29weSBvZiB0aGUgY29ycmVzcG9uZGluZyBz 563 | b3VyY2UgY29kZSwgdG8gYmUKICAgIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiBTZWN0 564 | aW9ucyAxIGFuZCAyIGFib3ZlIG9uIGEgbWVkaXVtCiAgICBjdXN0b21hcmlseSB1c2VkIGZvciBz 565 | b2Z0d2FyZSBpbnRlcmNoYW5nZTsgb3IsCgogICAgYykgQWNjb21wYW55IGl0IHdpdGggdGhlIGlu 566 | Zm9ybWF0aW9uIHlvdSByZWNlaXZlZCBhcyB0byB0aGUgb2ZmZXIKICAgIHRvIGRpc3RyaWJ1dGUg 567 | Y29ycmVzcG9uZGluZyBzb3VyY2UgY29kZS4gIChUaGlzIGFsdGVybmF0aXZlIGlzCiAgICBhbGxv 568 | d2VkIG9ubHkgZm9yIG5vbmNvbW1lcmNpYWwgZGlzdHJpYnV0aW9uIGFuZCBvbmx5IGlmIHlvdQog 569 | ICAgcmVjZWl2ZWQgdGhlIHByb2dyYW0gaW4gb2JqZWN0IGNvZGUgb3IgZXhlY3V0YWJsZSBmb3Jt 570 | IHdpdGggc3VjaAogICAgYW4gb2ZmZXIsIGluIGFjY29yZCB3aXRoIFN1YnNlY3Rpb24gYiBhYm92 571 | ZS4pCgpUaGUgc291cmNlIGNvZGUgZm9yIGEgd29yayBtZWFucyB0aGUgcHJlZmVycmVkIGZvcm0g 572 | b2YgdGhlIHdvcmsgZm9yCm1ha2luZyBtb2RpZmljYXRpb25zIHRvIGl0LiAgRm9yIGFuIGV4ZWN1 573 | dGFibGUgd29yaywgY29tcGxldGUgc291cmNlCmNvZGUgbWVhbnMgYWxsIHRoZSBzb3VyY2UgY29k 574 | ZSBmb3IgYWxsIG1vZHVsZXMgaXQgY29udGFpbnMsIHBsdXMgYW55CmFzc29jaWF0ZWQgaW50ZXJm 575 | YWNlIGRlZmluaXRpb24gZmlsZXMsIHBsdXMgdGhlIHNjcmlwdHMgdXNlZCB0bwpjb250cm9sIGNv 576 | bXBpbGF0aW9uIGFuZCBpbnN0YWxsYXRpb24gb2YgdGhlIGV4ZWN1dGFibGUuICBIb3dldmVyLCBh 577 | cyBhCnNwZWNpYWwgZXhjZXB0aW9uLCB0aGUgc291cmNlIGNvZGUgZGlzdHJpYnV0ZWQgbmVlZCBu 578 | b3QgaW5jbHVkZQphbnl0aGluZyB0aGF0IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChpbiBlaXRo 579 | ZXIgc291cmNlIG9yIGJpbmFyeQpmb3JtKSB3aXRoIHRoZSBtYWpvciBjb21wb25lbnRzIChjb21w 580 | aWxlciwga2VybmVsLCBhbmQgc28gb24pIG9mIHRoZQpvcGVyYXRpbmcgc3lzdGVtIG9uIHdoaWNo 581 | IHRoZSBleGVjdXRhYmxlIHJ1bnMsIHVubGVzcyB0aGF0IGNvbXBvbmVudAppdHNlbGYgYWNjb21w 582 | YW5pZXMgdGhlIGV4ZWN1dGFibGUuCgpJZiBkaXN0cmlidXRpb24gb2YgZXhlY3V0YWJsZSBvciBv 583 | YmplY3QgY29kZSBpcyBtYWRlIGJ5IG9mZmVyaW5nCmFjY2VzcyB0byBjb3B5IGZyb20gYSBkZXNp 584 | Z25hdGVkIHBsYWNlLCB0aGVuIG9mZmVyaW5nIGVxdWl2YWxlbnQKYWNjZXNzIHRvIGNvcHkgdGhl 585 | IHNvdXJjZSBjb2RlIGZyb20gdGhlIHNhbWUgcGxhY2UgY291bnRzIGFzCmRpc3RyaWJ1dGlvbiBv 586 | ZiB0aGUgc291cmNlIGNvZGUsIGV2ZW4gdGhvdWdoIHRoaXJkIHBhcnRpZXMgYXJlIG5vdApjb21w 587 | ZWxsZWQgdG8gY29weSB0aGUgc291cmNlIGFsb25nIHdpdGggdGhlIG9iamVjdCBjb2RlLgoMCiAg 588 | NC4gWW91IG1heSBub3QgY29weSwgbW9kaWZ5LCBzdWJsaWNlbnNlLCBvciBkaXN0cmlidXRlIHRo 589 | ZSBQcm9ncmFtCmV4Y2VwdCBhcyBleHByZXNzbHkgcHJvdmlkZWQgdW5kZXIgdGhpcyBMaWNlbnNl 590 | LiAgQW55IGF0dGVtcHQKb3RoZXJ3aXNlIHRvIGNvcHksIG1vZGlmeSwgc3VibGljZW5zZSBvciBk 591 | aXN0cmlidXRlIHRoZSBQcm9ncmFtIGlzCnZvaWQsIGFuZCB3aWxsIGF1dG9tYXRpY2FsbHkgdGVy 592 | bWluYXRlIHlvdXIgcmlnaHRzIHVuZGVyIHRoaXMgTGljZW5zZS4KSG93ZXZlciwgcGFydGllcyB3 593 | aG8gaGF2ZSByZWNlaXZlZCBjb3BpZXMsIG9yIHJpZ2h0cywgZnJvbSB5b3UgdW5kZXIKdGhpcyBM 594 | aWNlbnNlIHdpbGwgbm90IGhhdmUgdGhlaXIgbGljZW5zZXMgdGVybWluYXRlZCBzbyBsb25nIGFz 595 | IHN1Y2gKcGFydGllcyByZW1haW4gaW4gZnVsbCBjb21wbGlhbmNlLgoKICA1LiBZb3UgYXJlIG5v 596 | dCByZXF1aXJlZCB0byBhY2NlcHQgdGhpcyBMaWNlbnNlLCBzaW5jZSB5b3UgaGF2ZSBub3QKc2ln 597 | bmVkIGl0LiAgSG93ZXZlciwgbm90aGluZyBlbHNlIGdyYW50cyB5b3UgcGVybWlzc2lvbiB0byBt 598 | b2RpZnkgb3IKZGlzdHJpYnV0ZSB0aGUgUHJvZ3JhbSBvciBpdHMgZGVyaXZhdGl2ZSB3b3Jrcy4g 599 | IFRoZXNlIGFjdGlvbnMgYXJlCnByb2hpYml0ZWQgYnkgbGF3IGlmIHlvdSBkbyBub3QgYWNjZXB0 600 | IHRoaXMgTGljZW5zZS4gIFRoZXJlZm9yZSwgYnkKbW9kaWZ5aW5nIG9yIGRpc3RyaWJ1dGluZyB0 601 | aGUgUHJvZ3JhbSAob3IgYW55IHdvcmsgYmFzZWQgb24gdGhlClByb2dyYW0pLCB5b3UgaW5kaWNh 602 | dGUgeW91ciBhY2NlcHRhbmNlIG9mIHRoaXMgTGljZW5zZSB0byBkbyBzbywgYW5kCmFsbCBpdHMg 603 | dGVybXMgYW5kIGNvbmRpdGlvbnMgZm9yIGNvcHlpbmcsIGRpc3RyaWJ1dGluZyBvciBtb2RpZnlp 604 | bmcKdGhlIFByb2dyYW0gb3Igd29ya3MgYmFzZWQgb24gaXQuCgogIDYuIEVhY2ggdGltZSB5b3Ug 605 | cmVkaXN0cmlidXRlIHRoZSBQcm9ncmFtIChvciBhbnkgd29yayBiYXNlZCBvbiB0aGUKUHJvZ3Jh 606 | bSksIHRoZSByZWNpcGllbnQgYXV0b21hdGljYWxseSByZWNlaXZlcyBhIGxpY2Vuc2UgZnJvbSB0 607 | aGUKb3JpZ2luYWwgbGljZW5zb3IgdG8gY29weSwgZGlzdHJpYnV0ZSBvciBtb2RpZnkgdGhlIFBy 608 | b2dyYW0gc3ViamVjdCB0bwp0aGVzZSB0ZXJtcyBhbmQgY29uZGl0aW9ucy4gIFlvdSBtYXkgbm90 609 | IGltcG9zZSBhbnkgZnVydGhlcgpyZXN0cmljdGlvbnMgb24gdGhlIHJlY2lwaWVudHMnIGV4ZXJj 610 | aXNlIG9mIHRoZSByaWdodHMgZ3JhbnRlZCBoZXJlaW4uCllvdSBhcmUgbm90IHJlc3BvbnNpYmxl 611 | IGZvciBlbmZvcmNpbmcgY29tcGxpYW5jZSBieSB0aGlyZCBwYXJ0aWVzIHRvCnRoaXMgTGljZW5z 612 | ZS4KCiAgNy4gSWYsIGFzIGEgY29uc2VxdWVuY2Ugb2YgYSBjb3VydCBqdWRnbWVudCBvciBhbGxl 613 | Z2F0aW9uIG9mIHBhdGVudAppbmZyaW5nZW1lbnQgb3IgZm9yIGFueSBvdGhlciByZWFzb24gKG5v 614 | dCBsaW1pdGVkIHRvIHBhdGVudCBpc3N1ZXMpLApjb25kaXRpb25zIGFyZSBpbXBvc2VkIG9uIHlv 615 | dSAod2hldGhlciBieSBjb3VydCBvcmRlciwgYWdyZWVtZW50IG9yCm90aGVyd2lzZSkgdGhhdCBj 616 | b250cmFkaWN0IHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZSwgdGhleSBkbyBub3QKZXhj 617 | dXNlIHlvdSBmcm9tIHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZS4gIElmIHlvdSBjYW5u 618 | b3QKZGlzdHJpYnV0ZSBzbyBhcyB0byBzYXRpc2Z5IHNpbXVsdGFuZW91c2x5IHlvdXIgb2JsaWdh 619 | dGlvbnMgdW5kZXIgdGhpcwpMaWNlbnNlIGFuZCBhbnkgb3RoZXIgcGVydGluZW50IG9ibGlnYXRp 620 | b25zLCB0aGVuIGFzIGEgY29uc2VxdWVuY2UgeW91Cm1heSBub3QgZGlzdHJpYnV0ZSB0aGUgUHJv 621 | Z3JhbSBhdCBhbGwuICBGb3IgZXhhbXBsZSwgaWYgYSBwYXRlbnQKbGljZW5zZSB3b3VsZCBub3Qg 622 | cGVybWl0IHJveWFsdHktZnJlZSByZWRpc3RyaWJ1dGlvbiBvZiB0aGUgUHJvZ3JhbSBieQphbGwg 623 | dGhvc2Ugd2hvIHJlY2VpdmUgY29waWVzIGRpcmVjdGx5IG9yIGluZGlyZWN0bHkgdGhyb3VnaCB5 624 | b3UsIHRoZW4KdGhlIG9ubHkgd2F5IHlvdSBjb3VsZCBzYXRpc2Z5IGJvdGggaXQgYW5kIHRoaXMg 625 | TGljZW5zZSB3b3VsZCBiZSB0bwpyZWZyYWluIGVudGlyZWx5IGZyb20gZGlzdHJpYnV0aW9uIG9m 626 | IHRoZSBQcm9ncmFtLgoKSWYgYW55IHBvcnRpb24gb2YgdGhpcyBzZWN0aW9uIGlzIGhlbGQgaW52 627 | YWxpZCBvciB1bmVuZm9yY2VhYmxlIHVuZGVyCmFueSBwYXJ0aWN1bGFyIGNpcmN1bXN0YW5jZSwg 628 | dGhlIGJhbGFuY2Ugb2YgdGhlIHNlY3Rpb24gaXMgaW50ZW5kZWQgdG8KYXBwbHkgYW5kIHRoZSBz 629 | ZWN0aW9uIGFzIGEgd2hvbGUgaXMgaW50ZW5kZWQgdG8gYXBwbHkgaW4gb3RoZXIKY2lyY3Vtc3Rh 630 | bmNlcy4KCkl0IGlzIG5vdCB0aGUgcHVycG9zZSBvZiB0aGlzIHNlY3Rpb24gdG8gaW5kdWNlIHlv 631 | dSB0byBpbmZyaW5nZSBhbnkKcGF0ZW50cyBvciBvdGhlciBwcm9wZXJ0eSByaWdodCBjbGFpbXMg 632 | b3IgdG8gY29udGVzdCB2YWxpZGl0eSBvZiBhbnkKc3VjaCBjbGFpbXM7IHRoaXMgc2VjdGlvbiBo 633 | YXMgdGhlIHNvbGUgcHVycG9zZSBvZiBwcm90ZWN0aW5nIHRoZQppbnRlZ3JpdHkgb2YgdGhlIGZy 634 | ZWUgc29mdHdhcmUgZGlzdHJpYnV0aW9uIHN5c3RlbSwgd2hpY2ggaXMKaW1wbGVtZW50ZWQgYnkg 635 | cHVibGljIGxpY2Vuc2UgcHJhY3RpY2VzLiAgTWFueSBwZW9wbGUgaGF2ZSBtYWRlCmdlbmVyb3Vz 636 | IGNvbnRyaWJ1dGlvbnMgdG8gdGhlIHdpZGUgcmFuZ2Ugb2Ygc29mdHdhcmUgZGlzdHJpYnV0ZWQK 637 | dGhyb3VnaCB0aGF0IHN5c3RlbSBpbiByZWxpYW5jZSBvbiBjb25zaXN0ZW50IGFwcGxpY2F0aW9u 638 | IG9mIHRoYXQKc3lzdGVtOyBpdCBpcyB1cCB0byB0aGUgYXV0aG9yL2Rvbm9yIHRvIGRlY2lkZSBp 639 | ZiBoZSBvciBzaGUgaXMgd2lsbGluZwp0byBkaXN0cmlidXRlIHNvZnR3YXJlIHRocm91Z2ggYW55 640 | IG90aGVyIHN5c3RlbSBhbmQgYSBsaWNlbnNlZSBjYW5ub3QKaW1wb3NlIHRoYXQgY2hvaWNlLgoK 641 | VGhpcyBzZWN0aW9uIGlzIGludGVuZGVkIHRvIG1ha2UgdGhvcm91Z2hseSBjbGVhciB3aGF0IGlz 642 | IGJlbGlldmVkIHRvCmJlIGEgY29uc2VxdWVuY2Ugb2YgdGhlIHJlc3Qgb2YgdGhpcyBMaWNlbnNl 643 | LgoMCiAgOC4gSWYgdGhlIGRpc3RyaWJ1dGlvbiBhbmQvb3IgdXNlIG9mIHRoZSBQcm9ncmFtIGlz 644 | IHJlc3RyaWN0ZWQgaW4KY2VydGFpbiBjb3VudHJpZXMgZWl0aGVyIGJ5IHBhdGVudHMgb3IgYnkg 645 | Y29weXJpZ2h0ZWQgaW50ZXJmYWNlcywgdGhlCm9yaWdpbmFsIGNvcHlyaWdodCBob2xkZXIgd2hv 646 | IHBsYWNlcyB0aGUgUHJvZ3JhbSB1bmRlciB0aGlzIExpY2Vuc2UKbWF5IGFkZCBhbiBleHBsaWNp 647 | dCBnZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9uIGxpbWl0YXRpb24gZXhjbHVkaW5nCnRob3NlIGNv 648 | dW50cmllcywgc28gdGhhdCBkaXN0cmlidXRpb24gaXMgcGVybWl0dGVkIG9ubHkgaW4gb3IgYW1v 649 | bmcKY291bnRyaWVzIG5vdCB0aHVzIGV4Y2x1ZGVkLiAgSW4gc3VjaCBjYXNlLCB0aGlzIExpY2Vu 650 | c2UgaW5jb3Jwb3JhdGVzCnRoZSBsaW1pdGF0aW9uIGFzIGlmIHdyaXR0ZW4gaW4gdGhlIGJvZHkg 651 | b2YgdGhpcyBMaWNlbnNlLgoKICA5LiBUaGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIG1heSBw 652 | dWJsaXNoIHJldmlzZWQgYW5kL29yIG5ldyB2ZXJzaW9ucwpvZiB0aGUgR2VuZXJhbCBQdWJsaWMg 653 | TGljZW5zZSBmcm9tIHRpbWUgdG8gdGltZS4gIFN1Y2ggbmV3IHZlcnNpb25zIHdpbGwKYmUgc2lt 654 | aWxhciBpbiBzcGlyaXQgdG8gdGhlIHByZXNlbnQgdmVyc2lvbiwgYnV0IG1heSBkaWZmZXIgaW4g 655 | ZGV0YWlsIHRvCmFkZHJlc3MgbmV3IHByb2JsZW1zIG9yIGNvbmNlcm5zLgoKRWFjaCB2ZXJzaW9u 656 | IGlzIGdpdmVuIGEgZGlzdGluZ3Vpc2hpbmcgdmVyc2lvbiBudW1iZXIuICBJZiB0aGUgUHJvZ3Jh 657 | bQpzcGVjaWZpZXMgYSB2ZXJzaW9uIG51bWJlciBvZiB0aGlzIExpY2Vuc2Ugd2hpY2ggYXBwbGll 658 | cyB0byBpdCBhbmQgImFueQpsYXRlciB2ZXJzaW9uIiwgeW91IGhhdmUgdGhlIG9wdGlvbiBvZiBm 659 | b2xsb3dpbmcgdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zCmVpdGhlciBvZiB0aGF0IHZlcnNpb24g 660 | b3Igb2YgYW55IGxhdGVyIHZlcnNpb24gcHVibGlzaGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZv 661 | dW5kYXRpb24uICBJZiB0aGUgUHJvZ3JhbSBkb2VzIG5vdCBzcGVjaWZ5IGEgdmVyc2lvbiBudW1i 662 | ZXIgb2YKdGhpcyBMaWNlbnNlLCB5b3UgbWF5IGNob29zZSBhbnkgdmVyc2lvbiBldmVyIHB1Ymxp 663 | c2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZQpGb3VuZGF0aW9uLgoKICAxMC4gSWYgeW91IHdpc2gg 664 | dG8gaW5jb3Jwb3JhdGUgcGFydHMgb2YgdGhlIFByb2dyYW0gaW50byBvdGhlciBmcmVlCnByb2dy 665 | YW1zIHdob3NlIGRpc3RyaWJ1dGlvbiBjb25kaXRpb25zIGFyZSBkaWZmZXJlbnQsIHdyaXRlIHRv 666 | IHRoZSBhdXRob3IKdG8gYXNrIGZvciBwZXJtaXNzaW9uLiAgRm9yIHNvZnR3YXJlIHdoaWNoIGlz 667 | IGNvcHlyaWdodGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZvdW5kYXRpb24sIHdyaXRlIHRvIHRo 668 | ZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHdlIHNvbWV0aW1lcwptYWtlIGV4Y2VwdGlvbnMg 669 | Zm9yIHRoaXMuICBPdXIgZGVjaXNpb24gd2lsbCBiZSBndWlkZWQgYnkgdGhlIHR3byBnb2Fscwpv 670 | ZiBwcmVzZXJ2aW5nIHRoZSBmcmVlIHN0YXR1cyBvZiBhbGwgZGVyaXZhdGl2ZXMgb2Ygb3VyIGZy 671 | ZWUgc29mdHdhcmUgYW5kCm9mIHByb21vdGluZyB0aGUgc2hhcmluZyBhbmQgcmV1c2Ugb2Ygc29m 672 | dHdhcmUgZ2VuZXJhbGx5LgoKCQkJICAgIE5PIFdBUlJBTlRZCgogIDExLiBCRUNBVVNFIFRIRSBQ 673 | Uk9HUkFNIElTIExJQ0VOU0VEIEZSRUUgT0YgQ0hBUkdFLCBUSEVSRSBJUyBOTyBXQVJSQU5UWQpG 674 | T1IgVEhFIFBST0dSQU0sIFRPIFRIRSBFWFRFTlQgUEVSTUlUVEVEIEJZIEFQUExJQ0FCTEUgTEFX 675 | LiAgRVhDRVBUIFdIRU4KT1RIRVJXSVNFIFNUQVRFRCBJTiBXUklUSU5HIFRIRSBDT1BZUklHSFQg 676 | SE9MREVSUyBBTkQvT1IgT1RIRVIgUEFSVElFUwpQUk9WSURFIFRIRSBQUk9HUkFNICJBUyBJUyIg 677 | V0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRUlUSEVSIEVYUFJFU1NFRApPUiBJTVBMSUVE 678 | LCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgVEhFIElNUExJRUQgV0FSUkFOVElFUyBP 679 | RgpNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAg 680 | VEhFIEVOVElSRSBSSVNLIEFTClRPIFRIRSBRVUFMSVRZIEFORCBQRVJGT1JNQU5DRSBPRiBUSEUg 681 | UFJPR1JBTSBJUyBXSVRIIFlPVS4gIFNIT1VMRCBUSEUKUFJPR1JBTSBQUk9WRSBERUZFQ1RJVkUs 682 | IFlPVSBBU1NVTUUgVEhFIENPU1QgT0YgQUxMIE5FQ0VTU0FSWSBTRVJWSUNJTkcsClJFUEFJUiBP 683 | UiBDT1JSRUNUSU9OLgoKICAxMi4gSU4gTk8gRVZFTlQgVU5MRVNTIFJFUVVJUkVEIEJZIEFQUExJ 684 | Q0FCTEUgTEFXIE9SIEFHUkVFRCBUTyBJTiBXUklUSU5HCldJTEwgQU5ZIENPUFlSSUdIVCBIT0xE 685 | RVIsIE9SIEFOWSBPVEhFUiBQQVJUWSBXSE8gTUFZIE1PRElGWSBBTkQvT1IKUkVESVNUUklCVVRF 686 | IFRIRSBQUk9HUkFNIEFTIFBFUk1JVFRFRCBBQk9WRSwgQkUgTElBQkxFIFRPIFlPVSBGT1IgREFN 687 | QUdFUywKSU5DTFVESU5HIEFOWSBHRU5FUkFMLCBTUEVDSUFMLCBJTkNJREVOVEFMIE9SIENPTlNF 688 | UVVFTlRJQUwgREFNQUdFUyBBUklTSU5HCk9VVCBPRiBUSEUgVVNFIE9SIElOQUJJTElUWSBUTyBV 689 | U0UgVEhFIFBST0dSQU0gKElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQKVE8gTE9TUyBPRiBEQVRB 690 | IE9SIERBVEEgQkVJTkcgUkVOREVSRUQgSU5BQ0NVUkFURSBPUiBMT1NTRVMgU1VTVEFJTkVEIEJZ 691 | CllPVSBPUiBUSElSRCBQQVJUSUVTIE9SIEEgRkFJTFVSRSBPRiBUSEUgUFJPR1JBTSBUTyBPUEVS 692 | QVRFIFdJVEggQU5ZIE9USEVSClBST0dSQU1TKSwgRVZFTiBJRiBTVUNIIEhPTERFUiBPUiBPVEhF 693 | UiBQQVJUWSBIQVMgQkVFTiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFH 694 | RVMuCgoJCSAgICAgRU5EIE9GIFRFUk1TIEFORCBDT05ESVRJT05TCgwKCSAgICBIb3cgdG8gQXBw 695 | bHkgVGhlc2UgVGVybXMgdG8gWW91ciBOZXcgUHJvZ3JhbXMKCiAgSWYgeW91IGRldmVsb3AgYSBu 696 | ZXcgcHJvZ3JhbSwgYW5kIHlvdSB3YW50IGl0IHRvIGJlIG9mIHRoZSBncmVhdGVzdApwb3NzaWJs 697 | ZSB1c2UgdG8gdGhlIHB1YmxpYywgdGhlIGJlc3Qgd2F5IHRvIGFjaGlldmUgdGhpcyBpcyB0byBt 698 | YWtlIGl0CmZyZWUgc29mdHdhcmUgd2hpY2ggZXZlcnlvbmUgY2FuIHJlZGlzdHJpYnV0ZSBhbmQg 699 | Y2hhbmdlIHVuZGVyIHRoZXNlIHRlcm1zLgoKICBUbyBkbyBzbywgYXR0YWNoIHRoZSBmb2xsb3dp 700 | bmcgbm90aWNlcyB0byB0aGUgcHJvZ3JhbS4gIEl0IGlzIHNhZmVzdAp0byBhdHRhY2ggdGhlbSB0 701 | byB0aGUgc3RhcnQgb2YgZWFjaCBzb3VyY2UgZmlsZSB0byBtb3N0IGVmZmVjdGl2ZWx5CmNvbnZl 702 | eSB0aGUgZXhjbHVzaW9uIG9mIHdhcnJhbnR5OyBhbmQgZWFjaCBmaWxlIHNob3VsZCBoYXZlIGF0 703 | IGxlYXN0CnRoZSAiY29weXJpZ2h0IiBsaW5lIGFuZCBhIHBvaW50ZXIgdG8gd2hlcmUgdGhlIGZ1 704 | bGwgbm90aWNlIGlzIGZvdW5kLgoKICAgIDxvbmUgbGluZSB0byBnaXZlIHRoZSBwcm9ncmFtJ3Mg 705 | bmFtZSBhbmQgYSBicmllZiBpZGVhIG9mIHdoYXQgaXQgZG9lcy4+CiAgICBDb3B5cmlnaHQgKEMp 706 | IDE5eXkgIDxuYW1lIG9mIGF1dGhvcj4KCiAgICBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2Fy 707 | ZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQogICAgaXQgdW5kZXIgdGhl 708 | IHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnkK 709 | ICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhl 710 | IExpY2Vuc2UsIG9yCiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLgoKICAg 711 | IFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUg 712 | dXNlZnVsLAogICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGlt 713 | cGxpZWQgd2FycmFudHkgb2YKICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBB 714 | UlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKICAgIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl 715 | IGZvciBtb3JlIGRldGFpbHMuCgogICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBv 716 | ZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKICAgIGFsb25nIHdpdGggdGhpcyBwcm9n 717 | cmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBJ 718 | bmMuLCA1OSBUZW1wbGUgUGxhY2UsIFN1aXRlIDMzMCwgQm9zdG9uLCBNQSAgMDIxMTEtMTMwNyAg 719 | VVNBCgoKQWxzbyBhZGQgaW5mb3JtYXRpb24gb24gaG93IHRvIGNvbnRhY3QgeW91IGJ5IGVsZWN0 720 | cm9uaWMgYW5kIHBhcGVyIG1haWwuCgpJZiB0aGUgcHJvZ3JhbSBpcyBpbnRlcmFjdGl2ZSwgbWFr 721 | ZSBpdCBvdXRwdXQgYSBzaG9ydCBub3RpY2UgbGlrZSB0aGlzCndoZW4gaXQgc3RhcnRzIGluIGFu 722 | IGludGVyYWN0aXZlIG1vZGU6CgogICAgR25vbW92aXNpb24gdmVyc2lvbiA2OSwgQ29weXJpZ2h0 723 | IChDKSAxOXl5IG5hbWUgb2YgYXV0aG9yCiAgICBHbm9tb3Zpc2lvbiBjb21lcyB3aXRoIEFCU09M 724 | VVRFTFkgTk8gV0FSUkFOVFk7IGZvciBkZXRhaWxzIHR5cGUgYHNob3cgdycuCiAgICBUaGlzIGlz 725 | IGZyZWUgc29mdHdhcmUsIGFuZCB5b3UgYXJlIHdlbGNvbWUgdG8gcmVkaXN0cmlidXRlIGl0CiAg 726 | ICB1bmRlciBjZXJ0YWluIGNvbmRpdGlvbnM7IHR5cGUgYHNob3cgYycgZm9yIGRldGFpbHMuCgpU 727 | aGUgaHlwb3RoZXRpY2FsIGNvbW1hbmRzIGBzaG93IHcnIGFuZCBgc2hvdyBjJyBzaG91bGQgc2hv 728 | dyB0aGUgYXBwcm9wcmlhdGUKcGFydHMgb2YgdGhlIEdlbmVyYWwgUHVibGljIExpY2Vuc2UuICBP 729 | ZiBjb3Vyc2UsIHRoZSBjb21tYW5kcyB5b3UgdXNlIG1heQpiZSBjYWxsZWQgc29tZXRoaW5nIG90 730 | aGVyIHRoYW4gYHNob3cgdycgYW5kIGBzaG93IGMnOyB0aGV5IGNvdWxkIGV2ZW4gYmUKbW91c2Ut 731 | Y2xpY2tzIG9yIG1lbnUgaXRlbXMtLXdoYXRldmVyIHN1aXRzIHlvdXIgcHJvZ3JhbS4KCllvdSBz 732 | aG91bGQgYWxzbyBnZXQgeW91ciBlbXBsb3llciAoaWYgeW91IHdvcmsgYXMgYSBwcm9ncmFtbWVy 733 | KSBvciB5b3VyCnNjaG9vbCwgaWYgYW55LCB0byBzaWduIGEgImNvcHlyaWdodCBkaXNjbGFpbWVy 734 | IiBmb3IgdGhlIHByb2dyYW0sIGlmCm5lY2Vzc2FyeS4gIEhlcmUgaXMgYSBzYW1wbGU7IGFsdGVy 735 | IHRoZSBuYW1lczoKCiAgWW95b2R5bmUsIEluYy4sIGhlcmVieSBkaXNjbGFpbXMgYWxsIGNvcHly 736 | aWdodCBpbnRlcmVzdCBpbiB0aGUgcHJvZ3JhbQogIGBHbm9tb3Zpc2lvbicgKHdoaWNoIG1ha2Vz 737 | IHBhc3NlcyBhdCBjb21waWxlcnMpIHdyaXR0ZW4gYnkgSmFtZXMgSGFja2VyLgoKICA8c2lnbmF0 738 | dXJlIG9mIFR5IENvb24+LCAxIEFwcmlsIDE5ODkKICBUeSBDb29uLCBQcmVzaWRlbnQgb2YgVmlj 739 | ZQoKVGhpcyBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGRvZXMgbm90IHBlcm1pdCBpbmNvcnBvcmF0 740 | aW5nIHlvdXIgcHJvZ3JhbSBpbnRvCnByb3ByaWV0YXJ5IHByb2dyYW1zLiAgSWYgeW91ciBwcm9n 741 | cmFtIGlzIGEgc3Vicm91dGluZSBsaWJyYXJ5LCB5b3UgbWF5CmNvbnNpZGVyIGl0IG1vcmUgdXNl 742 | ZnVsIHRvIHBlcm1pdCBsaW5raW5nIHByb3ByaWV0YXJ5IGFwcGxpY2F0aW9ucyB3aXRoIHRoZQps 743 | aWJyYXJ5LiAgSWYgdGhpcyBpcyB3aGF0IHlvdSB3YW50IHRvIGRvLCB1c2UgdGhlIEdOVSBMaWJy 744 | YXJ5IEdlbmVyYWwKUHVibGljIExpY2Vuc2UgaW5zdGVhZCBvZiB0aGlzIExpY2Vuc2UuCg== 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | -------------------------------------------------------------------------------- /tests/test_data/config/component_mappings.yml: -------------------------------------------------------------------------------- 1 | # list all of the bugzilla components, and their desired gitlab issue label 2 | branching-framework: branch 3 | Build Envrionment: makefile 4 | conflict: core 5 | cons-bivariate: cons 6 | Documentation: documentation 7 | fileio: core 8 | statistics: core 9 | -------------------------------------------------------------------------------- /tests/test_data/config/defaults.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # If true, don't execute any `put` or `post` requests. 3 | # dry_run=true still executes get requests though. 4 | # set to true for debugging or development. 5 | dry_run: true 6 | 7 | # gitlab api base url without trailing slash 8 | gitlab_base_url: "https://git.example.com/api/v4" 9 | 10 | # Enable tls certification verification. Disable for local development. 11 | verify: true 12 | 13 | # Auto-generate the issue id in Gitlab. If set to `true`, create a Gitlab issue with 14 | # the same id as the Bugzilla ticket number. The group members which are 15 | # mapped in the user mappings need to be defined as project owners for this 16 | # to work properly. Otherwise the Gitlab API will silently ignore the `iid` setting 17 | # and fallback to the default behavior (i.e. auto-generate the issue id). 18 | use_bugzilla_id: false 19 | 20 | # The gitlab project id 21 | gitlab_project_id: 5 22 | 23 | # The private token of the gitlab Administrator. This program needs GitLab sudo to run. 24 | # Read more about sudo here: 25 | # http://docs.gitlab.com/ce/api/#sudo 26 | gitlab_private_token: "SUPERSECRETTOKEN" 27 | 28 | # bugzilla base url without trailing slash 29 | bugzilla_base_url: "https://landfill.bugzilla.org/bugzilla-5.0-branch" 30 | 31 | # bugzilla user, set to an empty string when anonymous access is allowed 32 | bugzilla_user: 33 | 34 | # if not empty, you will be prompted for password once upon script startup 35 | bugzilla_password: 36 | 37 | # The way to format datetime strings in the gitlab issue description and comment body. 38 | datetime_format_string: "%b %d, %Y %H:%M" 39 | 40 | # Generic bugzilla user that bug reporting forms would create bugs as. Performs better formatting for the Issue description 41 | # Optional 42 | bugzilla_auto_reporter: "webreporter" 43 | 44 | # Bugzilla states that close the issue in gitlab 45 | bugzilla_closed_states: 46 | - "RESOLVED" 47 | - "VERIFIED" 48 | - "CLOSED" 49 | 50 | # Generic gitLab user for misc or old bugzilla users that don't have GitLab accounts 51 | gitlab_misc_user: "bugzilla" 52 | 53 | # Default label(s) to add to all migrated bugs 54 | # Optional 55 | default_gitlab_labels: 56 | - "bugzilla" 57 | - "legacy" 58 | 59 | # Include a link to the original bugzilla bug in the GitLab issue description 60 | include_bugzilla_link: true 61 | 62 | # Set to true to map bugzilla operating system to GitLab 63 | map_operating_system: false 64 | 65 | # Set to true to map bugzilla keywords to GitLab 66 | map_keywords: true 67 | 68 | # Do not map these bugzilla keywords to GitLab 69 | keywords_to_skip: 70 | - "SKIPME" 71 | - "NOTHING" 72 | # Set to true to map bugzilla milestones to GitLab 73 | map_milestones: true 74 | 75 | # Do not map these bugzilla milestones to GitLab 76 | milestones_to_skip: 77 | - "---" 78 | - "UNKNOWN" 79 | -------------------------------------------------------------------------------- /tests/test_data/config/user_mappings.yml: -------------------------------------------------------------------------------- 1 | --- 2 | matt: mcline 3 | cyeh: cyeh 4 | christyloanle: cloanle 5 | bmc: bugzilla 6 | --------------------------------------------------------------------------------