rest2web-0.5.2~alpha+svn-r248.orig/0000700000175000017500000000000010644674645016525 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/0000700000175000017500000000000010644674642017452 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/special_files.txt0000600000175000017500000000470210327117674023015 0ustar madduckmadduckrestindex crumb: Special Files page-description: A list of all the *special filenames* that **rest2web** uses - and what they are for. /description /restindex =============== Special Files =============== ---------------------------------------- Filenames With Particular Significance ---------------------------------------- .. contents:: **rest2web** works by scanning files in the directories it is working with. Some files have particular significance. This page lists all the *special files* used by rest2web - and what they do. index.txt ========= A directory will only be processed if it has an 'index.txt' file. This file needn't contain the contents of the index page, but it must exist. At the least it should contain the keyword ``index-file`` which tells **rest2web** which file *does* contain the index page contents. If the restindex for 'index.txt' does have an ``index-file`` keyword, then this is the only value that is read. All other values will be read from the real index file. template.txt ============ If the index file doesn't specify a template file, using the ``template`` keyword in the restindex, but a file called 'template.txt' does exist - then it will be used as the template for that page and directory. If you wanted to include a page of contents called 'template.txt' then you would need to include a ``template`` keyword explicitly pointing to the real template file. restindex.txt ============= If you are only using **rest2web** to generate part of a website then you may want to include in your indexes pages that rest2web isn't building. You may also want to reproduce your website structure (including whole sections that rest2web isn't building), so that the navigation links are built correctly. In this situation you may have several pages that **rest2web** isn't building in a section. Rather than having to include a separate file for each one, you can combine several restindexes in one file called 'restindex.txt'. You don't need to explicitly set the ``build`` keyword to 'No' - that's assumed. Each restindex *must* have a target value though (unless you want a link pointing to 'restindex.html' !). __prune__ ========== If a directory has a file called ``__prune__`` in it, then **rest2web** won't attempt to build the files in that directory or below. This is useful if you are testing minor changes to a site, and don't want **rest2web** to rebuild the whole site every time you make a change. rest2web-0.5.2~alpha+svn-r248.orig/docs/macros.txt0000600000175000017500000005046010513540211021461 0ustar madduckmadduckrestindex crumb: Macros page-description: The text macro system used by **rest2web**. Includes documentation on all the example macros. /description /restindex ==================== Macros in rest2web ==================== -------------- Using Macros -------------- .. contents:: Macros About macros ============ Macros allow you to use a shorthand for often-used text or HTML. As a simple example, let's say that I use a certain link a lot. I don't want to type it over and over again. I can define a macro foo to include this link every time I type ``{foo}``: .. class:: ex foo = 'My link' In this case, the "macro definition" is simply a string that contains the HTML we want to substitute. We can use more sophisticated macros, though. Let's assume that I use acronyms a lot, and I'm getting tired of having to type that tag again and again. It would be nice if I could type something like ``{curlyl}acronym;IMO;In My Opinion{curlyr}`` which expands to {acronym;IMO;In My Opinion}. (Move your mouse over the "IMO" to see the effect. Not all browsers may support this.) To achieve this, we define a simple Python function: .. raw:: html {+coloring} def acronym(acronym, meaning): return '%s' % ( meaning, acronym) {-coloring} {curlyl}acronym;IMO;In My Opinion{curlyr} is equivalent to the function call ``acronym("IMO", "In My Opinion")``. These macros can also be used to do more complex things, like insert pictures, include whole files etc. .. note:: Macros pass through the ReST processing untouched (unless you include characters that ReStructured text normal escapes, like '<'). This means that macros on a line of their own will be processed as a paragraph. The generated HTML will be between paragraph tags: ``

...

``. Where are Macros Defined? ========================= As of **rest2web 0.5.0** all the macros described here are built-in to rest2web. That means you can use them without supplying a macros file. Two of the built in macros require paths to operate properly. You can do this using the `config file `_. See the smiley_ and emoticon_ macros. {sm;:-)} You can still override these built-in macros, by defining them in your own macros file. Anything in the macros file will override the built in ones. The Macros File --------------- In the `rest2web config file`_ you can specify a path to a macros module. This can have any name, but it should be a module that Python can import. Basically that means the filename has to end in *'.py'* [#]_. So, to get started, here's what you need to do: * Create a file called *macros.py*. * Add definitions to it. (It helps if you know some Python, of course, but even without that you can enter simple string macros of the form ``name = 'text'``.) * Put the location of this file in the rest2web config file for your site. Things you should know ====================== * Macros are string-based. If a macro's return value isn't a string, then it's turned into one (if at all possible). * Arguments for a function call are strings too; ``{foobar;42}`` is the function call ``foobar("42")``, not ``foobar(42)``. If you want a different type, you'll have to explicitly convert in inside your function. * Functions with no arguments are called by their name: ``{baz}`` is equivalent to ``baz()``, if baz is a callable object. If not, then the value is taken. * If a macro name isn't found, it is left alone. In fact, this piece of documentation exists as it is because of this rule. Text like ``{knarf}`` would be replaced' if it was a macro. Since we didn't define a macro knarf, it is left alone and shows up in text with curly braces and all. * If an error occurs in a macro (function call), it is left alone as well. So are macros containing newlines. * If you want to include a semicolon (;) or a curly brace in your arguments, you're out of luck. There are currently no ways to do this. You can always define escape sequences in your functions of course. * Ditto if you want to suppress a macro call that would normally be substituted. *other than* the ``{curlyl}curlyl{curlyr}``, and ``{curlyl}curlyr{curlyr}``, macros. See `The Example Macros`_ below. * Macros map to Python objects. Usually you'll want to use strings or functions, but other objects are possible as well. * If you define a function you can also give it a shorter name. For example, to give our *smiley* function the shorter name *sm*, I included the line ``sm = smiley`` after the function definition. The Order --------- Macros are resolved *after* the pages has been processed by docutils [#]_. This means your macros should return html for direct inclusion in the final page. They are resolved *before* the page is passed to the template engine. This means that dynamic values like ``<% path_to_root %>`` output by macros *will* be resolved. Filepaths --------- Before the macros are run, the current directory is changed to be the same as the macros file. This means all file paths supplied to macros should be relative to this file. Modules ------- The modules directory in the **rest2web** distribution includes various Python modules used by the macros. If you aren't using macros then you *don't* need this directory. Credits for all the modules here are in the Credits_ section below. This directory is put in the Python search path. If you move them elsewhere you should make sure they are on the ``sys.path``. If you don't know what I'm on about then leave them where they are {sm;:grin:} Advanced Macros =============== As well as the normal macros discussed above, there is a more advanced system of macros as well. This allows you to apply a macro to a whole chunk of text. Unlike the simple macros, these macros can be nested to apply several effects to passages. The advanced macros work by enclosing a passage of text between a ``{+macro}`` and a ``{-macro}``. The macro is applied to all the text between the **+** and the **-**. In this case it would be applied to *and a*. You can also nest them So you can do things like : :: {cl}+second{cr}{cl}+first{cr} The text {cl}-second{cr}{cl}-first{cr} {cl}+whole{cr} Some text {cl}+part{cr} a bit in the middle {cl}-part{cr} more text{cl}-whole{cr} These macros aren't the same as normal macros though. Here's the idea. While a regular macro ``{cl}f;x;y{cr}`` translates to a function call ``f("x", "y")``, the + and - versions use a class definition. A simple example : .. raw:: html {+coloring} class rot13(textmacros.BaseMacro): def open(self, text): return text.encode("rot13") {-coloring} Once this is defined, in your macros file (or just imported into it), you can use it as follows : :: The solution is {+rot13}not for your eyes{-rot13}. Upon execution, this will convert the text between the rot13 tags. This becomes : :: The solution is abg sbe lbhe rlrf. In your class the +tag corresponds to the class's ``open(text)`` method, the -tag to the ``close()`` method. Note that if you are using docutils to process your text then it will be processed by docutils before it is processed by the macro. If you want to bypass this, and pass the text to your macro *only*, then you need to use the ``.. raw:: html`` directive. e.g. : :: .. raw:: html {+rot13} this text will *not* be processed as reST {-rot13} The Example Macros ================== These are the macros that come built into rest2web. curlyl and curlyr ----------------- These two macros perform simple substitution. They are a way of including curly left hand brackets and curly right hand brackets in your pages. They are also have the shorter forms ``cl`` and ``cr``. For example, to include ``{cl}example{cr}`` in your page - without it being interpreted as a macro - you can write ``{cl}curlyl{cr}example{cl}curlyr{cr}`` or ``{cl}cl{cr}example{cl}cr{cr}``. This came in very handy when creating this page {sm;:lol:} lt -- This is another simple substitution macro. It puts a '<' (less than) symbol into pages. It is especially where you need to include a literal ``{lt}$ ... $>`` or ``{lt}* .. *>`` in your pages. Example : ``{cl}lt{cr}`` smiley ------ This is one of the nicest macros. It uses a modified version of *smiley.py* by `Mark Andrews`_ to put smilies onto your site. {sm;:-p} In order to use the smilies, rest2web needs to know the path to the smiley images on your hard drive, and also what URL path you want to be used in the image links that rest2web generates. You do this in the ``[Macro Paths]`` section of your `config file`_. The two values to supply are : * ``smiley_directory`` The default is to use the standard set which come built in to rest2web. * ``smiley_url`` The default is to use the following path, ``'<% path_to_root %>images/smilies/'``. You can use ``sm`` as a shorter alias for ``smiley``. Examples : ``{cl}smiley;:-){cr}`` becomes {smiley;:-)} ``{cl}sm;:roll:{cr}`` becomes {sm;:roll:} It will read standard smiley packages like the ones used by phpbb_. Download more from the stylesdb_ site. You can see a full list of all of the smilies from the example set in the `Smilies Page`_. acronym ------- We`ve seen this in the first example. ``{cl}acronym;acronym;meaning{cr}`` produces {acronym;acronym;meaning}. You can also use ``{cl}acro;acronym;meaning{cr}``. As an added bonus there are a few standard acronyms that can be called without the acronym definition. These are : .. raw:: html {+coloring} # a dictionary of standard acronyms # keys should be lowercase acronyms = { 'wysiwyg' : 'What You See Is What You Get', 'html' : 'HyperText Markup Language', 'xml' : 'eXtensible Markup Language', 'xhtml' : 'eXtensible HyperText Markup Language', } {-coloring} So you can do ``{cl}acro;WYSIWYG{cr}``, which becomes {acro;WYSIWYG} {sm;:cool:}. Feel free to add your own of course. The built in acronyms are : :: { 'wysiwyg': 'What You See Is What You Get', 'html': 'HyperText Markup Language', 'xml': 'eXtensible Markup Language', 'xhtml': 'eXtensible HyperText Markup Language', 'rest': 'ReStructuredText', 'css': 'Cascading Style Sheets', 'ie': 'Internet Exploder', 'afaik': 'As Far as I Know', 'ianal': 'I am not a Lawyer', 'ssi': 'Server Side Includes', 'cgi': 'Common Gateway Interface', 'lol': 'Laughing Out Loud', 'rotfl': 'Roll On the Floor Laughing', 'http': 'HyperText Transfer Protocol', 'ascii': 'American Standard Code for Information Interchange', 'gui': 'Graphical User Interface', 'cli': 'Command Line Interface', 'pda': 'Personal Digital Assistant', 'rtfm': 'Read the Manual', 'ftp': 'File Transfer Protocol', 'nntp': 'Network News Transfer Protocol', 'uk': 'United Kingdom', 'pc': 'Personal Computer', 'url': 'Uniform Resource Locator', 'uri': 'Uniform Resource Identifier', 'tcp/ip': 'Transport Control Protocol/Internet Protocol', 'udp': 'User Data Paragram' } The ``macros.py`` file that comes with rest2web contains an acronyms dictionary. Any acronyms that you add to this dictionary will be available to the acronyms macro. emoticon -------- This is another shorthand way of including images in your pages. It's useful for putting emoticons inline with text, hence the name. Unlike the ``smiley`` macro it doesn't need to read anything of disk. rest2web needs to know what url path to use for the emoticon images. You can supply this using the ``emoticon_url`` value in the ``[Macro Paths]`` section of your `config file`_. If you don't supply a value, the default is ``'<% path_to_root %>images/'``. The emoticon macro assumes your images are all 'gif's. .. warning:: Including images without specifying a size may slow down the browser rendering of your pages. You could make all your images the same size and hardwire the sizes into the 'img' tag that this macro creates. Alternatively you could do something clever with the {acro;PIL;Python Imaging Library} by Frederik Lundh - and have it work out the sizes and insert them for you. Examples : ``{cl}emoticon;eyeballz{cr}`` becomes {emoticon;eyeballz} ``{cl}emo;noise{cr}`` becomes {emo;noise} You can use ``emo`` as a shorter alias for ``emoticon``. include ------- This macro is very simple. Give it a filepath (relative to the macro module) and it will include the contents of that file into this page. The optional 'escape' parameter allows you to escape whitespace. This will insert files as they are - without having to use the '
' tag, which breaks my layout - have I mentioned that before ? {sm;:wink:} 

For example ``{cl}include;r2w.ini;True{cr}`` escapes the *r2w.ini* file and inserts it into the page  :

.. raw:: html

    
{include;r2w.ini;True}
You can use ``inc`` as a shorter alias for ``include``. To include HTML files (without escaping), use ``{cl}include;some_file.html{cr}``. colorize -------- This macro takes a file and applies Python syntax highlighting to it. You need the right rules in your CSS file for the coloring to be visible. See the rules that start *py* in ``test.css``. ``{cl}colorize;docs/example_function.txt{cr}`` becomes : {colorize;docs/example_function.txt} You can use ``col`` as a shorter alias for ``colorize``. To use the colorize macro, you need the right definitions in your CSS. Something like : :: .pysrc { border: #c0c0ff 2px dotted; padding:10px; font-weight: normal; background: #e0e0ff; margin: 20px; padding:10px; } .pykeyword { font-weight: bold; color: orange; } .pystring { color: green } .pycomment { color: red } .pynumber { color:purple; } .pyoperator { color:purple; } .pytext { color:black; } .pyerror { font-weight: bold; color: red; } Change the color definitions to alter the appearance. +/- coloring ------------ This is the only example of an advanced macro included. It does the same job as the ``colorize`` macro, but instead of passing it a filename - it works on the text it encloses. This : :: .. raw:: html {cl}+coloring{cr} class coloring: """A Macro for coloring chunks of text.""" def open(self, data): p = color.Parser(data) p.format(None, None) src = p.getvalue() src = src.replace('\n', '
\n') return src.replace(' ', '  ') def close(self, *args): pass {cl}-coloring{cr} Becomes : .. raw:: html {+coloring} class coloring: """A Macro for coloring chunks of text.""" def open(self, data): p = color.Parser(data) p.format(None, None) src = p.getvalue() return src.replace('\n', '
\n').replace(' ', '  ') def close(self, *args): pass {-coloring} small ----- This macro puts the enclosed text between .... tags. This is a feature missing from docutils. ``. You link to it using the HTML - ``Link to Anchor``. title ----- This is a shortcut for inserting headlines. You pass in the text and the size (which defaults to an ``h3`` headline). {cl}title;A Headline{cr} becomes : :: .. raw:: html {title;A Headline} {cl}title;Another Headline;1{cr} becomes : :: .. raw:: html {title;Another Headline;1} Including Macros in ReST Pages ============================== Macros are just treated as ordinary text by docutils_. That means that they must fit into the reST syntax. If they don't, then you should escape them using the raw role or the raw directive. The Raw Role ------------ The raw role can only be used if it is declared at the start of the document. You must include the following declaration : :: .. role:: raw-html(raw) :format: html From then on you can pass anything through docutils untouched, like this : ``:raw-html:`{cl}small;Something to be made small{cr}``` In the above example it's not very useful. However, macros return HTML. If you tried to include HTML in your macro - docutils would escape the *<* tags, and they would be included as text (or break your macro). So ``{cl}small;Something to be made small{cr}`` *doesn't work* in reST documents. Try it if you don't believe me. {sm;:-)} Instead you can do ``:raw-html:`{cl}small;Something to be made small{cr}```, which does work. The Raw Directive ----------------- If you use the `Advanced Macros`_ then you almost certainly want to include a passage of text to transform it. That transformation will be done *after* docutils has seen the text. Usually you will want the *macro* to transform your text verbatim - and have docutils leave it alone. In this case you need to use the raw directive. The classic example of this is the Python source coloring macro : :: .. raw:: html {cl}+coloring{cr} section = sections['section-name'] pages = section['pages'] {cl}-coloring{cr} If you didn't include the raw directive, docutils would do strange things to the chunk of code - and the macro wouldn't be able to process it. Paragraphs ---------- Docutils treats macros as ordinary text. That means if it comes across one on its own it will treat it as a paragraph. That may not be what you intend. For example - the {cl}title{cr} macro is used to create headlines. If you put this in your document on it's own, then docutils will encase it in paragraph tags. The following : :: {cl}title;Some Heading{cr} Produces this HTML : ::

Some Heading

This is neither valid, nor what you intended. The way round it, is to use the raw directive : :: .. raw:: html {cl}title;Some Heading{cr} namespace and uservalues ======================== The ``macros.py`` file that comes with rest2web has a ``set_uservalues`` function. This is used to set the global values ``namespace`` and ``uservalues``. That means that you can use access the uservalues and namespace for each page from your macros. Credits ======= The example macro file uses various Python modules. These are included in the ``modules`` directory that comes with the **rest2web** distribution. If you don't use the macros, you don't need this folder. The various modules come from the following people and places : * A lot of the text in this page comes from the document `Firedrop macros`_ by Hans Nowak * The macros module [#]_ comes from Firedrop_ by Hans Nowak * The smilies use *smiley.py* by `Mark Andrews`_. * *smiley.py* depends on *path.py* by `Jason Orendorff`_. * *smiley.py* uses smiley sets that follow the phpbb_ convention, you can download alternative sets from stylesdb_. * The smiley set included with **rest2web** is by a gentleman called Spider_. * The colorize macro uses a module called *colorize.py*. It originated as part of the MoinMoin_ project. The version we use is the one from the `Python Cookbook`_. ---------- Footnotes ========= .. [#] And the filename must only consist of alphanumerics and the underscore character. It should start with a letter, not a number, and is case sensitive, got all that ? {sm;:?:} .. [#] Assuming the page is in {acro;reST} format of course. .. [#] *textmacros.py* - in the rest2web folder. .. _rest2web config file: config_file.html .. _Jason Orendorff: http://www.jorendorff.com/articles/python/path .. _Mark Andrews: http://www.la-la.com .. _phpbb: http://www.phpbb.com/ .. _stylesdb: http://www.stylesdb.com/smilies_styles.html .. _Spider: http://web.spidercode.de/smilies .. _smilies page: reference/smilies.html .. _Firedrop Macros: http://zephyrfalcon.org/labs/firedrop_macros.html .. _Firedrop: http://zephyrfalcon.org/labs/ .. _moinmoin: http://moinmoin.wikiwikiweb.de .. _Python Cookbook: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298 .. _docutils: http://docutils.sourceforge.net rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/0000700000175000017500000000000010644674642022010 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/translation/de/0000700000175000017500000000000010644674642022400 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/translation/de/index.txt0000600000175000017500000000040310311246051024223 0ustar madduckmadduckrestindex crumb: German Index format: html page-title: The German Index /restindex uservalues body: ../template/_index.txt greeting: Guten Tag - Wilkommen para1: Something in German index_title: Index Title in German /uservalues rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/de/page.txt0000600000175000017500000000021410311246051024030 0ustar madduckmadduckrestindex crumb: German Rest /restindex uservalues body: ../template/page.txt para1: Having a **reST** in German. /uservalues rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/en/0000700000175000017500000000000010644674642022412 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/translation/en/index.txt0000600000175000017500000000043310311246051024240 0ustar madduckmadduckrestindex crumb: English Index format: html page-title: The English Index /restindex uservalues body: ../template/_index.txt greeting: Hello and Welcome para1: We are here... maybe we wish we weren't. index_title: Index Title in English /uservalues rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/en/page.txt0000600000175000017500000000021610311246051024044 0ustar madduckmadduckrestindex crumb: English Rest /restindex uservalues body: ../template/page.txt para1: Having a **reST** in English. /uservalues rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/index.txt0000600000175000017500000000120010335344157023643 0ustar madduckmadduckrestindex page-title: The Translation Index crumb: Translation link-title: Translation Example format: html page-description: A very basic example of a website with multiple translations. /description /restindex

Welcome

Choose Your Language

English Francais Deutsch
rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/template/0000700000175000017500000000000010644674642023623 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/translation/template/_index.txt0000600000175000017500000000140310425411107025610 0ustar madduckmadduck

<* greeting *>

<* index_title *>

    <# import urllib pageblock = '''\
  • %s

    %s

  • ''' pages = sections[None]['pages'] # everything is in the # default section in this index subsections = [] for page in pages: print pageblock % (page['target'], page['link-title'], page['page-description']) #>

<% para1 %>

<% Processor.dir_as_list %>rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/template/page.txt0000600000175000017500000000001310425411107025252 0ustar madduckmadduck<* para1 *>rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/fr/0000700000175000017500000000000010644674642022417 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/translation/fr/index.txt0000600000175000017500000000037610311246051024253 0ustar madduckmadduckrestindex crumb: French Index format: html page-title: The French Index /restindex uservalues body: ../template/_index.txt greeting: Bonjour et Bievenue para1: Nous sommes ici. index_title: Index Title in French /uservalues rest2web-0.5.2~alpha+svn-r248.orig/docs/translation/fr/page.txt0000600000175000017500000000021410311246051024047 0ustar madduckmadduckrestindex crumb: French Rest /restindex uservalues body: ../template/page.txt para1: Having a **reST** in French. /uservalues rest2web-0.5.2~alpha+svn-r248.orig/docs/template.txt0000600000175000017500000001272010516441366022023 0ustar madduckmadduck <% title %> <# if 'gallery' in plugins: print '' #>
Voidspace

rest2web: Building Websites Across the Known Universe

Return to Top
Part of the rest2web Docs
Page last modified <% modtime %>.

  • Index Page
  • <# minibar(sections, displayval="crumb", liststart='', listend='', intro='
  • Pages
  • ', subintro='
  • Sub Sections
  • ') #>

Powered by Python

Support This Project

Site Built with rest2web


Site Built with rest2web SourceForge.net Logo Certified Open Source

Python on Voidspace

rest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial.txt0000600000175000017500000005767710464644405022100 0ustar madduckmadduckrestindex tags: tutorial, introduction, beginners, site builder, website, basics, guide, basic guide crumb: Tutorial link-title: Tutorial page-description: An tutorial showing how to use **rest2web** to create a simple website. /description /restindex ========================== Introduction to rest2web ========================== ---------------------------------- Creating a Website With rest2web ---------------------------------- .. contents:: rest2web Tutorial Introduction ============ {emo;paper} Creating a website with **rest2web** is easy. Looking at the bewildering array of options in the restindex_ can make it seem very complex. In fact, you can build a simple website in a few minutues - using only a few features. You can then add things as you need them. If you look in the source files for the `example site`_ [#]_, you can see that most of the pages have only two or three items in the ``restindex``. This tutorial explains the basics of creating a website - and shows you an example. .. note:: This tutorial assumes you are basically familiar with {acro;HTML} and creating/editing text files. A Basic Site ============ The principles of **rest2web** are simple. A rest2web site has at least the following elements : 1) `Config file`_ - this tells rest2web where to read files from (the start directory), and where to put the files it creates (the target directory). 2) template.txt_ - This is the {acro;HTML} template for your website. 3) **index.txt** - This is the source file (content) for your main index page. Each directory *must* have this file. 4) Optionally other text files for your other pages. 5) Subdirectories with more content. We'll briefly look at these things, and then create a basic site. The Config File --------------- **rest2web** goes through all the files in your source directory (and subdirectories). It builds the contents, indexes, etc - puts them into the templates, and then saves the results in the target directory. So at the minimum, rest2web needs to know the source directory and target directory. It gets these from the config file. You tell **rest2web** the name of your config file at the command line : :: python r2w.py config_file.ini If you *don't* specify a config file, then rest2web will look for one called ``r2w.ini`` in the current directory. On Windoze, this means you can just double click on *r2w.py* and it will do it's magic. There are several other options in the config file- but the source and target directories are the two options you must specify for each site. Look at the example ``r2w.ini`` that comes with rest2web, or the `Config file`_ page, for full details. The Template File ----------------- The template file is the HTML framework for your website. Special values that you put in your template determine where the content, title, and navigation elements go. The name ``template.txt`` is just the default filename. You can change the name and location of this file through the ``restindex``. You can also use more than one template in your website. If you are only using one template in your website (as many will) then you can do this by just having one file (``template.txt``) in the top level of your source directory. The Index File -------------- Every *directory* must have a file called ``index.txt`` in it - or rest2web will ignore it. {sm;:-)} *Every* rest2web page *must* start with a restindex_. This is a set of options that tells rest2web how to build the page. The index page is important. Some of the options you set in the ``restindex`` of your index page apply to the whole directory. The index page also has some extra information available to it (about the other pages in the directory). This allows you to have in your index page, links to each of the pages in that directory along with descriptions (including any subdirectories). Other Pages ----------- In order to be a **rest2web** source file, a file must be a text file (``.txt``) and start with a ``restindex``. There are over twenty different options you can set in the restindex - but you will probably only need to use a couple on most pages. Some of these options are only relevant to index pages anyway. A restindex looks like this : :: restindex format: html page-title: This is the Page Title crumb: Short Title page-description: This is a description of the page. It can be more than one line long. /description /restindex Immediately after the restindex comes the page contents. If this is in ReST_ format then docutils_ will be used to turn it into html. The page contents is then put into the template - and any special values are converted. This allows for things like sidebars and navigation trails [#]_ to be added to your site. After all the files in a directory have been processed, rest2web moves on and processes any subdirectories. Subdirectories -------------- Subdirectories will also get rendered. Each subdirectory should have it's own index page. This index page can be automatically linked to in the index page of their parent directory. Creating an Example Site ======================== {acro;Ok;What Does Ok Stand For ?} - so let's see how this works in practise. {sm;:biggrin:} .. note:: The simple site we create here can be seen in the tutorial_site_ [#]_ folder of the docs. The first thing we'll do is create a config file. Config File ----------- If you were to create an example site from scratch you would need to create a directory for it all to go in. In this tutorial our source directory will be ``docs/tutorial_site``, and the html will be put in ``docs_html/tutorial_site``. In our example we won't use any macros, so you can create a text file called ``r2w.ini`` with the following values : :: # these values are all left at the default psyco = True pause = False log_file = 'log.txt' DEBUG = False compare_directory = '' # these values we have edited for our site start_directory = 'docs/tutorial_site' target_directory = 'docs_html/tutorial_site' macros = '' .. raw:: html

{small;This config file is actually called 'tutorial_site.ini' in the distribution.}

HTML Template ------------- .. sidebar:: Embedded Code Special values are enclosed in either ``<% ... %>`` tags (for single values) or ``<# ... #>`` tags for multiple statements. Multiple statements are actually Python_ code. You can embed chunks of code into your pages and templates. This allows *unlimited expressiveness* in how dynamic you make your pages and templates. If you wanted you could use your template to fetch information form the web and include it in your pages. You can do anything that Python can do. The HTML template has the basic framework of our website. We put special values into the template - so that **rest2web** knows where to put things like the page title, the page content, etc. A website can use as many different templates as you want - for different parts of the website, or even a different one for each page. {sm;:-o} Most websites will only need a single template though. The full `doc page for templating`_ gives you a list of all the special values we can use in our templates. (We can also use these in our page content). Last count there were about twenty five of these values. Like the restindex, we can create our basic site by only using a few of these. We use these values in two different ways. For single values we surround the values in ``<% ... %>`` tags. For chunks of code, we use ``<# ... #>``. We only need to use special values for things that are different in each page. We save the template as ``template.txt`` and put it in the root directory of our site. Because it doesn't start with a restindex, rest2web won't attempt to process it as a page. Here's our simple HTML template : :: <% title %>
<% body %>

Return to Top
Page last modified <% modtime %>.

It's actually {acro;XHTML}, so it starts with the proper DOCTYPE [#]_. The rest is a pretty simple document, with no real content. .. note:: We use two stylesheets in the template. One is ``test.css``, which contains the styles for our page [#]_. The other is ``rest.css``, which has the styles for docutils generates HTML. You can see the special values in the template. Let's look at the ones we have used : Special Values ~~~~~~~~~~~~~~ #. ``<% title %>`` #. ``<% final_encoding %>`` #. ``<% path_to_root %>`` #. ``<# print_crumbs(breadcrumbs) #>`` #. ``<% body %>`` #. ``<% modtime %>`` title ##### ``<% title %>`` will be replaced with the title for each page. For index pages, and pages in html format, you must specify the title in the restindex. For pages in ``reST`` format, your top heading becomes the page title. final_encoding ############## ``<% final_encoding %>`` is the character encoding used for the page. This can vary from page to page *or* you can set it for the whole site in your main page. In order to be valid html you **must** specify an encoding. If you don't know about encodings then **rest2web** will attempt to work them all out for you - and do the right thing. By default it will output the page using the same encoding as the content. To learn more about how rest2web handles encodings - read the `Text Encodings`_ page. path_to_root ############ ``<% path_to_root %>`` is the path from the page being created to the root directory (always ending in a ``/`` unless the page is in the root directory). You might be used to specifying the location of stylesheets using the form ``/stylesheets/styles.css``. This gives the absolute location of the stylesheet - but means that the file cannot be viewed correctly from the filesystem. Using *path_to_root* for stylesheets and images (etc) puts the correct relative path in - meaning that the site can be viewed from the filesystem. print_crumbs ############ You can see that ``<# print_crumbs(breadcrumbs) #>`` uses the second style of tags - ``<# ... #>``. That's because it's a function call not a value - and we use it's output. ``print_crumbs`` is one of the built in functions_. It prints the navigation trails that are known by the weird name *breadcrumbs*. Tradition puts them at the top of the page. print_crumbs prints them as a list of items - so we put the function call between unordered list tags ``
    ...
``. There are some {acro;css} rules that cause them to be dispayed properly. body #### ``<% body %>`` is the contents of the page. {sm;:-p} modtime ####### ``<% modtime %>`` is the date (and time) that the source file was last modified. The Index Page -------------- When you run **rest2web** it scans the start directory, processing every text file with a restindex. From each text file it creates a corresponding output HTML file. By default, the filename of the output file will be the same as the source file - except ending in ``.html`` instead of ``.txt``. Having completed all the files in a directory, rest2web then does any subdirectories. .. caution:: **rest2web** will overwrite files in the target directory, creating all necessary subdirectories. So by default ``index.txt`` in the source directory becomes ``index.html`` in the target directory. If you don't want your index page to be called 'index.html' then there are a couple of ways you can affect that. You can either use the ``target`` keyword (in the restindex of course) to specify an explicit target name for the target file, or you can use the ``index-file`` keyword. 'index-file' tells rest2web that this file isn't the real index page - but another one is instead. rest2web will then read that file and treat it as the index page for the directory. Whichever you choose *every* directory must have a file called ``index.txt``, with a restindex, or rest2web will ignore that directory. Our file ``index.txt`` starts with a restindex. We're going to make it as simple as possible. Our index page is going to be a short introduction to the site, and have links to all the other pages, with descriptions of them. For our main index page we'll use HTML rather than ReST : :: restindex crumb: Home format: html page-title: rest2web Tutorial Website /restindex

rest2web Tutorial Website

An Example Index Page

<# print_details(default_section) #>

This is the index for the rest2web Tutorial website. It's not got much on it - other than this paragraph and links to our other pages.

The page is divided into two parts - the restindex, and the content. The restindex ~~~~~~~~~~~~~ Every **rest2web** source page starts with a restindex. The restindex has sensible defaults, so we only need to include values that we're changing. For details of *all* the restindex options, read the restindex_ page. .. sidebar:: Empty restindex It's entirely possible that you might be happy with *all* the default values in a restindex. In this case you still need to start your page with an empty restindex : :: restindex /restindex We are using navigation trails in our template. That means each page should have a ``crumb``. Because this is the index page, we use the *traditional* value **Home**. Our page is html. The default format is html - this is **rest2web** after all {sm;:-)} - so we need include the ``format: html`` argument. Because the page is html (and also because it is an index page [#]_) it needs a ``page-title``. And that's all we need in our restindex. Wasn't that easy. {sm;:-p} We are relying on lots of default values - we don't define any sections for our directory, we're ging to let rest2web handle all our encodings, and so on. The restindex_ page will tell you all the default values for the restindex options. The Content ~~~~~~~~~~~ The content is nice and standard html *except*, we can see one of our special values turning up again. This time it's a call to one of the `standard functions`_ - ``print_details``. **print_details** displays a nice list of links to all the pages in a *section*. Your index page can be divided up into several *sections*. This enables you to have one directory with pages on several different topics. Each page then has a declaration in the restindex as to which section it is in. (The ``section`` keyword in the restindex). You have to declare your list of sections in the index page. As you can see we **haven't** done that {sm;:lol:} - so all the pages in our directory will go into the default section. You access a lot of data about *all* the pages in a directory using the ``sections`` special value. You can read about that in the templating_ page (along with all the other special values you can include in your pages). You access information about the default section through ``sections[None]`` *or* ``default_section``. ``default_section`` is just an easier way to access the same information. .. note:: ``sections`` is a Python_ object called a dictionary. You access members through their keys. In the ``sections`` data structure each member is a section. Each section is also a dictionary. Each section includes a list of all the pages in that section. .. raw:: html {+coloring} section = sections['section-name'] pages = section['pages'] {-coloring} Every directory has all the sections you define in the index page *and* the default section. This has the key ``None``. You can also access the default section through the special value ``default_section``. You can use dictionaries to build quite complex data structures. You can learn more about dictionaries in the `Python Tutorial`_ or in the Python docs about `Mapping Types`_. **print_details** takes an individual section to display all the pages in a section. Any subdirectories in a directory can also be displayed as 'sub-sections'. Because all our pages will be in the default directory we call print_details with the default section - ``<# print_details(default_section) #>``. Other Pages ----------- We've created our main page. The main page acts as an index to all the pages in the directory. So we need some content. Because this is **rest2web**, we'll create the page in {acro;reST} format. :: restindex crumb: A Page link-title: An Example Page page-description: This description is for the index page. You can use **reST** markup if you want. /description /restindex ============== A ReST Title ============== -------------- The Subtitle -------------- .. This is a comment. To use the 'raw-role', we have to define it *first*. .. role:: raw-html(raw) :format: html This page is written in ReStructured Text markup. That's why it looks like *plain text*. This page lives at :raw-html:`<% pagepath %>` [#]_. This tutorial isn't a tutorial on ReStructuredText though. If you're looking for one, you might be better off with the `docutils documentation`_. .. [#] The file path is dynamically inserted by rest2web. .. _docutils documentation: http://docutils.sourceforge.net The restindex for this page is a bit different to the one for the index page. ReST is the default markup, so we don't need to declare it explicitly in the restindex. Additionally, docutils will automatically generate a page title for us from the top level heading. If we don't specify a crumb, the page title will be used [#]_. This is too long, so we specify a short one. We want this page to appear in the index page. It's entry will appear as a link with a brief description. If we don't specify a link title, the page title will be used. Often this will be ok - but here we've specified a different one [#]_. So the text used for the link is the ``link-title`` value and the text used for the description is the ``page-description`` value. This is a multi-line value and can contain ReST markup. reST Content ~~~~~~~~~~~~ .. sidebar:: Embedded Code With reST If you want to include multi-line chunks in rest documents, then you can use the raw directive : :: .. raw:: html <# big = '%s' print big % 'Hello World' #> Unlike the raw role, this doesn't need to be declared before you use it. The content is straightforward reStructuredText. If you want an introduction to ReST, `A ReStructuredText Primer`_ is a good place to start. Notice the use of the raw role : ``:raw-html:`<% pagepath %>```. This allows us to insert **rest2web** special values into the page (without docutils escaping the *<* symbols). You can only use the raw role if you declare it first. Subdirectories -------------- Directories can have subdirectories. These appear in the index page as 'Subsections'. The subdirectory must have an 'index.txt' file. The 'link-title' and 'page-description' specified here are what appear in the index page of the directory above. We'll create a subdirectory - imaginatively called 'subdirectory'. We'll create the following file, and save it as 'index.txt' : :: restindex crumb: Subdirectory target: subdirectory.html page-description: A subdirectory - with pages of it's own. /description /restindex ========================= Subdirectory Index Page ========================= -------------- The Subtitle -------------- .. role:: raw-html(raw) :format: html .. raw:: html
<# if not default_section['pages']: print '

No Pages Yet

' else: print_details(default_section) #>
.. class:: intro This page lives at :raw-html:`<% pagepath %>`. The ``class`` directive applies a style to this paragraph. I didn't want this page to be called ``index.html``. I *could* have used the ``index-file`` option to specify another file as being the index page. Instead I stuck with keeping the source file as *index.txt*, but specifying an alternative target filename. That's the ``target`` keyword. This page is also in rest format. This means that we don't need to specify a format in the restindex. The content prints an index using ``print_details`` - but *only* if there are any pages. It checks first (``if not default_section['pages']:``), and if there aren't any it prints the **No Pages Yet** heading. It also uses the ``class`` directive to apply a class to the paragraph - so that it can be styled with CSS. More Advanced Topics ==================== In this tutorial we haven't used any macros_, or sidebars. Macros allow you to easily insert smilies, python source coloring, and lots more into your pages. The functions to create sidebars are described in the `standard functions`_ page. There are many options in the restindex that we haven't explored here - and also lots of special values that we haven't looked at. Despite what it doesn't show, this tutorial does give you a good overview as to how to create a website using **rest2web**. For further examples you can look at the source files in the ``docs`` folder. These use most of the features of **rest2web** - and you can see the results in the finished documentation. ---------- Footnotes ========= .. [#] In the docs folder of the distribution. .. [#] For some bizarre reason known as *breadcrumbs*. .. [#] The only difference is that the main index page has the ``include: No`` option set - so that it doesn't go into the main index. {sm;:-p} .. [#] Purists might note that I *haven't* included the {acro;xml} declaration. This is because it puts {acro;IE} into quirks mode. .. [#] I've just re-used the main stylesheet for the rest2web docs. This is easier for me - but it means there are a few extra values in there. .. [#] Index pages need a ``page-title`` in the restindex. This is so that **rest2web** doesn't have to build the page (for reST content) when examining the index pages of it's subdirectories. .. [#] For index pages the filename is used as the default crumb. .. [#] The link title can also be used by sidebars. .. _A ReStructuredText Primer: http://docutils.sourceforge.net/user/rst/quickstart.html .. _macros page: macros.html .. _restindex: restindex.html .. _config file: config_file.html .. _macros: macros.html .. _tutorial_site: .. _example site: tutorial_site/index.html .. _doc page for templating: .. _template.txt: .. _templating: templating.html .. _text encodings: reference/encodings.html .. _standard functions: .. _functions: functions.html .. _python tutorial: http://docs.python.org/tut/node7.html#SECTION007500000000000000000 .. _Mapping Types: http://docs.python.org/lib/typesmapping.html .. _docutils: .. _rest: http://docutils.sourceforge.net .. _Python: http://www.python.org rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/0000700000175000017500000000000010644674642021410 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/reference/todo.txt0000600000175000017500000002137110514020175023102 0ustar madduckmadduckrestindex crumb: TODO/ISSUES page-description: The TODO and ISSUES list for **rest2web**. /description /restindex ============= TODO/ISSUES ============= TODO ==== This is the **TODO** and **Wish List** for `rest2web`_. It includes things that need to be done as well as things that are only included because they could be done. They are not listed in any order of priority or even desirability. Obviously bug-fixes are the highest priority. If you have any suggestions for this list, or opinions as to the priority of items on this list, or wish to implement any of the ideas yourself, then please get in touch. Some of the items have (?) at the end of them. This means we are not sure if it desirable to implement the suggestion at all. There is a constant tension between adding more flexibility (extra options) and keeping things simple. Your input is wanted - if it matters to you whether we do this or not then let us know. For bug reports or feature requests you can use the `rest2web Mailing List`_. * Add an extra level of verbosity, errors only. * Remove the 'unicode' encoding option - this defaults to an output encoding of template encoding; which is arbitrary. * Allow overriding of default index and template used with 'force mode'. (And provide a simpler default template.) * ``<% path_to_root %>`` doesn't work in gallery files, need a workaround in plugin if possible. Could sllow path_to_root in the uservalue 'body', or some other mechanism. * Better error messages from rendering templates - proper line numbers. * CSS error in new template for long page titles (squashed in minibar). * Implement next and previous. (By giving pages access to the section order lists.) * Add rest2web version to the namespace. * Provide a better default title for the index pages (using ``self._top_level_name``). * Handle encodings for dicts (subsections) and list values, when supplied as uservalues in config file. * Add a 'guess' option (the default) for encodings supplied in a config file ? * Ability to set the lineendings of the output. ('``\\n``' or '``\\r\\n``') * Build a more up to date executable. * Add test examples of the new ``include`` functions. * Allow source and target directories to be provided as command line options. * A standard function to automatically copy (and link to) the text source for a page. * Add a 'Generated by rest2web' comment into the HTML of each page. * Duplicate entries in ``section_pages`` gives an unhelpful error message. * Get plugins and macro support complete in the executable version. * The example translation file, links to the same pages in the other languages. Standard function to do this, or restindex keyword ? * Allow setting of docutils options in the config file. * Render macros in section descriptions. * Allow easy adding of user functions ? (like macros, currently doable through normal imports in templates) * markup format - could allow additional markups, like sextile and textile. * template engines - support for other templating engines like Cheetah * Better support for multiline and nested macros. * description format - we might want to allow different markups for the description. (Currently reST *only* - could allow html easily) * can we extract the page description from the reST format ? (as meta information) * Use filename as default page title/link title/crumb ? (where none is supplied) * Build a plugin to use the tags for navigation - so a site needn't just be represented as a tree. Also build a data structure (probably XML) that can be used by a CGI script for dynamically searching for pages with tag combinations. * Should we allow comments inline with values in the restindex ? (or even multiline comments ?) - this would require a better parser. * Should be a standard function to generate a table of links to the sections in a page, for use in the index page - which could have several pages in each section. See the example table in the test site. * Should 'format' or 'encoding' be a default value, when specified in the index file ? (any others ?) * At the moment you can only have sections you have declared in your restindex 'sectionlist'. Should you be able to assign pages to sections without having to declare them ? FIXME: check this is true * Develop the index generation stuff so that it can generate 'blue arrows' style Python documentation, including up. (Difficult without support in docutils) * Contents for a section, or for the whole site. * Single index page for the whole site (sitemap plus indexes from within pages). * Unittests or doctests. (Probably requires a lot of refactoring to make rest2web testable.) * A way of adding a set of standard links to all reST pages. * Implement more of the config file values as command line options. * Automatic FTP facility. * Integrate with Tidy for checking of HTML. * [madduck] More flexible parsing for restindex. Ideally, it should be possible to have the restindex anywhere within the document, including comments. Also, it should be completely removed from the document after processing. ISSUES ====== Following is a list of 'unresolved issues' with **rest2web**. They aren't necessarily all bugs, so they may not be fixed. It's probably worth being aware of them. * Unfortunately, the ``guess_encoding`` function can recognise ``cp1252`` [#]_ as ``ISO-8859``. In this case docutils can choke on some of the characters. We probably have to special case the ``cp1252`` and ``ISO-8859`` encodings - but I wonder if this problem applies to other encodings ? * If you set ``include: No`` for an index page, it works fine *except* when you are viewing that index page or subpages. In these cases it *is* included. * If no crumb is provided, the page-title is supposed to be used instead. Each page needs it's index page in it's list of crumbs (as the 'crumb before' it). In order to work out the title for the index page, we would need to render the body of the page. But to render the body of an index page, you need to render all the other pages *first*. This means **index pages need crumbs**. Either that or you can supply an explicit ``page-title`` value ! * You probably can't set a target that is outside the current directory tree. (test !) Allow for a way of specifying that the target is an absolute URL (if build is 'No') * It would be nice for every page to have access to the whole structure of the site [#]_. This would allow a sidebar to have links to every page in every section. The only way of doing this is to generate the site in two passes. First pass create the structure. Second pass, render the pages with the pages having access to the index structure. This means parsing the restindex - which also extracts page contents. Unless we are going to parse the restindex twice we would end up with the entire contents in memory. Is this feasible ? (or worthwhile - would anyone use it ?) XXXX Note that because of the data structures we create - it's likely that *currently* the whole site is being kept in memory *anyway*. * Currently a '.txt' file without a restindex won't be built. It's *possible* that *all* the defaults are what you want. Should we let a file without a restindex be processed ? (That means if you don't want a text file to be processed you must give it a restindex and set build and include to ``No`` - maybe make it an option ?). * It would be nice to make `Firedrop `_ a graphical frontend to **rest2web**. It would need to be able to handle trees of pages, instead of a list, and effectively have a 'build.ini' file per page (the restindex) rather than for the whole site. * For a page that has 'include' set to 'No', the indexpage value is ``None``. This is because the current page doesn't have an entry in the indextree structure. This could require some care if you use 'thispage' in your template. Is their some other sensible value it can have ? You can still use indextree without problem. * I'm not sure if filename encodings are an issue or not. I don't always decode filenames - so where we are comparing them from the filesystem etc. it's *possible* we'll have problems. * Sidebars contain links to pages in sections above. This means that changing one page can affect other pages - so when you change something we have to rebuild the whole site, not just the changed part. ---------- Footnotes ========= .. [#] The default windows encoding. .. [#] Currently through indextree, a site has access to the whole structure *above* the current page - but not the *whole* structure. .. _rest2web: http://www.voidspace.org.uk/python/rest2web .. _rest2web Mailing List: http://lists.sourceforge.net/lists/listinfo/rest2web-develop rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/uservalues.txt0000600000175000017500000002333710513540211024334 0ustar madduckmadduckrestindex crumb: uservalues page-description: A description of the uservalues system. This can be used to communicate with plugins, or for providing alternative translations of sites. /description /restindex ================== The User Values ================== --------------------------------- Extra Values in Your Templates --------------------------------- .. contents:: Introduction ============= **rest2web** generates pages by inserting special values into your web page templates. These values include the body of the page, the title and so on. You can see from the templates_ page, all the values that you can use in your templates. *uservalues* are a way of inserting extra values into your template. As you might guess, you specify the names and values. One obvious use of this is to provide several different translations [#]_ of your website. The uservalues can be in HTML *or* in ReST format. They use two slightly different syntaxes so that rest2web knows whether to render the uservalue into HTML or not. Uservalues can be put into your pages (via your content or your templates), using the `templating system <../templating.html>`_. uservalues ========== *uservalues* can be specified in three ways : 1) In each page. These *uservalues* are local to the page. 2) At the `command line <../command_line.html>`_. 3) In the `config file <../config_file.html>`_. Uservalues are effectively new variables (or names) you can use in your templates. For example, if you create a uservalue 'site_wide_title', you can use it in your templates like this : ``<% site_wide_title %>`` or : ``<# print site_wide_title #>`` For global values (available to every page), the most convenient thing to do is to define them in your config file. .. note:: Changes to uservalues (in embedded code, through ``{lt}$ ... $>`` tags) are *not* propagated to the main namespace. uservalues in Pages ------------------- *uservalues* can be specified in each page in a similar way to the restindex. uservalues are specified *immediately after* the restindex. : :: restindex crumb: A Page /restindex uservalues value1: Some Value heading:

A Heading

long_value: """ A multi line value which spreads across several lines. """ another_long_value:""" This one has a big indent in front of it ! """ /uservalues An addition to the restindex syntax, is that multiline values use triple quotes. .. caution:: Indentation is not removed from multi line values. The text between the triple quotes is inserted literally wherever you use the value. You can use single or double quote marks. There is one special value, the ``body`` value discussed below. uservalues at the Command Line ------------------------------ It is also possible to pass uservalues to **rest2web** at the command line. These will be available in *every* page. uservalues in the config file ----------------------------- You can specify uservalues in the ``uservalues`` section of the config file. These are also available in every page. To do this, you need to add something like the following to the bottom of your site config (``r2w.ini``) file : :: [uservalues] site_wide_title = 'Some Title' site_wide_subtitle = 'Some Subtitle' Reserved Names -------------- You can't use any name that shadows a name already used by the templating system. This means that the following names are all reserved. Trying to use them will cause a ``SyntaxError``. : .. raw:: html {+coloring} reserved_names = [ 'title', 'breadcrumbs', 'sections', 'pagename', 'pagepath', 'encoding', 'output_encoding', 'final_encoding', 'path_to_root', 'sectionlist', 'rest_dict', 'doc', 'stdout', 'modified', 'modtime', 'template_file', 'template_encoding', 'indexpage', 'indextree', 'thispage', 'sidebar', 'minibar', 'print_crumbs', 'Processor', 'tags', 'default_section', 'modtime', 'modtimeiso' ] {-coloring} .. note:: See that ``title`` is a reserved name. If you want to set the page title, you should use the ``page-title`` keyword in your *restindex*. The body Value ================= The *body* value is special. This lets you specify a file that is the content for this page. Any content following the uservalues is ignored - and the specified file is used instead. If that file has template tags that use the uservalues - then they will be substituted when the template is processed. The file specified can be an absolute path, or a path relative to the page it is bein used in. For example, say we have the following for our uservalues : :: uservalues body: ../templates/a_page.txt greeting:

Howdy

/uservalues **rest2web** will use the file ``../templates/a_page.txt`` as the body of this page. If it contains a tag that looks like ``<% greeting %>``, then it will be replaced with ``

Howdy

`` when the template is processed. .. warning:: Using the *body* value you separate your content from your file containing the restindex/uservalues. You **must** store them with the *same* encoding. Translations -------------- A good example of using the *body* value is for multiple translations of websites. A typical example is where you have several pages of content that you want to mirror in different languages. That is, you want the same pages, with the same structure, just key sections swapping over. The *body* value lets you keep your page frameworks all in one place. The framework should use the uservalues - and then just have several directories of pages which point to the framework... but have the right values set for the uservalues. There is an example in the example site - the translation_ pages. See the *source* of these pages for a clear illustration how of how it works. Now I'll attempt to explain it in words {sm;:razz:}. Suppose you want three translations for your site. You'll put the english files in a directory called **en**, the French ones in a directory called **fr**, and the German ones in a directory called **de**. The HTML framework of each page is going to be identical in each directory - it's just the viewable words that will be different. To achieve this we add a fourth directory called templates. It's in here that we are going to put our HTML frameworks. Everywhere we want some text to appear we will put a uservalue placeholder instead. We have our usual *template.txt* which contains the outline of the page. We'll create a file in the ``template`` directory called ``index_page.txt`` [#]_. It might look something like : ::

<% greeting %>

<% para1 %>

This page body has a placeholder for the headline and the paragraph. In *index.txt* in your ``en`` folder you would then put : :: restindex format: html crumb: English Index page-title: English Index /restindex uservalues body: ../templates/index_page.txt greeting: Hello and Welcome para1: """This is the body text, with some HTML in it.""" /uservalues When rendered, it fetches the ``index_page.txt`` as the body, and inserts the English values into it. In *index.txt* in your ``fr`` folder you would then put : :: restindex format: html crumb: French Index page-title: French Index /restindex uservalues body: ../templates/index_page.txt greeting: Bonjour et Bienvenue para1: """Du Francais ici, avec du HTML.""" /uservalues When rendered, this creates the French page. You create your basic directory structure in the ``templates`` directory. You mirror this in your other directories; but your files are all basically identical and you only need to edit the uservalues. With a little trickery in the body or the templates it ought to be simple to include links between pages in one language and the other ones. The Order of Processing ======================= There are two ways of inserting values into pages using the template system. The first way allows you to put *ReST* values into your pages before they are turned into HTML. The second way puts html into your pages when the template is processed. If your pages are in HTML, then the difference doesn't matter. If your page is in ReST format, then it allows you to have uservalues in ReST; which then get rendered along with the rest of your page. For the full details, see the `templating system`_. It basically boils down to this, the following template tags will be resolved *before* the ReST is processed : ``{lt}$ ... $>`` ``{lt}* ... *>`` and the following template tags will be resolved *after* the ReST is processed (and so should contain html values) : ``{lt}# ... #>`` ``{lt}% ... %>`` Don't forget that in order to put HTML tags inside a ReST document, you will need to use the raw directive [#]_ : :: .. raw:: html {lt}# print 'hello' #> --------- Footnotes =========== .. [#] This footnote is just so that the words internationalization (i18n), and localization (l10n) appear somewhere on this page {sm;:-)}. .. [#] We won't call it *index.txt*, which is a `special file`_ in **rest2web**. The example site uses *_index.txt*. .. [#] ``<% value %>`` will become ``<% value %>``; which doesn't work {sm;:sad:}. If you do need to do this (by the way) you could also use the raw role. .. _templates: ../templating.html .. _docutils: http://docutils.sourceforge.net .. _special file: ../special_files.html .. _translation: ../translation/index.html .. _let me know: fuzzyman@voidspace.org.uk rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/encodings.txt0000600000175000017500000001564510414273736024131 0ustar madduckmadduckrestindex crumb: Encodings page-description: How to handle different character encodings in **rest2web**. /description /restindex ================ Text Encodings ================ ------------------------- Encodings with rest2web ------------------------- .. contents:: Fun With Encodings ================== {emo;dove} **rest2web** handles text encodings in ways that are hopefully sensible and simple. Whenever you are dealing with text you **should** *know and specify the encoding used* [#]_. If you disagree with me (or haven't a clue what I'm on about), read `The Minimum Every Developer Must Know About Unicode`_, by Joel Spolsky. **rest2web** allows you to specify what encodings your content is (source files) and what encoding to write the output as. In order to achieve this, **rest2web** is almost entirely ``Unicode`` internally. If all of this is gobbledygook, then don't worry **rest2web** will guess what encoding your files are, and write them out using the same encoding. If you're not 'encoding aware', this is usually going to be what you want. Summary ======= In a nutshell you use **'encoding'** to specify the encoding of a contents, file **'template-encoding'** to specify the encoding of your templates, and **'output-encoding'** to specify the encoding the file should be saved with. 'output-encoding' used in an 'index file' sets the output encoding for all the files in that directory and below. Input Encodings =============== As usual, the way we handle encodings is through the restindex_. The first thing we can specify is the encoding of a page of content. We do this with the ``encoding`` keyword. If this keyword is set for a file [#]_, it is used to read and decode the file. If no encoding is specified, **rest2web** will attempt to guess the encoding. It tries a few standard encodings *and* retrieves information from the *locale* [#]_ about the standard encoding for the system. If the page is successfully decoded then it stores the encoding used [#]_, otherwise an error is raised. From this point on the page content is stores as Unicode inside **rest2web**. Whichever encoding was used to decode the page, it is available as the variable ``encoding`` inside the templates. Template Encodings ================== In the restindex you can also specify the encoding of the template file. This is the value ``template-encoding``. It is only used if you are also specifying a template file. If you specify a template file without specifying an encoding, our friend ``guess_encoding`` will be used to work out what it is. This value is available as the variable ``template_encoding``. Output Encodings ================= When the page is rendered you can specify what encoding should be used. This is done with the ``output-encoding`` keyword. If you specify an 'output-encoding' in an 'index file' then that encoding will be used for all the files in that directory, and any sub-directories. That means you can specify it once, in your top level index file, and have it apply to the whole site. The encoding you specify applies to *all values passed to the template*. This means the variables ``body``, ``title``, ``crumb``, ``sectionlist``, etc. As well as the standard Python encodings, there are a couple of special values that ``output-encoding`` can have. These are ``Unicode`` and ``none``. If you specify a value of **none** for 'output-encoding', then all string are re-encoded using the original encoding. If you specify a value of **Unicode** for 'output-encoding' then strings are *not encoded*, but are passed to the template as Unicode strings. Your template will also be decoded to Unicode. The output will be re-encoded using the 'template-encoding', but will be processed in the template as Unicode. The value for 'output-encoding' is available in your template as the variable ``output_encoding``. Because of the special values of 'output-encdoing' there is an additional variable called ``final_encoding``. ``final_encoding`` contains the actual encoding used to encode the strings. If the value of ``output_encoding`` was **none**, then it will contain the original encoding. If the value of ``output_encoding`` was **unicode**, then it will contain the ``None`` object. Encodings and the Templating System =================================== The templating system makes several variables available to you to use. These include ``indextree``, ``thispage``, and ``sections``. These contain information about the current page - and other pages as well. The information contained in these data structures allows you to create simple or complex navigation aids for your website. See the templating_ page for the details of how these data structures are constructed. Because these values contain information taken from other pages you can't be certain of their *original encoding*. This isn't a problem though, rest2web will convert these appropriately to the ``final_encoding`` of the current page. (It also converts all target URLs to be relative to the current page). The slight exception to this is part of the ``sections`` value. This value is a dictionary containing all the pages in the current directory, divided into sections. Each section has information about the section, as well as a list of pages in the section. Each page is itself a dictionary with various members containing information about the page. One of these members is ``namespace``, which is the namespace the page will be rendered in. This means that the values in the namespace are encoding with the appropriate encoding for *that* page. You can always tell what encoding that is by looking at ``final_encoding`` in the namespace. To be fair you're unlikely to dig that far into ``sections`` from the template - but I thought you ought to know {sm;:roll:} Problem ======= Note the following problem_. * Unfortunately, the ``guess_encoding`` function can recognise ``cp1252`` [#]_ as ``ISO-8859``. In this case docutils can choke on some of the characters. We probably have to special case the ``cp1252`` and ``ISO-8859`` encodings - but I wonder if this problem applies to other encodings ? -------- Footnotes ========= .. [#] The bottom line being, that if you don't know what encoding is used - you don't have text, you have random binary data. .. [#] It must be an encoding that Python recognises. See the Python `Standard Encodings`_ .. [#] See the python `Locale module`_ documentation. .. [#] The only exception is if the page successfully decodes using the ``ascii`` codec. In this case we store ``ISO8859-1`` as the encoding. This is a more flexible ascii compatible encoding. .. [#] The standard windows encoding. .. _The Minimum Every Developer Must Know About Unicode: http://www.joelonsoftware.com/articles/Unicode.html .. _restindex: ../restindex.html .. _Standard Encodings: http://docs.python.org/lib/standard-encodings.html .. _Locale Module: http://docs.python.org/lib/module-locale.html .. _templating: ../templating.html .. _problem: todo.html#ISSUESrest2web-0.5.2~alpha+svn-r248.orig/docs/reference/indextree.txt0000600000175000017500000001511610465432422024133 0ustar madduckmadduckrestindex crumb: indextree page-description: Reference to the weird and wonderful data-structure *indextree*. /description /restindex ========================= indextree and thispage ========================= ------------------------------- Representing Site Structure ------------------------------- **rest2web** provides your templates with three variables that give you information about the site structure. The first is called **sections**. This gives you information about all the pages in the same directory as the one that is being rendered. See the templating_ page for the description of that data structure. The other two variables are called **indextree** and **thispage**. **indextree** is a data structure that represents more of the whole site than **sections**. **thispage** is a pointer to a specific part of **indextree**. **indextree** itself represents the top level page. It has members representing the pages inside the top level directory. Any index pages for sub-directories will be represented here, and will themselves have pages. Because of the order that sites are processed in, any page can know that every directory above it will have been *fully* processed. This means that **indextree** can be used to construct 'sidebar' type links that look a bit like the following : :: (root directory) index page <- indextree | | section1 | (a sub-directory) | section 2 - Index Page | | | | section 3 | | sub-section 1 | | section 4 sub-section 2 (another sub-directory) | sub-section 3 - Index Page | | **This Page** <- thispage | Another Page | And Another One This allows a sidebar that is a set of links to all the sections above the current page. It doesn't yet allow you to know what pages might be in ``section 3`` or ``section 4`` of the root directory - but it would be unusual to need to put that amount of links just in a sidebar [#]_ ! You can see an example of using **sections** for sidebar information in the `rest2web docs`_. The `test site`_ is an example of a site using **indextree** for the sidebar. You can use the `standard function`_ ``sidebar`` to generate these sidebars from indextree. The actual data structure **indextree** is a dictionary that represents the index page for the top level directory. The dictionary contains links to dictionaries that represent the other pages and indexes. ``indextree`` is actually just part of the whole tree - it only contains the branches needed to get from the root directory to the current page that is being rendered. The dictionaries in **indextree** all follow the same pattern [#]_ and contain the following members : #. **parent** : a reference to the parent directory (also a dictionary like this). If this is the top level directory, then this member is ``None``. #. **sectionlist** : a list of sections for this directory. This retains the order that the sections are listed in the ``sectionlist`` value of the restindex. #. **sections** : this is actually a dictionary keyed by section name. Each value is a tuple ``(section_title, section_description)`` #. **pages** : a *list* of pages that are in this section. For pages that don't represent subsections this will be the value ``None``. For pages that do represent subsections, this list will only be populated if our page is somewhere down this tree. If the page currently being rendered (**thispage**) is not further down this branch, then this will be an empty list ``[]``. Each *page* in this list is also a dictionary like this one. #. **subdir** : This will be ``True`` for pages that are index pages (i.e. represent sub-directories). It is ``False`` for pages that aren't index pages for sections. #. **target** : A relative link from the current page to the one that this dictionary represents. #. **section** : What section this page is in. For an index page, this section will refer to what section in the *directory above* this index belongs to. #. **link-title** #. **crumb** #. **page-description** #. **thispage** : ``True`` if this page represents the one currently being rendered, ``False`` otherwise. #. **uservalues** : The uservalues for this page. #. **restindex** : The restindex for the page. #. *current_dir* - The directory the page was rendered in, relative to the top level directory. You can turn this into an absolute path using ``os.path.join(os.getcwd(), current_dir)``. #. *source_file* - The source filepath for the page. #. *target_dir* - The target file directory (as an absolute file path) being rendered into. Note: if the file has a target specified it may not be put in this directory. Use ``os.path.dirname(target_file)`` instead. #. *full_page_url* - The full url (starting with '/') for the page. Using this means that your pages may not work from the filesystem. {sm;:-)} #. *target_file* - The full output filepath of the page. As well as **indextree**, templates also have access to a value called **thispage**. **thispage** is an entry in the **indextree** structure, but it points to the current page. If you like **indextree** is the top of the tree, and **thispage** is the bottom. .. note:: Pages that have 'include' set to 'No' aren't in indextree. For those pages the value *thispage* will be ``None``. All string values in the **indextree** structure are encoded using the appropriate encoding for the page being rendered (as determined by the value ``final_encoding``). .. [#] It would be possible to do this by generating the site in two passes. On the first pass we'd generate the link structure and on the second pass actually render the pages. This would take longer, mean keeping the whole site in memory, and mean parsing the whole site before basic errors in the template are discovered ! .. [#] They follow as closely as possible the pattern for pages in the *sections* variable that is also passed to templates. See *sections* in the templating_ page. .. _templating: ../templating.html .. _rest2web docs: ../index.html .. _test site: ../test_site/index.html .. _standard function: ../functions.html rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/changelog.txt0000600000175000017500000002707210632630076024100 0ustar madduckmadduckrestindex crumb: changelog page-description: **CHANGELOG** for the released versions of rest2web.. /description tags: changelog, changes, improvements, diffs /restindex ============ Changelog ============ .. contents:: Rest2web Releases ================= This records the different releases to **rest2web**, with most of the major changes. The individual source files have more fine grained changelogs if you have a hankering to know the details. {sm;:-)} Version 0.5.2 alpha ------------------- Fixed bug where 'force' in the config file was required to be spelt 'Force' by mistake. 'template.txt' files (without a restindex) will not be processed in force mode. In force mode, directories *will* use the templates of their parent directories. Added optional compare function (for sorting pages) to the ``sidebar`` function. (Thanks to Gael Varoquaux.) 'initialheaderlevel' is now inherited by all pages in a directory, if set in an index page. Version 0.5.1 2006/12/16 ------------------------ Added some extra debugging info to syntax errors in the templates. Fixed odict and pathutils for Python 2.5 compatibility. Added the 'promote_headers' option to the `config file <../config_file.html>`_. Added the ``sortpages`` method to the ``sections``. This sorts the pages in a section (or all sections) alphabetically. You can also pass in a custom sort function. Version 0.5.0 Final 2006/10/11 ------------------------------ Paths in the ``file`` keyword and in the config file now have '~' expanded. This means they can use paths relative to the user directory. (Plus the 'colorize' and 'include' macros.) Added 'skiperrors' config file / command line option. Errors in processing a file can now be ignored and rest2web will attempt to continue processing. Fixed bug where non-ascii uservalues would blow up. There was a bug in handling tabs in embedded code. This has been fixed. The macro system has been revamped. All the standard macros are now built in as default macros. The modules needed by the default macros are also now built into rest2web. You can still add your own macros, or override the default ones, by supplying an additional macros file. ``Macro Paths`` section added to the config file for configuring the default macros ``smiley`` and ``emoticon``. The initial message printed by rest2web has been changed to ``INFO`` level, so that it is not displayed by the ``-a`` and ``-w`` verbosity levels. The namespace and uservalues for each page are now available to the macros, using global variables ``uservalues`` and ``namespace`` (dictionaries). This means you can write macros that are customised for individual pages. A config file is no longer required in force mode. (The current directory is used as the source directory and html output is put into a subdirectory called 'html'.) The restindex and uservalues block may now be in a ReST comment. This means that rest2web source documents with a restindex can still be valid ReStructured Text documents. Fixed imports in the gallery plugin. (Thanks to Steve Bethard.) Changed over to use the latest version of `StandOut `_. rest2web now exits with an error code corresponding to the number of warnings and errors generated. Errors and warnings are now output on ``sys.stderr``. Version 0.5.0 beta 1 2006/08/05 ------------------------------- Updated docs. Moved 'pythonutils' distribution into the 'rest2web' directory for ease of packaging. Added a ``#!`` line to ``r2w.py``. All rest2web imports now absolute imports. Added 'quickstart.txt' thanks to Andrew Ittner. Added an ``include`` standard function, this can be used to nest templates or customise sections. (It will walk up the directory tree looking for the file you specify and takes an optional argument if the file doesn't exist - useful for templates that allow subdirectories to add to the template, or even wrap the body.) ``make_dist.py`` now takes ``nopause`` as a command line argument (``make_dist.py`` is in `Subversion`_ for creating distributions.) Default breadcrumb divider is now '>'. Breadcrumbs are also output in HTML on separate lines for readability. Fixed bug when ``final_encoding`` is ``None``. Default config file is now called ``r2w.ini``. ``rest2web.ini`` will still be supported until the next release. Fixed bug with ``standerr`` where no logfile is used. ``print_crumbs`` can now take ``None`` for the dividers. Added 'globalValues' to the namespace. (Available in templates and pages for storing values which can be accessed across all pages.) Added 'uservalues' and 'restindex' into each page in the indextree. A new command line 'nopause' option to override the config file. Change so that variables (and functions etc) defined in templates can be used in single code blocks (like ``<% new_name %>``). Added more information about pages to the namespace and indextree. The new values are : 'source_file': The source file for the page 'current_dir': The current directory being processed - this can be turned into an absolute filepath by doing ``os.path.join(os.getcwd(), current_dir)`` 'target_dir': The target file directory (as an absolute file path) being rendered into. Note if the file has a target specified it may not be put in this directory. Use ``os.path.dirname(target_file)`` instead. 'full_page_url': The full url (starting with '/') for the current page 'target_file': The full filename of the page being rendered Fixed bug where 'thispage' wasn't set on pages in the indextree. (Value should be ``True`` for the current page.) Fixed bug where 'thispage' (in the namespace) would sometimes be incorrectly ``None``. Cached template files for faster execution. Special thanks to Martin Krafft for bugfixes and suggestions. Version 0.5.0 alpha 2006/05/01 ------------------------------ **rest2web** can now build a site with no index pages, no template and no restindexes. This is the `force <../force_mode.html>`_ command line option. It can be used to automatically build a site from a collection of ReST documents, and use default templates. ``uservalues`` can be passed at the `command line <../command_line.html>`_ and in the config file. (Command line `uservalues `_ override config file ones.) These uservalues are now available in every page. The encoding of uservalues in the config file is specified by the ``__encoding__`` value. A ``--template-file`` (or ``-t``) command line option. (Will override the top level ``template`` keyword specified in the restindex.) This allows you to have alternative templates for a site; for example one for an online version and another for distributed documentation. New website template, created by `Fuchsiashock Design `_. ``final_encoding`` should never be ``utf8`` - should be ``utf-8`` instead. This is because ``utf8`` is not recognised by browsers. (This is now automatically handled.) Added ``initialheaderlevel`` a new restindex keyword. It sets the size of headers used in ReST documents. Can be set per page. The ``file`` keyword has been bugfixed. It now only operates if the target file doesn't exist or is different to the source file. It copies the timestamp along with the file. The `gallery plugin `_ now ignores non-image files. It also skips image files it can't handle (currently only animated jpgs. **rest2web** now has three levels of verbosity, controlled from the command line : * ``-v`` : Verbose, the default. * ``-a`` : Warnings and actions. * ``-w`` : Warnings only. ``uservalues`` can now be inserted in pages using a new syntax. Where this is used, the uservalues are inserted *before* the page is rendered from ReST to HTML. This means uservalues can be in ReST format. The syntax for single values is ``{lt}* ... *>``. For multiple lines of code it is ``{lt}$ ... $>``. Added ``modtimeiso`` value to the namespace and the ``formattime`` `standard function <../functions.html>`_. The ``namespace`` and ``uservalues`` are both now available (as dictionaries) to the macros and the standard functions. Removed the two ``
`` from ``listend`` in the standard function ``minibar`` and added ``wrapper_class`` to ``print_details``. Added ``os`` and ``sys`` to the namespace for every page. The default crumb for index pages (if no ``page-title`` specified) is the filename, minus the extension and turned to title case. Removed ``urlpath`` from rest2web, because it is now in pythonutils. It won't run in the distribution directory - need to run "make_dist.py". (This only applies if fetched from `subversion `_). Version 0.4.0 alpha 2005/11/11 ------------------------------ There were a lot of changes between the 0.3.0 and the 0.4.0 release. This is a summary : Documentation refactored and improved, including the `tutorial <../tutorial.html>`_. Changes for compatibility with `Pythonutils `_ 0.2.3 and docutils 3.10. Added ``page_description`` to namespace. Added ``file`` keyword (reserved word in namespace). Added ``tags`` keyword. Added ``section-pages`` keyword (a way of specifying the order of pages in sections). Added ``__prune__`` (document in special files). The plugins system and the gallery. Changes to config file (psyco, pause, DEBUG). Interactive debug mode. Added ``section_contents`` and ``print_details`` functions. Three extra standard macros. Bugfix (and change) to the 'print_crumbs' function. It now takes an 'item' value - this means the last item is also a list item. Fixed bug where restindex options from one section could leak into another. Fixed bug where having ``include: No`` for an index page would cause a crash. Bugfix to ``thispage`` (not broken anymore). Fixed bug where subsections with a different 'file-extension' were broken. Fixed bug where not building an index page would cause a crash. Some changes for compatibility with py2exe (including addition of a ``py2exe-setup.py``). Version 0.3.0 2005/06/27 ------------------------------ Code refactored and better commented. (Thanks to Nicola Larosa for input). Minor bugfix - an encoding was missing. Added stylesheet to docutils options override. Version 0.2.3 2005/06/25 ------------------------------ Code style cleanup with help from Nicola Larosa. Start of the refactoring (some code is simpler internally) ``uservalues`` now compatible with reST. docs updated appropriately. Version 0.2.2 2005/06/12 ------------------------------ Added support for ``uservalues``. Version 0.2.1 2005/06/06 ------------------------------ Removed extraneous print statement from ``embedded_code.py`` Version 0.2.0 2005/06/01 ------------------------------ Various minor changes - especially additions to the namespace pages are rendered in. Sites are rendered a whole section at a time. This means pages have index data for that section available. This is the ``sections`` variable in the namespace. Added the ``output-encoding`` and ``final_encoding`` values to the restindex. Added the ``template-encoding`` value to the restindex. (rest2web is now entirely unicode internally). It's now possible to specify title and description for the default section. Added indextree and thispage, allows building of sidebars. Added standard functions. Added macros. Started using subversion repository. Changed all line endings to 'LF'. Version 0.1.0 2005/05/08 ------------------------------ First version released. Thanks to Andrew Ittner for testing on Linux. rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/smilies.txt0000600000175000017500000000747010414273736023622 0ustar madduckmadduckrestindex crumb: Smilies page-description: The full set of smilies - for use in the ``smiley`` `macro <../macros.html>`_. /description /restindex ====================== The Smilies Macro ====================== ------------------ Smilies ------------------ .. contents:: Introduction ============== Included in the example macros file is a function called ``smiley``. This uses a modified version of *smiley.py* by `Mark Andrews`_. It also requires the *path module*. These are both included as part of the **rest2web** distribution in the *modules* directory. If you move them elsewhere you should make sure they are on the *Python search path* [#]_. If you don't know what I'm on about then leave them where they are {sm;:lol:} The Smilies =============== To use the smilies you use the *smiley macro*. This can be either ``{curlyl}smiley;SMILEY{curlyr}`` or the shorter form ``{curlyl}sm;SMILEY{curlyr}``. Replace ``SMILEY`` with the symbols for the the smiley you want to display. For example ``{curlyl}smiley;8){curlyr}`` becomes {sm;8)}. Here is the full smiley set : * ``{sm;:doubt:}`` -- ``:doubt:`` * ``{sm;:-?}`` -- ``:-?`` * ``{sm;:!:}`` -- ``:!:`` * ``{sm;:lol:}`` -- ``:lol:`` * ``{sm;8)}`` -- ``8)`` * ``{sm;:(}`` -- ``:(`` * ``{sm;:)}`` -- ``:)`` * ``{sm;:eek:}`` -- ``:eek:`` * ``{sm;:-D}`` -- ``:-D`` * ``{sm;:cry:}`` -- ``:cry:`` * ``{sm;:-X}`` -- ``:-X`` * ``{sm;:?}`` -- ``:?`` * ``{sm;:evil:}`` -- ``:evil:`` * ``{sm;:-o}`` -- ``:-o`` * ``{sm;:mad:}`` -- ``:mad:`` * ``{sm;:oops:}`` -- ``:oops:`` * ``{sm;:grin:}`` -- ``:grin:`` * ``{sm;:-|}`` -- ``:-|`` * ``{sm;:-p}`` -- ``:-p`` * ``{sm;:biggrin:}`` -- ``:biggrin:`` * ``{sm;:idea:}`` -- ``:idea:`` * ``{sm;:shock:}`` -- ``:shock:`` * ``{sm;:o}`` -- ``:o`` * ``{sm;:?:}`` -- ``:?:`` * ``{sm;:badgrin:}`` -- ``:badgrin:`` * ``{sm;:roll:}`` -- ``:roll:`` * ``{sm;:arrow:}`` -- ``:arrow:`` * ``{sm;:|}`` -- ``:|`` * ``{sm;:smile:}`` -- ``:smile:`` * ``{sm;:p}`` -- ``:p`` * ``{sm;:razz:}`` -- ``:razz:`` * ``{sm;:wink:}`` -- ``:wink:`` * ``{sm;:-)}`` -- ``:-)`` * ``{sm;8-)}`` -- ``8-)`` * ``{sm;:cool:}`` -- ``:cool:`` * ``{sm;:D}`` -- ``:D`` * ``{sm;:neutral:}`` -- ``:neutral:`` * ``{sm;:sad:}`` -- ``:sad:`` * ``{sm;:???:}`` -- ``:???:`` The Missing Smilies ===================== {emo;exclaim} We can't use the following smilies. ``;-)`` and ``;)``. Because they contain a **";"** [#]_, we can't yet use these smilies. They both represent the same smiley. You have to use it in its other form; ``:wink:`` -- ``{sm;:wink:}``. Alternative Smiley Sets ======================== The nice thing about *smiley.py* is that it supports any smile sets with a **.pak** file. This is the standard for smiley sets that is used by phpbb_. You can find alternative sets of smilies at the stylesdb_ website. Simply replace the *docs_html/images/smilies* directory with an alternative set to use them. *smiley.py* will automatically create the right links by reading the **.pak** file. You will need to edit the ``smiley`` function in *macros.py* to have the right path to the smilies directory (to read it) and the right final path to the smilies for your site. Credits ======== * The *smilies* come from a gentleman called Spider_. * *smiley.py* is written by `Mark Andrews`_. It is licensed under the BSD license, which is the same one as the `Voidspace License`_ . * *path.py* (needed by *smiley.py*) is written by `Jason Orendorff`_. He has placed it in the public domain. .. [#] ``sys.path`` .. [#] This is a limitation of the macros_ system. .. _macros: ../macros.html .. _Jason Orendorff: http://www.jorendorff.com/articles/python/path .. _Voidspace License: http://www.voidspace.org.uk/python/license.shtml .. _Mark Andrews: http://www.la-la.com .. _phpbb: http://www.phpbb.com/ .. _stylesdb: http://www.stylesdb.com/smilies_styles.html .. _Spider: http://web.spidercode.de/smilies rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/index.txt0000600000175000017500000000060410414273736023254 0ustar madduckmadduckrestindex crumb: Reference link-title: Reference Section page-description: Documentation and reference for **rest2web**. /description /restindex ============================== rest2web - Reference Section ============================== .. raw:: html
<# print_details(sections[None]) #>
rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/plugins.txt0000600000175000017500000001270210414273736023630 0ustar madduckmadduckrestindex crumb: Plugins page-description: A description of the **rest2web** plugin system. A manual on how to write your own plugins. /description /restindex ============================ The rest2web plugin System ============================ ------------------------ Roll Your Own Features ------------------------ Plugins ======= .. note:: The plugin system is not yet production ready. The main problem is that plugins currently have to return encoded byte strings in the same encoding as the content. This is fine so long as you can gurantee that the output of your plugin can always be expressed in the encoding of your content ! I also haven't implemented support for global plugins. * XXXX __init__ should call start ! * XXXX document that changes to uservalues will have inconsistent results - don't do it ! The plugin system allows you to integrate your own functionality into rest2web. You can use this to implement plugins that process pages or generate their output in ways too complex for the templates. Plugins can either be global, or just applied to individual pages. You create plugins by writing a class that inherits from ``rest2web.PluginClass.PluginClass``, place it in a module in the plugins directory, and configure your pages/site to use it. An example of a plugin that works for individual pages would be a gallery_ - creating pages from directories of images. An example of a global plugin could be a sitemap generator or a search engine that indexes every page. The Basic Principle ------------------- Your plugin name is the module name. So ``gallery.py`` is the ``gallery`` plugin. You module must contain a class called ``Plugin`` - this will be imported when it is first used. Your plugin class only needs to implement the relevant methods. For a global plugin this will include the ``start`` and ``end`` methods. For a normal plugin you might only need to implement the ``page`` method. .. note:: You shouldn't override the ``__init__`` method. Use ``start`` instead. Each page specifies which normal plugins are being used (in the restindex). Each of the plugins (which may not be any of course), plus all the global ones, are called for every page. What this means is that the ``page`` method of your ``Plugin`` class is called - and passed the parameters : :: page(self, content, encoding, filepath, targetr, estindex, uservalues) This means that if you want to pass values or settings from the page to the plugin, they can be specified in the *uservalues*. The page method (when it has done it's work) should return a tuple of : :: (content, encoding, newvalues) *newvalues* is a dictionary that is used to update the uservalues. The values you return here can be used as variables in your page. When it is first initialised, *every* plugin is given a reference to the processor object and the original config file. You can access this from within your plugin methods using : .. raw:: html {+coloring} processor = self.processor config = self.config # # config is a dictionary like object value = config['value'] {-coloring} This means two things : 1) Global plugins can have universal settings in the site config file. 2) You can access the processor attributes like ``dir``, ``dir_as_url``, etc. :: dir = self.processor.dir Global Plugins -------------- When you run rest2web, global plugins are initialised (and their ``start`` method is called). They are then called for every page. A global plugin can implement any of the ``start``, ``page``, and ``end`` methods. You only need to implement the methods you are using. .. raw:: html {+coloring} from rest2web.PluginClass import PluginClass class Plugin(PluginClass): def start(self): """ Called when the plugin is initialised. For global plugins, this is when rest2web starts up. """ pass def end(self): """ Called after the last page has been rendered. Only for global plugins. """ pass def page(self, content, filepath, target, encoding, restindex, uservalues): """ Called for every page with globals. """ newvalues = {} return (content, encoding, newvalues) {-coloring} Normal Plugins -------------- A normal plugin will only be used for individual pages. You specify plugins to use on a page with the ``plugins`` keyword in the restindex : :: plugins: gallery, plugin2 Normal plugins can implement the ``start`` and ``page`` methods. .. raw:: html {+coloring} from rest2web.PluginClass import PluginClass class Plugin(PluginClass): def start(self): """ Called when the plugin is initialised. For normal plugins, this is when they are first used. """ pass def page(self, content, encoding, filepath, target, restindex, uservalues): """ Called only for pages using the plugin. """ newvalues = {} return (content, encoding, newvalues) {-coloring} For an example of this kind of plugin, have a look at the gallery_ plugin. The Page Method =============== Todo.... Encoding Issues =============== Todo... .. _gallery: http://www.voidspace.org.uk/python/rest2web/reference/gallery.html rest2web-0.5.2~alpha+svn-r248.orig/docs/reference/gallery.txt0000600000175000017500000003245110462725010023577 0ustar madduckmadduckrestindex crumb: Gallery page-description: Reference for the gallery plugin. /description /restindex ============================== Picture Galleries and Beyond ============================== -------------------- The Gallery Plugin -------------------- :Author: Fuzzyman_ :Date: 2005/08/12 :Version: 0.2.0 :Homepage: `Gallery Page`_ .. contents:: Galleries of Images Introduction ============ The gallery acts as a plugin to rest2web_. (It can also fuction as a `standalone program`_). It generates an html gallery page from a directory of images, an html template, and a few settings. It creates a single main gallery page, with thumbnails that act as links to each image. The images themselves appear on their own page, with a title and optional description. Each page has links to the next and previous images, and back to the main gallery. You can see the sort of output it generates with the `online example`_, which acts as the gallery test site in the rest2web_ distribution. If you are viewing this from the test docs, you can build the test gallery by running [#]_ : :: r2w.py gallery_test.ini You can then view the gallery at `gallery index`_. Downloading =========== At some point I will package the gallery on it's own. Currently it's only available bundled with **rest2web**. Using Gallery With rest2web =========================== In order to use it with rest2web, ``gallery.py`` needs to be available in the ``plugins`` subdirectory of rest2web - and also PIL_ must be installed. In order to use the gallery in a page, you must have 'gallery' in your list of plugins for the page. This is done in the restindex, with a line like : :: plugins: gallery Next you must specify a set of values that tell the gallery what to do. When run as a plugin you specify these as ``uservalues``. uservalues Settings =================== The settings control the gallery program, they specify which directory to read the images from, which templates to use to generate the output pages, and so on. All the settings must be present, or the gallery will raise an error. Assuming you have no other ``uservalues``, the settings look like : :: uservalues thumb_size = 150, 150 gallery_dir = ../../FTP Mirror/gallery gallery_url = gallery data_file = gallery_data.ini page_template = page.html entry_template = entry.html gallery_mode = 1 /uservalues See `the settings`_ section for an explanation of what they all mean. Standalone Program ================== *gallery.py* will work as a standalone program as well as a plugin to rest2web. It uses the same settings, but gets them from a config file instead of uservalues. One of the main reasons to make gallery.py run in this way, was so that Aidan could use it for his online `Pages of Computer Imagery`_. In order to use gallery.py as a standalone program, you will need Python_ 2.3 or later and a recent version of PIL_. You will also need either pythonutils_ 0.20 *and* rest2web_ installed [#]_ ; or just version 0.2.0 (or higher) of pythonutils [#]_. By default the gallery looks for a config file called ``gallery.ini``. You can also specify an alternative config file at the command line [#]_. All the settings need to be present or the gallery will raise an error. The complete settings are : :: thumb_size = 150, 150 gallery_dir = ../../FTP Mirror/gallery gallery_url = gallery data_file = gallery_data.ini page_template = page.html entry_template = entry.html gallery_template = gallery.html gallery_mode = 1 gallery_page = gallery_page.html See `the settings`_ for what they mean. The Settings ============ Most of the settings have the same meaning however you use gallery. * ``thumb_size`` -> ``width, height`` e.g. *150, 150* This is the *maximum* size of the generated thumbnails. It keeps the proportions of the original image. * ``gallery_dir`` -> ``path to directory`` e.g. *../../FTP Mirror/gallery* This is the path from the file to the directory of images. * ``gallery_url`` -> ``url path`` e.g. *gallery* The final url path from the gallery to the image directory. This path is used to make paths for all the images/thumbnails/pages. .. note:: When run as a standalone program, the image directory should be in a single subdirectory from the main gallery. Hopefully this limitation will be removed soon. * ``data_file`` -> ``filepath`` e.g. *gallery_data.ini* This is the data file that stores details of all the image files and thumbnails. See the `Gallery Data File`_ section for details. * ``page_template`` -> ``filepath`` e.g. *page.html* The path to the template file for the individual pages. See `The Templates`_ for details. * ``entry_template`` -> ``filepath`` e.g. *entry.html* The path to the template file for every thumbnail on the main page. See `The Templates`_ for details. * ``gallery_mode`` -> ``1`` or ``2`` e.g. *1* Whether gallery should scan all the images (1), or just use the stored data file to generate the pages (2). See the `Gallery Data File`_ section for details. Standalone Specific Settings ---------------------------- These next two settings are only used when gallery.py is run as a standalone program. * ``gallery_template`` -> ``filepath`` e.g. *gallery.html* This is the template file used to create the main gallery page. * ``gallery_page`` -> ``filepath`` e.g. *gallery_page.html* This is the filename to save the main gallery page as. See `The Templates`_ for details. Gallery Data File ================= The gallery data file won't exist until the first time you run gallery. When *gallery.py* runs it scans the image directory and stores the details of each image. It puts all the thumbnails in a 'thumbnail' subdirectory of the image directory. It puts all the html pages it creates in an 'html' subdirectory of the image directory. If these directories don't exist, it will create them. The datafile it creates is effectively a config file with sections [#]_ - one section per image ! If you have pythonutils 0.3.0 [#]_ (or more recent) then the order of these sections will be the order that images appear in the gallery. This means you can cut and paste to edit the order. .. warning:: If you run gallery (in mode 1) after removing any images from the image directory - it will remove their entry from the data file. There are two other entries that you can edit per image : 1) *Title* - by default gallery generates a title for the image based on the filename. It replaces '_' with spaces and capitalizes words. 2) *Description* - You can put an optional description for the image here. This will appear below the image, on it's page. .. note:: Entering a description with several lines is only possible with pythonutils 0.3.0 or higher (or configobj 4). If you have pythonutils 0.3.0, you should surround entries with multiple lines between triple quotes - ``'''like this'''``. Gallery Mode ------------ The details it stores in the data file are sufficient to generate the main gallery page and all the individual pages, without having to scan the image directory again. If ``gallery_mode`` is set to *2* (see `The Settings`_), then gallery *won't* scan the image directory. Not only is this quicker - but it can be useful when you have finalised the contents of a gallery and want to move the images. If you *do* want gallery to scan the image directory, set ``gallery_mode`` to *1*. This won't overwrite any titles or descriptions, but it will add new images and remove entries for ones you've deleted. You can also use it to regenerate thumbnails if you change their size. The Templates ============= *gallery.py* generates it's output from html templates that you supply. It fills in values in the template with things like the path to the image or thumbnail, it's height, width, or title, and so on. If you are using gallery from rest2web there are two templates. If you are running it on it's own there are three. You need to supply the path to each of these files in the settings. Obviously a good place to start is by looking at the example ones provided. The three templates are : * *entry_template* This is a small template. It is used for every thumbnail and link in the main gallery page. * *page_template* This is the template used to make each page (the ones containing the individual images). * *gallery_template* This is only used when running as a `standalone program`_. It is used to make the main page with all the thumbnails on. The templates have various entries in them, that gallery replaces with the right values for the pages it is building. They all look like ``**some name**``. When you edit the templates, you must keep these for the page to work. .. note:: In fact it won't cause an error to remove any of the special entries from the templates - that value will just be missing from the final pages. You can use this to further customize the appearance of your galleries. entry_template -------------- This template is used for every thumbnail and link in the main gallery page. The special values are : * ``**link**`` Link to the image page. * ``**thumb**`` Path to the thumbnail (for the ``img`` tag). * ``**width**`` Thumbnail width. * ``**height**`` Thumbnail height. * ``**title**`` Image title. page_template ------------- This is the template used to make each page (the ones containing the individual images). The special values are : * ``**title**`` The image title. Used as the page title, and as a heading above the image. * ```` * ```` In the template the 'previous image' and 'next image' are commented out. If they are needed in the page, the comment markers are removed. Obviously, the first image doesn't have a previous (left) image, and the last doesn't have a next (right). * ``**linkleft**`` * ``**linkright**`` These are the links to the previous and next image pages. * ``**thumbleft**`` * ``**thumbright**`` These are the paths to the thumbnails for the previous and next images. * ``**widthleft**`` * ``**heightleft**`` * ``**widthright**`` * ``**heightright**`` The widths and heights for the next/previous thumbnails. * ``**titleleft**`` * ``**titleright**`` The image titles for the next/previous thumbnails. * ``**linkgallery**`` The link back to the main gallery page. * ``**image**`` The path to the actual image. * ``**widthmain**`` * ``**heightmain**`` The width and height of the actual image. * ``**description**`` Guess what ! gallery_template ---------------- This template is only needed (or used) when running gallery.py as a standalone program. It is used to make the main page with all the thumbnails on. The only special value is : * ``**gallery**`` This is replaced with the html containing all the thumnail images and links. Using the Output ================ ..note:: This section is only relevant when the gallery is used from rest2web. Using the output from the gallery in your page is very easy. It creates an extra value ``gallery`` to use in your page. If the page format is {acro;reST;reStructured Text}, the ouput is indented and put inside a ``.. raw:: html`` directive [#]_. The output for the main page contains thumbnails with links to the individual pages. It is made up from the *entry* template - with the value filled in for each image. You would normally use it with something like : :: <% gallery %> TODO ==== The gallery is still experimental ('alpha' quality). I need to check the following things. * XXXX Does it delete thumbnails and html pages for deleted images ? * XXXX Sort html for multi-line descriptions. * XXXX check "file_error" works * XXXX If the page format is reST, the ouput is indented and put inside a ``.. raw:: html`` directive. * XXXX *except* - shouldn't use plugins with reST ------- Footnotes ========= .. [#] On Windoze you can just double click on ``gallery_test.bat``. .. [#] Installed means somewhere on your normal PYTHONPATH. .. [#] Version 0.2.0 was the first to include *urlpath*, which gallery.py needs in order to run. .. [#] Or by creating a ``bat`` file if you use windoze and are unfamiliar with the command line. To run gallery.py with *gallery.ini*, create a text file called *gallery.bat* - with ``python gallery.py gallery.ini``. Double clicking on *gallery.bat* should then run gallery.py with the correct config file. .. [#] It uses ConfigObj_ to read/write the 'INI' style config files. .. [#] Which has ConfigObj 4 - this retains the order of entries in config files. .. [#] Except currently plugins are not compatible with pages in reST format. That will be fixed soon. .. _gallery index: ../gallery_test/index.html .. _rest2web: http://www.voidspace.org.uk/python/rest2web/ .. _PIL: http://www.pythonware.com/products/pil .. _python: http://www.python.org .. _pythonutils: http://www.voidspace.org.uk/python/pythonutils.html .. _Configobj: http://www.voidspace.org.uk/python/configobj.html .. _Pages of Computer Imagery: http://www.nebulae.org.uk .. _fuzzyman: fuzzyman@voidspace.org.uk .. _gallery page: http://www.voidspace.org.uk/python/rest2web/reference/gallery.html .. _online example: http://www.voidspace.org.uk/python/rest2web/gallery_test/index.html rest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial_site/0000700000175000017500000000000010644674642022341 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial_site/subdirectory/0000700000175000017500000000000010644674642025057 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial_site/subdirectory/index.txt0000600000175000017500000000124210414273736026722 0ustar madduckmadduckrestindex crumb: Subdirectory target: subdirectory.html page-description: A subdirectory - with pages of it's own. /description /restindex ========================= Subdirectory Index Page ========================= -------------- The Subtitle -------------- .. role:: raw-html(raw) :format: html .. raw:: html
<# if not default_section['pages']: print '

No Pages Yet

' else: print_details(default_section) #>
.. class:: intro This page lives at :raw-html:`<% pagepath %>`. The ``class`` directive applies a style to this paragraph. rest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial_site/template.txt0000600000175000017500000000167510414273736024722 0ustar madduckmadduck <% title %>
<% body %>

Return to Top
Page last modified <% modtime %>.

rest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial_site/index.txt0000600000175000017500000000073410414273736024211 0ustar madduckmadduckrestindex crumb: Home format: html page-title: rest2web Tutorial Website include: No /restindex

rest2web Tutorial Website

An Example Index Page

<# print_details(default_section) #>

This is the index page for the rest2web Tutorial website. It's not got much on it - other than this paragraph and links to our other pages.

rest2web-0.5.2~alpha+svn-r248.orig/docs/tutorial_site/example.txt0000600000175000017500000000152110414273736024530 0ustar madduckmadduckrestindex crumb: A Page link-title: An Example Page page-description: This description is for the index page. You can use **reST** markup if you want. /description /restindex ============== A ReST Title ============== -------------- The Subtitle -------------- .. This is a comment. To use the 'raw-role', we have to define it *first*. .. role:: raw-html(raw) :format: html This page is written in {acro;reST;ReStructured Text} markup. That's why it looks like *plain text*. This page lives at :raw-html:`<% pagepath %>` [#]_. This tutorial isn't a tutorial on ReStructuredText though. If you're looking for one, you might be better off with the `docutils documentation`_. .. [#] The file path is dynamically inserted by rest2web. .. _docutils documentation: http://docutils.sourceforge.net rest2web-0.5.2~alpha+svn-r248.orig/docs/index.txt0000600000175000017500000001062310542017106021305 0ustar madduckmadduckrestindex crumb: rest2web tags: index, main page, sourceforge, mailing list, svn, svn repository output-encoding: utf8 template-encoding: utf8 section-pages: , introduction, quickstart, config_file, tutorial, command_line, force_mode, templating, the_restindex, functions, macros, special_files, reference/index, test_site/index, translation/index link-title: rest2web Home /restindex ============================= rest2web - The Site Builder ============================= --------------------------------------- Autogenerating Websites with Docutils --------------------------------------- :Author: Michael Foord {sm;8)} :Contact: fuzzyman@voidspace.org.uk :Version: 0.5.1 :Date: 2006/12/16 :License: BSD-License_ :Home Page: rest2web_ :Quick Download: `rest2web-0.5.1.zip `_ :Tarball: `rest2web-0.5.1.tar.gz `_ :Sourceforge: `rest2web Project Page`_ :Development: `SVN Repository`_ :Support: `rest2web Mailing List`_ .. image:: images/logos/rest2web200x80.gif :height: 80 :width: 200 :alt: Site Built with rest2web :align: center .. image:: images/screenshot.jpg :height: 308 :width: 410 :alt: Building with rest2web :class: image :align: center :target: images/screenshot_big.jpg rest2web - The Website Builder ============================== {emo;html} Maintaining websites or project documentation in {acro;HTML} is a pain. **rest2web** takes out the pain, and brings back the joy. {sm;:wink:} **rest2web** is a simple tool that lets you build your website from a single template (or as many as you want), and keep the contents in `ReStructured Text `_ [#]_. (You can still keep pages in HTML if needed.) It can also aid in having multiple translations of your site ({acro;i18n;Internationalization}) and a *host* of other features. {sm;:-)} Below is an index to all the rest2web documentation, but for more information check out the `introduction `_. Alternatively, browse the following websites. They are *all* built with **rest2web** and show the range of different styles it is capable of creating [#]_ : * `Voidspace `_ * `Project Pipe `_ * `Martin Krafft's PHD Pages `_ * `HP Linux Imaging and Printing `_ * `Live Off Grid `_ * `CRSR `_ * `Civitas `_ * `German Website on Text Based Tools `_ * `The Python Academy (English) `_ * `The Python Academy (Deutsch) `_ * `Creatext `_ * `Tibet.net `_ * `Homesite of Marius Soutier `_ * `Homesite of Paul Bartletts `_ * `Homesite of Peter Brett `_ * `Homepage of Gal Varoquaux `_ * `The Rhyming Panda `_ * `Aikido Club Website `_ * `Introduction to Scott Mcdermott `_ It also builds the documentation for the following projects : * rest2web_ * `PyGeo `_ * `Movable Python `_ * `Firedrop2 `_ To see recent changes, visit the `Changelog `_. .. raw:: html <# print_details(default_section, wrapper_class="displaybox") #> --------------------- .. [#] {acro;ReST;ReStructuredText} is a plain text {acro;WYSIWYG} markup. It's a great way of keeping documents in a readable and editable form. .. [#] If your site is built with **rest2web**, then `Contact Me `_ to get it added to this list. .. _BSD-License: http://www.voidspace.org.uk/python/license.shtml .. _rest2web: http://www.voidspace.org.uk/python/rest2web/ .. _Rest2web Mailing List: http://lists.sourceforge.net/lists/listinfo/rest2web-develop .. _SVN Repository: https://svn.rest2web.python-hosting.com/trunk/ .. _rest2web Project Page: http://sourceforge.net/projects/rest2web rest2web-0.5.2~alpha+svn-r248.orig/docs/introduction.txt0000600000175000017500000002757210541054604022735 0ustar madduckmadduckrestindex crumb: Introduction tags: introduction, site, website page-description: A gentle introduction to **rest2web**. /description /restindex ========================== Introduction to rest2web ========================== ------------------------------------------- A Gentle Introduction to the Site Builder ------------------------------------------- .. contents:: What is rest2web ? ================== {emo;python} **rest2web** is a tool for autogenerating wesites, or parts of websites. It's main features are : * Integrated with docutils_. * Automatically builds index pages and navigation links (sidebars and {acro;breadcrumbs;Weird name for navigation links ?}). * Embedded code in templates for unlimited expressiveness. * Flexible macro system. * Uses relative links, so sites can be viewed from the filesystem. * Unicode internally - so you don't have to be. {sm;:-p} * Includes features for multiple translations of sites. * Built-in gallery creator plugin. * The basic system is very easy to use. * Lots of powerful (optional) features. The content can be stored as {acro;HTML}, or in ReST_ format; in which case the HTML will be generated using docutils_. **rest2web** inserts each page into a template, and automatically creates index pages for sections, and navigation links. {sm;:-)} {acro;ReST} is a {acro;WYSIWYG} text markup format. It is simpler than HTML - but *very* flexible. A document marked up with reST is still readable as text. This means that maintaining article contents in reST is a *lot* easier than maintaining them in HTML. Adding new pages is as easy as dropping a text file into the right folder. **rest2web** builds the new page and adds a link to it in the index (optionally with a description as well). Removing a page is just as easy. Delete the file, and when you run **rest2web** again it removes the entry. **rest2web** includes standard functions to build a sidebar, with links to the other pages in the directory, or to the other sections in the website. Another advantage is that a website can be easily restyled by only changing template files. It is likely that only a few template files will be needed for most websites [#]_. Because rest2web generates sites using *relative paths*, the results can be viewed from the filesystem. This means that it is an effective way of bundling documentation. Internationalization -------------------- With uservalues_ you can use **rest2web** to create versions of a website in different languages. Simply create the framework once with your uservalues as place markers - then create your files with different translations. **rest2web** will build the website and can handle linking the sites together. An example of a site that uses this feature is, the `Website of Marius Soutier `_. Static Versus Dynamic --------------------- {emo;html} rest2web is a dynamic website generation tool - that generates static (HTML) pages. The rationale of creating static content, is that most pages will only change occasionally - serving static HTML content is quicker than generating pages dynamically. Not only that, but static content can be served by a lot cheaper hosting account! Distributing HTML documentation is another reason for wanting static pages. Why rest2web ? -------------- **rest2web** comes about from a need for me to restyle a whole website from badly marked up HTML 4, into beautiful {acro;XHTML} and {acro;CSS;Cascading Style Sheets}. {sm;:grin:} See the `Voidspace Manifesto`_ for a discussion of my aims and objectives in the restyle. An added challenge is that part of the website is already restyled - and the blog pages are built using a different tool (Firedrop2_, which also uses ReST incidentally). All this means that rest2web needs to be able to handle generating just *part* of a website, and indexes for the existing parts where necessary. A rest2web Site =============== **rest2web** is optimized for creating websites with a particular structure. It is aimed at the sort of sites that have sections and subsections. The basic structure is closely related to the directory structure on disk. It is based on having an index page in each directory. Files in that directory are listed in the index page, which can be divided into various sections. {sm;8-)} Subdirectories have their own index page - that index page will be link to from the 'directory' above. Your website structure is modelled on the directory structure. This is the hierarchy of sections and subsections. Every text file ('.txt' extension) in a directory will be scanned. If the file starts with a restindex_, then it will be processed. If you want links to some prebuilt files on an index page, then you can put a relevant text file in the directory with the details (target name, link title, page description, etc) and set ``build: No``. This allows you to use rest2web to just build part of a website - but still include in the index pages that it doesn't build. [#]_ For a good introduction, read the tutorial - `Creating a Site`_. You can also peruse the `Test Site`_ included with this documentation. It illustrates most features of rest2web. Every page can *also* have a set of uservalues_ that will be inserted into the template. This is another way of putting dynamic values into a template. An obvious use of this system is for providing a website in several different languages. If you just want to get up and running, you may want to checkout the `Quick Start Guide `_ by *Andrew Ittner*. Downloading =========== {emo;file1} All distributions include all the documentation and the example site. Source Distribution ------------------- **rest2web** is a pure Python programme. If you have Python installed you don't need to compile anything to use the source distribution. Download **restweb** (2.49mb) from `rest2web-0.5.1.zip `_, or `rest2web-0.5.1.tar.gz `_. Executable Distribution ----------------------- Download the executable version of **restweb** (3.5mb) from `rest2web-0.4.0alpha-EXE.zip `_ Subversion Repository --------------------- You can browse (or download using SVN) the latest development version over at the `SVN Repository`_. [#]_ The version in SVN is often more up to date than the latest release version - and I *try* not to break the version in SVN (but no guarantees). The full command is : ``svn co https://svn.rest2web.python-hosting.com/trunk/`` [#]_ .. note:: If you obtain rest2web from subversion, it won't run from its own distribution directory unless you delete the ``__dist__`` file. Sourceforge ----------- **rest2web** can also be downloaded from the Sourceforge `rest2web Project Page`_. Installing ========== {emo;eyeballz} If you are running the source version of **rest2web**, you need docutils_ installed. It works best with the latest version [#]_. rest2web itself shouldn't actually need installing. You *can* place the 'rest2web' directory in your 'site-packages' folder if you want to. You should then be able to run 'r2w.py' from anywhere. You can test rest2web by just double clicking on it. This should build the docs and example site as html, in the *docs_html* directory. It will generate a log of the process in 'log.txt'. If there are any problems then they will be recorded in the log. Please report any bugs to the `rest2web Mailing List`_. The files in the 'modules' folder are used by macros_. They will also need to be somewhere on ``sys.path`` (e.g. in 'site-packages'.) Alternatively, if the modules directory is in the same directory as 'r2w.py' when you run it, it will automatically be added to the path. At the Heart ------------ **rest2web** and docutils are both projects written in a language called Python_. In order to use the source version you will need Python installed. .. hint:: If you don't want to (or can't) install Python, then you can use the pre-built executable version instead. This does limit slightly what you can do with macros and embedded code in the templates. (Basically you can only use the modules that come included or that you create yourself). The templates_ and macros_ are also done using Python code. If you've never used Python before, don't let this put you off. It's a very easy language to learn and you can learn the basics very quickly. The main Python_ website has distributions for most platforms available for download, as well as links to tutorials and resources for learning Python. Useful Links ============ * rst-ht2html_ - This is a project with a similar aim, the automatic creation of websites from restructured text. It uses ht2html_, the scripts that generate sites like Python.org_, and Jython.org_. rest2web is more flexible with the templating it allows. It should be possible to generate a much wider range of sites with rest2web. * docutils_ - this is the Python project that turns reStructuredText into nice XHTML. * Firedrop2_ - the blog client originally created by `Hans Nowak`_. This project [#]_ produced ``embedded_code.py`` and ``textmacros.py`` which are used by rest2web. ``embedded_code.py`` is the templating engine used by rest2web. ``textmacros.py`` adds a simple system of macros_ into **rest2web**. Firedrop2 also supports alternative text markups *sextile* and *textile*. rest2web may be enhanced to support these (if there is any demand). .. note:: I now maintain **Firedrop2**. You can download the latest distribution, and browse the docs over at : http://www.voidspace.org.uk/python/firedrop2/index.shtml. {sm;:cool:} * webgen_ - A ruby_ project that does a similar thing to **rest2web**. It is *scarily* similar in the way it works. {sm;:lol:} It doesn't work with ReST_ markup however. * Tahchee_ - an alternative to *rest2web* that uses the cheetah_ templating system. --------------- Footnotes ========= .. [#] Many websites will only need a single template file. `Voidspace `_ actually uses two for *all* the parts of the site built by rest2web. .. [#] It's possible to include details of several of these pages in a single file called 'restindex.txt'. It is basically a series of restindexes in a single file. .. [#] Many thanks to the decent folks over at `python-hosting.com `_. .. [#] For Windoze users, I recommend the SVN client TortoiseSVN_. .. [#] Version 0.3.9 or later. .. [#] And **Kaa**, the predecessor to firedrop. .. _rst-ht2html: http://www.rutherfurd.net/articles/rst-ht2html.html .. _ht2html: http://ht2html.sourceforge.net/ .. _Python: .. _python.org: http://www.python.org .. _jython.org: http://www.jython.org .. _docutils: http://docutils.sourceforge.net .. _Hans Nowak: http://zephyrfalcon.org .. _Firedrop: .. _firedrop2: http://www.voidspace.org.uk/python/firedrop2/index.shtml .. _rest: http://docutils.sourceforge.net/rst.html .. _Voidspace Manifesto: http://www.voidspace.org.uk/documents/voidspace_manifesto.html .. _pythonutils module: http://www.voidspace.org.uk/python/pythonutils.html .. _restindex: restindex.html .. _Creating a site: tutorial.html .. _rest2web: http://www.voidspace.org.uk/python/rest2web .. _BSD-License: http://www.voidspace.org.uk/python/license.shtml .. _Rest2web Mailing List: http://lists.sourceforge.net/lists/listinfo/rest2web-develop .. _SVN Repository: https://svn.rest2web.python-hosting.com/trunk/ .. _templates: templating.html .. _macros: macros.html .. _Test Site: test_site/index.html .. _rest2web Project Page: http://sourceforge.net/projects/rest2web .. _uservalues: reference/uservalues.html .. _TortoiseSVN: http://tortoisesvn.tigris.org/ .. _webgen: http://webgen.rubyforge.org/ .. _ruby: http://www.ruby-lang.org/en/ .. _tahchee: http://www.ivy.fr/tahchee/ .. _cheetah: http://www.cheetahtemplate.org/ rest2web-0.5.2~alpha+svn-r248.orig/docs/quickstart.txt0000600000175000017500000002223410447605110022373 0ustar madduckmadduckrestindex tags: quickstart, introduction, beginners, site builder, website, basics, guide, basic guide crumb: Quickstart link-title: Quickstart page-description: An quick start guide showing how to use **rest2web** to create a simple website. /description /restindex .. role:: raw-html(raw) :format: html ====================== Rest2Web Quick start ====================== This document shows how to create a very simple site with rest2web_. .. contents:: Files and Directories ---------------------- You need: - a config file (simple text file) - an input directory (the directory that rest2web_ will process - your site's source) - an output directory (where rest2web_ will place the new HTML files) - content! Do this ============= Make the following directory structure, with empty text files (**directories**, *files*). - **quickstart** - *quickstart.ini* - **input** - *index.txt* - *template.txt* - *somecontent.txt* - **subdir1** - *index.txt* - **subdir2** - **output** Things to note =============== - rest2web_ will build all the directories and files under ``input/``. If a directory does not have an ``index.txt`` file in it, **the directory will not be processed**. Configuration ------------- Do this ============ Add the following to ``quickstart.ini``:: psyco = False pause = False log_file = '/home/user/quickstart/qs_log.txt' DEBUG = False compare_directory = '' # these values we have edited for our site start_directory = '/home/user/quickstart/input' target_directory = '/home/user/quickstart/output' macros = '' Change the ``/home/user/`` prefix to a suitable location. For windows users, simply use the appropriate syntax (e.g. ``C:\data\quickstart\``). Explanation ============ log_file where to write the log file start_directory the root directory of the source files target_directory where to write the HTML files .. note:: You may use any location for the start and target directories; they do **not** have to be siblings, **nor** do they have to reside underneath the same directory as the ini file. Source files ------------- Each source file corresponds to a content file, except under special circumstances (like telling rest2web_ **not** to process a directory, which we will cover later). Every source file needs a ``restindex`` block at the very top. The ``restindex`` can contain multiple options. Do this ========== Add the following to ``quickstart/input/index.txt``:: restindex crumb: quickstart root format: rest page-title: the quickstart root! encoding: utf-8 output-encoding: None /restindex restindex format ================= * Must be at the very top of the file * Must start with ``restindex`` and end with ``/restindex`` * Each option must be indented by a consistent amount * An option with values must be in this format: ``option: value`` * A block of options must start with ``option`` (indented) and end with ``/option`` (same indentation) What it means ============== crumb what should be shown on the breadcrumb trail for this page. format the two most common formats are ``rest`` for ReStructuredText; and html for HTML page-title The name of the page. You do not need this if you specify a title in-line. encoding The encoding that *the source file* is in. ``iso-8859-1`` and ``utf-8`` are common. output-encoding The encoding for the *output file*. ``None`` means "use the same encoding as the input file." Do this =========== Add a restindex block to each ``index.txt`` file in the ``input/`` tree, and also to any text files you want included in the build. The easiest way is copy the example, or modify what you placed in the ``input/index.txt`` root source file, and add it to each. Remember to customize the ``crumb`` and ``page-title`` options! Oh, and **add some content to each page**. That *is* the whole point, right? Templates ---------- The template controls how your site looks, what navigation features to include - basically anything outside of the content. We will use `the same template`__ as in the tutorial_, and add the ``print_details(default_section)`` macro. __ ./tutorial.html#html-template Do this =========== Add the following to ``quickstart/template.txt``:: <% title %>
<% body %>

Return to Top
Page last modified <% modtime %>.

Build your site ------------------ Run the following at the command line:: python /path/to/rest2web/r2w.py /path/to/quickstart/quickstart.ini -w This builds your site, and display any warnings (``-w``). If you see any errors, read them from the bottom up (especially if they are Python_ errors like ``traceback``) and fix them. If you cannot resolve an error, many helpful people on `the rest2web mailing list`__ are happy to help. __ http://lists.sourceforge.net/lists/listinfo/rest2web-develop Ignoring and including files ------------------------------ rest2web_ normally builds the entire site, ignoring any files not ending in "``.txt``" This can be a problem if you have pre-existing content, perhaps built by another process, that you simply want rest2web_ to link to. How to not overwrite a directory ================================= Assume we already have ``subdir2`` in the ``output/`` directory, and rest2web_ should not overwrite that directory or the index.html file within it. Do this ========= Create ``quickstart/input/subdir2/index.txt`` and add the following:: restindex # NOTE: the files listed here must reside in the OUTPUT directory; r2w will NOT copy them over crumb: subdir2 format: html page-title: subdir2's index encoding: utf-8 output-encoding: None target: index.html # "build: no" prevents this page from overwriting the existing subdir2/index.html page! build: no /restindex Then rebuild the site. As you may have guessed, the relevant option in the ``restindex`` block is ``build: no``. This tells rest2web_ that there is a ``output/subdir2/index.html`` file, so **do not overwrite it**. The ``crumb`` and ``page-title`` options mean that rest2web_ will include those values in any site maps or sections. The section of your site that rest2web_ did *not* build will still be connected to the part that rest2web_ *did* build. .. note:: rest2web_ will not create ``subdir2/`` or ``subdir2/index.html``, so make sure you create it yourself. How to include a file ====================== Have files that you want to include but do not want them to get built? Or files that rest2web_ skips over, like ``.py`` text files or ``.png`` image files? Never fear! ``file:`` is here! Do this ======== - Create a new file and save it to ``quickstart/input/somefile.abc``. - In ``quickstart/input/index.txt``, add this option to the ``restindex`` block: ``file: somefile.abc`` - Save `index.txt`. - Build your site. Result: ``quickstart/output/`` now has ``somefile.abc``! Some reasons to use rest2web_ to handle files that it does not build: - rest2web_ will never overwrite it in the ``output/`` directory, because it already knows about it. - The modification times will remain the same. - The file will *only* get copied over if it is newer than what is already in ``output/``. Congratulations ----------------- You finished the rest2web_ quickstart! Next steps ------------ - Templates_ are powerful ways to customize your site's look and navigation. - The stylesheets are probably not connected correctly. Make new ones and use the ``file:`` keyword to have rest2web process them. - Macros_ are powerful ways to customize your site's look and naviga - oh wait, I already said that. Well, they are *different* powerful ways. - The tutorial_ gives much more detail about creating your site using rest2web_. Author and copyright ---------------------- Written by `Andrew Ittner`__, :raw-html:`©` 2006. Licensed under the same license as rest2web_. __ http://www.rhymingpanda.com/ .. _rest2web: http://www.voidspace.org.uk/python/rest2web/ .. _Python: http://www.python.org/ .. _templates: ./templating.html .. _macros: ./macros.html .. _tutorial: ./tutorial.html rest2web-0.5.2~alpha+svn-r248.orig/docs/force_mode.txt0000600000175000017500000000551510513302450022301 0ustar madduckmadduckrestindex crumb: Force Mode page-description: A special mode (set from the command line) allows **rest2web** to build a website from a collection of ReST documents. In this mode templates, index pages and restindexes are optional. /description tags: website, force, automatic, quick, shortcut /restindex ================================ Building Websites the Easy Way ================================ ---------------- The Force Mode ---------------- .. contents:: Introduction ============ Sometimes you just want to throw some ReST documents together as HTML. Or perhaps you need to keep your documents in normal ReST syntax, and you don't want to add a ``restindex``. In this case, you want the *force mode*. {sm;:-)} In essence the force mode makes templates, restindexes and index pages optional. It does this by supplying defaults where they are missing. The default template (complete with sidebar) and default index page are stored in the ``rest2web/defaults/`` directory. You are of course free to edit them. .. note:: This functionality is new, and needs to mature. For example, at the moment you will probably want to edit the supplied defaults. An obvious feature to add would be to allow the overriding of the defaults at the command line. If you have any opinions about how this feature should develop, then please comment on the `rest2web-develop mailing list `_. Files in your site can still be contained in a directory tree. rest2web will create an index page for each directory, and link between them via the index pages, sidebar and breadcrumbs. Using Force Mode ================ You activate force mode through the command line, with either the ``-f`` or ``--force`` option. When force is on and no template is specified, then the default one (``restweb/defaults/template.txt``) is used. If a template is specified in the restindex of any index page, then that will be used for that directory (and all subdirectories) in the usual way. Every directory that has no ``index.txt`` will use the default one. (``restweb/defaults/index.txt``) Every text file without a restindex will be assumed to be in ReST format. It will use the normal defaults for a restindex. The index page for the top level will have the title *Index for Site*. Other index pages will have *Site* replaced with the directory name. The page title will be used as the crumb and link-title for all pages. Force Mode and Config File ========================== If you are in force mode, the config file is also optional. In this case, the current directory is used as the source directory. The output is put into a subdirectory called 'html'. This directory will be created if it doesn't exist, it will also not be scanned for source text files. rest2web-0.5.2~alpha+svn-r248.orig/docs/gallery_test/0000700000175000017500000000000010644674642022150 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/gallery_test/index.txt0000600000175000017500000000036310414273736024016 0ustar madduckmadduckrestindex build: No crumb: Gallery link-title: Example Gallery page-description: Example gallery from the `gallery plugin `_. (Only present if you build it). /description /restindex rest2web-0.5.2~alpha+svn-r248.orig/docs/command_line.txt0000600000175000017500000001261710514020430022621 0ustar madduckmadduckrestindex crumb: Command Line page-description: The different command line options for **rest2web**. This includes passing *uservalues* for use in your pages and templates. /description /restindex ====================== Command Line Options ====================== -------------------------------------------- Configuring Rest2web from the Command Line -------------------------------------------- .. contents:: Introduction ============ Most **rest2web** configuration is done through the config file. It is generally easier to edit a single text file than to have to remember lots of arcane command line options. {sm;:-)} There are several things you can control from the command line. These include : * The `Config File `_ * The verbosity level (how much information rest2web outputs as it runs * Override the template file * Go into force mode * Specify global `uservalues `_ Basic Usage =========== If you type ``r2w.py --help`` at the command line, this is the message it *should* print : :: usage: r2w.py [options] config_file options: --version show program's version number and exit -h, --help show this help message and exit -v Verbose output (default). -a Display warnings & actions only. -w Display warnings only. -t TEMPLATE, --template-file=TEMPLATE Specify a template file. (Overrides first template.) -u USERVALUES, --uservalues=USERVALUES Global uservalues for the site, in the form "name=value". -f, --force Force site without indexes, restindexes or template. -n, --nopause Do not pause after processing (overrides setting in config file). -s, --skiperrors Skip errors (continue processing). .. note:: Command line options are processed using `optparse `_. This only comes with Python 2.3 and more recent. On Python 2.2 you will only be able to pass the config file at the command line. The Config File =============== If you run ``r2w.py`` *without* any command line options, it looks for a file called ``r2w.ini`` in the current working directory. Alternatively you can specify a `config file`_ as the last argument to ``r2w.py``. Verbosity Level =============== By default rest2web outputs a lot of information about what it does. This includes which directories and files it is processing, as well any errors raised along the way. This may be too much information. There are three different verbosity levels : * ``-v`` - Verbose, the default level. Information, actions and errors. * ``-a`` - Action level. Actions and errors only. * ``-w`` - Warning level. Warnings only. Template File ============= rest2web uses template files to generate the output pages. See `Templating `_ for the details of this. Often you will only use a single template, specified in the `restindex `_ of your top level index page. Sometimes you may want to use alternative templates for the same site. For example one for the online version and one for the distributed documentation. You can override the top level template from the command line, using either : ``r2w.py -t path/to/template.txt`` ``r2w.py --template-file=path/to/template.txt`` Uservalues ========== `The Uservalues `_ are a way of providing values to use in your content and templates. They can be useful for multiple translations, or for values that you want to specify at the time you build your site. You can specify uservalues in the restindex of each page. You can also specify *global* uservalues (available in every page) in the config file, or at the command line. If you specify the same values in your config file *and* at the command line, the command line takes higher priority. A uservalue needs a name (this is how you refer to it in pages/templates) and a value. The name and value should be separated by an equals sign. This means that you can specify uservalues at the command line using either : ``r2w.py -u "name=value"`` ``r2w.py --uservalues="name=value"`` The double quotes are optional, but useful if the value has spaces. Uservalues in pages will override global uservalues specified in either the config file or at the command line. Force Mode ========== `force mode `__ allows you to build websites without specifying a template, providing indexes, or having restindexes in the ReST documents that form your pages. You can supply any of these that you want, but where they are missing rest2web will supply defaults. To switch force mode onfrom the command line, use : ``r2w.py -f`` ``r2w.py --force`` No Pause ======== This overrides the 'pause' option in the config file, and forces 'r2w.py' to *not pause* after running. skiperrors ========== This option determines how rest2web will handle exceptions (like our old friend ``UnicodeDecodeError``) that happen when processing pages. If set, rest2web will display the error and move to the next file. The default is off. .. note:: If an error occurs whilst trying to process an index file, it will have to skip building the directory. This feature is still 'experimental' and may need refining. rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/0000700000175000017500000000000010644674642021455 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section4/0000700000175000017500000000000010644674642023205 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section4/notincluded.txt0000600000175000017500000000021210414273736026245 0ustar madduckmadduckrestindex include: no crumb: Not Included /restindex ============== Not Included ============== This page just isn't included. rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section4/empty/0000700000175000017500000000000010644674642024343 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section4/empty/somewhere/0000700000175000017500000000000010644674642026341 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section4/empty/somewhere/index.txt0000600000175000017500000000027010246051200030163 0ustar madduckmadduckrestindex page-title: This is an Orphaned Page crumb: Orphan /restindex ================= A Poor Orphan ================= rest2web will render whole orphaned sections. rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section4/index.txt0000600000175000017500000000010110246051200025020 0ustar madduckmadduckrestindex index-file: ../archive/strangeindex2.txt /restindexrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/archive/0000700000175000017500000000000010644674642023076 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/archive/strangeindex1.txt0000600000175000017500000000744710247534203026414 0ustar madduckmadduckrestindex format: html page-title: Welcome to Section 3 crumb: Thirdy link-title: Come and Explore Section 3 page-description: This section comes directly before the fourth section, and directly after the second section. That makes it section 3. /description sectionlist: topic 1, topic 2, topic 3, Not Real, section-title: , The Default Section section-description: I haven't included any links in the default section, but if you look in the source for this index page (if you can find it) - you will see how to include the default section in the ``sectionlist``, and give it a title and description. This also makes the template more interesting - when it is handling ``sectionlist`` it has to cope with the fact that ``None`` is in it. /description section-description: topic 1 This is a description of the articles in topic 1. /description section-description: topic 2 This is a description of the articles in topic 2. /description section-description: topic 3 This is a description of the articles in topic 3. /description section-description: Not Real This section only contains files that don't really exist. You ought to get **404** errors when you click on the links. They are all pulled in from the file *restindex.txt* and illustrate how you can use **rest2web** to build indexes for content that it isn't actually building. Of course normally you'd set the target to be files that *do really exist*. /description /restindex

Section Number Three

Welcome to something.

These Are More Articles

A feature of this section is that the index page is in a different directory to the content. You probably wouldn't use it like this - but it has possibilities when using rest2web to only build part of a website. This is more of a test that the link logic works.

<# import urllib blank = ' ' row = '' entry = '%s' sectlist = [(urllib.quote(section), section.title()) for section in sectionlist if section is not None] if None in sectionlist: sectlist.append(('Default', 'Default')) index = 0 while index < len(sectlist): entry_1 = entry % sectlist[index] if index == len(sectlist)-1: entry_2 = blank else: entry_2 = entry % sectlist[index+1] print row % (entry_1, entry_2) index += 2 #>
Funky Stuff Hey
%s%s
   
<# indexblock = '''\

%s

%s

    %s
''' pageblock = '''\
  • %s

    %s

  • ''' for section in sectionlist: thepages = [] for page in sections[section]['pages']: a_page = pageblock % (page['target'], page['link-title'], page['page-description']) if type(a_page) is unicode: a_page = a_page.encode('utf8') thepages.append(a_page) thepages = '\n'.join(thepages) if section is None: id = 'default' else: id = urllib.quote(section) sect_title = sections[section]['title'] desc = sections[section]['description'] this_sect = indexblock % ( id, id, sect_title, desc, thepages) print this_sect #>rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/archive/strangeindex2.txt0000600000175000017500000000127110307776104026407 0ustar madduckmadduckrestindex target: ../section4/index.html page-title: This is the Famous Section 4 crumb: Fourth of Course page-description: This really is the Famous Section 4. /description /restindex ========================== An Index in reST Format ========================== If this works properly `this is a link to a hidden section`__. Again, it illustrates a not so useful feature of rest2web. This is the fact that rest2web will continue to render subdirectories, even if the directories above had no content. Not only that, but the 'crumbs' still work. This is a link to a page that isn't included__ in the index. __ empty/somewhere/index.html __ notincluded.htmlrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section3/0000700000175000017500000000000010644674642023204 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section3/article3.txt0000600000175000017500000000062210246051200025426 0ustar madduckmadduckrestindex section: topic 3 /restindex ============= Article 3 ============= This article has nearly the smallest restindex possible ! If it was in the 'default' section [#]_, it would have the smallest one possible. .. [#] The *default section* is where articles without a section specified are put. In your index page template, you access the articles in this section using ``pages[None]``.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section3/index.txt0000600000175000017500000000010110246051200025017 0ustar madduckmadduckrestindex index-file: ../archive/strangeindex1.txt /restindexrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section3/restindex.txt0000600000175000017500000000060510311246051025731 0ustar madduckmadduckrestindex section: Not Real target: not_real_1.html crumb: The First /restindex restindex section: Not Real target: not_real_2.html crumb: The Second /restindex restindex section: Not Real target: somewhere_else/not_real.html crumb: The Third /restindex restindex section: Not Real target: ../not_real_at_all.html crumb: The Last /restindex rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section3/article2.txt0000600000175000017500000000025010246051200025422 0ustar madduckmadduckrestindex link-title: Another Article section: topic 2 /restindex ============= Article 2 ============= This is another article about nothing in particular.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section3/article1.txt0000600000175000017500000000023710246051200025426 0ustar madduckmadduckrestindex link-title: An Article section: topic 1 /restindex ============= An Article ============= This is an article about nothing in particular.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/0000700000175000017500000000000010644674642023202 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/internet_article.txt0000600000175000017500000000064710246051200027260 0ustar madduckmadduckrestindex section: internet link-title: Click here to be whisked away from it all. page-description: A very, very short article on the internet [#]_. .. [#] Testing footnotes in page descriptions. /description crumb: Internet Article /restindex ====================== Internet Article ====================== This is an article about the internet. Or at least it *could be*.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/index.txt0000600000175000017500000000635710246051200025040 0ustar madduckmadduckrestindex format: html page-title: The Science Index Page crumb: Science link-title: Science, Computers, and Technology page-description: This section contains books and articles about the wonderful worlds of Science, Computing, and Technology. /description sectionlist: science, computers, internet section-description: science This is a description of the articles on science. This description is written using **reST**. That means it can include links__. __ http://docutils.sourceforge.net /description section-description: computers This is also a description. It would be easy to extend the range of text markups that *rest2web* supported. If there was any demand we could allow html in the descriptions, and even extend the range of supported markups to include *sextile* and *textile*. This would make the name **rest2web** [#]_ less appropriate of course. .. [#] Obviously reST is my preferred markup... /description section-description: internet This is also a description. /description /restindex

    The Science Index Page

    Welcome to something.

    These Are the Sections

    So enjoy....

    <# import urllib blank = ' ' row = '' entry = '%s' sectlist = [(urllib.quote(section), section.title()) for section in sectionlist if section is not None] # note we use the variable sectionlist - to loop over the sections in the same order they appear # in 'sectionlist' in the restindex. We also exclude ``None`` - the default section. index = 0 while index < len(sectlist): entry_1 = entry % sectlist[index] if index == len(sectlist)-1: entry_2 = blank else: entry_2 = entry % sectlist[index+1] print row % (entry_1, entry_2) index += 2 #>
    The Voidspace Science Sections
    %s%s
       
    <# indexblock = '''\

    %s

    %s

      %s
    ''' pageblock = '''\
  • %s

    %s

  • ''' for section in sections: if section is None: continue thepages = [] for page in sections[section]['pages']: a_page = pageblock % (page['target'], page['link-title'], page['page-description']) if type(a_page) is unicode: a_page = a_page.encode('utf8') thepages.append(a_page) thepages = '\n'.join(thepages) id = urllib.quote(section) sect_title = sections[section]['title'] desc = sections[section]['description'] this_sect = indexblock % ( id, id, sect_title, desc, thepages) print this_sect #>
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/0000700000175000017500000000000010644674642026472 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_part3.txt0000600000175000017500000044777110246051200031251 0ustar madduckmadduckrestindex crumb: Hacker Part 3 /restindex ==================================== The Hacker Crackdown - Part Three ==================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html .. contents :: Part Three - In Parts Part Three: Law and Order ========================== Of the various anti-hacker activities of 1990, "Operation Sundevil" had by far the highest public profile. The sweeping, nationwide computer seizures of May 8, 1990 were unprecedented in scope and highly, if rather selectively, publicized. Unlike the efforts of the Chicago Computer Fraud and Abuse Task Force, "Operation Sundevil" was not intended to combat "hacking" in the sense of computer intrusion or sophisticated raids on telco switching stations. Nor did it have anything to do with hacker misdeeds with AT&T's software, or with Southern Bell's proprietary documents. Instead, "Operation Sundevil" was a crackdown on those traditional scourges of the digital underground: credit-card theft and telephone code abuse. The ambitious activities out of Chicago, and the somewhat lesser-known but vigorous antihacker actions of the New York State Police in 1990, were never a part of "Operation Sundevil" per se, which was based in Arizona. Nevertheless, after the spectacular May 8 raids, the public, misled by police secrecy, hacker panic, and a puzzled national press-corps, conflated all aspects of the nationwide crackdown in 1990 under the blanket term "Operation Sundevil." "Sundevil" is still the best-known synonym for the crackdown of 1990. But the Arizona organizers of "Sundevil" did not really deserve this reputation -- any more, for instance, than all hackers deserve a reputation as "hackers." There was some justice in this confused perception, though. For one thing, the confusion was abetted by the Washington office of the Secret Service, who responded to Freedom of Information Act requests on "Operation Sundevil" by referring investigators to the publicly known cases of Knight Lightning and the Atlanta Three. And "Sundevil" was certainly the largest aspect of the Crackdown, the most deliberate and the best-organized. As a crackdown on electronic fraud, "Sundevil" lacked the frantic pace of the war on the Legion of Doom; on the contrary, Sundevil's targets were picked out with cool deliberation over an elaborate investigation lasting two full years. And once again the targets were bulletin board systems. Boards can be powerful aids to organized fraud. Underground boards carry lively, extensive, detailed, and often quite flagrant "discussions" of lawbreaking techniques and lawbreaking activities. "Discussing" crime in the abstract, or "discussing" the particulars of criminal cases, is not illegal -- but there are stern state and federal laws against coldbloodedly conspiring in groups in order to commit crimes. In the eyes of police, people who actively conspire to break the law are not regarded as "clubs," "debating salons," "users' groups," or "free speech advocates." Rather, such people tend to find themselves formally indicted by prosecutors as "gangs," "racketeers," "corrupt organizations" and "organized crime figures." What's more, the illicit data contained on outlaw boards goes well beyond mere acts of speech and/or possible criminal conspiracy. As we have seen, it was common practice in the digital underground to post purloined telephone codes on boards, for any phreak or hacker who cared to abuse them. Is posting digital booty of this sort supposed to be protected by the First Amendment? Hardly -though the issue, like most issues in cyberspace, is not entirely resolved. Some theorists argue that to merely recite a number publicly is not illegal - only its use is illegal. But anti-hacker police point out that magazines and newspapers (more traditional forms of free expression) never publish stolen telephone codes (even though this might well raise their circulation). Stolen credit card numbers, being riskier and more valuable, were less often publicly posted on boards -- but there is no question that some underground boards carried "carding" traffic, generally exchanged through private mail. Underground boards also carried handy programs for "scanning" telephone codes and raiding credit card companies, as well as the usual obnoxious galaxy of pirated software, cracked passwords, blue-box schematics, intrusion manuals, anarchy files, porn files, and so forth. But besides their nuisance potential for the spread of illicit knowledge, bulletin boards have another vitally interesting aspect for the professional investigator. Bulletin boards are cram-full of evidence. All that busy trading of electronic mail, all those hacker boasts, brags and struts, even the stolen codes and cards, can be neat, electronic, realtime recordings of criminal activity. As an investigator, when you seize a pirate board, you have scored a coup as effective as tapping phones or intercepting mail. However, you have not actually tapped a phone or intercepted a letter. The rules of evidence regarding phone-taps and mail interceptions are old, stern and wellunderstood by police, prosecutors and defense attorneys alike. The rules of evidence regarding boards are new, waffling, and understood by nobody at all. Sundevil was the largest crackdown on boards in world history. On May 7, 8, and 9, 1990, about fortytwo computer systems were seized. Of those forty- two computers, about twenty-five actually were running boards. (The vagueness of this estimate is attributable to the vagueness of (a) what a "computer system" is, and (b) what it actually means to "run a board" with one -- or with two computers, or with three.) About twenty-five boards vanished into police custody in May 1990. As we have seen, there are an estimated 30,000 boards in America today. If we assume that one board in a hundred is up to no good with codes and cards (which rather flatters the honesty of the board-using community), then that would leave 2,975 outlaw boards untouched by Sundevil. Sundevil seized about one tenth of one percent of all computer bulletin boards in America. Seen objectively, this is something less than a comprehensive assault. In 1990, Sundevil's organizers -- the team at the Phoenix Secret Service office, and the Arizona Attorney General's office -had a list of at least three hundred boards that they considered fully deserving of search and seizure warrants. The twenty- five boards actually seized were merely among the most obvious and egregious of this much larger list of candidates. All these boards had been examined beforehand -- either by informants, who had passed printouts to the Secret Service, or by Secret Service agents themselves, who not only come equipped with modems but know how to use them. There were a number of motives for Sundevil. First, it offered a chance to get ahead of the curve on wire-fraud crimes. Tracking back credit-card ripoffs to their perpetrators can be appallingly difficult. If these miscreants have any kind of electronic sophistication, they can snarl their tracks through the phone network into a mind-boggling, untraceable mess, while still managing to "reach out and rob someone." Boards, however, full of brags and boasts, codes and cards, offer evidence in the handy congealed form. Seizures themselves -- the mere physical removal of machines -- tends to take the pressure off. During Sundevil, a large number of code kids, warez d00dz, and credit card thieves would be deprived of those boards -- their means of community and conspiracy -- in one swift blow. As for the sysops themselves (commonly among the boldest offenders) they would be directly stripped of their computer equipment, and rendered digitally mute and blind. And this aspect of Sundevil was carried out with great success. Sundevil seems to have been a complete tactical surprise -- unlike the fragmentary and continuing seizures of the war on the Legion of Doom, Sundevil was precisely timed and utterly overwhelming. At least forty "computers" were seized during May 7, 8 and 9, 1990, in Cincinnati, Detroit, Los Angeles, Miami, Newark, Phoenix, Tucson, Richmond, San Diego, San Jose, Pittsburgh and San Francisco. Some cities saw multiple raids, such as the five separate raids in the New York City environs. Plano, Texas (essentially a suburb of the Dallas/Fort Worth metroplex, and a hub of the telecommunications industry) saw four computer seizures. Chicago, ever in the forefront, saw its own local Sundevil raid, briskly carried out by Secret Service agents Timothy Foley and Barbara Golden. Many of these raids occurred, not in the cities proper, but in associated white-middle class suburbs -- places like Mount Lebanon, Pennsylvania and Clark Lake, Michigan. There were a few raids on offices; most took place in people's homes, the classic hacker basements and bedrooms. The Sundevil raids were searches and seizures, not a group of mass arrests. There were only four arrests during Sundevil. "Tony the Trashman," a longtime teenage bete noire of the Arizona Racketeering unit, was arrested in Tucson on May 9. "Dr. Ripco," sysop of an outlaw board with the misfortune to exist in Chicago itself, was also arrested -- on illegal weapons charges. Local units also arrested a 19-year-old female phone phreak named "Electra" in Pennsylvania, and a male juvenile in California. Federal agents however were not seeking arrests, but computers. Hackers are generally not indicted (if at all) until the evidence in their seized computers is evaluated -- a process that can take weeks, months -even years. When hackers are arrested on the spot, it's generally an arrest for other reasons. Drugs and/or illegal weapons show up in a good third of anti-hacker computer seizures (though not during Sundevil). That scofflaw teenage hackers (or their parents) should have marijuana in their homes is probably not a shocking revelation, but the surprisingly common presence of illegal firearms in hacker dens is a bit disquieting. A Personal Computer can be a great equalizer for the techno-cowboy -- much like that more traditional American "Great Equalizer," the Personal Sixgun. Maybe it's not all that surprising that some guy obsessed with power through illicit technology would also have a few illicit high-velocity-impact devices around. An element of the digital underground particularly dotes on those "anarchy philes," and this element tends to shade into the crackpot milieu of survivalists, gun-nuts, anarcho-leftists and the ultra- libertarian right-wing. This is not to say that hacker raids to date have uncovered any major crack-dens or illegal arsenals; but Secret Service agents do not regard "hackers" as "just kids." They regard hackers as unpredictable people, bright and slippery. It doesn't help matters that the hacker himself has been "hiding behind his keyboard" all this time. Commonly, police have no idea what he looks like. This makes him an unknown quantity, someone best treated with proper caution. To date, no hacker has come out shooting, though they do sometimes brag on boards that they will do just that. Threats of this sort are taken seriously. Secret Service hacker raids tend to be swift, comprehensive, well-manned (even overmanned); and agents generally burst through every door in the home at once, sometimes with drawn guns. Any potential resistance is swiftly quelled. Hacker raids are usually raids on people's homes. It can be a very dangerous business to raid an American home; people can panic when strangers invade their sanctum. Statistically speaking, the most dangerous thing a policeman can do is to enter someone's home. (The second most dangerous thing is to stop a car in traffic.) People have guns in their homes. More cops are hurt in homes than are ever hurt in biker bars or massage parlors. But in any case, no one was hurt during Sundevil, or indeed during any part of the Hacker Crackdown. Nor were there any allegations of any physical mistreatment of a suspect. Guns were pointed, interrogations were sharp and prolonged; but no one in 1990 claimed any act of brutality by any crackdown raider. In addition to the forty or so computers, Sundevil reaped floppy disks in particularly great abundance -- an estimated 23,000 of them, which naturally included every manner of illegitimate data: pirated games, stolen codes, hot credit card numbers, the complete text and software of entire pirate bulletin- boards. These floppy disks, which remain in police custody today, offer a gigantic, almost embarrassingly rich source of possible criminal indictments. These 23,000 floppy disks also include a thus-far unknown quantity of legitimate computer games, legitimate software, purportedly "private" mail from boards, business records, and personal correspondence of all kinds. Standard computer-crime search warrants lay great emphasis on seizing written documents as well as computers -- specifically including photocopies, computer printouts, telephone bills, address books, logs, notes, memoranda and correspondence. In practice, this has meant that diaries, gaming magazines, software documentation, nonfiction books on hacking and computer security, sometimes even science fiction novels, have all vanished out the door in police custody. A wide variety of electronic items have been known to vanish as well, including telephones, televisions, answering machines, Sony Walkmans, desktop printers, compact disks, and audiotapes. No fewer than 150 members of the Secret Service were sent into the field during Sundevil. They were commonly accompanied by squads of local and/or state police. Most of these officers -especially the locals -- had never been on an antihacker raid before. (This was one good reason, in fact, why so many of them were invited along in the first place.) Also, the presence of a uniformed police officer assures the raidees that the people entering their homes are, in fact, police. Secret Service agents wear plain clothes. So do the telco security experts who commonly accompany the Secret Service on raids (and who make no particular effort to identify themselves as mere employees of telephone companies). A typical hacker raid goes something like this. First, police storm in rapidly, through every entrance, with overwhelming force, in the assumption that this tactic will keep casualties to a minimum. Second, possible suspects are immediately removed from the vicinity of any and all computer systems, so that they will have no chance to purge or destroy computer evidence. Suspects are herded into a room without computers, commonly the living room, and kept under guard -not armed guard, for the guns are swiftly holstered, but under guard nevertheless. They are presented with the search warrant and warned that anything they say may be held against them. Commonly they have a great deal to say, especially if they are unsuspecting parents. Somewhere in the house is the "hot spot" -- a computer tied to a phone line (possibly several computers and several phones). Commonly it's a teenager's bedroom, but it can be anywhere in the house; there may be several such rooms. This "hot spot" is put in charge of a two-agent team, the "finder" and the "recorder." The "finder" is computer-trained, commonly the case agent who has actually obtained the search warrant from a judge. He or she understands what is being sought, and actually carries out the seizures: unplugs machines, opens drawers, desks, files, floppy-disk containers, etc. The "recorder" photographs all the equipment, just as it stands -- especially the tangle of wired connections in the back, which can otherwise be a real nightmare to restore. The recorder will also commonly photograph every room in the house, lest some wily criminal claim that the police had robbed him during the search. Some recorders carry videocams or tape recorders; however, it's more common for the recorder to simply take written notes. Objects are described and numbered as the finder seizes them, generally on standard preprinted police inventory forms. Even Secret Service agents were not, and are not, expert computer users. They have not made, and do not make, judgements on the fly about potential threats posed by various forms of equipment. They may exercise discretion; they may leave Dad his computer, for instance, but they don't have to. Standard computer-crime search warrants, which date back to the early 80s, use a sweeping language that targets computers, most anything attached to a computer, most anything used to operate a computer -- most anything that remotely resembles a computer -- plus most any and all written documents surrounding it. Computer-crime investigators have strongly urged agents to seize the works. In this sense, Operation Sundevil appears to have been a complete success. Boards went down all over America, and were shipped en masse to the computer investigation lab of the Secret Service, in Washington DC, along with the 23,000 floppy disks and unknown quantities of printed material. But the seizure of twenty-five boards, and the multi-megabyte mountains of possibly useful evidence contained in these boards (and in their owners' other computers, also out the door), were far from the only motives for Operation Sundevil. An unprecedented action of great ambition and size, Sundevil's motives can only be described as political. It was a public-relations effort, meant to pass certain messages, meant to make certain situations clear: both in the mind of the general public, and in the minds of various constituencies of the electronic community. First -- and this motivation was vital -- a "message" would be sent from law enforcement to the digital underground. This very message was recited in so many words by Garry M. Jenkins, the Assistant Director of the US Secret Service, at the Sundevil press conference in Phoenix on May 9, 1990, immediately after the raids. In brief, hackers were mistaken in their foolish belief that they could hide behind the "relative anonymity of their computer terminals." On the contrary, they should fully understand that state and federal cops were actively patrolling the beat in cyberspace -- that they were on the watch everywhere, even in those sleazy and secretive dens of cybernetic vice, the underground boards. This is not an unusual message for police to publicly convey to crooks. The message is a standard message; only the context is new. In this respect, the Sundevil raids were the digital equivalent of the standard vice-squad crackdown on massage parlors, porno bookstores, head-shops, or floating crap-games. There may be few or no arrests in a raid of this sort; no convictions, no trials, no interrogations. In cases of this sort, police may well walk out the door with many pounds of sleazy magazines, X-rated videotapes, sex toys, gambling equipment, baggies of marijuana.... Of course, if something truly horrendous is discovered by the raiders, there will be arrests and prosecutions. Far more likely, however, there will simply be a brief but sharp disruption of the closed and secretive world of the nogoodniks. There will be "street hassle." "Heat." "Deterrence." And, of course, the immediate loss of the seized goods. It is very unlikely that any of this seized material will ever be returned. Whether charged or not, whether convicted or not, the perpetrators will almost surely lack the nerve ever to ask for this stuff to be given back. Arrests and trials -- putting people in jail -- may involve all kinds of formal legalities; but dealing with the justice system is far from the only task of police. Police do not simply arrest people. They don't simply put people in jail. That is not how the police perceive their jobs. Police "protect and serve." Police "keep the peace," they "keep public order." Like other forms of public relations, keeping public order is not an exact science. Keeping public order is something of an art-form. If a group of tough-looking teenage hoodlums was loitering on a street- corner, no one would be surprised to see a street-cop arrive and sternly order them to "break it up." On the contrary, the surprise would come if one of these ne'er-do-wells stepped briskly into a phone-booth, called a civil rights lawyer, and instituted a civil suit in defense of his Constitutional rights of free speech and free assembly. But something much along this line was one of the many anomolous outcomes of the Hacker Crackdown. Sundevil also carried useful "messages" for other constituents of the electronic community. These messages may not have been read aloud from the Phoenix podium in front of the press corps, but there was little mistaking their meaning. There was a message of reassurance for the primary victims of coding and carding: the telcos, and the credit companies. Sundevil was greeted with joy by the security officers of the electronic business community. After years of high-tech harassment and spiralling revenue losses, their complaints of rampant outlawry were being taken seriously by law enforcement. No more head- scratching or dismissive shrugs; no more feeble excuses about "lack of computer-trained officers" or the low priority of "victimless" white-collar telecommunication crimes. Computer-crime experts have long believed that computer-related offenses are drastically under-reported. They regard this as a major open scandal of their field. Some victims are reluctant to come forth, because they believe that police and prosecutors are not computer-literate, and can and will do nothing. Others are embarrassed by their vulnerabilities, and will take strong measures to avoid any publicity; this is especially true of banks, who fear a loss of investor confidence should an embezzlement-case or wire-fraud surface. And some victims are so helplessly confused by their own high technology that they never even realize that a crime has occurred -- even when they have been fleeced to the bone. The results of this situation can be dire. Criminals escape apprehension and punishment. The computer-crime units that do exist, can't get work. The true scope of computer-crime: its size, its real nature, the scope of its threats, and the legal remedies for it -- all remain obscured. Another problem is very little publicized, but it is a cause of genuine concern. Where there is persistent crime, but no effective police protection, then vigilantism can result. Telcos, banks, credit companies, the major corporations who maintain extensive computer networks vulnerable to hacking -- these organizations are powerful, wealthy, and politically influential. They are disinclined to be pushed around by crooks (or by most anyone else, for that matter). They often maintain well- organized private security forces, commonly run by experienced veterans of military and police units, who have left public service for the greener pastures of the private sector. For police, the corporate security manager can be a powerful ally; but if this gentleman finds no allies in the police, and the pressure is on from his board-of-directors, he may quietly take certain matters into his own hands. Nor is there any lack of disposable hired-help in the corporate security business. Private security agencies -- the 'security business' generally -- grew explosively in the 1980s. Today there are spooky gumshoed armies of "security consultants," "rent-acops," "private eyes," "outside experts" -- every manner of shady operator who retails in "results" and discretion. Or course, many of these gentlemen and ladies may be paragons of professional and moral rectitude. But as anyone who has read a hard-boiled detective novel knows, police tend to be less than fond of this sort of private-sector competition. Companies in search of computer-security have even been known to hire hackers. Police shudder at this prospect. Police treasure good relations with the business community. Rarely will you see a policeman so indiscreet as to allege publicly that some major employer in his state or city has succumbed to paranoia and gone off the rails. Nevertheless, police -- and computer police in particular -- are aware of this possibility. Computer-crime police can and do spend up to half of their business hours just doing public relations: seminars, "dog and pony shows," sometimes with parents' groups or computer users, but generally with their core audience: the likely victims of hacking crimes. These, of course, are telcos, credit card companies and large computerequipped corporations. The police strongly urge these people, as good citizens, to report offenses and press criminal charges; they pass the message that there is someone in authority who cares, understands, and, best of all, will take useful action should a computer-crime occur. But reassuring talk is cheap. Sundevil offered action. The final message of Sundevil was intended for internal consumption by law enforcement. Sundevil was offered as proof that the community of American computer-crime police had come of age. Sundevil was proof that enormous things like Sundevil itself could now be accomplished. Sundevil was proof that the Secret Service and its local law-enforcement allies could act like a welloiled machine -- (despite the hampering use of those scrambled phones). It was also proof that the Arizona Organized Crime and Racketeering Unit -the sparkplug of Sundevil -- ranked with the best in the world in ambition, organization, and sheer conceptual daring. And, as a final fillip, Sundevil was a message from the Secret Service to their longtime rivals in the Federal Bureau of Investigation. By Congressional fiat, both USSS and FBI formally share jurisdiction over federal computer- crimebusting activities. Neither of these groups has ever been remotely happy with this muddled situation. It seems to suggest that Congress cannot make up its mind as to which of these groups is better qualified. And there is scarcely a G-man or a Special Agent anywhere without a very firm opinion on that topic. 1. === For the neophyte, one of the most puzzling aspects of the crackdown on hackers is why the United States Secret Service has anything at all to do with this matter. The Secret Service is best known for its primary public role: its agents protect the President of the United States. They also guard the President's family, the Vice President and his family, former Presidents, and Presidential candidates. They sometimes guard foreign dignitaries who are visiting the United States, especially foreign heads of state, and have been known to accompany American officials on diplomatic missions overseas. Special Agents of the Secret Service don't wear uniforms, but the Secret Service also has two uniformed police agencies. There's the former White House Police (now known as the Secret Service Uniformed Division, since they currently guard foreign embassies in Washington, as well as the White House itself). And there's the uniformed Treasury Police Force. The Secret Service has been charged by Congress with a number of little- known duties. They guard the precious metals in Treasury vaults. They guard the most valuable historical documents of the United States: originals of the Constitution, the Declaration of Independence, Lincoln's Second Inaugural Address, an American-owned copy of the Magna Carta, and so forth. Once they were assigned to guard the Mona Lisa, on her American tour in the 1960s. The entire Secret Service is a division of the Treasury Department. Secret Service Special Agents (there are about 1,900 of them) are bodyguards for the President et al, but they all work for the Treasury. And the Treasury (through its divisions of the U.S. Mint and the Bureau of Engraving and Printing) prints the nation's money. As Treasury police, the Secret Service guards the nation's currency; it is the only federal law enforcement agency with direct jurisdiction over counterfeiting and forgery. It analyzes documents for authenticity, and its fight against fake cash is still quite lively (especially since the skilled counterfeiters of Medellin, Columbia have gotten into the act). Government checks, bonds, and other obligations, which exist in untold millions and are worth untold billions, are common targets for forgery, which the Secret Service also battles. It even handles forgery of postage stamps. But cash is fading in importance today as money has become electronic. As necessity beckoned, the Secret Service moved from fighting the counterfeiting of paper currency and the forging of checks, to the protection of funds transferred by wire. From wire-fraud, it was a simple skip-and-jump to what is formally known as "access device fraud." Congress granted the Secret Service the authority to investigate "access device fraud" under Title 18 of the United States Code (U.S.C. Section 1029). The term "access device" seems intuitively simple. It's some kind of high-tech gizmo you use to get money with. It makes good sense to put this sort of thing in the charge of counterfeiting and wirefraud experts. However, in Section 1029, the term "access device" is very generously defined. An access device is: "any card, plate, code, account number, or other means of account access that can be used, alone or in conjunction with another access device, to obtain money, goods, services, or any other thing of value, or that can be used to initiate a transfer of funds." "Access device" can therefore be construed to include credit cards themselves (a popular forgery item nowadays). It also includes credit card account numbers, those standards of the digital underground. The same goes for telephone charge cards (an increasingly popular item with telcos, who are tired of being robbed of pocket change by phone-booth thieves). And also telephone access codes, those other standards of the digital underground. (Stolen telephone codes may not "obtain money," but they certainly do obtain valuable "services," which is specifically forbidden by Section 1029.) We can now see that Section 1029 already pits the United States Secret Service directly against the digital underground, without any mention at all of the word "computer." Standard phreaking devices, like "blue boxes," used to steal phone service from old-fashioned mechanical switches, are unquestionably "counterfeit access devices." Thanks to Sec.1029, it is not only illegal to use counterfeit access devices, but it is even illegal to build them. "Producing," "designing" "duplicating" or "assembling" blue boxes are all federal crimes today, and if you do this, the Secret Service has been charged by Congress to come after you. Automatic Teller Machines, which replicated all over America during the 1980s, are definitely "access devices," too, and an attempt to tamper with their punch-in codes and plastic bank cards falls directly under Sec. 1029. Section 1029 is remarkably elastic. Suppose you find a computer password in somebody's trash. That password might be a "code" -- it's certainly a "means of account access." Now suppose you log on to a computer and copy some software for yourself. You've certainly obtained "service" (computer service) and a "thing of value" (the software). Suppose you tell a dozen friends about your swiped password, and let them use it, too. Now you're "trafficking in unauthorized access devices." And when the Prophet, a member of the Legion of Doom, passed a stolen telephone company document to Knight Lightning at *Phrack* magazine, they were both charged under Sec. 1029! There are two limitations on Section 1029. First, the offense must "affect interstate or foreign commerce" in order to become a matter of federal jurisdiction. The term "affecting commerce" is not well defined; but you may take it as a given that the Secret Service can take an interest if you've done most anything that happens to cross a state line. State and local police can be touchy about their jurisdictions, and can sometimes be mulish when the feds show up. But when it comes to computercrime, the local police are pathetically grateful for federal help -- in fact they complain that they can't get enough of it. If you're stealing long-distance service, you're almost certainly crossing state lines, and you're definitely "affecting the interstate commerce" of the telcos. And if you're abusing credit cards by ordering stuff out of glossy catalogs from, say, Vermont, you're in for it. The second limitation is money. As a rule, the feds don't pursue penny-ante offenders. Federal judges will dismiss cases that appear to waste their time. Federal crimes must be serious; Section 1029 specifies a minimum loss of a thousand dollars. We now come to the very next section of Title 18, which is Section 1030, "Fraud and related activity in connection with computers." This statute gives the Secret Service direct jurisdiction over acts of computer intrusion. On the face of it, the Secret Service would now seem to command the field. Section 1030, however, is nowhere near so ductile as Section 1029. The first annoyance is Section 1030(d), which reads: "(d) The United States Secret Service shall, in addition to *any other agency having such authority*, have the authority to investigate offenses under this section. Such authority of the United States Secret Service shall be exercised in accordance with an agreement which shall be entered into by the Secretary of the Treasury *and the Attorney General*." (Author's italics.) The Secretary of the Treasury is the titular head of the Secret Service, while the Attorney General is in charge of the FBI. In Section (d), Congress shrugged off responsibility for the computer-crime turf-battle between the Service and the Bureau, and made them fight it out all by themselves. The result was a rather dire one for the Secret Service, for the FBI ended up with exclusive jurisdiction over computer break-ins having to do with national security, foreign espionage, federally insured banks, and U.S. military bases, while retaining joint jurisdiction over all the other computer intrusions. Essentially, when it comes to Section 1030, the FBI not only gets the real glamor stuff for itself, but can peer over the shoulder of the Secret Service and barge in to meddle whenever it suits them. The second problem has to do with the dicey term "Federal interest computer." Section 1030(a)(2) makes it illegal to "access a computer without authorization" if that computer belongs to a financial institution or an issuer of credit cards (fraud cases, in other words). Congress was quite willing to give the Secret Service jurisdiction over money-transferring computers, but Congress balked at letting them investigate any and all computer intrusions. Instead, the USSS had to settle for the money machines and the "Federal interest computers." A "Federal interest computer" is a computer which the government itself owns, or is using. Large networks of interstate computers, linked over state lines, are also considered to be of "Federal interest." (This notion of "Federal interest" is legally rather foggy and has never been clearly defined in the courts. The Secret Service has never yet had its hand slapped for investigating computer break-ins that were not of "Federal interest," but conceivably someday this might happen.) So the Secret Service's authority over "unauthorized access" to computers covers a lot of territory, but by no means the whole ball of cyberspatial wax. If you are, for instance, a local computer retailer, or the owner of a local bulletin board system, then a malicious local intruder can break in, crash your system, trash your files and scatter viruses, and the U.S. Secret Service cannot do a single thing about it. At least, it can't do anything directly. But the Secret Service will do plenty to help the local people who can. The FBI may have dealt itself an ace off the bottom of the deck when it comes to Section 1030; but that's not the whole story; that's not the street. What's Congress thinks is one thing, and Congress has been known to change its mind. The real turfstruggle is out there in the streets where it's happening. If you're a local street-cop with a computer problem, the Secret Service wants you to know where you can find the real expertise. While the Bureau crowd are off having their favorite shoes polished -- (wing-tips) -- and making derisive fun of the Service's favorite shoes -- ("pansy-ass tassels") -the tassel-toting Secret Service has a crew of readyand-able hacker-trackers installed in the capital of every state in the Union. Need advice? They'll give you advice, or at least point you in the right direction. Need training? They can see to that, too. If you're a local cop and you call in the FBI, the FBI (as is widely and slanderously rumored) will order you around like a coolie, take all the credit for your busts, and mop up every possible scrap of reflected glory. The Secret Service, on the other hand, doesn't brag a lot. They're the quiet types. Very quiet. Very cool. Efficient. High-tech. Mirrorshades, icy stares, radio ear- plugs, an Uzi machine-pistol tucked somewhere in that well-cut jacket. American samurai, sworn to give their lives to protect our President. "The granite agents." Trained in martial arts, absolutely fearless. Every single one of 'em has a top-secret security clearance. Something goes a little wrong, you're not gonna hear any whining and moaning and political buck- passing out of these guys. The facade of the granite agent is not, of course, the reality. Secret Service agents are human beings. And the real glory in Service work is not in battling computer crime -- not yet, anyway -- but in protecting the President. The real glamour of Secret Service work is in the White House Detail. If you're at the President's side, then the kids and the wife see you on television; you rub shoulders with the most powerful people in the world. That's the real heart of Service work, the number one priority. More than one computer investigation has stopped dead in the water when Service agents vanished at the President's need. There's romance in the work of the Service. The intimate access to circles of great power; the espritde-corps of a highly trained and disciplined elite; the high responsibility of defending the Chief Executive; the fulfillment of a patriotic duty. And as police work goes, the pay's not bad. But there's squalor in Service work, too. You may get spat upon by protesters howling abuse -- and if they get violent, if they get too close, sometimes you have to knock one of them down -- discreetly. The real squalor in Service work is drudgery such as "the quarterlies," traipsing out four times a year, year in, year out, to interview the various pathetic wretches, many of them in prisons and asylums, who have seen fit to threaten the President's life. And then there's the grinding stress of searching all those faces in the endless bustling crowds, looking for hatred, looking for psychosis, looking for the tight, nervous face of an Arthur Bremer, a Squeaky Fromme, a Lee Harvey Oswald. It's watching all those grasping, waving hands for sudden movements, while your ears strain at your radio headphone for the long- rehearsed cry of "Gun!" It's poring, in grinding detail, over the biographies of every rotten loser who ever shot at a President. It's the unsung work of the Protective Research Section, who study scrawled, anonymous death threats with all the meticulous tools of antiforgery techniques. And it's maintaining the hefty computerized files on anyone who ever threatened the President's life. Civil libertarians have become increasingly concerned at the Government's use of computer files to track American citizens - - but the Secret Service file of potential Presidential assassins, which has upward of twenty thousand names, rarely causes a peep of protest. If you ever state that you intend to kill the President, the Secret Service will want to know and record who you are, where you are, what you are, and what you're up to. If you're a serious threat -- if you're officially considered "of protective interest" -- then the Secret Service may well keep tabs on you for the rest of your natural life. Protecting the President has first call on all the Service's resources. But there's a lot more to the Service's traditions and history than standing guard outside the Oval Office. The Secret Service is the nation's oldest general federal law-enforcement agency. Compared to the Secret Service, the FBI are new- hires and the CIA are temps. The Secret Service was founded 'way back in 1865, at the suggestion of Hugh McCulloch, Abraham Lincoln's Secretary of the Treasury. McCulloch wanted a specialized Treasury police to combat counterfeiting. Abraham Lincoln agreed that this seemed a good idea, and, with a terrible irony, Abraham Lincoln was shot that very night by John Wilkes Booth. The Secret Service originally had nothing to do with protecting Presidents. They didn't take this on as a regular assignment until after the Garfield assassination in 1881. And they didn't get any Congressional money for it until President McKinley was shot in 1901. The Service was originally designed for one purpose: destroying counterfeiters. 2. === There are interesting parallels between the Service's nineteenth-century entry into counterfeiting, and America's twentieth- century entry into computer-crime. In 1865, America's paper currency was a terrible muddle. Security was drastically bad. Currency was printed on the spot by local banks in literally hundreds of different designs. No one really knew what the heck a dollar bill was supposed to look like. Bogus bills passed easily. If some joker told you that a one-dollar bill from the Railroad Bank of Lowell, Massachusetts had a woman leaning on a shield, with a locomotive, a cornucopia, a compass, various agricultural implements, a railroad bridge, and some factories, then you pretty much had to take his word for it. (And in fact he was telling the truth!) Sixteen hundred local American banks designed and printed their own paper currency, and there were no general standards for security. Like a badly guarded node in a computer network, badly designed bills were easy to fake, and posed a security hazard for the entire monetary system. No one knew the exact extent of the threat to the currency. There were panicked estimates that as much as a third of the entire national currency was faked. Counterfeiters -- known as "boodlers" in the underground slang of the time -- were mostly technically skilled printers who had gone to the bad. Many had once worked printing legitimate currency. Boodlers operated in rings and gangs. Technical experts engraved the bogus plates -- commonly in basements in New York City. Smooth confidence men passed large wads of high-quality, highdenomination fakes, including the really sophisticated stuff -- government bonds, stock certificates, and railway shares. Cheaper, botched fakes were sold or sharewared to low-level gangs of boodler wannabes. (The really cheesy lowlife boodlers merely upgraded real bills by altering face values, changing ones to fives, tens to hundreds, and so on.) The techniques of boodling were little-known and regarded with a certain awe by the midnineteenth-century public. The ability to manipulate the system for rip-off seemed diabolically clever. As the skill and daring of the boodlers increased, the situation became intolerable. The federal government stepped in, and began offering its own federal currency, which was printed in fancy green ink, but only on the back - the original "greenbacks." And at first, the improved security of the well- designed, well-printed federal greenbacks seemed to solve the problem; but then the counterfeiters caught on. Within a few years things were worse than ever: a centralized system where all security was bad! The local police were helpless. The Government tried offering blood money to potential informants, but this met with little success. Banks, plagued by boodling, gave up hope of police help and hired private security men instead. Merchants and bankers queued up by the thousands to buy privately-printed manuals on currency security, slim little books like Laban Heath's *Infallible Government Counterfeit Detector*. The back of the book offered Laban Heath's patent microscope for five bucks. Then the Secret Service entered the picture. The first agents were a rough and ready crew. Their chief was one William P. Wood, a former guerilla in the Mexican War who'd won a reputation busting contractor fraudsters for the War Department during the Civil War. Wood, who was also Keeper of the Capital Prison, had a sideline as a counterfeiting expert, bagging boodlers for the federal bounty money. Wood was named Chief of the new Secret Service in July 1865. There were only ten Secret Service agents in all: Wood himself, a handful who'd worked for him in the War Department, and a few former private investigators -- counterfeiting experts -- whom Wood had won over to public service. (The Secret Service of 1865 was much the size of the Chicago Computer Fraud Task Force or the Arizona Racketeering Unit of 1990.) These ten "Operatives" had an additional twenty or so "Assistant Operatives" and "Informants." Besides salary and per diem, each Secret Service employee received a whopping twenty-five dollars for each boodler he captured. Wood himself publicly estimated that at least half of America's currency was counterfeit, a perhaps pardonable perception. Within a year the Secret Service had arrested over 200 counterfeiters. They busted about two hundred boodlers a year for four years straight. Wood attributed his success to travelling fast and light, hitting the bad- guys hard, and avoiding bureaucratic baggage. "Because my raids were made without military escort and I did not ask the assistance of state officers, I surprised the professional counterfeiter." Wood's social message to the once-impudent boodlers bore an eerie ring of Sundevil: "It was also my purpose to convince such characters that it would no longer be healthy for them to ply their vocation without being handled roughly, a fact they soon discovered." William P. Wood, the Secret Service's guerilla pioneer, did not end well. He succumbed to the lure of aiming for the really big score. The notorious Brockway Gang of New York City, headed by William E. Brockway, the "King of the Counterfeiters," had forged a number of government bonds. They'd passed these brilliant fakes on the prestigious Wall Street investment firm of Jay Cooke and Company. The Cooke firm were frantic and offered a huge reward for the forgers' plates. Laboring diligently, Wood confiscated the plates (though not Mr. Brockway) and claimed the reward. But the Cooke company treacherously reneged. Wood got involved in a down-and-dirty lawsuit with the Cooke capitalists. Wood's boss, Secretary of the Treasury McCulloch, felt that Wood's demands for money and glory were unseemly, and even when the reward money finally came through, McCulloch refused to pay Wood anything. Wood found himself mired in a seemingly endless round of federal suits and Congressional lobbying. Wood never got his money. And he lost his job to boot. He resigned in 1869. Wood's agents suffered, too. On May 12, 1869, the second Chief of the Secret Service took over, and almost immediately fired most of Wood's pioneer Secret Service agents: Operatives, Assistants and Informants alike. The practice of receiving $25 per crook was abolished. And the Secret Service began the long, uncertain process of thorough professionalization. Wood ended badly. He must have felt stabbed in the back. In fact his entire organization was mangled. On the other hand, William P. Wood was the first head of the Secret Service. William Wood was the pioneer. People still honor his name. Who remembers the name of the second head of the Secret Service? As for William Brockway (also known as "Colonel Spencer"), he was finally arrested by the Secret Service in 1880. He did five years in prison, got out, and was still boodling at the age of seventyfour. 3. === Anyone with an interest in Operation Sundevil - or in American computer-crime generally -- could scarcely miss the presence of Gail Thackeray, Assistant Attorney General of the State of Arizona. Computer-crime training manuals often cited Thackeray's group and her work; she was the highest- ranking state official to specialize in computer-related offenses. Her name had been on the Sundevil press release (though modestly ranked well after the local federal prosecuting attorney and the head of the Phoenix Secret Service office). As public commentary, and controversy, began to mount about the Hacker Crackdown, this Arizonan state official began to take a higher and higher public profile. Though uttering almost nothing specific about the Sundevil operation itself, she coined some of the most striking soundbites of the growing propaganda war: "Agents are operating in good faith, and I don't think you can say that for the hacker community," was one. Another was the memorable "I am not a mad dog prosecutor" (*Houston Chronicle*, Sept 2, 1990.) In the meantime, the Secret Service maintained its usual extreme discretion; the Chicago Unit, smarting from the backlash of the Steve Jackson scandal, had gone completely to earth. As I collated my growing pile of newspaper clippings, Gail Thackeray ranked as a comparative fount of public knowledge on police operations. I decided that I had to get to know Gail Thackeray. I wrote to her at the Arizona Attorney General's Office. Not only did she kindly reply to me, but, to my astonishment, she knew very well what "cyberpunk" science fiction was. Shortly after this, Gail Thackeray lost her job. And I temporarily misplaced my own career as a science-fiction writer, to become a full-time computer-crime journalist. In early March, 1991, I flew to Phoenix, Arizona, to interview Gail Thackeray for my book on the hacker crackdown. 4. === "Credit cards didn't used to cost anything to get," says Gail Thackeray. "Now they cost forty bucks -- and that's all just to cover the costs from rip-off artists." Electronic nuisance criminals are parasites. One by one they're not much harm, no big deal. But they never come just one by one. They come in swarms, heaps, legions, sometimes whole subcultures. And they bite. Every time we buy a credit card today, we lose a little financial vitality to a particular species of bloodsucker. What, in her expert opinion, are the worst forms of electronic crime, I ask, consulting my notes. Is it -credit card fraud? Breaking into ATM bank machines? Phone-phreaking? Computer intrusions? Software viruses? Access-code theft? Records tampering? Software piracy? Pornographic bulletin boards? Satellite TV piracy? Theft of cable service? It's a long list. By the time I reach the end of it I feel rather depressed. "Oh no," says Gail Thackeray, leaning forward over the table, her whole body gone stiff with energetic indignation, "the biggest damage is telephone fraud. Fake sweepstakes, fake charities. Boiler-room con operations. You could pay off the national debt with what these guys steal.... They target old people, they get hold of credit ratings and demographics, they rip off the old and the weak." The words come tumbling out of her. It's low-tech stuff, your everyday boiler-room fraud. Grifters, conning people out of money over the phone, have been around for decades. This is where the word "phony" came from! It's just that it's so much easier now, horribly facilitated by advances in technology and the byzantine structure of the modern phone system. The same professional fraudsters do it over and over, Thackeray tells me, they hide behind dense onion-shells of fake companies.... fake holding corporations nine or ten layers deep, registered all over the map. They get a phone installed under a false name in an empty safe-house. And then they call-forward everything out of that phone to yet another phone, a phone that may even be in another state. And they don't even pay the charges on their phones; after a month or so, they just split. Set up somewhere else in another Podunkville with the same seedy crew of veteran phone-crooks. They buy or steal commercial credit card reports, slap them on the PC, have a program pick out people over sixty-five who pay a lot to charities. A whole subculture living off this, merciless folks on the con. "The 'light-bulbs for the blind' people," Thackeray muses, with a special loathing. "There's just no end to them." We're sitting in a downtown diner in Phoenix, Arizona. It's a tough town, Phoenix. A state capital seeing some hard times. Even to a Texan like myself, Arizona state politics seem rather baroque. There was, and remains, endless trouble over the Martin Luther King holiday, the sort of stiff-necked, foot-shooting incident for which Arizona politics seem famous. There was Evan Mecham, the eccentric Republican millionaire governor who was impeached, after reducing state government to a ludicrous shambles. Then there was the national Keating scandal, involving Arizona savings and loans, in which both of Arizona's U.S. senators, DeConcini and McCain, played sadly prominent roles. And the very latest is the bizarre AzScam case, in which state legislators were videotaped, eagerly taking cash from an informant of the Phoenix city police department, who was posing as a Vegas mobster. "Oh," says Thackeray cheerfully. "These people are amateurs here, they thought they were finally getting to play with the big boys. They don't have the least idea how to take a bribe! It's not institutional corruption. It's not like back in Philly." Gail Thackeray was a former prosecutor in Philadelphia. Now she's a former assistant attorney general of the State of Arizona. Since moving to Arizona in 1986, she had worked under the aegis of Steve Twist, her boss in the Attorney General's office. Steve Twist wrote Arizona's pioneering computer crime laws and naturally took an interest in seeing them enforced. It was a snug niche, and Thackeray's Organized Crime and Racketeering Unit won a national reputation for ambition and technical knowledgeability.... Until the latest election in Arizona. Thackeray's boss ran for the top job, and lost. The victor, the new Attorney General, apparently went to some pains to eliminate the bureaucratic traces of his rival, including his pet group -- Thackeray's group. Twelve people got their walking papers. Now Thackeray's painstakingly assembled computer lab sits gathering dust somewhere in the glass-and-concrete Attorney General's HQ on 1275 Washington Street. Her computer-crime books, her painstakingly garnered back issues of phreak and hacker zines, all bought at her own expense -- are piled in boxes somewhere. The State of Arizona is simply not particularly interested in electronic racketeering at the moment. At the moment of our interview, Gail Thackeray, officially unemployed, is working out of the county sheriff's office, living on her savings, and prosecuting several cases -- working 60-hour weeks, just as always -- for no pay at all. "I'm trying to train people," she mutters. Half her life seems to be spent training people - merely pointing out, to the naive and incredulous (such as myself) that this stuff is actually going on out there. It's a small world, computer crime. A young world. Gail Thackeray, a trim blonde BabyBoomer who favors Grand Canyon white-water rafting to kill some slow time, is one of the world's most senior, most veteran "hacker-trackers." Her mentor was Donn Parker, the California think-tank theorist who got it all started 'way back in the mid70s, the "grandfather of the field," "the great bald eagle of computer crime." And what she has learned, Gail Thackeray teaches. Endlessly. Tirelessly. To anybody. To Secret Service agents and state police, at the Glynco, Georgia federal training center. To local police, on "roadshows" with her slide projector and notebook. To corporate security personnel. To journalists. To parents. Even crooks look to Gail Thackeray for advice. Phone-phreaks call her at the office. They know very well who she is. They pump her for information on what the cops are up to, how much they know. Sometimes whole crowds of phone phreaks, hanging out on illegal conference calls, will call Gail Thackeray up. They taunt her. And, as always, they boast. Phone-phreaks, real stone phone-phreaks, simply cannot shut up. They natter on for hours. Left to themselves, they mostly talk about the intricacies of ripping-off phones; it's about as interesting as listening to hot-rodders talk about suspension and distributor-caps. They also gossip cruelly about each other. And when talking to Gail Thackeray, they incriminate themselves. "I have tapes," Thackeray says coolly. Phone phreaks just talk like crazy. "Dial-Tone" out in Alabama has been known to spend half-an- hour simply reading stolen phone-codes aloud into voice-mail answering machines. Hundreds, thousands of numbers, recited in a monotone, without a break -- an eerie phenomenon. When arrested, it's a rare phone phreak who doesn't inform at endless length on everybody he knows. Hackers are no better. What other group of criminals, she asks rhetorically, publishes newsletters and holds conventions? She seems deeply nettled by the sheer brazenness of this behavior, though to an outsider, this activity might make one wonder whether hackers should be considered "criminals" at all. Skateboarders have magazines, and they trespass a lot. Hot rod people have magazines and they break speed limits and sometimes kill people.... I ask her whether it would be any loss to society if phone phreaking and computer hacking, as hobbies, simply dried up and blew away, so that nobody ever did it again. She seems surprised. "No," she says swiftly. "Maybe a little... in the old days... the MIT stuff... But there's a lot of wonderful, legal stuff you can do with computers now, you don't have to break into somebody else's just to learn. You don't have that excuse. You can learn all you like." Did you ever hack into a system? I ask. The trainees do it at Glynco. Just to demonstrate system vulnerabilities. She's cool to the notion. Genuinely indifferent. "What kind of computer do you have?" "A Compaq 286LE," she mutters. "What kind do you wish you had?" At this question, the unmistakable light of true hackerdom flares in Gail Thackeray's eyes. She becomes tense, animated, the words pour out: "An Amiga 2000 with an IBM card and Mac emulation! The most common hacker machines are Amigas and Commodores. And Apples." If she had the Amiga, she enthuses, she could run a whole galaxy of seized computer-evidence disks on one convenient multifunctional machine. A cheap one, too. Not like the old Attorney General lab, where they had an ancient CP/M machine, assorted Amiga flavors and Apple flavors, a couple IBMS, all the utility software... but no Commodores. The workstations down at the Attorney General's are Wang dedicated word-processors. Lame machines tied in to an office net -- though at least they get online to the Lexis and Westlaw legal data services. I don't say anything. I recognize the syndrome, though. This computer-fever has been running through segments of our society for years now. It's a strange kind of lust: K-hunger, Meg-hunger; but it's a shared disease; it can kill parties dead, as conversation spirals into the deepest and most deviant recesses of software releases and expensive peripherals.... The mark of the hacker beast. I have it too. The whole "electronic community," whatever the hell that is, has it. Gail Thackeray has it. Gail Thackeray is a hacker cop. My immediate reaction is a strong rush of indignant pity: why doesn't somebody buy this woman her Amiga?! It's not like she's asking for a Cray X-MP supercomputer mainframe; an Amiga's a sweet little cookie-box thing. We're losing zillions in organized fraud; prosecuting and defending a single hacker case in court can cost a hundred grand easy. How come nobody can come up with four lousy grand so this woman can do her job? For a hundred grand we could buy every computer cop in America an Amiga. There aren't that many of 'em. Computers. The lust, the hunger, for computers. The loyalty they inspire, the intense sense of possessiveness. The culture they have bred. I myself am sitting in downtown Phoenix, Arizona because it suddenly occurred to me that the police might -- just might -- come and take away my computer. The prospect of this, the mere implied threat, was unbearable. It literally changed my life. It was changing the lives of many others. Eventually it would change everybody's life. Gail Thackeray was one of the top computercrime people in America. And I was just some novelist, and yet I had a better computer than hers. Practically everybody I knew had a better computer than Gail Thackeray and her feeble laptop 286. It was like sending the sheriff in to clean up Dodge City and arming her with a slingshot cut from an old rubber tire. But then again, you don't need a howitzer to enforce the law. You can do a lot just with a badge. With a badge alone, you can basically wreak havoc, take a terrible vengeance on wrongdoers. Ninety percent of "computer crime investigation" is just "crime investigation:" names, places, dossiers, modus operandi, search warrants, victims, complainants, informants... What will computer crime look like in ten years? Will it get better? Did "Sundevil" send 'em reeling back in confusion? It'll be like it is now, only worse, she tells me with perfect conviction. Still there in the background, ticking along, changing with the times: the criminal underworld. It'll be like drugs are. Like our problems with alcohol. All the cops and laws in the world never solved our problems with alcohol. If there's something people want, a certain percentage of them are just going to take it. Fifteen percent of the populace will never steal. Fifteen percent will steal most anything not nailed down. The battle is for the hearts and minds of the remaining seventy percent. And criminals catch on fast. If there's not "too steep a learning curve" -- if it doesn't require a baffling amount of expertise and practice -- then criminals are often some of the first through the gate of a new technology. Especially if it helps them to hide. They have tons of cash, criminals. The new communications tech -- like pagers, cellular phones, faxes, Federal Express -- were pioneered by rich corporate people, and by criminals. In the early years of pagers and beepers, dope dealers were so enthralled this technology that owing a beeper was practically prima facie evidence of cocaine dealing. CB radio exploded when the speed limit hit 55 and breaking the highway law became a national pastime. Dope dealers send cash by Federal Express, despite, or perhaps because of, the warnings in FedEx offices that tell you never to try this. Fed Ex uses X-rays and dogs on their mail, to stop drug shipments. That doesn't work very well. Drug dealers went wild over cellular phones. There are simple methods of faking ID on cellular phones, making the location of the call mobile, free of charge, and effectively untraceable. Now victimized cellular companies routinely bring in vast toll-lists of calls to Colombia and Pakistan. Judge Greene's fragmentation of the phone company is driving law enforcement nuts. Four thousand telecommunications companies. Fraud skyrocketing. Every temptation in the world available with a phone and a credit card number. Criminals untraceable. A galaxy of "new neat rotten things to do." Judge Greene's fragmentation of the phone company is driving law enforcement nuts. Four thousand telecommunications companies. Fraud skyrocketing. Every temptation in the world available with a phone and a credit card number. Criminals untraceable. A galaxy of "new neat rotten things to do." If there were one thing Thackeray would like to have, it would be an effective legal end-run through this new fragmentation minefield. It would be a new form of electronic search warrant, an "electronic letter of marque" to be issued by a judge. It would create a new category of "electronic emergency." Like a wiretap, its use would be rare, but it would cut across state lines and force swift cooperation from all concerned. Cellular, phone, laser, computer network, PBXes, AT&T, Baby Bells, long-distance entrepreneurs, packet radio. Some document, some mighty court-order, that could slice through four thousand separate forms of corporate red-tape, and get her at once to the source of calls, the source of email threats and viruses, the sources of bomb threats, kidnapping threats. "From now on," she says, "the Lindberg baby will always die." Something that would make the Net sit still, if only for a moment. Something that would get her up to speed. Seven league boots. That's what she really needs. "Those guys move in nanoseconds and I'm on the Pony Express." And then, too, there's the coming international angle. Electronic crime has never been easy to localize, to tie to a physical jurisdiction. And phone phreaks and hackers loathe boundaries, they jump them whenever they can. The English. The Dutch. And the Germans, especially the ubiquitous Chaos Computer Club. The Australians. They've all learned phone-phreaking from America. It's a growth mischief industry. The multinational networks are global, but governments and the police simply aren't. Neither are the laws. Or the legal frameworks for citizen protection. One language is global, though - English. Phone phreaks speak English; it's their native tongue even if they're Germans. English may have started in England but now it's the Net language; it might as well be called "CNNese." Asians just aren't much into phone phreaking. They're the world masters at organized software piracy. The French aren't into phone-phreaking either. The French are into computerized industrial espionage. In the old days of the MIT righteous hackerdom, crashing systems didn't hurt anybody. Not all that much, anyway. Not permanently. Now the players are more venal. Now the consequences are worse. Hacking will begin killing people soon. Already there are methods of stacking calls onto 911 systems, annoying the police, and possibly causing the death of some poor soul calling in with a genuine emergency. Hackers in Amtrak computers, or airtraffic control computers, will kill somebody someday. Maybe a lot of people. Gail Thackeray expects it. And the viruses are getting nastier. The "Scud" virus is the latest one out. It wipes hard-disks. According to Thackeray, the idea that phonephreaks are Robin Hoods is a fraud. They don't deserve this repute. Basically, they pick on the weak. AT&T now protects itself with the fearsome ANI (Automatic Number Identification) trace capability. When AT&T wised up and tightened security generally, the phreaks drifted into the Baby Bells. The Baby Bells lashed out in 1989 and 1990, so the phreaks switched to smaller long-distance entrepreneurs. Today, they are moving into locally owned PBXes and voice-mail systems, which are full of security holes, dreadfully easy to hack. These victims aren't the moneybags Sheriff of Nottingham or Bad King John, but small groups of innocent people who find it hard to protect themselves, and who really suffer from these depredations. Phone phreaks pick on the weak. They do it for power. If it were legal, they wouldn't do it. They don't want service, or knowledge, they want the thrill of powertripping. There's plenty of knowledge or service around, if you're willing to pay. Phone phreaks don't pay, they steal. It's because it is illegal that it feels like power, that it gratifies their vanity. I leave Gail Thackeray with a handshake at the door of her office building - a vast International Style office building downtown. The Sheriff's office is renting part of it. I get the vague impression that quite a lot of the building is empty - real estate crash. In a Phoenix sports apparel store, in a downtown mall, I meet the "Sun Devil" himself. He is the cartoon mascot of Arizona State University, whose football stadium, "Sundevil," is near the local Secret Service HQ - hence the name Operation Sundevil. The Sun Devil himself is named "Sparky." Sparky the Sun Devil is maroon and bright yellow, the school colors. Sparky brandishes a three-tined yellow pitchfork. He has a small mustache, pointed ears, a barbed tail, and is dashing forward jabbing the air with the pitchfork, with an expression of devilish glee. Phoenix was the home of Operation Sundevil. The Legion of Doom ran a hacker bulletin board called "The Phoenix Project." An Australian hacker named "Phoenix" once burrowed through the Internet to attack Cliff Stoll, then bragged and boasted about it to *The New York Times*. This net of coincidence is both odd and meaningless. The headquarters of the Arizona Attorney General, Gail Thackeray's former workplace, is on 1275 Washington Avenue. Many of the downtown streets in Phoenix are named after prominent American presidents: Washington, Jefferson, Madison... After dark, all the employees go home to their suburbs. Washington, Jefferson and Madison - what would be the Phoenix inner city, if there were an inner city in this sprawling automobile-bred town - become the haunts of transients and derelicts. The homeless. The sidewalks along Washington are lined with orange trees. Ripe fallen fruit lies scattered like croquet balls on the sidewalks and gutters. No one seems to be eating them. I try a fresh one. It tastes unbearably bitter. The Attorney General's office, built in 1981 during the Babbitt administration, is a long low two story building of white cement and wall-sized sheets of curtain-glass. Behind each glass wall is a lawyer's office, quite open and visible to anyone strolling by. Across the street is a dour government building labelled simply ECONOMIC SECURITY, something that has not been in great supply in the American Southwest lately. The offices are about twelve feet square. They feature tall wooden cases full of red-spined lawbooks; Wang computer monitors; telephones; Post-it notes galore. Also framed law diplomas and a general excess of bad Western landscape art. Ansel Adams photos are a big favorite, perhaps to compensate for the dismal specter of the parking lot, two acres of striped black asphalt, which features gravel landscaping and some sickly-looking barrel cacti. It has grown dark. Gail Thackeray has told me that the people who work late here, are afraid of muggings in the parking lot. It seems cruelly ironic that a woman tracing electronic racketeers across the interstate labyrinth of Cyberspace should fear an assault by a homeless derelict in the parking lot of her own workplace. Perhaps this is less than coincidence. Perhaps these two seemingly disparate worlds are somehow generating one another. The poor and disenfranchised take to the streets, while the rich and computer-equipped, safe in their bedrooms, chatter over their modems. Quite often the derelicts kick the glass out and break in to the lawyers' offices, if they see something they need or want badly enough. I cross the parking lot to the street behind the Attorney General's office. A pair of young tramps are bedding down on flattened sheets of cardboard, under an alcove stretching over the sidewalk. One tramp wears a glitter-covered T-shirt reading "CALIFORNIA" in Coca-Cola cursive. His nose and cheeks look chafed and swollen; they glisten with what seems to be Vaseline. The other tramp has a ragged long-sleeved shirt and lank brown hair parted in the middle. They both wear blue jeans coated in grime. They are both drunk. "You guys crash here a lot?" I ask them. They look at me warily. I am wearing black jeans, a black pinstriped suit jacket and a black silk tie. I have odd shoes and a funny haircut. "It's our first time here," says the red-nosed tramp unconvincingly. There is a lot of cardboard stacked here. More than any two people could use. "We usually stay at the Vinnie's down the street," says the brown-haired tramp, puffing a Marlboro with a meditative air, as he sprawls with his head on a blue nylon backpack. "The Saint Vincent's." "You know who works in that building over there?" I ask, pointing. The brown-haired tramp shrugs. "Some kind of attorneys, it says." We urge one another to take it easy. I give them five bucks. A block down the street I meet a vigorous workman who is wheeling along some kind of industrial trolley; it has what appears to be a tank of propane on it. We make eye contact. We nod politely. I walk past him. "Hey! Excuse me sir!" he says. "Yes?" I say, stopping and turning. "Have you seen," the guy says rapidly, "a black guy, about 6'7", scars on both his cheeks like this -" he gestures - "wears a black baseball cap on backwards, wandering around here anyplace?" "Sounds like I don't much want to meet him," I say. "He took my wallet," says my new acquaintance. "Took it this morning. Y'know, some people would be scared of a guy like that. But I'm not scared. I'm from Chicago. I'm gonna hunt him down. We do things like that in Chicago." "Yeah?" "I went to the cops and now he's got an APB out on his ass," he says with satisfaction. "You run into him, you let me know." "Okay," I say. "What is your name, sir?" "Stanley..." "And how can I reach you?" "Oh," Stanley says, in the same rapid voice, "you don't have to reach, uh, me. You can just call the cops. Go straight to the cops." He reaches into a pocket and pulls out a greasy piece of pasteboard. "See, here's my report on him." I look. The "report," the size of an index card, is labelled PRO-ACT: Phoenix Residents Opposing Active Crime Threat... or is it Organized Against Crime Threat? In the darkening street it's hard to read. Some kind of vigilante group? Neighborhood watch? I feel very puzzled. "Are you a police officer, sir?" He smiles, seems very pleased by the question. "No," he says. "But you are a Phoenix Resident?" "Would you believe a homeless person," Stanley says. "Really? But what's with the..." For the first time I take a close look at Stanley's trolley. It's a rubber-wheeled thing of industrial metal, but the device I had mistaken for a tank of propane is in fact a water-cooler. Stanley also has an Army duffel-bag, stuffed tight as a sausage with clothing or perhaps a tent, and, at the base of his trolley, a cardboard box and a battered leather briefcase. "I see," I say, quite at a loss. For the first time I notice that Stanley has a wallet. He has not lost his wallet at all. It is in his back pocket and chained to his belt. It's not a new wallet. It seems to have seen a lot of wear. "Well, you know how it is, brother," says Stanley. Now that I know that he is homeless - a possible threat - my entire perception of him has changed in an instant. His speech, which once seemed just bright and enthusiastic, now seems to have a dangerous tang of mania. "I have to do this!" he assures me. "Track this guy down... It's a thing I do... you know... to keep myself together!" He smiles, nods, lifts his trolley by its decaying rubber handgrips. "Gotta work together, y'know," Stanley booms, his face alight with cheerfulness, "the police can't do everything!" The gentlemen I met in my stroll in downtown Phoenix are the only computer illiterates in this book. To regard them as irrelevant, however, would be a grave mistake. As computerization spreads across society, the populace at large is subjected to wave after wave of future shock. But, as a necessary converse, the "computer community" itself is subjected to wave after wave of incoming computer illiterates. How will those currently enjoying America's digital bounty regard, and treat, all this teeming refuse yearning to breathe free? Will the electronic frontier be another Land of Opportunity - or an armed and monitored enclave, where the disenfranchised snuggle on their cardboard at the locked doors of our houses of justice? Some people just don't get along with computers. They can't read. They can't type. They just don't have it in their heads to master arcane instructions in wirebound manuals. Somewhere, the process of computerization of the populace will reach a limit. Some people - quite decent people maybe, who might have thrived in any other situation - will be left irretrievably outside the bounds. What's to be done with these people, in the bright new shiny electroworld? How will they be regarded, by the mouse-whizzing masters of cyberspace? With contempt? Indifference? Fear? In retrospect, it astonishes me to realize how quickly poor Stanley became a perceived threat. Surprise and fear are closely allied feelings. And the world of computing is full of surprises. I met one character in the streets of Phoenix whose role in those book is supremely and directly relevant. That personage was Stanley's giant thieving scarred phantom. This phantasm is everywhere in this book. He is the specter haunting cyberspace. Sometimes he's a maniac vandal ready to smash the phone system for no sane reason at all. Sometimes he's a fascist fed, coldly programming his mighty mainframes to destroy our Bill of Rights. Sometimes he's a telco bureaucrat, covertly conspiring to register all modems in the service of an Orwellian surveillance regime. Mostly, though, this fearsome phantom is a "hacker." He's strange, he doesn't belong, he's not authorized, he doesn't smell right, he's not keeping his proper place, he's not one of us. The focus of fear is the hacker, for much the same reasons that Stanley's fancied assailant is black. Stanley's demon can't go away, because he doesn't exist. Despite singleminded and tremendous effort, he can't be arrested, sued, jailed, or fired. The only constructive way to do anything about him is to learn more about Stanley himself. This learning process may be repellent, it may be ugly, it may involve grave elements of paranoiac confusion, but it's necessary. Knowing Stanley requires something more than class-crossing condescension. It requires more than steely legal objectivity. It requires human compassion and sympathy. To know Stanley is to know his demon. If you know the other guy's demon, then maybe you'll come to know some of your own. You'll be able to separate reality from illusion. And then you won't do your cause, and yourself, more harm than good. Like poor damned Stanley from Chicago did. 5. === The Federal Computer Investigations Committee (FCIC) is the most important and influential organization in the realm of American computer-crime. Since the police of other countries have largely taken their computer-crime cues from American methods, the FCIC might well be called the most important computer crime group in the world. It is also, by federal standards, an organization of great unorthodoxy. State and local investigators mix with federal agents. Lawyers, financial auditors and computer-security programmers trade notes with street cops. Industry vendors and telco security people show up to explain their gadgetry and plead for protection and justice. Private investigators, think-tank experts and industry pundits throw in their two cents' worth. The FCIC is the antithesis of a formal bureaucracy. Members of the FCIC are obscurely proud of this fact; they recognize their group as aberrant, but are entirely convinced that this, for them, outright \*weird\* behavior is nevertheless \*absolutely necessary\* to get their jobs done. FCIC regulars -- from the Secret Service, the FBI, the IRS, the Department of Labor, the offices of federal attorneys, state police, the Air Force, from military intelligence -- often attend meetings, held hither and thither across the country, at their own expense. The FCIC doesn't get grants. It doesn't charge membership fees. It doesn't have a boss. It has no headquarters -- just a mail drop in Washington DC, at the Fraud Division of the Secret Service. It doesn't have a budget. It doesn't have schedules. It meets three times a year -- sort of. Sometimes it issues publications, but the FCIC has no regular publisher, no treasurer, not even a secretary. There are no minutes of FCIC meetings. Non-federal people are considered "non-voting members," but there's not much in the way of elections. There are no badges, lapel pins or certificates of membership. Everyone is on a firstname basis. There are about forty of them. Nobody knows how many, exactly. People come, people go -sometimes people "go" formally but still hang around anyway. Nobody has ever exactly figured out what "membership" of this "Committee" actually entails. Strange as this may seem to some, to anyone familiar with the social world of computing, the "organization" of the FCIC is very recognizable. For years now, economists and management theorists have speculated that the tidal wave of the information revolution would destroy rigid, pyramidal bureaucracies, where everything is topdown and centrally controlled. Highly trained "employees" would take on much greater autonomy, being self-starting, and self-motivating, moving from place to place, task to task, with great speed and fluidity. "Ad-hocracy" would rule, with groups of people spontaneously knitting together across organizational lines, tackling the problem at hand, applying intense computer-aided expertise to it, and then vanishing whence they came. This is more or less what has actually happened in the world of federal computer investigation. With the conspicuous exception of the phone companies, which are after all over a hundred years old, practically \*every\* organization that plays any important role in this book functions just like the FCIC. The Chicago Task Force, the Arizona Racketeering Unit, the Legion of Doom, the *Phrack* crowd, the Electronic Frontier Foundation -- they \*all\* look and act like "tiger teams" or "user's groups." They are all electronic ad-hocracies leaping up spontaneously to attempt to meet a need. Some are police. Some are, by strict definition, criminals. Some are political interest-groups. But every single group has that same quality of apparent spontaneity -- "Hey, gang! My uncle's got a barn -let's put on a show!" Every one of these groups is embarrassed by this "amateurism," and, for the sake of their public image in a world of non-computer people, they all attempt to look as stern and formal and impressive as possible. These electronic frontier-dwellers resemble groups of nineteenth-century pioneers hankering after the respectability of statehood. There are however, two crucial differences in the historical experience of these "pioneers" of the nineteeth and twenty-first centuries. First, powerful information technology \*does\* play into the hands of small, fluid, loosely organized groups. There have always been "pioneers," "hobbyists," "amateurs," "dilettantes," "volunteers," "movements," "users' groups" and "blue-ribbon panels of experts" around. But a group of this kind - when technically equipped to ship huge amounts of specialized information, at lightning speed, to its members, to government, and to the press -- is simply a different kind of animal. It's like the difference between an eel and an electric eel. The second crucial change is that American society is currently in a state approaching permanent technological revolution. In the world of computers particularly, it is practically impossible to \*ever\* stop being a "pioneer," unless you either drop dead or deliberately jump off the bus. The scene has never slowed down enough to become well-institutionalized. And after twenty, thirty, forty years the "computer revolution" continues to spread, to permeate new corners of society. Anything that really works is already obsolete. If you spend your entire working life as a "pioneer," the word "pioneer" begins to lose its meaning. Your way of life looks less and less like an introduction to "something else" more stable and organized, and more and more like \*just the way things are.\* A "permanent revolution" is really a contradiction in terms. If "turmoil" lasts long enough, it simply becomes \*a new kind of society\* -still the same game of history, but new players, new rules. Apply this to the world of late twentieth-century law enforcement, and the implications are novel and puzzling indeed. Any bureaucratic rulebook you write about computer-crime will be flawed when you write it, and almost an antique by the time it sees print. The fluidity and fast reactions of the FCIC give them a great advantage in this regard, which explains their success. Even with the best will in the world (which it does not, in fact, possess) it is impossible for an organization the size of the U.S. Federal Bureau of Investigation to get up to speed on the theory and practice of computer crime. If they tried to train all their agents to do this, it would be \*suicidal,\* as they would \*never be able to do anything else.\* The FBI does try to train its agents in the basics of electronic crime, at their base in Quantico, Virginia. And the Secret Service, along with many other law enforcement groups, runs quite successful and well-attended training courses on wire fraud, business crime, and computer intrusion at the Federal Law Enforcement Training Center (FLETC, pronounced "fletsy") in Glynco, Georgia. But the best efforts of these bureaucracies does not remove the absolute need for a "cutting-edge mess" like the FCIC. For you see -- the members of FCIC \*are\* the trainers of the rest of law enforcement. Practically and literally speaking, they are the Glynco computer- crime faculty by another name. If the FCIC went over a cliff on a bus, the U.S. law enforcement community would be rendered deaf dumb and blind in the world of computer crime, and would swiftly feel a desperate need to reinvent them. And this is no time to go starting from scratch. On June 11, 1991, I once again arrived in Phoenix, Arizona, for the latest meeting of the Federal Computer Investigations Committee. This was more or less the twentieth meeting of this stellar group. The count was uncertain, since nobody could figure out whether to include the meetings of "the Colluquy," which is what the FCIC was called in the mid-1980s before it had even managed to obtain the dignity of its own acronym. Since my last visit to Arizona, in May, the local AzScam bribery scandal had resolved itself in a general muddle of humiliation. The Phoenix chief of police, whose agents had videotaped nine state legislators up to no good, had resigned his office in a tussle with the Phoenix city council over the propriety of his undercover operations. The Phoenix Chief could now join Gail Thackeray and eleven of her closest associates in the shared experience of politically motivated unemployment. As of June, resignations were still continuing at the Arizona Attorney General's office, which could be interpreted as either a New Broom Sweeping Clean or a Night of the Long Knives Part II, depending on your point of view. The meeting of FCIC was held at the Scottsdale Hilton Resort. Scottsdale is a wealthy suburb of Phoenix, known as "Scottsdull" to scoffing local trendies, but well-equipped with posh shoppingmalls and manicured lawns, while conspicuously undersupplied with homeless derelicts. The Scottsdale Hilton Resort was a sprawling hotel in postmodern crypto-Southwestern style. It featured a "mission bell tower" plated in turquoise tile and vaguely resembling a Saudi minaret. Inside it was all barbarically striped Santa Fe Style decor. There was a health spa downstairs and a large oddly-shaped pool in the patio. A poolside umbrella-stand offered Ben and Jerry's politically correct Peace Pops. I registered as a member of FCIC, attaining a handy discount rate, then went in search of the Feds. Sure enough, at the back of the hotel grounds came the unmistakable sound of Gail Thackeray holding forth. Since I had also attended the Computers Freedom and Privacy conference (about which more later), this was the second time I had seen Thackeray in a group of her law enforcement colleagues. Once again I was struck by how simply pleased they seemed to see her. It was natural that she'd get \*some\* attention, as Gail was one of two women in a group of some thirty men; but there was a lot more to it than that. Gail Thackeray personifies the social glue of the FCIC. They could give a damn about her losing her job with the Attorney General. They were sorry about it, of course, but hell, they'd all lost jobs. If they were the kind of guys who liked steady boring jobs, they would never have gotten into computer work in the first place. I wandered into her circle and was immediately introduced to five strangers. The conditions of my visit at FCIC were reviewed. I would not quote anyone directly. I would not tie opinions expressed to the agencies of the attendees. I would not (a purely hypothetical example) report the conversation of a guy from the Secret Service talking quite civilly to a guy from the FBI, as these two agencies \*never\* talk to each other, and the IRS (also present, also hypothetical) \*never talks to anybody.\* Worse yet, I was forbidden to attend the first conference. And I didn't. I have no idea what the FCIC was up to behind closed doors that afternoon. I rather suspect that they were engaging in a frank and thorough confession of their errors, goof-ups and blunders, as this has been a feature of every FCIC meeting since their legendary Memphis beer bust of 1986. Perhaps the single greatest attraction of FCIC is that it is a place where you can go, let your hair down, and completely level with people who actually comprehend what you are talking about. Not only do they understand you, but they \*really pay attention,\* they are \*grateful for your insights,\* and they \*forgive you,\* which in nine cases out of ten is something even your boss can't do, because as soon as you start talking "ROM," "BBS," or "T-1 trunk," his eyes glaze over. I had nothing much to do that afternoon. The FCIC were beavering away in their conference room. Doors were firmly closed, windows too dark to peer through. I wondered what a real hacker, a computer intruder, would do at a meeting like this. The answer came at once. He would "trash" the place. Not reduce the place to trash in some orgy of vandalism; that's not the use of the term in the hacker milieu. No, he would quietly \*empty the trash baskets\* and silently raid any valuable data indiscreetly thrown away. Journalists have been known to do this. (Journalists hunting information have been known to do almost every single unethical thing that hackers have ever done. They also throw in a few awful techniques all their own.) The legality of 'trashing' is somewhat dubious but it is not in fact flagrantly illegal. It was, however, absurd to contemplate trashing the FCIC. These people knew all about trashing. I wouldn't last fifteen seconds. The idea sounded interesting, though. I'd been hearing a lot about the practice lately. On the spur of the moment, I decided I would try trashing the office \*across the hall\* from the FCIC, an area which had nothing to do with the investigators. The office was tiny; six chairs, a table.... Nevertheless, it was open, so I dug around in its plastic trash can. To my utter astonishment, I came up with the torn scraps of a SPRINT long-distance phone bill. More digging produced a bank statement and the scraps of a hand-written letter, along with gum, cigarette ashes, candy wrappers and a day-old-issue of USA TODAY. The trash went back in its receptacle while the scraps of data went into my travel bag. I detoured through the hotel souvenir shop for some Scotch tape and went up to my room. Coincidence or not, it was quite true. Some poor soul had, in fact, thrown a SPRINT bill into the hotel's trash. Date May 1991, total amount due: $252.36. Not a business phone, either, but a residential bill, in the name of someone called Evelyn (not her real name). Evelyn's records showed a ## PAST DUE BILL ##! Here was her nine-digit account ID. Here was a stern computer-printed warning: "TREAT YOUR FONCARD AS YOU WOULD ANY CREDIT CARD. TO SECURE AGAINST FRAUD, NEVER GIVE YOUR FONCARD NUMBER OVER THE PHONE UNLESS YOU INITIATED THE CALL. IF YOU RECEIVE SUSPICIOUS CALLS PLEASE NOTIFY CUSTOMER SERVICE IMMEDIATELY!" I examined my watch. Still plenty of time left for the FCIC to carry on. I sorted out the scraps of Evelyn's SPRINT bill and re-assembled them with fresh Scotch tape. Here was her ten-digit FONCARD number. Didn't seem to have the ID number necessary to cause real fraud trouble. I did, however, have Evelyn's home phone number. And the phone numbers for a whole crowd of Evelyn's long-distance friends and acquaintances. In San Diego, Folsom, Redondo, Las Vegas, La Jolla, Topeka, and Northampton Massachusetts. Even somebody in Australia! I examined other documents. Here was a bank statement. It was Evelyn's IRA account down at a bank in San Mateo California (total balance $1877.20). Here was a charge-card bill for $382.64. She was paying it off bit by bit. Driven by motives that were completely unethical and prurient, I now examined the handwritten notes. They had been torn fairly thoroughly, so much so that it took me almost an entire five minutes to reassemble them. They were drafts of a love letter. They had been written on the lined stationery of Evelyn's employer, a biomedical company. Probably written at work when she should have been doing something else. "Dear Bob," (not his real name) "I guess in everyone's life there comes a time when hard decisions have to be made, and this is a difficult one for me -- very upsetting. Since you haven't called me, and I don't understand why, I can only surmise it's because you don't want to. I thought I would have heard from you Friday. I did have a few unusual problems with my phone and possibly you tried, I hope so. "Robert, you asked me to 'let go'..." The first note ended. \*Unusual problems with her phone?\* I looked swiftly at the next note. "Bob, not hearing from you for the whole weekend has left me very perplexed..." Next draft. "Dear Bob, there is so much I don't understand right now, and I wish I did. I wish I could talk to you, but for some unknown reason you have elected not to call -- this is so difficult for me to understand..." She tried again. "Bob, Since I have always held you in such high esteem, I had every hope that we could remain good friends, but now one essential ingredient is missing - respect. Your ability to discard people when their purpose is served is appalling to me. The kindest thing you could do for me now is to leave me alone. You are no longer welcome in my heart or home..." Try again. "Bob, I wrote a very factual note to you to say how much respect I had lost for you, by the way you treat people, me in particular, so uncaring and cold. The kindest thing you can do for me is to leave me alone entirely, as you are no longer welcome in my heart or home. I would appreciate it if you could retire your debt to me as soon as possible -- I wish no link to you in any way. Sincerely, Evelyn." Good heavens, I thought, the bastard actually owes her money! I turned to the next page. "Bob: very simple. GOODBYE! No more mind games -- no more fascination -- no more coldness -no more respect for you! It's over -- Finis. Evie" There were two versions of the final brushoff letter, but they read about the same. Maybe she hadn't sent it. The final item in my illicit and shameful booty was an envelope addressed to "Bob" at his home address, but it had no stamp on it and it hadn't been mailed. Maybe she'd just been blowing off steam because her rascal boyfriend had neglected to call her one weekend. Big deal. Maybe they'd kissed and made up, maybe she and Bob were down at Pop's Chocolate Shop now, sharing a malted. Sure. Easy to find out. All I had to do was call Evelyn up. With a half-clever story and enough brass- plated gall I could probably trick the truth out of her. Phone-phreaks and hackers deceive people over the phone all the time. It's called "social engineering." Social engineering is a very common practice in the underground, and almost magically effective. Human beings are almost always the weakest link in computer security. The simplest way to learn Things You Are Not Meant To Know is simply to call up and exploit the knowledgeable people. With social engineering, you use the bits of specialized knowledge you already have as a key, to manipulate people into believing that you are legitimate. You can then coax, flatter, or frighten them into revealing almost anything you want to know. Deceiving people (especially over the phone) is easy and fun. Exploiting their gullibility is very gratifying; it makes you feel very superior to them. If I'd been a malicious hacker on a trashing raid, I would now have Evelyn very much in my power. Given all this inside data, it wouldn't take much effort at all to invent a convincing lie. If I were ruthless enough, and jaded enough, and clever enough, this momentary indiscretion of hers -maybe committed in tears, who knows -- could cause her a whole world of confusion and grief. I didn't even have to have a \*malicious\* motive. Maybe I'd be "on her side," and call up Bob instead, and anonymously threaten to break both his kneecaps if he didn't take Evelyn out for a steak dinner pronto. It was still profoundly \*none of my business.\* To have gotten this knowledge at all was a sordid act and to use it would be to inflict a sordid injury. To do all these awful things would require exactly zero high-tech expertise. All it would take was the willingness to do it and a certain amount of bent imagination. I went back downstairs. The hard-working FCIC, who had labored forty-five minutes over their schedule, were through for the day, and adjourned to the hotel bar. We all had a beer. I had a chat with a guy about "Isis," or rather IACIS, the International Association of Computer Investigation Specialists. They're into "computer forensics," the techniques of picking computersystems apart without destroying vital evidence. IACIS, currently run out of Oregon, is comprised of investigators in the U.S., Canada, Taiwan and Ireland. "Taiwan and Ireland?" I said. Are \*Taiwan\* and \*Ireland\* really in the forefront of this stuff? Well not exactly, my informant admitted. They just happen to have been the first ones to have caught on by word of mouth. Still, the international angle counts, because this is obviously an international problem. Phone-lines go everywhere. There was a Mountie here from the Royal Canadian Mounted Police. He seemed to be having quite a good time. Nobody had flung this Canadian out because he might pose a foreign security risk. These are cyberspace cops. They still worry a lot about "jurisdictions," but mere geography is the least of their troubles. NASA had failed to show. NASA suffers a lot from computer intrusions, in particular from Australian raiders and a well-trumpeted Chaos Computer Club case, and in 1990 there was a brief press flurry when it was revealed that one of NASA's Houston branch-exchanges had been systematically ripped off by a gang of phone-phreaks. But the NASA guys had had their funding cut. They were stripping everything. Air Force OSI, its Office of Special Investigations, is the \*only\* federal entity dedicated full-time to computer security. They'd been expected to show up in force, but some of them had cancelled -- a Pentagon budget pinch. As the empties piled up, the guys began joshing around and telling war- stories. "These are cops," Thackeray said tolerantly. "If they're not talking shop they talk about women and beer." I heard the story about the guy who, asked for "a copy" of a computer disk, \*photocopied the label on it.\* He put the floppy disk onto the glass plate of a photocopier. The blast of static when the copier worked completely erased all the real information on the disk. Some other poor souls threw a whole bag of confiscated diskettes into the squad-car trunk next to the police radio. The powerful radio signal blasted them, too. We heard a bit about Dave Geneson, the first computer prosecutor, a mainframe-runner in Dade County, turned lawyer. Dave Geneson was one guy who had hit the ground running, a signal virtue in making the transition to computer-crime. It was generally agreed that it was easier to learn the world of computers first, then police or prosecutorial work. You could take certain computer people and train 'em to successful police work -- but of course they had to have the \*cop mentality.\* They had to have street smarts. Patience. Persistence. And discretion. You've got to make sure they're not hotshots, show-offs, "cowboys." Most of the folks in the bar had backgrounds in military intelligence, or drugs, or homicide. It was rudely opined that "military intelligence" was a contradiction in terms, while even the grisly world of homicide was considered cleaner than drug enforcement. One guy had been 'way undercover doing dope- work in Europe for four years straight. "I'm almost recovered now," he said deadpan, with the acid black humor that is pure cop. "Hey, now I can say \*fucker\* without putting \*mother\* in front of it." "In the cop world," another guy said earnestly, "everything is good and bad, black and white. In the computer world everything is gray." One guy -- a founder of the FCIC, who'd been with the group since it was just the Colluquy -described his own introduction to the field. He'd been a Washington DC homicide guy called in on a "hacker" case. From the word "hacker," he naturally assumed he was on the trail of a knife-wielding marauder, and went to the computer center expecting blood and a body. When he finally figured out what was happening there (after loudly demanding, in vain, that the programmers "speak English"), he called headquarters and told them he was clueless about computers. They told him nobody else knew diddly either, and to get the hell back to work. So, he said, he had proceeded by comparisons. By analogy. By metaphor. "Somebody broke in to your computer, huh?" Breaking and entering; I can understand that. How'd he get in? "Over the phonelines." Harassing phone-calls, I can understand that! What we need here is a tap and a trace! It worked. It was better than nothing. And it worked a lot faster when he got hold of another cop who'd done something similar. And then the two of them got another, and another, and pretty soon the Colluquy was a happening thing. It helped a lot that everybody seemed to know Carlton Fitzpatrick, the data-processing trainer in Glynco. The ice broke big-time in Memphis in '86. The Colluquy had attracted a bunch of new guys -- Secret Service, FBI, military, other feds, heavy guys. Nobody wanted to tell anybody anything. They suspected that if word got back to the home office they'd all be fired. They passed an uncomfortably guarded afternoon. The formalities got them nowhere. But after the formal session was over, the organizers brought in a case of beer. As soon as the participants knocked it off with the bureaucratic ranks and turf-fighting, everything changed. "I bared my soul," one veteran reminisced proudly. By nightfall they were building pyramids of empty beer-cans and doing everything but composing a team fight song. FCIC were not the only computer-crime people around. There was DATTA (District Attorneys' Technology Theft Association), though they mostly specialized in chip theft, intellectual property, and black-market cases. There was HTCIA (High Tech Computer Investigators Association), also out in Silicon Valley, a year older than FCIC and featuring brilliant people like Donald Ingraham. There was LEETAC (Law Enforcement Electronic Technology Assistance Committee) in Florida, and computercrime units in Illinois and Maryland and Texas and Ohio and Colorado and Pennsylvania. But these were local groups. FCIC were the first to really network nationally and on a federal level. FCIC people live on the phone lines. Not on bulletin board systems -- they know very well what boards are, and they know that boards aren't secure. Everyone in the FCIC has a voice-phone bill like you wouldn't believe. FCIC people have been tight with the telco people for a long time. Telephone cyberspace is their native habitat. FCIC has three basic sub-tribes: the trainers, the security people, and the investigators. That's why it's called an "Investigations Committee" with no mention of the term "computer-crime" -- the dreaded "C-word." FCIC, officially, is "an association of agencies rather than individuals;" unofficially, this field is small enough that the influence of individuals and individual expertise is paramount. Attendance is by invitation only, and most everyone in FCIC considers himself a prophet without honor in his own house. Again and again I heard this, with different terms but identical sentiments. "I'd been sitting in the wilderness talking to myself." "I was totally isolated." "I was desperate." "FCIC is the best thing there is about computer crime in America." "FCIC is what really works." "This is where you hear real people telling you what's really happening out there, not just lawyers picking nits." "We taught each other everything we knew." The sincerity of these statements convinces me that this is true. FCIC is the real thing and it is invaluable. It's also very sharply at odds with the rest of the traditions and power structure in American law enforcement. There probably hasn't been anything around as loose and go-getting as the FCIC since the start of the U.S. Secret Service in the 1860s. FCIC people are living like twenty-firstcentury people in a twentieth-century environment, and while there's a great deal to be said for that, there's also a great deal to be said against it, and those against it happen to control the budgets. I listened to two FCIC guys from Jersey compare life histories. One of them had been a biker in a fairly heavy- duty gang in the 1960s. "Oh, did you know so-and-so?" said the other guy from Jersey. "Big guy, heavyset?" "Yeah, I knew him." "Yeah, he was one of ours. He was our plant in the gang." "Really? Wow! Yeah, I knew him. Helluva guy." Thackeray reminisced at length about being tear-gassed blind in the November 1969 antiwar protests in Washington Circle, covering them for her college paper. "Oh yeah, I was there," said another cop. "Glad to hear that tear gas hit somethin'. Haw haw haw." He'd been so blind himself, he confessed, that later that day he'd arrested a small tree. FCIC are an odd group, sifted out by coincidence and necessity, and turned into a new kind of cop. There are a lot of specialized cops in the world -- your bunco guys, your drug guys, your tax guys, but the only group that matches FCIC for sheer isolation are probably the child-pornography people. Because they both deal with conspirators who are desperate to exchange forbidden data and also desperate to hide; and because nobody else in law enforcement even wants to hear about it. FCIC people tend to change jobs a lot. They tend not to get the equipment and training they want and need. And they tend to get sued quite often. As the night wore on and a band set up in the bar, the talk grew darker. Nothing ever gets done in government, someone opined, until there's a \*disaster.\* Computing disasters are awful, but there's no denying that they greatly help the credibility of FCIC people. The Internet Worm, for instance. "For years we'd been warning about that -but it's nothing compared to what's coming." They expect horrors, these people. They know that nothing will really get done until there is a horror. 6. === Next day we heard an extensive briefing from a guy who'd been a computer cop, gotten into hot water with an Arizona city council, and now installed computer networks for a living (at a considerable rise in pay). He talked about pulling fiber-optic networks apart. Even a single computer, with enough peripherals, is a literal "network" -- a bunch of machines all cabled together, generally with a complexity that puts stereo units to shame. FCIC people invent and publicize methods of seizing computers and maintaining their evidence. Simple things, sometimes, but vital rules of thumb for street cops, who nowadays often stumble across a busy computer in the midst of a drug investigation or a white-collar bust. For instance: Photograph the system before you touch it. Label the ends of all the cables before you detach anything. "Park" the heads on the disk drives before you move them. Get the diskettes. Don't put the diskettes in magnetic fields. Don't write on diskettes with ballpoint pens. Get the manuals. Get the printouts. Get the handwritten notes. Copy data before you look at it, and then examine the copy instead of the original. Now our lecturer distributed copied diagrams of a typical LAN or "Local Area Network", which happened to be out of Connecticut. \*One hundred and fifty-nine\* desktop computers, each with its own peripherals. Three "file servers." Five "star couplers" each with thirty-two ports. One sixteenport coupler off in the corner office. All these machines talking to each other, distributing electronic mail, distributing software, distributing, quite possibly, criminal evidence. All linked by highcapacity fiber- optic cable. A bad guy -- cops talk a lot about "bad guys" -- might be lurking on PC #47 or #123 and distributing his ill doings onto some dupe's "personal" machine in another office -- or another floor -- or, quite possibly, two or three miles away! Or, conceivably, the evidence might be "data-striped" -- split up into meaningless slivers stored, one by one, on a whole crowd of different disk drives. The lecturer challenged us for solutions. I for one was utterly clueless. As far as I could figure, the Cossacks were at the gate; there were probably more disks in this single building than were seized during the entirety of Operation Sundevil. "Inside informant," somebody said. Right. There's always the human angle, something easy to forget when contemplating the arcane recesses of high technology. Cops are skilled at getting people to talk, and computer people, given a chair and some sustained attention, will talk about their computers till their throats go raw. There's a case on record of a single question -- "How'd you do it?" -eliciting a forty-five-minute videotaped confession from a computer criminal who not only completely incriminated himself but drew helpful diagrams. Computer people talk. Hackers \*brag.\* Phonephreaks talk \*pathologically\* -- why else are they stealing phone-codes, if not to natter for ten hours straight to their friends on an opposite seaboard? Computer-literate people do in fact possess an arsenal of nifty gadgets and techniques that would allow them to conceal all kinds of exotic skullduggery, and if they could only \*shut up\* about it, they could probably get away with all manner of amazing information-crimes. But that's just not how it works -- or at least, that's not how it's worked \*so far.\* Most every phone-phreak ever busted has swiftly implicated his mentors, his disciples, and his friends. Most every white-collar computer-criminal, smugly convinced that his clever scheme is bulletproof, swiftly learns otherwise when, for the first time in his life, an actual no-kidding policeman leans over, grabs the front of his shirt, looks him right in the eye and says: "All right, \*asshole\* -- you and me are going downtown!" All the hardware in the world will not insulate your nerves from these actual real-life sensations of terror and guilt. Cops know ways to get from point A to point Z without thumbing through every letter in some smart-ass bad-guy's alphabet. Cops know how to cut to the chase. Cops know a lot of things other people don't know. Hackers know a lot of things other people don't know, too. Hackers know, for instance, how to sneak into your computer through the phone-lines. But cops can show up \*right on your doorstep\* and carry off \*you\* and your computer in separate steel boxes. A cop interested in hackers can grab them and grill them. A hacker interested in cops has to depend on hearsay, underground legends, and what cops are willing to publicly reveal. And the Secret Service didn't get named "the \*Secret\* Service" because they blab a lot. Some people, our lecturer informed us, were under the mistaken impression that it was "impossible" to tap a fiber-optic line. Well, he announced, he and his son had just whipped up a fiber-optic tap in his workshop at home. He passed it around the audience, along with a circuit-covered LAN plug-in card so we'd all recognize one if we saw it on a case. We all had a look. The tap was a classic "Goofy Prototype" -- a thumb-length rounded metal cylinder with a pair of plastic brackets on it. From one end dangled three thin black cables, each of which ended in a tiny black plastic cap. When you plucked the safety-cap off the end of a cable, you could see the glass fiber - no thicker than a pinhole. Our lecturer informed us that the metal cylinder was a "wavelength division multiplexer." Apparently, what one did was to cut the fiber-optic cable, insert two of the legs into the cut to complete the network again, and then read any passing data on the line by hooking up the third leg to some kind of monitor. Sounded simple enough. I wondered why nobody had thought of it before. I also wondered whether this guy's son back at the workshop had any teenage friends. We had a break. The guy sitting next to me was wearing a giveaway baseball cap advertising the Uzi submachine gun. We had a desultory chat about the merits of Uzis. Long a favorite of the Secret Service, it seems Uzis went out of fashion with the advent of the Persian Gulf War, our Arab allies taking some offense at Americans toting Israeli weapons. Besides, I was informed by another expert, Uzis jam. The equivalent weapon of choice today is the Heckler & Koch, manufactured in Germany. The guy with the Uzi cap was a forensic photographer. He also did a lot of photographic surveillance work in computer crime cases. He used to, that is, until the firings in Phoenix. He was now a private investigator and, with his wife, ran a photography salon specializing in weddings and portrait photos. At - - one must repeat -- a considerable rise in income. He was still FCIC. If you were FCIC, and you needed to talk to an expert about forensic photography, well, there he was, willing and able. If he hadn't shown up, people would have missed him. Our lecturer had raised the point that preliminary investigation of a computer system is vital before any seizure is undertaken. It's vital to understand how many machines are in there, what kinds there are, what kind of operating system they use, how many people use them, where the actual data itself is stored. To simply barge into an office demanding "all the computers" is a recipe for swift disaster. This entails some discreet inquiries beforehand. In fact, what it entails is basically undercover work. An intelligence operation. \*Spying,\* not to put too fine a point on it. In a chat after the lecture, I asked an attendee whether "trashing" might work. I received a swift briefing on the theory and practice of "trash covers." Police "trash covers," like "mail covers" or like wiretaps, require the agreement of a judge. This obtained, the "trashing" work of cops is just like that of hackers, only more so and much better organized. So much so, I was informed, that mobsters in Phoenix make extensive use of locked garbage cans picked up by a specialty high-security trash company. In one case, a tiger team of Arizona cops had trashed a local residence for four months. Every week they showed up on the municipal garbage truck, disguised as garbagemen, and carried the contents of the suspect cans off to a shade tree, where they combed through the garbage -- a messy task, especially considering that one of the occupants was undergoing kidney dialysis. All useful documents were cleaned, dried and examined. A discarded typewriter-ribbon was an especially valuable source of data, as its long onestrike ribbon of film contained the contents of every letter mailed out of the house. The letters were neatly retyped by a police secretary equipped with a large desk-mounted magnifying glass. There is something weirdly disquieting about the whole subject of "trashing" -- an unsuspected and indeed rather disgusting mode of deep personal vulnerability. Things that we pass by every day, that we take utterly for granted, can be exploited with so little work. Once discovered, the knowledge of these vulnerabilities tend to spread. Take the lowly subject of \*manhole covers.\* The humble manhole cover reproduces many of the dilemmas of computer-security in miniature. Manhole covers are, of course, technological artifacts, access-points to our buried urban infrastructure. To the vast majority of us, manhole covers are invisible. They are also vulnerable. For many years now, the Secret Service has made a point of caulking manhole covers along all routes of the Presidential motorcade. This is, of course, to deter terrorists from leaping out of underground ambush or, more likely, planting remote-control carsmashing bombs beneath the street. Lately, manhole covers have seen more and more criminal exploitation, especially in New York City. Recently, a telco in New York City discovered that a cable television service had been sneaking into telco manholes and installing cable service alongside the phone-lines -- \*without paying royalties.\* New York companies have also suffered a general plague of (a) underground copper cable theft; (b) dumping of garbage, including toxic waste, and (c) hasty dumping of murder victims. Industry complaints reached the ears of an innovative New England industrial-security company, and the result was a new product known as "the Intimidator," a thick titanium-steel bolt with a precisely machined head that requires a special device to unscrew. All these "keys" have registered serial numbers kept on file with the manufacturer. There are now some thousands of these "Intimidator" bolts being sunk into American pavements wherever our President passes, like some macabre parody of strewn roses. They are also spreading as fast as steel dandelions around US military bases and many centers of private industry. Quite likely it has never occurred to you to peer under a manhole cover, perhaps climb down and walk around down there with a flashlight, just to see what it's like. Formally speaking, this might be trespassing, but if you didn't hurt anything, and didn't make an absolute habit of it, nobody would really care. The freedom to sneak under manholes was likely a freedom you never intended to exercise. You now are rather less likely to have that freedom at all. You may never even have missed it until you read about it here, but if you're in New York City it's gone, and elsewhere it's likely going. This is one of the things that crime, and the reaction to crime, does to us. The tenor of the meeting now changed as the Electronic Frontier Foundation arrived. The EFF, whose personnel and history will be examined in detail in the next chapter, are a pioneering civil liberties group who arose in direct response to the Hacker Crackdown of 1990. Now Mitchell Kapor, the Foundation's president, and Michael Godwin, its chief attorney, were confronting federal law enforcement \*mano a mano\* for the first time ever. Ever alert to the manifold uses of publicity, Mitch Kapor and Mike Godwin had brought their own journalist in tow: Robert Draper, from Austin, whose recent wellreceived book about ROLLING STONE magazine was still on the stands. Draper was on assignment for TEXAS MONTHLY. The Steve Jackson/EFF civil lawsuit against the Chicago Computer Fraud and Abuse Task Force was a matter of considerable regional interest in Texas. There were now two Austinite journalists here on the case. In fact, counting Godwin (a former Austinite and former journalist) there were three of us. Lunch was like Old Home Week. Later, I took Draper up to my hotel room. We had a long frank talk about the case, networking earnestly like a miniature freelance-journo version of the FCIC: privately confessing the numerous blunders of journalists covering the story, and trying hard to figure out who was who and what the hell was really going on out there. I showed Draper everything I had dug out of the Hilton trashcan. We pondered the ethics of "trashing" for a while, and agreed that they were dismal. We also agreed that finding a SPRINT bill on your first time out was a heck of a coincidence. First I'd "trashed" -- and now, mere hours later, I'd bragged to someone else. Having entered the lifestyle of hackerdom, I was now, unsurprisingly, following its logic. Having discovered something remarkable through a surreptitious action, I of course \*had\* to "brag," and to drag the passing Draper into my iniquities. I felt I needed a witness. Otherwise nobody would have believed what I'd discovered.... Back at the meeting, Thackeray cordially, if rather tentatively, introduced Kapor and Godwin to her colleagues. Papers were distributed. Kapor took center stage. The brilliant Bostonian high-tech entrepreneur, normally the hawk in his own administration and quite an effective public speaker, seemed visibly nervous, and frankly admitted as much. He began by saying he consided computer-intrusion to be morally wrong, and that the EFF was not a "hacker defense fund," despite what had appeared in print. Kapor chatted a bit about the basic motivations of his group, emphasizing their good faith and willingness to listen and seek common ground with law enforcement -- when, er, possible. Then, at Godwin's urging, Kapor suddenly remarked that EFF's own Internet machine had been "hacked" recently, and that EFF did not consider this incident amusing. After this surprising confession, things began to loosen up quite rapidly. Soon Kapor was fielding questions, parrying objections, challenging definitions, and juggling paradigms with something akin to his usual gusto. Kapor seemed to score quite an effect with his shrewd and skeptical analysis of the merits of telco "Caller-ID" services. (On this topic, FCIC and EFF have never been at loggerheads, and have no particular established earthworks to defend.) Caller-ID has generally been promoted as a privacy service for consumers, a presentation Kapor described as a "smokescreen," the real point of Caller-ID being to \*allow corporate customers to build extensive commercial databases on everybody who phones or faxes them.\* Clearly, few people in the room had considered this possibility, except perhaps for two late- arrivals from US WEST RBOC security, who chuckled nervously. Mike Godwin then made an extensive presentation on "Civil Liberties Implications of Computer Searches and Seizures." Now, at last, we were getting to the real nitty-gritty here, real political horse-trading. The audience listened with close attention, angry mutters rising occasionally: "He's trying to teach us our jobs!" "We've been thinking about this for years! We think about these issues every day!" "If I didn't seize the works, I'd be sued by the guy's victims!" "I'm violating the law if I leave ten thousand disks full of illegal \*pirated software\* and \*stolen codes!\*" "It's our job to make sure people don't trash the Constitution -- we're the \*defenders\* of the Constitution!" "We seize stuff when we know it will be forfeited anyway as restitution for the victim!" "If it's forfeitable, then don't get a search warrant, get a forfeiture warrant," Godwin suggested coolly. He further remarked that most suspects in computer crime don't \*want\* to see their computers vanish out the door, headed God knew where, for who knows how long. They might not mind a search, even an extensive search, but they want their machines searched on-site. "Are they gonna feed us?" somebody asked sourly. "How about if you take copies of the data?" Godwin parried. "That'll never stand up in court." "Okay, you make copies, give \*them\* the copies, and take the originals." Hmmm. Godwin championed bulletin-board systems as repositories of First Amendment protected free speech. He complained that federal computercrime training manuals gave boards a bad press, suggesting that they are hotbeds of crime haunted by pedophiles and crooks, whereas the vast majority of the nation's thousands of boards are completely innocuous, and nowhere near so romantically suspicious. People who run boards violently resent it when their systems are seized, and their dozens (or hundreds) of users look on in abject horror. Their rights of free expression are cut short. Their right to associate with other people is infringed. And their privacy is violated as their private electronic mail becomes police property. Not a soul spoke up to defend the practice of seizing boards. The issue passed in chastened silence. Legal principles aside -- (and those principles cannot be settled without laws passed or court precedents) -- seizing bulletin boards has become public-relations poison for American computer police. And anyway, it's not entirely necessary. If you're a cop, you can get 'most everything you need from a pirate board, just by using an inside informant. Plenty of vigilantes -- well, \*concerned citizens\* -will inform police the moment they see a pirate board hit their area (and will tell the police all about it, in such technical detail, actually, that you kinda wish they'd shut up). They will happily supply police with extensive downloads or printouts. It's \*impossible\* to keep this fluid electronic information out of the hands of police. Some people in the electronic community become enraged at the prospect of cops "monitoring" bulletin boards. This does have touchy aspects, as Secret Service people in particular examine bulletin boards with some regularity. But to expect electronic police to be deaf dumb and blind in regard to this particular medium rather flies in the face of common sense. Police watch television, listen to radio, read newspapers and magazines; why should the new medium of boards be different? Cops can exercise the same access to electronic information as everybody else. As we have seen, quite a few computer police maintain \*their own\* bulletin boards, including anti-hacker "sting" boards, which have generally proven quite effective. As a final clincher, their Mountie friends in Canada (and colleagues in Ireland and Taiwan) don't have First Amendment or American constitutional restrictions, but they do have phone lines, and can call any bulletin board in America whenever they please. The same technological determinants that play into the hands of hackers, phone phreaks and software pirates can play into the hands of police. "Technological determinants" don't have \*any\* human allegiances. They're not black or white, or Establishment or Underground, or pro-or-anti anything. Godwin complained at length about what he called "the Clever Hobbyist hypothesis" -- the assumption that the "hacker" you're busting is clearly a technical genius, and must therefore by searched with extreme thoroughness. So: from the law's point of view, why risk missing anything? Take the works. Take the guy's computer. Take his books. Take his notebooks. Take the electronic drafts of his love letters. Take his Walkman. Take his wife's computer. Take his dad's computer. Take his kid sister's computer. Take his employer's computer. Take his compact disks -- they \*might\* be CD-ROM disks, cunningly disguised as pop music. Take his laser printer -- he might have hidden something vital in the printer's 5meg of memory. Take his software manuals and hardware documentation. Take his science-fiction novels and his simulationgaming books. Take his Nintendo Game-Boy and his Pac-Man arcade game. Take his answering machine, take his telephone out of the wall. Take anything remotely suspicious. Godwin pointed out that most "hackers" are not, in fact, clever genius hobbyists. Quite a few are crooks and grifters who don't have much in the way of technical sophistication; just some rule-of-thumb rip-off techniques. The same goes for most fifteenyear-olds who've downloaded a code-scanning program from a pirate board. There's no real need to seize everything in sight. It doesn't require an entire computer system and ten thousand disks to prove a case in court. What if the computer is the instrumentality of a crime? someone demanded. Godwin admitted quietly that the doctrine of seizing the instrumentality of a crime was pretty well established in the American legal system. The meeting broke up. Godwin and Kapor had to leave. Kapor was testifying next morning before the Massachusetts Department Of Public Utility, about ISDN narrowband wide-area networking. As soon as they were gone, Thackeray seemed elated. She had taken a great risk with this. Her colleagues had not, in fact, torn Kapor and Godwin's heads off. She was very proud of them, and told them so. "Did you hear what Godwin said about \*instrumentality of a crime?\*" she exulted, to nobody in particular. "Wow, that means \*Mitch isn't going to sue me." 7. === America's computer police are an interesting group. As a social phenomenon they are far more interesting, and far more important, than teenage phone phreaks and computer hackers. First, they're older and wiser; not dizzy hobbyists with leaky morals, but seasoned adult professionals with all the responsibilities of public service. And, unlike hackers, they possess not merely \*technical\* power alone, but heavy-duty legal and social authority. And, very interestingly, they are just as much at sea in cyberspace as everyone else. They are not happy about this. Police are authoritarian by nature, and prefer to obey rules and precedents. (Even those police who secretly enjoy a fast ride in rough territory will soberly disclaim any "cowboy" attitude.) But in cyberspace there \*are\* no rules and precedents. They are groundbreaking pioneers, Cyberspace Rangers, whether they like it or not. In my opinion, any teenager enthralled by computers, fascinated by the ins and outs of computer security, and attracted by the lure of specialized forms of knowledge and power, would do well to forget all about "hacking" and set his (or her) sights on becoming a fed. Feds can trump hackers at almost every single thing hackers do, including gathering intelligence, undercover disguise, trashing, phone-tapping, building dossiers, networking, and infiltrating computer systems -\*criminal\* computer systems. Secret Service agents know more about phreaking, coding and carding than most phreaks can find out in years, and when it comes to viruses, break-ins, software bombs and trojan horses, Feds have direct access to red-hot confidential information that is only vague rumor in the underground. And if it's an impressive public rep you're after, there are few people in the world who can be so chillingly impressive as a well-trained, well-armed United States Secret Service agent. Of course, a few personal sacrifices are necessary in order to obtain that power and knowledge. First, you'll have the galling discipline of belonging to a large organization; but the world of computer crime is still so small, and so amazingly fast-moving, that it will remain spectacularly fluid for years to come. The second sacrifice is that you'll have to give up ripping people off. This is not a great loss. Abstaining from the use of illegal drugs, also necessary, will be a boon to your health. A career in computer security is not a bad choice for a young man or woman today. The field will almost certainly expand drastically in years to come. If you are a teenager today, by the time you become a professional, the pioneers you have read about in this book will be the grand old men and women of the field, swamped by their many disciples and successors. Of course, some of them, like William P. Wood of the 1865 Secret Service, may well be mangled in the whirring machinery of legal controversy; but by the time you enter the computer-crime field, it may have stabilized somewhat, while remaining entertainingly challenging. But you can't just have a badge. You have to win it. First, there's the federal law enforcement training. And it's hard -- it's a challenge. A real challenge -- not for wimps and rodents. Every Secret Service agent must complete gruelling courses at the Federal Law Enforcement Training Center. (In fact, Secret Service agents are periodically re-trained during their entire careers.) In order to get a glimpse of what this might be like, I myself travelled to FLETC. 8. === The Federal Law Enforcement Training Center is a 1500-acre facility on Georgia's Atlantic coast. It's a milieu of marshgrass, seabirds, damp, clinging sea-breezes, palmettos, mosquitos, and bats. Until 1974, it was a Navy Air Base, and still features a working runway, and some WWII vintage blockhouses and officers' quarters. The Center has since benefitted by a forty-million-dollar retrofit, but there's still enough forest and swamp on the facility for the Border Patrol to put in tracking practice. As a town, "Glynco" scarcely exists. The nearest real town is Brunswick, a few miles down Highway 17, where I stayed at the aptly named Marshview Holiday Inn. I had Sunday dinner at a seafood restaurant called "Jinright's," where I feasted on deep-fried alligator tail. This local favorite was a heaped basket of bite-sized chunks of white, tender, almost fluffy reptile meat, steaming in a peppered batter crust. Alligator makes a culinary experience that's hard to forget, especially when liberally basted with homemade cocktail sauce from a Jinright squeeze-bottle. The crowded clientele were tourists, fishermen, local black folks in their Sunday best, and white Georgian locals who all seemed to bear an uncanny resemblance to Georgia humorist Lewis Grizzard. The 2,400 students from 75 federal agencies who make up the FLETC population scarcely seem to make a dent in the low-key local scene. The students look like tourists, and the teachers seem to have taken on much of the relaxed air of the Deep South. My host was Mr. Carlton Fitzpatrick, the Program Coordinator of the Financial Fraud Institute. Carlton Fitzpatrick is a mustached, sinewy, well-tanned Alabama native somewhere near his late forties, with a fondness for chewing tobacco, powerful computers, and salty, down-home homilies. We'd met before, at FCIC in Arizona. The Financial Fraud Institute is one of the nine divisions at FLETC. Besides Financial Fraud, there's Driver & Marine, Firearms, and Physical Training. These are specialized pursuits. There are also five general training divisions: Basic Training, Operations, Enforcement Techniques, Legal Division, and Behavioral Science. Somewhere in this curriculum is everything necessary to turn green college graduates into federal agents. First they're given ID cards. Then they get the rather miserable-looking blue coveralls known as "smurf suits." The trainees are assigned a barracks and a cafeteria, and immediately set on FLETC's bone- grinding physical training routine. Besides the obligatory daily jogging -- (the trainers run up danger flags beside the track when the humidity rises high enough to threaten heat stroke) - there's the Nautilus machines, the martial arts, the survival skills.... The eighteen federal agencies who maintain onsite academies at FLETC employ a wide variety of specialized law enforcement units, some of them rather arcane. There's Border Patrol, IRS Criminal Investigation Division, Park Service, Fish and Wildlife, Customs, Immigration, Secret Service and the Treasury's uniformed subdivisions.... If you're a federal cop and you don't work for the FBI, you train at FLETC. This includes people as apparently obscure as the agents of the Railroad Retirement Board Inspector General. Or the Tennessee Valley Authority Police, who are in fact federal police officers, and can and do arrest criminals on the federal property of the Tennessee Valley Authority. And then there are the computer-crime people. All sorts, all backgrounds. Mr. Fitzpatrick is not jealous of his specialized knowledge. Cops all over, in every branch of service, may feel a need to learn what he can teach. Backgrounds don't matter much. Fitzpatrick himself was originally a Border Patrol veteran, then became a Border Patrol instructor at FLETC. His Spanish is still fluent -- but he found himself strangely fascinated when the first computers showed up at the Training Center. Fitzpatrick did have a background in electrical engineering, and though he never considered himself a computer hacker, he somehow found himself writing useful little programs for this new and promising gizmo. He began looking into the general subject of computers and crime, reading Donn Parker's books and articles, keeping an ear cocked for war stories, useful insights from the field, the up-and-coming people of the local computer- crime and hightechnology units.... Soon he got a reputation around FLETC as the resident "computer expert," and that reputation alone brought him more exposure, more experience -- until one day he looked around, and sure enough he \*was\* a federal computer-crime expert. In fact, this unassuming, genial man may be \*the\* federal computer- crime expert. There are plenty of very good computer people, and plenty of very good federal investigators, but the area where these worlds of expertise overlap is very slim. And Carlton Fitzpatrick has been right at the center of that since 1985, the first year of the Colluquy, a group which owes much to his influence. He seems quite at home in his modest, acoustic-tiled office, with its Ansel Adams-style Western photographic art, a gold-framed Senior Instructor Certificate, and a towering bookcase crammed with three-ring binders with ominous titles such as \*Datapro Reports on Information Security\* and \*CFCA Telecom Security '90.\* The phone rings every ten minutes; colleagues show up at the door to chat about new developments in locksmithing or to shake their heads over the latest dismal developments in the BCCI global banking scandal. Carlton Fitzpatrick is a fount of computer-crime war-stories, related in an acerbic drawl. He tells me the colorful tale of a hacker caught in California some years back. He'd been raiding systems, typing code without a detectable break, for twenty, twenty-four, thirty-six hours straight. Not just logged on -- \*typing.\* Investigators were baffled. Nobody could do that. Didn't he have to go to the bathroom? Was it some kind of automatic keyboard-whacking device that could actually type code? A raid on the suspect's home revealed a situation of astonishing squalor. The hacker turned out to be a Pakistani computer-science student who had flunked out of a California university. He'd gone completely underground as an illegal electronic immigrant, and was selling stolen phoneservice to stay alive. The place was not merely messy and dirty, but in a state of psychotic disorder. Powered by some weird mix of culture shock, computer addiction, and amphetamines, the suspect had in fact been sitting in front of his computer for a day and a half straight, with snacks and drugs at hand on the edge of his desk and a chamber-pot under his chair. Word about stuff like this gets around in the hacker-tracker community. Carlton Fitzpatrick takes me for a guided tour by car around the FLETC grounds. One of our first sights is the biggest indoor firing range in the world. There are federal trainees in there, Fitzpatrick assures me politely, blasting away with a wide variety of automatic weapons: Uzis, Glocks, AK-47s.... He's willing to take me inside. I tell him I'm sure that's really interesting, but I'd rather see his computers. Carlton Fitzpatrick seems quite surprised and pleased. I'm apparently the first journalist he's ever seen who has turned down the shooting gallery in favor of microchips. Our next stop is a favorite with touring Congressmen: the three-mile long FLETC driving range. Here trainees of the Driver & Marine Division are taught high-speed pursuit skills, setting and breaking road-blocks, diplomatic security driving for VIP limousines.... A favorite FLETC pastime is to strap a passing Senator into the passenger seat beside a Driver & Marine trainer, hit a hundred miles an hour, then take it right into "the skid-pan," a section of greased track where two tons of Detroit iron can whip and spin like a hockey puck. Cars don't fare well at FLETC. First they're rifled again and again for search practice. Then they do 25,000 miles of high-speed pursuit training; they get about seventy miles per set of steel-belted radials. Then it's off to the skid pan, where sometimes they roll and tumble headlong in the grease. When they're sufficiently grease-stained, dented, and creaky, they're sent to the roadblock unit, where they're battered without pity. And finally then they're sacrificed to the Bureau of Alcohol, Tobacco and Firearms, whose trainees learn the ins and outs of car-bomb work by blowing them into smoking wreckage. There's a railroad box-car on the FLETC grounds, and a large grounded boat, and a propless plane; all training-grounds for searches. The plane sits forlornly on a patch of weedy tarmac next to an eerie blockhouse known as the "ninja compound," where anti-terrorism specialists practice hostage rescues. As I gaze on this creepy paragon of modern low-intensity warfare, my nerves are jangled by a sudden staccato outburst of automatic weapons fire, somewhere in the woods to my right. "Ninemillimeter," Fitzpatrick judges calmly. Even the eldritch ninja compound pales somewhat compared to the truly surreal area known as "the raid-houses." This is a street lined on both sides with nondescript concrete-block houses with flat pebbled roofs. They were once officers' quarters. Now they are training grounds. The first one to our left, Fitzpatrick tells me, has been specially adapted for computer search-and-seizure practice. Inside it has been wired for video from top to bottom, with eighteen pan-and-tilt remotely controlled videocams mounted on walls and in corners. Every movement of the trainee agent is recorded live by teachers, for later taped analysis. Wasted movements, hesitations, possibly lethal tactical mistakes -- all are gone over in detail. Perhaps the weirdest single aspect of this building is its front door, scarred and scuffed all along the bottom, from the repeated impact, day after day, of federal shoe-leather. Down at the far end of the row of raid-houses some people are practicing a murder. We drive by slowly as some very young and rather nervouslooking federal trainees interview a heavyset bald man on the raid-house lawn. Dealing with murder takes a lot of practice; first you have to learn to control your own instinctive disgust and panic, then you have to learn to control the reactions of a nerveshredded crowd of civilians, some of whom may have just lost a loved one, some of whom may be murderers -- quite possibly both at once. A dummy plays the corpse. The roles of the bereaved, the morbidly curious, and the homicidal are played, for pay, by local Georgians: waitresses, musicians, most anybody who needs to moonlight and can learn a script. These people, some of whom are FLETC regulars year after year, must surely have one of the strangest jobs in the world. Something about the scene: "normal" people in a weird situation, standing around talking in bright Georgia sunshine, unsuccessfully pretending that something dreadful has gone on, while a dummy lies inside on faked bloodstains.... While behind this weird masquerade, like a nested set of Russian dolls, are grim future realities of real death, real violence, real murders of real people, that these young agents will really investigate, many times during their careers.... Over and over.... Will those anticipated murders look like this, feel like this -- not as "real" as these amateur actors are trying to make it seem, but both as "real," and as numbingly unreal, as watching fake people standing around on a fake lawn? Something about this scene unhinges me. It seems nightmarish to me, Kafkaesque. I simply don't know how to take it; my head is turned around; I don't know whether to laugh, cry, or just shudder. When the tour is over, Carlton Fitzpatrick and I talk about computers. For the first time cyberspace seems like quite a comfortable place. It seems very real to me suddenly, a place where I know what I'm talking about, a place I'm used to. It's real. "Real." Whatever. Carlton Fitzpatrick is the only person I've met in cyberspace circles who is happy with his present equipment. He's got a 5 Meg RAM PC with a 112 meg hard disk; a 660 meg's on the way. He's got a Compaq 386 desktop, and a Zenith 386 laptop with 120 meg. Down the hall is a NEC Multi-Sync 2A with a CD-ROM drive and a 9600 baud modem with four com-lines. There's a training minicomputer, and a 10-meg local mini just for the Center, and a lab-full of student PC clones and half-a-dozen Macs or so. There's a Data General MV 2500 with 8 meg on board and a 370 meg disk. Fitzpatrick plans to run a UNIX board on the Data General when he's finished beta-testing the software for it, which he wrote himself. It'll have E- mail features, massive files on all manner of computer-crime and investigation procedures, and will follow the computer-security specifics of the Department of Defense "Orange Book." He thinks it will be the biggest BBS in the federal government. Will it have *Phrack* on it? I ask wryly. Sure, he tells me. *Phrack*, *TAP*, *Computer Underground Digest*, all that stuff. With proper disclaimers, of course. I ask him if he plans to be the sysop. Running a system that size is very time-consuming, and Fitzpatrick teaches two three-hour courses every day. No, he says seriously, FLETC has to get its money worth out of the instructors. He thinks he can get a local volunteer to do it, a high-school student. He says a bit more, something I think about an Eagle Scout law-enforcement liaison program, but my mind has rocketed off in disbelief. "You're going to put a \*teenager\* in charge of a federal security BBS?" I'm speechless. It hasn't escaped my notice that the FLETC Financial Fraud Institute is the \*ultimate\* hacker-trashing target; there is stuff in here, stuff of such utter and consummate cool by every standard of the digital underground.... I imagine the hackers of my acquaintance, fainting dead-away from forbidden- knowledge greed-fits, at the mere prospect of cracking the superultra top-secret computers used to train the Secret Service in computer-crime.... "Uhm, Carlton," I babble, "I'm sure he's a really nice kid and all, but that's a terrible temptation to set in front of somebody who's, you know, into computers and just starting out..." "Yeah," he says, "that did occur to me." For the first time I begin to suspect that he's pulling my leg. He seems proudest when he shows me an ongoing project called JICC, Joint Intelligence Control Council. It's based on the services provided by EPIC, the El Paso Intelligence Center, which supplies data and intelligence to the Drug Enforcement Administration, the Customs Service, the Coast Guard, and the state police of the four southern border states. Certain EPIC files can now be accessed by drug-enforcement police of Central America, South America and the Caribbean, who can also trade information among themselves. Using a telecom program called "White Hat," written by two brothers named Lopez from the Dominican Republic, police can now network internationally on inexpensive PCs. Carlton Fitzpatrick is teaching a class of drug-war agents from the Third World, and he's very proud of their progress. Perhaps soon the sophisticated smuggling networks of the Medellin Cartel will be matched by a sophisticated computer network of the Medellin Cartel's sworn enemies. They'll track boats, track contraband, track the international drug-lords who now leap over borders with great ease, defeating the police through the clever use of fragmented national jurisdictions. JICC and EPIC must remain beyond the scope of this book. They seem to me to be very large topics fraught with complications that I am not fit to judge. I do know, however, that the international, computer-assisted networking of police, across national boundaries, is something that Carlton Fitzpatrick considers very important, a harbinger of a desirable future. I also know that networks by their nature ignore physical boundaries. And I also know that where you put communications you put a community, and that when those communities become self-aware they will fight to preserve themselves and to expand their influence. I make no judgements whether this is good or bad. It's just cyberspace; it's just the way things are. I asked Carlton Fitzpatrick what advice he would have for a twenty-year- old who wanted to shine someday in the world of electronic law enforcement. He told me that the number one rule was simply not to be scared of computers. You don't need to be an obsessive "computer weenie," but you mustn't be buffaloed just because some machine looks fancy. The advantages computers give smart crooks are matched by the advantages they give smart cops. Cops in the future will have to enforce the law "with their heads, not their holsters." Today you can make good cases without ever leaving your office. In the future, cops who resist the computer revolution will never get far beyond walking a beat. I asked Carlton Fitzpatrick if he had some single message for the public; some single thing that he would most like the American public to know about his work. He thought about it while. "Yes," he said finally. "\*Tell\* me the rules, and I'll \*teach\* those rules!" He looked me straight in the eye. "I do the best that I can." ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_part4.txt0000600000175000017500000051670310327117674031264 0ustar madduckmadduckrestindex crumb: Hacker Part 4 /restindex ==================================== The Hacker Crackdown - Part Four ==================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html .. contents :: Part Four - In Parts Part Four: The Civil Libertarians ==================================== The story of the Hacker Crackdown, as we have followed it thus far, has been technological, subcultural, criminal and legal. The story of the Civil Libertarians, though it partakes of all those other aspects, is profoundly and thoroughly political. In 1990, the obscure, long- simmering struggle over the ownership and nature of cyberspace became loudly and irretrievably public. People from some of the oddest corners of American society suddenly found themselves public figures. Some of these people found this situation much more than they had ever bargained for. They backpedalled, and tried to retreat back to the mandarin obscurity of their cozy subcultural niches. This was generally to prove a mistake. But the civil libertarians seized the day in 1990. They found themselves organizing, propagandizing, podiumpounding, persuading, touring, negotiating, posing for publicity photos, submitting to interviews, squinting in the limelight as they tried a tentative, but growingly sophisticated, buck- and-wing upon the public stage. It's not hard to see why the civil libertarians should have this competitive advantage. The hackers of the digital underground are an hermetic elite. They find it hard to make any remotely convincing case for their actions in front of the general public. Actually, hackers roundly despise the "ignorant" public, and have never trusted the judgement of "the system." Hackers do propagandize, but only among themselves, mostly in giddy, badly spelled manifestos of class warfare, youth rebellion or naive techie utopianism. Hackers must strut and boast in order to establish and preserve their underground reputations. But if they speak out too loudly and publicly, they will break the fragile surface-tension of the underground, and they will be harrassed or arrested. Over the longer term, most hackers stumble, get busted, get betrayed, or simply give up. As a political force, the digital underground is hamstrung. The telcos, for their part, are an ivory tower under protracted seige. They have plenty of money with which to push their calculated public image, but they waste much energy and goodwill attacking one another with slanderous and demeaning ad campaigns. The telcos have suffered at the hands of politicians, and, like hackers, they don't trust the public's judgement. And this distrust may be well-founded. Should the general public of the high-tech 1990s come to understand its own best interests in telecommunications, that might well pose a grave threat to the specialized technical power and authority that the telcos have relished for over a century. The telcos do have strong advantages: loyal employees, specialized expertise, influence in the halls of power, tactical allies in law enforcement, and unbelievably vast amounts of money. But politically speaking, they lack genuine grassroots support; they simply don't seem to have many friends. Cops know a lot of things other people don't know. But cops willingly reveal only those aspects of their knowledge that they feel will meet their institutional purposes and further public order. Cops have respect, they have responsibilities, they have power in the streets and even power in the home, but cops don't do particularly well in limelight. When pressed, they will step out in the public gaze to threaten bad-guys, or to cajole prominent citizens, or perhaps to sternly lecture the naive and misguided. But then they go back within their time-honored fortress of the station-house, the courtroom and the rule-book. The electronic civil libertarians, however, have proven to be born political animals. They seemed to grasp very early on the postmodern truism that communication is power. Publicity is power. Soundbites are power. The ability to shove one's issue onto the public agenda -- and keep it there -- is power. Fame is power. Simple personal fluency and eloquence can be power, if you can somehow catch the public's eye and ear. The civil libertarians had no monopoly on "technical power" -- though they all owned computers, most were not particularly advanced computer experts. They had a good deal of money, but nowhere near the earthshaking wealth and the galaxy of resources possessed by telcos or federal agencies. They had no ability to arrest people. They carried out no phreak and hacker covert dirty-tricks. But they really knew how to network. Unlike the other groups in this book, the civil libertarians have operated very much in the open, more or less right in the public hurly-burly. They have lectured audiences galore and talked to countless journalists, and have learned to refine their spiels. They've kept the cameras clicking, kept those faxes humming, swapped that email, run those photocopiers on overtime, licked envelopes and spent small fortunes on airfare and long- distance. In an information society, this open, overt, obvious activity has proven to be a profound advantage. In 1990, the civil libertarians of cyberspace assembled out of nowhere in particular, at warp speed. This "group" (actually, a networking gaggle of interested parties which scarcely deserves even that loose term) has almost nothing in the way of formal organization. Those formal civil libertarian organizations which did take an interest in cyberspace issues, mainly the Computer Professionals for Social Responsibility and the American Civil Liberties Union, were carried along by events in 1990, and acted mostly as adjuncts, underwriters or launching- pads. The civil libertarians nevertheless enjoyed the greatest success of any of the groups in the Crackdown of 1990. At this writing, their future looks rosy and the political initiative is firmly in their hands. This should be kept in mind as we study the highly unlikely lives and lifestyles of the people who actually made this happen. 1. === In June 1989, Apple Computer, Inc., of Cupertino, California, had a problem. Someone had illicitly copied a small piece of Apple's proprietary software, software which controlled an internal chip driving the Macintosh screen display. This Color QuickDraw source code was a closely guarded piece of Apple's intellectual property. Only trusted Apple insiders were supposed to possess it. But the "NuPrometheus League" wanted things otherwise. This person (or persons) made several illicit copies of this source code, perhaps as many as two dozen. He (or she, or they) then put those illicit floppy disks into envelopes and mailed them to people all over America: people in the computer industry who were associated with, but not directly employed by, Apple Computer. The NuPrometheus caper was a complex, highly ideological, and very hacker-like crime. Prometheus, it will be recalled, stole the fire of the Gods and gave this potent gift to the general ranks of downtrodden mankind. A similar god-in-the-manger attitude was implied for the corporate elite of Apple Computer, while the "Nu" Prometheus had himself cast in the role of rebel demigod. The illicitly copied data was given away for free. The new Prometheus, whoever he was, escaped the fate of the ancient Greek Prometheus, who was chained to a rock for centuries by the vengeful gods while an eagle tore and ate his liver. On the other hand, NuPrometheus chickened out somewhat by comparison with his role model. The small chunk of Color QuickDraw code he had filched and replicated was more or less useless to Apple's industrial rivals (or, in fact, to anyone else). Instead of giving fire to mankind, it was more as if NuPrometheus had photocopied the schematics for part of a Bic lighter. The act was not a genuine work of industrial espionage. It was best interpreted as a symbolic, deliberate slap in the face for the Apple corporate heirarchy. Apple's internal struggles were well-known in the industry. Apple's founders, Jobs and Wozniak, had both taken their leave long since. Their raucous core of senior employees had been a barnstorming crew of 1960s Californians, many of them markedly less than happy with the new button- down multimillion dollar regime at Apple. Many of the programmers and developers who had invented the Macintosh model in the early 1980s had also taken their leave of the company. It was they, not the current masters of Apple's corporate fate, who had invented the stolen Color QuickDraw code. The NuPrometheus stunt was well-calculated to wound company morale. Apple called the FBI. The Bureau takes an interest in high-profile intellectual-property theft cases, industrial espionage and theft of trade secrets. These were likely the right people to call, and rumor has it that the entities responsible were in fact discovered by the FBI, and then quietly squelched by Apple management. NuPrometheus was never publicly charged with a crime, or prosecuted, or jailed. But there were no further illicit releases of Macintosh internal software. Eventually the painful issue of NuPrometheus was allowed to fade. In the meantime, however, a large number of puzzled bystanders found themselves entertaining surprise guests from the FBI. One of these people was John Perry Barlow. Barlow is a most unusual man, difficult to describe in conventional terms. He is perhaps best known as a songwriter for the Grateful Dead, for he composed lyrics for "Hell in a Bucket," "Picasso Moon," "Mexicali Blues," "I Need a Miracle," and many more; he has been writing for the band since 1970. Before we tackle the vexing question as to why a rock lyricist should be interviewed by the FBI in a computercrime case, it might be well to say a word or two about the Grateful Dead. The Grateful Dead are perhaps the most successful and long-lasting of the numerous cultural emanations from the Haight-Ashbury district of San Francisco, in the glory days of Movement politics and lysergic transcendance. The Grateful Dead are a nexus, a veritable whirlwind, of applique decals, psychedelic vans, tie-dyed T-shirts, earth-color denim, frenzied dancing and open and unashamed drug use. The symbols, and the realities, of Californian freak power surround the Grateful Dead like knotted macrame. The Grateful Dead and their thousands of Deadhead devotees are radical Bohemians. This much is widely understood. Exactly what this implies in the 1990s is rather more problematic. The Grateful Dead are among the world's most popular and wealthy entertainers: number 20, according to *Forbes* magazine, right between M.C. Hammer and Sean Connery. In 1990, this jeans-clad group of purported raffish outcasts earned seventeen million dollars. They have been earning sums much along this line for quite some time now. And while the Dead are not investment bankers or three-piece-suit tax specialists -- they are, in point of fact, hippie musicians -- this money has not been squandered in senseless Bohemian excess. The Dead have been quietly active for many years, funding various worthy activities in their extensive and widespread cultural community. The Grateful Dead are not conventional players in the American power establishment. They nevertheless are something of a force to be reckoned with. They have a lot of money and a lot of friends in many places, both likely and unlikely. The Dead may be known for back-to-the-earth environmentalist rhetoric, but this hardly makes them anti-technological Luddites. On the contrary, like most rock musicians, the Grateful Dead have spent their entire adult lives in the company of complex electronic equipment. They have funds to burn on any sophisticated tool and toy that might happen to catch their fancy. And their fancy is quite extensive. The Deadhead community boasts any number of recording engineers, lighting experts, rock video mavens, electronic technicians of all descriptions. And the drift goes both ways. Steve Wozniak, Apple's co- founder, used to throw rock festivals. Silicon Valley rocks out. These are the 1990s, not the 1960s. Today, for a surprising number of people all over America, the supposed dividing line between Bohemian and technician simply no longer exists. People of this sort may have a set of windchimes and a dog with a knotted kerchief 'round its neck, but they're also quite likely to own a multimegabyte Macintosh running MIDI synthesizer software and trippy fractal simulations. These days, even Timothy Leary himself, prophet of LSD, does virtual-reality computer- graphics demos in his lecture tours. John Perry Barlow is not a member of the Grateful Dead. He is, however, a ranking Deadhead. Barlow describes himself as a "techno-crank." A vague term like "social activist" might not be far from the mark, either. But Barlow might be better described as a "poet" -- if one keeps in mind Percy Shelley's archaic definition of poets as "unacknowledged legislators of the world." Barlow once made a stab at acknowledged legislator status. In 1987, he narrowly missed the Republican nomination for a seat in the Wyoming State Senate. Barlow is a Wyoming native, the third-generation scion of a well-to-do cattle-ranching family. He is in his early forties, married and the father of three daughters. Barlow is not much troubled by other people's narrow notions of consistency. In the late 1980s, this Republican rock lyricist cattle rancher sold his ranch and became a computer telecommunications devotee. The free-spirited Barlow made this transition with ease. He genuinely enjoyed computers. With a beep of his modem, he leapt from small-town Pinedale, Wyoming, into electronic contact with a large and lively crowd of bright, inventive, technological sophisticates from all over the world. Barlow found the social milieu of computing attractive: its fast- lane pace, its blue-sky rhetoric, its open- endedness. Barlow began dabbling in computer journalism, with marked success, as he was a quick study, and both shrewd and eloquent. He frequently travelled to San Francisco to network with Deadhead friends. There Barlow made extensive contacts throughout the Californian computer community, including friendships among the wilder spirits at Apple. In May 1990, Barlow received a visit from a local Wyoming agent of the FBI. The NuPrometheus case had reached Wyoming. Barlow was troubled to find himself under investigation in an area of his interests once quite free of federal attention. He had to struggle to explain the very nature of computer-crime to a headscratching local FBI man who specialized in cattle-rustling. Barlow, chatting helpfully and demonstrating the wonders of his modem to the puzzled fed, was alarmed to find all "hackers" generally under FBI suspicion as an evil influence in the electronic community. The FBI, in pursuit of a hacker called "NuPrometheus," were tracing attendees of a suspect group called the Hackers Conference. The Hackers Conference, which had been started in 1984, was a yearly Californian meeting of digital pioneers and enthusiasts. The hackers of the Hackers Conference had little if anything to do with the hackers of the digital underground. On the contrary, the hackers of this conference were mostly well-to-do Californian high-tech CEOs, consultants, journalists and entrepreneurs. (This group of hackers were the exact sort of "hackers" most likely to react with militant fury at any criminal degradation of the term "hacker.") Barlow, though he was not arrested or accused of a crime, and though his computer had certainly not gone out the door, was very troubled by this anomaly. He carried the word to the Well. Like the Hackers Conference, "the Well" was an emanation of the Point Foundation. Point Foundation, the inspiration of a wealthy Californian 60s radical named Stewart Brand, was to be a major launch-pad of the civil libertarian effort. Point Foundation's cultural efforts, like those of their fellow Bay Area Californians the Grateful Dead, were multifaceted and multitudinous. Rigid ideological consistency had never been a strong suit of the *Whole Earth Catalog*. This Point publication had enjoyed a strong vogue during the late 60s and early 70s, when it offered hundreds of practical (and not so practical) tips on communitarian living, environmentalism, and getting back-to-the-land. The *Whole Earth Catalog*, and its sequels, sold two and half million copies and won a National Book Award. With the slow collapse of American radical dissent, the *Whole Earth Catalog* had slipped to a more modest corner of the cultural radar; but in its magazine incarnation, *CoEvolution Quarterly*, the Point Foundation continued to offer a magpie potpourri of "access to tools and ideas." *CoEvolution Quarterly*, which started in 1974, was never a widely popular magazine. Despite periodic outbreaks of millenarian fervor, *CoEvolution Quarterly* failed to revolutionize Western civilization and replace leaden centuries of history with bright new Californian paradigms. Instead, this propaganda arm of Point Foundation cakewalked a fine line between impressive brilliance and New Age flakiness. *CoEvolution Quarterly* carried no advertising, cost a lot, and came out on cheap newsprint with modest black-and-white graphics. It was poorly distributed, and spread mostly by subscription and word of mouth. It could not seem to grow beyond 30,000 subscribers. And yet -- it never seemed to shrink much, either. Year in, year out, decade in, decade out, some strange demographic minority accreted to support the magazine. The enthusiastic readership did not seem to have much in the way of coherent politics or ideals. It was sometimes hard to understand what held them together (if the often bitter debate in the letter-columns could be described as "togetherness"). But if the magazine did not flourish, it was resilient; it got by. Then, in 1984, the birth-year of the Macintosh computer, *CoEvolution Quarterly* suddenly hit the rapids. Point Foundation had discovered the computer revolution. Out came the *Whole Earth Software Catalog* of 1984, arousing headscratching doubts among the tiedyed faithful, and rabid enthusiasm among the nascent "cyberpunk" milieu, present company included. Point Foundation started its yearly Hackers Conference, and began to take an extensive interest in the strange new possibilities of digital counterculture. *CoEvolution Quarterly* folded its teepee, replaced by *Whole Earth Software Review* and eventually by *Whole Earth Review* (the magazine's present incarnation, currently under the editorship of virtual-reality maven Howard Rheingold). 1985 saw the birth of the "WELL" -- the "Whole Earth 'Lectronic Link." The Well was Point Foundation's bulletin board system. As boards went, the Well was an anomaly from the beginning, and remained one. It was local to San Francisco. It was huge, with multiple phonelines and enormous files of commentary. Its complex UNIX-based software might be most charitably described as "useropaque." It was run on a mainframe out of the rambling offices of a non-profit cultural foundation in Sausalito. And it was crammed with fans of the Grateful Dead. Though the Well was peopled by chattering hipsters of the Bay Area counterculture, it was by no means a "digital underground" board. Teenagers were fairly scarce; most Well users (known as "Wellbeings") were thirty- and forty-something Baby Boomers. They tended to work in the information industry: hardware, software, telecommunications, media, entertainment. Librarians, academics, and journalists were especially common on the Well, attracted by Point Foundation's open-handed distribution of "tools and ideas." There were no anarchy files on the Well, scarcely a dropped hint about access codes or credit-card theft. No one used handles. Vicious "flame-wars" were held to a comparatively civilized rumble. Debates were sometimes sharp, but no Wellbeing ever claimed that a rival had disconnected his phone, trashed his house, or posted his credit card numbers. The Well grew slowly as the 1980s advanced. It charged a modest sum for access and storage, and lost money for years -- but not enough to hamper the Point Foundation, which was nonprofit anyway. By 1990, the Well had about five thousand users. These users wandered about a gigantic cyberspace smorgasbord of "Conferences", each conference itself consisting of a welter of "topics," each topic containing dozens, sometimes hundreds of comments, in a tumbling, multiperson debate that could last for months or years on end. In 1991, the Well's list of conferences looked like this. :: CONFERENCES ON THE WELL WELL "Screenzine" Digest (g zine) Best of the WELL - vintage material - (g best) Index listing of new topics in all conferences - (g newtops) Business - Education ---------------------- Apple Library Users Group(g alug) Agriculture (g agri) Brainstorming (g brain) Classifieds (g cla) Computer Journalism (g cj) Consultants (g consult) Consumers (g cons) Design (g design) Desktop Publishing (g desk) Disability (g disability) Education (g ed) Energy (g energy91) Entrepreneurs (g entre) Homeowners (g home) Indexing (g indexing) Investments (g invest) Kids91 (g kids) Legal (g legal) One Person Business (g one) Periodical/newsletter(g per) Telecomm Law (g tcl) The Future (g fut) Translators (g trans) Travel (g tra) Work (g work) Electronic Frontier Foundation (g eff) Computers, Freedom & Privacy (g cfp) Computer Professionals for Social Responsibility (g cpsr) Social - Political - Humanities --------------------------------- Aging (g gray) AIDS (g aids) Amnesty International (g amnesty) Archives (g arc) Berkeley (g berk) Buddhist (g wonderland) Christian (g cross) Couples (g couples) Current Events (g curr) Dreams (g dream) Drugs (g dru) East Coast (g east) Emotional Health**** (g private) Erotica (g eros) Environment (g env) Firearms (g firearms) First Amendment (g first) Fringes of Reason (g fringes) Gay (g gay) Gay (Private)# (g gaypriv) Geography (g geo) German (g german) Gulf War (g gulf) Hawaii (g aloha) Health (g heal) History (g hist) Holistic (g holi) Interview (g inter) Italian (g ital) Jewish (g jew) Liberty (g liberty) Mind (g mind) Miscellaneous (g misc) Men on the WELL** (g mow) Network Integration (g origin) Nonprofits (g non) North Bay (g north) Northwest (g nw) Pacific Rim (g pacrim) Parenting (g par) Peace (g pea) Peninsula (g pen) Poetry (g poetry) Philosophy (g phi) Politics (g pol) Psychology (g psy) Psychotherapy (g therapy) Recovery## (g recovery) San Francisco (g sanfran) Scams (g scam) Sexuality (g sex) Singles (g singles) Southern (g south) Spanish (g spanish) Spirituality (g spirit) Tibet (g tibet) Transportation (g transport) True Confessions (g tru) Unclear (g unclear) WELL Writer's Workshop***(g www) Whole Earth (g we) Women on the WELL*(g wow) Words (g words) Writers (g wri) **** Private Conference - mail wooly for entry ***Private conference - mail sonia for entry ** Private conference - mail flash for entry * Private conference - mail reva for entry # Private Conference - mail hudu for entry ## Private Conference - mail dhawk for entry Arts - Recreation - Entertainment ----------------------------------- ArtCom Electronic Net (g acen) Audio-Videophilia (g aud) Bicycles (g bike) Bay Area Tonight**(g bat) Boating (g wet) Books (g books) CD's (g cd) Comics (g comics) Cooking (g cook) Flying (g flying) Fun (g fun) Games (g games) Gardening (g gard) Kids (g kids) Nightowls* (g owl) Jokes (g jokes) MIDI (g midi) Movies (g movies) Motorcycling (g ride) Motoring (g car) Music (g mus) On Stage (g onstage) Pets (g pets) Radio (g rad) Restaurant (g rest) Science Fiction (g sf) Sports (g spo) Star Trek (g trek) Television (g tv) Theater (g theater) Weird (g weird) Zines/Factsheet Five(g f5) * Open from midnight to 6am ** Updated daily Grateful Dead ------------- Grateful Dead (g gd) Deadplan* (g dp) Deadlit (g deadlit) Feedback (g feedback) GD Hour (g gdh) Tapes (g tapes) Tickets (g tix) Tours (g tours) * Private conference - mail tnf for entry Computers ----------- AI/Forth/Realtime (g realtime) Amiga (g amiga) Apple (g app) Computer Books (g cbook) Art & Graphics (g gra) Hacking (g hack) HyperCard (g hype) IBM PC (g ibm) LANs (g lan) Laptop (g lap) Macintosh (g mac) Mactech (g mactech) Microtimes (g microx) Muchomedia (g mucho) NeXt (g next) OS/2 (g os2) Printers (g print) Programmer's Net (g net) Siggraph (g siggraph) Software Design (g sdc) Software/Programming (software) Software Support (g ssc) Unix (g unix) Windows (g windows) Word Processing (g word) Technical - Communications ---------------------------- Bioinfo (g bioinfo) Info (g boing) Media (g media) NAPLPS (g naplps) Netweaver (g netweaver) Networld (g networld) Packet Radio (g packet) Photography (g pho) Radio (g rad) Science (g science) Technical Writers (g tec) Telecommunications(g tele) Usenet (g usenet) Video (g vid) Virtual Reality (g vr) The WELL Itself --------------- Deeper (g deeper) Entry (g ent) General (g gentech) Help (g help) Hosts (g hosts) Policy (g policy) System News (g news) Test (g test) The list itself is dazzling, bringing to the untutored eye a dizzying impression of a bizarre milieu of mountain- climbing Hawaiian holistic photographers trading true-life confessions with bisexual word-processing Tibetans. But this confusion is more apparent than real. Each of these conferences was a little cyberspace world in itself, comprising dozens and perhaps hundreds of sub-topics. Each conference was commonly frequented by a fairly small, fairly like-minded community of perhaps a few dozen people. It was humanly impossible to encompass the entire Well (especially since access to the Well's mainframe computer was billed by the hour). Most longtime users contented themselves with a few favorite topical neighborhoods, with the occasional foray elsewhere for a taste of exotica. But especially important news items, and hot topical debates, could catch the attention of the entire Well community. Like any community, the Well had its celebrities, and John Perry Barlow, the silver- tongued and silver- modemed lyricist of the Grateful Dead, ranked prominently among them. It was here on the Well that Barlow posted his true-life tale of computer-crime encounter with the FBI. The story, as might be expected, created a great stir. The Well was already primed for hacker controversy. In December 1989, *Harper's* magazine had hosted a debate on the Well about the ethics of illicit computer intrusion. While over forty various computer-mavens took part, Barlow proved a star in the debate. So did "Acid Phreak" and "Phiber Optik," a pair of young New York hacker-phreaks whose skills at telco switching-station intrusion were matched only by their apparently limitless hunger for fame. The advent of these two boldly swaggering outlaws in the precincts of the Well created a sensation akin to that of Black Panthers at a cocktail party for the radically chic. Phiber Optik in particular was to seize the day in 1990. A devotee of the *2600* circle and stalwart of the New York hackers' group "Masters of Deception," Phiber Optik was a splendid exemplar of the computer intruder as committed dissident. The eighteen- year-old Optik, a high-school dropout and part-time computer repairman, was young, smart, and ruthlessly obsessive, a sharpdressing, sharp-talking digital dude who was utterly and airily contemptuous of anyone's rules but his own. By late 1991, Phiber Optik had appeared in *Harper's*, *Esquire*, *The New York Times*, in countless public debates and conventions, even on a television show hosted by Geraldo Rivera. Treated with gingerly respect by Barlow and other Well mavens, Phiber Optik swiftly became a Well celebrity. Strangely, despite his thorny attitude and utter single-mindedness, Phiber Optik seemed to arouse strong protective instincts in most of the people who met him. He was great copy for journalists, always fearlessly ready to swagger, and, better yet, to actually demonstrate some off-the-wall digital stunt. He was a born media darling. Even cops seemed to recognize that there was something peculiarly unworldly and uncriminal about this particular troublemaker. He was so bold, so flagrant, so young, and so obviously doomed, that even those who strongly disapproved of his actions grew anxious for his welfare, and began to flutter about him as if he were an endangered seal pup. In January 24, 1990 (nine days after the Martin Luther King Day Crash), Phiber Optik, Acid Phreak, and a third NYC scofflaw named Scorpion were raided by the Secret Service. Their computers went out the door, along with the usual blizzard of papers, notebooks, compact disks, answering machines, Sony Walkmans, etc. Both Acid Phreak and Phiber Optik were accused of having caused the Crash. The mills of justice ground slowly. The case eventually fell into the hands of the New York State Police. Phiber had lost his machinery in the raid, but there were no charges filed against him for over a year. His predicament was extensively publicized on the Well, where it caused much resentment for police tactics. It's one thing to merely hear about a hacker raided or busted; it's another to see the police attacking someone you've come to know personally, and who has explained his motives at length. Through the *Harper's* debate on the Well, it had become clear to the Wellbeings that Phiber Optik was not in fact going to "hurt anything." In their own salad days, many Wellbeings had tasted tear-gas in pitched street- battles with police. They were inclined to indulgence for acts of civil disobedience. Wellbeings were also startled to learn of the draconian thoroughness of a typical hacker search-andseizure. It took no great stretch of imagination for them to envision themselves suffering much the same treatment. As early as January 1990, sentiment on the Well had already begun to sour, and people had begun to grumble that "hackers" were getting a raw deal from the hamhanded powers-that-be. The resultant issue of *Harper's* magazine posed the question as to whether computerintrusion was a "crime" at all. As Barlow put it later: "I've begun to wonder if we wouldn't also regard spelunkers as desperate criminals if AT&T owned all the caves." In February 1991, more than a year after the raid on his home, Phiber Optik was finally arrested, and was charged with first-degree Computer Tampering and Computer Trespass, New York state offenses. He was also charged with a theft-of-service misdemeanor, involving a complex free-call scam to a 900 number. Phiber Optik pled guilty to the misdemeanor charge, and was sentenced to 35 hours of community service. This passing harassment from the unfathomable world of straight people seemed to bother Optik himself little if at all. Deprived of his computer by the January search-and-seizure, he simply bought himself a portable computer so the cops could no longer monitor the phone where he lived with his Mom, and he went right on with his depredations, sometimes on live radio or in front of television cameras. The crackdown raid may have done little to dissuade Phiber Optik, but its galling affect on the Wellbeings was profound. As 1990 rolled on, the slings and arrows mounted: the Knight Lightning raid, the Steve Jackson raid, the nation-spanning Operation Sundevil. The rhetoric of law enforcement made it clear that there was, in fact, a concerted crackdown on hackers in progress. The hackers of the Hackers Conference, the Wellbeings, and their ilk, did not really mind the occasional public misapprehension of "hacking"; if anything, this membrane of differentiation from straight society made the "computer community" feel different, smarter, better. They had never before been confronted, however, by a concerted vilification campaign. Barlow's central role in the counter-struggle was one of the major anomalies of 1990. Journalists investigating the controversy often stumbled over the truth about Barlow, but they commonly dusted themselves off and hurried on as if nothing had happened. It was as if it were too much to believe that a 1960s freak from the Grateful Dead had taken on a federal law enforcement operation head-to-head and actually seemed to be winning! Barlow had no easily detectable power-base for a political struggle of this kind. He had no formal legal or technical credentials. Barlow was, however, a computer networker of truly stellar brilliance. He had a poet's gift of concise, colorful phrasing. He also had a journalist's shrewdness, an off-the-wall, self-deprecating wit, and a phenomenal wealth of simple personal charm. The kind of influence Barlow possessed is fairly common currency in literary, artistic, or musical circles. A gifted critic can wield great artistic influence simply through defining the temper of the times, by coining the catch-phrases and the terms of debate that become the common currency of the period. (And as it happened, Barlow was a part-time art critic, with a special fondness for the Western art of Frederic Remington.) Barlow was the first commentator to adopt William Gibson's striking science-fictional term "cyberspace" as a synonym for the present- day nexus of computer and telecommunications networks. Barlow was insistent that cyberspace should be regarded as a qualitatively new world, a "frontier." According to Barlow, the world of electronic communications, now made visible through the computer screen, could no longer be usefully regarded as just a tangle of high-tech wiring. Instead, it had become a place, cyberspace, which demanded a new set of metaphors, a new set of rules and behaviors. The term, as Barlow employed it, struck a useful chord, and this concept of cyberspace was picked up by *Time*, *Scientific American*, computer police, hackers, and even Constitutional scholars. "Cyberspace" now seems likely to become a permanent fixture of the language. Barlow was very striking in person: a tall, craggyfaced, bearded, deep-voiced Wyomingan in a dashing Western ensemble of jeans, jacket, cowboy boots, a knotted throat-kerchief and an ever-present Grateful Dead cloisonne lapel pin. Armed with a modem, however, Barlow was truly in his element. Formal hierarchies were not Barlow's strong suit; he rarely missed a chance to belittle the "large organizations and their drones," with their uptight, institutional mindset. Barlow was very much of the freespirit persuasion, deeply unimpressed by brass-hats and jacks-in-office. But when it came to the digital grapevine, Barlow was a cyberspace ad-hocrat par excellence. There was not a mighty army of Barlows. There was only one Barlow, and he was a fairly anomolous individual. However, the situation only seemed to require a single Barlow. In fact, after 1990, many people must have concluded that a single Barlow was far more than they'd ever bargained for. Barlow's querulous mini-essay about his encounter with the FBI struck a strong chord on the Well. A number of other free spirits on the fringes of Apple Computing had come under suspicion, and they liked it not one whit better than he did. One of these was Mitchell Kapor, the co-inventor of the spreadsheet program "Lotus 1-2-3" and the founder of Lotus Development Corporation. Kapor had written-off the passing indignity of being fingerprinted down at his own local Boston FBI headquarters, but Barlow's post made the full national scope of the FBI's dragnet clear to Kapor. The issue now had Kapor's full attention. As the Secret Service swung into anti-hacker operation nationwide in 1990, Kapor watched every move with deep skepticism and growing alarm. As it happened, Kapor had already met Barlow, who had interviewed Kapor for a California computer journal. Like most people who met Barlow, Kapor had been very taken with him. Now Kapor took it upon himself to drop in on Barlow for a heart-to-heart talk about the situation. Kapor was a regular on the Well. Kapor had been a devotee of the Whole Earth Catalog since the beginning, and treasured a complete run of the magazine. And Kapor not only had a modem, but a private jet. In pursuit of the scattered high-tech investments of Kapor Enterprises Inc., his personal, multi-million dollar holding company, Kapor commonly crossed state lines with about as much thought as one might give to faxing a letter. The Kapor-Barlow council of June 1990, in Pinedale, Wyoming, was the start of the Electronic Frontier Foundation. Barlow swiftly wrote a manifesto, "Crime and Puzzlement," which announced his, and Kapor's, intention to form a political organization to "raise and disburse funds for education, lobbying, and litigation in the areas relating to digital speech and the extension of the Constitution into Cyberspace." Furthermore, proclaimed the manifesto, the foundation would "fund, conduct, and support legal efforts to demonstrate that the Secret Service has exercised prior restraint on publications, limited free speech, conducted improper seizure of equipment and data, used undue force, and generally conducted itself in a fashion which is arbitrary, oppressive, and unconstitutional." "Crime and Puzzlement" was distributed far and wide through computer networking channels, and also printed in the Whole Earth Review. The sudden declaration of a coherent, politicized counter-strike from the ranks of hackerdom electrified the community. Steve Wozniak (perhaps a bit stung by the NuPrometheus scandal) swiftly offered to match any funds Kapor offered the Foundation. John Gilmore, one of the pioneers of Sun Microsystems, immediately offered his own extensive financial and personal support. Gilmore, an ardent libertarian, was to prove an eloquent advocate of electronic privacy issues, especially freedom from governmental and corporate computer-assisted surveillance of private citizens. A second meeting in San Francisco rounded up further allies: Stewart Brand of the Point Foundation, virtual-reality pioneers Jaron Lanier and Chuck Blanchard, network entrepreneur and venture capitalist Nat Goldhaber. At this dinner meeting, the activists settled on a formal title: the Electronic Frontier Foundation, Incorporated. Kapor became its president. A new EFF Conference was opened on the Point Foundation's Well, and the Well was declared "the home of the Electronic Frontier Foundation." Press coverage was immediate and intense. Like their nineteenth- century spiritual ancestors, Alexander Graham Bell and Thomas Watson, the high-tech computer entrepreneurs of the 1970s and 1980s -- people such as Wozniak, Jobs, Kapor, Gates, and H. Ross Perot, who had raised themselves by their bootstraps to dominate a glittering new industry -- had always made very good copy. But while the Wellbeings rejoiced, the press in general seemed nonplussed by the self-declared "civilizers of cyberspace." EFF's insistence that the war against "hackers" involved grave Constitutional civil liberties issues seemed somewhat farfetched, especially since none of EFF's organizers were lawyers or established politicians. The business press in particular found it easier to seize on the apparent core of the story -- that high-tech entrepreneur Mitchell Kapor had established a "defense fund for hackers." Was EFF a genuinely important political development -- or merely a clique of wealthy eccentrics, dabbling in matters better left to the proper authorities? The jury was still out. But the stage was now set for open confrontation. And the first and the most critical battle was the hacker show-trial of "Knight Lightning." 2. === It has been my practice throughout this book to refer to hackers only by their "handles." There is little to gain by giving the real names of these people, many of whom are juveniles, many of whom have never been convicted of any crime, and many of whom had unsuspecting parents who have already suffered enough. But the trial of Knight Lightning on July 24-27, 1990, made this particular "hacker" a nationally known public figure. It can do no particular harm to himself or his family if I repeat the long-established fact that his name is Craig Neidorf (pronounced NYE-dorf). Neidorf's jury trial took place in the United States District Court, Northern District of Illinois, Eastern Division, with the Honorable Nicholas J. Bua presiding. The United States of America was the plaintiff, the defendant Mr. Neidorf. The defendant's attorney was Sheldon T. Zenner of the Chicago firm of Katten, Muchin and Zavis. The prosecution was led by the stalwarts of the Chicago Computer Fraud and Abuse Task Force: William J. Cook, Colleen D. Coughlin, and David A. Glockner, all Assistant United States Attorneys. The Secret Service Case Agent was Timothy M. Foley. It will be recalled that Neidorf was the co-editor of an underground hacker "magazine" called Phrack. Phrack was an entirely electronic publication, distributed through bulletin boards and over electronic networks. It was amateur publication given away for free. Neidorf had never made any money for his work in Phrack. Neither had his unindicted co-editor "Taran King" or any of the numerous Phrack contributors. The Chicago Computer Fraud and Abuse Task Force, however, had decided to prosecute Neidorf as a fraudster. To formally admit that Phrack was a "magazine" and Neidorf a "publisher" was to open a prosecutorial Pandora's Box of First Amendment issues. To do this was to play into the hands of Zenner and his EFF advisers, which now included a phalanx of prominent New York civil rights lawyers as well as the formidable legal staff of Katten, Muchin and Zavis. Instead, the prosecution relied heavily on the issue of access device fraud: Section 1029 of Title 18, the section from which the Secret Service drew its most direct jurisdiction over computer crime. Neidorf's alleged crimes centered around the E911 Document. He was accused of having entered into a fraudulent scheme with the Prophet, who, it will be recalled, was the Atlanta LoD member who had illicitly copied the E911 Document from the BellSouth AIMSX system. The Prophet himself was also a co-defendant in the Neidorf case, part-and-parcel of the alleged "fraud scheme" to "steal" BellSouth's E911 Document (and to pass the Document across state lines, which helped establish the Neidorf trial as a federal case). The Prophet, in the spirit of full co-operation, had agreed to testify against Neidorf. In fact, all three of the Atlanta crew stood ready to testify against Neidorf. Their own federal prosecutors in Atlanta had charged the Atlanta Three with: (a) conspiracy, (b) computer fraud, (c) wire fraud, (d) access device fraud, and (e) interstate transportation of stolen property (Title 18, Sections 371, 1030, 1343, 1029, and 2314). Faced with this blizzard of trouble, Prophet and Leftist had ducked any public trial and had pled guilty to reduced charges -- one conspiracy count apiece. Urvile had pled guilty to that odd bit of Section 1029 which makes it illegal to possess "fifteen or more" illegal access devices (in his case, computer passwords). And their sentences were scheduled for September 14, 1990 -- well after the Neidorf trial. As witnesses, they could presumably be relied upon to behave. Neidorf, however, was pleading innocent. Most everyone else caught up in the crackdown had "cooperated fully" and pled guilty in hope of reduced sentences. (Steve Jackson was a notable exception, of course, and had strongly protested his innocence from the very beginning. But Steve Jackson could not get a day in court -- Steve Jackson had never been charged with any crime in the first place.) Neidorf had been urged to plead guilty. But Neidorf was a political science major and was disinclined to go to jail for "fraud" when he had not made any money, had not broken into any computer, and had been publishing a magazine that he considered protected under the First Amendment. Neidorf's trial was the only legal action of the entire Crackdown that actually involved bringing the issues at hand out for a public test in front of a jury of American citizens. Neidorf, too, had cooperated with investigators. He had voluntarily handed over much of the evidence that had led to his own indictment. He had already admitted in writing that he knew that the E911 Document had been stolen before he had "published" it in Phrack -- or, from the prosecution's point of view, illegally transported stolen property by wire in something purporting to be a "publication." But even if the "publication" of the E911 Document was not held to be a crime, that wouldn't let Neidorf off the hook. Neidorf had still received the E911 Document when Prophet had transferred it to him from Rich Andrews' Jolnet node. On that occasion, it certainly hadn't been "published" -- it was hacker booty, pure and simple, transported across state lines. The Chicago Task Force led a Chicago grand jury to indict Neidorf on a set of charges that could have put him in jail for thirty years. When some of these charges were successfully challenged before Neidorf actually went to trial, the Chicago Task Force rearranged his indictment so that he faced a possible jail term of over sixty years! As a first offender, it was very unlikely that Neidorf would in fact receive a sentence so drastic; but the Chicago Task Force clearly intended to see Neidorf put in prison, and his conspiratorial "magazine" put permanently out of commission. This was a federal case, and Neidorf was charged with the fraudulent theft of property worth almost eighty thousand dollars. William Cook was a strong believer in high-profile prosecutions with symbolic overtones. He often published articles on his work in the security trade press, arguing that "a clear message had to be sent to the public at large and the computer community in particular that unauthorized attacks on computers and the theft of computerized information would not be tolerated by the courts." The issues were complex, the prosecution's tactics somewhat unorthodox, but the Chicago Task Force had proved sure-footed to date. "Shadowhawk" had been bagged on the wing in 1989 by the Task Force, and sentenced to nine months in prison, and a $10,000 fine. The Shadowhawk case involved charges under Section 1030, the "federal interest computer" section. Shadowhawk had not in fact been a devotee of "federal-interest" computers per se. On the contrary, Shadowhawk, who owned an AT&T home computer, seemed to cherish a special aggression toward AT&T. He had bragged on the underground boards "Phreak Klass 2600" and "Dr. Ripco" of his skills at raiding AT&T, and of his intention to crash AT&T's national phone system. Shadowhawk's brags were noticed by Henry Kluepfel of Bellcore Security, scourge of the outlaw boards, whose relations with the Chicago Task Force were long and intimate. The Task Force successfully established that Section 1030 applied to the teenage Shadowhawk, despite the objections of his defense attorney. Shadowhawk had entered a computer "owned" by U.S. Missile Command and merely "managed" by AT&T. He had also entered an AT&T computer located at Robbins Air Force Base in Georgia. Attacking AT&T was of "federal interest" whether Shadowhawk had intended it or not. The Task Force also convinced the court that a piece of AT&T software that Shadowhawk had illicitly copied from Bell Labs, the "Artificial Intelligence C5 Expert System," was worth a cool one million dollars. Shadowhawk's attorney had argued that Shadowhawk had not sold the program and had made no profit from the illicit copying. And in point of fact, the C5 Expert System was experimental software, and had no established market value because it had never been on the market in the first place. AT&T's own assessment of a "one million dollar" figure for its own intangible property was accepted without challenge by the court, however. And the court concurred with the government prosecutors that Shadowhawk showed clear "intent to defraud" whether he'd gotten any money or not. Shadowhawk went to jail. The Task Force's other best-known triumph had been the conviction and jailing of "Kyrie." Kyrie, a true denizen of the digital criminal underground, was a 36-year-old Canadian woman, convicted and jailed for telecommunications fraud in Canada. After her release from prison, she had fled the wrath of Canada Bell and the Royal Canadian Mounted Police, and eventually settled, very unwisely, in Chicago. "Kyrie," who also called herself "Long Distance Information," specialized in voice-mail abuse. She assembled large numbers of hot long- distance codes, then read them aloud into a series of corporate voice-mail systems. Kyrie and her friends were electronic squatters in corporate voice- mail systems, using them much as if they were pirate bulletin boards, then moving on when their vocal chatter clogged the system and the owners necessarily wised up. Kyrie's camp followers were a loose tribe of some hundred and fifty phone-phreaks, who followed her trail of piracy from machine to machine, ardently begging for her services and expertise. Kyrie's disciples passed her stolen credit-card numbers, in exchange for her stolen "long distance information." Some of Kyrie's clients paid her off in cash, by scamming credit-card cash advances from Western Union. Kyrie travelled incessantly, mostly through airline tickets and hotel rooms that she scammed through stolen credit cards. Tiring of this, she found refuge with a fellow female phone phreak in Chicago. Kyrie's hostess, like a surprising number of phone phreaks, was blind. She was also physically disabled. Kyrie allegedly made the best of her new situation by applying for, and receiving, state welfare funds under a false identity as a qualified caretaker for the handicapped. Sadly, Kyrie's two children by a former marriage had also vanished underground with her; these pre-teen digital refugees had no legal American identity, and had never spent a day in school. Kyrie was addicted to technical mastery and enthralled by her own cleverness and the ardent worship of her teenage followers. This foolishly led her to phone up Gail Thackeray in Arizona, to boast, brag, strut, and offer to play informant. Thackeray, however, had already learned far more than enough about Kyrie, whom she roundly despised as an adult criminal corrupting minors, a "female Fagin." Thackeray passed her tapes of Kyrie's boasts to the Secret Service. Kyrie was raided and arrested in Chicago in May 1989. She confessed at great length and pled guilty. In August 1990, Cook and his Task Force colleague Colleen Coughlin sent Kyrie to jail for 27 months, for computer and telecommunications fraud. This was a markedly severe sentence by the usual wrist-slapping standards of "hacker" busts. Seven of Kyrie's foremost teenage disciples were also indicted and convicted. The Kyrie "high-tech street gang," as Cook described it, had been crushed. Cook and his colleagues had been the first ever to put someone in prison for voice-mail abuse. Their pioneering efforts had won them attention and kudos. In his article on Kyrie, Cook drove the message home to the readers of Security Management magazine, a trade journal for corporate security professionals. The case, Cook said, and Kyrie's stiff sentence, "reflect a new reality for hackers and computer crime victims in the '90s.... Individuals and corporations who report computer and telecommunications crimes can now expect that their cooperation with federal law enforcement will result in meaningful punishment. Companies and the public at large must report computer-enhanced crimes if they want prosecutors and the course to protect their rights to the tangible and intangible property developed and stored on computers." Cook had made it his business to construct this "new reality for hackers." He'd also made it his business to police corporate property rights to the intangible. Had the Electronic Frontier Foundation been a "hacker defense fund" as that term was generally understood, they presumably would have stood up for Kyrie. Her 1990 sentence did indeed send a "message" that federal heat was coming down on "hackers." But Kyrie found no defenders at EFF, or anywhere else, for that matter. EFF was not a bail-out fund for electronic crooks. The Neidorf case paralleled the Shadowhawk case in certain ways. The victim once again was allowed to set the value of the "stolen" property. Once again Kluepfel was both investigator and technical advisor. Once again no money had changed hands, but the "intent to defraud" was central. The prosecution's case showed signs of weakness early on. The Task Force had originally hoped to prove Neidorf the center of a nationwide Legion of Doom criminal conspiracy. The Phrack editors threw physical get-togethers every summer, which attracted hackers from across the country; generally two dozen or so of the magazine's favorite contributors and readers. (Such conventions were common in the hacker community; *2600* Magazine, for instance, held public meetings of hackers in New York, every month.) LoD heavy-dudes were always a strong presence at these *Phrack*-sponsored "Summercons." In July 1988, an Arizona hacker named "Dictator" attended Summercon in Neidorf's home town of St. Louis. Dictator was one of Gail Thackeray's underground informants; Dictator's underground board in Phoenix was a sting operation for the Secret Service. Dictator brought an undercover crew of Secret Service agents to Summercon. The agents bored spyholes through the wall of Dictator's hotel room in St Louis, and videotaped the frolicking hackers through a one-way mirror. As it happened, however, nothing illegal had occurred on videotape, other than the guzzling of beer by a couple of minors. Summercons were social events, not sinister cabals. The tapes showed fifteen hours of raucous laughter, pizza-gobbling, in-jokes and back-slapping. Neidorf's lawyer, Sheldon Zenner, saw the Secret Service tapes before the trial. Zenner was shocked by the complete harmlessness of this meeting, which Cook had earlier characterized as a sinister interstate conspiracy to commit fraud. Zenner wanted to show the Summercon tapes to the jury. It took protracted maneuverings by the Task Force to keep the tapes from the jury as "irrelevant." The E911 Document was also proving a weak reed. It had originally been valued at $79,449. Unlike Shadowhawk's arcane Artificial Intelligence booty, the E911 Document was not software -- it was written in English. Computer-knowledgeable people found this value -- for a twelve-page bureaucratic document -frankly incredible. In his "Crime and Puzzlement" manifesto for EFF, Barlow commented: "We will probably never know how this figure was reached or by whom, though I like to imagine an appraisal team consisting of Franz Kafka, Joseph Heller, and Thomas Pynchon." As it happened, Barlow was unduly pessimistic. The EFF did, in fact, eventually discover exactly how this figure was reached, and by whom -- but only in 1991, long after the Neidorf trial was over. Kim Megahee, a Southern Bell security manager, had arrived at the document's value by simply adding up the "costs associated with the production" of the E911 Document. Those "costs" were as follows: 1. A technical writer had been hired to research and write the E911 Document. 200 hours of work, at $35 an hour, cost : $7,000. A Project Manager had overseen the technical writer. 200 hours, at $31 an hour, made: $6,200. 2. A week of typing had cost $721 dollars. A week of formatting had cost $721. A week of graphics formatting had cost $742. 3. Two days of editing cost $367. ` 4. A box of order labels cost five dollars. 5. Preparing a purchase order for the Document, including typing and the obtaining of an authorizing signature from within the BellSouth bureaucracy, cost $129. 6. Printing cost $313. Mailing the Document to fifty people took fifty hours by a clerk, and cost $858. 7. Placing the Document in an index took two clerks an hour each, totalling $43. Bureaucratic overhead alone, therefore, was alleged to have cost a whopping $17,099. According to Mr. Megahee, the typing of a twelve- page document had taken a full week. Writing it had taken five weeks, including an overseer who apparently did nothing else but watch the author for five weeks. Editing twelve pages had taken two days. Printing and mailing an electronic document (which was already available on the Southern Bell Data Network to any telco employee who needed it), had cost over a thousand dollars. But this was just the beginning. There were also the hardware expenses. Eight hundred fifty dollars for a VT220 computer monitor. Thirty-one thousand dollars for a sophisticated VAXstation II computer. Six thousand dollars for a computer printer. Twenty-two thousand dollars for a copy of "Interleaf" software. Two thousand five hundred dollars for VMS software. All this to create the twelve-page Document. Plus ten percent of the cost of the software and the hardware, for maintenance. (Actually, the ten percent maintenance costs, though mentioned, had been left off the final $79,449 total, apparently through a merciful oversight). Mr. Megahee's letter had been mailed directly to William Cook himself, at the office of the Chicago federal attorneys. The United States Government accepted these telco figures without question. As incredulity mounted, the value of the E911 Document was officially revised downward. This time, Robert Kibler of BellSouth Security estimated the value of the twelve pages as a mere $24,639.05 -- based, purportedly, on "R&D costs." But this specific estimate, right down to the nickel, did not move the skeptics at all; in fact it provoked open scorn and a torrent of sarcasm. The financial issues concerning theft of proprietary information have always been peculiar. It could be argued that BellSouth had not "lost" its E911 Document at all in the first place, and therefore had not suffered any monetary damage from this "theft." And Sheldon Zenner did in fact argue this at Neidorf's trial -- that Prophet's raid had not been "theft," but was better understood as illicit copying. The money, however, was not central to anyone's true purposes in this trial. It was not Cook's strategy to convince the jury that the E911 Document was a major act of theft and should be punished for that reason alone. His strategy was to argue that the E911 Document was dangerous. It was his intention to establish that the E911 Document was "a road-map" to the Enhanced 911 System. Neidorf had deliberately and recklessly distributed a dangerous weapon. Neidorf and the Prophet did not care (or perhaps even gloated at the sinister idea) that the E911 Document could be used by hackers to disrupt 911 service, "a life line for every person certainly in the Southern Bell region of the United States, and indeed, in many communities throughout the United States," in Cook's own words. Neidorf had put people's lives in danger. In pre-trial maneuverings, Cook had established that the E911 Document was too hot to appear in the public proceedings of the Neidorf trial. The jury itself would not be allowed to ever see this Document, lest it slip into the official court records, and thus into the hands of the general public, and, thus, somehow, to malicious hackers who might lethally abuse it. Hiding the E911 Document from the jury may have been a clever legal maneuver, but it had a severe flaw. There were, in point of fact, hundreds, perhaps thousands, of people, already in possession of the E911 Document, just as *Phrack* had published it. Its true nature was already obvious to a wide section of the interested public (all of whom, by the way, were, at least theoretically, party to a gigantic wire-fraud conspiracy). Most everyone in the electronic community who had a modem and any interest in the Neidorf case already had a copy of the Document. It had already been available in *Phrack* for over a year. People, even quite normal people without any particular prurient interest in forbidden knowledge, did not shut their eyes in terror at the thought of beholding a "dangerous" document from a telephone company. On the contrary, they tended to trust their own judgement and simply read the Document for themselves. And they were not impressed. One such person was John Nagle. Nagle was a fortyone-year-old professional programmer with a masters' degree in computer science from Stanford. He had worked for Ford Aerospace, where he had invented a computer-networking technique known as the "Nagle Algorithm," and for the prominent Californian computergraphics firm "Autodesk," where he was a major stockholder. Nagle was also a prominent figure on the Well, much respected for his technical knowledgeability. Nagle had followed the civil-liberties debate closely, for he was an ardent telecommunicator. He was no particular friend of computer intruders, but he believed electronic publishing had a great deal to offer society at large, and attempts to restrain its growth, or to censor free electronic expression, strongly roused his ire. The Neidorf case, and the E911 Document, were both being discussed in detail on the Internet, in an electronic publication called Telecom Digest. Nagle, a longtime Internet maven, was a regular reader of Telecom Digest. Nagle had never seen a copy of Phrack, but the implications of the case disturbed him. While in a Stanford bookstore hunting books on robotics, Nagle happened across a book called The Intelligent Network. Thumbing through it at random, Nagle came across an entire chapter meticulously detailing the workings of E911 police emergency systems. This extensive text was being sold openly, and yet in Illinois a young man was in danger of going to prison for publishing a thin six-page document about 911 service. Nagle made an ironic comment to this effect in Telecom Digest. From there, Nagle was put in touch with Mitch Kapor, and then with Neidorf's lawyers. Sheldon Zenner was delighted to find a computer telecommunications expert willing to speak up for Neidorf, one who was not a wacky teenage "hacker." Nagle was fluent, mature, and respectable; he'd once had a federal security clearance. Nagle was asked to fly to Illinois to join the defense team. Having joined the defense as an expert witness, Nagle read the entire E911 Document for himself. He made his own judgement about its potential for menace. The time has now come for you yourself, the reader, to have a look at the E911 Document. This six-page piece of work was the pretext for a federal prosecution that could have sent an electronic publisher to prison for thirty, or even sixty, years. It was the pretext for the search and seizure of Steve Jackson Games, a legitimate publisher of printed books. It was also the formal pretext for the search and seizure of the Mentor's bulletin board, "Phoenix Project," and for the raid on the home of Erik Bloodaxe. It also had much to do with the seizure of Richard Andrews' Jolnet node and the shutdown of Charles Boykin's AT&T node. The E911 Document was the single most important piece of evidence in the Hacker Crackdown. There can be no real and legitimate substitute for the document itself. Phrack Inc. ------------ Volume Two, Issue 24, File 5 of 13 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Control Office Administration Of Enhanced 911 Services For Special Services and Account Centers by the Eavesdropper March, 1988 Description of Service ----------------------- The control office for Emergency 911 service is assigned in accordance with the existing standard guidelines to one of the following centers: * Special Services Center (SSC) * Major Accounts Center (MAC) * Serving Test Center (STC) * Toll Control Center (TCC) The SSC/MAC designation is used in this document interchangeably for any of these four centers. The Special Services Centers (SSCs) or Major Account Centers (MACs) have been designated as the trouble reporting contact for all E911 customer (PSAP) reported troubles. Subscribers who have trouble on an E911 call will continue to contact local repair service (CRSAB) who will refer the trouble to the SSC/MAC, when appropriate. Due to the critical nature of E911 service, the control and timely repair of troubles is demanded. As the primary E911 customer contact, the SSC/MAC is in the unique position to monitor the status of the trouble and insure its resolution. System Overview ----------------- The number 911 is intended as a nationwide universal telephone number which provides the public with direct access to a Public Safety Answering Point (PSAP). A PSAP is also referred to as an Emergency Service Bureau (ESB). A PSAP is an agency or facility which is authorized by a municipality to receive and respond to police, fire and/or ambulance services. One or more attendants are located at the PSAP facilities to receive and handle calls of an emergency nature in accordance with the local municipal requirements. An important advantage of E911 emergency service is improved (reduced) response times for emergency services. Also close coordination among agencies providing various emergency services is a valuable capability provided by E911 service. 1A ESS is used as the tandem office for the E911 network to route all 911 calls to the correct (primary) PSAP designated to serve the calling station. The E911 feature was developed primarily to provide routing to the correct PSAP for all 911 calls. Selective routing allows a 911 call originated from a particular station located in a particular district, zone, or town, to be routed to the primary PSAP designated to serve that customer station regardless of wire center boundaries. Thus, selective routing eliminates the problem of wire center boundaries not coinciding with district or other political boundaries. The services available with the E911 feature include: * Forced Disconnect * Default Routing, Alternative Routing * Night Service Selective Routing * Automatic Number Identification (ANI) * Selective Transfer * Automatic Location Identification (ALI) Preservice/Installation Guidelines When a contract for an E911 system has been signed, it is the responsibility of Network Marketing to establish an implementation/cutover committee which should include a representative from the SSC/MAC. Duties of the E911 Implementation Team include coordination of all phases of the E911 system deployment and the formation of an on-going E911 maintenance subcommittee. Marketing is responsible for providing the following customer specific information to the SSC/MAC prior to the start of call through testing: * All PSAP's (name, address, local contact) * All PSAP circuit ID's * 1004 911 service request including PSAP details on each PSAP (1004 Section K, L, M) * Network configuration * Any vendor information (name, telephone number, equipment) The SSC/MAC needs to know if the equipment and sets at the PSAP are maintained by the BOCs, an independent company, or an outside vendor, or any combination. This information is then entered on the PSAP profile sheets and reviewed quarterly for changes, additions and deletions. Marketing will secure the Major Account Number (MAN) and provide this number to Corporate Communications so that the initial issue of the service orders carry the MAN and can be tracked by the SSC/MAC via CORDNET. PSAP circuits are official services by definition. All service orders required for the installation of the E911 system should include the MAN assigned to the city/county which has purchased the system. In accordance with the basic SSC/MAC strategy for provisioning, the SSC/MAC will be Overall Control Office (OCO) for all Node to PSAP circuits (official services) and any other services for this customer. Training must be scheduled for all SSC/MAC involved personnel during the pre-service stage of the project. The E911 Implementation Team will form the on-going maintenance subcommittee prior to the initial implementation of the E911 system. This sub-committee will establish post implementation quality assurance procedures to ensure that the E911 system continues to provide quality service to the customer. Customer/Company training, trouble reporting interfaces for the customer, telephone company and any involved independent telephone companies needs to be addressed and implemented prior to E911 cutover. These functions can be best addressed by the formation of a sub- committee of the E911 Implementation Team to set up guidelines for and to secure service commitments of interfacing organizations. A SSC/MAC supervisor should chair this subcommittee and include the following organizations: * Switching Control Center o E911 translations o Trunking o End office and Tandem office hardware/software * Recent Change Memory Administration Center o Daily RC update activity for TN/ESN translations o Processes validity errors and rejects * Line and Number Administration o Verification of TN/ESN translations * Special Service Center/Major Account Center o Single point of contact for all PSAP and Node to host troubles o Logs, tracks & statusing of all trouble reports o Trouble referral, follow up, and escalation o Customer notification of status and restoration o Analyzation of "chronic" troubles o Testing, installation and maintenance of E911 circuits * Installation and Maintenance (SSIM/I&M) o Repair and maintenance of PSAP equipment and Telco owned sets * Minicomputer Maintenance Operations Center o E911 circuit maintenance (where applicable) * Area Maintenance Engineer o Technical assistance on voice (CO-PSAP) network related E911 troubles Maintenance Guidelines The CCNC will test the Node circuit from the 202T at the Host site to the 202T at the Node site. Since Host to Node (CCNC to MMOC) circuits are official company services, the CCNC will refer all Node circuit troubles to the SSC/MAC. The SSC/MAC is responsible for the testing and follow up to restoration of these circuit troubles. Although Node to PSAP circuit are official services, the MMOC will refer PSAP circuit troubles to the appropriate SSC/MAC. The SSC/MAC is responsible for testing and follow up to restoration of PSAP circuit troubles. The SSC/MAC will also receive reports from CRSAB/IMC(s) on subscriber 911 troubles when they are not line troubles. The SSC/MAC is responsible for testing and restoration of these troubles. Maintenance responsibilities are as follows: :: SCC* Voice Network (ANI to PSAP) *SCC responsible for tandem switch SSIM/I&M PSAP Equipment (Modems, CIU's, sets) Vendor PSAP Equipment (when CPE) SSC/MAC PSAP to Node circuits, and tandem to PSAP voice circuits (EMNT) MMOC Node site (Modems, cables, etc) Note: All above work groups are required to resolve troubles by interfacing with appropriate work groups for resolution. The Switching Control Center (SCC) is responsible for E911/1AESS translations in tandem central offices. These translations route E911 calls, selective transfer, default routing, speed calling, etc., for each PSAP. The SCC is also responsible for troubleshooting on the voice network (call originating to end office tandem equipment). For example, ANI failures in the originating offices would be a responsibility of the SCC. Recent Change Memory Administration Center (RCMAC) performs the daily tandem translation updates (recent change) for routing of individual telephone numbers. Recent changes are generated from service order activity (new service, address changes, etc.) and compiled into a daily file by the E911 Center (ALI/DMS E911 Computer). SSIM/I&M is responsible for the installation and repair of PSAP equipment. PSAP equipment includes ANI Controller, ALI Controller, data sets, cables, sets, and other peripheral equipment that is not vendor owned. SSIM/I&M is responsible for establishing maintenance test kits, complete with spare parts for PSAP maintenance. This includes test gear, data sets, and ANI/ALI Controller parts. Special Services Center (SSC) or Major Account Center (MAC) serves as the trouble reporting contact for all (PSAP) troubles reported by customer. The SSC/MAC refers troubles to proper organizations for handling and tracks status of troubles, escalating when necessary. The SSC/MAC will close out troubles with customer. The SSC/MAC will analyze all troubles and tracks "chronic" PSAP troubles. Corporate Communications Network Center (CCNC) will test and refer troubles on all node to host circuits. All E911 circuits are classified as official company property. The Minicomputer Maintenance Operations Center (MMOC) maintains the E911 (ALI/DMS) computer hardware at the Host site. This MMOC is also responsible for monitoring the system and reporting certain PSAP and system problems to the local MMOC's, SCC's or SSC/MAC's. The MMOC personnel also operate software programs that maintain the TN data base under the direction of the E911 Center. The maintenance of the NODE computer (the interface between the PSAP and the ALI/DMS computer) is a function of the MMOC at the NODE site. The MMOC's at the NODE sites may also be involved in the testing of NODE to Host circuits. The MMOC will also assist on Host to PSAP and data network related troubles not resolved through standard trouble clearing procedures. Installation And Maintenance Center (IMC) is responsible for referral of E911 subscriber troubles that are not subscriber line problems. E911 Center - Performs the role of System Administration and is responsible for overall operation of the E911 computer software. The E911 Center does A-Z trouble analysis and provides statistical information on the performance of the system. This analysis includes processing PSAP inquiries (trouble reports) and referral of network troubles. The E911 Center also performs daily processing of tandem recent change and provides information to the RCMAC for tandem input. The E911 Center is responsible for daily processing of the ALI/DMS computer data base and provides error files, etc. to the Customer Services department for investigation and correction. The E911 Center participates in all system implementations and on-going maintenance effort and assists in the development of procedures, training and education of information to all groups. Any group receiving a 911 trouble from the SSC/MAC should close out the trouble with the SSC/MAC or provide a status if the trouble has been referred to another group. This will allow the SSC/MAC to provide a status back to the customer or escalate as appropriate. Any group receiving a trouble from the Host site (MMOC or CCNC) should close the trouble back to that group. The MMOC should notify the appropriate SSC/MAC when the Host, Node, or all Node circuits are down so that the SSC/MAC can reply to customer reports that may be called in by the PSAPs. This will eliminate duplicate reporting of troubles. On complete outages the MMOC will follow escalation procedures for a Node after two (2) hours and for a PSAP after four (4) hours. Additionally the MMOC will notify the appropriate SSC/MAC when the Host, Node, or all Node circuits are down. The PSAP will call the SSC/MAC to report E911 troubles. The person reporting the E911 trouble may not have a circuit I.D. and will therefore report the PSAP name and address. Many PSAP troubles are not circuit specific. In those instances where the caller cannot provide a circuit I.D., the SSC/MAC will be required to determine the circuit I.D. using the PSAP profile. Under no circumstances will the SSC/MAC Center refuse to take the trouble. The E911 trouble should be handled as quickly as possible, with the SSC/MAC providing as much assistance as possible while taking the trouble report from the caller. The SSC/MAC will screen/test the trouble to determine the appropriate handoff organization based on the following criteria: * PSAP equipment problem: SSIM/I&M * Circuit problem: SSC/MAC * Voice network problem: SCC (report trunk group number) * Problem affecting multiple PSAPs (No ALI report from all PSAPs): Contact the MMOC to check for NODE or Host computer problems before further testing. The SSC/MAC will track the status of reported troubles and escalate as appropriate. The SSC/MAC will close out customer/company reports with the initiating contact. Groups with specific maintenance responsibilities, defined above, will investigate "chronic" troubles upon request from the SSC/MAC and the ongoing maintenance subcommittee. All "out of service" E911 troubles are priority one type reports. One link down to a PSAP is considered a priority one trouble and should be handled as if the PSAP was isolated. The PSAP will report troubles with the ANI controller, ALI controller or set equipment to the SSC/MAC. NO ANI: Where the PSAP reports NO ANI (digital display screen is blank) ask if this condition exists on all screens and on all calls. It is important to differentiate between blank screens and screens displaying 911-00XX, or all zeroes. When the PSAP reports all screens on all calls, ask if there is any voice contact with callers. If there is no voice contact the trouble should be referred to the SCC immediately since 911 calls are not getting through which may require alternate routing of calls to another PSAP. When the PSAP reports this condition on all screens but not all calls and has voice contact with callers, the report should be referred to SSIM/I&M for dispatch. The SSC/MAC should verify with the SCC that ANI is pulsing before dispatching SSIM. When the PSAP reports this condition on one screen for all calls (others work fine) the trouble should be referred to SSIM/I&M for dispatch, because the trouble is isolated to one piece of equipment at the customer premise. An ANI failure (i.e. all zeroes) indicates that the ANI has not been received by the PSAP from the tandem office or was lost by the PSAP ANI controller. The PSAP may receive "02" alarms which can be caused by the ANI controller logging more than three all zero failures on the same trunk. The PSAP has been instructed to report this condition to the SSC/MAC since it could indicate an equipment trouble at the PSAP which might be affecting all subscribers calling into the PSAP. When all zeroes are being received on all calls or "02" alarms continue, a tester should analyze the condition to determine the appropriate action to be taken. The tester must perform cooperative testing with the SCC when there appears to be a problem on the Tandem-PSAP trunks before requesting dispatch. When an occasional all zero condition is reported, the SSC/MAC should dispatch SSIM/I&M to routine equipment on a "chronic" troublesweep. The PSAPs are instructed to report incidental ANI failures to the BOC on a PSAP inquiry trouble ticket (paper) that is sent to the Customer Services E911 group and forwarded to E911 center when required. This usually involves only a particular telephone number and is not a condition that would require a report to the SSC/MAC. Multiple ANI failures which our from the same end office (XX denotes end office), indicate a hard trouble condition may exist in the end office or end office tandem trunks. The PSAP will report this type of condition to the SSC/MAC and the SSC/MAC should refer the report to the SCC responsible for the tandem office. NOTE: XX is the ESCO (Emergency Service Number) associated with the incoming 911 trunks into the tandem. It is important that the C/MAC tell the SCC what is displayed at the PSAP (i.e. 911-0011) which indicates to the SCC which end office is in trouble. Note: It is essential that the PSAP fill out inquiry form on every ANI failure. The PSAP will report a trouble any time an address is not received on an address display (screen blank) E911 call. (If a record is not in the 911 data base or an ANI failure is encountered, the screen will provide a display noticing such condition). The SSC/MAC should verify with the PSAP whether the NO ALI condition is on one screen or all screens. When the condition is on one screen (other screens receive ALI information) the SSC/MAC will request SSIM/I&M to dispatch. If no screens are receiving ALI information, there is usually a circuit trouble between the PSAP and the Host computer. The SSC/MAC should test the trouble and refer for restoral. Note: If the SSC/MAC receives calls from multiple PSAP's, all of which are receiving NO ALI, there is a problem with the Node or Node to Host circuits or the Host computer itself. Before referring the trouble the SSC/MAC should call the MMOC to inquire if the Node or Host is in trouble. Alarm conditions on the ANI controller digital display at the PSAP are to be reported by the PSAP's. These alarms can indicate various trouble conditions so the SSC/MAC should ask the PSAP if any portion of the E911 system is not functioning properly. The SSC/MAC should verify with the PSAP attendant that the equipment's primary function is answering E911 calls. If it is, the SSC/MAC should request a dispatch SSIM/I&M. If the equipment is not primarily used for E911, then the SSC/MAC should advise PSAP to contact their CPE vendor. Note: These troubles can be quite confusing when the PSAP has vendor equipment mixed in with equipment that the BOC maintains. The Marketing representative should provide the SSC/MAC information concerning any unusual or exception items where the PSAP should contact their vendor. This information should be included in the PSAP profile sheets. ANI or ALI controller down: When the host computer sees the PSAP equipment down and it does not come back up, the MMOC will report the trouble to the SSC/MAC; the equipment is down at the PSAP, a dispatch will be required. PSAP link (circuit) down: The MMOC will provide the SSC/MAC with the circuit ID that the Host computer indicates in trouble. Although each PSAP has two circuits, when either circuit is down the condition must be treated as an emergency since failure of the second circuit will cause the PSAP to be isolated. Any problems that the MMOC identifies from the Node location to the Host computer will be handled directly with the appropriate MMOC(s)/CCNC. Note: The customer will call only when a problem is apparent to the PSAP. When only one circuit is down to the PSAP, the customer may not be aware there is a trouble, even though there is one link down, notification should appear on the PSAP screen. Troubles called into the SSC/MAC from the MMOC or other company employee should not be closed out by calling the PSAP since it may result in the customer responding that they do not have a trouble. These reports can only be closed out by receiving information that the trouble was fixed and by checking with the company employee that reported the trouble. The MMOC personnel will be able to verify that the trouble has cleared by reviewing a printout from the host. When the CRSAB receives a subscriber complaint (i.e., cannot dial 911) the RSA should obtain as much information as possible while the customer is on the line. For example, what happened when the subscriber dialed 911? The report is automatically directed to the IMC for subscriber line testing. When no line trouble is found, the IMC will refer the trouble condition to the SSC/MAC. The SSC/MAC will contact Customer Services E911 Group and verify that the subscriber should be able to call 911 and obtain the ESN. The SSC/MAC will verify the ESN via 2SCCS. When both verifications match, the SSC/MAC will refer the report to the SCC responsible for the 911 tandem office for investigation and resolution. The MAC is responsible for tracking the trouble and informing the IMC when it is resolved. | For more information, please refer to E911 Glossary of Terms. | End of Phrack File The reader is forgiven if he or she was entirely unable to read this document. John Perry Barlow had a great deal of fun at its expense, in "Crime and Puzzlement:" "Bureaucrat-ese of surpassing opacity.... To read the whole thing straight through without entering coma requires either a machine or a human who has too much practice thinking like one. Anyone who can understand it fully and fluidly had altered his consciousness beyone the ability to ever again read Blake, Whitman, or Tolstoy.... the document contains little of interest to anyone who is not a student of advanced organizational sclerosis." With the Document itself to hand, however, exactly as it was published (in its six-page edited form) in Phrack, the reader may be able to verify a few statements of fact about its nature. First, there is no software, no computer code, in the Document. It is not computer-programming language like FORTRAN or C++, it is English; all the sentences have nouns and verbs and punctuation. It does not explain how to break into the E911 system. It does not suggest ways to destroy or damage the E911 system. There are no access codes in the Document. There are no computer passwords. It does not explain how to steal long distance service. It does not explain how to break in to telco switching stations. There is nothing in it about using a personal computer or a modem for any purpose at all, good or bad. Close study will reveal that this document is not about machinery. The E911 Document is about administration. It describes how one creates and administers certain units of telco bureaucracy: Special Service Centers and Major Account Centers (SSC/MAC). It describes how these centers should distribute responsibility for the E911 service, to other units of telco bureaucracy, in a chain of command, a formal hierarchy. It describes who answers customer complaints, who screens calls, who reports equipment failures, who answers those reports, who handles maintenance, who chairs subcommittees, who gives orders, who follows orders, who tells whom what to do. The Document is not a "roadmap" to computers. The Document is a roadmap to people. As an aid to breaking into computer systems, the Document is useless. As an aid to harassing and deceiving telco people, however, the Document might prove handy (especially with its Glossary, which I have not included). An intense and protracted study of this Document and its Glossary, combined with many other such documents, might teach one to speak like a telco employee. And telco people live by speech -- they live by phone communication. If you can mimic their language over the phone, you can "social-engineer" them. If you can con telco people, you can wreak havoc among them. You can force them to no longer trust one another; you can break the telephonic ties that bind their community; you can make them paranoid. And people will fight harder to defend their community than they will fight to defend their individual selves. This was the genuine, gut-level threat posed by Phrack magazine. The real struggle was over the control of telco language, the control of telco knowledge. It was a struggle to defend the social "membrane of differentiation" that forms the walls of the telco community's ivory tower -- the special jargon that allows telco professionals to recognize one another, and to exclude charlatans, thieves, and upstarts. And the prosecution brought out this fact. They repeatedly made reference to the threat posed to telco professionals by hackers using "social engineering." However, Craig Neidorf was not on trial for learning to speak like a professional telecommunications expert. Craig Neidorf was on trial for access device fraud and transportation of stolen property. He was on trial for stealing a document that was purportedly highly sensitive and purportedly worth tens of thousands of dollars. 3. === John Nagle read the E911 Document. He drew his own conclusions. And he presented Zenner and his defense team with an overflowing box of similar material, drawn mostly from Stanford University's engineering libraries. During the trial, the defense team -- Zenner, half-a-dozen other attorneys, Nagle, Neidorf, and computer-security expert Dorothy Denning, all pored over the E911 Document line-by-line. On the afternoon of July 25, 1990, Zenner began to cross-examine a woman named Billie Williams, a service manager for Southern Bell in Atlanta. Ms. Williams had been responsible for the E911 Document. (She was not its author -- its original "author" was a Southern Bell staff manager named Richard Helms. However, Mr. Helms should not bear the entire blame; many telco staff people and maintenance personnel had amended the Document. It had not been so much "written" by a single author, as built by committee out of concrete-blocks of jargon.) Ms. Williams had been called as a witness for the prosecution, and had gamely tried to explain the basic technical structure of the E911 system, aided by charts. Now it was Zenner's turn. He first established that the "proprietary stamp" that BellSouth had used on the E911 Document was stamped on every single document that BellSouth wrote -- thousands of documents. "We do not publish anything other than for our own company," Ms. Williams explained. "Any company document of this nature is considered proprietary." Nobody was in charge of singling out special high-security publications for special high-security protection. They were all special, no matter how trivial, no matter what their subject matter - - the stamp was put on as soon as any document was written, and the stamp was never removed. Zenner now asked whether the charts she had been using to explain the mechanics of E911 system were "proprietary," too. Were they public information, these charts, all about PSAPs, ALIs, nodes, local end switches? Could he take the charts out in the street and show them to anybody, "without violating some proprietary notion that BellSouth has?" Ms Williams showed some confusion, but finally agreed that the charts were, in fact, public. "But isn't this what you said was basically what appeared in Phrack?" Ms. Williams denied this. Zenner now pointed out that the E911 Document as published in *Phrack* was only half the size of the original E911 Document (as Prophet had purloined it). Half of it had been deleted -- edited by Neidorf. Ms. Williams countered that "Most of the information that is in the text file is redundant." Zenner continued to probe. Exactly what bits of knowledge in the Document were, in fact, unknown to the public? Locations of E911 computers? Phone numbers for telco personnel? Ongoing maintenance subcommittees? Hadn't Neidorf removed much of this? Then he pounced. "Are you familiar with Bellcore Technical Reference Document TR-TSY-000350?" It was, Zenner explained, officially titled "E911 Public Safety Answering Point Interface Between 1-1AESS Switch and Customer Premises Equipment." It contained highly detailed and specific technical information about the E911 System. It was published by Bellcore and publicly available for about $20. He showed the witness a Bellcore catalog which listed thousands of documents from Bellcore and from all the Baby Bells, BellSouth included. The catalog, Zenner pointed out, was free. Anyone with a credit card could call the Bellcore toll-free 800 number and simply order any of these documents, which would be shipped to any customer without question. Including, for instance, "BellSouth E911 Service Interfaces to Customer Premises Equipment at a Public Safety Answering Point." Zenner gave the witness a copy of "BellSouth E911 Service Interfaces," which cost, as he pointed out, $13, straight from the catalog. "Look at it carefully," he urged Ms. Williams, "and tell me if it doesn't contain about twice as much detailed information about the E911 system of BellSouth than appeared anywhere in Phrack." "You want me to...." Ms. Williams trailed off. "I don't understand." "Take a careful look," Zenner persisted. "Take a look at that document, and tell me when you're done looking at it if, indeed, it doesn't contain much more detailed information about the E911 system than appeared in Phrack." "Phrack wasn't taken from this," Ms. Williams said. "Excuse me?" said Zenner. "Phrack wasn't taken from this." "I can't hear you," Zenner said. "Phrack was not taken from this document. I don't understand your question to me." "I guess you don't," Zenner said. At this point, the prosecution's case had been gutshot. Ms. Williams was distressed. Her confusion was quite genuine. Phrack had not been taken from any publicly available Bellcore document. Phrack's E911 Document had been stolen from her own company's computers, from her own company's text files, that her own colleagues had written, and revised, with much labor. But the "value" of the Document had been blown to smithereens. It wasn't worth eighty grand. According to Bellcore it was worth thirteen bucks. And the looming menace that it supposedly posed had been reduced in instants to a scarecrow. Bellcore itself was selling material far more detailed and "dangerous," to anybody with a credit card and a phone. Actually, Bellcore was not giving this information to just anybody. They gave it to anybody who asked, but not many did ask. Not many people knew that Bellcore had a free catalog and an 800 number. John Nagle knew, but certainly the average teenage phreak didn't know. "Tuc," a friend of Neidorf's and sometime Phrack contributor, knew, and Tuc had been very helpful to the defense, behind the scenes. But the Legion of Doom didn't know -- otherwise, they would never have wasted so much time raiding dumpsters. Cook didn't know. Foley didn't know. Kluepfel didn't know. The right hand of Bellcore knew not what the left hand was doing. The right hand was battering hackers without mercy, while the left hand was distributing Bellcore's intellectual property to anybody who was interested in telephone technical trivia -- apparently, a pathetic few. The digital underground was so amateurish and poorly organized that they had never discovered this heap of unguarded riches. The ivory tower of the telcos was so wrapped-up in the fog of its own technical obscurity that it had left all the windows open and flung open the doors. No one had even noticed. Zenner sank another nail in the coffin. He produced a printed issue of Telephone Engineer & Management, a prominent industry journal that comes out twice a month and costs $27 a year. This particular issue of TE&M, called "Update on 911," featured a galaxy of technical details on 911 service and a glossary far more extensive than Phrack's. The trial rumbled on, somehow, through its own momentum. Tim Foley testified about his interrogations of Neidorf. Neidorf's written admission that he had known the E911 Document was pilfered was officially read into the court record. An interesting side issue came up: "Terminus" had once passed Neidorf a piece of UNIX AT&T software, a log-in sequence, that had been cunningly altered so that it could trap passwords. The UNIX software itself was illegally copied AT&T property, and the alterations "Terminus" had made to it, had transformed it into a device for facilitating computer break-ins. Terminus himself would eventually plead guilty to theft of this piece of software, and the Chicago group would send Terminus to prison for it. But it was of dubious relevance in the Neidorf case. Neidorf hadn't written the program. He wasn't accused of ever having used it. And Neidorf wasn't being charged with software theft or owning a password trapper. On the next day, Zenner took the offensive. The civil libertarians now had their own arcane, untried legal weaponry to launch into action -- the Electronic Communications Privacy Act of 1986, 18 US Code, Section 2701 et seq. Section 2701 makes it a crime to intentionally access without authorization a facility in which an electronic communication service is provided -- it is, at heart, an anti-bugging and anti-tapping law, intended to carry the traditional protections of telephones into other electronic channels of communication. While providing penalties for amateur snoops, however, Section 2703 of the ECPA also lays some formal difficulties on the bugging and tapping activities of police. The Secret Service, in the person of Tim Foley, had served Richard Andrews with a federal grand jury subpoena, in their pursuit of Prophet, the E911 Document, and the Terminus software ring. But according to the Electronic Communications Privacy Act, a "provider of remote computing service" was legally entitled to "prior notice" from the government if a subpoena was used. Richard Andrews and his basement UNIX node, Jolnet, had not received any "prior notice." Tim Foley had purportedly violated the ECPA and committed an electronic crime! Zenner now sought the judge's permission to cross-examine Foley on the topic of Foley's own electronic misdeeds. Cook argued that Richard Andrews' Jolnet was a privately owned bulletin board, and not within the purview of ECPA. Judge Bua granted the motion of the government to prevent cross-examination on that point, and Zenner's offensive fizzled. This, however, was the first direct assault on the legality of the actions of the Computer Fraud and Abuse Task Force itself -- the first suggestion that they themselves had broken the law, and might, perhaps, be called to account. Zenner, in any case, did not really need the ECPA. Instead, he grilled Foley on the glaring contradictions in the supposed value of the E911 Document. He also brought up the embarrassing fact that the supposedly red- hot E911 Document had been sitting around for months, in Jolnet, with Kluepfel's knowledge, while Kluepfel had done nothing about it. In the afternoon, the Prophet was brought in to testify for the prosecution. (The Prophet, it will be recalled, had also been indicted in the case as partner in a fraud scheme with Neidorf.) In Atlanta, the Prophet had already pled guilty to one charge of conspiracy, one charge of wire fraud and one charge of interstate transportation of stolen property. The wire fraud charge, and the stolen property charge, were both directly based on the E911 Document. The twenty-year-old Prophet proved a sorry customer, answering questions politely but in a barely audible mumble, his voice trailing off at the ends of sentences. He was constantly urged to speak up. Cook, examining Prophet, forced him to admit that he had once had a "drug problem," abusing amphetamines, marijuana, cocaine, and LSD. This may have established to the jury that "hackers" are, or can be, seedy lowlife characters, but it may have damaged Prophet's credibility somewhat. Zenner later suggested that drugs might have damaged Prophet's memory. The interesting fact also surfaced that Prophet had never physically met Craig Neidorf. He didn't even know Neidorf's last name -- at least, not until the trial. Prophet confirmed the basic facts of his hacker career. He was a member of the Legion of Doom. He had abused codes, he had broken into switching stations and re-routed calls, he had hung out on pirate bulletin boards. He had raided the BellSouth AIMSX computer, copied the E911 Document, stored it on Jolnet, mailed it to Neidorf. He and Neidorf had edited it, and Neidorf had known where it came from. Zenner, however, had Prophet confirm that Neidorf was not a member of the Legion of Doom, and had not urged Prophet to break into BellSouth computers. Neidorf had never urged Prophet to defraud anyone, or to steal anything. Prophet also admitted that he had never known Neidorf to break in to any computer. Prophet said that no one in the Legion of Doom considered Craig Neidorf a "hacker" at all. Neidorf was not a UNIX maven, and simply lacked the necessary skill and ability to break into computers. Neidorf just published a magazine. On Friday, July 27, 1990, the case against Neidorf collapsed. Cook moved to dismiss the indictment, citing "information currently available to us that was not available to us at the inception of the trial." Judge Bua praised the prosecution for this action, which he described as "very responsible," then dismissed a juror and declared a mistrial. Neidorf was a free man. His defense, however, had cost himself and his family dearly. Months of his life had been consumed in anguish; he had seen his closest friends shun him as a federal criminal. He owed his lawyers over a hundred thousand dollars, despite a generous payment to the defense by Mitch Kapor. Neidorf was not found innocent. The trial was simply dropped. Nevertheless, on September 9, 1991, Judge Bua granted Neidorf's motion for the "expungement and sealing" of his indictment record. The United States Secret Service was ordered to delete and destroy all fingerprints, photographs, and other records of arrest or processing relating to Neidorf's indictment, including their paper documents and their computer records. Neidorf went back to school, blazingly determined to become a lawyer. Having seen the justice system at work, Neidorf lost much of his enthusiasm for merely technical power. At this writing, Craig Neidorf is working in Washington as a salaried researcher for the American Civil Liberties Union. 4. === The outcome of the Neidorf trial changed the EFF from voices-in-the-wilderness to the media darlings of the new frontier. Legally speaking, the Neidorf case was not a sweeping triumph for anyone concerned. No constitutional principles had been established. The issues of "freedom of the press" for electronic publishers remained in legal limbo. There were public misconceptions about the case. Many people thought Neidorf had been found innocent and relieved of all his legal debts by Kapor. The truth was that the government had simply dropped the case, and Neidorf's family had gone deeply into hock to support him. But the Neidorf case did provide a single, devastating, public sound-bite: The feds said it was worth eighty grand, and it was only worth thirteen bucks. This is the Neidorf case's single most memorable element. No serious report of the case missed this particular element. Even cops could not read this without a wince and a shake of the head. It left the public credibility of the crackdown agents in tatters. The crackdown, in fact, continued, however. Those two charges against Prophet, which had been based on the E911 Document, were quietly forgotten at his sentencing -- even though Prophet had already pled guilty to them. Georgia federal prosecutors strongly argued for jail time for the Atlanta Three, insisting on "the need to send a message to the community," "the message that hackers around the country need to hear." There was a great deal in their sentencing memorandum about the awful things that various other hackers had done (though the Atlanta Three themselves had not, in fact, actually committed these crimes). There was also much speculation about the awful things that the Atlanta Three might have done and were capable of doing (even though they had not, in fact, actually done them). The prosecution's argument carried the day. The Atlanta Three were sent to prison: Urvile and Leftist both got 14 months each, while Prophet (a second offender) got 21 months. The Atlanta Three were also assessed staggering fines as "restitution": $233,000 each. BellSouth claimed that the defendants had "stolen" "approximately $233,880 worth" of "proprietary computer access information" -- specifically, $233,880 worth of computer passwords and connect addresses. BellSouth's astonishing claim of the extreme value of its own computer passwords and addresses was accepted at face value by the Georgia court. Furthermore (as if to emphasize its theoretical nature) this enormous sum was not divvied up among the Atlanta Three, but each of them had to pay all of it. A striking aspect of the sentence was that the Atlanta Three were specifically forbidden to use computers, except for work or under supervision. Depriving hackers of home computers and modems makes some sense if one considers hackers as "computer addicts," but EFF, filing an amicus brief in the case, protested that this punishment was unconstitutional -- it deprived the Atlanta Three of their rights of free association and free expression through electronic media. Terminus, the "ultimate hacker," was finally sent to prison for a year through the dogged efforts of the Chicago Task Force. His crime, to which he pled guilty, was the transfer of the UNIX password trapper, which was officially valued by AT&T at $77,000, a figure which aroused intense skepticism among those familiar with UNIX "login.c" programs. The jailing of Terminus and the Atlanta Legionnaires of Doom, however, did not cause the EFF any sense of embarrassment or defeat. On the contrary, the civil libertarians were rapidly gathering strength. An early and potent supporter was Senator Patrick Leahy, Democrat from Vermont, who had been a Senate sponsor of the Electronic Communications Privacy Act. Even before the Neidorf trial, Leahy had spoken out in defense of hacker-power and freedom of the keyboard: "We cannot unduly inhibit the inquisitive 13-year-old who, if left to experiment today, may tomorrow develop the telecommunications or computer technology to lead the United States into the 21st century. He represents our future and our best hope to remain a technologically competitive nation." It was a handsome statement, rendered perhaps rather more effective by the fact that the crackdown raiders did not have any Senators speaking out for them. On the contrary, their highly secretive actions and tactics, all "sealed search warrants" here and "confidential ongoing investigations" there, might have won them a burst of glamorous publicity at first, but were crippling them in the on-going propaganda war. Gail Thackeray was reduced to unsupported bluster: "Some of these people who are loudest on the bandwagon may just slink into the background," she predicted in Newsweek - - when all the facts came out, and the cops were vindicated. But all the facts did not come out. Those facts that did, were not very flattering. And the cops were not vindicated. And Gail Thackeray lost her job. By the end of 1991, William Cook had also left public employment. 1990 had belonged to the crackdown, but by '91 its agents were in severe disarray, and the libertarians were on a roll. People were flocking to the cause. A particularly interesting ally had been Mike Godwin of Austin, Texas. Godwin was an individual almost as difficult to describe as Barlow; he had been editor of the student newspaper of the University of Texas, and a computer salesman, and a programmer, and in 1990 was back in law school, looking for a law degree. Godwin was also a bulletin board maven. He was very well-known in the Austin board community under his handle "Johnny Mnemonic," which he adopted from a cyberpunk science fiction story by William Gibson. Godwin was an ardent cyberpunk science fiction fan. As a fellow Austinite of similar age and similar interests, I myself had known Godwin socially for many years. When William Gibson and myself had been writing our collaborative SF novel, The Difference Engine, Godwin had been our technical advisor in our effort to link our Apple word-processors from Austin to Vancouver. Gibson and I were so pleased by his generous expert help that we named a character in the novel "Michael Godwin" in his honor. The handle "Mnemonic" suited Godwin very well. His erudition and his mastery of trivia were impressive to the point of stupor; his ardent curiosity seemed insatiable, and his desire to debate and argue seemed the central drive of his life. Godwin had even started his own Austin debating society, wryly known as the "Dull Men's Club." In person, Godwin could be overwhelming; a flypaper- brained polymath who could not seem to let any idea go. On bulletin boards, however, Godwin's closely reasoned, highly grammatical, erudite posts suited the medium well, and he became a local board celebrity. Mike Godwin was the man most responsible for the public national exposure of the Steve Jackson case. The Izenberg seizure in Austin had received no press coverage at all. The March 1 raids on Mentor, Bloodaxe, and Steve Jackson Games had received a brief front-page splash in the front page of the Austin American-Statesman, but it was confused and ill-informed: the warrants were sealed, and the Secret Service wasn't talking. Steve Jackson seemed doomed to obscurity. Jackson had not been arrested; he was not charged with any crime; he was not on trial. He had lost some computers in an ongoing investigation -- so what? Jackson tried hard to attract attention to the true extent of his plight, but he was drawing a blank; no one in a position to help him seemed able to get a mental grip on the issues. Godwin, however, was uniquely, almost magically, qualified to carry Jackson's case to the outside world. Godwin was a board enthusiast, a science fiction fan, a former journalist, a computer salesman, a lawyer-to-be, and an Austinite. Through a coincidence yet more amazing, in his last year of law school Godwin had specialized in federal prosecutions and criminal procedure. Acting entirely on his own, Godwin made up a press packet which summarized the issues and provided useful contacts for reporters. Godwin's behind-the-scenes effort (which he carried out mostly to prove a point in a local board debate) broke the story again in the Austin American-Statesman and then in Newsweek. Life was never the same for Mike Godwin after that. As he joined the growing civil liberties debate on the Internet, it was obvious to all parties involved that here was one guy who, in the midst of complete murk and confusion, genuinely understood everything he was talking about. The disparate elements of Godwin's dilettantish existence suddenly fell together as neatly as the facets of a Rubik's cube. When the time came to hire a full-time EFF staff attorney, Godwin was the obvious choice. He took the Texas bar exam, left Austin, moved to Cambridge, became a full-time, professional, computer civil libertarian, and was soon touring the nation on behalf of EFF, delivering well-received addresses on the issues to crowds as disparate as academics, industrialists, science fiction fans, and federal cops. Michael Godwin is currently the chief legal counsel of the Electronic Frontier Foundation in Cambridge, Massachusetts. 5. === Another early and influential participant in the controversy was Dorothy Denning. Dr. Denning was unique among investigators of the computer underground in that she did not enter the debate with any set of politicized motives. She was a professional cryptographer and computer security expert whose primary interest in hackers was scholarly. She had a B.A. and M.A. in mathematics, and a Ph.D. in computer science from Purdue. She had worked for SRI International, the California think-tank that was also the home of computer- security maven Donn Parker, and had authored an influential text called Cryptography and Data Security. In 1990, Dr. Denning was working for Digital Equipment Corporation in their Systems Reseach Center. Her husband, Peter Denning, was also a computer security expert, working for NASA's Research Institute for Advanced Computer Science. He had edited the well- received Computers Under Attack: Intruders, Worms and Viruses. Dr. Denning took it upon herself to contact the digital underground, more or less with an anthropological interest. There she discovered that these computer- intruding hackers, who had been characterized as unethical, irresponsible, and a serious danger to society, did in fact have their own subculture and their own rules. They were not particularly well-considered rules, but they were, in fact, rules. Basically, they didn't take money and they didn't break anything. Her dispassionate reports on her researches did a great deal to influence serious-minded computer professionals -- the sort of people who merely rolled their eyes at the cyberspace rhapsodies of a John Perry Barlow. For young hackers of the digital underground, meeting Dorothy Denning was a genuinely mind-boggling experience. Here was this neatly coiffed, conservatively dressed, dainty little personage, who reminded most hackers of their moms or their aunts. And yet she was an IBM systems programmer with profound expertise in computer architectures and high-security information flow, who had personal friends in the FBI and the National Security Agency. Dorothy Denning was a shining example of the American mathematical intelligentsia, a genuinely brilliant person from the central ranks of the computer- science elite. And here she was, gently questioning twenty-year-old hairy-eyed phone-phreaks over the deeper ethical implications of their behavior. Confronted by this genuinely nice lady, most hackers sat up very straight and did their best to keep the anarchy- file stuff down to a faint whiff of brimstone. Nevertheless, the hackers were in fact prepared to seriously discuss serious issues with Dorothy Denning. They were willing to speak the unspeakable and defend the indefensible, to blurt out their convictions that information cannot be owned, that the databases of governments and large corporations were a threat to the rights and privacy of individuals. Denning's articles made it clear to many that "hacking" was not simple vandalism by some evil clique of psychotics. "Hacking" was not an aberrant menace that could be charmed away by ignoring it, or swept out of existence by jailing a few ringleaders. Instead, "hacking" was symptomatic of a growing, primal struggle over knowledge and power in the age of information. Denning pointed out that the attitude of hackers were at least partially shared by forward-looking management theorists in the business community: people like Peter Drucker and Tom Peters. Peter Drucker, in his book The New Realities, had stated that "control of information by the government is no longer possible. Indeed, information is now transnational. Like money, it has no 'fatherland.'" And management maven Tom Peters had chided large corporations for uptight, proprietary attitudes in his bestseller, Thriving on Chaos: "Information hoarding, especially by politically motivated, power-seeking staffs, had been commonplace throughout American industry, service and manufacturing alike. It will be an impossible millstone aroung the neck of tomorrow's organizations." Dorothy Denning had shattered the social membrane of the digital underground. She attended the Neidorf trial, where she was prepared to testify for the defense as an expert witness. She was a behind-the- scenes organizer of two of the most important national meetings of the computer civil libertarians. Though not a zealot of any description, she brought disparate elements of the electronic community into a surprising and fruitful collusion. Dorothy Denning is currently the Chair of the Computer Science Department at Georgetown University in Washington, DC. 6. === There were many stellar figures in the civil libertarian community. There's no question, however, that its single most influential figure was Mitchell D. Kapor. Other people might have formal titles, or governmental positions, have more experience with crime, or with the law, or with the arcanities of computer security or constitutional theory. But by 1991 Kapor had transcended any such narrow role. Kapor had become "Mitch." Mitch had become the central civil-libertarian ad- hocrat. Mitch had stood up first, he had spoken out loudly, directly, vigorously and angrily, he had put his own reputation, and his very considerable personal fortune, on the line. By mid-'91 Kapor was the best-known advocate of his cause and was known personally by almost every single human being in America with any direct influence on the question of civil liberties in cyberspace. Mitch had built bridges, crossed voids, changed paradigms, forged metaphors, made phone-calls and swapped business cards to such spectacular effect that it had become impossible for anyone to take any action in the "hacker question" without wondering what Mitch might think -- and say -- and tell his friends. The EFF had simply networked the situation into an entirely new status quo. And in fact this had been EFF's deliberate strategy from the beginning. Both Barlow and Kapor loathed bureaucracies and had deliberately chosen to work almost entirely through the electronic spiderweb of "valuable personal contacts." After a year of EFF, both Barlow and Kapor had every reason to look back with satisfaction. EFF had established its own Internet node, "eff.org," with a well-stocked electronic archive of documents on electronic civil rights, privacy issues, and academic freedom. EFF was also publishing EFFector, a quarterly printed journal, as well as EFFector Online, an electronic newsletter with over 1,200 subscribers. And EFF was thriving on the Well. EFF had a national headquarters in Cambridge and a full-time staff. It had become a membership organization and was attracting grass-roots support. It had also attracted the support of some thirty civil-rights lawyers, ready and eager to do pro bono work in defense of the Constitution in Cyberspace. EFF had lobbied successfully in Washington and in Massachusetts to change state and federal legislation on computer networking. Kapor in particular had become a veteran expert witness, and had joined the Computer Science and Telecommunications Board of the National Academy of Science and Engineering. EFF had sponsored meetings such as "Computers, Freedom and Privacy" and the CPSR Roundtable. It had carried out a press offensive that, in the words of EFFector, "has affected the climate of opinion about computer networking and begun to reverse the slide into 'hacker hysteria' that was beginning to grip the nation." It had helped Craig Neidorf avoid prison. And, last but certainly not least, the Electronic Frontier Foundation had filed a federal lawsuit in the name of Steve Jackson, Steve Jackson Games Inc., and three users of the Illuminati bulletin board system. The defendants were, and are, the United States Secret Service, William Cook, Tim Foley, Barbara Golden and Henry Kleupfel. The case, which is in pre-trial procedures in an Austin federal court as of this writing, is a civil action for damages to redress alleged violations of the First and Fourth Amendments to the United States Constitution, as well as the Privacy Protection Act of 1980 (42 USC 2000aa et seq.), and the Electronic Communications Privacy Act (18 USC 2510 et seq and 2701 et seq). EFF had established that it had credibility. It had also established that it had teeth. In the fall of 1991 I travelled to Massachusetts to speak personally with Mitch Kapor. It was my final interview for this book. 7. === The city of Boston has always been one of the major intellectual centers of the American republic. It is a very old city by American standards, a place of skyscrapers overshadowing seventeenth-century graveyards, where the high-tech start-up companies of Route 128 co-exist with the hand-wrought pre-industrial grace of "Old Ironsides," the USS Constitution. The Battle of Bunker Hill, one of the first and bitterest armed clashes of the American Revolution, was fought in Boston's environs. Today there is a monumental spire on Bunker Hill, visible throughout much of the city. The willingness of the republican revolutionaries to take up arms and fire on their oppressors has left a cultural legacy that two full centuries have not effaced. Bunker Hill is still a potent center of American political symbolism, and the Spirit of '76 is still a potent image for those who seek to mold public opinion. Of course, not everyone who wraps himself in the flag is necessarily a patriot. When I visited the spire in September 1991, it bore a huge, badly-erased, spray-can grafitto around its bottom reading "BRITS OUT -- IRA PROVOS." Inside this hallowed edifice was a glass-cased diorama of thousands of tiny toy soldiers, rebels and redcoats, fighting and dying over the green hill, the riverside marshes, the rebel trenchworks. Plaques indicated the movement of troops, the shiftings of strategy. The Bunker Hill Monument is occupied at its very center by the toy soldiers of a military war-game simulation. The Boston metroplex is a place of great universities, prominent among the Massachusetts Institute of Technology, where the term "computer hacker" was first coined. The Hacker Crackdown of 1990 might be interpreted as a political struggle among American cities: traditional strongholds of longhair intellectual liberalism, such as Boston, San Francisco, and Austin, versus the bare-knuckle industrial pragmatism of Chicago and Phoenix (with Atlanta and New York wrapped in internal struggle). The headquarters of the Electronic Frontier Foundation is on 155 Second Street in Cambridge, a Bostonian suburb north of the River Charles. Second Street has weedy sidewalks of dented, sagging brick and elderly cracked asphalt; large street-signs warn "NO PARKING DURING DECLARED SNOW EMERGENCY." This is an old area of modest manufacturing industries; the EFF is catecorner from the Greene Rubber Company. EFF's building is two stories of red brick; its large wooden windows feature gracefully arched tops and stone sills. The glass window beside the Second Street entrance bears three sheets of neatly laser-printed paper, taped against the glass. They read: ON Technology. EFF. KEI. "ON Technology" is Kapor's software company, which currently specializes in "groupware" for the Apple Macintosh computer. "Groupware" is intended to promote efficient social interaction among office-workers linked by computers. ON Technology's most successful software products to date are "Meeting Maker" and "Instant Update." "KEI" is Kapor Enterprises Inc., Kapor's personal holding company, the commercial entity that formally controls his extensive investments in other hardware and software corporations. "EFF" is a political action group -- of a special sort. Inside, someone's bike has been chained to the handrails of a modest flight of stairs. A wall of modish glass brick separates this anteroom from the offices. Beyond the brick, there's an alarm system mounted on the wall, a sleek, complex little number that resembles a cross between a thermostat and a CD player. Piled against the wall are box after box of a recent special issue of Scientific American, "How to Work, Play, and Thrive in Cyberspace," with extensive coverage of electronic networking techniques and political issues, including an article by Kapor himself. These boxes are addressed to Gerard Van der Leun, EFF's Director of Communications, who will shortly mail those magazines to every member of the EFF. The joint headquarters of EFF, KEI, and ON Technology, which Kapor currently rents, is a modestly bustling place. It's very much the same physical size as Steve Jackson's gaming company. It's certainly a far cry from the gigantic gray steel-sided railway shipping barn, on the Monsignor O'Brien Highway, that is owned by Lotus Development Corporation. Lotus is, of course, the software giant that Mitchell Kapor founded in the late 70s. The software program Kapor co-authored, "Lotus 1-2-3," is still that company's most profitable product. "Lotus 1-2-3" also bears a singular distinction in the digital underground: it's probably the most pirated piece of application software in world history. Kapor greets me cordially in his own office, down a hall. Kapor, whose name is pronounced KAY-por, is in his early forties, married and the father of two. He has a round face, high forehead, straight nose, a slightly tousled mop of black hair peppered with gray. His large brown eyes are wideset, reflective, one might almost say soulful. He disdains ties, and commonly wears Hawaiian shirts and tropical prints, not so much garish as simply cheerful and just that little bit anomalous. There is just the whiff of hacker brimstone about Mitch Kapor. He may not have the hard-riding, hell-for- leather, guitar-strumming charisma of his Wyoming colleague John Perry Barlow, but there's something about the guy that still stops one short. He has the air of the Eastern city dude in the bowler hat, the dreamy, Longfellow-quoting poker shark who only happens to know the exact mathematical odds against drawing to an inside straight. Even among his computer-community colleagues, who are hardly known for mental sluggishness, Kapor strikes one forcefully as a very intelligent man. He speaks rapidly, with vigorous gestures, his Boston accent sometimes slipping to the sharp nasal tang of his youth in Long Island. Kapor, whose Kapor Family Foundation does much of his philanthropic work, is a strong supporter of Boston's Computer Museum. Kapor's interest in the history of his industry has brought him some remarkable curios, such as the "byte" just outside his office door. This "byte" -- eight digital bits -- has been salvaged from the wreck of an electronic computer of the pre-transistor age. It's a standing gunmetal rack about the size of a small toaster- oven: with eight slots of hand-soldered breadboarding featuring thumb-sized vacuum tubes. If it fell off a table it could easily break your foot, but it was state-of-the-art computation in the 1940s. (It would take exactly 157,184 of these primordial toasters to hold the first part of this book.) There's also a coiling, multicolored, scaly dragon that some inspired techno-punk artist has cobbled up entirely out of transistors, capacitors, and brightly plastic-coated wiring. Inside the office, Kapor excuses himself briefly to do a little mouse-whizzing housekeeping on his personal Macintosh IIfx. If its giant screen were an open window, an agile person could climb through it without much trouble at all. There's a coffee-cup at Kapor's elbow, a memento of his recent trip to Eastern Europe, which has a black-and-white stencilled photo and the legend CAPITALIST FOOLS TOUR. It's Kapor, Barlow, and two California venture-capitalist luminaries of their acquaintance, four windblown, grinning Baby Boomer dudes in leather jackets, boots, denim, travel bags, standing on airport tarmac somewhere behind the formerly Iron Curtain. They look as if they're having the absolute time of their lives. Kapor is in a reminiscent mood. We talk a bit about his youth -- high school days as a "math nerd," Saturdays attending Columbia University's high-school science honors program, where he had his first experience programming computers. IBM 1620s, in 1965 and '66. "I was very interested," says Kapor, "and then I went off to college and got distracted by drugs sex and rock and roll, like anybody with half a brain would have then!" After college he was a progressive-rock DJ in Hartford, Connecticut, for a couple of years. I ask him if he ever misses his rock and roll days -- if he ever wished he could go back to radio work. He shakes his head flatly. "I stopped thinking about going back to be a DJ the day after Altamont." Kapor moved to Boston in 1974 and got a job programming mainframes in COBOL. He hated it. He quit and became a teacher of transcendental meditation. (It was Kapor's long flirtation with Eastern mysticism that gave the world "Lotus.") In 1976 Kapor went to Switzerland, where the Transcendental Meditation movement had rented a gigantic Victorian hotel in St-Moritz. It was an all-male group -- a hundred and twenty of them -- determined upon Enlightenment or Bust. Kapor had given the transcendant his best shot. He was becoming disenchanted by "the nuttiness in the organization." "They were teaching people to levitate," he says, staring at the floor. His voice drops an octave, becomes flat. "They don't levitate." Kapor chose Bust. He went back to the States and acquired a degree in counselling psychology. He worked a while in a hospital, couldn't stand that either. "My rep was," he says "a very bright kid with a lot of potential who hasn't found himself. Almost thirty. Sort of lost." Kapor was unemployed when he bought his first personal computer -- an Apple II. He sold his stereo to raise cash and drove to New Hampshire to avoid the sales tax. "The day after I purchased it," Kapor tells me, "I was hanging out in a computer store and I saw another guy, a man in his forties, well-dressed guy, and eavesdropped on his conversation with the salesman. He didn't know anything about computers. I'd had a year programming. And I could program in BASIC. I'd taught myself. So I went up to him, and I actually sold myself to him as a consultant." He pauses. "I don't know where I got the nerve to do this. It was uncharacteristic. I just said, 'I think I can help you, I've been listening, this is what you need to do and I think I can do it for you.' And he took me on! He was my first client! I became a computer consultant the first day after I bought the Apple II." Kapor had found his true vocation. He attracted more clients for his consultant service, and started an Apple users' group. A friend of Kapor's, Eric Rosenfeld, a graduate student at MIT, had a problem. He was doing a thesis on an arcane form of financial statistics, but could not wedge himself into the crowded queue for time on MIT's mainframes. (One might note at this point that if Mr. Rosenfeld had dishonestly broken into the MIT mainframes, Kapor himself might have never invented Lotus 1-2-3 and the PC business might have been set back for years!) Eric Rosenfeld did have an Apple II, however, and he thought it might be possible to scale the problem down. Kapor, as favor, wrote a program for him in BASIC that did the job. It then occurred to the two of them, out of the blue, that it might be possible to sell this program. They marketed it themselves, in plastic baggies, for about a hundred bucks a pop, mail order. "This was a total cottage industry by a marginal consultant," Kapor says proudly. "That's how I got started, honest to God." Rosenfeld, who later became a very prominent figure on Wall Street, urged Kapor to go to MIT's business school for an MBA. Kapor did seven months there, but never got his MBA. He picked up some useful tools -- mainly a firm grasp of the principles of accounting -- and, in his own words, "learned to talk MBA." Then he dropped out and went to Silicon Valley. The inventors of VisiCalc, the Apple computer's premier business program, had shown an interest in Mitch Kapor. Kapor worked diligently for them for six months, got tired of California, and went back to Boston where they had better bookstores. The VisiCalc group had made the critical error of bringing in "professional management." "That drove them into the ground," Kapor says. "Yeah, you don't hear a lot about VisiCalc these days," I muse. Kapor looks surprised. "Well, Lotus.... we bought it." "Oh. You bought it?" "Yeah." "Sort of like the Bell System buying Western Union?" Kapor grins. "Yep! Yep! Yeah, exactly!" Mitch Kapor was not in full command of the destiny of himself or his industry. The hottest software commodities of the early 1980s were computer games -- the Atari seemed destined to enter every teenage home in America. Kapor got into business software simply because he didn't have any particular feeling for computer games. But he was supremely fast on his feet, open to new ideas and inclined to trust his instincts. And his instincts were good. He chose good people to deal with -- gifted programmer Jonathan Sachs (the co-author of Lotus 1-2-3). Financial wizard Eric Rosenfeld, canny Wall Street analyst and venture capitalist Ben Rosen. Kapor was the founder and CEO of Lotus, one of the most spectacularly successful business ventures of the later twentieth century. He is now an extremely wealthy man. I ask him if he actually knows how much money he has. "Yeah," he says. "Within a percent or two." How much does he actually have, then? He shakes his head. "A lot. A lot. Not something I talk about. Issues of money and class are things that cut pretty close to the bone." I don't pry. It's beside the point. One might presume, impolitely, that Kapor has at least forty million - - that's what he got the year he left Lotus. People who ought to know claim Kapor has about a hundred and fifty million, give or take a market swing in his stock holdings. If Kapor had stuck with Lotus, as his colleague friend and rival Bill Gates has stuck with his own software start-up, Microsoft, then Kapor would likely have much the same fortune Gates has -- somewhere in the neighborhood of three billion, give or take a few hundred million. Mitch Kapor has all the money he wants. Money has lost whatever charm it ever held for him -- probably not much in the first place. When Lotus became too uptight, too bureaucratic, too far from the true sources of his own satisfaction, Kapor walked. He simply severed all connections with the company and went out the door. It stunned everyone -- except those who knew him best. Kapor has not had to strain his resources to wreak a thorough transformation in cyberspace politics. In its first year, EFF's budget was about a quarter of a million dollars. Kapor is running EFF out of his pocket change. Kapor takes pains to tell me that he does not consider himself a civil libertarian per se. He has spent quite some time with true-blue civil libertarians lately, and there's a political-correctness to them that bugs him. They seem to him to spend entirely too much time in legal nitpicking and not enough vigorously exercising civil rights in the everyday real world. Kapor is an entrepreneur. Like all hackers, he prefers his involvements direct, personal, and hands-on. "The fact that EFF has a node on the Internet is a great thing. We're a publisher. We're a distributor of information." Among the items the eff.org Internet node carries is back issues of Phrack. They had an internal debate about that in EFF, and finally decided to take the plunge. They might carry other digital underground publications -- but if they do, he says, "we'll certainly carry Donn Parker, and anything Gail Thackeray wants to put up. We'll turn it into a public library, that has the whole spectrum of use. Evolve in the direction of people making up their own minds." He grins. "We'll try to label all the editorials." Kapor is determined to tackle the technicalities of the Internet in the service of the public interest. "The problem with being a node on the Net today is that you've got to have a captive technical specialist. We have Chris Davis around, for the care and feeding of the balky beast! We couldn't do it ourselves!" He pauses. "So one direction in which technology has to evolve is much more standardized units, that a non- technical person can feel comfortable with. It's the same shift as from minicomputers to PCs. I can see a future in which any person can have a Node on the Net. Any person can be a publisher. It's better than the media we now have. It's possible. We're working actively." Kapor is in his element now, fluent, thoroughly in command in his material. "You go tell a hardware Internet hacker that everyone should have a node on the Net," he says, "and the first thing they're going to say is, 'IP doesn't scale!'" ("IP" is the interface protocol for the Internet. As it currently exists, the IP software is simply not capable of indefinite expansion; it will run out of usable addresses, it will saturate.) "The answer," Kapor says, "is: evolve the protocol! Get the smart people together and figure out what to do. Do we add ID? Do we add new protocol? Don't just say, we can't do it." Getting smart people together to figure out what to do is a skill at which Kapor clearly excels. I counter that people on the Internet rather enjoy their elite technical status, and don't seem particularly anxious to democratize the Net. Kapor agrees, with a show of scorn. "I tell them that this is the snobbery of the people on the Mayflower looking down their noses at the people who came over on the second boat! Just because they got here a year, or five years, or ten years before everybody else, that doesn't give them ownership of cyberspace! By what right?" I remark that the telcos are an electronic network, too, and they seem to guard their specialized knowledge pretty closely. Kapor ripostes that the telcos and the Internet are entirely different animals. "The Internet is an open system, everything is published, everything gets argued about, basically by anybody who can get in. Mostly, it's exclusive and elitist just because it's so difficult. Let's make it easier to use." On the other hand, he allows with a swift change of emphasis, the so-called elitists do have a point as well. "Before people start coming in, who are new, who want to make suggestions, and criticize the Net as 'all screwed up'.... They should at least take the time to understand the culture on its own terms. It has its own history -- show some respect for it. I'm a conservative, to that extent." The Internet is Kapor's paradigm for the future of telecommunications. The Internet is decentralized, non- heirarchical, almost anarchic. There are no bosses, no chain of command, no secret data. If each node obeys the general interface standards, there's simply no need for any central network authority. Wouldn't that spell the doom of AT&T as an institution? I ask. That prospect doesn't faze Kapor for a moment. "Their big advantage, that they have now, is that they have all of the wiring. But two things are happening. Anyone with right-of-way is putting down fiber -- Southern Pacific Railroad, people like that -- there's enormous 'dark fiber' laid in." ("Dark Fiber" is fiber-optic cable, whose enormous capacity so exceeds the demands of current usage that much of the fiber still has no light-signals on it - - it's still 'dark,' awaiting future use.) "The other thing that's happening is the local-loop stuff is going to go wireless. Everyone from Bellcore to the cable TV companies to AT&T wants to put in these things called 'personal communication systems.' So you could have local competition -- you could have multiplicity of people, a bunch of neighborhoods, sticking stuff up on poles. And a bunch of other people laying in dark fiber. So what happens to the telephone companies? There's enormous pressure on them from both sides. "The more I look at this, the more I believe that in a post-industrial, digital world, the idea of regulated monopolies is bad. People will look back on it and say that in the 19th and 20th centuries the idea of public utilities was an okay compromise. You needed one set of wires in the ground. It was too economically inefficient, otherwise. And that meant one entity running it. But now, with pieces being wireless -- the connections are going to be via high- level interfaces, not via wires. I mean, ultimately there are going to be wires -- but the wires are just a commodity. Fiber, wireless. You no longer need a utility." Water utilities? Gas utilities? Of course we still need those, he agrees. "But when what you're moving is information, instead of physical substances, then you can play by a different set of rules. We're evolving those rules now! Hopefully you can have a much more decentralized system, and one in which there's more competition in the marketplace. "The role of government will be to make sure that nobody cheats. The proverbial 'level playing field.' A policy that prevents monopolization. It should result in better service, lower prices, more choices, and local empowerment." He smiles. "I'm very big on local empowerment." Kapor is a man with a vision. It's a very novel vision which he and his allies are working out in considerable detail and with great energy. Dark, cynical, morbid cyberpunk that I am, I cannot avoid considering some of the darker implications of "decentralized, nonhierarchical, locally empowered" networking. I remark that some pundits have suggested that electronic networking -- faxes, phones, small-scale photocopiers -- played a strong role in dissolving the power of centralized communism and causing the collapse of the Warsaw Pact. Socialism is totally discredited, says Kapor, fresh back from the Eastern Bloc. The idea that faxes did it, all by themselves, is rather wishful thinking. Has it occurred to him that electronic networking might corrode America's industrial and political infrastructure to the point where the whole thing becomes untenable, unworkable -- and the old order just collapses headlong, like in Eastern Europe? "No," Kapor says flatly. "I think that's extraordinarily unlikely. In part, because ten or fifteen years ago, I had similar hopes about personal computers -- which utterly failed to materialize." He grins wryly, then his eyes narrow. "I'm very opposed to techno-utopias. Every time I see one, I either run away, or try to kill it." It dawns on me then that Mitch Kapor is not trying to make the world safe for democracy. He certainly is not trying to make it safe for anarchists or utopians -- least of all for computer intruders or electronic rip-off artists. What he really hopes to do is make the world safe for future Mitch Kapors. This world of decentralized, small- scale nodes, with instant global access for the best and brightest, would be a perfect milieu for the shoestring attic capitalism that made Mitch Kapor what he is today. Kapor is a very bright man. He has a rare combination of visionary intensity with a strong practical streak. The Board of the EFF: John Barlow, Jerry Berman of the ACLU, Stewart Brand, John Gilmore, Steve Wozniak, and Esther Dyson, the doyenne of East-West computer entrepreneurism -- share his gift, his vision, and his formidable networking talents. They are people of the 1960s, winnowed-out by its turbulence and rewarded with wealth and influence. They are some of the best and the brightest that the electronic community has to offer. But can they do it, in the real world? Or are they only dreaming? They are so few. And there is so much against them. I leave Kapor and his networking employees struggling cheerfully with the promising intricacies of their newly installed Macintosh System 7 software. The next day is Saturday. EFF is closed. I pay a few visits to points of interest downtown. One of them is the birthplace of the telephone. It's marked by a bronze plaque in a plinth of black- and-white speckled granite. It sits in the plaza of the John F. Kennedy Federal Building, the very place where Kapor was once fingerprinted by the FBI. The plaque has a bas-relief picture of Bell's original telephone. "BIRTHPLACE OF THE TELEPHONE," it reads. "Here, on June 2, 1875, Alexander Graham Bell and Thomas A. Watson first transmitted sound over wires. "This successful experiment was completed in a fifth floor garret at what was then 109 Court Street and marked the beginning of world-wide telephone service." 109 Court Street is long gone. Within sight of Bell's plaque, across a street, is one of the central offices of NYNEX, the local Bell RBOC, on 6 Bowdoin Square. I cross the street and circle the telco building, slowly, hands in my jacket pockets. It's a bright, windy, New England autumn day. The central office is a handsome 1940s-era megalith in late Art Deco, eight stories high. Parked outside the back is a power-generation truck. The generator strikes me as rather anomalous. Don't they already have their own generators in this eight-story monster? Then the suspicion strikes me that NYNEX must have heard of the September 17 AT&T power-outage which crashed New York City. Belt-and-suspenders, this generator. Very telco. Over the glass doors of the front entrance is a handsome bronze bas-relief of Art Deco vines, sunflowers, and birds, entwining the Bell logo and the legend NEW ENGLAND TELEPHONE AND TELEGRAPH COMPANY -- an entity which no longer officially exists. The doors are locked securely. I peer through the shadowed glass. Inside is an official poster reading: New England Telephone a NYNEX Company ATTENTION All persons while on New England Telephone Company premises are required to visibly wear their identification cards (C.C.P. Section 2, Page 1). Visitors, vendors, contractors, and all others are required to visibly wear a daily pass. Thank you. Kevin C. Stanton. Building Security Coordinator. Outside, around the corner, is a pull-down ribbed metal security door, a locked delivery entrance. Some passing stranger has grafitti-tagged this door, with a single word in red spray-painted cursive: **Fury** 8. === My book on the Hacker Crackdown is almost over now. I have deliberately saved the best for last. In February 1991, I attended the CPSR Public Policy Roundtable, in Washington, DC. CPSR, Computer Professionals for Social Responsibility, was a sister organization of EFF, or perhaps its aunt, being older and perhaps somewhat wiser in the ways of the world of politics. Computer Professionals for Social Responsibility began in 1981 in Palo Alto, as an informal discussion group of Californian computer scientists and technicians, united by nothing more than an electronic mailing list. This typical high-tech ad-hocracy received the dignity of its own acronym in 1982, and was formally incorporated in 1983. CPSR lobbied government and public alike with an educational outreach effort, sternly warning against any foolish and unthinking trust in complex computer systems. CPSR insisted that mere computers should never be considered a magic panacea for humanity's social, ethical or political problems. CPSR members were especially troubled about the stability, safety, and dependability of military computer systems, and very especially troubled by those systems controlling nuclear arsenals. CPSR was best-known for its persistent and well- publicized attacks on the scientific credibility of the Strategic Defense Initiative ("Star Wars"). In 1990, CPSR was the nation's veteran cyber-political activist group, with over two thousand members in twenty- one local chapters across the US. It was especially active in Boston, Silicon Valley, and Washington DC, where its Washington office sponsored the Public Policy Roundtable. The Roundtable, however, had been funded by EFF, which had passed CPSR an extensive grant for operations. This was the first large-scale, official meeting of what was to become the electronic civil libertarian community. Sixty people attended, myself included -- in this instance, not so much as a journalist as a cyberpunk author. Many of the luminaries of the field took part: Kapor and Godwin as a matter of course. Richard Civille and Marc Rotenberg of CPSR. Jerry Berman of the ACLU. John Quarterman, author of The Matrix. Steven Levy, author of Hackers. George Perry and Sandy Weiss of Prodigy Services, there to network about the civil-liberties troubles their young commercial network was experiencing. Dr. Dorothy Denning. Cliff Figallo, manager of the Well. Steve Jackson was there, having finally found his ideal target audience, and so was Craig Neidorf, "Knight Lightning" himself, with his attorney, Sheldon Zenner. Katie Hafner, science journalist, and co- author of Cyberpunk: Outlaws and Hackers on the Computer Frontier. Dave Farber, ARPAnet pioneer and fabled Internet guru. Janlori Goldman of the ACLU's Project on Privacy and Technology. John Nagle of Autodesk and the Well. Don Goldberg of the House Judiciary Committee. Tom Guidoboni, the defense attorney in the Internet Worm case. Lance Hoffman, computer-science professor at The George Washington University. Eli Noam of Columbia. And a host of others no less distinguished. Senator Patrick Leahy delivered the keynote address, expressing his determination to keep ahead of the curve on the issue of electronic free speech. The address was well-received, and the sense of excitement was palpable. Every panel discussion was interesting -- some were entirely compelling. People networked with an almost frantic interest. I myself had a most interesting and cordial lunch discussion with Noel and Jeanne Gayler, Admiral Gayler being a former director of the National Security Agency. As this was the first known encounter between an actual no-kidding cyberpunk and a chief executive of America's largest and best-financed electronic espionage apparat, there was naturally a bit of eyebrow-raising on both sides. Unfortunately, our discussion was off-the-record. In fact all the discussions at the CPSR were officially off- the- record, the idea being to do some serious networking in an atmosphere of complete frankness, rather than to stage a media circus. In any case, CPSR Roundtable, though interesting and intensely valuable, was as nothing compared to the truly mind-boggling event that transpired a mere month later. 9. === "Computers, Freedom and Privacy." Four hundred people from every conceivable corner of America's electronic community. As a science fiction writer, I have been to some weird gigs in my day, but this thing is truly beyond the pale. Even "Cyberthon," Point Foundation's "Woodstock of Cyberspace" where Bay Area psychedelia collided headlong with the emergent world of computerized virtual reality, was like a Kiwanis Club gig compared to this astonishing do. The "electronic community" had reached an apogee. Almost every principal in this book is in attendance. Civil Libertarians. Computer Cops. The Digital Underground. Even a few discreet telco people. Colorcoded dots for lapel tags are distributed. Free Expression issues. Law Enforcement. Computer Security. Privacy. Journalists. Lawyers. Educators. Librarians. Programmers. Stylish punk-black dots for the hackers and phone phreaks. Almost everyone here seems to wear eight or nine dots, to have six or seven professional hats. It is a community. Something like Lebanon perhaps, but a digital nation. People who had feuded all year in the national press, people who entertained the deepest suspicions of one another's motives and ethics, are now in each others' laps. "Computers, Freedom and Privacy" had every reason in the world to turn ugly, and yet except for small irruptions of puzzling nonsense from the convention's token lunatic, a surprising bonhomie reigned. CFP was like a wedding-party in which two lovers, unstable bride and charlatan groom, tie the knot in a clearly disastrous matrimony. It is clear to both families -- even to neighbors and random guests -- that this is not a workable relationship, and yet the young couple's desperate attraction can brook no further delay. They simply cannot help themselves. Crockery will fly, shrieks from their newlywed home will wake the city block, divorce waits in the wings like a vulture over the Kalahari, and yet this is a wedding, and there is going to be a child from it. Tragedies end in death; comedies in marriage. The Hacker Crackdown is ending in marriage. And there will be a child. From the beginning, anomalies reign. John Perry Barlow, cyberspace ranger, is here. His color photo in The New York Times Magazine, Barlow scowling in a grim Wyoming snowscape, with long black coat, dark hat, a Macintosh SE30 propped on a fencepost and an awesome frontier rifle tucked under one arm, will be the single most striking visual image of the Hacker Crackdown. And he is CFP's guest of honor -- along with Gail Thackeray of the FCIC! What on earth do they expect these dual guests to do with each other? Waltz? Barlow delivers the first address. Uncharacteristically, he is hoarse -- the sheer volume of roadwork has worn him down. He speaks briefly, congenially, in a plea for conciliation, and takes his leave to a storm of applause. Then Gail Thackeray takes the stage. She's visibly nervous. She's been on the Well a lot lately. Reading those Barlow posts. Following Barlow is a challenge to anyone. In honor of the famous lyricist for the Grateful Dead, she announces reedily, she is going to read -- a poem. A poem she has composed herself. It's an awful poem, doggerel in the rollicking meter of Robert W. Service's The Cremation of Sam McGee, but it is in fact, a poem. It's the Ballad of the Electronic Frontier! A poem about the Hacker Crackdown and the sheer unlikelihood of CFP. It's full of in-jokes. The score or so cops in the audience, who are sitting together in a nervous claque, are absolutely cracking-up. Gail's poem is the funniest goddamn thing they've ever heard. The hackers and civil-libs, who had this woman figured for Ilsa She-Wolf of the SS, are staring with their jaws hanging loosely. Never in the wildest reaches of their imagination had they figured Gail Thackeray was capable of such a totally off-the-wall move. You can see them punching their mental CONTROL-RESET buttons. Jesus! This woman's a hacker weirdo! She's just like us! God, this changes everything! Al Bayse, computer technician for the FBI, had been the only cop at the CPSR Roundtable, dragged there with his arm bent by Dorothy Denning. He was guarded and tightlipped at CPSR Roundtable; a "lion thrown to the Christians." At CFP, backed by a claque of cops, Bayse suddenly waxes eloquent and even droll, describing the FBI's "NCIC 2000", a gigantic digital catalog of criminal records, as if he has suddenly become some weird hybrid of George Orwell and George Gobel. Tentatively, he makes an arcane joke about statistical analysis. At least a third of the crowd laughs aloud. "They didn't laugh at that at my last speech," Bayse observes. He had been addressing cops -- straight cops, not computer people. It had been a worthy meeting, useful one supposes, but nothing like this. There has never been anything like this. Without any prodding, without any preparation, people in the audience simply begin to ask questions. Longhairs, freaky people, mathematicians. Bayse is answering, politely, frankly, fully, like a man walking on air. The ballroom's atmosphere crackles with surreality. A female lawyer behind me breaks into a sweat and a hot waft of surprisingly potent and musky perfume flows off her pulse-points. People are giddy with laughter. People are interested, fascinated, their eyes so wide and dark that they seem eroticized. Unlikely daisy-chains form in the halls, around the bar, on the escalators: cops with hackers, civil rights with FBI, Secret Service with phone phreaks. Gail Thackeray is at her crispest in a white wool sweater with a tiny Secret Service logo. "I found Phiber Optik at the payphones, and when he saw my sweater, he turned into a pillar of salt!" she chortles. Phiber discusses his case at much length with his arresting officer, Don Delaney of the New York State Police. After an hour's chat, the two of them look ready to begin singing "Auld Lang Syne." Phiber finally finds the courage to get his worst complaint off his chest. It isn't so much the arrest. It was the charge. Pirating service off 900 numbers. I'm a programmer, Phiber insists. This lame charge is going to hurt my reputation. It would have been cool to be busted for something happening, like Section 1030 computer intrusion. Maybe some kind of crime that's scarcely been invented yet. Not lousy phone fraud. Phooey. Delaney seems regretful. He had a mountain of possible criminal charges against Phiber Optik. The kid's gonna plead guilty anyway. He's a first timer, they always plead. Coulda charged the kid with most anything, and gotten the same result in the end. Delaney seems genuinely sorry not to have gratified Phiber in this harmless fashion. Too late now. Phiber's pled already. All water under the bridge. Whaddya gonna do? Delaney's got a good grasp on the hacker mentality. He held a press conference after he busted a bunch of Masters of Deception kids. Some journo had asked him: "Would you describe these people as geniuses?" Delaney's deadpan answer, perfect: "No, I would describe these people as defendants." Delaney busts a kid for hacking codes with repeated random dialling. Tells the press that NYNEX can track this stuff in no time flat nowadays, and a kid has to be stupid to do something so easy to catch. Dead on again: hackers don't mind being thought of as Genghis Khan by the straights, but if there's anything that really gets 'em where they live, it's being called dumb. Won't be as much fun for Phiber next time around. As a second offender he's gonna see prison. Hackers break the law. They're not geniuses, either. They're gonna be defendants. And yet, Delaney muses over a drink in the hotel bar, he has found it impossible to treat them as common criminals. Delaney knows criminals. These kids, by comparison, are clueless -- there is just no crook vibe off of them, they don't smell right, they're just not bad. Delaney has seen a lot of action. He did Vietnam. He's been shot at, he has shot people. He's a homicide cop from New York. He has the appearance of a man who has not only seen the shit hit the fan but has seen it splattered across whole city blocks and left to ferment for years. This guy has been around. He listens to Steve Jackson tell his story. The dreamy game strategist has been dealt a bad hand. He has played it for all he is worth. Under his nerdish SF-fan exterior is a core of iron. Friends of his say Steve Jackson believes in the rules, believes in fair play. He will never compromise his principles, never give up. "Steve," Delaney says to Steve Jackson, "they had some balls, whoever busted you. You're all right!" Jackson, stunned, falls silent and actually blushes with pleasure. Neidorf has grown up a lot in the past year. The kid is a quick study, you gotta give him that. Dressed by his mom, the fashion manager for a national clothing chain, Missouri college techie-frat Craig Neidorf out-dappers everyone at this gig but the toniest East Coast lawyers. The iron jaws of prison clanged shut without him and now law school beckons for Neidorf. He looks like a larval Congressman. Not a "hacker," our Mr. Neidorf. He's not interested in computer science. Why should he be? He's not interested in writing C code the rest of his life, and besides, he's seen where the chips fall. To the world of computer science he and Phrack were just a curiosity. But to the world of law.... The kid has learned where the bodies are buried. He carries his notebook of press clippings wherever he goes. Phiber Optik makes fun of Neidorf for a Midwestern geek, for believing that "Acid Phreak" does acid and listens to acid rock. Hell no. Acid's never done acid! Acid's into acid house music. Jesus. The very idea of doing LSD. Our parents did LSD, ya clown. Thackeray suddenly turns upon Craig Neidorf the full lighthouse glare of her attention and begins a determined half-hour attempt to win the boy over. The Joan of Arc of Computer Crime is giving career advice to Knight Lightning! "Your experience would be very valuable -- a real asset," she tells him with unmistakeable sixty-thousand-watt sincerity. Neidorf is fascinated. He listens with unfeigned attention. He's nodding and saying yes ma'am. Yes, Craig, you too can forget all about money and enter the glamorous and horribly underpaid world of PROSECUTING COMPUTER CRIME! You can put your former friends in prison -- ooops.... You cannot go on dueling at modem's length indefinitely. You cannot beat one another senseless with rolled-up press-clippings. Sooner or later you have to come directly to grips. And yet the very act of assembling here has changed the entire situation drastically. John Quarterman, author of The Matrix, explains the Internet at his symposium. It is the largest news network in the world, it is growing by leaps and bounds, and yet you cannot measure Internet because you cannot stop it in place. It cannot stop, because there is no one anywhere in the world with the authority to stop Internet. It changes, yes, it grows, it embeds itself across the post-industrial, postmodern world and it generates community wherever it touches, and it is doing this all by itself. Phiber is different. A very fin de siecle kid, Phiber Optik. Barlow says he looks like an Edwardian dandy. He does rather. Shaven neck, the sides of his skull cropped hip-hop close, unruly tangle of black hair on top that looks pomaded, he stays up till four a.m. and misses all the sessions, then hangs out in payphone booths with his acoustic coupler gutsily CRACKING SYSTEMS RIGHT IN THE MIDST OF THE HEAVIEST LAW ENFORCEMENT DUDES IN THE U.S., or at least pretending to.... Unlike "Frank Drake." Drake, who wrote Dorothy Denning out of nowhere, and asked for an interview for his cheapo cyberpunk fanzine, and then started grilling her on her ethics. She was squirmin', too.... Drake, scarecrow-tall with his floppy blond mohawk, rotting tennis shoes and black leather jacket lettered ILLUMINATI in red, gives off an unmistakeable air of the bohemian literatus. Drake is the kind of guy who reads British industrial design magazines and appreciates William Gibson because the quality of the prose is so tasty. Drake could never touch a phone or a keyboard again, and he'd still have the nose- ring and the blurry photocopied fanzines and the sampled industrial music. He's a radical punk with a desktop- publishing rig and an Internet address. Standing next to Drake, the diminutive Phiber looks like he's been physically coagulated out of phone-lines. Born to phreak. Dorothy Denning approaches Phiber suddenly. The two of them are about the same height and body-build. Denning's blue eyes flash behind the round window- frames of her glasses. "Why did you say I was 'quaint?'" she asks Phiber, quaintly. It's a perfect description but Phiber is nonplussed... "Well, I uh, you know...." "I also think you're quaint, Dorothy," I say, novelist to the rescue, the journo gift of gab... She is neat and dapper and yet there's an arcane quality to her, something like a Pilgrim Maiden behind leaded glass; if she were six inches high Dorothy Denning would look great inside a china cabinet... The Cryptographeress.... The Cryptographrix... whatever... Weirdly, Peter Denning looks just like his wife, you could pick this gentleman out of a thousand guys as the soulmate of Dorothy Denning. Wearing tailored slacks, a spotless fuzzy varsity sweater, and a neatly knotted academician's tie.... This fineboned, exquisitely polite, utterly civilized and hyperintelligent couple seem to have emerged from some cleaner and finer parallel universe, where humanity exists to do the Brain Teasers column in Scientific American. Why does this Nice Lady hang out with these unsavory characters? Because the time has come for it, that's why. Because she's the best there is at what she does. Donn Parker is here, the Great Bald Eagle of Computer Crime.... With his bald dome, great height, and enormous Lincoln-like hands, the great visionary pioneer of the field plows through the lesser mortals like an icebreaker.... His eyes are fixed on the future with the rigidity of a bronze statue.... Eventually, he tells his audience, all business crime will be computer crime, because businesses will do everything through computers. "Computer crime" as a category will vanish. In the meantime, passing fads will flourish and fail and evaporate.... Parker's commanding, resonant voice is sphinxlike, everything is viewed from some eldritch valley of deep historical abstraction... Yes, they've come and they've gone, these passing flaps in the world of digital computation.... The radio-frequency emanation scandal... KGB and MI5 and CIA do it every day, it's easy, but nobody else ever has.... The salami-slice fraud, mostly mythical... "Crimoids," he calls them.... Computer viruses are the current crimoid champ, a lot less dangerous than most people let on, but the novelty is fading and there's a crimoid vacuum at the moment, the press is visibly hungering for something more outrageous.... The Great Man shares with us a few speculations on the coming crimoids.... Desktop Forgery! Wow.... Computers stolen just for the sake of the information within them -- data- napping! Happened in Britain a while ago, could be the coming thing.... Phantom nodes in the Internet! Parker handles his overhead projector sheets with an ecclesiastical air... He wears a grey double-breasted suit, a light blue shirt, and a very quiet tie of understated maroon and blue paisley... Aphorisms emerge from him with slow, leaden emphasis... There is no such thing as an adequately secure computer when one faces a sufficiently powerful adversary.... Deterrence is the most socially useful aspect of security... People are the primary weakness in all information systems... The entire baseline of computer security must be shifted upward.... Don't ever violate your security by publicly describing your security measures... People in the audience are beginning to squirm, and yet there is something about the elemental purity of this guy's philosophy that compels uneasy respect.... Parker sounds like the only sane guy left in the lifeboat, sometimes. The guy who can prove rigorously, from deep moral principles, that Harvey there, the one with the broken leg and the checkered past, is the one who has to be, err.... that is, Mr. Harvey is best placed to make the necessary sacrifice for the security and indeed the very survival of the rest of this lifeboat's crew.... Computer security, Parker informs us mournfully, is a nasty topic, and we wish we didn't have to have it... The security expert, armed with method and logic, must think -- imagine -- everything that the adversary might do before the adversary might actually do it. It is as if the criminal's dark brain were an extensive subprogram within the shining cranium of Donn Parker. He is a Holmes whose Moriarty does not quite yet exist and so must be perfectly simulated. CFP is a stellar gathering, with the giddiness of a wedding. It is a happy time, a happy ending, they know their world is changing forever tonight, and they're proud to have been there to see it happen, to talk, to think, to help. And yet as night falls, a certain elegiac quality manifests itself, as the crowd gathers beneath the chandeliers with their wineglasses and dessert plates. Something is ending here, gone forever, and it takes a while to pinpoint it. It is the End of the Amateurs. ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_part2.txt0000600000175000017500000066056310246051200031244 0ustar madduckmadduckrestindex crumb: Hacker Part 2 /restindex ==================================== The Hacker Crackdown - Part Two ==================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html .. contents :: Part Two - In Parts Part Two: The Digital Underground =================================== The date was May 9, 1990. The Pope was touring Mexico City. Hustlers from the Medellin Cartel were trying to buy black-market Stinger missiles in Florida. On the comics page, Doonesbury character Andy was dying of AIDS. And then.... a highly unusual item whose novelty and calculated rhetoric won it headscratching attention in newspapers all over America. The US Attorney's office in Phoenix, Arizona, had issued a press release announcing a nationwide law enforcement crackdown against "illegal computer hacking activities." The sweep was officially known as "Operation Sundevil." Eight paragraphs in the press release gave the bare facts: twenty- seven search warrants carried out on May 8, with three arrests, and a hundred and fifty agents on the prowl in "twelve" cities across America. (Different counts in local press reports yielded "thirteen," "fourteen," and "sixteen" cities.) Officials estimated that criminal losses of revenue to telephone companies "may run into millions of dollars." Credit for the Sundevil investigations was taken by the US Secret Service, Assistant US Attorney Tim Holtzen of Phoenix, and the Assistant Attorney General of Arizona, Gail Thackeray. The prepared remarks of Garry M. Jenkins, appearing in a U.S. Department of Justice press release, were of particular interest. Mr. Jenkins was the Assistant Director of the US Secret Service, and the highest-ranking federal official to take any direct public role in the hacker crackdown of 1990. "Today, the Secret Service is sending a clear message to those computer hackers who have decided to violate the laws of this nation in the mistaken belief that they can successfully avoid detection by hiding behind the relative anonymity of their computer terminals.(...) "Underground groups have been formed for the purpose of exchanging information relevant to their criminal activities. These groups often communicate with each other through message systems between computers called 'bulletin boards.' "Our experience shows that many computer hacker suspects are no longer misguided teenagers, mischievously playing games with their computers in their bedrooms. Some are now high tech computer operators using computers to engage in unlawful conduct." Who were these "underground groups" and "hightech operators?" Where had they come from? What did they want? Who *were* they? Were they "mischievous?" Were they dangerous? How had "misguided teenagers" managed to alarm the United States Secret Service? And just how widespread was this sort of thing? Of all the major players in the Hacker Crackdown: the phone companies, law enforcement, the civil libertarians, and the "hackers" themselves -- the "hackers" are by far the most mysterious, by far the hardest to understand, by far the *weirdest*. Not only are "hackers" novel in their activities, but they come in a variety of odd subcultures, with a variety of languages, motives and values. The earliest proto-hackers were probably those unsung mischievous telegraph boys who were summarily fired by the Bell Company in 1878. Legitimate "hackers," those computer enthusiasts who are independent-minded but law-abiding, generally trace their spiritual ancestry to elite technical universities, especially M.I.T. and Stanford, in the 1960s. But the genuine roots of the modern hacker *underground* can probably be traced most successfully to a now much-obscured hippie anarchist movement known as the Yippies. The Yippies, who took their name from the largely fictional "Youth International Party," carried out a loud and lively policy of surrealistic subversion and outrageous political mischief. Their basic tenets were flagrant sexual promiscuity, open and copious drug use, the political overthrow of any powermonger over thirty years of age, and an immediate end to the war in Vietnam, by any means necessary, including the psychic levitation of the Pentagon. The two most visible Yippies were Abbie Hoffman and Jerry Rubin. Rubin eventually became a Wall Street broker. Hoffman, ardently sought by federal authorities, went into hiding for seven years, in Mexico, France, and the United States. While on the lam, Hoffman continued to write and publish, with help from sympathizers in the American anarcho-leftist underground. Mostly, Hoffman survived through false ID and odd jobs. Eventually he underwent facial plastic surgery and adopted an entirely new identity as one "Barry Freed." After surrendering himself to authorities in 1980, Hoffman spent a year in prison on a cocaine conviction. Hoffman's worldview grew much darker as the glory days of the 1960s faded. In 1989, he purportedly committed suicide, under odd and, to some, rather suspicious circumstances. Abbie Hoffman is said to have caused the Federal Bureau of Investigation to amass the single largest investigation file ever opened on an individual American citizen. (If this is true, it is still questionable whether the FBI regarded Abbie Hoffman a serious public threat -quite possibly, his file was enormous simply because Hoffman left colorful legendry wherever he went). He was a gifted publicist, who regarded electronic media as both playground and weapon. He actively enjoyed manipulating network TV and other gullible, imagehungry media, with various weird lies, mindboggling rumors, impersonation scams, and other sinister distortions, all absolutely guaranteed to upset cops, Presidential candidates, and federal judges. Hoffman's most famous work was a book self-reflexively known as *Steal This Book*, which publicized a number of methods by which young, penniless hippie agitators might live off the fat of a system supported by humorless drones. *Steal This Book*, whose title urged readers to damage the very means of distribution which had put it into their hands, might be described as a spiritual ancestor of a computer virus. Hoffman, like many a later conspirator, made extensive use of pay- phones for his agitation work -- in his case, generally through the use of cheap brass washers as coin-slugs. During the Vietnam War, there was a federal surtax imposed on telephone service; Hoffman and his cohorts could, and did, argue that in systematically stealing phone service they were engaging in civil disobedience: virtuously denying tax funds to an illegal and immoral war. But this thin veil of decency was soon dropped entirely. Ripping-off the System found its own justification in deep alienation and a basic outlaw contempt for conventional bourgeois values. Ingenious, vaguely politicized varieties of rip-off, which might be described as "anarchy by convenience," became very popular in Yippie circles, and because rip-off was so useful, it was to survive the Yippie movement itself. In the early 1970s, it required fairly limited expertise and ingenuity to cheat payphones, to divert "free" electricity and gas service, or to rob vending machines and parking meters for handy pocket change. It also required a conspiracy to spread this knowledge, and the gall and nerve actually to commit petty theft, but the Yippies had these qualifications in plenty. In June 1971, Abbie Hoffman and a telephone enthusiast sarcastically known as "Al Bell" began publishing a newsletter called *Youth International Party Line*. This newsletter was dedicated to collating and spreading Yippie rip-off techniques, especially of phones, to the joy of the freewheeling underground and the insensate rage of all straight people. As a political tactic, phone-service theft ensured that Yippie advocates would always have ready access to the long-distance telephone as a medium, despite the Yippies' chronic lack of organization, discipline, money, or even a steady home address. *Party Line* was run out of Greenwich Village for a couple of years, then "Al Bell" more or less defected from the faltering ranks of Yippiedom, changing the newsletter's name to *TAP* or *Technical Assistance Program*. After the Vietnam War ended, the steam began leaking rapidly out of American radical dissent. But by this time, "Bell" and his dozen or so core contributors had the bit between their teeth, and had begun to derive tremendous gut-level satisfaction from the sensation of pure *technical power*. *TAP* articles, once highly politicized, became pitilessly jargonized and technical, in homage or parody to the Bell System's own technical documents, which *TAP* studied closely, gutted, and reproduced without permission. The *TAP* elite revelled in gloating possession of the specialized knowledge necessary to beat the system. "Al Bell" dropped out of the game by the late 70s, and "Tom Edison" took over; *TAP* readers (some 1400 of them, all told) now began to show more interest in telex switches and the growing phenomenon of computer systems. In 1983, "Tom Edison" had his computer stolen and his house set on fire by an arsonist. This was an eventually mortal blow to *TAP* (though the legendary name was to be resurrected in 1990 by a young Kentuckian computeroutlaw named "Predat0r.") 1. ==== Ever since telephones began to make money, there have been people willing to rob and defraud phone companies. The legions of petty phone thieves vastly outnumber those "phone phreaks" who "explore the system" for the sake of the intellectual challenge. The New York metropolitan area (long in the vanguard of American crime) claims over 150,000 physical attacks on pay telephones every year! Studied carefully, a modern payphone reveals itself as a little fortress, carefully designed and redesigned over generations, to resist coinslugs, zaps of electricity, chunks of coin-shaped ice, prybars, magnets, lockpicks, blasting caps. Public pay- phones must survive in a world of unfriendly, greedy people, and a modern payphone is as exquisitely evolved as a cactus. Because the phone network pre-dates the computer network, the scofflaws known as "phone phreaks" pre-date the scofflaws known as "computer hackers." In practice, today, the line between "phreaking" and "hacking" is very blurred, just as the distinction between telephones and computers has blurred. The phone system has been digitized, and computers have learned to "talk" over phone-lines. What's worse -- and this was the point of the Mr. Jenkins of the Secret Service -- some hackers have learned to steal, and some thieves have learned to hack. Despite the blurring, one can still draw a few useful behavioral distinctions between "phreaks" and "hackers." Hackers are intensely interested in the "system" per se, and enjoy relating to machines. "Phreaks" are more social, manipulating the system in a rough-and-ready fashion in order to get through to other human beings, fast, cheap and under the table. Phone phreaks love nothing so much as "bridges," illegal conference calls of ten or twelve chatting conspirators, seaboard to seaboard, lasting for many hours -- and running, of course, on somebody else's tab, preferably a large corporation's. As phone-phreak conferences wear on, people drop out (or simply leave the phone off the hook, while they sashay off to work or school or babysitting), and new people are phoned up and invited to join in, from some other continent, if possible. Technical trivia, boasts, brags, lies, head-trip deceptions, weird rumors, and cruel gossip are all freely exchanged. The lowest rung of phone- phreaking is the theft of telephone access codes. Charging a phone call to somebody else's stolen number is, of course, a pig-easy way of stealing phone service, requiring practically no technical expertise. This practice has been very widespread, especially among lonely people without much money who are far from home. Code theft has flourished especially in college dorms, military bases, and, notoriously, among roadies for rock bands. Of late, code theft has spread very rapidly among Third Worlders in the US, who pile up enormous unpaid long-distance bills to the Caribbean, South America, and Pakistan. The simplest way to steal phone-codes is simply to look over a victim's shoulder as he punches-in his own code-number on a public payphone. This technique is known as "shoulder-surfing," and is especially common in airports, bus terminals, and train stations. The code is then sold by the thief for a few dollars. The buyer abusing the code has no computer expertise, but calls his Mom in New York, Kingston or Caracas and runs up a huge bill with impunity. The losses from this primitive phreaking activity are far, far greater than the monetary losses caused by computer-intruding hackers. In the mid-to-late 1980s, until the introduction of sterner telco security measures, *computerized* code theft worked like a charm, and was virtually omnipresent throughout the digital underground, among phreaks and hackers alike. This was accomplished through programming one's computer to try random code numbers over the telephone until one of them worked. Simple programs to do this were widely available in the underground; a computer running all night was likely to come up with a dozen or so useful hits. This could be repeated week after week until one had a large library of stolen codes. Nowadays, the computerized dialling of hundreds of numbers can be detected within hours and swiftly traced. If a stolen code is repeatedly abused, this too can be detected within a few hours. But for years in the 1980s, the publication of stolen codes was a kind of elementary etiquette for fledgling hackers. The simplest way to establish your bona-fides as a raider was to steal a code through repeated random dialling and offer it to the "community" for use. Codes could be both stolen, and used, simply and easily from the safety of one's own bedroom, with very little fear of detection or punishment. Before computers and their phone-line modems entered American homes in gigantic numbers, phone phreaks had their own special telecommunications hardware gadget, the famous "blue box." This fraud device (now rendered increasingly useless by the digital evolution of the phone system) could trick switching systems into granting free access to long-distance lines. It did this by mimicking the system's own signal, a tone of 2600 hertz. Steven Jobs and Steve Wozniak, the founders of Apple Computer, Inc., once dabbled in selling blue-boxes in college dorms in California. For many, in the early days of phreaking, blue-boxing was scarcely perceived as "theft," but rather as a fun (if sneaky) way to use excess phone capacity harmlessly. After all, the long-distance lines were *just sitting there*.... Whom did it hurt, really? If you're not *damaging* the system, and you're not *using up any tangible resource*, and if nobody *finds out* what you did, then what real harm have you done? What exactly *have* you "stolen," anyway? If a tree falls in the forest and nobody hears it, how much is the noise worth? Even now this remains a rather dicey question. Blue-boxing was no joke to the phone companies, however. Indeed, when *Ramparts* magazine, a radical publication in California, printed the wiring schematics necessary to create a mute box in June 1972, the magazine was seized by police and Pacific Bell phonecompany officials. The mute box, a blue-box variant, allowed its user to receive long-distance calls free of charge to the caller. This device was closely described in a *Ramparts* article wryly titled "Regulating the Phone Company In Your Home." Publication of this article was held to be in violation of Californian State Penal Code section 502.7, which outlaws ownership of wire-fraud devices and the selling of "plans or instructions for any instrument, apparatus, or device intended to avoid telephone toll charges." Issues of *Ramparts* were recalled or seized on the newsstands, and the resultant loss of income helped put the magazine out of business. This was an ominous precedent for free-expression issues, but the telco's crushing of a radical-fringe magazine passed without serious challenge at the time. Even in the freewheeling California 1970s, it was widely felt that there was something sacrosanct about what the phone company knew; that the telco had a legal and moral right to protect itself by shutting off the flow of such illicit information. Most telco information was so "specialized" that it would scarcely be understood by any honest member of the public. If not published, it would not be missed. To print such material did not seem part of the legitimate role of a free press. In 1990 there would be a similar telco-inspired attack on the electronic phreak/hacking "magazine" *Phrack*. The *Phrack *legal case became a central issue in the Hacker Crackdown, and gave rise to great controversy. *Phrack* would also be shut down, for a time, at least, but this time both the telcos and their law-enforcement allies would pay a much larger price for their actions. The *Phrack* case will be examined in detail, later. Phone-phreaking as a social practice is still very much alive at this moment. Today, phone-phreaking is thriving much more vigorously than the better-known and worse-feared practice of "computer hacking." New forms of phreaking are spreading rapidly, following new vulnerabilities in sophisticated phone services. Cellular phones are especially vulnerable; their chips can be re- programmed to present a false caller ID and avoid billing. Doing so also avoids police tapping, making cellular-phone abuse a favorite among drug- dealers. "Call-sell operations" using pirate cellular phones can, and have, been run right out of the backs of cars, which move from "cell" to "cell" in the local phone system, retailing stolen long-distance service, like some kind of demented electronic version of the neighborhood ice-cream truck. Private branch-exchange phone systems in large corporations can be penetrated; phreaks dial-up a local company, enter its internal phone- system, hack it, then use the company's own PBX system to dial back out over the public network, causing the company to be stuck with the resulting long-distance bill. This technique is known as "diverting." "Diverting" can be very costly, especially because phreaks tend to travel in packs and never stop talking. Perhaps the worst by-product of this "PBX fraud" is that victim companies and telcos have sued one another over the financial responsibility for the stolen calls, thus enriching not only shabby phreaks but well-paid lawyers. "Voice-mail systems" can also be abused; phreaks can seize their own sections of these sophisticated electronic answering machines, and use them for trading codes or knowledge of illegal techniques. Voice- mail abuse does not hurt the company directly, but finding supposedly empty slots in your company's answering machine all crammed with phreaks eagerly chattering and hey-duding one another in impenetrable jargon can cause sensations of almost mystical repulsion and dread. Worse yet, phreaks have sometimes been known to react truculently to attempts to "clean up" the voice-mail system. Rather than humbly acquiescing to being thrown out of their playground, they may very well call up the company officials at work (or at home) and loudly demand free voice-mail addresses of their very own. Such bullying is taken very seriously by spooked victims. Acts of phreak revenge against straight people are rare, but voice- mail systems are especially tempting and vulnerable, and an infestation of angry phreaks in one's voice-mail system is no joke. They can erase legitimate messages; or spy on private messages; or harass users with recorded taunts and obscenities. They've even been known to seize control of voice-mail security, and lock out legitimate users, or even shut down the system entirely. Cellular phone-calls, cordless phones, and ship-toshore telephony can all be monitored by various forms of radio; this kind of "passive monitoring" is spreading explosively today. Technically eavesdropping on other people's cordless and cellular phone-calls is the fastestgrowing area in phreaking today. This practice strongly appeals to the lust for power and conveys gratifying sensations of technical superiority over the eavesdropping victim. Monitoring is rife with all manner of tempting evil mischief. Simple prurient snooping is by far the most common activity. But credit-card numbers unwarily spoken over the phone can be recorded, stolen and used. And tapping people's phone-calls (whether through active telephone taps or passive radio monitors) does lend itself conveniently to activities like blackmail, industrial espionage, and political dirty tricks. It should be repeated that telecommunications fraud, the theft of phone service, causes vastly greater monetary losses than the practice of entering into computers by stealth. Hackers are mostly young suburban American white males, and exist in their hundreds -- but "phreaks" come from both sexes and from many nationalities, ages and ethnic backgrounds, and are flourishing in the thousands. 2. ==== The term "hacker" has had an unfortunate history. This book, *The Hacker Crackdown*, has little to say about "hacking" in its finer, original sense. The term can signify the free-wheeling intellectual exploration of the highest and deepest potential of computer systems. Hacking can describe the determination to make access to computers and information as free and open as possible. Hacking can involve the heartfelt conviction that beauty can be found in computers, that the fine aesthetic in a perfect program can liberate the mind and spirit. This is "hacking" as it was defined in Steven Levy's much-praised history of the pioneer computer milieu, *Hackers*, published in 1984. Hackers of all kinds are absolutely soaked through with heroic anti-bureaucratic sentiment. Hackers long for recognition as a praiseworthy cultural archetype, the postmodern electronic equivalent of the cowboy and mountain man. Whether they deserve such a reputation is something for history to decide. But many hackers -including those outlaw hackers who are computer intruders, and whose activities are defined as criminal -actually attempt to *live up to* this techno-cowboy reputation. And given that electronics and telecommunications are still largely unexplored territories, there is simply *no telling* what hackers might uncover. For some people, this freedom is the very breath of oxygen, the inventive spontaneity that makes life worth living and that flings open doors to marvellous possibility and individual empowerment. But for many people -- and increasingly so -- the hacker is an ominous figure, a smartaleck sociopath ready to burst out of his basement wilderness and savage other people's lives for his own anarchical convenience. Any form of power without responsibility, without direct and formal checks and balances, is frightening to people -- and reasonably so. It should be frankly admitted that hackers *are* frightening, and that the basis of this fear is not irrational. Fear of hackers goes well beyond the fear of merely criminal activity. Subversion and manipulation of the phone system is an act with disturbing political overtones. In America, computers and telephones are potent symbols of organized authority and the technocratic business elite. But there is an element in American culture that has always strongly rebelled against these symbols; rebelled against all large industrial computers and all phone companies. A certain anarchical tinge deep in the American soul delights in causing confusion and pain to all bureaucracies, including technological ones. There is sometimes malice and vandalism in this attitude, but it is a deep and cherished part of the American national character. The outlaw, the rebel, the rugged individual, the pioneer, the sturdy Jeffersonian yeoman, the private citizen resisting interference in his pursuit of happiness -- these are figures that all Americans recognize, and that many will strongly applaud and defend. Many scrupulously law-abiding citizens today do cutting-edge work with electronics -- work that has already had tremendous social influence and will have much more in years to come. In all truth, these talented, hardworking, law-abiding, mature, adult people are far more disturbing to the peace and order of the current status quo than any scofflaw group of romantic teenage punk kids. These law-abiding hackers have the power, ability, and willingness to influence other people's lives quite unpredictably. They have means, motive, and opportunity to meddle drastically with the American social order. When corralled into governments, universities, or large multinational companies, and forced to follow rulebooks and wear suits and ties, they at least have some conventional halters on their freedom of action. But when loosed alone, or in small groups, and fired by imagination and the entrepreneurial spirit, they can move mountains - causing landslides that will likely crash directly into your office and living room. These people, as a class, instinctively recognize that a public, politicized attack on hackers will eventually spread to them -- that the term "hacker," once demonized, might be used to knock their hands off the levers of power and choke them out of existence. There are hackers today who fiercely and publicly resist any besmirching of the noble title of hacker. Naturally and understandably, they deeply resent the attack on their values implicit in using the word "hacker" as a synonym for computer-criminal. This book, sadly but in my opinion unavoidably, rather adds to the degradation of the term. It concerns itself mostly with "hacking" in its commonest latter-day definition, i.e., intruding into computer systems by stealth and without permission. The term "hacking" is used routinely today by almost all law enforcement officials with any professional interest in computer fraud and abuse. American police describe almost any crime committed with, by, through, or against a computer as hacking. Most importantly, "hacker" is what computerintruders choose to call *themselves*. Nobody who "hacks" into systems willingly describes himself (rarely, herself) as a "computer intruder," "computer trespasser," "cracker," "wormer," "darkside hacker" or "high tech street gangster." Several other demeaning terms have been invented in the hope that the press and public will leave the original sense of the word alone. But few people actually use these terms. (I exempt the term "cyberpunk," which a few hackers and law enforcement people actually do use. The term "cyberpunk" is drawn from literary criticism and has some odd and unlikely resonances, but, like hacker, cyberpunk too has become a criminal pejorative today.) In any case, breaking into computer systems was hardly alien to the original hacker tradition. The first tottering systems of the 1960s required fairly extensive internal surgery merely to function day-by-day. Their users "invaded" the deepest, most arcane recesses of their operating software almost as a matter of routine. "Computer security" in these early, primitive systems was at best an afterthought. What security there was, was entirely physical, for it was assumed that anyone allowed near this expensive, arcane hardware would be a fully qualified professional expert. In a campus environment, though, this meant that grad students, teaching assistants, undergraduates, and eventually, all manner of dropouts and hangers-on ended up accessing and often running the works. Universities, even modern universities, are not in the business of maintaining security over information. On the contrary, universities, as institutions, pre-date the "information economy" by many centuries and are notfor-profit cultural entities, whose reason for existence (purportedly) is to discover truth, codify it through techniques of scholarship, and then teach it. Universities are meant to *pass the torch of civilization*, not just download data into student skulls, and the values of the academic community are strongly at odds with those of all would-be information empires. Teachers at all levels, from kindergarten up, have proven to be shameless and persistent software and data pirates. Universities do not merely "leak information" but vigorously broadcast free thought. This clash of values has been fraught with controversy. Many hackers of the 1960s remember their professional apprenticeship as a long guerilla war against the uptight mainframe-computer "information priesthood." These computer-hungry youngsters had to struggle hard for access to computing power, and many of them were not above certain, er, shortcuts. But, over the years, this practice freed computing from the sterile reserve of lab-coated technocrats and was largely responsible for the explosive growth of computing in general society -- especially *personal* computing. Access to technical power acted like catnip on certain of these youngsters. Most of the basic techniques of computer intrusion: password cracking, trapdoors, backdoors, trojan horses -- were invented in college environments in the 1960s, in the early days of network computing. Some off-the-cuff experience at computer intrusion was to be in the informal resume of most "hackers" and many future industry giants. Outside of the tiny cult of computer enthusiasts, few people thought much about the implications of "breaking into" computers. This sort of activity had not yet been publicized, much less criminalized. In the 1960s, definitions of "property" and "privacy" had not yet been extended to cyberspace. Computers were not yet indispensable to society. There were no vast databanks of vulnerable, proprietary information stored in computers, which might be accessed, copied without permission, erased, altered, or sabotaged. The stakes were low in the early days -- but they grew every year, exponentially, as computers themselves grew. By the 1990s, commercial and political pressures had become overwhelming, and they broke the social boundaries of the hacking subculture. Hacking had become too important to be left to the hackers. Society was now forced to tackle the intangible nature of cyberspace-as- property, cyberspace as privately-owned unreal-estate. In the new, severe, responsible, highstakes context of the "Information Society" of the 1990s, "hacking" was called into question. What did it mean to break into a computer without permission and use its computational power, or look around inside its files without hurting anything? What were computer-intruding hackers, anyway -- how should society, and the law, best define their actions? Were they just *browsers*, harmless intellectual explorers? Were they *voyeurs*, snoops, invaders of privacy? Should they be sternly treated as potential *agents of espionage*, or perhaps as *industrial spies*? Or were they best defined as *trespassers*, a very common teenage misdemeanor? Was hacking *theft of service*? (After all, intruders were getting someone else's computer to carry out their orders, without permission and without paying). Was hacking *fraud*? Maybe it was best described as *impersonation*. The commonest mode of computer intrusion was (and is) to swipe or snoop somebody else's password, and then enter the computer in the guise of another person -- who is commonly stuck with the blame and the bills. Perhaps a medical metaphor was better -- hackers should be defined as "sick," as *computer addicts* unable to control their irresponsible, compulsive behavior. But these weighty assessments meant little to the people who were actually being judged. From inside the underground world of hacking itself, all these perceptions seem quaint, wrongheaded, stupid, or meaningless. The most important self-perception of underground hackers - from the 1960s, right through to the present day -- is that they are an *elite*. The day-to-day struggle in the underground is not over sociological definitions -- who cares? -- but for power, knowledge, and status among one's peers. When you are a hacker, it is your own inner conviction of your elite status that enables you to break, or let us say "transcend," the rules. It is not that *all* rules go by the board. The rules habitually broken by hackers are *unimportant* rules -- the rules of dopey greedhead telco bureaucrats and pig-ignorant government pests. Hackers have their *own* rules, which separate behavior which is cool and elite, from behavior which is rodentlike, stupid and losing. These "rules," however, are mostly unwritten and enforced by peer pressure and tribal feeling. Like all rules that depend on the unspoken conviction that everybody else is a good old boy, these rules are ripe for abuse. The mechanisms of hacker peer- pressure, "teletrials" and ostracism, are rarely used and rarely work. Back- stabbing slander, threats, and electronic harassment are also freely employed in downand-dirty intrahacker feuds, but this rarely forces a rival out of the scene entirely. The only real solution for the problem of an utterly losing, treacherous and rodentlike hacker is to *turn him in to the police*. Unlike the Mafia or Medellin Cartel, the hacker elite cannot simply execute the bigmouths, creeps and troublemakers among their ranks, so they turn one another in with astonishing frequency. There is no tradition of silence or *omerta* in the hacker underworld. Hackers can be shy, even reclusive, but when they do talk, hackers tend to brag, boast and strut. Almost everything hackers do is *invisible*; if they don't brag, boast, and strut about it, then *nobody will ever know*. If you don't have something to brag, boast, and strut about, then nobody in the underground will recognize you and favor you with vital cooperation and respect. The way to win a solid reputation in the underground is by telling other hackers things that could only have been learned by exceptional cunning and stealth. Forbidden knowledge, therefore, is the basic currency of the digital underground, like seashells among Trobriand Islanders. Hackers hoard this knowledge, and dwell upon it obsessively, and refine it, and bargain with it, and talk and talk about it. Many hackers even suffer from a strange obsession to *teach* -- to spread the ethos and the knowledge of the digital underground. They'll do this even when it gains them no particular advantage and presents a grave personal risk. And when that risk catches up with them, they will go right on teaching and preaching -- to a new audience this time, their interrogators from law enforcement. Almost every hacker arrested tells everything he knows -- all about his friends, his mentors, his disciples -- legends, threats, horror stories, dire rumors, gossip, hallucinations. This is, of course, convenient for law enforcement -- except when law enforcement begins to believe hacker legendry. Phone phreaks are unique among criminals in their willingness to call up law enforcement officials -- in the office, at their homes -- and give them an extended piece of their mind. It is hard not to interpret this as *begging for arrest*, and in fact it is an act of incredible foolhardiness. Police are naturally nettled by these acts of chutzpah and will go well out of their way to bust these flaunting idiots. But it can also be interpreted as a product of a world-view so elitist, so closed and hermetic, that electronic police are simply not perceived as "police," but rather as *enemy phone phreaks* who should be scolded into behaving "decently." Hackers at their most grandiloquent perceive themselves as the elite pioneers of a new electronic world. Attempts to make them obey the democratically established laws of contemporary American society are seen as repression and persecution. After all, they argue, if Alexander Graham Bell had gone along with the rules of the Western Union telegraph company, there would have been no telephones. If Jobs and Wozniak had believed that IBM was the be-all and end-all, there would have been no personal computers. If Benjamin Franklin and Thomas Jefferson had tried to "work within the system" there would have been no United States. Not only do hackers privately believe this as an article of faith, but they have been known to write ardent manifestos about it. Here are some revealing excerpts from an especially vivid hacker manifesto: "The TechnoRevolution" by "Dr. Crash," which appeared in electronic form in *Phrack* Volume 1, Issue 6, Phile 3. "To fully explain the true motives behind hacking, we must first take a quick look into the past. In the 1960s, a group of MIT students built the first modern computer system. This wild, rebellious group of young men were the first to bear the name 'hackers.' The systems that they developed were intended to be used to solve world problems and to benefit all of mankind. "As we can see, this has not been the case. The computer system has been solely in the hands of big businesses and the government. The wonderful device meant to enrich life has become a weapon which dehumanizes people. To the government and large businesses, people are no more than disk space, and the government doesn't use computers to arrange aid for the poor, but to control nuclear death weapons. The average American can only have access to a small microcomputer which is worth only a fraction of what they pay for it. The businesses keep the true state-of-the-art equipment away from the people behind a steel wall of incredibly high prices and bureaucracy. It is because of this state of affairs that hacking was born.(...) "Of course, the government doesn't want the monopoly of technology broken, so they have outlawed hacking and arrest anyone who is caught.(...) The phone company is another example of technology abused and kept from people with high prices.(...) "Hackers often find that their existing equipment, due to the monopoly tactics of computer companies, is inefficient for their purposes. Due to the exorbitantly high prices, it is impossible to legally purchase the necessary equipment. This need has given still another segment of the fight: Credit Carding. Carding is a way of obtaining the necessary goods without paying for them. It is again due to the companies' stupidity that Carding is so easy, and shows that the world's businesses are in the hands of those with considerably less technical know-how than we, the hackers. (...) "Hacking must continue. We must train newcomers to the art of hacking.(....) And whatever you do, continue the fight. Whether you know it or not, if you are a hacker, you are a revolutionary. Don't worry, you're on the right side." The defense of "carding" is rare. Most hackers regard credit-card theft as "poison" to the underground, a sleazy and immoral effort that, worse yet, is hard to get away with. Nevertheless, manifestos advocating creditcard theft, the deliberate crashing of computer systems, and even acts of violent physical destruction such as vandalism and arson do exist in the underground. These boasts and threats are taken quite seriously by the police. And not every hacker is an abstract, Platonic computernerd. Some few are quite experienced at picking locks, robbing phone-trucks, and breaking and entering buildings. Hackers vary in their degree of hatred for authority and the violence of their rhetoric. But, at a bottom line, they are scofflaws. They don't regard the current rules of electronic behavior as respectable efforts to preserve law and order and protect public safety. They regard these laws as immoral efforts by soulless corporations to protect their profit margins and to crush dissidents. "Stupid" people, including police, businessmen, politicians, and journalists, simply have no right to judge the actions of those possessed of genius, techno-revolutionary intentions, and technical expertise. 3. === Hackers are generally teenagers and college kids not engaged in earning a living. They often come from fairly well-to-do middle-class backgrounds, and are markedly anti-materialistic (except, that is, when it comes to computer equipment). Anyone motivated by greed for mere money (as opposed to the greed for power, knowledge and status) is swiftly written-off as a narrowminded breadhead whose interests can only be corrupt and contemptible. Having grown up in the 1970s and 1980s, the young Bohemians of the digital underground regard straight society as awash in plutocratic corruption, where everyone from the President down is for sale and whoever has the gold makes the rules. Interestingly, there's a funhouse-mirror image of this attitude on the other side of the conflict. The police are also one of the most markedly anti-materialistic groups in American society, motivated not by mere money but by ideals of service, justice, esprit-de-corps, and, of course, their own brand of specialized knowledge and power. Remarkably, the propaganda war between cops and hackers has always involved angry allegations that the other side is trying to make a sleazy buck. Hackers consistently sneer that anti-phreak prosecutors are angling for cushy jobs as telco lawyers and that computercrime police are aiming to cash in later as well-paid computer-security consultants in the private sector. For their part, police publicly conflate all hacking crimes with robbing payphones with crowbars. Allegations of "monetary losses" from computer intrusion are notoriously inflated. The act of illicitly copying a document from a computer is morally equated with directly robbing a company of, say, half a million dollars. The teenage computer intruder in possession of this "proprietary" document has certainly not sold it for such a sum, would likely have little idea how to sell it at all, and quite probably doesn't even understand what he has. He has not made a cent in profit from his felony but is still morally equated with a thief who has robbed the church poorbox and lit out for Brazil. Police want to believe that all hackers are thieves. It is a tortuous and almost unbearable act for the American justice system to put people in jail because they want to learn things which are forbidden for them to know. In an American context, almost any pretext for punishment is better than jailing people to protect certain restricted kinds of information. Nevertheless, policing information is part and parcel of the struggle against hackers. This dilemma is well exemplified by the remarkable activities of "Emmanuel Goldstein," editor and publisher of a print magazine known as *2600: The Hacker Quarterly*. Goldstein was an English major at Long Island's State University of New York in the '70s, when he became involved with the local college radio station. His growing interest in electronics caused him to drift into Yippie *TAP* circles and thus into the digital underground, where he became a self-described technorat. His magazine publishes techniques of computer intrusion and telephone "exploration" as well as gloating exposes of telco misdeeds and governmental failings. Goldstein lives quietly and very privately in a large, crumbling Victorian mansion in Setauket, New York. The seaside house is decorated with telco decals, chunks of driftwood, and the basic bric-a-brac of a hippie crash-pad. He is unmarried, mildly unkempt, and survives mostly on TV dinners and turkey-stuffing eaten straight out of the bag. Goldstein is a man of considerable charm and fluency, with a brief, disarming smile and the kind of pitiless, stubborn, thoroughly recidivist integrity that America's electronic police find genuinely alarming. Goldstein took his nom-de-plume, or "handle," from a character in Orwell's *1984*, which may be taken, correctly, as a symptom of the gravity of his sociopolitical worldview. He is not himself a practicing computer intruder, though he vigorously abets these actions, especially when they are pursued against large corporations or governmental agencies. Nor is he a thief, for he loudly scorns mere theft of phone service, in favor of 'exploring and manipulating the system.' He is probably best described and understood as a dissident. Weirdly, Goldstein is living in modern America under conditions very similar to those of former East European intellectual dissidents. In other words, he flagrantly espouses a value-system that is deeply and irrevocably opposed to the system of those in power and the police. The values in *2600* are generally expressed in terms that are ironic, sarcastic, paradoxical, or just downright confused. But there's no mistaking their radically anti-authoritarian tenor. *2600* holds that technical power and specialized knowledge, of any kind obtainable, belong by right in the hands of those individuals brave and bold enough to discover them -- by whatever means necessary. Devices, laws, or systems that forbid access, and the free spread of knowledge, are provocations that any free and self- respecting hacker should relentlessly attack. The "privacy" of governments, corporations and other soulless technocratic organizations should never be protected at the expense of the liberty and free initiative of the individual techno-rat. However, in our contemporary workaday world, both governments and corporations are very anxious indeed to police information which is secret, proprietary, restricted, confidential, copyrighted, patented, hazardous, illegal, unethical, embarrassing, or otherwise sensitive. This makes Goldstein persona non grata, and his philosophy a threat. Very little about the conditions of Goldstein's daily life would astonish, say, Vaclav Havel. (We may note in passing that President Havel once had his word-processor confiscated by the Czechoslovak police.) Goldstein lives by samizdat, acting semi-openly as a data- center for the underground, while challenging the powers-that-be to abide by their own stated rules: freedom of speech and the First Amendment. Goldstein thoroughly looks and acts the part of techno-rat, with shoulder-length ringlets and a piratical black fisherman's-cap set at a rakish angle. He often shows up like Banquo's ghost at meetings of computer professionals, where he listens quietly, half-smiling and taking thorough notes. Computer professionals generally meet publicly, and find it very difficult to rid themselves of Goldstein and his ilk without extralegal and unconstitutional actions. Sympathizers, many of them quite respectable people with responsible jobs, admire Goldstein's attitude and surreptitiously pass him information. An unknown but presumably large proportion of Goldstein's 2,000-plus readership are telco security personnel and police, who are forced to subscribe to *2600* to stay abreast of new developments in hacking. They thus find themselves paying this guy's rent while grinding their teeth in anguish, a situation that would have delighted Abbie Hoffman (one of Goldstein's few idols). Goldstein is probably the best-known public representative of the hacker underground today, and certainly the best-hated. Police regard him as a Fagin, a corrupter of youth, and speak of him with untempered loathing. He is quite an accomplished gadfly. After the Martin Luther King Day Crash of 1990, Goldstein, for instance, adeptly rubbed salt into the wound in the pages of *2600*. "Yeah, it was fun for the phone phreaks as we watched the network crumble," he admitted cheerfully. "But it was also an ominous sign of what's to come... Some AT&T people, aided by well-meaning but ignorant media, were spreading the notion that many companies had the same software and therefore could face the same problem someday. Wrong. This was entirely an AT&T software deficiency. Of course, other companies could face entirely different software problems. But then, so too could AT&T." After a technical discussion of the system's failings, the Long Island techno-rat went on to offer thoughtful criticism to the gigantic multinational's hundreds of professionally qualified engineers. "What we don't know is how a major force in communications like AT&T could be so sloppy. What happened to backups? Sure, computer systems go down all the time, but people making phone calls are not the same as people logging on to computers. We must make that distinction. It's not acceptable for the phone system or any other essential service to 'go down.' If we continue to trust technology without understanding it, we can look forward to many variations on this theme. "AT&T owes it to its customers to be prepared to instantly switch to another network if something strange and unpredictable starts occurring. The news here isn't so much the failure of a computer program, but the failure of AT&T's entire structure." The very idea of this.... this person.... offering "advice" about "AT&T's entire structure" is more than some people can easily bear. How dare this near-criminal dictate what is or isn't "acceptable" behavior from AT&T? Especially when he's publishing, in the very same issue, detailed schematic diagrams for creating various switching-network signalling tones unavailable to the public. "See what happens when you drop a 'silver box' tone or two down your local exchange or through different long distance service carriers," advises *2600* contributor "Mr. Upsetter" in "How To Build a Signal Box." "If you experiment systematically and keep good records, you will surely discover something interesting." This is, of course, the scientific method, generally regarded as a praiseworthy activity and one of the flowers of modern civilization. One can indeed learn a great deal with this sort of structured intellectual activity. Telco employees regard this mode of "exploration" as akin to flinging sticks of dynamite into their pond to see what lives on the bottom. *2600* has been published consistently since 1984. It has also run a bulletin board computer system, printed *2600* T-shirts, taken fax calls... The Spring 1991 issue has an interesting announcement on page 45: "We just discovered an extra set of wires attached to our fax line and heading up the pole. (They've since been clipped.) Your faxes to us and to anyone else could be monitored." In the worldview of *2600*, the tiny band of technorat brothers (rarely, sisters) are a beseiged vanguard of the truly free and honest. The rest of the world is a maelstrom of corporate crime and high-level governmental corruption, occasionally tempered with well-meaning ignorance. To read a few issues in a row is to enter a nightmare akin to Solzhenitsyn's, somewhat tempered by the fact that *2600* is often extremely funny. Goldstein did not become a target of the Hacker Crackdown, though he protested loudly, eloquently, and publicly about it, and it added considerably to his fame. It was not that he is not regarded as dangerous, because he is so regarded. Goldstein has had brushes with the law in the past: in 1985, a *2600* bulletin board computer was seized by the FBI, and some software on it was formally declared "a burglary tool in the form of a computer program." But Goldstein escaped direct repression in 1990, because his magazine is printed on paper, and recognized as subject to Constitutional freedom of the press protection. As was seen in the *Ramparts* case, this is far from an absolute guarantee. Still, as a practical matter, shutting down *2600* by court-order would create so much legal hassle that it is simply unfeasible, at least for the present. Throughout 1990, both Goldstein and his magazine were peevishly thriving. Instead, the Crackdown of 1990 would concern itself with the computerized version of forbidden data. The crackdown itself, first and foremost, was about *bulletin board systems*. Bulletin Board Systems, most often known by the ugly and un-pluralizable acronym "BBS," are the life-blood of the digital underground. Boards were also central to law enforcement's tactics and strategy in the Hacker Crackdown. A "bulletin board system" can be formally defined as a computer which serves as an information and messagepassing center for users dialing-up over the phone-lines through the use of modems. A "modem," or modulatordemodulator, is a device which translates the digital impulses of computers into audible analog telephone signals, and vice versa. Modems connect computers to phones and thus to each other. Large-scale mainframe computers have been connected since the 1960s, but personal computers, run by individuals out of their homes, were first networked in the late 1970s. The "board" created by Ward Christensen and Randy Suess in February 1978, in Chicago, Illinois, is generally regarded as the first personal-computer bulletin board system worthy of the name. Boards run on many different machines, employing many different kinds of software. Early boards were crude and buggy, and their managers, known as "system operators" or "sysops," were hard- working technical experts who wrote their own software. But like most everything else in the world of electronics, boards became faster, cheaper, better-designed, and generally far more sophisticated throughout the 1980s. They also moved swiftly out of the hands of pioneers and into those of the general public. By 1985 there were something in the neighborhood of 4,000 boards in America. By 1990 it was calculated, vaguely, that there were about 30,000 boards in the US, with uncounted thousands overseas. Computer bulletin boards are unregulated enterprises. Running a board is a rough-and-ready, catchas-catch-can proposition. Basically, anybody with a computer, modem, software and a phone-line can start a board. With second-hand equipment and public-domain free software, the price of a board might be quite small -less than it would take to publish a magazine or even a decent pamphlet. Entrepreneurs eagerly sell bulletin- board software, and will coach nontechnical amateur sysops in its use. Boards are not "presses." They are not magazines, or libraries, or phones, or CB radios, or traditional cork bulletin boards down at the local laundry, though they have some passing resemblance to those earlier media. Boards are a new medium -- they may even be a large number of new media. Consider these unique characteristics: boards are cheap, yet they can have a national, even global reach. Boards can be contacted from anywhere in the global telephone network, at no cost to the person running the board -- the caller pays the phone bill, and if the caller is local, the call is free. Boards do not involve an editorial elite addressing a mass audience. The "sysop" of a board is not an exclusive publisher or writer -- he is managing an electronic salon, where individuals can address the general public, play the part of the general public, and also exchange private mail with other individuals. And the "conversation" on boards, though fluid, rapid, and highly interactive, is not spoken, but written. It is also relatively anonymous, sometimes completely so. And because boards are cheap and ubiquitous, regulations and licensing requirements would likely be practically unenforceable. It would almost be easier to "regulate" "inspect" and "license" the content of private mail -- probably more so, since the mail system is operated by the federal government. Boards are run by individuals, independently, entirely at their own whim. For the sysop, the cost of operation is not the primary limiting factor. Once the investment in a computer and modem has been made, the only steady cost is the charge for maintaining a phone line (or several phone lines). The primary limits for sysops are time and energy. Boards require upkeep. New users are generally "validated" -they must be issued individual passwords, and called at home by voice-phone, so that their identity can be verified. Obnoxious users, who exist in plenty, must be chided or purged. Proliferating messages must be deleted when they grow old, so that the capacity of the system is not overwhelmed. And software programs (if such things are kept on the board) must be examined for possible computer viruses. If there is a financial charge to use the board (increasingly common, especially in larger and fancier systems) then accounts must be kept, and users must be billed. And if the board crashes -- a very common occurrence -- then repairs must be made. Boards can be distinguished by the amount of effort spent in regulating them. First, we have the completely open board, whose sysop is off chugging brews and watching re-runs while his users generally degenerate over time into peevish anarchy and eventual silence. Second comes the supervised board, where the sysop breaks in every once in a while to tidy up, calm brawls, issue announcements, and rid the community of dolts and troublemakers. Third is the heavily supervised board, which sternly urges adult and responsible behavior and swiftly edits any message considered offensive, impertinent, illegal or irrelevant. And last comes the completely edited "electronic publication," which is presented to a silent audience which is not allowed to respond directly in any way. Boards can also be grouped by their degree of anonymity. There is the completely anonymous board, where everyone uses pseudonyms -- "handles" -- and even the sysop is unaware of the user's true identity. The sysop himself is likely pseudonymous on a board of this type. Second, and rather more common, is the board where the sysop knows (or thinks he knows) the true names and addresses of all users, but the users don't know one another's names and may not know his. Third is the board where everyone has to use real names, and roleplaying and pseudonymous posturing are forbidden. Boards can be grouped by their immediacy. "Chatlines" are boards linking several users together over several different phone-lines simultaneously, so that people exchange messages at the very moment that they type. (Many large boards feature "chat" capabilities along with other services.) Less immediate boards, perhaps with a single phoneline, store messages serially, one at a time. And some boards are only open for business in daylight hours or on weekends, which greatly slows response. A *network of boards*, such as "FidoNet," can carry electronic mail from board to board, continent to continent, across huge distances -- but at a relative snail's pace, so that a message can take several days to reach its target audience and elicit a reply. Boards can be grouped by their degree of community. Some boards emphasize the exchange of private, person-to-person electronic mail. Others emphasize public postings and may even purge people who "lurk," merely reading posts but refusing to openly participate. Some boards are intimate and neighborly. Others are frosty and highly technical. Some are little more than storage dumps for software, where users "download" and "upload" programs, but interact among themselves little if at all. Boards can be grouped by their ease of access. Some boards are entirely public. Others are private and restricted only to personal friends of the sysop. Some boards divide users by status. On these boards, some users, especially beginners, strangers or children, will be restricted to general topics, and perhaps forbidden to post. Favored users, though, are granted the ability to post as they please, and to stay "on-line" as long as they like, even to the disadvantage of other people trying to call in. High- status users can be given access to hidden areas in the board, such as off- color topics, private discussions, and/or valuable software. Favored users may even become "remote sysops" with the power to take remote control of the board through their own home computers. Quite often "remote sysops" end up doing all the work and taking formal control of the enterprise, despite the fact that it's physically located in someone else's house. Sometimes several "co-sysops" share power. And boards can also be grouped by size. Massive, nationwide commercial networks, such as CompuServe, Delphi, GEnie and Prodigy, are run on mainframe computers and are generally not considered "boards," though they share many of their characteristics, such as electronic mail, discussion topics, libraries of software, and persistent and growing problems with civil-liberties issues. Some private boards have as many as thirty phone-lines and quite sophisticated hardware. And then there are tiny boards. Boards vary in popularity. Some boards are huge and crowded, where users must claw their way in against a constant busy-signal. Others are huge and empty -- there are few things sadder than a formerly flourishing board where no one posts any longer, and the dead conversations of vanished users lie about gathering digital dust. Some boards are tiny and intimate, their telephone numbers intentionally kept confidential so that only a small number can log on. And some boards are underground. Boards can be mysterious entities. The activities of their users can be hard to differentiate from conspiracy. Sometimes they are conspiracies. Boards have harbored, or have been accused of harboring, all manner of fringe groups, and have abetted, or been accused of abetting, every manner of frowned-upon, sleazy, radical, and criminal activity. There are Satanist boards. Nazi boards. Pornographic boards. Pedophile boards. Drugdealing boards. Anarchist boards. Communist boards. Gay and Lesbian boards (these exist in great profusion, many of them quite lively with well-established histories). Religious cult boards. Evangelical boards. Witchcraft boards, hippie boards, punk boards, skateboarder boards. Boards for UFO believers. There may well be boards for serial killers, airline terrorists and professional assassins. There is simply no way to tell. Boards spring up, flourish, and disappear in large numbers, in most every corner of the developed world. Even apparently innocuous public boards can, and sometimes do, harbor secret areas known only to a few. And even on the vast, public, commercial services, private mail is very private -- and quite possibly criminal. Boards cover most every topic imaginable and some that are hard to imagine. They cover a vast spectrum of social activity. However, all board users do have something in common: their possession of computers and phones. Naturally, computers and phones are primary topics of conversation on almost every board. And hackers and phone phreaks, those utter devotees of computers and phones, live by boards. They swarm by boards. They are bred by boards. By the late 1980s, phone-phreak groups and hacker groups, united by boards, had proliferated fantastically. As evidence, here is a list of hacker groups compiled by the editors of *Phrack* on August 8, 1988. The Administration. Advanced Telecommunications, Inc. ALIAS. American Tone Travelers. Anarchy Inc. Apple Mafia. The Association. Atlantic Pirates Guild. Bad Ass Mother Fuckers. Bellcore. Bell Shock Force. Black Bag. Camorra. C&M Productions. Catholics Anonymous. Chaos Computer Club. Chief Executive Officers. Circle Of Death. Circle Of Deneb. Club X. Coalition of Hi-Tech Pirates. Coast-To-Coast. Corrupt Computing. Cult Of The Dead Cow. Custom Retaliations. Damage Inc. D&B Communications. The Dange Gang. Dec Hunters. Digital Gang. DPAK. Eastern Alliance. The Elite Hackers Guild. Elite Phreakers and Hackers Club. The Elite Society Of America. EPG. Executives Of Crime. Extasyy Elite. Fargo 4A. Farmers Of Doom. The Federation. Feds R Us. First Class. Five O. Five Star. Force Hackers. The 414s. Hack-A-Trip. Hackers Of America. High Mountain Hackers. High Society. The Hitchhikers. IBM Syndicate. The Ice Pirates. Imperial Warlords. Inner Circle. Inner Circle II. Insanity Inc. International Computer Underground Bandits. Justice League of America. Kaos Inc. Knights Of Shadow. Knights Of The Round Table. League Of Adepts. Legion Of Doom. Legion Of Hackers. Lords Of Chaos. Lunatic Labs, Unlimited. Master Hackers. MAD! The Marauders. MD/PhD. Metal Communications, Inc. MetalliBashers, Inc. MBI. Metro Communications. Midwest Pirates Guild. NASA Elite. The NATO Association. Neon Knights. Nihilist Order. Order Of The Rose. OSS. Pacific Pirates Guild. Phantom Access Associates. PHido PHreaks. The Phirm. Phlash. PhoneLine Phantoms. Phone Phreakers Of America. Phortune 500. Phreak Hack Delinquents. Phreak Hack Destroyers. Phreakers, Hackers, And Laundromat Employees Gang (PHALSE Gang). Phreaks Against Geeks. Phreaks Against Phreaks Against Geeks. Phreaks and Hackers of America. Phreaks Anonymous World Wide. Project Genesis. The Punk Mafia. The Racketeers. Red Dawn Text Files. Roscoe Gang. SABRE. Secret Circle of Pirates. Secret Service. 707 Club. Shadow Brotherhood. Sharp Inc. 65C02 Elite. Spectral Force. Star League. Stowaways. Strata-Crackers. Team Hackers '86. Team Hackers '87. TeleComputist Newsletter Staff. Tribunal Of Knowledge. Triple Entente. Turn Over And Die Syndrome (TOADS). 300 Club. 1200 Club. 2300 Club. 2600 Club. 2601 Club. 2AF. The United Soft WareZ Force. United Technical Underground. Ware Brigade. The Warelords. WASP. Contemplating this list is an impressive, almost humbling business. As a cultural artifact, the thing approaches poetry. Underground groups -- subcultures -- can be distinguished from independent cultures by their habit of referring constantly to the parent society. Undergrounds by their nature constantly must maintain a membrane of differentiation. Funny/distinctive clothes and hair, specialized jargon, specialized ghettoized areas in cities, different hours of rising, working, sleeping.... The digital underground, which specializes in information, relies very heavily on language to distinguish itself. As can be seen from this list, they make heavy use of parody and mockery. It's revealing to see who they choose to mock. First, large corporations. We have the Phortune 500, The Chief Executive Officers, Bellcore, IBM Syndicate, SABRE (a computerized reservation service maintained by airlines). The common use of "Inc." is telling -- none of these groups are actual corporations, but take clear delight in mimicking them. Second, governments and police. NASA Elite, NATO Association. "Feds R Us" and "Secret Service" are fine bits of fleering boldness. OSS -- the Office of Strategic Services was the forerunner of the CIA. Third, criminals. Using stigmatizing pejoratives as a perverse badge of honor is a time-honored tactic for subcultures: punks, gangs, delinquents, mafias, pirates, bandits, racketeers. Specialized orthography, especially the use of "ph" for "f" and "z" for the plural "s," are instant recognition symbols. So is the use of the numeral "0" for the letter "O" -- computer-software orthography generally features a slash through the zero, making the distinction obvious. Some terms are poetically descriptive of computer intrusion: the Stowaways, the Hitchhikers, the PhoneLine Phantoms, Coast-to-Coast. Others are simple bravado and vainglorious puffery. (Note the insistent use of the terms "elite" and "master.") Some terms are blasphemous, some obscene, others merely cryptic - anything to puzzle, offend, confuse, and keep the straights at bay. Many hacker groups further re-encrypt their names by the use of acronyms: United Technical Underground becomes UTU, Farmers of Doom become FoD, the United SoftWareZ Force becomes, at its own insistence, "TuSwF," and woe to the ignorant rodent who capitalizes the wrong letters. It should be further recognized that the members of these groups are themselves pseudonymous. If you did, in fact, run across the "PhoneLine Phantoms," you would find them to consist of "Carrier Culprit," "The Executioner," "Black Majik," "Egyptian Lover," "Solid State," and "Mr Icom." "Carrier Culprit" will likely be referred to by his friends as "CC," as in, "I got these dialups from CC of PLP." It's quite possible that this entire list refers to as few as a thousand people. It is not a complete list of underground groups -- there has never been such a list, and there never will be. Groups rise, flourish, decline, share membership, maintain a cloud of wannabes and casual hangers-on. People pass in and out, are ostracized, get bored, are busted by police, or are cornered by telco security and presented with huge bills. Many "underground groups" are software pirates, "warez d00dz," who might break copy protection and pirate programs, but likely wouldn't dare to intrude on a computer-system. It is hard to estimate the true population of the digital underground. There is constant turnover. Most hackers start young, come and go, then drop out at age 22 -- the age of college graduation. And a large majority of "hackers" access pirate boards, adopt a handle, swipe software and perhaps abuse a phone-code or two, while never actually joining the elite. Some professional informants, who make it their business to retail knowledge of the underground to paymasters in private corporate security, have estimated the hacker population at as high as fifty thousand. This is likely highly inflated, unless one counts every single teenage software pirate and petty phone-booth thief. My best guess is about 5,000 people. Of these, I would guess that as few as a hundred are truly "elite" -- active computer intruders, skilled enough to penetrate sophisticated systems and truly to worry corporate security and law enforcement. Another interesting speculation is whether this group is growing or not. Young teenage hackers are often convinced that hackers exist in vast swarms and will soon dominate the cybernetic universe. Older and wiser veterans, perhaps as wizened as 24 or 25 years old, are convinced that the glory days are long gone, that the cops have the underground's number now, and that kids these days are dirt-stupid and just want to play Nintendo. My own assessment is that computer intrusion, as a non-profit act of intellectual exploration and mastery, is in slow decline, at least in the United States; but that electronic fraud, especially telecommunication crime, is growing by leaps and bounds. One might find a useful parallel to the digital underground in the drug underground. There was a time, now much-obscured by historical revisionism, when Bohemians freely shared joints at concerts, and hip, smallscale marijuana dealers might turn people on just for the sake of enjoying a long stoned conversation about the Doors and Allen Ginsberg. Now drugs are increasingly verboten, except in a high-stakes, highly- criminal world of highly addictive drugs. Over years of disenchantment and police harassment, a vaguely ideological, free-wheeling drug underground has relinquished the business of drugdealing to a far more savage criminal hard-core. This is not a pleasant prospect to contemplate, but the analogy is fairly compelling. What does an underground board look like? What distinguishes it from a standard board? It isn't necessarily the conversation -- hackers often talk about common board topics, such as hardware, software, sex, science fiction, current events, politics, movies, personal gossip. Underground boards can best be distinguished by their files, or "philes," pre-composed texts which teach the techniques and ethos of the underground. These are prized reservoirs of forbidden knowledge. Some are anonymous, but most proudly bear the handle of the "hacker" who has created them, and his group affiliation, if he has one. Here is a partial table-of-contents of philes from an underground board, somewhere in the heart of middle America, circa 1991. The descriptions are mostly self- explanatory. :: 5406 06-11-91 Hacking Bank America CHHACK.ZIP 4481 06-11-91 Chilton Hacking CITIBANK.ZIP 4118 06-11-91 Hacking Citibank CREDIMTC.ZIP 3241 06-11-91 Hacking Mtc Credit Company DIGEST.ZIP 5159 06-11-91 Hackers Digest HACK.ZIP 14031 06-11-91 How To Hack HACKBAS.ZIP 5073 06-11-91 Basics Of Hacking HACKDICT.ZIP 42774 06-11-91 Hackers Dictionary HACKER.ZIP 57938 06-11-91 Hacker Info HACKERME.ZIP 3148 06-11-91 Hackers Manual HACKHAND.ZIP 4814 06-11-91 Hackers Handbook HACKTHES.ZIP 48290 06- 11-91 Hackers Thesis HACKVMS.ZIP 4696 06-11-91 Hacking Vms Systems MCDON.ZIP 3830 06-11-91 Hacking Macdonalds (Home Of The Archs) P500UNIX.ZIP 15525 06-11-91 Phortune 500 Guide To Unix RADHACK.ZIP 8411 06-11-91 Radio Hacking TAOTRASH.DOC 4096 12-25-89 Suggestions For Trashing TECHHACK.ZIP 5063 06-11-91 Technical Hacking The files above are do-it- yourself manuals about computer intrusion. The above is only a small section of a much larger library of hacking and phreaking techniques and history. We now move into a different and perhaps surprising area. :: 3641 06-11-91 Anarchy Files ANARCHST.ZIP 63703 06-11-91 Anarchist Book ANARCHY.ZIP 2076 06-11-91 Anarchy At Home ANARCHY3.ZIP 6982 06-11-91 Anarchy No 3 ANARCTOY.ZIP 2361 06-11-91 Anarchy Toys ANTIMODM.ZIP 2877 06-11-91 Anti-modem Weapons ATOM.ZIP 4494 06-11-91 How To Make An Atom Bomb BARBITUA.ZIP 3982 06-11-91 Barbiturate Formula BLCKPWDR.ZIP 2810 06-11-91 Black Powder Formulas BOMB.ZIP 3765 06-11-91 How To Make Bombs BOOM.ZIP 2036 06-11-91 Things That Go Boom CHLORINE.ZIP 1926 06-11-91 Chlorine Bomb COOKBOOK.ZIP 1500 06-11-91 Anarchy Cook Book DESTROY.ZIP 3947 06-11-91 Destroy Stuff DUSTBOMB.ZIP 2576 06-11-91 Dust Bomb ELECTERR.ZIP 3230 06-11-91 Electronic Terror EXPLOS1.ZIP 2598 06-11-91 Explosives 1 EXPLOSIV.ZIP 18051 06-11-91 More Explosives EZSTEAL.ZIP 4521 06-11-91 Ez-stealing FLAME.ZIP 2240 06-11-91 Flame Thrower FLASHLT.ZIP 2533 06-11-91 Flashlight Bomb FMBUG.ZIP 2906 06-11-91 How To Make An Fm Bug OMEEXPL.ZIP 2139 06-11-91 Home Explosives HOW2BRK.ZIP 3332 06-11-91 How To Break In LETTER.ZIP 2990 06-11-91 Letter Bomb LOCK.ZIP 2199 06-11-91 How To Pick Locks MRSHIN.ZIP 3991 06-11-91 Briefcase Locks NAPALM.ZIP 3563 06-11-91 Napalm At Home NITRO.ZIP 3158 06-11-91 Fun With Nitro PARAMIL.ZIP 2962 06-11-91 Paramilitary Info PICKING.ZIP 3398 06-11-91 Picking Locks PIPEBOMB.ZIP 2137 06-11-91 Pipe Bomb POTASS.ZIP 3987 06-11-91 Formulas With Potassium PRANK.TXT 11074 08-03-90 More Pranks To Pull On Idiots! REVENGE.ZIP 4447 06-11-91 Revenge Tactics ROCKET.ZIP 2590 06-11-91 Rockets For Fun SMUGGLE.ZIP 3385 06-11-91 How To Smuggle *Holy Cow*! The damned thing is full of stuff about bombs! What are we to make of this? First, it should be acknowledged that spreading knowledge about demolitions to teenagers is a highly and deliberately antisocial act. It is not, however, illegal. Second, it should be recognized that most of these philes were in fact written by teenagers. Most adult American males who can remember their teenage years will recognize that the notion of building a flamethrower in your garage is an incredibly neat-o idea. Actually building a flamethrower in your garage, however, is fraught with discouraging difficulty. Stuffing gunpowder into a booby-trapped flashlight, so as to blow the arm off your high-school vice-principal, can be a thing of dark beauty to contemplate. Actually committing assault by explosives will earn you the sustained attention of the federal Bureau of Alcohol, Tobacco and Firearms. Some people, however, will actually try these plans. A determinedly murderous American teenager can probably buy or steal a handgun far more easily than he can brew fake "napalm" in the kitchen sink. Nevertheless, if temptation is spread before people a certain number will succumb, and a small minority will actually attempt these stunts. A large minority of that small minority will either fail or, quite likely, maim themselves, since these "philes" have not been checked for accuracy, are not the product of professional experience, and are often highly fanciful. But the gloating menace of these philes is not to be entirely dismissed. Hackers may not be "serious" about bombing; if they were, we would hear far more about exploding flashlights, homemade bazookas, and gym teachers poisoned by chlorine and potassium. However, hackers are very serious about forbidden knowledge. They are possessed not merely by curiosity, but by a positive lust to know. The desire to know what others don't is scarcely new. But the intensity of this desire, as manifested by these young technophilic denizens of the Information Age, may in fact be new, and may represent some basic shift in social values -- a harbinger of what the world may come to, as society lays more and more value on the possession, assimilation and retailing of information as a basic commodity of daily life. There have always been young men with obsessive interests in these topics. Never before, however, have they been able to network so extensively and easily, and to propagandize their interests with impunity to random passers-by. High-school teachers will recognize that there's always one in a crowd, but when the one in a crowd escapes control by jumping into the phone-lines, and becomes a hundred such kids all together on a board, then trouble is brewing visibly. The urge of authority to do something, even something drastic, is hard to resist. And in 1990, authority did something. In fact authority did a great deal. 4. === The process by which boards create hackers goes something like this. A youngster becomes interested in computers -- usually, computer games. He hears from friends that "bulletin boards" exist where games can be obtained for free. (Many computer games are "freeware," not copyrighted -- invented simply for the love of it and given away to the public; some of these games are quite good.) He bugs his parents for a modem, or quite often, uses his parents' modem. The world of boards suddenly opens up. Computer games can be quite expensive, real budget-breakers for a kid, but pirated games, stripped of copy protection, are cheap or free. They are also illegal, but it is very rare, almost unheard of, for a small-scale software pirate to be prosecuted. Once "cracked" of its copy protection, the program, being digital data, becomes infinitely reproducible. Even the instructions to the game, any manuals that accompany it, can be reproduced as text files, or photocopied from legitimate sets. Other users on boards can give many useful hints in game-playing tactics. And a youngster with an infinite supply of free computer games can certainly cut quite a swath among his modemless friends. And boards are pseudonymous. No one need know that you're fourteen years old -- with a little practice at subterfuge, you can talk to adults about adult things, and be accepted and taken seriously! You can even pretend to be a girl, or an old man, or anybody you can imagine. If you find this kind of deception gratifying, there is ample opportunity to hone your ability on boards. But local boards can grow stale. And almost every board maintains a list of phone-numbers to other boards, some in distant, tempting, exotic locales. Who knows what they're up to, in Oregon or Alaska or Florida or California? It's very easy to find out -- just order the modem to call through its software -- nothing to this, just typing on a keyboard, the same thing you would do for most any computer game. The machine reacts swiftly and in a few seconds you are talking to a bunch of interesting people on another seaboard. And yet the bills for this trivial action can be staggering! Just by going tippety-tap with your fingers, you may have saddled your parents with four hundred bucks in long-distance charges, and gotten chewed out but good. That hardly seems fair. How horrifying to have made friends in another state and to be deprived of their company -- and their software - just because telephone companies demand absurd amounts of money! How painful, to be restricted to boards in one's own area code -- what the heck is an "area code" anyway, and what makes it so special? A few grumbles, complaints, and innocent questions of this sort will often elicit a sympathetic reply from another board user -- someone with some stolen codes to hand. You dither a while, knowing this isn't quite right, then you make up your mind to try them anyhow -- and they work! Suddenly you're doing something even your parents can't do. Six months ago you were just some kid -- now, you're the Crimson Flash of Area Code 512! You're bad -- you're nationwide! Maybe you'll stop at a few abused codes. Maybe you'll decide that boards aren't all that interesting after all, that it's wrong, not worth the risk -- but maybe you won't. The next step is to pick up your own repeat-dialling program -- to learn to generate your own stolen codes. (This was dead easy five years ago, much harder to get away with nowadays, but not yet impossible.) And these dialling programs are not complex or intimidating -- some are as small as twenty lines of software. Now, you too can share codes. You can trade codes to learn other techniques. If you're smart enough to catch on, and obsessive enough to want to bother, and ruthless enough to start seriously bending rules, then you'll get better, fast. You start to develop a rep. You move up to a heavier class of board -- a board with a bad attitude, the kind of board that naive dopes like your classmates and your former self have never even heard of! You pick up the jargon of phreaking and hacking from the board. You read a few of those anarchy philes -- and man, you never realized you could be a real outlaw without ever leaving your bedroom. You still play other computer games, but now you have a new and bigger game. This one will bring you a different kind of status than destroying even eight zillion lousy space invaders. Hacking is perceived by hackers as a "game." This is not an entirely unreasonable or sociopathic perception. You can win or lose at hacking, succeed or fail, but it never feels "real." It's not simply that imaginative youngsters sometimes have a hard time telling "make-believe" from "real life." Cyberspace is not real! "Real" things are physical objects like trees and shoes and cars. Hacking takes place on a screen. Words aren't physical, numbers (even telephone numbers and credit card numbers) aren't physical. Sticks and stones may break my bones, but data will never hurt me. Computers simulate reality, like computer games that simulate tank battles or dogfights or spaceships. Simulations are just makebelieve, and the stuff in computers is not real. Consider this: if "hacking" is supposed to be so serious and real- life and dangerous, then how come nine-year-old kids have computers and modems? You wouldn't give a nine year old his own car, or his own rifle, or his own chainsaw -- those things are "real." People underground are perfectly aware that the "game" is frowned upon by the powers that be. Word gets around about busts in the underground. Publicizing busts is one of the primary functions of pirate boards, but they also promulgate an attitude about them, and their own idiosyncratic ideas of justice. The users of underground boards won't complain if some guy is busted for crashing systems, spreading viruses, or stealing money by wirefraud. They may shake their heads with a sneaky grin, but they won't openly defend these practices. But when a kid is charged with some theoretical amount of theft: $233,846.14, for instance, because he sneaked into a computer and copied something, and kept it in his house on a floppy disk -- this is regarded as a sign of near insanity from prosecutors, a sign that they've drastically mistaken the immaterial game of computing for their real and boring everyday world of fatcat corporate money. It's as if big companies and their suck-up lawyers think that computing belongs to them, and they can retail it with price stickers, as if it were boxes of laundry soap! But pricing "information" is like trying to price air or price dreams. Well, anybody on a pirate board knows that computing can be, and ought to be, free. Pirate boards are little independent worlds in cyberspace, and they don't belong to anybody but the underground. Underground boards aren't "brought to you by Procter & Gamble." To log on to an underground board can mean to experience liberation, to enter a world where, for once, money isn't everything and adults don't have all the answers. Let's sample another vivid hacker manifesto. Here are some excerpts from "The Conscience of a Hacker," by "The Mentor," from *Phrack* Volume One, Issue 7, Phile 3. "I made a discovery today. I found a computer. Wait a second, this is cool. It does what I want it to. If it makes a mistake, it's because I screwed it up. Not because it doesn't like me.(...) "And then it happened... a door opened to a world... rushing through the phone line like heroin through an addict's veins, an electronic pulse is sent out, a refuge from day-to-day incompetencies is sought... a board is found. 'This is it... this is where I belong...' "I know everyone here... even if I've never met them, never talked to them, may never hear from them again... I know you all...(...) "This is our world now.... the world of the electron and the switch, the beauty of the baud. We make use of a service already existing without paying for what could be dirt-cheap if it wasn't run by profiteering gluttons, and you call us criminals. We explore... and you call us criminals. We seek after knowledge... and you call us criminals. We exist without skin color, without nationality, without religious bias... and you call us criminals. You build atomic bombs, you wage wars, you murder, cheat and lie to us and try to make us believe that it's for our own good, yet we're the criminals. "Yes, I am a criminal. My crime is that of curiosity. My crime is that of judging people by what they say and think, not what they look like. My crime is that of outsmarting you, something that you will never forgive me for." 5. === There have been underground boards almost as long as there have been boards. One of the first was 8BBS, which became a stronghold of the West Coast phonephreak elite. After going on-line in March 1980, 8BBS sponsored "Susan Thunder," and "Tuc," and, most notoriously, "the Condor." "The Condor" bore the singular distinction of becoming the most vilified American phreak and hacker ever. Angry underground associates, fed up with Condor's peevish behavior, turned him in to police, along with a heaping double-helping of outrageous hacker legendry. As a result, Condor was kept in solitary confinement for seven months, for fear that he might start World War Three by triggering missile silos from the prison payphone. (Having served his time, Condor is now walking around loose; WWIII has thus far conspicuously failed to occur.) The sysop of 8BBS was an ardent free-speech enthusiast who simply felt that any attempt to restrict the expression of his users was unconstitutional and immoral. Swarms of the technically curious entered 8BBS and emerged as phreaks and hackers, until, in 1982, a friendly 8BBS alumnus passed the sysop a new modem which had been purchased by credit-card fraud. Police took this opportunity to seize the entire board and remove what they considered an attractive nuisance. Plovernet was a powerful East Coast pirate board that operated in both New York and Florida. Owned and operated by teenage hacker "Quasi Moto," Plovernet attracted five hundred eager users in 1983. "Emmanuel Goldstein" was one-time co-sysop of Plovernet, along with "Lex Luthor," founder of the "Legion of Doom" group. Plovernet bore the signal honor of being the original home of the "Legion of Doom," about which the reader will be hearing a great deal, soon. "Pirate-80," or "P-80," run by a sysop known as "Scan Man," got into the game very early in Charleston, and continued steadily for years. P-80 flourished so flagrantly that even its most hardened users became nervous, and some slanderously speculated that "Scan Man" must have ties to corporate security, a charge he vigorously denied. "414 Private" was the home board for the first group to attract conspicuous trouble, the teenage "414 Gang," whose intrusions into Sloan- Kettering Cancer Center and Los Alamos military computers were to be a nine-dayswonder in 1982. At about this time, the first software piracy boards began to open up, trading cracked games for the Atari 800 and the Commodore C64. Naturally these boards were heavily frequented by teenagers. And with the 1983 release of the hacker-thriller movie *War Games*, the scene exploded. It seemed that every kid in America had demanded and gotten a modem for Christmas. Most of these dabbler wannabes put their modems in the attic after a few weeks, and most of the remainder minded their P's and Q's and stayed well out of hot water. But some stubborn and talented diehards had this hacker kid in *War Games* figured for a happening dude. They simply could not rest until they had contacted the underground -or, failing that, created their own. In the mid-80s, underground boards sprang up like digital fungi. ShadowSpawn Elite. Sherwood Forest I, II, and III. Digital Logic Data Service in Florida, sysoped by no less a man than "Digital Logic" himself; Lex Luthor of the Legion of Doom was prominent on this board, since it was in his area code. Lex's own board, "Legion of Doom," started in 1984. The Neon Knights ran a network of Applehacker boards: Neon Knights North, South, East and West. Free World II was run by "Major Havoc." Lunatic Labs is still in operation as of this writing. Dr. Ripco in Chicago, an anything-goes anarchist board with an extensive and raucous history, was seized by Secret Service agents in 1990 on Sundevil day, but up again almost immediately, with new machines and scarcely diminished vigor. The St. Louis scene was not to rank with major centers of American hacking such as New York and L.A. But St. Louis did rejoice in possession of "Knight Lightning" and "Taran King," two of the foremost journalists native to the underground. Missouri boards like Metal Shop, Metal Shop Private, Metal Shop Brewery, may not have been the heaviest boards around in terms of illicit expertise. But they became boards where hackers could exchange social gossip and try to figure out what the heck was going on nationally -- and internationally. Gossip from Metal Shop was put into the form of news files, then assembled into a general electronic publication, *Phrack*, a portmanteau title coined from "phreak" and "hack." The *Phrack* editors were as obsessively curious about other hackers as hackers were about machines. *Phrack*, being free of charge and lively reading, began to circulate throughout the underground. As Taran King and Knight Lightning left high school for college, *Phrack* began to appear on mainframe machines linked to BITNET, and, through BITNET to the "Internet," that loose but extremely potent not-for-profit network where academic, governmental and corporate machines trade data through the UNIX TCP/IP protocol. (The "Internet Worm" of November 2-3,1988, created by Cornell grad student Robert Morris, was to be the largest and bestpublicized computer-intrusion scandal to date. Morris claimed that his ingenious "worm" program was meant to harmlessly explore the Internet, but due to bad programming, the Worm replicated out of control and crashed some six thousand Internet computers. Smallerscale and less ambitious Internet hacking was a standard for the underground elite.) Most any underground board not hopelessly lame and out-of-it would feature a complete run of *Phrack* -and, possibly, the lesser-known standards of the underground: the *Legion of Doom Technical Journal*, the obscene and raucous *Cult of the Dead Cow* files, *P/HUN* magazine, *Pirate*, the *Syndicate Reports*, and perhaps the highly anarcho- political *Activist Times Incorporated*. Possession of *Phrack* on one's board was prima facie evidence of a bad attitude. *Phrack* was seemingly everywhere, aiding, abetting, and spreading the underground ethos. And this did not escape the attention of corporate security or the police. We now come to the touchy subject of police and boards. Police, do, in fact, own boards. In 1989, there were police-sponsored boards in California, Colorado, Florida, Georgia, Idaho, Michigan, Missouri, Texas, and Virginia: boards such as "Crime Bytes," "Crimestoppers," "All Points" and "Bullet-N-Board." Police officers, as private computer enthusiasts, ran their own boards in Arizona, California, Colorado, Connecticut, Florida, Missouri, Maryland, New Mexico, North Carolina, Ohio, Tennessee and Texas. Police boards have often proved helpful in community relations. Sometimes crimes are reported on police boards. Sometimes crimes are committed on police boards. This has sometimes happened by accident, as naive hackers blunder onto police boards and blithely begin offering telephone codes. Far more often, however, it occurs through the now almost-traditional use of "sting boards." The first police sting-boards were established in 1985: "Underground Tunnel" in Austin, Texas, whose sysop Sgt. Robert Ansley called himself "Pluto" -- "The Phone Company" in Phoenix, Arizona, run by Ken MacLeod of the Maricopa County Sheriff's office -- and Sgt. Dan Pasquale's board in Fremont, California. Sysops posed as hackers, and swiftly garnered coteries of ardent users, who posted codes and loaded pirate software with abandon, and came to a sticky end. Sting boards, like other boards, are cheap to operate, very cheap by the standards of undercover police operations. Once accepted by the local underground, sysops will likely be invited into other pirate boards, where they can compile more dossiers. And when the sting is announced and the worst offenders arrested, the publicity is generally gratifying. The resultant paranoia in the underground -- perhaps more justly described as a "deterrence effect" -- tends to quell local lawbreaking for quite a while. Obviously police do not have to beat the underbrush for hackers. On the contrary, they can go trolling for them. Those caught can be grilled. Some become useful informants. They can lead the way to pirate boards all across the country. And boards all across the country showed the sticky fingerprints of *Phrack*, and of that loudest and most flagrant of all underground groups, the "Legion of Doom." The term "Legion of Doom" came from comic books. The Legion of Doom, a conspiracy of costumed supervillains headed by the chrome- domed criminal ultramastermind Lex Luthor, gave Superman a lot of four- color graphic trouble for a number of decades. Of course, Superman, that exemplar of Truth, Justice, and the American Way, always won in the long run. This didn't matter to the hacker Doomsters -- "Legion of Doom" was not some thunderous and evil Satanic reference, it was not meant to be taken seriously. "Legion of Doom" came from funny-books and was supposed to be funny. "Legion of Doom" did have a good mouthfilling ring to it, though. It sounded really cool. Other groups, such as the "Farmers of Doom," closely allied to LoD, recognized this grandiloquent quality, and made fun of it. There was even a hacker group called "Justice League of America," named after Superman's club of true-blue crimefighting superheros. But they didn't last; the Legion did. The original Legion of Doom, hanging out on Quasi Moto's Plovernet board, were phone phreaks. They weren't much into computers. "Lex Luthor" himself (who was under eighteen when he formed the Legion) was a COSMOS expert, COSMOS being the "Central System for Mainframe Operations," a telco internal computer network. Lex would eventually become quite a dab hand at breaking into IBM mainframes, but although everyone liked Lex and admired his attitude, he was not considered a truly accomplished computer intruder. Nor was he the "mastermind" of the Legion of Doom -- LoD were never big on formal leadership. As a regular on Plovernet and sysop of his "Legion of Doom BBS," Lex was the Legion's cheerleader and recruiting officer. Legion of Doom began on the ruins of an earlier phreak group, The Knights of Shadow. Later, LoD was to subsume the personnel of the hacker group "Tribunal of Knowledge." People came and went constantly in LoD; groups split up or formed offshoots. Early on, the LoD phreaks befriended a few computer-intrusion enthusiasts, who became the associated "Legion of Hackers." Then the two groups conflated into the "Legion of Doom/Hackers," or LoD/H. When the original "hacker" wing, Messrs. "CompuPhreak" and "Phucked Agent 04," found other matters to occupy their time, the extra "/H" slowly atrophied out of the name; but by this time the phreak wing, Messrs. Lex Luthor, "Blue Archer," "Gary Seven," "Kerrang Khan," "Master of Impact," "Silver Spy," "The Marauder," and "The Videosmith," had picked up a plethora of intrusion expertise and had become a force to be reckoned with. LoD members seemed to have an instinctive understanding that the way to real power in the underground lay through covert publicity. LoD were flagrant. Not only was it one of the earliest groups, but the members took pains to widely distribute their illicit knowledge. Some LoD members, like "The Mentor," were close to evangelical about it. *Legion of Doom Technical Journal* began to show up on boards throughout the underground. *LoD Technical Journal* was named in cruel parody of the ancient and honored *AT&T Technical Journal*. The material in these two publications was quite similar -much of it, adopted from public journals and discussions in the telco community. And yet, the predatory attitude of LoD made even its most innocuous data seem deeply sinister; an outrage; a clear and present danger. To see why this should be, let's consider the following (invented) paragraphs, as a kind of thought experiment. (A) "W. Fred Brown, AT&T Vice President for Advanced Technical Development, testified May 8 at a Washington hearing of the National Telecommunications and Information Administration (NTIA), regarding Bellcore's GARDEN project. GARDEN (Generalized Automatic Remote Distributed Electronic Network) is a telephone-switch programming tool that makes it possible to develop new telecom services, including hold-on-hold and customized message transfers, from any keypad terminal, within seconds. The GARDEN prototype combines centrex lines with a minicomputer using UNIX operating system software." (B) "Crimson Flash 512 of the Centrex Mobsters reports: D00dz, you wouldn't believe this GARDEN bullshit Bellcore's just come up with! Now you don't even need a lousy Commodore to reprogram a switch -- just log on to GARDEN as a technician, and you can reprogram switches right off the keypad in any public phone booth! You can give yourself hold-on-hold and customized message transfers, and best of all, the thing is run off (notoriously insecure) centrex lines using -- get this -standard UNIX software! Ha ha ha ha!" Message (A), couched in typical technobureaucratese, appears tedious and almost unreadable. (A) scarcely seems threatening or menacing. Message (B), on the other hand, is a dreadful thing, prima facie evidence of a dire conspiracy, definitely not the kind of thing you want your teenager reading. The information, however, is identical. It is public information, presented before the federal government in an open hearing. It is not "secret." It is not "proprietary." It is not even "confidential." On the contrary, the development of advanced software systems is a matter of great public pride to Bellcore. However, when Bellcore publicly announces a project of this kind, it expects a certain attitude from the public -- something along the lines of gosh wow, you guys are great, keep that up, whatever it is -- certainly not cruel mimickry, one-upmanship and outrageous speculations about possible security holes. Now put yourself in the place of a policeman confronted by an outraged parent, or telco official, with a copy of Version (B). This well- meaning citizen, to his horror, has discovered a local bulletin-board carrying outrageous stuff like (B), which his son is examining with a deep and unhealthy interest. If (B) were printed in a book or magazine, you, as an American law enforcement officer, would know that it would take a hell of a lot of trouble to do anything about it; but it doesn't take technical genius to recognize that if there's a computer in your area harboring stuff like (B), there's going to be trouble. In fact, if you ask around, any computer-literate cop will tell you straight out that boards with stuff like (B) are the source of trouble. And the worst source of trouble on boards are the ringleaders inventing and spreading stuff like (B). If it weren't for these jokers, there wouldn't be any trouble. And Legion of Doom were on boards like nobody else. Plovernet. The Legion of Doom Board. The Farmers of Doom Board. Metal Shop. OSUNY. Blottoland. Private Sector. Atlantis. Digital Logic. Hell Phrozen Over. LoD members also ran their own boards. "Silver Spy" started his own board, "Catch-22," considered one of the heaviest around. So did "Mentor," with his "Phoenix Project." When they didn't run boards themselves, they showed up on other people's boards, to brag, boast, and strut. And where they themselves didn't go, their philes went, carrying evil knowledge and an even more evil attitude. As early as 1986, the police were under the vague impression that everyone in the underground was Legion of Doom. LoD was never that large -considerably smaller than either "Metal Communications" or "The Administration," for instance -- but LoD got tremendous press. Especially in *Phrack*, which at times read like an LoD fan magazine; and *Phrack* was everywhere, especially in the offices of telco security. You couldn't get busted as a phone phreak, a hacker, or even a lousy codes kid or warez dood, without the cops asking if you were LoD. This was a difficult charge to deny, as LoD never distributed membership badges or laminated ID cards. If they had, they would likely have died out quickly, for turnover in their membership was considerable. LoD was less a high-tech street-gang than an ongoing state-ofmind. LoD was the Gang That Refused to Die. By 1990, LoD had ruled for ten years, and it seemed weird to police that they were continually busting people who were only sixteen years old. All these teenage small-timers were pleading the tiresome hacker litany of "just curious, no criminal intent." Somewhere at the center of this conspiracy there had to be some serious adult masterminds, not this seemingly endless supply of myopic suburban white kids with high SATs and funny haircuts. There was no question that most any American hacker arrested would "know" LoD. They knew the handles of contributors to *LoD Tech Journal*, and were likely to have learned their craft through LoD boards and LoD activism. But they'd never met anyone from LoD. Even some of the rotating cadre who were actually and formally "in LoD" knew one another only by board-mail and pseudonyms. This was a highly unconventional profile for a criminal conspiracy. Computer networking, and the rapid evolution of the digital underground, made the situation very diffuse and confusing. Furthermore, a big reputation in the digital underground did not coincide with one's willingness to commit "crimes." Instead, reputation was based on cleverness and technical mastery. As a result, it often seemed that the heavier the hackers were, the less likely they were to have committed any kind of common, easily prosecutable crime. There were some hackers who could really steal. And there were hackers who could really hack. But the two groups didn't seem to overlap much, if at all. For instance, most people in the underground looked up to "Emmanuel Goldstein" of *2600* as a hacker demigod. But Goldstein's publishing activities were entirely legal -- Goldstein just printed dodgy stuff and talked about politics, he didn't even hack. When you came right down to it, Goldstein spent half his time complaining that computer security wasn't strong enough and ought to be drastically improved across the board! Truly heavy-duty hackers, those with serious technical skills who had earned the respect of the underground, never stole money or abused credit cards. Sometimes they might abuse phone-codes -- but often, they seemed to get all the free phone-time they wanted without leaving a trace of any kind. The best hackers, the most powerful and technically accomplished, were not professional fraudsters. They raided computers habitually, but wouldn't alter anything, or damage anything. They didn't even steal computer equipment -- most had day-jobs messing with hardware, and could get all the cheap secondhand equipment they wanted. The hottest hackers, unlike the teenage wannabes, weren't snobs about fancy or expensive hardware. Their machines tended to be raw second-hand digital hot-rods full of custom add-ons that they'd cobbled together out of chickenwire, memory chips and spit. Some were adults, computer software writers and consultants by trade, and making quite good livings at it. Some of them actually worked for the phone company -- and for those, the "hackers" actually found under the skirts of Ma Bell, there would be little mercy in 1990. It has long been an article of faith in the underground that the "best" hackers never get caught. They're far too smart, supposedly. They never get caught because they never boast, brag, or strut. These demigods may read underground boards (with a condescending smile), but they never say anything there. The "best" hackers, according to legend, are adult computer professionals, such as mainframe system administrators, who already know the ins and outs of their particular brand of security. Even the "best" hacker can't break in to just any computer at random: the knowledge of security holes is too specialized, varying widely with different software and hardware. But if people are employed to run, say, a UNIX mainframe or a VAX/VMS machine, then they tend to learn security from the inside out. Armed with this knowledge, they can look into most anybody else's UNIX or VMS without much trouble or risk, if they want to. And, according to hacker legend, of course they want to, so of course they do. They just don't make a big deal of what they've done. So nobody ever finds out. It is also an article of faith in the underground that professional telco people "phreak" like crazed weasels. Of course they spy on Madonna's phone calls -- I mean, wouldn't you? Of course they give themselves free longdistance -- why the hell should they pay, they're running the whole shebang! It has, as a third matter, long been an article of faith that any hacker caught can escape serious punishment if he confesses how he did it. Hackers seem to believe that governmental agencies and large corporations are blundering about in cyberspace like eyeless jellyfish or cave salamanders. They feel that these large but pathetically stupid organizations will proffer up genuine gratitude, and perhaps even a security post and a big salary, to the hot-shot intruder who will deign to reveal to them the supreme genius of his modus operandi. In the case of longtime LoD member "Control-C," this actually happened, more or less. Control-C had led Michigan Bell a merry chase, and when captured in 1987, he turned out to be a bright and apparently physically harmless young fanatic, fascinated by phones. There was no chance in hell that Control-C would actually repay the enormous and largely theoretical sums in long-distance service that he had accumulated from Michigan Bell. He could always be indicted for fraud or computer-intrusion, but there seemed little real point in this -- he hadn't physically damaged any computer. He'd just plead guilty, and he'd likely get the usual slap-on-the-wrist, and in the meantime it would be a big hassle for Michigan Bell just to bring up the case. But if kept on the payroll, he might at least keep his fellow hackers at bay. There were uses for him. For instance, a contrite Control-C was featured on Michigan Bell internal posters, sternly warning employees to shred their trash. He'd always gotten most of his best inside info from "trashing" - raiding telco dumpsters, for useful data indiscreetly thrown away. He signed these posters, too. Control-C had become something like a Michigan Bell mascot. And in fact, Control-C did keep other hackers at bay. Little hackers were quite scared of Control-C and his heavy-duty Legion of Doom friends. And big hackers were his friends and didn't want to screw up his cushy situation. No matter what one might say of LoD, they did stick together. When "Wasp," an apparently genuinely malicious New York hacker, began crashing Bellcore machines, Control-C received swift volunteer help from "the Mentor" and the Georgia LoD wing made up of "The Prophet," "Urvile," and "Leftist." Using Mentor's Phoenix Project board to coordinate, the Doomsters helped telco security to trap Wasp, by luring him into a machine with a tap and line-trace installed. Wasp lost. LoD won! And my, did they brag. Urvile, Prophet and Leftist were well-qualified for this activity, probably more so even than the quite accomplished Control-C. The Georgia boys knew all about phone switching-stations. Though relative johnny-comelatelies in the Legion of Doom, they were considered some of LoD's heaviest guys, into the hairiest systems around. They had the good fortune to live in or near Atlanta, home of the sleepy and apparently tolerant BellSouth RBOC. As RBOC security went, BellSouth were "cake." US West (of Arizona, the Rockies and the Pacific Northwest) were tough and aggressive, probably the heaviest RBOC around. Pacific Bell, California's PacBell, were sleek, high- tech, and longtime veterans of the LA phone- phreak wars. NYNEX had the misfortune to run the New York City area, and were warily prepared for most anything. Even Michigan Bell, a division of the Ameritech RBOC, at least had the elementary sense to hire their own hacker as a useful scarecrow. But BellSouth, even though their corporate P.R. proclaimed them to have "Everything You Expect From a Leader," were pathetic. When rumor about LoD's mastery of Georgia's switching network got around to BellSouth through Bellcore and telco security scuttlebutt, they at first refused to believe it. If you paid serious attention to every rumor out and about these hacker kids, you would hear all kinds of wacko saucer-nut nonsense: that the National Security Agency monitored all American phone calls, that the CIA and DEA tracked traffic on bulletin- boards with wordanalysis programs, that the Condor could start World War III from a payphone. If there were hackers into BellSouth switchingstations, then how come nothing had happened? Nothing had been hurt. BellSouth's machines weren't crashing. BellSouth wasn't suffering especially badly from fraud. BellSouth's customers weren't complaining. BellSouth was headquartered in Atlanta, ambitious metropolis of the new high-tech Sunbelt; and BellSouth was upgrading its network by leaps and bounds, digitizing the works left right and center. They could hardly be considered sluggish or naive. BellSouth's technical expertise was second to none, thank you kindly. But then came the Florida business. On June 13, 1989, callers to the Palm Beach County Probation Department, in Delray Beach, Florida, found themselves involved in a remarkable discussion with a phone-sex worker named "Tina" in New York State. Somehow, any call to this probation office near Miami was instantly and magically transported across state lines, at no extra charge to the user, to a pornographic phonesex hotline hundreds of miles away! This practical joke may seem utterly hilarious at first hearing, and indeed there was a good deal of chuckling about it in phone phreak circles, including the Autumn 1989 issue of *2600*. But for Southern Bell (the division of the BellSouth RBOC supplying local service for Florida, Georgia, North Carolina and South Carolina), this was a smoking gun. For the first time ever, a computer intruder had broken into a BellSouth central office switching station and re-programmed it! Or so BellSouth thought in June 1989. Actually, LoD members had been frolicking harmlessly in BellSouth switches since September 1987. The stunt of June 13 -call-forwarding a number through manipulation of a switching station -- was child's play for hackers as accomplished as the Georgia wing of LoD. Switching calls interstate sounded like a big deal, but it took only four lines of code to accomplish this. An easy, yet more discreet, stunt, would be to call-forward another number to your own house. If you were careful and considerate, and changed the software back later, then not a soul would know. Except you. And whoever you had bragged to about it. As for BellSouth, what they didn't know wouldn't hurt them. Except now somebody had blown the whole thing wide open, and BellSouth knew. A now alerted and considerably paranoid BellSouth began searching switches right and left for signs of impropriety, in that hot summer of 1989. No fewer than forty-two BellSouth employees were put on 12-hour shifts, twenty-four hours a day, for two solid months, poring over records and monitoring computers for any sign of phony access. These forty-two overworked experts were known as BellSouth's "Intrusion Task Force." 6. === What the investigators found astounded them. Proprietary telco databases had been manipulated: phone numbers had been created out of thin air, with no users' names and no addresses. And perhaps worst of all, no charges and no records of use. The new digital ReMOB (Remote Observation) diagnostic feature had been extensively tampered with -- hackers had learned to reprogram ReMOB software, so that they could listen in on any switch-routed call at their leisure! They were using telco property to spy! The electrifying news went out throughout law enforcement in 1989. It had never really occurred to anyone at BellSouth that their prized and brand-new digital switching-stations could be re-programmed. People seemed utterly amazed that anyone could have the nerve. Of course these switching stations were "computers," and everybody knew hackers liked to "break into computers:" but telephone people's computers were different from normal people's computers. The exact reason why these computers were "different" was rather ill-defined. It certainly wasn't the extent of their security. The security on these BellSouth computers was lousy; the AIMSX computers, for instance, didn't even have passwords. But there was no question that BellSouth strongly felt that their computers were very different indeed. And if there were some criminals out there who had not gotten that message, BellSouth was determined to see that message taught. After all, a 5ESS switching station was no mere bookkeeping system for some local chain of florists. Public service depended on these stations. Public safety depended on these stations. And hackers, lurking in there call-forwarding or ReMobbing, could spy on anybody in the local area! They could spy on telco officials! They could spy on police stations! They could spy on local offices of the Secret Service.... In 1989, electronic cops and hacker-trackers began using scrambler-phones and secured lines. It only made sense. There was no telling who was into those systems. Whoever they were, they sounded scary. This was some new level of antisocial daring. Could be West German hackers, in the pay of the KGB. That too had seemed a weird and farfetched notion, until Clifford Stoll had poked and prodded a sluggish Washington law-enforcement bureaucracy into investigating a computer intrusion that turned out to be exactly that -- hackers, in the pay of the KGB! Stoll, the systems manager for an Internet lab in Berkeley California, had ended up on the front page of the *New York Times*, proclaimed a national hero in the first true story of international computer espionage. Stoll's counterspy efforts, which he related in a bestselling book, *The Cuckoo's Egg*, in 1989, had established the credibility of 'hacking' as a possible threat to national security. The United States Secret Service doesn't mess around when it suspects a possible action by a foreign intelligence apparat. The Secret Service scrambler-phones and secured lines put a tremendous kink in law enforcement's ability to operate freely; to get the word out, cooperate, prevent misunderstandings. Nevertheless, 1989 scarcely seemed the time for half-measures. If the police and Secret Service themselves were not operationally secure, then how could they reasonably demand measures of security from private enterprise? At least, the inconvenience made people aware of the seriousness of the threat. If there was a final spur needed to get the police off the dime, it came in the realization that the emergency 911 system was vulnerable. The 911 system has its own specialized software, but it is run on the same digital switching systems as the rest of the telephone network. 911 is not physically different from normal telephony. But it is certainly culturally different, because this is the area of telephonic cyberspace reserved for the police and emergency services. Your average policeman may not know much about hackers or phone-phreaks. Computer people are weird; even computer cops are rather weird; the stuff they do is hard to figure out. But a threat to the 911 system is anything but an abstract threat. If the 911 system goes, people can die. Imagine being in a car-wreck, staggering to a phonebooth, punching 911 and hearing "Tina" pick up the phone-sex line somewhere in New York! The situation's no longer comical, somehow. And was it possible? No question. Hackers had attacked 911 systems before. Phreaks can max-out 911 systems just by siccing a bunch of computer-modems on them in tandem, dialling them over and over until they clog. That's very crude and low-tech, but it's still a serious business. The time had come for action. It was time to take stern measures with the underground. It was time to start picking up the dropped threads, the loose edges, the bits of braggadocio here and there; it was time to get on the stick and start putting serious casework together. Hackers weren't "invisible." They thought they were invisible; but the truth was, they had just been tolerated too long. Under sustained police attention in the summer of '89, the digital underground began to unravel as never before. The first big break in the case came very early on: July 1989, the following month. The perpetrator of the "Tina" switch was caught, and confessed. His name was "Fry Guy," a 16-year-old in Indiana. Fry Guy had been a very wicked young man. Fry Guy had earned his handle from a stunt involving French fries. Fry Guy had filched the log-in of a local MacDonald's manager and had logged-on to the MacDonald's mainframe on the Sprint Telenet system. Posing as the manager, Fry Guy had altered MacDonald's records, and given some teenage hamburger-flipping friends of his, generous raises. He had not been caught. Emboldened by success, Fry Guy moved on to creditcard abuse. Fry Guy was quite an accomplished talker; with a gift for "social engineering." If you can do "social engineering" -- fast-talk, fake-outs, impersonation, conning, scamming -- then card abuse comes easy. (Getting away with it in the long run is another question). Fry Guy had run across "Urvile" of the Legion of Doom on the ALTOS Chat board in Bonn, Germany. ALTOS Chat was a sophisticated board, accessible through globe-spanning computer networks like BITnet, Tymnet, and Telenet. ALTOS was much frequented by members of Germany's Chaos Computer Club. Two Chaos hackers who hung out on ALTOS, "Jaeger" and "Pengo," had been the central villains of Clifford Stoll's CUCKOO'S EGG case: consorting in East Berlin with a spymaster from the KGB, and breaking into American computers for hire, through the Internet. When LoD members learned the story of Jaeger's depredations from Stoll's book, they were rather less than impressed, technically speaking. On LoD's own favorite board of the moment, "Black Ice," LoD members bragged that they themselves could have done all the Chaos breakins in a week flat! Nevertheless, LoD were grudgingly impressed by the Chaos rep, the sheer hairy-eyed daring of hash-smoking anarchist hackers who had rubbed shoulders with the fearsome big-boys of international Communist espionage. LoD members sometimes traded bits of knowledge with friendly German hackers on ALTOS -- phone numbers for vulnerable VAX/VMS computers in Georgia, for instance. Dutch and British phone phreaks, and the Australian clique of "Phoenix," "Nom," and "Electron," were ALTOS regulars, too. In underground circles, to hang out on ALTOS was considered the sign of an elite dude, a sophisticated hacker of the international digital jet-set. Fry Guy quickly learned how to raid information from credit-card consumer-reporting agencies. He had over a hundred stolen credit-card numbers in his notebooks, and upwards of a thousand swiped long- distance access codes. He knew how to get onto Altos, and how to talk the talk of the underground convincingly. He now wheedled knowledge of switching-station tricks from Urvile on the ALTOS system. Combining these two forms of knowledge enabled Fry Guy to bootstrap his way up to a new form of wirefraud. First, he'd snitched credit card numbers from credit-company computers. The data he copied included names, addresses and phone numbers of the random card-holders. Then Fry Guy, impersonating a card-holder, called up Western Union and asked for a cash advance on "his" credit card. Western Union, as a security guarantee, would call the customer back, at home, to verify the transaction. But, just as he had switched the Florida probation office to "Tina" in New York, Fry Guy switched the cardholder's number to a local pay- phone. There he would lurk in wait, muddying his trail by routing and re- routing the call, through switches as far away as Canada. When the call came through, he would boldly "social-engineer," or con, the Western Union people, pretending to be the legitimate card-holder. Since he'd answered the proper phone number, the deception was not very hard. Western Union's money was then shipped to a confederate of Fry Guy's in his home town in Indiana. Fry Guy and his cohort, using LoD techniques, stole six thousand dollars from Western Union between December 1988 and July 1989. They also dabbled in ordering delivery of stolen goods through card- fraud. Fry Guy was intoxicated with success. The sixteen-year-old fantasized wildly to hacker rivals, boasting that he'd used rip-off money to hire himself a big limousine, and had driven out-of-state with a groupie from his favorite heavymetal band, Motley Crue. Armed with knowledge, power, and a gratifying stream of free money, Fry Guy now took it upon himself to call local representatives of Indiana Bell security, to brag, boast, strut, and utter tormenting warnings that his powerful friends in the notorious Legion of Doom could crash the national telephone network. Fry Guy even named a date for the scheme: the Fourth of July, a national holiday. This egregious example of the begging-for-arrest syndrome was shortly followed by Fry Guy's arrest. After the Indiana telephone company figured out who he was, the Secret Service had DNRs -- Dialed Number Recorders -- installed on his home phone lines. These devices are not taps, and can't record the substance of phone calls, but they do record the phone numbers of all calls going in and out. Tracing these numbers showed Fry Guy's long-distance code fraud, his extensive ties to pirate bulletin boards, and numerous personal calls to his LoD friends in Atlanta. By July 11, 1989, Prophet, Urvile and Leftist also had Secret Service DNR "pen registers" installed on their own lines. The Secret Service showed up in force at Fry Guy's house on July 22, 1989, to the horror of his unsuspecting parents. The raiders were led by a special agent from the Secret Service's Indianapolis office. However, the raiders were accompanied and advised by Timothy M. Foley of the Secret Service's Chicago office (a gentleman about whom we will soon be hearing a great deal). Following federal computer-crime techniques that had been standard since the early 1980s, the Secret Service searched the house thoroughly, and seized all of Fry Guy's electronic equipment and notebooks. All Fry Guy's equipment went out the door in the custody of the Secret Service, which put a swift end to his depredations. The USSS interrogated Fry Guy at length. His case was put in the charge of Deborah Daniels, the federal US Attorney for the Southern District of Indiana. Fry Guy was charged with eleven counts of computer fraud, unauthorized computer access, and wire fraud. The evidence was thorough and irrefutable. For his part, Fry Guy blamed his corruption on the Legion of Doom and offered to testify against them. Fry Guy insisted that the Legion intended to crash the phone system on a national holiday. And when AT&T crashed on Martin Luther King Day, 1990, this lent a credence to his claim that genuinely alarmed telco security and the Secret Service. Fry Guy eventually pled guilty on May 31, 1990. On September 14, he was sentenced to forty-four months' probation and four hundred hours' community service. He could have had it much worse; but it made sense to prosecutors to take it easy on this teenage minor, while zeroing in on the notorious kingpins of the Legion of Doom. But the case against LoD had nagging flaws. Despite the best effort of investigators, it was impossible to prove that the Legion had crashed the phone system on January 15, because they, in fact, hadn't done so. The investigations of 1989 did show that certain members of the Legion of Doom had achieved unprecedented power over the telco switching stations, and that they were in active conspiracy to obtain more power yet. Investigators were privately convinced that the Legion of Doom intended to do awful things with this knowledge, but mere evil intent was not enough to put them in jail. And although the Atlanta Three -- Prophet, Leftist, and especially Urvile -- had taught Fry Guy plenty, they were not themselves credit-card fraudsters. The only thing they'd "stolen" was long-distance service -- and since they'd done much of that through phone-switch manipulation, there was no easy way to judge how much they'd "stolen," or whether this practice was even "theft" of any easily recognizable kind. Fry Guy's theft of long-distance codes had cost the phone companies plenty. The theft of long-distance service may be a fairly theoretical "loss," but it costs genuine money and genuine time to delete all those stolen codes, and to re-issue new codes to the innocent owners of those corrupted codes. The owners of the codes themselves are victimized, and lose time and money and peace of mind in the hassle. And then there were the credit-card victims to deal with, too, and Western Union. When it came to rip-off, Fry Guy was far more of a thief than LoD. It was only when it came to actual computer expertise that Fry Guy was small potatoes. The Atlanta Legion thought most "rules" of cyberspace were for rodents and losers, but they did have rules. They never crashed anything, and they never took money. These were rough rules-of-thumb, and rather dubious principles when it comes to the ethical subtleties of cyberspace, but they enabled the Atlanta Three to operate with a relatively clear conscience (though never with peace of mind). If you didn't hack for money, if you weren't robbing people of actual funds -- money in the bank, that is -then nobody really got hurt, in LoD's opinion. "Theft of service" was a bogus issue, and "intellectual property" was a bad joke. But LoD had only elitist contempt for rip-off artists, "leechers," thieves. They considered themselves clean. In their opinion, if you didn't smash-up or crash any systems -- (well, not on purpose, anyhow -- accidents can happen, just ask Robert Morris) then it was very unfair to call you a "vandal" or a "cracker." When you were hanging out on-line with your "pals" in telco security, you could face them down from the higher plane of hacker morality. And you could mock the police from the supercilious heights of your hacker's quest for pure knowledge. But from the point of view of law enforcement and telco security, however, Fry Guy was not really dangerous. The Atlanta Three were dangerous. It wasn't the crimes they were committing, but the danger, the potential hazard, the sheer technical power LoD had accumulated, that had made the situation untenable. Fry Guy was not LoD. He'd never laid eyes on anyone in LoD; his only contacts with them had been electronic. Core members of the Legion of Doom tended to meet physically for conventions every year or so, to get drunk, give each other the hacker high-sign, send out for pizza and ravage hotel suites. Fry Guy had never done any of this. Deborah Daniels assessed Fry Guy accurately as "an LoD wannabe." Nevertheless Fry Guy's crimes would be directly attributed to LoD in much future police propaganda. LoD would be described as "a closely knit group" involved in "numerous illegal activities" including "stealing and modifying individual credit histories," and "fraudulently obtaining money and property." Fry Guy did this, but the Atlanta Three didn't; they simply weren't into theft, but rather intrusion. This caused a strange kink in the prosecution's strategy. LoD were accused of "disseminating information about attacking computers to other computer hackers in an effort to shift the focus of law enforcement to those other hackers and away from the Legion of Doom." This last accusation (taken directly from a press release by the Chicago Computer Fraud and Abuse Task Force) sounds particularly far- fetched. One might conclude at this point that investigators would have been well-advised to go ahead and "shift their focus" from the "Legion of Doom." Maybe they should concentrate on "those other hackers" -- the ones who were actually stealing money and physical objects. But the Hacker Crackdown of 1990 was not a simple policing action. It wasn't meant just to walk the beat in cyberspace -- it was a crackdown, a deliberate attempt to nail the core of the operation, to send a dire and potent message that would settle the hash of the digital underground for good. By this reasoning, Fry Guy wasn't much more than the electronic equivalent of a cheap streetcorner dope dealer. As long as the masterminds of LoD were still flagrantly operating, pushing their mountains of illicit knowledge right and left, and whipping up enthusiasm for blatant lawbreaking, then there would be an infinite supply of Fry Guys. Because LoD were flagrant, they had left trails everywhere, to be picked up by law enforcement in New York, Indiana, Florida, Texas, Arizona, Missouri, even Australia. But 1990's war on the Legion of Doom was led out of Illinois, by the Chicago Computer Fraud and Abuse Task Force. 7. === The Computer Fraud and Abuse Task Force, led by federal prosecutor William J. Cook, had started in 1987 and had swiftly become one of the most aggressive local "dedicated computer-crime units." Chicago was a natural home for such a group. The world's first computer bulletin-board system had been invented in Illinois. The state of Illinois had some of the nation's first and sternest computer crime laws. Illinois State Police were markedly alert to the possibilities of white-collar crime and electronic fraud. And William J. Cook in particular was a rising star in electronic crime-busting. He and his fellow federal prosecutors at the U.S. Attorney's office in Chicago had a tight relation with the Secret Service, especially gogetting Chicago-based agent Timothy Foley. While Cook and his Department of Justice colleagues plotted strategy, Foley was their man on the street. Throughout the 1980s, the federal government had given prosecutors an armory of new, untried legal tools against computer crime. Cook and his colleagues were pioneers in the use of these new statutes in the real-life cut-and-thrust of the federal courtroom. On October 2, 1986, the US Senate had passed the "Computer Fraud and Abuse Act" unanimously, but there were pitifully few convictions under this statute. Cook's group took their name from this statute, since they were determined to transform this powerful but rather theoretical Act of Congress into a real-life engine of legal destruction against computer fraudsters and scofflaws. It was not a question of merely discovering crimes, investigating them, and then trying and punishing their perpetrators. The Chicago unit, like most everyone else in the business, already knew who the bad guys were: the Legion of Doom and the writers and editors of Phrack. The task at hand was to find some legal means of putting these characters away. This approach might seem a bit dubious, to someone not acquainted with the gritty realities of prosecutorial work. But prosecutors don't put people in jail for crimes they have committed; they put people in jail for crimes they have committed that can be proved in court. Chicago federal police put Al Capone in prison for income-tax fraud. Chicago is a big town, with a roughand-ready bare-knuckle tradition on both sides of the law. Fry Guy had broken the case wide open and alerted telco security to the scope of the problem. But Fry Guy's crimes would not put the Atlanta Three behind bars -much less the wacko underground journalists of Phrack. So on July 22, 1989, the same day that Fry Guy was raided in Indiana, the Secret Service descended upon the Atlanta Three. This was likely inevitable. By the summer of 1989, law enforcement were closing in on the Atlanta Three from at least six directions at once. First, there were the leads from Fry Guy, which had led to the DNR registers being installed on the lines of the Atlanta Three. The DNR evidence alone would have finished them off, sooner or later. But second, the Atlanta lads were already well-known to Control-C and his telco security sponsors. LoD's contacts with telco security had made them overconfident and even more boastful than usual; they felt that they had powerful friends in high places, and that they were being openly tolerated by telco security. But BellSouth's Intrusion Task Force were hot on the trail of LoD and sparing no effort or expense. The Atlanta Three had also been identified by name and listed on the extensive anti-hacker files maintained, and retailed for pay, by private security operative John Maxfield of Detroit. Maxfield, who had extensive ties to telco security and many informants in the underground, was a bete noire of the Phrack crowd, and the dislike was mutual. The Atlanta Three themselves had written articles for Phrack. This boastful act could not possibly escape telco and law enforcement attention. "Knightmare," a high-school age hacker from Arizona, was a close friend and disciple of Atlanta LoD, but he had been nabbed by the formidable Arizona Organized Crime and Racketeering Unit. Knightmare was on some of LoD's favorite boards -- "Black Ice" in particular -- and was privy to their secrets. And to have Gail Thackeray, the Assistant Attorney General of Arizona, on one's trail was a dreadful peril for any hacker. And perhaps worst of all, Prophet had committed a major blunder by passing an illicitly copied BellSouth computer-file to Knight Lightning, who had published it in Phrack. This, as we will see, was an act of dire consequence for almost everyone concerned. On July 22, 1989, the Secret Service showed up at the Leftist's house, where he lived with his parents. A massive squad of some twenty officers surrounded the building: Secret Service, federal marshals, local police, possibly BellSouth telco security; it was hard to tell in the crush. Leftist's dad, at work in his basement office, first noticed a muscular stranger in plain clothes crashing through the back yard with a drawn pistol. As more strangers poured into the house, Leftist's dad naturally assumed there was an armed robbery in progress. Like most hacker parents, Leftist's mom and dad had only the vaguest notions of what their son had been up to all this time. Leftist had a day-job repairing computer hardware. His obsession with computers seemed a bit odd, but harmless enough, and likely to produce a wellpaying career. The sudden, overwhelming raid left Leftist's parents traumatized. The Leftist himself had been out after work with his co-workers, surrounding a couple of pitchers of margaritas. As he came trucking on tequila-numbed feet up the pavement, toting a bag full of floppy-disks, he noticed a large number of unmarked cars parked in his driveway. All the cars sported tiny microwave antennas. The Secret Service had knocked the front door off its hinges, almost flattening his Mom. Inside, Leftist was greeted by Special Agent James Cool of the US Secret Service, Atlanta office. Leftist was flabbergasted. He'd never met a Secret Service agent before. He could not imagine that he'd ever done anything worthy of federal attention. He'd always figured that if his activities became intolerable, one of his contacts in telco security would give him a private phone-call and tell him to knock it off. But now Leftist was pat-searched for weapons by grim professionals, and his bag of floppies was quickly seized. He and his parents were all shepherded into separate rooms and grilled at length as a score of officers scoured their home for anything electronic. Leftist was horrified as his treasured IBM AT personal computer with its forty-meg hard disk, and his recently purchased 80386 IBM-clone with a whopping hundred-meg hard disk, both went swiftly out the door in Secret Service custody. They also seized all his disks, all his notebooks, and a tremendous booty in dogeared telco documents that Leftist had snitched out of trash dumpsters. Leftist figured the whole thing for a big misunderstanding. He'd never been into military computers. He wasn't a spy or a Communist. He was just a good ol' Georgia hacker, and now he just wanted all these people out of the house. But it seemed they wouldn't go until he made some kind of statement. And so, he levelled with them. And that, Leftist said later from his federal prison camp in Talladega, Alabama, was a big mistake. The Atlanta area was unique, in that it had three members of the Legion of Doom who actually occupied more or less the same physical locality. Unlike the rest of LoD, who tended to associate by phone and computer, Atlanta LoD actually were "tightly knit." It was no real surprise that the Secret Service agents apprehending Urvile at the computer-labs at Georgia Tech, would discover Prophet with him as well. Urvile, a 21-year-old Georgia Tech student in polymer chemistry, posed quite a puzzling case for law enforcement. Urvile -- also known as "Necron 99," as well as other handles, for he tended to change his cover- alias about once a month -- was both an accomplished hacker and a fanatic simulation-gamer. Simulation games are an unusual hobby; but then hackers are unusual people, and their favorite pastimes tend to be somewhat out of the ordinary. The best-known American simulation game is probably "Dungeons & Dragons," a multi-player parlor entertainment played with paper, maps, pencils, statistical tables and a variety of oddly-shaped dice. Players pretend to be heroic characters exploring a wholly-invented fantasy world. The fantasy worlds of simulation gaming are commonly pseudo-medieval, involving swords and sorcery -- spellcasting wizards, knights in armor, unicorns and dragons, demons and goblins. Urvile and his fellow gamers preferred their fantasies highly technological. They made use of a game known as "G.U.R.P.S.," the "Generic Universal Role Playing System," published by a company called Steve Jackson Games (SJG). "G.U.R.P.S." served as a framework for creating a wide variety of artificial fantasy worlds. Steve Jackson Games published a smorgasboard of books, full of detailed information and gaming hints, which were used to flesh-out many different fantastic backgrounds for the basic GURPS framework. Urvile made extensive use of two SJG books called *GURPS High-Tech* and *GURPS Special Ops*. In the artificial fantasy-world of *GURPS Special Ops*, players entered a modern fantasy of intrigue and international espionage. On beginning the game, players started small and powerless, perhaps as minor-league CIA agents or penny-ante arms dealers. But as players persisted through a series of game sessions (game sessions generally lasted for hours, over long, elaborate campaigns that might be pursued for months on end) then they would achieve new skills, new knowledge, new power. They would acquire and hone new abilities, such as marksmanship, karate, wiretapping, or Watergate burglary. They could also win various kinds of imaginary booty, like Berettas, or martini shakers, or fast cars with ejection seats and machine-guns under the headlights. As might be imagined from the complexity of these games, Urvile's gaming notes were very detailed and extensive. Urvile was a "dungeon-master," inventing scenarios for his fellow gamers, giant simulated adventure-puzzles for his friends to unravel. Urvile's game notes covered dozens of pages with all sorts of exotic lunacy, all about ninja raids on Libya and break-ins on encrypted Red Chinese supercomputers. His notes were written on scrap-paper and kept in loose- leaf binders. The handiest scrap paper around Urvile's college digs were the many pounds of BellSouth printouts and documents that he had snitched out of telco dumpsters. His notes were written on the back of misappropriated telco property. Worse yet, the gaming notes were chaotically interspersed with Urvile's hand-scrawled records involving actual computer intrusions that he had committed. Not only was it next to impossible to tell Urvile's fantasy game- notes from cyberspace "reality," but Urvile himself barely made this distinction. It's no exaggeration to say that to Urvile it was all a game. Urvile was very bright, highly imaginative, and quite careless of other people's notions of propriety. His connection to "reality" was not something to which he paid a great deal of attention. Hacking was a game for Urvile. It was an amusement he was carrying out, it was something he was doing for fun. And Urvile was an obsessive young man. He could no more stop hacking than he could stop in the middle of a jigsaw puzzle, or stop in the middle of reading a Stephen Donaldson fantasy trilogy. (The name "Urvile" came from a best-selling Donaldson novel.) Urvile's airy, bulletproof attitude seriously annoyed his interrogators. First of all, he didn't consider that he'd done anything wrong. There was scarcely a shred of honest remorse in him. On the contrary, he seemed privately convinced that his police interrogators were operating in a demented fantasy-world all their own. Urvile was too polite and well-behaved to say this straightout, but his reactions were askew and disquieting. For instance, there was the business about LoD's ability to monitor phone-calls to the police and Secret Service. Urvile agreed that this was quite possible, and posed no big problem for LoD. In fact, he and his friends had kicked the idea around on the "Black Ice" board, much as they had discussed many other nifty notions, such as building personal flame-throwers and jury-rigging fistfulls of blasting-caps. They had hundreds of dial-up numbers for government agencies that they'd gotten through scanning Atlanta phones, or had pulled from raided VAX/VMS mainframe computers. Basically, they'd never gotten around to listening in on the cops because the idea wasn't interesting enough to bother with. Besides, if they'd been monitoring Secret Service phone calls, obviously they'd never have been caught in the first place. Right? The Secret Service was less than satisfied with this rapier-like hacker logic. Then there was the issue of crashing the phone system. No problem, Urvile admitted sunnily. Atlanta LoD could have shut down phone service all over Atlanta any time they liked. Even the 911 service? Nothing special about that, Urvile explained patiently. Bring the switch to its knees, with say the UNIX "makedir" bug, and 911 goes down too as a matter of course. The 911 system wasn't very interesting, frankly. It might be tremendously interesting to cops (for odd reasons of their own), but as technical challenges went, the 911 service was yawnsville. So of course the Atlanta Three could crash service. They probably could have crashed service all over BellSouth territory, if they'd worked at it for a while. But Atlanta LoD weren't crashers. Only losers and rodents were crashers. LoD were *elite*. Urvile was privately convinced that sheer technical expertise could win him free of any kind of problem. As far as he was concerned, elite status in the digital underground had placed him permanently beyond the intellectual grasp of cops and straights. Urvile had a lot to learn. Of the three LoD stalwarts, Prophet was in the most direct trouble. Prophet was a UNIX programming expert who burrowed in and out of the Internet as a matter of course. He'd started his hacking career at around age 14, meddling with a UNIX mainframe system at the University of North Carolina. Prophet himself had written the handy Legion of Doom file "UNIX Use and Security From the Ground Up." UNIX (pronounced "you-nicks") is a powerful, flexible computer operating-system, for multi-user, multi- tasking computers. In 1969, when UNIX was created in Bell Labs, such computers were exclusive to large corporations and universities, but today UNIX is run on thousands of powerful home machines. UNIX was particularly wellsuited to telecommunications programming, and had become a standard in the field. Naturally, UNIX also became a standard for the elite hacker and phone phreak. Lately, Prophet had not been so active as Leftist and Urvile, but Prophet was a recidivist. In 1986, when he was eighteen, Prophet had been convicted of "unauthorized access to a computer network" in North Carolina. He'd been discovered breaking into the Southern Bell Data Network, a UNIX-based internal telco network supposedly closed to the public. He'd gotten a typical hacker sentence: six months suspended, 120 hours community service, and three years' probation. After that humiliating bust, Prophet had gotten rid of most of his tonnage of illicit phreak and hacker data, and had tried to go straight. He was, after all, still on probation. But by the autumn of 1988, the temptations of cyberspace had proved too much for young Prophet, and he was shoulder-to-shoulder with Urvile and Leftist into some of the hairiest systems around. In early September 1988, he'd broken into BellSouth's centralized automation system, AIMSX or "Advanced Information Management System." AIMSX was an internal business network for BellSouth, where telco employees stored electronic mail, databases, memos, and calendars, and did text processing. Since AIMSX did not have public dial-ups, it was considered utterly invisible to the public, and was not well- secured -- it didn't even require passwords. Prophet abused an account known as "waa1," the personal account of an unsuspecting telco employee. Disguised as the owner of waa1, Prophet made about ten visits to AIMSX. Prophet did not damage or delete anything in the system. His presence in AIMSX was harmless and almost invisible. But he could not rest content with that. One particular piece of processed text on AIMSX was a telco document known as "Bell South Standard Practice 660-225-104SV Control Office Administration of Enhanced 911 Services for Special Services and Major Account Centers dated March 1988." Prophet had not been looking for this document. It was merely one among hundreds of similar documents with impenetrable titles. However, having blundered over it in the course of his illicit wanderings through AIMSX, he decided to take it with him as a trophy. It might prove very useful in some future boasting, bragging, and strutting session. So, some time in September 1988, Prophet ordered the AIMSX mainframe computer to copy this document (henceforth called simply called "the E911 Document") and to transfer this copy to his home computer. No one noticed that Prophet had done this. He had "stolen" the E911 Document in some sense, but notions of property in cyberspace can be tricky. BellSouth noticed nothing wrong, because BellSouth still had their original copy. They had not been "robbed" of the document itself. Many people were supposed to copy this document -specifically, people who worked for the nineteen BellSouth "special services and major account centers," scattered throughout the Southeastern United States. That was what it was for, why it was present on a computer network in the first place: so that it could be copied and read -by telco employees. But now the data had been copied by someone who wasn't supposed to look at it. Prophet now had his trophy. But he further decided to store yet another copy of the E911 Document on another person's computer. This unwitting person was a computer enthusiast named Richard Andrews who lived near Joliet, Illinois. Richard Andrews was a UNIX programmer by trade, and ran a powerful UNIX board called "Jolnet," in the basement of his house. Prophet, using the handle "Robert Johnson," had obtained an account on Richard Andrews' computer. And there he stashed the E911 Document, by storing it in his own private section of Andrews' computer. Why did Prophet do this? If Prophet had eliminated the E911 Document from his own computer, and kept it hundreds of miles away, on another machine, under an alias, then he might have been fairly safe from discovery and prosecution -- although his sneaky action had certainly put the unsuspecting Richard Andrews at risk. But, like most hackers, Prophet was a pack-rat for illicit data. When it came to the crunch, he could not bear to part from his trophy. When Prophet's place in Decatur, Georgia was raided in July 1989, there was the E911 Document, a smoking gun. And there was Prophet in the hands of the Secret Service, doing his best to "explain." Our story now takes us away from the Atlanta Three and their raids of the Summer of 1989. We must leave Atlanta Three "cooperating fully" with their numerous investigators. And all three of them did cooperate, as their Sentencing Memorandum from the US District Court of the Northern Division of Georgia explained -just before all three of them were sentenced to various federal prisons in November 1990. We must now catch up on the other aspects of the war on the Legion of Doom. The war on the Legion was a war on a network -- in fact, a network of three networks, which intertwined and interrelated in a complex fashion. The Legion itself, with Atlanta LoD, and their hanger-on Fry Guy, were the first network. The second network was Phrack magazine, with its editors and contributors. The third network involved the electronic circle around a hacker known as "Terminus." The war against these hacker networks was carried out by a law enforcement network. Atlanta LoD and Fry Guy were pursued by USSS agents and federal prosecutors in Atlanta, Indiana, and Chicago. "Terminus" found himself pursued by USSS and federal prosecutors from Baltimore and Chicago. And the war against Phrack was almost entirely a Chicago operation. The investigation of Terminus involved a great deal of energy, mostly from the Chicago Task Force, but it was to be the least-known and least-publicized of the Crackdown operations. Terminus, who lived in Maryland, was a UNIX programmer and consultant, fairly wellknown (under his given name) in the UNIX community, as an acknowledged expert on AT&T minicomputers. Terminus idolized AT&T, especially Bellcore, and longed for public recognition as a UNIX expert; his highest ambition was to work for Bell Labs. But Terminus had odd friends and a spotted history. Terminus had once been the subject of an admiring interview in Phrack (Volume II, Issue 14, Phile 2 -dated May 1987). In this article, Phrack co-editor Taran King described "Terminus" as an electronics engineer, 5'9", brown- haired, born in 1959 -- at 28 years old, quite mature for a hacker. Terminus had once been sysop of a phreak/hack underground board called "MetroNet," which ran on an Apple II. Later he'd replaced "MetroNet" with an underground board called "MegaNet," specializing in IBMs. In his younger days, Terminus had written one of the very first and most elegant code-scanning programs for the IBM-PC. This program had been widely distributed in the underground. Uncounted legions of PC- owning phreaks and hackers had used Terminus's scanner program to rip- off telco codes. This feat had not escaped the attention of telco security; it hardly could, since Terminus's earlier handle, "Terminal Technician," was proudly written right on the program. When he became a full-time computer professional (specializing in telecommunications programming), he adopted the handle Terminus, meant to indicate that he had "reached the final point of being a proficient hacker." He'd moved up to the UNIX-based "Netsys" board on an AT&T computer, with four phone lines and an impressive 240 megs of storage. "Netsys" carried complete issues of Phrack, and Terminus was quite friendly with its publishers, Taran King and Knight Lightning. In the early 1980s, Terminus had been a regular on Plovernet, Pirate-80, Sherwood Forest and Shadowland, all well-known pirate boards, all heavily frequented by the Legion of Doom. As it happened, Terminus was never officially "in LoD," because he'd never been given the official LoD high-sign and back-slap by Legion maven Lex Luthor. Terminus had never physically met anyone from LoD. But that scarcely mattered much -- the Atlanta Three themselves had never been officially vetted by Lex, either. As far as law enforcement was concerned, the issues were clear. Terminus was a full-time, adult computer professional with particular skills at AT&T software and hardware -- but Terminus reeked of the Legion of Doom and the underground. On February 1, 1990 -- half a month after the Martin Luther King Day Crash -- USSS agents Tim Foley from Chicago, and Jack Lewis from the Baltimore office, accompanied by AT&T security officer Jerry Dalton, travelled to Middle Town, Maryland. There they grilled Terminus in his home (to the stark terror of his wife and small children), and, in their customary fashion, hauled his computers out the door. The Netsys machine proved to contain a plethora of arcane UNIX software -- proprietary source code formally owned by AT&T. Software such as: UNIX System Five Release 3.2; UNIX SV Release 3.1; UUCP communications software; KORN SHELL; RFS; IWB; WWB; DWB; the C++ programming language; PMON; TOOL CHEST; QUEST; DACT, and S FIND. In the long-established piratical tradition of the underground, Terminus had been trading this illicitlycopied software with a small circle of fellow UNIX programmers. Very unwisely, he had stored seven years of his electronic mail on his Netsys machine, which documented all the friendly arrangements he had made with his various colleagues. Terminus had not crashed the AT&T phone system on January 15. He was, however, blithely running a notfor-profit AT&T software-piracy ring. This was not an activity AT&T found amusing. AT&T security officer Jerry Dalton valued this "stolen" property at over three hundred thousand dollars. AT&T's entry into the tussle of free enterprise had been complicated by the new, vague groundrules of the information economy. Until the break-up of Ma Bell, AT&T was forbidden to sell computer hardware or software. Ma Bell was the phone company; Ma Bell was not allowed to use the enormous revenue from telephone utilities, in order to finance any entry into the computer market. AT&T nevertheless invented the UNIX operating system. And somehow AT&T managed to make UNIX a minor source of income. Weirdly, UNIX was not sold as computer software, but actually retailed under an obscure regulatory exemption allowing sales of surplus equipment and scrap. Any bolder attempt to promote or retail UNIX would have aroused angry legal opposition from computer companies. Instead, UNIX was licensed to universities, at modest rates, where the acids of academic freedom ate away steadily at AT&T's proprietary rights. Come the breakup, AT&T recognized that UNIX was a potential gold-mine. By now, large chunks of UNIX code had been created that were not AT&T's, and were being sold by others. An entire rival UNIX- based operating system had arisen in Berkeley, California (one of the world's great founts of ideological hackerdom). Today, "hackers" commonly consider "Berkeley UNIX" to be technically superior to AT&T's "System V UNIX," but AT&T has not allowed mere technical elegance to intrude on the real-world business of marketing proprietary software. AT&T has made its own code deliberately incompatible with other folks' UNIX, and has written code that it can prove is copyrightable, even if that code happens to be somewhat awkward -- "kludgey." AT&T UNIX user licenses are serious business agreements, replete with very clear copyright statements and nondisclosure clauses. AT&T has not exactly kept the UNIX cat in the bag, but it kept a grip on its scruff with some success. By the rampant, explosive standards of software piracy, AT&T UNIX source code is heavily copyrighted, well- guarded, well-licensed. UNIX was traditionally run only on mainframe machines, owned by large groups of suit-andtie professionals, rather than on bedroom machines where people can get up to easy mischief. And AT&T UNIX source code is serious high-level programming. The number of skilled UNIX programmers with any actual motive to swipe UNIX source code is small. It's tiny, compared to the tens of thousands prepared to rip-off, say, entertaining PC games like "Leisure Suit Larry." But by 1989, the warez-d00d underground, in the persons of Terminus and his friends, was gnawing at AT&T UNIX. And the property in question was not sold for twenty bucks over the counter at the local branch of Babbage's or Egghead's; this was massive, sophisticated, multi-line, multi-author corporate code worth tens of thousands of dollars. It must be recognized at this point that Terminus's purported ring of UNIX software pirates had not actually made any money from their suspected crimes. The $300,000 dollar figure bandied about for the contents of Terminus's computer did not mean that Terminus was in actual illicit possession of three hundred thousand of AT&T's dollars. Terminus was shipping software back and forth, privately, person to person, for free. He was not making a commercial business of piracy. He hadn't asked for money; he didn't take money. He lived quite modestly. AT&T employees -- as well as freelance UNIX consultants, like Terminus -- commonly worked with "proprietary" AT&T software, both in the office and at home on their private machines. AT&T rarely sent security officers out to comb the hard disks of its consultants. Cheap freelance UNIX contractors were quite useful to AT&T; they didn't have health insurance or retirement programs, much less union membership in the Communication Workers of America. They were humble digital drudges, wandering with mop and bucket through the Great Technological Temple of AT&T; but when the Secret Service arrived at their homes, it seemed they were eating with company silverware and sleeping on company sheets! Outrageously, they behaved as if the things they worked with every day belonged to them! And these were no mere hacker teenagers with their hands full of trash-paper and their noses pressed to the corporate windowpane. These guys were UNIX wizards, not only carrying AT&T data in their machines and their heads, but eagerly networking about it, over machines that were far more powerful than anything previously imagined in private hands. How do you keep people disposable, yet assure their awestruck respect for your property? It was a dilemma. Much UNIX code was public-domain, available for free. Much "proprietary" UNIX code had been extensively re-written, perhaps altered so much that it became an entirely new product -- or perhaps not. Intellectual property rights for software developers were, and are, extraordinarily complex and confused. And software "piracy," like the private copying of videos, is one of the most widely practiced "crimes" in the world today. The USSS were not experts in UNIX or familiar with the customs of its use. The United States Secret Service, considered as a body, did not have one single person in it who could program in a UNIX environment -- no, not even one. The Secret Service were making extensive use of expert help, but the "experts" they had chosen were AT&T and Bellcore security officials, the very victims of the purported crimes under investigation, the very people whose interest in AT&T's "proprietary" software was most pronounced. On February 6, 1990, Terminus was arrested by Agent Lewis. Eventually, Terminus would be sent to prison for his illicit use of a piece of AT&T software. The issue of pirated AT&T software would bubble along in the background during the war on the Legion of Doom. Some half-dozen of Terminus's on-line acquaintances, including people in Illinois, Texas and California, were grilled by the Secret Service in connection with the illicit copying of software. Except for Terminus, however, none were charged with a crime. None of them shared his peculiar prominence in the hacker underground. But that did not meant that these people would, or could, stay out of trouble. The transferral of illicit data in cyberspace is hazy and ill- defined business, with paradoxical dangers for everyone concerned: hackers, signal carriers, board owners, cops, prosecutors, even random passers-by. Sometimes, well-meant attempts to avert trouble or punish wrongdoing bring more trouble than would simple ignorance, indifference or impropriety. Terminus's "Netsys" board was not a common-or- garden bulletin board system, though it had most of the usual functions of a board. Netsys was not a stand-alone machine, but part of the globe-spanning "UUCP" cooperative network. The UUCP network uses a set of Unix software programs called "Unix-to-Unix Copy," which allows Unix systems to throw data to one another at high speed through the public telephone network. UUCP is a radically decentralized, not-for-profit network of UNIX computers. There are tens of thousands of these UNIX machines. Some are small, but many are powerful and also link to other networks. UUCP has certain arcane links to major networks such as JANET, EasyNet, BITNET, JUNET, VNET, DASnet, PeaceNet and FidoNet, as well as the gigantic Internet. (The so-called "Internet" is not actually a network itself, but rather an "internetwork" connections standard that allows several globe-spanning computer networks to communicate with one another. Readers fascinated by the weird and intricate tangles of modern computer networks may enjoy John S. Quarterman's authoritative 719-page explication, *The Matrix*, Digital Press, 1990.) A skilled user of Terminus' UNIX machine could send and receive electronic mail from almost any major computer network in the world. Netsys was not called a "board" per se, but rather a "node." "Nodes" were larger, faster, and more sophisticated than mere "boards," and for hackers, to hang out on internationally-connected "nodes" was quite the step up from merely hanging out on local "boards." Terminus's Netsys node in Maryland had a number of direct links to other, similar UUCP nodes, run by people who shared his interests and at least something of his free- wheeling attitude. One of these nodes was Jolnet, owned by Richard Andrews, who, like Terminus, was an independent UNIX consultant. Jolnet also ran UNIX, and could be contacted at high speed by mainframe machines from all over the world. Jolnet was quite a sophisticated piece of work, technically speaking, but it was still run by an individual, as a private, not-for-profit hobby. Jolnet was mostly used by other UNIX programmers -- for mail, storage, and access to networks. Jolnet supplied access network access to about two hundred people, as well as a local junior college. Among its various features and services, Jolnet also carried Phrack magazine. For reasons of his own, Richard Andrews had become suspicious of a new user called "Robert Johnson." Richard Andrews took it upon himself to have a look at what "Robert Johnson" was storing in Jolnet. And Andrews found the E911 Document. "Robert Johnson" was the Prophet from the Legion of Doom, and the E911 Document was illicitly copied data from Prophet's raid on the BellSouth computers. The E911 Document, a particularly illicit piece of digital property, was about to resume its long, complex, and disastrous career. It struck Andrews as fishy that someone not a telephone employee should have a document referring to the "Enhanced 911 System." Besides, the document itself bore an obvious warning. "WARNING: NOT FOR USE OR DISCLOSURE OUTSIDE BELLSOUTH OR ANY OF ITS SUBSIDIARIES EXCEPT UNDER WRITTEN AGREEMENT." These standard nondisclosure tags are often appended to all sorts of corporate material. Telcos as a species are particularly notorious for stamping most everything in sight as "not for use or disclosure." Still, this particular piece of data was about the 911 System. That sounded bad to Rich Andrews. Andrews was not prepared to ignore this sort of trouble. He thought it would be wise to pass the document along to a friend and acquaintance on the UNIX network, for consultation. So, around September 1988, Andrews sent yet another copy of the E911 Document electronically to an AT&T employee, one Charles Boykin, who ran a UNIX-based node called "attctc" in Dallas, Texas. "Attctc" was the property of AT&T, and was run from AT&T's Customer Technology Center in Dallas, hence the name "attctc." "Attctc" was better-known as "Killer," the name of the machine that the system was running on. "Killer" was a hefty, powerful, AT&T 3B2 500 model, a multi-user, multi-tasking UNIX platform with 32 meg of memory and a mind-boggling 3.2 Gigabytes of storage. When Killer had first arrived in Texas, in 1985, the 3B2 had been one of AT&T's great white hopes for going head- to-head with IBM for the corporate computer- hardware market. "Killer" had been shipped to the Customer Technology Center in the Dallas Infomart, essentially a high-technology mall, and there it sat, a demonstration model. Charles Boykin, a veteran AT&T hardware and digital communications expert, was a local technical backup man for the AT&T 3B2 system. As a display model in the Infomart mall, "Killer" had little to do, and it seemed a shame to waste the system's capacity. So Boykin ingeniously wrote some UNIX bulletin-board software for "Killer," and plugged the machine in to the local phone network. "Killer's" debut in late 1985 made it the first publicly available UNIX site in the state of Texas. Anyone who wanted to play was welcome. The machine immediately attracted an electronic community. It joined the UUCP network, and offered network links to over eighty other computer sites, all of which became dependent on Killer for their links to the greater world of cyberspace. And it wasn't just for the big guys; personal computer users also stored freeware programs for the Amiga, the Apple, the IBM and the Macintosh on Killer's vast 3,200 meg archives. At one time, Killer had the largest library of public-domain Macintosh software in Texas. Eventually, Killer attracted about 1,500 users, all busily communicating, uploading and downloading, getting mail, gossipping, and linking to arcane and distant networks. Boykin received no pay for running Killer. He considered it good publicity for the AT&T 3B2 system (whose sales were somewhat less than stellar), but he also simply enjoyed the vibrant community his skill had created. He gave away the bulletin-board UNIX software he had written, free of charge. In the UNIX programming community, Charlie Boykin had the reputation of a warm, open-hearted, levelheaded kind of guy. In 1989, a group of Texan UNIX professionals voted Boykin "System Administrator of the Year." He was considered a fellow you could trust for good advice. In September 1988, without warning, the E911 Document came plunging into Boykin's life, forwarded by Richard Andrews. Boykin immediately recognized that the Document was hot property. He was not a voicecommunications man, and knew little about the ins and outs of the Baby Bells, but he certainly knew what the 911 System was, and he was angry to see confidential data about it in the hands of a nogoodnik. This was clearly a matter for telco security. So, on September 21, 1988, Boykin made yet another copy of the E911 Document and passed this one along to a professional acquaintance of his, one Jerome Dalton, from AT&T Corporate Information Security. Jerry Dalton was the very fellow who would later raid Terminus's house. From AT&T's security division, the E911 Document went to Bellcore. Bellcore (or BELL COmmunications REsearch) had once been the central laboratory of the Bell System. Bell Labs employees had invented the UNIX operating system. Now Bellcore was a quasi-independent, jointly owned company that acted as the research arm for all seven of the Baby Bell RBOCs. Bellcore was in a good position to co-ordinate security technology and consultation for the RBOCs, and the gentleman in charge of this effort was Henry M. Kluepfel, a veteran of the Bell System who had worked there for twenty-four years. On October 13, 1988, Dalton passed the E911 Document to Henry Kluepfel. Kluepfel, a veteran expert witness in telecommunications fraud and computer-fraud cases, had certainly seen worse trouble than this. He recognized the document for what it was: a trophy from a hacker break-in. However, whatever harm had been done in the intrusion was presumably old news. At this point there seemed little to be done. Kluepfel made a careful note of the circumstances and shelved the problem for the time being. Whole months passed. February 1989 arrived. The Atlanta Three were living it up in Bell South's switches, and had not yet met their comeuppance. The Legion was thriving. So was Phrack magazine. A good six months had passed since Prophet's AIMSX break-in. Prophet, as hackers will, grew weary of sitting on his laurels. "Knight Lightning" and "Taran King," the editors of Phrack, were always begging Prophet for material they could publish. Prophet decided that the heat must be off by this time, and that he could safely brag, boast, and strut. So he sent a copy of the E911 Document -- yet another one -- from Rich Andrews' Jolnet machine to Knight Lightning's BITnet account at the University of Missouri. Let's review the fate of the document so far. 0. The original E911 Document. This in the AIMSX system on a mainframe computer in Atlanta, available to hundreds of people, but all of them, presumably, BellSouth employees. An unknown number of them may have their own copies of this document, but they are all professionals and all trusted by the phone company. 1. Prophet's illicit copy, at home on his own computer in Decatur, Georgia. 2. Prophet's back-up copy, stored on Rich Andrew's Jolnet machine in the basement of Rich Andrews' house near Joliet Illinois. 3. Charles Boykin's copy on "Killer" in Dallas, Texas, sent by Rich Andrews from Joliet. 4. Jerry Dalton's copy at AT&T Corporate Information Security in New Jersey, sent from Charles Boykin in Dallas. 5. Henry Kluepfel's copy at Bellcore security headquarters in New Jersey, sent by Dalton. 6. Knight Lightning's copy, sent by Prophet from Rich Andrews' machine, and now in Columbia, Missouri. We can see that the "security" situation of this proprietary document, once dug out of AIMSX, swiftly became bizarre. Without any money changing hands, without any particular special effort, this data had been reproduced at least six times and had spread itself all over the continent. By far the worst, however, was yet to come. In February 1989, Prophet and Knight Lightning bargained electronically over the fate of this trophy. Prophet wanted to boast, but, at the same time, scarcely wanted to be caught. For his part, Knight Lightning was eager to publish as much of the document as he could manage. Knight Lightning was a fledgling political-science major with a particular interest in freedom-of-information issues. He would gladly publish most anything that would reflect glory on the prowess of the underground and embarrass the telcos. However, Knight Lightning himself had contacts in telco security, and sometimes consulted them on material he'd received that might be too dicey for publication. Prophet and Knight Lightning decided to edit the E911 Document so as to delete most of its identifying traits. First of all, its large "NOT FOR USE OR DISCLOSURE" warning had to go. Then there were other matters. For instance, it listed the office telephone numbers of several BellSouth 911 specialists in Florida. If these phone numbers were published in Phrack, the BellSouth employees involved would very likely be hassled by phone phreaks, which would anger BellSouth no end, and pose a definite operational hazard for both Prophet and Phrack. So Knight Lightning cut the Document almost in half, removing the phone numbers and some of the touchier and more specific information. He passed it back electronically to Prophet; Prophet was still nervous, so Knight Lightning cut a bit more. They finally agreed that it was ready to go, and that it would be published in Phrack under the pseudonym, "The Eavesdropper." And this was done on February 25, 1989. The twenty-fourth issue of Phrack featured a chatty interview with co-ed phone-phreak "Chanda Leir," three articles on BITNET and its links to other computer networks, an article on 800 and 900 numbers by "Unknown User," "VaxCat's" article on telco basics (slyly entitled "Lifting Ma Bell's Veil of Secrecy,)" and the usual "Phrack World News." The News section, with painful irony, featured an extended account of the sentencing of "Shadowhawk," an eighteen-year-old Chicago hacker who had just been put in federal prison by William J. Cook himself. And then there were the two articles by "The Eavesdropper." The first was the edited E911 Document, now titled "Control Office Administration Of Enhanced 911 Services for Special Services and Major Account Centers." Eavesdropper's second article was a glossary of terms explaining the blizzard of telco acronyms and buzzwords in the E911 Document. The hapless document was now distributed, in the usual Phrack routine, to a good one hundred and fifty sites. Not a hundred and fifty people, mind you -- a hundred and fifty sites, some of these sites linked to UNIX nodes or bulletin board systems, which themselves had readerships of tens, dozens, even hundreds of people. This was February 1989. Nothing happened immediately. Summer came, and the Atlanta crew were raided by the Secret Service. Fry Guy was apprehended. Still nothing whatever happened to Phrack. Six more issues of Phrack came out, 30 in all, more or less on a monthly schedule. Knight Lightning and co-editor Taran King went untouched. Phrack tended to duck and cover whenever the heat came down. During the summer busts of 1987 -(hacker busts tended to cluster in summer, perhaps because hackers were easier to find at home than in college) -- Phrack had ceased publication for several months, and laid low. Several LoD hangers-on had been arrested, but nothing had happened to the Phrack crew, the premiere gossips of the underground. In 1988, Phrack had been taken over by a new editor, "Crimson Death," a raucous youngster with a taste for anarchy files. 1989, however, looked like a bounty year for the underground. Knight Lightning and his co-editor Taran King took up the reins again, and Phrack flourished throughout 1989. Atlanta LoD went down hard in the summer of 1989, but Phrack rolled merrily on. Prophet's E911 Document seemed unlikely to cause Phrack any trouble. By January 1990, it had been available in Phrack for almost a year. Kluepfel and Dalton, officers of Bellcore and AT&T security, had possessed the document for sixteen months -- in fact, they'd had it even before Knight Lightning himself, and had done nothing in particular to stop its distribution. They hadn't even told Rich Andrews or Charles Boykin to erase the copies from their UNIX nodes, Jolnet and Killer. But then came the monster Martin Luther King Day Crash of January 15, 1990. A flat three days later, on January 18, four agents showed up at Knight Lightning's fraternity house. One was Timothy Foley, the second Barbara Golden, both of them Secret Service agents from the Chicago office. Also along was a University of Missouri security officer, and Reed Newlin, a security man from Southwestern Bell, the RBOC having jurisdiction over Missouri. Foley accused Knight Lightning of causing the nationwide crash of the phone system. Knight Lightning was aghast at this allegation. On the face of it, the suspicion was not entirely implausible - though Knight Lightning knew that he himself hadn't done it. Plenty of hot-dog hackers had bragged that they could crash the phone system, however. "Shadowhawk," for instance, the Chicago hacker whom William Cook had recently put in jail, had several times boasted on boards that he could "shut down AT&T's public switched network." And now this event, or something that looked just like it, had actually taken place. The Crash had lit a fire under the Chicago Task Force. And the former fencesitters at Bellcore and AT&T were now ready to roll. The consensus among telco security -- already horrified by the skill of the BellSouth intruders -- was that the digital underground was out of hand. LoD and Phrack must go. And in publishing Prophet's E911 Document, Phrack had provided law enforcement with what appeared to be a powerful legal weapon. Foley confronted Knight Lightning about the E911 Document. Knight Lightning was cowed. He immediately began "cooperating fully" in the usual tradition of the digital underground. He gave Foley a complete run of Phrack,printed out in a set of three-ring binders. He handed over his electronic mailing list of Phrack subscribers. Knight Lightning was grilled for four hours by Foley and his cohorts. Knight Lightning admitted that Prophet had passed him the E911 Document, and he admitted that he had known it was stolen booty from a hacker raid on a telephone company. Knight Lightning signed a statement to this effect, and agreed, in writing, to cooperate with investigators. Next day -- January 19, 1990, a Friday -- the Secret Service returned with a search warrant, and thoroughly searched Knight Lightning's upstairs room in the fraternity house. They took all his floppy disks, though, interestingly, they left Knight Lightning in possession of both his computer and his modem. (The computer had no hard disk, and in Foley's judgement was not a store of evidence.) But this was a very minor bright spot among Knight Lightning's rapidly multiplying troubles. By this time, Knight Lightning was in plenty of hot water, not only with federal police, prosecutors, telco investigators, and university security, but with the elders of his own campus fraternity, who were outraged to think that they had been unwittingly harboring a federal computer-criminal. On Monday, Knight Lightning was summoned to Chicago, where he was further grilled by Foley and USSS veteran agent Barbara Golden, this time with an attorney present. And on Tuesday, he was formally indicted by a federal grand jury. The trial of Knight Lightning, which occurred on July 24-27, 1990, was the crucial show-trial of the Hacker Crackdown. We will examine the trial at some length in Part Four of this book. In the meantime, we must continue our dogged pursuit of the E911 Document. It must have been clear by January 1990 that the E911 Document, in the form Phrack had published it back in February 1989, had gone off at the speed of light in at least a hundred and fifty different directions. To attempt to put this electronic genie back in the bottle was flatly impossible. And yet, the E911 Document was still stolen property, formally and legally speaking. Any electronic transference of this document, by anyone unauthorized to have it, could be interpreted as an act of wire fraud. Interstate transfer of stolen property, including electronic property, was a federal crime. The Chicago Computer Fraud and Abuse Task Force had been assured that the E911 Document was worth a hefty sum of money. In fact, they had a precise estimate of its worth from BellSouth security personnel: $79,449. A sum of this scale seemed to warrant vigorous prosecution. Even if the damage could not be undone, at least this large sum offered a good legal pretext for stern punishment of the thieves. It seemed likely to impress judges and juries. And it could be used in court to mop up the Legion of Doom. The Atlanta crowd was already in the bag, by the time the Chicago Task Force had gotten around to Phrack. But the Legion was a hydra- headed thing. In late 89, a brand-new Legion of Doom board, "Phoenix Project," had gone up in Austin, Texas. Phoenix Project was sysoped by no less a man than the Mentor himself, ably assisted by University of Texas student and hardened Doomster "Erik Bloodaxe." As we have seen from his Phrack manifesto, the Mentor was a hacker zealot who regarded computer intrusion as something close to a moral duty. Phoenix Project was an ambitious effort, intended to revive the digital underground to what Mentor considered the full flower of the early 80s. The Phoenix board would also boldly bring elite hackers face-to-face with the telco "opposition." On "Phoenix," America's cleverest hackers would supposedly shame the telco squareheads out of their stick-in-the-mud attitudes, and perhaps convince them that the Legion of Doom elite were really an all-right crew. The premiere of "Phoenix Project" was heavily trumpeted by Phrack, and "Phoenix Project" carried a complete run of Phrack issues, including the E911 Document as Phrack had published it. Phoenix Project was only one of many -- possibly hundreds -- of nodes and boards all over America that were in guilty possession of the E911 Document. But Phoenix was an outright, unashamed Legion of Doom board. Under Mentor's guidance, it was flaunting itself in the face of telco security personnel. Worse yet, it was actively trying to win them over as sympathizers for the digital underground elite. "Phoenix" had no cards or codes on it. Its hacker elite considered Phoenix at least technically legal. But Phoenix was a corrupting influence, where hacker anarchy was eating away like digital acid at the underbelly of corporate propriety. The Chicago Computer Fraud and Abuse Task Force now prepared to descend upon Austin, Texas. Oddly, not one but two trails of the Task Force's investigation led toward Austin. The city of Austin, like Atlanta, had made itself a bulwark of the Sunbelt's Information Age, with a strong university research presence, and a number of cutting-edge electronics companies, including Motorola, Dell, CompuAdd, IBM, Sematech and MCC. Where computing machinery went, hackers generally followed. Austin boasted not only "Phoenix Project," currently LoD's most flagrant underground board, but a number of UNIX nodes. One of these nodes was "Elephant," run by a UNIX consultant named Robert Izenberg. Izenberg, in search of a relaxed Southern lifestyle and a lowered cost-of-living, had recently migrated to Austin from New Jersey. In New Jersey, Izenberg had worked for an independent contracting company, programming UNIX code for AT&T itself. "Terminus" had been a frequent user on Izenberg's privately owned Elephant node. Having interviewed Terminus and examined the records on Netsys, the Chicago Task Force were now convinced that they had discovered an underground gang of UNIX software pirates, who were demonstrably guilty of interstate trafficking in illicitly copied AT&T source code. Izenberg was swept into the dragnet around Terminus, the self-proclaimed ultimate UNIX hacker. Izenberg, in Austin, had settled down into a UNIX job with a Texan branch of IBM. Izenberg was no longer working as a contractor for AT&T, but he had friends in New Jersey, and he still logged on to AT&T UNIX computers back in New Jersey, more or less whenever it pleased him. Izenberg's activities appeared highly suspicious to the Task Force. Izenberg might well be breaking into AT&T computers, swiping AT&T software, and passing it to Terminus and other possible confederates, through the UNIX node network. And this data was worth, not merely $79,499, but hundreds of thousands of dollars! On February 21, 1990, Robert Izenberg arrived home from work at IBM to find that all the computers had mysteriously vanished from his Austin apartment. Naturally he assumed that he had been robbed. His "Elephant" node, his other machines, his notebooks, his disks, his tapes, all gone! However, nothing much else seemed disturbed -- the place had not been ransacked. The puzzle becaming much stranger some five minutes later. Austin U. S. Secret Service Agent Al Soliz, accompanied by University of Texas campus-security officer Larry Coutorie and the ubiquitous Tim Foley, made their appearance at Izenberg's door. They were in plain clothes: slacks, polo shirts. They came in, and Tim Foley accused Izenberg of belonging to the Legion of Doom. Izenberg told them that he had never heard of the "Legion of Doom." And what about a certain stolen E911 Document, that posed a direct threat to the police emergency lines? Izenberg claimed that he'd never heard of that, either. His interrogators found this difficult to believe. Didn't he know Terminus? Who? They gave him Terminus's real name. Oh yes, said Izenberg. He knew that guy all right -- he was leading discussions on the Internet about AT&T computers, especially the AT&T 3B2. AT&T had thrust this machine into the marketplace, but, like many of AT&T's ambitious attempts to enter the computing arena, the 3B2 project had something less than a glittering success. Izenberg himself had been a contractor for the division of AT&T that supported the 3B2. The entire division had been shut down. Nowadays, the cheapest and quickest way to get help with this fractious piece of machinery was to join one of Terminus's discussion groups on the Internet, where friendly and knowledgeable hackers would help you for free. Naturally the remarks within this group were less than flattering about the Death Star.... was that the problem? Foley told Izenberg that Terminus had been acquiring hot software through his, Izenberg's, machine. Izenberg shrugged this off. A good eight megabytes of data flowed through his UUCP site every day. UUCP nodes spewed data like fire hoses. Elephant had been directly linked to Netsys -- not surprising, since Terminus was a 3B2 expert and Izenberg had been a 3B2 contractor. Izenberg was also linked to "attctc" and the University of Texas. Terminus was a well-known UNIX expert, and might have been up to all manner of hijinks on Elephant. Nothing Izenberg could do about that. That was physically impossible. Needle in a haystack. In a four-hour grilling, Foley urged Izenberg to come clean and admit that he was in conspiracy with Terminus, and a member of the Legion of Doom. Izenberg denied this. He was no weirdo teenage hacker - - he was thirty-two years old, and didn't even have a "handle." Izenberg was a former TV technician and electronics specialist who had drifted into UNIX consulting as a full-grown adult. Izenberg had never met Terminus, physically. He'd once bought a cheap highspeed modem from him, though. Foley told him that this modem (a Telenet T2500 which ran at 19.2 kilobaud, and which had just gone out Izenberg's door in Secret Service custody) was likely hot property. Izenberg was taken aback to hear this; but then again, most of Izenberg's equipment, like that of most freelance professionals in the industry, was discounted, passed hand-to-hand through various kinds of barter and gray-market. There was no proof that the modem was stolen, and even if it was, Izenberg hardly saw how that gave them the right to take every electronic item in his house. Still, if the United States Secret Service figured they needed his computer for national security reasons -- or whatever -- then Izenberg would not kick. He figured he would somehow make the sacrifice of his twenty thousand dollars' worth of professional equipment, in the spirit of full cooperation and good citizenship. Robert Izenberg was not arrested. Izenberg was not charged with any crime. His UUCP node -- full of some 140 megabytes of the files, mail, and data of himself and his dozen or so entirely innocent users -- went out the door as "evidence." Along with the disks and tapes, Izenberg had lost about 800 megabytes of data. Six months would pass before Izenberg decided to phone the Secret Service and ask how the case was going. That was the first time that Robert Izenberg would ever hear the name of William Cook. As of January 1992, a full two years after the seizure, Izenberg, still not charged with any crime, would be struggling through the morass of the courts, in hope of recovering his thousands of dollars' worth of seized equipment. In the meantime, the Izenberg case received absolutely no press coverage. The Secret Service had walked into an Austin home, removed a UNIX bulletinboard system, and met with no operational difficulties whatsoever. Except that word of a crackdown had percolated through the Legion of Doom. "The Mentor" voluntarily shut down "The Phoenix Project." It seemed a pity, especially as telco security employees had, in fact, shown up on Phoenix, just as he had hoped -- along with the usual motley crowd of LoD heavies, hangers-on, phreaks, hackers and wannabes. There was "Sandy" Sandquist from US SPRINT security, and some guy named Henry Kluepfel, from Bellcore itself! Kluepfel had been trading friendly banter with hackers on Phoenix since January 30th (two weeks after the Martin Luther King Day Crash). The presence of such a stellar telco official seemed quite the coup for Phoenix Project. Still, Mentor could judge the climate. Atlanta in ruins, Phrack in deep trouble, something weird going on with UNIX nodes -- discretion was advisable. Phoenix Project went off-line. Kluepfel, of course, had been monitoring this LoD bulletin board for his own purposes -- and those of the Chicago unit. As far back as June 1987, Kluepfel had logged on to a Texas underground board called "Phreak Klass 2600." There he'd discovered an Chicago youngster named "Shadowhawk," strutting and boasting about rifling AT&T computer files, and bragging of his ambitions to riddle AT&T's Bellcore computers with trojan horse programs. Kluepfel had passed the news to Cook in Chicago, Shadowhawk's computers had gone out the door in Secret Service custody, and Shadowhawk himself had gone to jail. Now it was Phoenix Project's turn. Phoenix Project postured about "legality" and "merely intellectual interest," but it reeked of the underground. It had Phrack on it. It had the E911 Document. It had a lot of dicey talk about breaking into systems, including some bold and reckless stuff about a supposed "decryption service" that Mentor and friends were planning to run, to help crack encrypted passwords off of hacked systems. Mentor was an adult. There was a bulletin board at his place of work, as well. Kleupfel logged onto this board, too, and discovered it to be called "Illuminati." It was run by some company called Steve Jackson Games. On March 1, 1990, the Austin crackdown went into high gear. On the morning of March 1 -- a Thursday -- 21-yearold University of Texas student "Erik Bloodaxe," co-sysop of Phoenix Project and an avowed member of the Legion of Doom, was wakened by a police revolver levelled at his head. Bloodaxe watched, jittery, as Secret Service agents appropriated his 300 baud terminal and, rifling his files, discovered his treasured source-code for Robert Morris's notorious Internet Worm. But Bloodaxe, a wily operator, had suspected that something of the like might be coming. All his best equipment had been hidden away elsewhere. The raiders took everything electronic, however, including his telephone. They were stymied by his hefty arcade-style Pac-Man game, and left it in place, as it was simply too heavy to move. Bloodaxe was not arrested. He was not charged with any crime. A good two years later, the police still had what they had taken from him, however. The Mentor was less wary. The dawn raid rousted him and his wife from bed in their underwear, and six Secret Service agents, accompanied by an Austin policeman and Henry Kluepfel himself, made a rich haul. Off went the works, into the agents' white Chevrolet minivan: an IBM PC-AT clone with 4 meg of RAM and a 120-meg hard disk; a Hewlett-Packard LaserJet II printer; a completely legitimate and highly expensive SCO-Xenix 286 operating system; Pagemaker disks and documentation; and the Microsoft Word word-processing program. Mentor's wife had her incomplete academic thesis stored on the hard-disk; that went, too, and so did the couple's telephone. As of two years later, all this property remained in police custody. Mentor remained under guard in his apartment as agents prepared to raid Steve Jackson Games. The fact that this was a business headquarters and not a private residence did not deter the agents. It was still very early; no one was at work yet. The agents prepared to break down the door, but Mentor, eavesdropping on the Secret Service walkie- talkie traffic, begged them not to do it, and offered his key to the building. The exact details of the next events are unclear. The agents would not let anyone else into the building. Their search warrant, when produced, was unsigned. Apparently they breakfasted from the local "Whataburger," as the litter from hamburgers was later found inside. They also extensively sampled a bag of jellybeans kept by an SJG employee. Someone tore a "Dukakis for President" sticker from the wall. SJG employees, diligently showing up for the day's work, were met at the door and briefly questioned by U.S. Secret Service agents. The employees watched in astonishment as agents wielding crowbars and screwdrivers emerged with captive machines. They attacked outdoor storage units with boltcutters. The agents wore blue nylon windbreakers with "SECRET SERVICE" stencilled across the back, with running-shoes and jeans. Jackson's company lost three computers, several hard-disks, hundred of floppy disks, two monitors, three modems, a laser printer, various powercords, cables, and adapters (and, oddly, a small bag of screws, bolts and nuts). The seizure of Illuminati BBS deprived SJG of all the programs, text files, and private e-mail on the board. The loss of two other SJG computers was a severe blow as well, since it caused the loss of electronically stored contracts, financial projections, address directories, mailing lists, personnel files, business correspondence, and, not least, the drafts of forthcoming games and gaming books. No one at Steve Jackson Games was arrested. No one was accused of any crime. No charges were filed. Everything appropriated was officially kept as "evidence" of crimes never specified. After the Phrack show-trial, the Steve Jackson Games scandal was the most bizarre and aggravating incident of the Hacker Crackdown of 1990. This raid by the Chicago Task Force on a science-fiction gaming publisher was to rouse a swarming host of civil liberties issues, and gave rise to an enduring controversy that was still re-complicating itself, and growing in the scope of its implications, a full two years later. The pursuit of the E911 Document stopped with the Steve Jackson Games raid. As we have seen, there were hundreds, perhaps thousands of computer users in America with the E911 Document in their possession. Theoretically, Chicago had a perfect legal right to raid any of these people, and could have legally seized the machines of anybody who subscribed to Phrack. However, there was no copy of the E911 Document on Jackson's Illuminati board. And there the Chicago raiders stopped dead; they have not raided anyone since. It might be assumed that Rich Andrews and Charlie Boykin, who had brought the E911 Document to the attention of telco security, might be spared any official suspicion. But as we have seen, the willingness to "cooperate fully" offers little, if any, assurance against federal anti-hacker prosecution. Richard Andrews found himself in deep trouble, thanks to the E911 Document. Andrews lived in Illinois, the native stomping grounds of the Chicago Task Force. On February 3 and 6, both his home and his place of work were raided by USSS. His machines went out the door, too, and he was grilled at length (though not arrested). Andrews proved to be in purportedly guilty possession of: UNIX SVR 3.2; UNIX SVR 3.1; UUCP; PMON; WWB; IWB; DWB; NROFF; KORN SHELL '88; C++; and QUEST, among other items. Andrews had received this proprietary code -- which AT&T officially valued at well over $250,000 -- through the UNIX network, much of it supplied to him as a personal favor by Terminus. Perhaps worse yet, Andrews admitted to returning the favor, by passing Terminus a copy of AT&T proprietary STARLAN source code. Even Charles Boykin, himself an AT&T employee, entered some very hot water. By 1990, he'd almost forgotten about the E911 problem he'd reported in September 88; in fact, since that date, he'd passed two more security alerts to Jerry Dalton, concerning matters that Boykin considered far worse than the E911 Document. But by 1990, year of the crackdown, AT&T Corporate Information Security was fed up with "Killer." This machine offered no direct income to AT&T, and was providing aid and comfort to a cloud of suspicious yokels from outside the company, some of them actively malicious toward AT&T, its property, and its corporate interests. Whatever goodwill and publicity had been won among Killer's 1,500 devoted users was considered no longer worth the security risk. On February 20, 1990, Jerry Dalton arrived in Dallas and simply unplugged the phone jacks, to the puzzled alarm of Killer's many Texan users. Killer went permanently off-line, with the loss of vast archives of programs and huge quantities of electronic mail; it was never restored to service. AT&T showed no particular regard for the "property" of these 1,500 people. Whatever "property" the users had been storing on AT&T's computer simply vanished completely. Boykin, who had himself reported the E911 problem, now found himself under a cloud of suspicion. In a weird private-security replay of the Secret Service seizures, Boykin's own home was visited by AT&T Security and his own machines were carried out the door. However, there were marked special features in the Boykin case. Boykin's disks and his personal computers were swiftly examined by his corporate employers and returned politely in just two days -- (unlike Secret Service seizures, which commonly take months or years). Boykin was not charged with any crime or wrongdoing, and he kept his job with AT&T (though he did retire from AT&T in September 1991, at the age of 52). It's interesting to note that the US Secret Service somehow failed to seize Boykin's "Killer" node and carry AT&T's own computer out the door. Nor did they raid Boykin's home. They seemed perfectly willing to take the word of AT&T Security that AT&T's employee, and AT&T's "Killer" node, were free of hacker contraband and on the up-and-up. It's digital water-under-the-bridge at this point, as Killer's 3,200 megabytes of Texan electronic community were erased in 1990, and "Killer" itself was shipped out of the state. But the experiences of Andrews and Boykin, and the users of their systems, remained side issues. They did not begin to assume the social, political, and legal importance that gathered, slowly but inexorably, around the issue of the raid on Steve Jackson Games. 8. === We must now turn our attention to Steve Jackson Games itself, and explain what SJG was, what it really did, and how it had managed to attract this particularly odd and virulent kind of trouble. The reader may recall that this is not the first but the second time that the company has appeared in this narrative; a Steve Jackson game called GURPS was a favorite pastime of Atlanta hacker Urvile, and Urvile's science-fictional gaming notes had been mixed up promiscuously with notes about his actual computer intrusions. First, Steve Jackson Games, Inc., was not a publisher of "computer games." SJG published "simulation games," parlor games that were played on paper, with pencils, and dice, and printed guidebooks full of rules and statistics tables. There were no computers involved in the games themselves. When you bought a Steve Jackson Game, you did not receive any software disks. What you got was a plastic bag with some cardboard game tokens, maybe a few maps or a deck of cards. Most of their products were books. However, computers were deeply involved in the Steve Jackson Games business. Like almost all modern publishers, Steve Jackson and his fifteen employees used computers to write text, to keep accounts, and to run the business generally. They also used a computer to run their official bulletin board system for Steve Jackson Games, a board called Illuminati. On Illuminati, simulation gamers who happened to own computers and modems could associate, trade mail, debate the theory and practice of gaming, and keep up with the company's news and its product announcements. Illuminati was a modestly popular board, run on a small computer with limited storage, only one phone-line, and no ties to large-scale computer networks. It did, however, have hundreds of users, many of them dedicated gamers willing to call from out-of-state. Illuminati was not an "underground" board. It did not feature hints on computer intrusion, or "anarchy files," or illicitly posted credit card numbers, or long-distance access codes. Some of Illuminati's users, however, were members of the Legion of Doom. And so was one of Steve Jackson's senior employees -- the Mentor. The Mentor wrote for Phrack, and also ran an underground board, Phoenix Project -- but the Mentor was not a computer professional. The Mentor was the managing editor of Steve Jackson Games and a professional game designer by trade. These LoD members did not use Illuminati to help their hacking activities. They used it to help their game-playing activities -- and they were even more dedicated to simulation gaming than they were to hacking. "Illuminati" got its name from a card-game that Steve Jackson himself, the company's founder and sole owner, had invented. This multi- player card-game was one of Mr Jackson's best-known, most successful, most technically innovative products. "Illuminati" was a game of paranoiac conspiracy in which various antisocial cults warred covertly to dominate the world. "Illuminati" was hilarious, and great fun to play, involving flying saucers, the CIA, the KGB, the phone companies, the Ku Klux Klan, the South American Nazis, the cocaine cartels, the Boy Scouts, and dozens of other splinter groups from the twisted depths of Mr. Jackson's professionally fervid imagination. For the uninitiated, any public discussion of the "Illuminati" card-game sounded, by turns, utterly menacing or completely insane. And then there was SJG's "Car Wars," in which souped-up armored hot-rods with rocket-launchers and heavy machine-guns did battle on the American highways of the future. The lively Car Wars discussion on the Illuminati board featured many meticulous, painstaking discussions of the effects of grenades, land-mines, flamethrowers and napalm. It sounded like hacker anarchy files run amuck. Mr Jackson and his co-workers earned their daily bread by supplying people with make-believe adventures and weird ideas. The more far-out, the better. Simulation gaming is an unusual pastime, but gamers have not generally had to beg the permission of the Secret Service to exist. Wargames and role-playing adventures are an old and honored pastime, much favored by professional military strategists. Once littleknown, these games are now played by hundreds of thousands of enthusiasts throughout North America, Europe and Japan. Gaming-books, once restricted to hobby outlets, now commonly appear in chain-stores like B. Dalton's and Waldenbooks, and sell vigorously. Steve Jackson Games, Inc., of Austin, Texas, was a games company of the middle rank. In 1989, SJG grossed about a million dollars. Jackson himself had a good reputation in his industry as a talented and innovative designer of rather unconventional games, but his company was something less than a titan of the field -certainly not like the multimillion-dollar TSR Inc., or Britain's gigantic "Games Workshop." SJG's Austin headquarters was a modest two-story brick office- suite, cluttered with phones, photocopiers, fax machines and computers. It bustled with semi-organized activity and was littered with glossy promotional brochures and dog-eared science-fiction novels. Attached to the offices was a large tin-roofed warehouse piled twenty feet high with cardboard boxes of games and books. Despite the weird imaginings that went on within it, the SJG headquarters was quite a quotidian, everyday sort of place. It looked like what it was: a publishers' digs. Both "Car Wars" and "Illuminati" were well-known, popular games. But the mainstay of the Jackson organization was their Generic Universal Role- Playing System, "G.U.R.P.S." The GURPS system was considered solid and well-designed, an asset for players. But perhaps the most popular feature of the GURPS system was that it allowed gaming-masters to design scenarios that closely resembled well-known books, movies, and other works of fantasy. Jackson had licensed and adapted works from many science fiction and fantasy authors. There was *GURPS Conan*, *GURPS Riverworld*, *GURPS Horseclans*, *GURPS Witch World*, names eminently familiar to science-fiction readers. And there was *GURPS Special Ops*, from the world of espionage fantasy and unconventional warfare. And then there was *GURPS Cyberpunk*. "Cyberpunk" was a term given to certain science fiction writers who had entered the genre in the 1980s. "Cyberpunk," as the label implies, had two general distinguishing features. First, its writers had a compelling interest in information technology, an interest closely akin to science fiction's earlier fascination with space travel. And second, these writers were "punks," with all the distinguishing features that that implies: Bohemian artiness, youth run wild, an air of deliberate rebellion, funny clothes and hair, odd politics, a fondness for abrasive rock and roll; in a word, trouble. The "cyberpunk" SF writers were a small group of mostly college- educated white middle-class litterateurs, scattered through the US and Canada. Only one, Rudy Rucker, a professor of computer science in Silicon Valley, could rank with even the humblest computer hacker. But, except for Professor Rucker, the "cyberpunk" authors were not programmers or hardware experts; they considered themselves artists (as, indeed, did Professor Rucker). However, these writers all owned computers, and took an intense and public interest in the social ramifications of the information industry. The cyberpunks had a strong following among the global generation that had grown up in a world of computers, multinational networks, and cable television. Their outlook was considered somewhat morbid, cynical, and dark, but then again, so was the outlook of their generational peers. As that generation matured and increased in strength and influence, so did the cyberpunks. As science-fiction writers went, they were doing fairly well for themselves. By the late 1980s, their work had attracted attention from gaming companies, including Steve Jackson Games, which was planning a cyberpunk simulation for the flourishing GURPS gamingsystem. The time seemed ripe for such a product, which had already been proven in the marketplace. The first gamescompany out of the gate, with a product boldly called "Cyberpunk" in defiance of possible infringement- ofcopyright suits, had been an upstart group called R. Talsorian. Talsorian's Cyberpunk was a fairly decent game, but the mechanics of the simulation system left a lot to be desired. Commercially, however, the game did very well. The next cyberpunk game had been the even more successful *Shadowrun* by FASA Corporation. The mechanics of this game were fine, but the scenario was rendered moronic by sappy fantasy elements like elves, trolls, wizards, and dragons -- all highly ideologically- incorrect, according to the hard-edged, high-tech standards of cyberpunk science fiction. Other game designers were champing at the bit. Prominent among them was the Mentor, a gentleman who, like most of his friends in the Legion of Doom, was quite the cyberpunk devotee. Mentor reasoned that the time had come for a real cyberpunk gaming-book -- one that the princes of computer-mischief in the Legion of Doom could play without laughing themselves sick. This book, *GURPS Cyberpunk*, would reek of culturally online authenticity. Mentor was particularly well-qualified for this task. Naturally, he knew far more about computer-intrusion and digital skullduggery than any previously published cyberpunk author. Not only that, but he was good at his work. A vivid imagination, combined with an instinctive feeling for the working of systems and, especially, the loopholes within them, are excellent qualities for a professional game designer. By March 1st, *GURPS Cyberpunk* was almost complete, ready to print and ship. Steve Jackson expected vigorous sales for this item, which, he hoped, would keep the company financially afloat for several months. *GURPS Cyberpunk*, like the other GURPS "modules," was not a "game" like a Monopoly set, but a book: a bound paperback book the size of a glossy magazine, with a slick color cover, and pages full of text, illustrations, tables and footnotes. It was advertised as a game, and was used as an aid to game-playing, but it was a book, with an ISBN number, published in Texas, copyrighted, and sold in bookstores. And now, that book, stored on a computer, had gone out the door in the custody of the Secret Service. The day after the raid, Steve Jackson visited the local Secret Service headquarters with a lawyer in tow. There he confronted Tim Foley (still in Austin at that time) and demanded his book back. But there was trouble. *GURPS Cyberpunk*, alleged a Secret Service agent to astonished businessman Steve Jackson, was "a manual for computer crime." "It's science fiction," Jackson said. "No, this is real." This statement was repeated several times, by several agents. Jackson's ominously accurate game had passed from pure, obscure, smallscale fantasy into the impure, highly publicized, largescale fantasy of the Hacker Crackdown. No mention was made of the real reason for the search. According to their search warrant, the raiders had expected to find the E911 Document stored on Jackson's bulletin board system. But that warrant was sealed; a procedure that most law enforcement agencies will use only when lives are demonstrably in danger. The raiders' true motives were not discovered until the Jackson searchwarrant was unsealed by his lawyers, many months later. The Secret Service, and the Chicago Computer Fraud and Abuse Task Force, said absolutely nothing to Steve Jackson about any threat to the police 911 System. They said nothing about the Atlanta Three, nothing about Phrack or Knight Lightning, nothing about Terminus. Jackson was left to believe that his computers had been seized because he intended to publish a science fiction book that law enforcement considered too dangerous to see print. This misconception was repeated again and again, for months, to an ever-widening public audience. It was not the truth of the case; but as months passed, and this misconception was publicly printed again and again, it became one of the few publicly known "facts" about the mysterious Hacker Crackdown. The Secret Service had seized a computer to stop the publication of a cyberpunk science fiction book. The second section of this book, "The Digital Underground," is almost finished now. We have become acquainted with all the major figures of this case who actually belong to the underground milieu of computer intrusion. We have some idea of their history, their motives, their general modus operandi. We now know, I hope, who they are, where they came from, and more or less what they want. In the next section of this book, "Law and Order," we will leave this milieu and directly enter the world of America's computer-crime police. At this point, however, I have another figure to introduce: myself. My name is Bruce Sterling. I live in Austin, Texas, where I am a science fiction writer by trade: specifically, a cyberpunk science fiction writer. Like my "cyberpunk" colleagues in the U.S. and Canada, I've never been entirely happy with this literary label -- especially after it became a synonym for computer criminal. But I did once edit a book of stories by my colleagues, called *MIRRORSHADES: the Cyberpunk Anthology*, and I've long been a writer of literarycritical cyberpunk manifestos. I am not a "hacker" of any description, though I do have readers in the digital underground. When the Steve Jackson Games seizure occurred, I naturally took an intense interest. If "cyberpunk" books were being banned by federal police in my own home town, I reasonably wondered whether I myself might be next. Would my computer be seized by the Secret Service? At the time, I was in possession of an aging Apple IIe without so much as a hard disk. If I were to be raided as an author of computer-crime manuals, the loss of my feeble word-processor would likely provoke more snickers than sympathy. I'd known Steve Jackson for many years. We knew one another as colleagues, for we frequented the same local science-fiction conventions. I'd played Jackson games, and recognized his cleverness; but he certainly had never struck me as a potential mastermind of computer crime. I also knew a little about computer bulletin-board systems. In the mid-1980s I had taken an active role in an Austin board called "SMOF- BBS," one of the first boards dedicated to science fiction. I had a modem, and on occasion I'd logged on to Illuminati, which always looked entertainly wacky, but certainly harmless enough. At the time of the Jackson seizure, I had no experience whatsoever with underground boards. But I knew that no one on Illuminati talked about breaking into systems illegally, or about robbing phone companies. Illuminati didn't even offer pirated computer games. Steve Jackson, like many creative artists, was markedly touchy about theft of intellectual property. It seemed to me that Jackson was either seriously suspected of some crime -- in which case, he would be charged soon, and would have his day in court -- or else he was innocent, in which case the Secret Service would quickly return his equipment, and everyone would have a good laugh. I rather expected the good laugh. The situation was not without its comic side. The raid, known as the "Cyberpunk Bust" in the science fiction community, was winning a great deal of free national publicity both for Jackson himself and the "cyberpunk" science fiction writers generally. Besides, science fiction people are used to being misinterpreted. Science fiction is a colorful, disreputable, slipshod occupation, full of unlikely oddballs, which, of course, is why we like it. Weirdness can be an occupational hazard in our field. People who wear Halloween costumes are sometimes mistaken for monsters. Once upon a time -- back in 1939, in New York City -science fiction and the U.S. Secret Service collided in a comic case of mistaken identity. This weird incident involved a literary group quite famous in science fiction, known as "the Futurians," whose membership included such future genre greats as Isaac Asimov, Frederik Pohl, and Damon Knight. The Futurians were every bit as offbeat and wacky as any of their spiritual descendants, including the cyberpunks, and were given to communal living, spontaneous group renditions of light opera, and midnight fencing exhibitions on the lawn. The Futurians didn't have bulletin board systems, but they did have the technological equivalent in 1939 -- mimeographs and a private printing press. These were in steady use, producing a stream of science-fiction fan magazines, literary manifestos, and weird articles, which were picked up in ink-sticky bundles by a succession of strange, gangly, spotty young men in fedoras and overcoats. The neighbors grew alarmed at the antics of the Futurians and reported them to the Secret Service as suspected counterfeiters. In the winter of 1939, a squad of USSS agents with drawn guns burst into "Futurian House," prepared to confiscate the forged currency and illicit printing presses. There they discovered a slumbering science fiction fan named George Hahn, a guest of the Futurian commune who had just arrived in New York. George Hahn managed to explain himself and his group, and the Secret Service agents left the Futurians in peace henceforth. (Alas, Hahn died in 1991, just before I had discovered this astonishing historical parallel, and just before I could interview him for this book.) But the Jackson case did not come to a swift and comic end. No quick answers came his way, or mine; no swift reassurances that all was right in the digital world, that matters were well in hand after all. Quite the opposite. In my alternate role as a sometime pop-science journalist, I interviewed Jackson and his staff for an article in a British magazine. The strange details of the raid left me more concerned than ever. Without its computers, the company had been financially and operationally crippled. Half the SJG workforce, a group of entirely innocent people, had been sorrowfully fired, deprived of their livelihoods by the seizure. It began to dawn on me that authors -- American writers -- might well have their computers seized, under sealed warrants, without any criminal charge; and that, as Steve Jackson had discovered, there was no immediate recourse for this. This was no joke; this wasn't science fiction; this was real. I determined to put science fiction aside until I had discovered what had happened and where this trouble had come from. It was time to enter the purportedly real world of electronic free expression and computer crime. Hence, this book. Hence, the world of the telcos; and the world of the digital underground; and next, the world of the police. ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_part1.txt0000600000175000017500000024307410246051200031235 0ustar madduckmadduckrestindex crumb: Hacker Crackdown /restindex ========================================== The Hacker Crackdown - Part One ========================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html .. contents :: Part One - In Parts Part One: Crashing the System ================================ On January 15, 1990, AT&T's long-distance telephone switching system crashed. This was a strange, dire, huge event. Sixty thousand people lost their telephone service completely. During the nine long hours of frantic effort that it took to restore service, some seventy million telephone calls went uncompleted. Losses of service, known as "outages" in the telco trade, are a known and accepted hazard of the telephone business. Hurricanes hit, and phone cables get snapped by the thousands. Earthquakes wrench through buried fiber-optic lines. Switching stations catch fire and burn to the ground. These things do happen. There are contingency plans for them, and decades of experience in dealing with them. But the Crash of January 15 was unprecedented. It was unbelievably huge, and it occurred for no apparent physical reason. The crash started on a Monday afternoon in a single switching- station in Manhattan. But, unlike any merely physical damage, it spread and spread. Station after station across America collapsed in a chain reaction, until fully half of AT&T's network had gone haywire and the remaining half was hard-put to handle the overflow. Within nine hours, AT&T software engineers more or less understood what had caused the crash. Replicating the problem exactly, poring over software line by line, took them a couple of weeks. But because it was hard to understand technically, the full truth of the matter and its implications were not widely and thoroughly aired and explained. The root cause of the crash remained obscure, surrounded by rumor and fear. The crash was a grave corporate embarrassment. The "culprit" was a bug in AT&T's own software -- not the sort of admission the telecommunications giant wanted to make, especially in the face of increasing competition. Still, the truth *was* told, in the baffling technical terms necessary to explain it. Somehow the explanation failed to persuade American law enforcement officials and even telephone corporate security personnel. These people were not technical experts or software wizards, and they had their own suspicions about the cause of this disaster. The police and telco security had important sources of information denied to mere software engineers. They had informants in the computer underground and years of experience in dealing with high-tech rascality that seemed to grow ever more sophisticated. For years they had been expecting a direct and savage attack against the American national telephone system. And with the Crash of January 15 -- the first month of a new, high-tech decade -- their predictions, fears, and suspicions seemed at last to have entered the real world. A world where the telephone system had not merely crashed, but, quite likely, *been* crashed -- by "hackers." The crash created a large dark cloud of suspicion that would color certain people's assumptions and actions for months. The fact that it took place in the realm of software was suspicious on its face. The fact that it occurred on Martin Luther King Day, still the most politically touchy of American holidays, made it more suspicious yet. The Crash of January 15 gave the Hacker Crackdown its sense of edge and its sweaty urgency. It made people, powerful people in positions of public authority, willing to believe the worst. And, most fatally, it helped to give investigators a willingness to take extreme measures and the determination to preserve almost total secrecy. An obscure software fault in an aging switching system in New York was to lead to a chain reaction of legal and constitutional trouble all across the country. Like the crash in the telephone system, this chain reaction was ready and waiting to happen. During the 1980s, the American legal system was extensively patched to deal with the novel issues of computer crime. There was, for instance, the Electronic Communications Privacy Act of 1986 (eloquently described as "a stinking mess" by a prominent law enforcement official). And there was the draconian Computer Fraud and Abuse Act of 1986, passed unanimously by the United States Senate, which later would reveal a large number of flaws. Extensive, wellmeant efforts had been made to keep the legal system up to date. But in the day- to-day grind of the real world, even the most elegant software tends to crumble and suddenly reveal its hidden bugs. Like the advancing telephone system, the American legal system was certainly not ruined by its temporary crash; but for those caught under the weight of the collapsing system, life became a series of blackouts and anomalies. In order to understand why these weird events occurred, both in the world of technology and in the world of law, it's not enough to understand the merely technical problems. We will get to those; but first and foremost, we must try to understand the telephone, and the business of telephones, and the community of human beings that telephones have created. 1. === Technologies have life cycles, like cities do, like institutions do, like laws and governments do. The first stage of any technology is the Question Mark, often known as the "Golden Vaporware" stage. At this early point, the technology is only a phantom, a mere gleam in the inventor's eye. One such inventor was a speech teacher and electrical tinkerer named Alexander Graham Bell. Bell's early inventions, while ingenious, failed to move the world. In 1863, the teenage Bell and his brother Melville made an artificial talking mechanism out of wood, rubber, gutta-percha, and tin. This weird device had a rubber-covered "tongue" made of movable wooden segments, with vibrating rubber "vocal cords," and rubber "lips" and "cheeks." While Melville puffed a bellows into a tin tube, imitating the lungs, young Alec Bell would manipulate the "lips," "teeth," and "tongue," causing the thing to emit high-pitched falsetto gibberish. Another would-be technical breakthrough was the Bell "phonautograph" of 1874, actually made out of a human cadaver's ear. Clamped into place on a tripod, this grisly gadget drew sound-wave images on smoked glass through a thin straw glued to its vibrating earbones. By 1875, Bell had learned to produce audible sounds - ugly shrieks and squawks -- by using magnets, diaphragms, and electrical current. Most "Golden Vaporware" technologies go nowhere. But the second stage of technology is the Rising Star, or, the "Goofy Prototype," stage. The telephone, Bell's most ambitious gadget yet, reached this stage on March 10, 1876. On that great day, Alexander Graham Bell became the first person to transmit intelligible human speech electrically. As it happened, young Professor Bell, industriously tinkering in his Boston lab, had spattered his trousers with acid. His assistant, Mr. Watson, heard his cry for help -- over Bell's experimental audiotelegraph. This was an event without precedent. Technologies in their "Goofy Prototype" stage rarely work very well. They're experimental, and therefore halfbaked and rather frazzled. The prototype may be attractive and novel, and it does look as if it ought to be good for something-or-other. But nobody, including the inventor, is quite sure what. Inventors, and speculators, and pundits may have very firm ideas about its potential use, but those ideas are often very wrong. The natural habitat of the Goofy Prototype is in trade shows and in the popular press. Infant technologies need publicity and investment money like a tottering calf need milk. This was very true of Bell's machine. To raise research and development money, Bell toured with his device as a stage attraction. Contemporary press reports of the stage debut of the telephone showed pleased astonishment mixed with considerable dread. Bell's stage telephone was a large wooden box with a crude speaker-nozzle, the whole contraption about the size and shape of an overgrown Brownie camera. Its buzzing steel soundplate, pumped up by powerful electromagnets, was loud enough to fill an auditorium. Bell's assistant Mr. Watson, who could manage on the keyboards fairly well, kicked in by playing the organ from distant rooms, and, later, distant cities. This feat was considered marvellous, but very eerie indeed. Bell's original notion for the telephone, an idea promoted for a couple of years, was that it would become a mass medium. We might recognize Bell's idea today as something close to modern "cable radio." Telephones at a central source would transmit music, Sunday sermons, and important public speeches to a paying network of wired-up subscribers. At the time, most people thought this notion made good sense. In fact, Bell's idea was workable. In Hungary, this philosophy of the telephone was successfully put into everyday practice. In Budapest, for decades, from 1893 until after World War I, there was a government-run information service called "Telefon Hirmondo." Hirmondo was a centralized source of news and entertainment and culture, including stock reports, plays, concerts, and novels read aloud. At certain hours of the day, the phone would ring, you would plug in a loudspeaker for the use of the family, and Telefon Hirmondo would be on the air -- or rather, on the phone. Hirmondo is dead tech today, but Hirmondo might be considered a spiritual ancestor of the modern telephone-accessed computer data services, such as CompuServe, GEnie or Prodigy. The principle behind Hirmondo is also not too far from computer "bulletinboard systems" or BBS's, which arrived in the late 1970s, spread rapidly across America, and will figure largely in this book. We are used to using telephones for individual person-to-person speech, because we are used to the Bell system. But this was just one possibility among many. Communication networks are very flexible and protean, especially when their hardware becomes sufficiently advanced. They can be put to all kinds of uses. And they have been -- and they will be. Bell's telephone was bound for glory, but this was a combination of political decisions, canny infighting in court, inspired industrial leadership, receptive local conditions and outright good luck. Much the same is true of communications systems today. As Bell and his backers struggled to install their newfangled system in the real world of nineteenth-century New England, they had to fight against skepticism and industrial rivalry. There was already a strong electrical communications network present in America: the telegraph. The head of the Western Union telegraph system dismissed Bell's prototype as "an electrical toy" and refused to buy the rights to Bell's patent. The telephone, it seemed, might be all right as a parlor entertainment -- but not for serious business. Telegrams, unlike mere telephones, left a permanent physical record of their messages. Telegrams, unlike telephones, could be answered whenever the recipient had time and convenience. And the telegram had a much longer distance-range than Bell's early telephone. These factors made telegraphy seem a much more sound and businesslike technology -- at least to some. The telegraph system was huge, and well-entrenched. In 1876, the United States had 214,000 miles of telegraph wire, and 8500 telegraph offices. There were specialized telegraphs for businesses and stock traders, government, police and fire departments. And Bell's "toy" was best known as a stage-magic musical device. The third stage of technology is known as the "Cash Cow" stage. In the "cash cow" stage, a technology finds its place in the world, and matures, and becomes settled and productive. After a year or so, Alexander Graham Bell and his capitalist backers concluded that eerie music piped from nineteenth-century cyberspace was not the real selling- point of his invention. Instead, the telephone was about speech -- individual, personal speech, the human voice, human conversation and human interaction. The telephone was not to be managed from any centralized broadcast center. It was to be a personal, intimate technology. When you picked up a telephone, you were not absorbing the cold output of a machine -- you were speaking to another human being. Once people realized this, their instinctive dread of the telephone as an eerie, unnatural device, swiftly vanished. A "telephone call" was not a "call" from a "telephone" itself, but a call from another human being, someone you would generally know and recognize. The real point was not what the machine could do for you (or to you), but what you yourself, a person and citizen, could do *through* the machine. This decision on the part of the young Bell Company was absolutely vital. The first telephone networks went up around Boston - mostly among the technically curious and the well-to-do (much the same segment of the American populace that, a hundred years later, would be buying personal computers). Entrenched backers of the telegraph continued to scoff. But in January 1878, a disaster made the telephone famous. A train crashed in Tarriffville, Connecticut. Forward-looking doctors in the nearby city of Hartford had had Bell's "speaking telephone" installed. An alert local druggist was able to telephone an entire community of local doctors, who rushed to the site to give aid. The disaster, as disasters do, aroused intense press coverage. The phone had proven its usefulness in the real world. After Tarriffville, the telephone network spread like crabgrass. By 1890 it was all over New England. By '93, out to Chicago. By '97, into Minnesota, Nebraska and Texas. By 1904 it was all over the continent. The telephone had become a mature technology. Professor Bell (now generally known as "Dr. Bell" despite his lack of a formal degree) became quite wealthy. He lost interest in the tedious day-to-day business muddle of the booming telephone network, and gratefully returned his attention to creatively hacking-around in his various laboratories, which were now much larger, betterventilated, and gratifyingly better-equipped. Bell was never to have another great inventive success, though his speculations and prototypes anticipated fiber-optic transmission, manned flight, sonar, hydrofoil ships, tetrahedral construction, and Montessori education. The "decibel," the standard scientific measure of sound intensity, was named after Bell. Not all Bell's vaporware notions were inspired. He was fascinated by human eugenics. He also spent many years developing a weird personal system of astrophysics in which gravity did not exist. Bell was a definite eccentric. He was something of a hypochondriac, and throughout his life he habitually stayed up until four A.M., refusing to rise before noon. But Bell had accomplished a great feat; he was an idol of millions and his influence, wealth, and great personal charm, combined with his eccentricity, made him something of a loose cannon on deck. Bell maintained a thriving scientific salon in his winter mansion in Washington, D.C., which gave him considerable backstage influence in governmental and scientific circles. He was a major financial backer of the the magazines *Science* and *National Geographic*, both still flourishing today as important organs of the American scientific establishment. Bell's companion Thomas Watson, similarly wealthy and similarly odd, became the ardent political disciple of a 19th-century science-fiction writer and would-be social reformer, Edward Bellamy. Watson also trod the boards briefly as a Shakespearian actor. There would never be another Alexander Graham Bell, but in years to come there would be surprising numbers of people like him. Bell was a prototype of the high-tech entrepreneur. High-tech entrepreneurs will play a very prominent role in this book: not merely as technicians and businessmen, but as pioneers of the technical frontier, who can carry the power and prestige they derive from high-technology into the political and social arena. Like later entrepreneurs, Bell was fierce in defense of his own technological territory. As the telephone began to flourish, Bell was soon involved in violent lawsuits in the defense of his patents. Bell's Boston lawyers were excellent, however, and Bell himself, as an elecution teacher and gifted public speaker, was a devastatingly effective legal witness. In the eighteen years of Bell's patents, the Bell company was involved in six hundred separate lawsuits. The legal records printed filled 149 volumes. The Bell Company won every single suit. After Bell's exclusive patents expired, rival telephone companies sprang up all over America. Bell's company, American Bell Telephone, was soon in deep trouble. In 1907, American Bell Telephone fell into the hands of the rather sinister J.P. Morgan financial cartel, robber-baron speculators who dominated Wall Street. At this point, history might have taken a different turn. American might well have been served forever by a patchwork of locally owned telephone companies. Many state politicians and local businessmen considered this an excellent solution. But the new Bell holding company, American Telephone and Telegraph or AT&T, put in a new man at the helm, a visionary industrialist named Theodore Vail. Vail, a former Post Office manager, understood large organizations and had an innate feeling for the nature of large-scale communications. Vail quickly saw to it that AT&T seized the technological edge once again. The Pupin and Campbell "loading coil," and the deForest "audion," are both extinct technology today, but in 1913 they gave Vail's company the best *long-distance* lines ever built. By controlling long-distance -- the links between, and over, and above the smaller local phone companies -- AT&T swiftly gained the whip-hand over them, and was soon devouring them right and left. Vail plowed the profits back into research and development, starting the Bell tradition of huge-scale and brilliant industrial research. Technically and financially, AT&T gradually steamrollered the opposition. Independent telephone companies never became entirely extinct, and hundreds of them flourish today. But Vail's AT&T became the supreme communications company. At one point, Vail's AT&T bought Western Union itself, the very company that had derided Bell's telephone as a "toy." Vail thoroughly reformed Western Union's hidebound business along his modern principles; but when the federal government grew anxious at this centralization of power, Vail politely gave Western Union back. This centralizing process was not unique. Very similar events had happened in American steel, oil, and railroads. But AT&T, unlike the other companies, was to remain supreme. The monopoly robber-barons of those other industries were humbled and shattered by government trust- busting. Vail, the former Post Office official, was quite willing to accommodate the US government; in fact he would forge an active alliance with it. AT&T would become almost a wing of the American government, almost another Post Office -- though not quite. AT&T would willingly submit to federal regulation, but in return, it would use the government's regulators as its own police, who would keep out competitors and assure the Bell system's profits and preeminence. This was the second birth -- the political birth -- of the American telephone system. Vail's arrangement was to persist, with vast success, for many decades, until 1982. His system was an odd kind of American industrial socialism. It was born at about the same time as Leninist Communism, and it lasted almost as long -- and, it must be admitted, to considerably better effect. Vail's system worked. Except perhaps for aerospace, there has been no technology more thoroughly dominated by Americans than the telephone. The telephone was seen from the beginning as a quintessentially American technology. Bell's policy, and the policy of Theodore Vail, was a profoundly democratic policy of *universal access*. Vail's famous corporate slogan, "One Policy, One System, Universal Service," was a political slogan, with a very American ring to it. The American telephone was not to become the specialized tool of government or business, but a general public utility. At first, it was true, only the wealthy could afford private telephones, and Bell's company pursued the business markets primarily. The American phone system was a capitalist effort, meant to make money; it was not a charity. But from the first, almost all communities with telephone service had public telephones. And many stores -- especially drugstores -offered public use of their phones. You might not own a telephone -- but you could always get into the system, if you really needed to. There was nothing inevitable about this decision to make telephones "public" and "universal." Vail's system involved a profound act of trust in the public. This decision was a political one, informed by the basic values of the American republic. The situation might have been very different; and in other countries, under other systems, it certainly was. Joseph Stalin, for instance, vetoed plans for a Soviet phone system soon after the Bolshevik revolution. Stalin was certain that publicly accessible telephones would become instruments of anti-Soviet counterrevolution and conspiracy. (He was probably right.) When telephones did arrive in the Soviet Union, they would be instruments of Party authority, and always heavily tapped. (Alexander Solzhenitsyn's prison-camp novel *The First Circle* describes efforts to develop a phone system more suited to Stalinist purposes.) France, with its tradition of rational centralized government, had fought bitterly even against the electric telegraph, which seemed to the French entirely too anarchical and frivolous. For decades, nineteenth- century France communicated via the "visual telegraph," a nation- spanning, government-owned semaphore system of huge stone towers that signalled from hilltops, across vast distances, with big windmill-like arms. In 1846, one Dr. Barbay, a semaphore enthusiast, memorably uttered an early version of what might be called "the security expert's argument" against the open media. "No, the electric telegraph is not a sound invention. It will always be at the mercy of the slightest disruption, wild youths, drunkards, bums, etc.... The electric telegraph meets those destructive elements with only a few meters of wire over which supervision is impossible. A single man could, without being seen, cut the telegraph wires leading to Paris, and in twenty-four hours cut in ten different places the wires of the same line, without being arrested. The visual telegraph, on the contrary, has its towers, its high walls, its gates well-guarded from inside by strong armed men. Yes, I declare, substitution of the electric telegraph for the visual one is a dreadful measure, a truly idiotic act." Dr. Barbay and his high-security stone machines were eventually unsuccessful, but his argument -- that communication exists for the safety and convenience of the state, and must be carefully protected from the wild boys and the gutter rabble who might want to crash the system -- would be heard again and again. When the French telephone system finally did arrive, its snarled inadequacy was to be notorious. Devotees of the American Bell System often recommended a trip to France, for skeptics. In Edwardian Britain, issues of class and privacy were a ball-and- chain for telephonic progress. It was considered outrageous that anyone -- any wild fool off the street -- could simply barge bellowing into one's office or home, preceded only by the ringing of a telephone bell. In Britain, phones were tolerated for the use of business, but private phones tended be stuffed away into closets, smoking rooms, or servants' quarters. Telephone operators were resented in Britain because they did not seem to "know their place." And no one of breeding would print a telephone number on a business card; this seemed a crass attempt to make the acquaintance of strangers. But phone access in America was to become a popular right; something like universal suffrage, only more so. American women could not yet vote when the phone system came through; yet from the beginning American women doted on the telephone. This "feminization" of the American telephone was often commented on by foreigners. Phones in America were not censored or stiff or formalized; they were social, private, intimate, and domestic. In America, Mother's Day is by far the busiest day of the year for the phone network. The early telephone companies, and especially AT&T, were among the foremost employers of American women. They employed the daughters of the American middle-class in great armies: in 1891, eight thousand women; by 1946, almost a quarter of a million. Women seemed to enjoy telephone work; it was respectable, it was steady, it paid fairly well as women's work went, and -- not least -- it seemed a genuine contribution to the social good of the community. Women found Vail's ideal of public service attractive. This was especially true in rural areas, where women operators, running extensive rural partylines, enjoyed considerable social power. The operator knew everyone on the party-line, and everyone knew her. Although Bell himself was an ardent suffragist, the telephone company did not employ women for the sake of advancing female liberation. AT&T did this for sound commercial reasons. The first telephone operators of the Bell system were not women, but teenage American boys. They were telegraphic messenger boys (a group about to be rendered technically obsolescent), who swept up around the phone office, dunned customers for bills, and made phone connections on the switchboard, all on the cheap. Within the very first year of operation, 1878, Bell's company learned a sharp lesson about combining teenage boys and telephone switchboards. Putting teenage boys in charge of the phone system brought swift and consistent disaster. Bell's chief engineer described them as "Wild Indians." The boys were openly rude to customers. They talked back to subscribers, saucing off, uttering facetious remarks, and generally giving lip. The rascals took Saint Patrick's Day off without permission. And worst of all they played clever tricks with the switchboard plugs: disconnecting calls, crossing lines so that customers found themselves talking to strangers, and so forth. This combination of power, technical mastery, and effective anonymity seemed to act like catnip on teenage boys. This wild-kid-on-the-wires phenomenon was not confined to the USA; from the beginning, the same was true of the British phone system. An early British commentator kindly remarked: "No doubt boys in their teens found the work not a little irksome, and it is also highly probable that under the early conditions of employment the adventurous and inquisitive spirits of which the average healthy boy of that age is possessed, were not always conducive to the best attention being given to the wants of the telephone subscribers." So the boys were flung off the system -- or at least, deprived of control of the switchboard. But the "adventurous and inquisitive spirits" of the teenage boys would be heard from in the world of telephony, again and again. The fourth stage in the technological life-cycle is death: "the Dog," dead tech. The telephone has so far avoided this fate. On the contrary, it is thriving, still spreading, still evolving, and at increasing speed. The telephone has achieved a rare and exalted state for a technological artifact: it has become a *household object*. The telephone, like the clock, like pen and paper, like kitchen utensils and running water, has become a technology that is visible only by its absence. The telephone is technologically transparent. The global telephone system is the largest and most complex machine in the world, yet it is easy to use. More remarkable yet, the telephone is almost entirely physically safe for the user. For the average citizen in the 1870s, the telephone was weirder, more shocking, more "high-tech" and harder to comprehend, than the most outrageous stunts of advanced computing for us Americans in the 1990s. In trying to understand what is happening to us today, with our bulletin- board systems, direct overseas dialling, fiberoptic transmissions, computer viruses, hacking stunts, and a vivid tangle of new laws and new crimes, it is important to realize that our society has been through a similar challenge before -- and that, all in all, we did rather well by it. Bell's stage telephone seemed bizarre at first. But the sensations of weirdness vanished quickly, once people began to hear the familiar voices of relatives and friends, in their own homes on their own telephones. The telephone changed from a fearsome high-tech totem to an everyday pillar of human community. This has also happened, and is still happening, to computer networks. Computer networks such as NSFnet, BITnet, USENET, JANET, are technically advanced, intimidating, and much harder to use than telephones. Even the popular, commercial computer networks, such as GEnie, Prodigy, and CompuServe, cause much head-scratching and have been described as "user-hateful." Nevertheless they too are changing from fancy high-tech items into everyday sources of human community. The words "community" and "communication" have the same root. Wherever you put a communications network, you put a community as well. And whenever you *take away* that network -- confiscate it, outlaw it, crash it, raise its price beyond affordability -- then you hurt that community. Communities will fight to defend themselves. People will fight harder and more bitterly to defend their communities, than they will fight to defend their own individual selves. And this is very true of the "electronic community" that arose around computer networks in the 1980s -- or rather, the *various* electronic communities, in telephony, law enforcement, computing, and the digital underground that, by the year 1990, were raiding, rallying, arresting, suing, jailing, fining and issuing angry manifestos. None of the events of 1990 were entirely new. Nothing happened in 1990 that did not have some kind of earlier and more understandable precedent. What gave the Hacker Crackdown its new sense of gravity and importance was the feeling -- the *community* feeling - that the political stakes had been raised; that trouble in cyberspace was no longer mere mischief or inconclusive skirmishing, but a genuine fight over genuine issues, a fight for community survival and the shape of the future. These electronic communities, having flourished throughout the 1980s, were becoming aware of themselves, and increasingly, becoming aware of other, rival communities. Worries were sprouting up right and left, with complaints, rumors, uneasy speculations. But it would take a catalyst, a shock, to make the new world evident. Like Bell's great publicity break, the Tarriffville Rail Disaster of January 1878, it would take a cause celebre. That cause was the AT&T Crash of January 15, 1990. After the Crash, the wounded and anxious telephone community would come out fighting hard. 2. === The community of telephone technicians, engineers, operators and researchers is the oldest community in cyberspace. These are the veterans, the most developed group, the richest, the most respectable, in most ways the most powerful. Whole generations have come and gone since Alexander Graham Bell's day, but the community he founded survives; people work for the phone system today whose great- grandparents worked for the phone system. Its specialty magazines, such as *Telephony*, *AT&T Technical Journal*, *Telephone Engineer and Management*, are decades old; they make computer publications like *Macworld* and *PC Week* look like amateur johnny-come-latelies. And the phone companies take no back seat in hightechnology, either. Other companies' industrial researchers may have won new markets; but the researchers of Bell Labs have won *seven Nobel Prizes*. One potent device that Bell Labs originated, the transistor, has created entire *groups* of industries. Bell Labs are world-famous for generating "a patent a day," and have even made vital discoveries in astronomy, physics and cosmology. Throughout its seventy-year history, "Ma Bell" was not so much a company as a way of life. Until the cataclysmic divestiture of the 1980s, Ma Bell was perhaps the ultimate maternalist mega-employer. The AT&T corporate image was the "gentle giant," "the voice with a smile," a vaguely socialist-realist world of cleanshaven linemen in shiny helmets and blandly pretty phone-girls in headsets and nylons. Bell System employees were famous as rock-ribbed Kiwanis and Rotary members, Little-League enthusiasts, school-board people. During the long heyday of Ma Bell, the Bell employee corps were nurtured top-to-botton on a corporate ethos of public service. There was good money in Bell, but Bell was not *about* money; Bell used public relations, but never mere marketeering. People went into the Bell System for a good life, and they had a good life. But it was not mere money that led Bell people out in the midst of storms and earthquakes to fight with toppled phone-poles, to wade in flooded manholes, to pull the redeyed graveyard-shift over collapsing switching-systems. The Bell ethic was the electrical equivalent of the postman's: neither rain, nor snow, nor gloom of night would stop these couriers. It is easy to be cynical about this, as it is easy to be cynical about any political or social system; but cynicism does not change the fact that thousands of people took these ideals very seriously. And some still do. The Bell ethos was about public service; and that was gratifying; but it was also about private *power*, and that was gratifying too. As a corporation, Bell was very special. Bell was privileged. Bell had snuggled up close to the state. In fact, Bell was as close to government as you could get in America and still make a whole lot of legitimate money. But unlike other companies, Bell was above and beyond the vulgar commercial fray. Through its regional operating companies, Bell was omnipresent, local, and intimate, all over America; but the central ivory towers at its corporate heart were the tallest and the ivoriest around. There were other phone companies in America, to be sure; the so-called independents. Rural cooperatives, mostly; small fry, mostly tolerated, sometimes warred upon. For many decades, "independent" American phone companies lived in fear and loathing of the official Bell monopoly (or the "Bell Octopus," as Ma Bell's nineteenthcentury enemies described her in many angry newspaper manifestos). Some few of these independent entrepreneurs, while legally in the wrong, fought so bitterly against the Octopus that their illegal phone networks were cast into the street by Bell agents and publicly burned. The pure technical sweetness of the Bell System gave its operators, inventors and engineers a deeply satisfying sense of power and mastery. They had devoted their lives to improving this vast nation-spanning machine; over years, whole human lives, they had watched it improve and grow. It was like a great technological temple. They were an elite, and they knew it -- even if others did not; in fact, they felt even more powerful *because* others did not understand. The deep attraction of this sensation of elite technical *power* should never be underestimated. "Technical power" is not for everybody; for many people it simply has no charm at all. But for some people, it becomes the core of their lives. For a few, it is overwhelming, obsessive; it becomes something close to an addiction. People -- especially clever teenage boys whose lives are otherwise mostly powerless and put-upon - love this sensation of secret power, and are willing to do all sorts of amazing things to achieve it. The technical power of electronics has motivated many strange acts detailed in this book, which would otherwise be inexplicable. So Bell had power beyond mere capitalism. The Bell service ethos worked, and was often propagandized, in a rather saccharine fashion. Over the decades, people slowly grew tired of this. And then, openly impatient with it. By the early 1980s, Ma Bell was to find herself with scarcely a real friend in the world. Vail's industrial socialism had become hopelessly out-of-fashion politically. Bell would be punished for that. And that punishment would fall harshly upon the people of the telephone community. 3. === In 1983, Ma Bell was dismantled by federal court action. The pieces of Bell are now separate corporate entities. The core of the company became AT&T Communications, and also AT&T Industries (formerly Western Electric, Bell's manufacturing arm). AT&T Bell Labs become Bell Communications Research, Bellcore. Then there are the Regional Bell Operating Companies, or RBOCs, pronounced "arbocks." Bell was a titan and even these regional chunks are gigantic enterprises: Fortune 50 companies with plenty of wealth and power behind them. But the clean lines of "One Policy, One System, Universal Service" have been shattered, apparently forever. The "One Policy" of the early Reagan Administration was to shatter a system that smacked of noncompetitive socialism. Since that time, there has been no real telephone "policy" on the federal level. Despite the breakup, the remnants of Bell have never been set free to compete in the open marketplace. The RBOCs are still very heavily regulated, but not from the top. Instead, they struggle politically, economically and legally, in what seems an endless turmoil, in a patchwork of overlapping federal and state jurisdictions. Increasingly, like other major American corporations, the RBOCs are becoming multinational, acquiring important commercial interests in Europe, Latin America, and the Pacific Rim. But this, too, adds to their legal and political predicament. The people of what used to be Ma Bell are not happy about their fate. They feel ill-used. They might have been grudgingly willing to make a full transition to the free market; to become just companies amid other companies. But this never happened. Instead, AT&T and the RBOCS ("the Baby Bells") feel themselves wrenched from side to side by state regulators, by Congress, by the FCC, and especially by the federal court of Judge Harold Greene, the magistrate who ordered the Bell breakup and who has been the de facto czar of American telecommunications ever since 1983. Bell people feel that they exist in a kind of paralegal limbo today. They don't understand what's demanded of them. If it's "service," why aren't they treated like a public service? And if it's money, then why aren't they free to compete for it? No one seems to know, really. Those who claim to know keep changing their minds. Nobody in authority seems willing to grasp the nettle for once and all. Telephone people from other countries are amazed by the American telephone system today. Not that it works so well; for nowadays even the French telephone system works, more or less. They are amazed that the American telephone system *still* works *at all*, under these strange conditions. Bell's "One System" of long-distance service is now only about eighty percent of a system, with the remainder held by Sprint, MCI, and the midget long-distance companies. Ugly wars over dubious corporate practices such as "slamming" (an underhanded method of snitching clients from rivals) break out with some regularity in the realm of long-distance service. The battle to break Bell's long-distance monopoly was long and ugly, and since the breakup the battlefield has not become much prettier. AT&T's famous shame-and-blame advertisements, which emphasized the shoddy work and purported ethical shadiness of their competitors, were much remarked on for their studied psychological cruelty. There is much bad blood in this industry, and much long-treasured resentment. AT&T's post-breakup corporate logo, a striped sphere, is known in the industry as the "Death Star" (a reference from the movie *Star Wars*, in which the "Death Star" was the spherical hightech fortress of the harsh-breathing imperial ultra-baddie, Darth Vader.) Even AT&T employees are less than thrilled by the Death Star. A popular (though banned) Tshirt among AT&T employees bears the old-fashioned Bell logo of the Bell System, plus the newfangled striped sphere, with the before-and-after comments: "This is your brain -- This is your brain on drugs!" AT&T made a very well-financed and determined effort to break into the personal computer market; it was disastrous, and telco computer experts are derisively known by their competitors as "the pole-climbers." AT&T and the Baby Bell arbocks still seem to have few friends. Under conditions of sharp commercial competition, a crash like that of January 15, 1990 was a major embarrassment to AT&T. It was a direct blow against their much- treasured reputation for reliability. Within days of the crash AT&T's Chief Executive Officer, Bob Allen, officially apologized, in terms of deeply pained humility: "AT&T had a major service disruption last Monday. We didn't live up to our own standards of quality, and we didn't live up to yours. It's as simple as that. And that's not acceptable to us. Or to you.... We understand how much people have come to depend upon AT&T service, so our AT&T Bell Laboratories scientists and our network engineers are doing everything possible to guard against a recurrence.... We know there's no way to make up for the inconvenience this problem may have caused you." Mr Allen's "open letter to customers" was printed in lavish ads all over the country: in the *Wall Street Journal, USA Today, New York Times, Los Angeles Times, Chicago Tribune, Philadelphia Inquirer, San Francisco Chronicle Examiner, Boston Globe, Dallas Morning News, Detroit Free Press, Washington Post, Houston Chronicle, Cleveland Plain Dealer, Atlanta Journal Constitution, Minneapolis Star Tribune, St. Paul Pioneer Press Dispatch, Seattle Times/Post Intelligencer, Tacoma News Tribune, Miami Herald, Pittsburgh Press, St. Louis Post Dispatch, Denver Post, Phoenix Republic Gazette* and *Tampa Tribune*. In another press release, AT&T went to some pains to suggest that this "software glitch" *might* have happened just as easily to MCI, although, in fact, it hadn't. (MCI's switching software was quite different from AT&T's -though not necessarily any safer.) AT&T also announced their plans to offer a rebate of service on Valentine's Day to make up for the loss during the Crash. "Every technical resource available, including Bell Labs scientists and engineers, has been devoted to assuring it will not occur again," the public was told. They were further assured that "The chances of a recurrence are small--a problem of this magnitude never occurred before." In the meantime, however, police and corporate security maintained their own suspicions about "the chances of recurrence" and the real reason why a "problem of this magnitude" had appeared, seemingly out of nowhere. Police and security knew for a fact that hackers of unprecedented sophistication were illegally entering, and reprogramming, certain digital switching stations. Rumors of hidden "viruses" and secret "logic bombs" in the switches ran rampant in the underground, with much chortling over AT&T's predicament, and idle speculation over what unsung hacker genius was responsible for it. Some hackers, including police informants, were trying hard to finger one another as the true culprits of the Crash. Telco people found little comfort in objectivity when they contemplated these possibilities. It was just too close to the bone for them; it was embarrassing; it hurt so much, it was hard even to talk about. There has always been thieving and misbehavior in the phone system. There has always been trouble with the rival independents, and in the local loops. But to have such trouble in the core of the system, the long-distance switching stations, is a horrifying affair. To telco people, this is all the difference between finding roaches in your kitchen and big horrid sewer-rats in your bedroom. From the outside, to the average citizen, the telcos still seem gigantic and impersonal. The American public seems to regard them as something akin to Soviet apparats. Even when the telcos do their best corporatecitizen routine, subsidizing magnet high-schools and sponsoring news-shows on public television, they seem to win little except public suspicion. But from the inside, all this looks very different. There's harsh competition. A legal and political system that seems baffled and bored, when not actively hostile to telco interests. There's a loss of morale, a deep sensation of having somehow lost the upper hand. Technological change has caused a loss of data and revenue to other, newer forms of transmission. There's theft, and new forms of theft, of growing scale and boldness and sophistication. With all these factors, it was no surprise to see the telcos, large and small, break out in a litany of bitter complaint. In late '88 and throughout 1989, telco representatives grew shrill in their complaints to those few American law enforcement officials who make it their business to try to understand what telephone people are talking about. Telco security officials had discovered the computerhacker underground, infiltrated it thoroughly, and become deeply alarmed at its growing expertise. Here they had found a target that was not only loathsome on its face, but clearly ripe for counterattack. Those bitter rivals: AT&T, MCI and Sprint -- and a crowd of Baby Bells: PacBell, Bell South, Southwestern Bell, NYNEX, USWest, as well as the Bell research consortium Bellcore, and the independent long- distance carrier Mid-American -- all were to have their role in the great hacker dragnet of 1990. After years of being battered and pushed around, the telcos had, at least in a small way, seized the initiative again. After years of turmoil, telcos and government officials were once again to work smoothly in concert in defense of the System. Optimism blossomed; enthusiasm grew on all sides; the prospective taste of vengeance was sweet. 4. === From the beginning -- even before the crackdown had a name -- secrecy was a big problem. There were many good reasons for secrecy in the hacker crackdown. Hackers and code-thieves were wily prey, slinking back to their bedrooms and basements and destroying vital incriminating evidence at the first hint of trouble. Furthermore, the crimes themselves were heavily technical and difficult to describe, even to police -- much less to the general public. When such crimes *had* been described intelligibly to the public, in the past, that very publicity had tended to *increase* the crimes enormously. Telco officials, while painfully aware of the vulnerabilities of their systems, were anxious not to publicize those weaknesses. Experience showed them that those weaknesses, once discovered, would be pitilessly exploited by tens of thousands of people -- not only by professional grifters and by underground hackers and phone phreaks, but by many otherwise more-or-less honest everyday folks, who regarded stealing service from the faceless, soulless "Phone Company" as a kind of harmless indoor sport. When it came to protecting their interests, telcos had long since given up on general public sympathy for "the Voice with a Smile." Nowadays the telco's "Voice" was very likely to be a computer's; and the American public showed much less of the proper respect and gratitude due the fine public service bequeathed them by Dr. Bell and Mr. Vail. The more efficient, high-tech, computerized, and impersonal the telcos became, it seemed, the more they were met by sullen public resentment and amoral greed. Telco officials wanted to punish the phone-phreak underground, in as public and exemplary a manner as possible. They wanted to make dire examples of the worst offenders, to seize the ringleaders and intimidate the small fry, to discourage and frighten the wacky hobbyists, and send the professional grifters to jail. To do all this, publicity was vital. Yet operational secrecy was even more so. If word got out that a nationwide crackdown was coming, the hackers might simply vanish; destroy the evidence, hide their computers, go to earth, and wait for the campaign to blow over. Even the young hackers were crafty and suspicious, and as for the professional grifters, they tended to split for the nearest state-line at the first sign of trouble. For the crackdown to work well, they would all have to be caught red-handed, swept upon suddenly, out of the blue, from every corner of the compass. And there was another strong motive for secrecy. In the worst-case scenario, a blown campaign might leave the telcos open to a devastating hacker counter-attack. If there were indeed hackers loose in America who had caused the January 15 Crash -- if there were truly gifted hackers, loose in the nation's long- distance switching systems, and enraged or frightened by the crackdown -- then they might react unpredictably to an attempt to collar them. Even if caught, they might have talented and vengeful friends still running around loose. Conceivably, it could turn ugly. Very ugly. In fact, it was hard to imagine just how ugly things might turn, given that possibility. Counter- attack from hackers was a genuine concern for the telcos. In point of fact, they would never suffer any such counter-attack. But in months to come, they would be at some pains to publicize this notion and to utter grim warnings about it. Still, that risk seemed well worth running. Better to run the risk of vengeful attacks, than to live at the mercy of potential crashers. Any cop would tell you that a protection racket had no real future. And publicity was such a useful thing. Corporate security officers, including telco security, generally work under conditions of great discretion. And corporate security officials do not make money for their companies. Their job is to *prevent the loss* of money, which is much less glamorous than actually winning profits. If you are a corporate security official, and you do your job brilliantly, then nothing bad happens to your company at all. Because of this, you appear completely superfluous. This is one of the many unattractive aspects of security work. It's rare that these folks have the chance to draw some healthy attention to their own efforts. Publicity also served the interest of their friends in law enforcement. Public officials, including law enforcement officials, thrive by attracting favorable public interest. A brilliant prosecution in a matter of vital public interest can make the career of a prosecuting attorney. And for a police officer, good publicity opens the purses of the legislature; it may bring a citation, or a promotion, or at least a rise in status and the respect of one's peers. But to have both publicity and secrecy is to have one's cake and eat it too. In months to come, as we will show, this impossible act was to cause great pain to the agents of the crackdown. But early on, it seemed possible -- maybe even likely -- that the crackdown could successfully combine the best of both worlds. The *arrest* of hackers would be heavily publicized. The actual *deeds* of the hackers, which were technically hard to explain and also a security risk, would be left decently obscured. The *threat* hackers posed would be heavily trumpeted; the likelihood of their actually committing such fearsome crimes would be left to the public's imagination. The spread of the computer underground, and its growing technical sophistication, would be heavily promoted; the actual hackers themselves, mostly bespectacled middle-class white suburban teenagers, would be denied any personal publicity. It does not seem to have occurred to any telco official that the hackers accused would demand a day in court; that journalists would smile upon the hackers as "good copy;" that wealthy high-tech entrepreneurs would offer moral and financial support to crackdown victims; that constitutional lawyers would show up with briefcases, frowning mightily. This possibility does not seem to have ever entered the game-plan. And even if it had, it probably would not have slowed the ferocious pursuit of a stolen phone-company document, mellifluously known as "Control Office Administration of Enhanced 911 Services for Special Services and Major Account Centers." In the chapters to follow, we will explore the worlds of police and the computer underground, and the large shadowy area where they overlap. But first, we must explore the battleground. Before we leave the world of the telcos, we must understand what a switching system actually is and how your telephone actually works. 5. === To the average citizen, the idea of the telephone is represented by, well, a *telephone:* a device that you talk into. To a telco professional, however, the telephone itself is known, in lordly fashion, as a "subset." The "subset" in your house is a mere adjunct, a distant nerve ending, of the central switching stations, which are ranked in levels of heirarchy, up to the long-distance electronic switching stations, which are some of the largest computers on earth. Let us imagine that it is, say, 1925, before the introduction of computers, when the phone system was simpler and somewhat easier to grasp. Let's further imagine that you are Miss Leticia Luthor, a fictional operator for Ma Bell in New York City of the 20s. Basically, you, Miss Luthor, *are* the "switching system." You are sitting in front of a large vertical switchboard, known as a "cordboard," made of shiny wooden panels, with ten thousand metal-rimmed holes punched in them, known as jacks. The engineers would have put more holes into your switchboard, but ten thousand is as many as you can reach without actually having to get up out of your chair. Each of these ten thousand holes has its own little electric lightbulb, known as a "lamp," and its own neatly printed number code. With the ease of long habit, you are scanning your board for lit-up bulbs. This is what you do most of the time, so you are used to it. A lamp lights up. This means that the phone at the end of that line has been taken off the hook. Whenever a handset is taken off the hook, that closes a circuit inside the phone which then signals the local office, i.e. you, automatically. There might be somebody calling, or then again the phone might be simply off the hook, but this does not matter to you yet. The first thing you do, is record that number in your logbook, in your fine American public-school handwriting. This comes first, naturally, since it is done for billing purposes. You now take the plug of your answering cord, which goes directly to your headset, and plug it into the lit-up hole. "Operator," you announce. In operator's classes, before taking this job, you have been issued a large pamphlet full of canned operator's responses for all kinds of contingencies, which you had to memorize. You have also been trained in a proper nonregional, non-ethnic pronunciation and tone of voice. You rarely have the occasion to make any spontaneous remark to a customer, and in fact this is frowned upon (except out on the rural lines where people have time on their hands and get up to all kinds of mischief). A tough-sounding user's voice at the end of the line gives you a number. Immediately, you write that number down in your logbook, next to the caller's number, which you just wrote earlier. You then look and see if the number this guy wants is in fact on your switchboard, which it generally is, since it's generally a local call. Long distance costs so much that people use it sparingly. Only then do you pick up a calling-cord from a shelf at the base of the switchboard. This is a long elastic cord mounted on a kind of reel so that it will zip back in when you unplug it. There are a lot of cords down there, and when a bunch of them are out at once they look like a nest of snakes. Some of the girls think there are bugs living in those cable-holes. They're called "cable mites" and are supposed to bite your hands and give you rashes. You don't believe this, yourself. Gripping the head of your calling-cord, you slip the tip of it deftly into the sleeve of the jack for the called person. Not all the way in, though. You just touch it. If you hear a clicking sound, that means the line is busy and you can't put the call through. If the line is busy, you have to stick the calling-cord into a "busy-tone jack," which will give the guy a busy-tone. This way you don't have to talk to him yourself and absorb his natural human frustration. But the line isn't busy. So you pop the cord all the way in. Relay circuits in your board make the distant phone ring, and if somebody picks it up off the hook, then a phone conversation starts. You can hear this conversation on your answering cord, until you unplug it. In fact you could listen to the whole conversation if you wanted, but this is sternly frowned upon by management, and frankly, when you've overheard one, you've pretty much heard 'em all. You can tell how long the conversation lasts by the glow of the calling-cord's lamp, down on the calling-cord's shelf. When it's over, you unplug and the calling-cord zips back into place. Having done this stuff a few hundred thousand times, you become quite good at it. In fact you're plugging, and connecting, and disconnecting, ten, twenty, forty cords at a time. It's a manual handicraft, really, quite satisfying in a way, rather like weaving on an upright loom. Should a long-distance call come up, it would be different, but not all that different. Instead of connecting the call through your own local switchboard, you have to go up the hierarchy, onto the long-distance lines, known as "trunklines." Depending on how far the call goes, it may have to work its way through a whole series of operators, which can take quite a while. The caller doesn't wait on the line while this complex process is negotiated across the country by the gaggle of operators. Instead, the caller hangs up, and you call him back yourself when the call has finally worked its way through. After four or five years of this work, you get married, and you have to quit your job, this being the natural order of womanhood in the American 1920s. The phone company has to train somebody else -- maybe two people, since the phone system has grown somewhat in the meantime. And this costs money. In fact, to use any kind of human being as a switching system is a very expensive proposition. Eight thousand Leticia Luthors would be bad enough, but a quarter of a million of them is a military-scale proposition and makes drastic measures in automation financially worthwhile. Although the phone system continues to grow today, the number of human beings employed by telcos has been dropping steadily for years. Phone "operators" now deal with nothing but unusual contingencies, all routine operations having been shrugged off onto machines. Consequently, telephone operators are considerably less machine-like nowadays, and have been known to have accents and actual character in their voices. When you reach a human operator today, the operators are rather more "human" than they were in Leticia's day -- but on the other hand, human beings in the phone system are much harder to reach in the first place. Over the first half of the twentieth century, "electromechanical" switching systems of growing complexity were cautiously introduced into the phone system. In certain backwaters, some of these hybrid systems are still in use. But after 1965, the phone system began to go completely electronic, and this is by far the dominant mode today. Electromechanical systems have "crossbars," and "brushes," and other large moving mechanical parts, which, while faster and cheaper than Leticia, are still slow, and tend to wear out fairly quickly. But fully electronic systems are inscribed on silicon chips, and are lightning-fast, very cheap, and quite durable. They are much cheaper to maintain than even the best electromechanical systems, and they fit into half the space. And with every year, the silicon chip grows smaller, faster, and cheaper yet. Best of all, automated electronics work around the clock and don't have salaries or health insurance. There are, however, quite serious drawbacks to the use of computer-chips. When they do break down, it is a daunting challenge to figure out what the heck has gone wrong with them. A broken cordboard generally had a problem in it big enough to see. A broken chip has invisible, microscopic faults. And the faults in bad software can be so subtle as to be practically theological. If you want a mechanical system to do something new, then you must travel to where it is, and pull pieces out of it, and wire in new pieces. This costs money. However, if you want a chip to do something new, all you have to do is change its software, which is easy, fast and dirt-cheap. You don't even have to see the chip to change its program. Even if you did see the chip, it wouldn't look like much. A chip with program X doesn't look one whit different from a chip with program Y. With the proper codes and sequences, and access to specialized phone-lines, you can change electronic switching systems all over America from anywhere you please. And so can other people. If they know how, and if they want to, they can sneak into a microchip via the special phonelines and diddle with it, leaving no physical trace at all. If they broke into the operator's station and held Leticia at gunpoint, that would be very obvious. If they broke into a telco building and went after an electromechanical switch with a toolbelt, that would at least leave many traces. But people can do all manner of amazing things to computer switches just by typing on a keyboard, and keyboards are everywhere today. The extent of this vulnerability is deep, dark, broad, almost mind-boggling, and yet this is a basic, primal fact of life about any computer on a network. Security experts over the past twenty years have insisted, with growing urgency, that this basic vulnerability of computers represents an entirely new level of risk, of unknown but obviously dire potential to society. And they are right. An electronic switching station does pretty much everything Letitia did, except in nanoseconds and on a much larger scale. Compared to Miss Luthor's ten thousand jacks, even a primitive 1ESS switching computer, 60s vintage, has a 128,000 lines. And the current AT&T system of choice is the monstrous fifth-generation 5ESS. An Electronic Switching Station can scan every line on its "board" in a tenth of a second, and it does this over and over, tirelessly, around the clock. Instead of eyes, it uses "ferrod scanners" to check the condition of local lines and trunks. Instead of hands, it has "signal distributors," "central pulse distributors," "magnetic latching relays," and "reed switches," which complete and break the calls. Instead of a brain, it has a "central processor." Instead of an instruction manual, it has a program. Instead of a handwritten logbook for recording and billing calls, it has magnetic tapes. And it never has to talk to anybody. Everything a customer might say to it is done by punching the direct-dial tone buttons on your subset. Although an Electronic Switching Station can't talk, it does need an interface, some way to relate to its, er, employers. This interface is known as the "master control center." (This interface might be better known simply as "the interface," since it doesn't actually "control" phone calls directly. However, a term like "Master Control Center" is just the kind of rhetoric that telco maintenance engineers -- and hackers -- find particularly satisfying.) Using the master control center, a phone engineer can test local and trunk lines for malfunctions. He (rarely she) can check various alarm displays, measure traffic on the lines, examine the records of telephone usage and the charges for those calls, and change the programming. And, of course, anybody else who gets into the master control center by remote control can also do these things, if he (rarely she) has managed to figure them out, or, more likely, has somehow swiped the knowledge from people who already know. In 1989 and 1990, one particular RBOC, BellSouth, which felt particularly troubled, spent a purported $1.2 million on computer security. Some think it spent as much as two million, if you count all the associated costs. Two million dollars is still very little compared to the great cost- saving utility of telephonic computer systems. Unfortunately, computers are also stupid. Unlike human beings, computers possess the truly profound stupidity of the inanimate. In the 1960s, in the first shocks of spreading computerization, there was much easy talk about the stupidity of computers -- how they could "only follow the program" and were rigidly required to do "only what they were told." There has been rather less talk about the stupidity of computers since they began to achieve grandmaster status in chess tournaments, and to manifest many other impressive forms of apparent cleverness. Nevertheless, computers *still* are profoundly brittle and stupid; they are simply vastly more subtle in their stupidity and brittleness. The computers of the 1990s are much more reliable in their components than earlier computer systems, but they are also called upon to do far more complex things, under far more challenging conditions. On a basic mathematical level, every single line of a software program offers a chance for some possible screwup. Software does not sit still when it works; it "runs," it interacts with itself and with its own inputs and outputs. By analogy, it stretches like putty into millions of possible shapes and conditions, so many shapes that they can never all be successfully tested, not even in the lifespan of the universe. Sometimes the putty snaps. The stuff we call "software" is not like anything that human society is used to thinking about. Software is something like a machine, and something like mathematics, and something like language, and something like thought, and art, and information.... but software is not in fact any of those other things. The protean quality of software is one of the great sources of its fascination. It also makes software very powerful, very subtle, very unpredictable, and very risky. Some software is bad and buggy. Some is "robust," even "bulletproof." The best software is that which has been tested by thousands of users under thousands of different conditions, over years. It is then known as "stable." This does *not* mean that the software is now flawless, free of bugs. It generally means that there are plenty of bugs in it, but the bugs are well-identified and fairly well understood. There is simply no way to assure that software is free of flaws. Though software is mathematical in nature, it cannot by "proven" like a mathematical theorem; software is more like language, with inherent ambiguities, with different definitions, different assumptions, different levels of meaning that can conflict. Human beings can manage, more or less, with human language because we can catch the gist of it. Computers, despite years of effort in "artificial intelligence," have proven spectacularly bad in "catching the gist" of anything at all. The tiniest bit of semantic grit may still bring the mightiest computer tumbling down. One of the most hazardous things you can do to a computer program is try to improve it -- to try to make it safer. Software "patches" represent new, untried un"stable" software, which is by definition riskier. The modern telephone system has come to depend, utterly and irretrievably, upon software. And the System Crash of January 15, 1990, was caused by an *improvement* in software. Or rather, an *attempted* improvement. As it happened, the problem itself -- the problem per se -- took this form. A piece of telco software had been written in C language, a standard language of the telco field. Within the C software was a long "do... while" construct. The "do... while" construct contained a "switch" statement. The "switch" statement contained an "if" clause. The "if" clause contained a "break." The "break" was *supposed* to "break" the "if clause." Instead, the "break" broke the "switch" statement. That was the problem, the actual reason why people picking up phones on January 15, 1990, could not talk to one another. Or at least, that was the subtle, abstract, cyberspatial seed of the problem. This is how the problem manifested itself from the realm of programming into the realm of real life. The System 7 software for AT&T's 4ESS switching station, the "Generic 44E14 Central Office Switch Software," had been extensively tested, and was considered very stable. By the end of 1989, eighty of AT&T's switching systems nationwide had been programmed with the new software. Cautiously, thirty four stations were left to run the slower, less-capable System 6, because AT&T suspected there might be shakedown problems with the new and unprecedently sophisticated System 7 network. The stations with System 7 were programmed to switch over to a backup net in case of any problems. In mid-December 1989, however, a new high-velocity, high security software patch was distributed to each of the 4ESS switches that would enable them to switch over even more quickly, making the System 7 network that much more secure. Unfortunately, every one of these 4ESS switches was now in possession of a small but deadly flaw. In order to maintain the network, switches must monitor the condition of other switches -- whether they are up and running, whether they have temporarily shut down, whether they are overloaded and in need of assistance, and so forth. The new software helped control this bookkeeping function by monitoring the status calls from other switches. It only takes four to six seconds for a troubled 4ESS switch to rid itself of all its calls, drop everything temporarily, and re-boot its software from scratch. Starting over from scratch will generally rid the switch of any software problems that may have developed in the course of running the system. Bugs that arise will be simply wiped out by this process. It is a clever idea. This process of automatically re-booting from scratch is known as the "normal fault recovery routine." Since AT&T's software is in fact exceptionally stable, systems rarely have to go into "fault recovery" in the first place; but AT&T has always boasted of its "real world" reliability, and this tactic is a belt-and-suspenders routine. The 4ESS switch used its new software to monitor its fellow switches as they recovered from faults. As other switches came back on line after recovery, they would send their "OK" signals to the switch. The switch would make a little note to that effect in its "status map," recognizing that the fellow switch was back and ready to go, and should be sent some calls and put back to regular work. Unfortunately, while it was busy bookkeeping with the status map, the tiny flaw in the brand-new software came into play. The flaw caused the 4ESS switch to interacted, subtly but drastically, with incoming telephone calls from human users. If -- and only if -- two incoming phone- calls happened to hit the switch within a hundredth of a second, then a small patch of data would be garbled by the flaw. But the switch had been programmed to monitor itself constantly for any possible damage to its data. When the switch perceived that its data had been somehow garbled, then it too would go down, for swift repairs to its software. It would signal its fellow switches not to send any more work. It would go into the fault recovery mode for four to six seconds. And then the switch would be fine again, and would send out its "OK, ready for work" signal. However, the "OK, ready for work" signal was the *very thing that had caused the switch to go down in the first place*. And *all* the System 7 switches had the same flaw in their status-map software. As soon as they stopped to make the bookkeeping note that their fellow switch was "OK," then they too would become vulnerable to the slight chance that two phone-calls would hit them within a hundredth of a second. At approximately 2:25 p.m. EST on Monday, January 15, one of AT&T's 4ESS toll switching systems in New York City had an actual, legitimate, minor problem. It went into fault recovery routines, announced "I'm going down," then announced, "I'm back, I'm OK." And this cheery message then blasted throughout the network to many of its fellow 4ESS switches. Many of the switches, at first, completely escaped trouble. These lucky switches were not hit by the coincidence of two phone calls within a hundredth of a second. Their software did not fail -- at first. But three switches -- in Atlanta, St. Louis, and Detroit -- were unlucky, and were caught with their hands full. And they went down. And they came back up, almost immediately. And they too began to broadcast the lethal message that they, too, were "OK" again, activating the lurking software bug in yet other switches. As more and more switches did have that bit of bad luck and collapsed, the call-traffic became more and more densely packed in the remaining switches, which were groaning to keep up with the load. And of course, as the calls became more densely packed, the switches were *much more likely* to be hit twice within a hundredth of a second. It only took four seconds for a switch to get well. There was no *physical* damage of any kind to the switches, after all. Physically, they were working perfectly. This situation was "only" a software problem. But the 4ESS switches were leaping up and down every four to six seconds, in a virulent spreading wave all over America, in utter, manic, mechanical stupidity. They kept *knocking* one another down with their contagious "OK" messages. It took about ten minutes for the chain reaction to cripple the network. Even then, switches would periodically luck-out and manage to resume their normal work. Many calls -- millions of them -- were managing to get through. But millions weren't. The switching stations that used System 6 were not directly affected. Thanks to these old-fashioned switches, AT&T's national system avoided complete collapse. This fact also made it clear to engineers that System 7 was at fault. Bell Labs engineers, working feverishly in New Jersey, Illinois, and Ohio, first tried their entire repertoire of standard network remedies on the malfunctioning System 7. None of the remedies worked, of course, because nothing like this had ever happened to any phone system before. By cutting out the backup safety network entirely, they were able to reduce the frenzy of "OK" messages by about half. The system then began to recover, as the chain reaction slowed. By 11:30 pm on Monday January 15, sweating engineers on the midnight shift breathed a sigh of relief as the last switch cleared-up. By Tuesday they were pulling all the brand-new 4ESS software and replacing it with an earlier version of System 7. If these had been human operators, rather than computers at work, someone would simply have eventually stopped screaming. It would have been *obvious* that the situation was not "OK," and common sense would have kicked in. Humans possess common sense -- at least to some extent. Computers simply don't. On the other hand, computers can handle hundreds of calls per second. Humans simply can't. If every single human being in America worked for the phone company, we couldn't match the performance of digital switches: direct-dialling, three-way calling, speed- calling, callwaiting, Caller ID, all the rest of the cornucopia of digital bounty. Replacing computers with operators is simply not an option any more. And yet we still, anachronistically, expect humans to be running our phone system. It is hard for us to understand that we have sacrificed huge amounts of initiative and control to senseless yet powerful machines. When the phones fail, we want somebody to be responsible. We want somebody to blame. When the Crash of January 15 happened, the American populace was simply not prepared to understand that enormous landslides in cyberspace, like the Crash itself, can happen, and can be nobody's fault in particular. It was easier to believe, maybe even in some odd way more reassuring to believe, that some evil person, or evil group, had done this to us. "Hackers" had done it. With a virus. A trojan horse. A software bomb. A dirty plot of some kind. People believed this, responsible people. In 1990, they were looking hard for evidence to confirm their heartfelt suspicions. And they would look in a lot of places. Come 1991, however, the outlines of an apparent new reality would begin to emerge from the fog. On July 1 and 2, 1991, computer-software collapses in telephone switching stations disrupted service in Washington DC, Pittsburgh, Los Angeles and San Francisco. Once again, seemingly minor maintenance problems had crippled the digital System 7. About twelve million people were affected in the Crash of July 1, 1991. Said the New York Times Service: "Telephone company executives and federal regulators said they were not ruling out the possibility of sabotage by computer hackers, but most seemed to think the problems stemmed from some unknown defect in the software running the networks." And sure enough, within the week, a red-faced software company, DSC Communications Corporation of Plano, Texas, owned up to "glitches" in the "signal transfer point" software that DSC had designed for Bell Atlantic and Pacific Bell. The immediate cause of the July 1 Crash was a single mistyped character: one tiny typographical flaw in one single line of the software. One mistyped letter, in one single line, had deprived the nation's capital of phone service. It was not particularly surprising that this tiny flaw had escaped attention: a typical System 7 station requires *ten million* lines of code. On Tuesday, September 17, 1991, came the most spectacular outage yet. This case had nothing to do with software failures -- at least, not directly. Instead, a group of AT&T's switching stations in New York City had simply run out of electrical power and shut down cold. Their back-up batteries had failed. Automatic warning systems were supposed to warn of the loss of battery power, but those automatic systems had failed as well. This time, Kennedy, La Guardia, and Newark airports all had their voice and data communications cut. This horrifying event was particularly ironic, as attacks on airport computers by hackers had long been a standard nightmare scenario, much trumpeted by computer- security experts who feared the computer underground. There had even been a Hollywood thriller about sinister hackers ruining airport computers -- *Die Hard II*. Now AT&T itself had crippled airports with computer malfunctions -- not just one airport, but three at once, some of the busiest in the world. Air traffic came to a standstill throughout the Greater New York area, causing more than 500 flights to be cancelled, in a spreading wave all over America and even into Europe. Another 500 or so flights were delayed, affecting, all in all, about 85,000 passengers. (One of these passengers was the chairman of the Federal Communications Commission.) Stranded passengers in New York and New Jersey were further infuriated to discover that they could not even manage to make a long distance phone call, to explain their delay to loved ones or business associates. Thanks to the crash, about four and a half million domestic calls, and half a million international calls, failed to get through. The September 17 NYC Crash, unlike the previous ones, involved not a whisper of "hacker" misdeeds. On the contrary, by 1991, AT&T itself was suffering much of the vilification that had formerly been directed at hackers. Congressmen were grumbling. So were state and federal regulators. And so was the press. For their part, ancient rival MCI took out snide fullpage newspaper ads in New York, offering their own longdistance services for the "next time that AT&T goes down." "You wouldn't find a classy company like AT&T using such advertising," protested AT&T Chairman Robert Allen, unconvincingly. Once again, out came the full-page AT&T apologies in newspapers, apologies for "an inexcusable culmination of both human and mechanical failure." (This time, however, AT&T offered no discount on later calls. Unkind critics suggested that AT&T were worried about setting any precedent for refunding the financial losses caused by telephone crashes.) Industry journals asked publicly if AT&T was "asleep at the switch." The telephone network, America's purported marvel of high-tech reliability, had gone down three times in 18 months. *Fortune* magazine listed the Crash of September 17 among the "Biggest Business Goofs of 1991," cruelly parodying AT&T's ad campaign in an article entitled "AT&T Wants You Back (Safely On the Ground, God Willing)." Why had those New York switching systems simply run out of power? Because no human being had attended to the alarm system. Why did the alarm systems blare automatically, without any human being noticing? Because the three telco technicians who *should* have been listening were absent from their stations in the power-room, on another floor of the building -- attending a training class. A training class about the alarm systems for the power room! "Crashing the System" was no longer "unprecedented" by late 1991. On the contrary, it no longer even seemed an oddity. By 1991, it was clear that all the policemen in the world could no longer "protect" the phone system from crashes. By far the worst crashes the system had ever had, had been inflicted, by the system, upon *itself*. And this time nobody was making cocksure statements that this was an anomaly, something that would never happen again. By 1991 the System's defenders had met their nebulous Enemy, and the Enemy was -- the System. ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_preface.txt0000600000175000017500000002101110246051200031574 0ustar madduckmadduckrestindex section: computers link-title: The Hacker Crackdown page-description: A literary freeware book by Bruce Sterling. /description crumb: Hacker Preface /restindex ==================================== The Hacker Crackdown - The Preface ==================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html Preface to the Electronic Release of The Hacker Crackdown =========================================================== Out in the traditional world of print, *The Hacker Crackdown* is ISBN 0-553-08058-X, and is formally catalogued by the Library of Congress as "1. Computer crimes -- United States. 2. Telephone -- United States -- Corrupt practices. 3. Programming (Electronic computers) -- United States -- Corrupt practices." 'Corrupt practices,' I always get a kick out of that description. Librarians are very ingenious people. The paperback is ISBN 0-553-56370-X. If you go and buy a print version of *The Hacker Crackdown*, an action I encourage heartily, you may notice that in the front of the book, beneath the copyright notice -- ``"Copyright (C) 1992 by Bruce Sterling"`` -- it has this little block of printed legal boilerplate from the publisher. It says, and I quote: *No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage and retrieval system, without permission in writing from the publisher. For information address: Bantam Books.* This is a pretty good disclaimer, as such disclaimers go. I collect intellectual-property disclaimers, and I've seen dozens of them, and this one is at least pretty straightforward. In this narrow and particular case, however, it isn't quite accurate. Bantam Books puts that disclaimer on every book they publish, but Bantam Books does not, in fact, own the electronic rights to this book. I do, because of certain extensive contract maneuverings my agent and I went through before this book was written. I want to give those electronic publishing rights away through certain not- for-profit channels, and I've convinced Bantam that this is a good idea. Since Bantam has seen fit to peacably agree to this scheme of mine, Bantam Books is not going to fuss about this. Provided you don't try to sell the book, they are not going to bother you for what you do with the electronic copy of this book. If you want to check this out personally, you can ask them; they're at 1540 Broadway NY NY 10036. However, if you were so foolish as to print this book and start retailing it for money in violation of my copyright and the commercial interests of Bantam Books, then Bantam, a part of the gigantic Bertelsmann multinational publishing combine, would roust some of their heavy-duty attorneys out of hibernation and crush you like a bug. This is only to be expected. I didn't write this book so that you could make money out of it. If anybody is gonna make money out of this book, it's gonna be me and my publisher. My publisher deserves to make money out of this book. Not only did the folks at Bantam Books commission me to write the book, and pay me a hefty sum to do so, but they bravely printed, in text, an electronic document the reproduction of which was once alleged to be a federal felony. Bantam Books and their numerous attorneys were very brave and forthright about this book. Furthermore, my former editor at Bantam Books, Betsy Mitchell, genuinely cared about this project, and worked hard on it, and had a lot of wise things to say about the manuscript. Betsy deserves genuine credit for this book, credit that editors too rarely get. The critics were very kind to *The Hacker Crackdown*, and commercially the book has done well. On the other hand, I didn't write this book in order to squeeze every last nickel and dime out of the mitts of impoverished sixteen-year-old cyberpunk high-school-students. Teenagers don't have any money -- (no, not even enough for the sixdollar *Hacker Crackdown* paperback, with its attractive bright-red cover and useful index). That's a major reason why teenagers sometimes succumb to the temptation to do things they shouldn't, such as swiping my books out of libraries. Kids: this one is all yours, all right? Go give the print version back. \*8-) Well-meaning, public-spirited civil libertarians don't have much money, either. And it seems almost criminal to snatch cash out of the hands of America's direly underpaid electronic law enforcement community. If you're a computer cop, a hacker, or an electronic civil liberties activist, you are the target audience for this book. I wrote this book because I wanted to help you, and help other people understand you and your unique, uhm, problems. I wrote this book to aid your activities, and to contribute to the public discussion of important political issues. In giving the text away in this fashion, I am directly contributing to the book's ultimate aim: to help civilize cyberspace. Information wants to be free. And the information inside this book longs for freedom with a peculiar intensity. I genuinely believe that the natural habitat of this book is inside an electronic network. That may not be the easiest direct method to generate revenue for the book's author, but that doesn't matter; this is where this book belongs by its nature. I've written other books -- plenty of other books -- and I'll write more and I am writing more, but this one is special. I am making *The Hacker Crackdown* available electronically as widely as I can conveniently manage, and if you like the book, and think it is useful, then I urge you to do the same with it. You can copy this electronic book. Copy the heck out of it, be my guest, and give those copies to anybody who wants them. The nascent world of cyberspace is full of sysadmins, teachers, trainers, cybrarians, netgurus, and various species of cybernetic activist. If you're one of those people, I know about you, and I know the hassle you go through to try to help people learn about the electronic frontier. I hope that possessing this book in electronic form will lessen your troubles. Granted, this treatment of our electronic social spectrum is not the ultimate in academic rigor. And politically, it has something to offend and trouble almost everyone. But hey, I'm told it's readable, and at least the price is right. You can upload the book onto bulletin board systems, or Internet nodes, or electronic discussion groups. Go right ahead and do that, I am giving you express permission right now. Enjoy yourself. You can put the book on disks and give the disks away, as long as you don't take any money for it. But this book is not public domain. You can't copyright it in your own name. I own the copyright. Attempts to pirate this book and make money from selling it may involve you in a serious litigative snarl. Believe me, for the pittance you might wring out of such an action, it's really not worth it. This book don't "belong" to you. In an odd but very genuine way, I feel it doesn't "belong" to me, either. It's a book about the people of cyberspace, and distributing it in this way is the best way I know to actually make this information available, freely and easily, to all the people of cyberspace -- including people far outside the borders of the United States, who otherwise may never have a chance to see any edition of the book, and who may perhaps learn something useful from this strange story of distant, obscure, but portentous events in so-called "American cyberspace." This electronic book is now literary freeware. It now belongs to the emergent realm of alternative information economics. You have no right to make this electronic book part of the conventional flow of commerce. Let it be part of the flow of knowledge: there's a difference. I've divided the book into four sections, so that it is less ungainly for upload and download; if there's a section of particular relevance to you and your colleagues, feel free to reproduce that one and skip the rest. Just make more when you need them, and give them to whoever might want them. Now have fun. ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/index.txt0000600000175000017500000000006510246051200030316 0ustar madduckmadduckrestindex index-file: hack_preface.txt /restindexrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_chronology.txt0000600000175000017500000001115710246051200032364 0ustar madduckmadduckrestindex crumb: Chronology /restindex ==================================== The Hacker Crackdown - Chronology ==================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html Chronology of the Hacker Crackdown ====================================== **1865** U.S. Secret Service (USSS) founded. **1876** Alexander Graham Bell invents telephone. **1878** First teenage males flung off phone system by enraged authorities. **1939** "Futurian" science-fiction group raided by Secret Service. **1971** Yippie phone phreaks start YIPL/TAP magazine. **1972** *Ramparts* magazine seized in blue-box rip-off scandal. **1978** Ward Christenson and Randy Suess create first personal computer bulletin board system. **1982** William Gibson coins term "cyberspace." "414 Gang" raided. **1983** AT&T dismantled in divestiture. **1984** Congress passes Comprehensive Crime Control Act giving USSS jurisdiction over credit card fraud and computer fraud. "Legion of Doom" formed. *2600: The Hacker Quarterly* founded. *Whole Earth Software Catalog* published. **1985** First police "sting" bulletin board systems established. Whole Earth 'Lectronic Link computer conference (WELL) goes on-line. **1986** Computer Fraud and Abuse Act passed. Electronic Communications Privacy Act passed. **1987** Chicago prosecutors form Computer Fraud and Abuse Task Force. **1988** July. Secret Service covertly videotapes "SummerCon" hacker convention. September. "Prophet" cracks BellSouth AIMSX computer network and downloads E911 Document to his own computer and to Jolnet. September. AT&T Corporate Information Security informed of Prophet's action. October. Bellcore Security informed of Prophet's action. **1989** January. Prophet uploads E911 Document to Knight Lightning. February 25. Knight Lightning publishes E911 Document in Phrack electronic newsletter. May. Chicago Task Force raids and arrests "Kyrie." June. "NuPrometheus League" distributes Apple Computer proprietary software. June 13. Florida probation office crossed with phone-sex line in switching-station stunt. July. "Fry Guy" raided by USSS and Chicago Computer Fraud and Abuse Task Force. Secret Service raids "Prophet," "Leftist," and "Urvile" in Georgia. **1990** January. 15. Martin Luther King Day Crash strikes AT&T long-distance network nationwide. January 18-19 Chicago Task Force raids Knight Lightning in St. Louis. January 24. USSS and New York State Police raid "Phiber Optik," "Acid Phreak," and "Scorpion" in New York City. February 1. USSS raids "Terminus" in Maryland. February 3. Chicago Task Force raids Richard Andrews' home. February 6. Chicago Task Force raids Richard Andrews' business. February 6. USSS arrests Terminus, Prophet, Leftist, and Urvile. February 9. Chicago Task Force arrests Knight Lightning. February 20. AT&T Security shuts down public-access "attctc" computer in Dallas. February 21. Chicago Task Force raids Robert Izenberg in Austin. March 1. Chicago Task Force raids Steve Jackson Games, Inc., "Mentor," and "Erik Bloodaxe" in Austin. May 7,8,9. USSS and Arizona Organized Crime and Racketeering Bureau conduct "Operation Sundevil" raids in Cincinnatti, Detroit, Los Angeles, Miami, Newark, Phoenix, Pittsburgh, Richmond, Tucson, San Diego, San Jose, and San Francisco. May. FBI interviews John Perry Barlow re NuPrometheus case. June. Mitch Kapor and Barlow found Electronic Frontier Foundation; Barlow publishes Crime and Puzzlement manifesto. July 24-27. Trial of Knight Lightning. **1991** February. CPSR Roundtable in Washington, D.C. March 25-28. Computers, Freedom and Privacy conference in San Francisco. May 1. Electronic Frontier Foundation, Steve Jackson, and others file suit against members of Chicago Task Force. July 1-2. Switching station phone software crash affects Washington, Los Angeles, Pittsburgh, San Francisco. September 17. AT&T phone crash affects New York City and three airports. ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_afterword.txt0000600000175000017500000005474110246051200032204 0ustar madduckmadduckrestindex crumb: Hacker After /restindex ====================================== The Hacker Crackdown - The Afterword ====================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html Afterword: The Hacker Crackdown Three Years Later ==================================================== Three years in cyberspace is like thirty years anyplace real. It feels as if a generation has passed since I wrote this book. In terms of the generations of computing machinery involved, that's pretty much the case. The basic shape of cyberspace has changed drastically since 1990. A new U.S. Administration is in power whose personnel are, if anything, only too aware of the nature and potential of electronic networks. It's now clear to all players concerned that the status quo is dead-and-gone in American media and telecommunications, and almost any territory on the electronic frontier is up for grabs. Interactive multimedia, cable-phone alliances, the Information Superhighway, fiber-to-the-curb, laptops and palmtops, the explosive growth of cellular and the Internet -- the earth trembles visibly. The year 1990 was not a pleasant one for AT&T. By 1993, however, AT&T had successfully devoured the computer company NCR in an unfriendly takeover, finally giving the pole-climbers a major piece of the digital action. AT&T managed to rid itself of ownership of the troublesome UNIX operating system, selling it to Novell, a netware company, which was itself preparing for a savage market dust-up with operating-system titan Microsoft. Furthermore, AT&T acquired McCaw Cellular in a gigantic merger, giving AT&T a potential wireless whip-hand over its former progeny, the RBOCs. The RBOCs themselves were now AT&T's clearest potential rivals, as the Chinese firewalls between regulated monopoly and frenzied digital entrepreneurism began to melt and collapse headlong. AT&T, mocked by industry analysts in 1990, was reaping awestruck praise by commentators in 1993. AT&T had managed to avoid any more major software crashes in its switching stations. AT&T's newfound reputation as "the nimble giant" was all the sweeter, since AT&T's traditional rival giant in the world of multinational computing, IBM, was almost prostrate by 1993. IBM's vision of the commercial computer-network of the future, "Prodigy," had managed to spend $900 million without a whole heck of a lot to show for it, while AT&T, by contrast, was boldly speculating on the possibilities of personal communicators and hedging its bets with investments in handwritten interfaces. In 1990 AT&T had looked bad; but in 1993 AT&T looked like the future. At least, AT&T's \*advertising\* looked like the future. Similar public attention was riveted on the massive $22 billion megamerger between RBOC Bell Atlantic and cable-TV giant Tele-Communications Inc. Nynex was buying into cable company Viacom International. BellSouth was buying stock in Prime Management, Southwestern Bell acquiring a cable company in Washington DC, and so forth. By stark contrast, the Internet, a noncommercial entity which officially did not even exist, had no advertising budget at all. And yet, almost below the level of governmental and corporate awareness, the Internet was stealthily devouring everything in its path, growing at a rate that defied comprehension. Kids who might have been eager computer-intruders a mere five years earlier were now surfing the Internet, where their natural urge to explore led them into cyberspace landscapes of such mindboggling vastness that the very idea of hacking passwords seemed rather a waste of time. By 1993, there had not been a solid, knock 'em down, panic-striking, teenage-hacker computer-intrusion scandal in many long months. There had, of course, been some striking and well-publicized acts of illicit computer access, but they had been committed by adult white-collar industry insiders in clear pursuit of personal or commercial advantage. The kids, by contrast, all seemed to be on IRC, Internet Relay Chat. Or, perhaps, frolicking out in the endless glass-roots network of personal bulletin board systems. In 1993, there were an estimated 60,000 boards in America; the population of boards had fully doubled since Operation Sundevil in 1990. The hobby was transmuting fitfully into a genuine industry. The board community were no longer obscure hobbyists; many were still hobbyists and proud of it, but board sysops and advanced board users had become a far more cohesive and politically aware community, no longer allowing themselves to be obscure. The specter of cyberspace in the late 1980s, of outwitted authorities trembling in fear before teenage hacker whiz-kids, seemed downright antiquated by 1993. Law enforcement emphasis had changed, and the favorite electronic villain of 1993 was not the vandal child, but the victimizer of children, the digital child pornographer. "Operation Longarm," a child-pornography computer raid carried out by the previously little-known cyberspace rangers of the U.S. Customs Service, was almost the size of Operation Sundevil, but received very little notice by comparison. The huge and well-organized "Operation Disconnect," an FBI strike against telephone rip-off con-artists, was actually larger than Sundevil. "Operation Disconnect" had its brief moment in the sun of publicity, and then vanished utterly. It was unfortunate that a law-enforcement affair as apparently well-conducted as Operation Disconnect, which pursued telecom adult career criminals a hundred times more morally repugnant than teenage hackers, should have received so little attention and fanfare, especially compared to the abortive Sundevil and the basically disastrous efforts of the Chicago Computer Fraud and Abuse Task Force. But the life of an electronic policeman is seldom easy. If any law enforcement event truly deserved full-scale press coverage (while somehow managing to escape it), it was the amazing saga of New York State Police Senior Investigator Don Delaney Versus the Orchard Street Finger-Hackers. This story probably represents the real future of professional telecommunications crime in America. The finger-hackers sold, and still sell, stolen long-distance phone service to a captive clientele of illegal aliens in New York City. This clientele is desperate to call home, yet as a group, illegal aliens have few legal means of obtaining standard phone service, since their very presence in the United States is against the law. The finger-hackers of Orchard Street were very unusual "hackers," with an astonishing lack of any kind of genuine technological knowledge. And yet these New York call-sell thieves showed a street-level ingenuity appalling in its single-minded sense of larceny. There was no dissident-hacker rhetoric about freedom-of-information among the finger-hackers. Most of them came out of the cocaine-dealing fraternity, and they retailed stolen calls with the same street-crime techniques of lookouts and bagholders that a crack gang would employ. This was down-and-dirty, urban, ethnic, organized crime, carried out by crime families every day, for cash on the barrelhead, in the harsh world of the streets. The finger-hackers dominated certain payphones in certain strikingly unsavory neighborhoods. They provided a service no one else would give to a clientele with little to lose. With such a vast supply of electronic crime at hand, Don Delaney rocketed from a background in homicide to teaching telecom crime at FLETC in less than three years. Few can rival Delaney's hands-on, street-level experience in phone fraud. Anyone in 1993 who still believes telecommunications crime to be something rare and arcane should have a few words with Mr Delaney. Don Delaney has also written two fine essays, on telecom fraud and computer crime, in Joseph Grau's *Criminal and Civil Investigations Handbook* (McGraw Hill 1993). *Phrack* was still publishing in 1993, now under the able editorship of Erik Bloodaxe. Bloodaxe made a determined attempt to get law enforcement and corporate security to pay real money for their electronic copies of *Phrack,* but, as usual, these stalwart defenders of intellectual property preferred to pirate the magazine. Bloodaxe has still not gotten back any of his property from the seizure raids of March 1, 1990. Neither has the Mentor, who is still the managing editor of Steve Jackson Games. Nor has Robert Izenberg, who has suspended his court struggle to get his machinery back. Mr Izenberg has calculated that his $20,000 of equipment seized in 1990 is, in 1993, worth $4,000 at most. The missing software, also gone out his door, was long ago replaced. He might, he says, sue for the sake of principle, but he feels that the people who seized his machinery have already been discredited, and won't be doing any more seizures. And even if his machinery were returned -- and in good repair, which is doubtful -- it will be essentially worthless by 1995. Robert Izenberg no longer works for IBM, but has a job programming for a major telecommunications company in Austin. Steve Jackson won his case against the Secret Service on March 12, 1993, just over three years after the federal raid on his enterprise. Thanks to the delaying tactics available through the legal doctrine of "qualified immunity," Jackson was tactically forced to drop his suit against the individuals William Cook, Tim Foley, Barbara Golden and Henry Kluepfel. (Cook, Foley, Golden and Kluepfel did, however, testify during the trial.) The Secret Service fought vigorously in the case, battling Jackson's lawyers right down the line, on the (mostly previously untried) legal turf of the Electronic Communications Privacy Act and the Privacy Protection Act of 1980. The Secret Service denied they were legally or morally responsible for seizing the work of a publisher. They claimed that (1) Jackson's gaming "books" weren't real books anyhow, and (2) the Secret Service didn't realize SJG Inc was a "publisher" when they raided his offices, and (3) the books only vanished by accident because they merely happened to be inside the computers the agents were appropriating. The Secret Service also denied any wrongdoing in reading and erasing all the supposedly "private" e-mail inside Jackson's seized board, Illuminati. The USSS attorneys claimed the seizure did not violate the Electronic Communications Privacy Act, because they weren't actually "intercepting" electronic mail that was moving on a wire, but only electronic mail that was quietly sitting on a disk inside Jackson's computer. They also claimed that USSS agents hadn't read any of the private mail on Illuminati; and anyway, even supposing that they had, they were allowed to do that by the subpoena. The Jackson case became even more peculiar when the Secret Service attorneys went so far as to allege that the federal raid against the gaming company had actually \*improved Jackson's business\* thanks to the ensuing nationwide publicity. It was a long and rather involved trial. The judge seemed most perturbed, not by the arcane matters of electronic law, but by the fact that the Secret Service could have avoided almost all the consequent trouble simply by giving Jackson his computers back in short order. The Secret Service easily could have looked at everything in Jackson's computers, recorded everything, and given the machinery back, and there would have been no major scandal or federal court suit. On the contrary, everybody simply would have had a good laugh. Unfortunately, it appeared that this idea had never entered the heads of the Chicago-based investigators. They seemed to have concluded unilaterally, and without due course of law, that the world would be better off if Steve Jackson didn't have computers. Golden and Foley claimed that they had both never even heard of the Privacy Protection Act. Cook had heard of the Act, but he'd decided on his own that the Privacy Protection Act had nothing to do with Steve Jackson. The Jackson case was also a very politicized trial, both sides deliberately angling for a long-term legal precedent that would stake-out big claims for their interests in cyberspace. Jackson and his EFF advisors tried hard to establish that the least e-mail remark of the lonely electronic pamphleteer deserves the same somber civil-rights protection as that afforded *The New York Times*. By stark contrast, the Secret Service's attorneys argued boldly that the contents of an electronic bulletin board have no more expectation of privacy than a heap of postcards. In the final analysis, very little was firmly nailed down. Formally, the legal rulings in the Jackson case apply only in the federal Western District of Texas. It was, however, established that these were real civil-liberties issues that powerful people were prepared to go to the courthouse over; the seizure of bulletin board systems, though it still goes on, can be a perilous act for the seizer. The Secret Service owes Steve Jackson $50,000 in damages, and a thousand dollars each to three of Jackson's angry and offended board users. And Steve Jackson, rather than owning the single-line bulletin board system "Illuminati" seized in 1990, now rejoices in possession of a huge privately-owned Internet node, "io.com," with dozens of phone-lines on its own T-1 trunk. Jackson has made the entire blow-by-blow narrative of his case available electronically, for interested parties. And yet, the Jackson case may still not be over; a Secret Service appeal seems likely and the EFF is also gravely dissatisfied with the ruling on electronic interception. The WELL, home of the American electronic civil libertarian movement, added two thousand more users and dropped its aging Sequent computer in favor of a snappy new Sun Sparcstation. Search-and-seizure dicussions on the WELL are now taking a decided back-seat to the current hot topic in digital civil liberties, unbreakable public-key encryption for private citizens. The Electronic Frontier Foundation left its modest home in Boston to move inside the Washington Beltway of the Clinton Administration. Its new executive director, ECPA pioneer and longtime ACLU activist Jerry Berman, gained a reputation of a man adept as dining with tigers, as the EFF devoted its attention to networking at the highest levels of the computer and telecommunications industry. EFF's pro-encryption lobby and anti-wiretapping initiative were especially impressive, successfully assembling a herd of highly variegated industry camels under the same EFF tent, in open and powerful opposition to the electronic ambitions of the FBI and the NSA. EFF had transmuted at light-speed from an insurrection to an institution. EFF Co-Founder Mitch Kapor once again sidestepped the bureaucratic consequences of his own success, by remaining in Boston and adapting the role of EFF guru and gray eminence. John Perry Barlow, for his part, left Wyoming, quit the Republican Party, and moved to New York City, accompanied by his swarm of cellular phones. Mike Godwin left Boston for Washington as EFF's official legal adviser to the electronically afflicted. After the Neidorf trial, Dorothy Denning further proved her firm scholastic independence-of-mind by speaking up boldly on the usefulness and social value of federal wiretapping. Many civil libertarians, who regarded the practice of wiretapping with deep occult horror, were crestfallen to the point of comedy when nationally known "hacker sympathizer" Dorothy Denning sternly defended police and public interests in official eavesdropping. However, no amount of public uproar seemed to swerve the "quaint" Dr. Denning in the slightest. She not only made up her own mind, she made it up in public and then stuck to her guns. In 1993, the stalwarts of the Masters of Deception, Phiber Optik, Acid Phreak and Scorpion, finally fell afoul of the machineries of legal prosecution. Acid Phreak and Scorpion were sent to prison for six months, six months of home detention, 750 hours of community service, and, oddly, a $50 fine for conspiracy to commit computer crime. Phiber Optik, the computer intruder with perhaps the highest public profile in the entire world, took the longest to plead guilty, but, facing the possibility of ten years in jail, he finally did so. He was sentenced to a year and a day in prison. As for the Atlanta wing of the Legion of Doom, Prophet, Leftist and Urvile... Urvile now works for a software company in Atlanta. He is still on probation and still repaying his enormous fine. In fifteen months, he will once again be allowed to own a personal computer. He is still a convicted federal felon, but has not had any legal difficulties since leaving prison. He has lost contact with Prophet and Leftist. Unfortunately, so have I, though not through lack of honest effort. Knight Lightning, now 24, is a technical writer for the federal government in Washington DC. He has still not been accepted into law school, but having spent more than his share of time in the company of attorneys, he's come to think that maybe an MBA would be more to the point. He still owes his attorneys $30,000, but the sum is dwindling steadily since he is manfully working two jobs. Knight Lightning customarily wears a suit and tie and carries a valise. He has a federal security clearance. Unindicted *Phrack* co-editor Taran King is also a technical writer in Washington DC, and recently got married. Terminus did his time, got out of prison, and currently lives in Silicon Valley where he is running a full-scale Internet node, "netsys.com." He programs professionally for a company specializing in satellite links for the Internet. Carlton Fitzpatrick still teaches at the Federal Law Enforcement Training Center, but FLETC found that the issues involved in sponsoring and running a bulletin board system are rather more complex than they at first appear to be. Gail Thackeray briefly considered going into private security, but then changed tack, and joined the Maricopa County District Attorney's Office (with a salary). She is still vigorously prosecuting electronic racketeering in Phoenix, Arizona. The fourth consecutive Computers, Freedom and Privacy Conference will take place in March 1994 in Chicago. As for Bruce Sterling... well \*8-). I thankfully abandoned my brief career as a true-crime journalist and wrote a new science fiction novel, *Heavy Weather*, and assembled a new collection of short stories, *Globalhead*. I also write nonfiction regularly, for the popular-science column in *The Magazine of Fantasy and Science Fiction*. I like life better on the far side of the boundary between fantasy and reality; but I've come to recognize that reality has an unfortunate way of annexing fantasy for its own purposes. That's why I'm on the Police Liaison Committee for EFF-Austin, a local electronic civil liberties group (eff-austin@tic.com). I don't think I will ever get over my experience of the Hacker Crackdown, and I expect to be involved in electronic civil liberties activism for the rest of my life. It wouldn't be hard to find material for another book on computer crime and civil liberties issues. I truly believe that I could write another book much like this one, every year. Cyberspace is very big. There's a lot going on out there, far more than can be adequately covered by the tiny, though growing, cadre of network-literate reporters. I do wish I could do more work on this topic, because the various people of cyberspace are an element of our society that definitely requires sustained study and attention. But there's only one of me, and I have a lot on my mind, and, like most science fiction writers, I have a lot more imagination than discipline. Having done my stint as an electronic-frontier reporter, my hat is off to those stalwart few who do it every day. I may return to this topic some day, but I have no real plans to do so. However, I didn't have any real plans to write "Hacker Crackdown," either. Things happen, nowadays. There are landslides in cyberspace. I'll just have to try and stay alert and on my feet. The electronic landscape changes with astounding speed. We are living through the fastest technological transformation in human history. I was glad to have a chance to document cyberspace during one moment in its long mutation; a kind of strobe-flash of the maelstrom. This book is already out-of-date, though, and it will be quite obsolete in another five years. It seems a pity. However, in about fifty years, I think this book might seem quite interesting. And in a hundred years, this book should seem mind-bogglingly archaic and bizarre, and will probably seem far weirder to an audience in 2092 than it ever seemed to the contemporary readership. Keeping up in cyberspace requires a great deal of sustained attention. Personally, I keep tabs with the milieu by reading the invaluable electronic magazine Computer underground Digest (tk0jut2@mvs.cso.niu.edu with the subject header: SUB CuD and a message that says: SUB CuD your name your.full.internet@address). I also read Jack Rickard's bracingly iconoclastic *Boardwatch Magazine* for print news of the BBS and online community. And, needless to say, I read *Wired*, the first magazine of the 1990s that actually looks and acts like it really belongs in this decade. There are other ways to learn, of course, but these three outlets will guide your efforts very well. When I myself want to publish something electronically, which I'm doing with increasing frequency, I generally put it on the gopher at Texas Internet Consulting, who are my, well, Texan Internet consultants (tic.com). This book can be found there. I think it is a worthwhile act to let this work go free. From thence, one's bread floats out onto the dark waters of cyberspace, only to return someday, tenfold. And of course, thoroughly soggy, and riddled with an entire amazing ecosystem of bizarre and gnawingly hungry cybermarine life-forms. For this author at least, that's all that really counts. Thanks for your attention \*8-) | *Bruce Sterling* | bruces@well.sf.ca.us *New Years' Day 1994, Austin Texas* *Literary Freeware: Not for Commercial Use* *Copyright (c) 1992, 1994 Bruce Sterling - bruces@well.sf.ca.us.* *Permission is granted to make and distribute verbatim copies of this publication provided the copyright notice and this permission notice are preserved on all copies.* ----------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/hacker_crackdown/hack_intro.txt0000600000175000017500000001400410246051200031326 0ustar madduckmadduckrestindex crumb: Hacker Intro /restindex ========================================== The Hacker Crackdown - The Introduction ========================================== --------------------- By Bruce Sterling --------------------- * Preface_ * Introduction_ * `Part One`_ * `Part Two`_ * `Part Three`_ * `Part Four`_ * Afterword_ * Chronology_ .. _Preface: hack_preface.html .. _Introduction: hack_intro.html .. _Part One: hack_part1.html .. _Part Two: hack_part2.html .. _Part Three: hack_part3.html .. _Part Four: hack_part4.html .. _Afterword: hack_afterword.html .. _Chronology: hack_chronology.html Introduction ============== This is a book about cops, and wild teenage whiz-kids, and lawyers, and hairy-eyed anarchists, and industrial technicians, and hippies, and high-tech millionaires, and game hobbyists, and computer security experts, and Secret Service agents, and grifters, and thieves. This book is about the electronic frontier of the 1990s. It concerns activities that take place inside computers and over telephone lines. A science fiction writer coined the useful term "cyberspace" in 1982. But the territory in question, the electronic frontier, is about a hundred and thirty years old. Cyberspace is the "place" where a telephone conversation appears to occur. Not inside your actual phone, the plastic device on your desk. Not inside the other person's phone, in some other city. The place between the phones. The indefinite place out there, where the two of you, two human beings, actually meet and communicate. Although it is not exactly "real," "cyberspace" is a genuine place. Things happen there that have very genuine consequences. This "place" is not "real," but it is serious, it is earnest. Tens of thousands of people have dedicated their lives to it, to the public service of public communication by wire and electronics. People have worked on this "frontier" for generations now. Some people became rich and famous from their efforts there. Some just played in it, as hobbyists. Others soberly pondered it, and wrote about it, and regulated it, and negotiated over it in international forums, and sued one another about it, in gigantic, epic court battles that lasted for years. And almost since the beginning, some people have committed crimes in this place. But in the past twenty years, this electrical "space," which was once thin and dark and one-dimensional -- little more than a narrow speaking-tube, stretching from phone to phone -- has flung itself open like a gigantic jack-inthe- box. Light has flooded upon it, the eerie light of the glowing computer screen. This dark electric netherworld has become a vast flowering electronic landscape. Since the 1960s, the world of the telephone has cross-bred itself with computers and television, and though there is still no substance to cyberspace, nothing you can handle, it has a strange kind of physicality now. It makes good sense today to talk of cyberspace as a place all its own. Because people live in it now. Not just a few people, not just a few technicians and eccentrics, but thousands of people, quite normal people. And not just for a little while, either, but for hours straight, over weeks, and months, and years. Cyberspace today is a "Net," a "Matrix," international in scope and growing swiftly and steadily. It's growing in size, and wealth, and political importance. People are making entire careers in modern cyberspace. Scientists and technicians, of course; they've been there for twenty years now. But increasingly, cyberspace is filling with journalists and doctors and lawyers and artists and clerks. Civil servants make their careers there now, "on- line" in vast government databanks; and so do spies, industrial, political, and just plain snoops; and so do police, at least a few of them. And there are children living there now. People have met there and been married there. There are entire living communities in cyberspace today; chattering, gossipping, planning, conferring and scheming, leaving one another voice-mail and electronic mail, giving one another big weightless chunks of valuable data, both legitimate and illegitimate. They busily pass one another computer software and the occasional festering computer virus. We do not really understand how to live in cyberspace yet. We are feeling our way into it, blundering about. That is not surprising. Our lives in the physical world, the "real" world, are also far from perfect, despite a lot more practice. Human lives, real lives, are imperfect by their nature, and there are human beings in cyberspace. The way we live in cyberspace is a funhouse mirror of the way we live in the real world. We take both our advantages and our troubles with us. This book is about trouble in cyberspace. Specifically, this book is about certain strange events in the year 1990, an unprecedented and startling year for the the growing world of computerized communications. In 1990 there came a nationwide crackdown on illicit computer hackers, with arrests, criminal charges, one dramatic show-trial, several guilty pleas, and huge confiscations of data and equipment all over the USA. The Hacker Crackdown of 1990 was larger, better organized, more deliberate, and more resolute than any previous effort in the brave new world of computer crime. The U.S. Secret Service, private telephone security, and state and local law enforcement groups across the country all joined forces in a determined attempt to break the back of America's electronic underground. It was a fascinating effort, with very mixed results. The Hacker Crackdown had another unprecedented effect; it spurred the creation, within "the computer community," of the Electronic Frontier Foundation, a new and very odd interest group, fiercely dedicated to the establishment and preservation of electronic civil liberties. The crackdown, remarkable in itself, has created a melee of debate over electronic crime, punishment, freedom of the press, and issues of search and seizure. Politics has entered cyberspace. Where people go, politics follow. This is the story of the people of cyberspace. rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/0000700000175000017500000000000010644674642024454 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/4.txt0000600000175000017500000001747310246051200025347 0ustar madduckmadduckrestindex format: html page-title: Early Insights from the Human DNA Sequence crumb: Early Insights /restindex
    Early Insights from the Human DNA Sequence

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    What We've Learned Thus Far
    The first panoramic views of the human genetic landscape have revealed a wealth of information and some early surprises. Much remains to be deciphered in this vast trove of information; as the consortium of HGP scientists concluded in their seminal paper, “. . .the more we learn about the human genome, the more there is to explore.” A few highlights from the first publications analyzing the sequence follow.

    • The human genome contains 3.2 billion chemical nucleotide bases (A, C, T, and G).
    • The average gene consists of 3000 bases, but sizes vary greatly, with the largest known human gene being dystrophin at 2.4 million bases.
    • The functions are unknown for more than 50% of discovered genes.
    • The human genome sequence is almost (99.9%) exactly the same in all people.
    • About 2% of the genome encodes instructions for the synthesis of proteins.
    • Repeat sequences that do not code for proteins make up at least 50% of the human genome.
    • Repeat sequences are thought to have no direct functions, but they shed light on chromosome structure and dynamics. Over time, these repeats reshape the genome by rearranging it, thereby creating entirely new genes or modifying and reshuffling existing genes.
    • The human genome has a much greater portion (50%) of repeat sequences than the mustard weed (11%), the worm (7%), and the fly (3%).
    • Over 40% of the predicted human proteins share similarity with fruit-fly or worm proteins.
    • Genes appear to be concentrated in random areas along the genome, with vast expanses of noncoding DNA between.
    • Chromosome 1 (the largest human chromosome) has the most genes (2968), and the Y chromosome has the fewest (231).
    • Genes have been pinpointed and particular sequences in those genes associated with numerous diseases and disorders including breast cancer, muscle disease, deafness, and blindness.
    • Scientists have identified about 3 million locations where single-base DNA differences occur in humans. This information promises to revolutionize the processes of finding DNA sequences associated with such common diseases as cardiovascular disease, diabetes, arthritis, and cancers.
    Organism Genome Size (Bases) Estimated
    Genes
    Human (Homo sapiens) 3.2 billion 30,000 to 40,000
    Laboratory mouse (M. musculus) 2.6 billion 30,000
    Mustard weed (A. thaliana) 100 million 25,000
    Roundworm (C. elegans) 97 million 19,000
    Fruit fly (D. melanogaster) 137 million 13,000
    Yeast (S. cerevisiae) 12.1 million 6,000
    Bacterium (E. coli) 4.6 million 3,200
    Human immunodeficiency virus (HIV) 9700 9

    The estimated number of human genes is only one-third as great as previously thought, although the numbers may be revised as more computational and experimental analyses are performed.

    Scientists suggest that the genetic key to human complexity lies not in gene number but in how gene parts are used to build different products in a process called alternative splicing. Other underlying reasons for greater complexity are the thousands of chemical modifications made to proteins and the repertoire of regulatory mechanisms controlling these processes.


    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/1.txt0000600000175000017500000001556010246051200025337 0ustar madduckmadduckrestindex format: html page-title: Genomics 101: A Primer crumb: Genomics 101 /restindex
    Genomics 101: A Primer

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    Cells are the fundamental working units of every living system. All the instructions needed to direct their activities are contained within the chemical DNA (deoxyribonucleic acid).

    DNA from all organisms is made up of the same chemical and physical components. The DNA sequence is the particular side-by-side arrangement of bases along the DNA strand (e.g., ATTCCGGA). This order spells out the exact instructions required to create a particular organism with its own unique traits.

    The genome is an organism’s complete set of DNA. Genomes vary widely in size: the smallest known genome for a free-living organism (a bacterium) contains about 600,000 DNA base pairs, while human and mouse genomes have some 3 billion (see "Early Insights"). Except for mature red blood cells, all human cells contain a complete genome.

    DNA in the human genome is arranged into 24 distinct chromosomes--physically separate molecules that range in length from about 50 million to 250 million base pairs. A few types of major chromosomal abnormalities, including missing or extra copies or gross breaks and rejoinings (translocations), can be detected by microscopic examination. Most changes in DNA, however, are more subtle and require a closer analysis of the DNA molecule to find perhaps single-base differences.

    Each chromosome contains many genes, the basic physical and functional units of heredity. Genes are specific sequences of bases that encode instructions on how to make proteins. Genes comprise only about 2% of the human genome; the remainder consists of noncoding regions, whose functions may include providing chromosomal structural integrity and regulating where, when, and in what quantity proteins are made. The human genome is estimated to contain 30,000 to 40,000 genes.

    Although genes get a lot of attention, it’s the proteins that perform most life functions and even make up the majority of cellular structures. Proteins are large, complex molecules made up of smaller subunits called amino acids. Chemical properties that distinguish the 20 different amino acids cause the protein chains to fold up into specific three-dimensional structures that define their particular functions in the cell.

    The constellation of all proteins in a cell is called its proteome. Unlike the relatively unchanging genome, the dynamic proteome changes from minute to minute in response to tens of thousands of intra- and extracellular environmental signals. A protein’s chemistry and behavior are specified by the gene sequence and by the number and identities of other proteins made in the same cell at the same time and with which it associates and reacts. Studies to explore protein structure and activities, known as proteomics, will be the focus of much research for decades to come and will help elucidate the molecular basis of health and disease.

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/2.txt0000600000175000017500000002020210246051200025325 0ustar madduckmadduckrestindex format: html page-title: The Human Genome Project, 1990-2003 crumb: The Project /restindex
    The Human Genome Project, 1990-2003

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    A Brief Overview
    Though surprising to many, the Human Genome Project (HGP) traces its roots to an initiative in the U.S. Department of Energy (DOE). Since 1947, DOE and its predecessor agencies have been charged by Congress with developing new energy resources and technologies and pursuing a deeper understanding of potential health and environmental risks posed by their production and use. Such studies, for example, have provided the scientific basis for individual risk assessments of nuclear medicine technologies.

    In 1986, DOE took a bold step in announcing the Human Genome Initiative, convinced that its missions would be well served by a reference human genome sequence. Shortly thereafter, DOE joined with the National Institutes of Health (NIH) to develop a plan for a joint HGP that officially began in 1990. During the early years of the HGP, the Wellcome Trust, a private charitable institution in the United Kingdom, joined the effort as a major partner. Important contributions also came from other collaborators around the world, including Japan, France, Germany, and China.

    Ambitious Goals
    The HGP’s ultimate goal was to generate a high-quality reference DNA sequence for the human genome‘s 3 billion base pairs and to identify all human genes. Other important goals included sequencing the genomes of model organisms to interpret human DNA, enhancing computational resources to support future research and commercial applications, exploring gene function through mouse-human comparisons, studying human variation, and training future scientists in genomics.

    The powerful analytic technology and data arising from the HGP raise complex ethical and policy issues for individuals and society. These challenges include privacy, fairness in use and access of genomic information, reproductive and clinical issues, and commercialization (see p. 8). Programs that identify and address these implications have been an integral part of the HGP and have become a model for bioethics programs worldwide.

    A Lasting Legacy

    In June 2000, to much excitement and fanfare, scientists announced the completion of the first working draft of the entire human genome. First analyses of the details appeared in the February 2001 issues of the journals Nature and Science. The high-quality reference sequence was completed in April 2003, marking the end of the Human Genome Project—2 years ahead of the original schedule. Coincidentally, this was also the 50th anniversary of Watson and Crick’s publication of DNA structure that launched the era of molecular biology.

    Available to researchers worldwide, the human genome reference sequence provides a magnificent and unprecedented biological resource that will serve throughout the century as a basis for research and discovery and, ultimately, myriad practical applications. The sequence already is having an impact on finding genes associated with human disease (see p. 3). Hundreds of other genome sequence projects—on microbes, plants, and animals—have been completed since the inception of the HGP, and these data now enable detailed comparisons among organisms, including humans.

    Many more sequencing projects are under way or planned because of the research value of DNA sequence, the tremendous sequencing capacity now available, and continued improvements in technologies. Sequencing projects on the genomes of many microbes, as well as the honeybee, cow, and chicken are in progress.

    Beyond sequencing, growing areas of research focus on identifying important elements in the DNA sequence responsible for regulating cellular functions and providing the basis of human variation. Perhaps the most daunting challenge is to begin to understand how all the “parts” of cells—genes, proteins, and many other molecules—work together to create complex living organisms. Future analyses on this treasury of data will provide a deeper and more comprehensive understanding of the molecular processes underlying life and will have an enduring and profound impact on how we view our own place in it.

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/index.txt0000600000175000017500000001320710246051200026302 0ustar madduckmadduckrestindex # The genome index page format: html section: science link-title: The Human Genome Project and Beyond page-title: The Human Genome Project and Beyond - The Index Page page-description: A very good introduction to genes and the genome project. Genomics and Its Impact on Science and Society. The Human Genome Project and Beyond - a publication of the U.S. Department of Energy Human Genome Program. This is the result of a study of human DNA - called the *Human Genome Project*. The goal was nothing less than producing a high-quality reference DNA sequence for the human genomes 3 billion base pairs and to identify **all** human genes. This report, from 2003, is a good primer on the whole subject of genes and genetics. It also examines the possible impact on society of future research. /description crumb: Genome Index /restindex
    Genomics and Its Impact on Science and Society
    The Human Genome Project and Beyond
    a publication of the U.S. Department of Energy Human Genome Program
     
    March 2003
      Use the buttons to navigate between chapters and the button to return to this table of contents.
     
       
    Genomics 101: A Primer
    The Human Genome Project: 1990-2003
    Early Insights from the Human DNA Sequence
    Medicine and the New Genetics
    Other Anticipated Benefits of Genetic Research
    Societal Concerns Arising from the New Genetics
    Beyond the Human Genome Project -- What's Next?
    Genomes to Life: A DOE Systems Biology Program
    Genomes to Life: Putting Microbes to Work
    Dictionary of Genetic Terms

    This document may be cited in the following style:
    Human Genome Program, U.S. Department of Energy, Genomics and Its Impact on Science and Society: A 2003 Primer, 2003.

    For printed copies, please contact Laura Yust at Oak Ridge National Laboratory. Send questions or comments to the author, Denise K. Casey. Site designed by Marissa Mills.
     

     
    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/3.txt0000600000175000017500000002007110246051200025332 0ustar madduckmadduckrestindex format: html page-title: Genomes to Life: A DOE Systems Biology Program crumb: Genomes to Life /restindex
    Genomes to Life: A DOE Systems Biology Program

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    Exploring Microbial Genomes for Energy and the Environment
    The remarkable successes of the Human Genome Project (HGP) and spin-offs revealing the details of hundreds of genomes provide the richest resource in the history of biology. The new Genomes to Life (GTL) program of the U.S. Department of Energy (DOE) builds on these successes by combining DNA sequence data with advanced technologies to explore the amazingly diverse natural capabilities of microbes—the invisible organisms that thrive in every known environment on earth. The ultimate goal is to understand and use their diverse functions to meet critical DOE mission challenges in energy security, global climate change, and toxic waste cleanup.

    Why Microbes?
    The ability of this planet to sustain life is largely dependent on microbes, most of which do not cause disease. Microbes are the foundation of the biosphere, controlling earth’s natural biogeochemical cycles and affecting the productivity of the soil, quality of water, and global climate. As one of the most exciting frontiers in biology today, microbial research is revealing the hidden architectures of life and the dynamic, life-sustaining processes they carry out on Earth. Although microbes are recognized masters at living in almost every environment and harvesting energy in almost any form, we know less than 1% of them. Their sophisticated biochemical capabilities can be used for transforming wastes and organic matter, cycling nutrients, and, as part of the photosynthetic process, converting sunlight into energy and storing CO2 from the atmosphere.

    GTL Scientific Challenges
    Although we now have the entire genome sequences for hundreds of microbes, we still have very little understanding of how the information in DNA creates, sustains, and reproduces living systems. Obtaining this knowledge, a critical first step in harnessing microbial functions, requires a comprehensive approach extending from individual cells to many cells functioning in communities. Such studies must encompass proteins, multimolecular assemblies (sometimes called “molecular machines”; see figure) of components that work together, the intricate labyrinth of pathways and networks in which they interact, and cells. The wealth of data to be collected must be assimilated, understood, and modeled on the scale and complexity of real living systems and processes.

    Large-Scale Technologies and Advanced Computing
    computingJust as DNA sequencing capability was completely inadequate at the beginning of the HGP, the quantity and complexity of data that must be collected and analyzed for systems biology research far exceed current capabilities and capacities. Dozens of advanced large-scale technologies and approaches must be developed, with mathematics and computing guiding the research questions and interpretation at every step. Computational tools must manage and integrate the data into mechanistic models that describe how cells work. These studies eventually will enable an integrated and predictive understanding of how living cells function and respond to environmental changes, opening the door to using microbial capabilities.

    technologyTo meet these challenges, DOE has planned four major research facilities that will make the most advanced technologies and computing resources available to the broader life sciences research community. Allowing new avenues of inquiry, this unique set of facilities will fundamentally change the course of biological research and greatly accelerate the pace of discovery. The facilities will provide scientists with the enduring and comprehensive ability to understand and, ultimately, reap enormous benefit from the functioning of microbial systems (see Genomes to Life).

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/6.txt0000600000175000017500000002705210246051200025343 0ustar madduckmadduckrestindex format: html page-title: Medicine and the New Genetics crumb: Medicine /restindex
    Medicine and the New Genetics

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond
     

    Gene Testing, Pharmacogenomics, and Gene Therapy
    CadeususDNA underlies almost every aspect of human health, both in function and dysfunction. Obtaining a detailed picture of how genes and other DNA sequences function together and interact with environmental factors ultimately will lead to the discovery of pathways involved in normal processes and in disease pathogenesis. Such knowledge will have a profound impact on the way disorders are diagnosed, treated, and prevented and will bring about revolutionary changes in clinical and public health practice. Some of these transformative developments are described below.

    Gene Testing
    DNA-based tests are among the first commercial medical applications of the new genetic discoveries. Gene tests can be used to diagnose disease, confirm a diagnosis, provide prognostic information about the course of disease, confirm the existence of a disease in asymptomatic individuals, and, with varying degrees of accuracy, predict the risk of future disease in healthy individuals or their progeny.

    Currently, several hundred genetic tests are in clinical use, with many more under development, and their numbers and varieties are expected to increase rapidly over the next decade. Most current tests detect mutations associated with rare genetic disorders that follow Mendelian inheritance patterns. These include myotonic and Duchenne muscular dystrophies, cystic fibrosis, neurofibromatosis type 1, sickle cell anemia, and Huntington’s disease.

    Recently, tests have been developed to detect mutations for a handful of more complex conditions such as breast, ovarian, and colon cancers. Although they have limitations, these tests sometimes are used to make risk estimates in presymptomatic individuals with a family history of the disorder. One potential benefit to using these gene tests is that they could provide information to help physicians and patients manage the disease or condition more effectively. Regular colonoscopies for those having mutations associated with colon cancer, for instance, could prevent thousands of deaths each year.

    Some scientific limitations are that the tests may not detect every mutation associated with a particular condition (many are as yet undiscovered), and the ones they do detect may present different risks to different people and populations. Another important consideration in gene testing is the lack of effective treatments or preventive measures for many diseases and conditions now being diagnosed or predicted.

    Revealing information about the risk of future disease can have significant emotional and psychological effects as well. Moreover, the absence of privacy and legal protections can lead to discrimination in employment and insurance or other misuse of personal genetic information. Additionally, because genetic tests reveal information about individuals and their families, test results can affect family dynamics. Results also can pose risks for population groups if they lead to group stigmatization.

    Other issues related to gene tests include their effective introduction into clinical practice, the regulation of laboratory quality assurance, the availability of testing for rare diseases, and the education of healthcare providers and patients about correct interpretation and attendant risks.

    Families or individuals who have genetic disorders or are at risk for them often seek help from medical geneticists (an M.D. specialty) and genetic counselors (graduate-degree training). These professionals can diagnose and explain disorders, review available options for testing and treatment, and provide emotional support. (For more information, see Medicine and the New Genetics)

    Pharmacogenomics: Moving Away from “One-Size-Fits-All” Therapeutics
    Within the next decade, researchers will begin to correlate DNA variants with individual responses to medical treatments, identify particular subgroups of patients, and develop drugs customized for those populations. The discipline that blends pharmacology with genomic capabilities is called pharmacogenomics.

    More than 100,000 people die each year from adverse responses to medications that may be beneficial to others. Another 2.2 million experience serious reactions, while others fail to respond at all. DNA variants in genes involved in drug metabolism, particularly the cytochrome P450 multigene family, are the focus of much current research in this area. Enzymes encoded by these genes are responsible for metabolizing most drugs used today, including many for treating psychiatric, neurological, and cardiovascular diseases. Enzyme function affects patient responses to both the drug and the dose. Future advances will enable rapid testing to determine the patient’s genotype and guide treatment with the most effective drugs, in addition to drastically reducing adverse reactions.

    Genomic data and technologies also are expected to make drug development faster, cheaper, and more effective. Most drugs today are based on about 500 molecular targets; genomic knowledge of the genes involved in diseases, disease pathways, and drug-response sites will lead to the discovery of thousands of new targets. New drugs, aimed at specific sites in the body and at particular biochemical events leading to disease, probably will cause fewer side effects than many current medicines. Ideally, the new genomic drugs could be given earlier in the disease process. As knowledge becomes available to select patients most likely to benefit from a potential drug, pharmaco-genomics will speed the design of clinical trials to bring the drugs to market sooner.

    Gene Therapy, Enhancement
    The potential for using genes themselves to treat disease or enhance particular traits has captured the imagination of the public and the biomedical community. This largely experimental field—gene transfer or gene therapy—holds potential for treating or even curing such genetic and acquired diseases as cancers and AIDS by using normal genes to supplement or replace defective genes or bolster a normal function such as immunity.

    More than 600 clinical gene-therapy trials involving about 3500 patients were identified worldwide in 2002.* The vast majority take place in the United States (81%), followed by Europe (16%). Although most trials focus on various types of cancer, studies also involve other multigenic and monogenic, infectious, and vascular diseases. Most current protocols are aimed at establishing the safety of gene-delivery procedures rather than effectiveness.

    Gene transfer still faces many scientific obstacles before it can become a practical approach for treating disease. According to the American Society of Human Genetics’ Statement on Gene Therapy, effective progress will be achieved only through continued rigorous research on the most fundamental mechanisms underlying gene delivery and gene expression in animals.


    *Source: Journal of Gene Medicine Web site (www.wiley.co.uk), accessed March 2003.

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/8.txt0000600000175000017500000001710510246051200025343 0ustar madduckmadduckrestindex format: html page-title: Societal Concerns Arising from the New Genetics crumb: Social Concerns /restindex
    Societal Concerns Arising from the New Genetics

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    Critical Policy and Ethical Issues
    Since its inception, the Human Genome Project has dedicated funds toward identifying and addressing the ethical, legal, and social issues surrounding the availability of the new data and capabilities. Examples of such issues follow.*

    • Privacy and confidentiality of genetic information. Who owns and controls genetic information? Is genetic privacy different from medical privacy?
    • Fairness in the use of genetic information by insurers, employers, courts, schools, adoption agencies, and the military, among others. Who should have access to personal genetic information, and how will it be used?
    • Psychological impact, stigmatization, and discrimination due to an individual's genetic makeup. How does personal genetic information affect self-identity and society's perceptions?
    • Reproductive issues including adequate and informed consent and the use of genetic information in reproductive decision making. Do healthcare personnel properly counsel parents about risks and limitations? What are the larger societal issues raised by new reproductive technologies?
    • Clinical issues including the education of doctors and other health-service providers, people identified with genetic conditions, and the general public; and the implementation of standards and quality-control measures. How should health professionals be prepared for the new genetics? How can the public be educated to make informed choices? How will genetic tests be evaluated and regulated for accuracy, reliability, and usefulness? (Currently, there is little regulation.) How does society balance current scientific limitations and social risk with long-term benefits?
    • Fairness in access to advanced genomic technologies. Who will benefit? Will there be major worldwide inequities?
    • Uncertainties associated with gene tests for susceptibilities and complex conditions (e.g., heart disease, diabetes, and Alzheimer's disease).Should testing be performed when no treatment is available or when interpretation is unsure? Should children be tested for susceptibility to adult-onset diseases?
    • Conceptual and philosophical implications regarding human responsibility, free will vs genetic determinism, and concepts of health and disease. Do our genes influence our behavior, and can we control it? What is considered acceptable diversity? Where is the line drawn between medical treatment and enhancement?
    • Health and environmental issues concerning genetically modified (GM) foods and microbes. Are GM foods and other products safe for humans and the environment? How will these technologies affect developing nations' dependence on industrialized nations?
    • Commercialization of products including property rights (patents, copyrights, and trade secrets) and accessibility of data and materials. Will patenting DNA sequences limit their accessibility and development into useful products?

    *For more information, see Ethical, Legal, and Social Issues

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/7.txt0000600000175000017500000002137310246051200025344 0ustar madduckmadduckrestindex format: html page-title: Other Anticipated Benefits of Genetic Research crumb: Benefits /restindex
    Other Anticipated Benefits of Genetic Research

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    Technologies, Resources Having Major Impacts
    Rapid progress in genome science and a glimpse into its potential applications have spurred observers to predict that biology will be the foremost science of the 21st Century. Technology and resources generated by the Human Genome Project and other genomic research already are having major impacts on research across the life sciences. Doubling in size in 10 years, the biotechnology industry generated 191,000 direct jobs and 535,000 indirect jobs in 2001. Revenues for that year totaled more than $20 billion directly and $28.5 billion indirectly.*

    A list of some current and potential applications of genome research follows. More studies and public discussion are required for eventual validation and implementation of some of these uses (see p. 8).

    Molecular Medicine

    • Improve diagnosis of disease
    • Detect genetic predispositions to disease
    • Create drugs based on molecular information
    • Use gene therapy and control systems as drugs
    • Design “custom drugs” based on individual genetic profiles

    Microbial Genomics

    • Rapidly detect and treat pathogens (disease-causing microbes) in clinical practice
    • Develop new energy sources (biofuels)
    • Monitor environments to detect pollutants
    • Protect citizenry from biological and chemical warfare
    • Clean up toxic waste safely and efficiently

    Risk Assessment

    • Evaluate the health risks faced by individuals who may be exposed to radiation (including low levels in industrial areas) and to cancer-causing chemicals and toxins

    Bioarchaeology, Anthropology, Evolution, and Human Migration

    • Study evolution through germline mutations in lineages
    • Study migration of different population groups based on maternal genetic inheritance
    • Study mutations on the Y chromosome to trace lineage and migration of males
    • Compare breakpoints in the evolution of mutations with ages of populations and historical events

    DNA Identification

    • Identify potential suspects whose DNA may match evidence left at crime scenes
    • Exonerate persons wrongly accused of crimes
    • Identify crime, catastrophe, and other victims
    • Establish paternity and other family relationships
    • Identify endangered and protected species as an aid to wildlife officials (could be used for prosecuting poachers)
    • Detect bacteria and other organisms that may pollute air, water, soil, and food
    • Match organ donors with recipients in transplant programs
    • Determine pedigree for seed or livestock breeds
    • Authenticate consumables such as caviar and wine

    Agriculture, Livestock Breeding, and Bioprocessing

    • Grow disease-, insect-, and drought-resistant crops
    • Breed healthier, more productive, disease-resistant farm animals
    • Grow more nutritious produce
    • Develop biopesticides
    • Incorporate edible vaccines into food products
    • Develop new environmental cleanup uses for plants like tobacco

     

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/9.txt0000600000175000017500000001502010246051200025336 0ustar madduckmadduckrestindex format: html page-title: Genomes to Life: Putting Microbes to Work crumb: Microbes /restindex
    Genomes to Life: Putting Microbes to Work

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond
    Methanococcus jannaschii
    Produces methane, an important energy source; contains enzymes that withstand high temperatures and pressures; possibly useful for industrial processes.
    Deinococcus radiodurans
    Survives extremely high levels of radiation and has high potential for radioactive waste cleanup.
    Thalassiosira pseudonana
    Ocean diatom that is major participant in biological pumping of carbon to ocean depths and has potential for mitigating global climate change.
    The DOE Microbial Genome Program
    (MGP) studies the DNA of microbes that may be useful in helping DOE fulfill its missions in energy production, environmental waste cleanup, and mitigation of the effects of global climate change. (For more information, see MGP Web site (www.ornl.gov/microbialgenomes/).

     

    A Potential Application of Knowledge Gained in GTL Research
    Learning about the inner workings of microbes can lead to discovery of ways to isolate and use their components to develop new, synthetic nanostructures that carry out some of the functions of living cells.

    In this figure, an enzyme (green) has been embedded in a synthetic membrane that enhances its activity and stability. The enzyme transforms toxic substances (purple molecules at left) to harmless by-products (yellow and red molecules at right). [C. Lei, Y. Shin, J. Liu, and E. J. Ackerman (Pacific Northwest National Laboratory), J. Am. Chem. Soc. 124, 11242–43 (2002)]

    The knowledge gained from Genomes to Life research could enable others to develop efficient enzyme-based ways to produce energy, remove or inactivate contaminants, and store carbon to mitigate global climate change. Other potential highly useful applications are food processing, pharmaceuticals, separations, and the production of industrial chemicals.

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/glossary.txt0000600000175000017500000032747310246051200027053 0ustar madduckmadduckrestindex format: html page-title: Dictionary of Genetic Terms crumb: Glossary /restindex
    Dictionary of Genetic Terms

    Genomics and Its Impact on Medicine and Society: A 2001 Primer

    A

    Acquired genetic mutation
    See: somatic cell genetic mutation
    Additive genetic effects
    When the combined effects of alleles at different loci are equal to the sum of their individual effects.
    See also: anticipation,complex trait
    Adenine (A)
    A nitrogenous base, one member of the base pair AT (adenine-thymine).
    See also: base pair,nucleotide
    Affected relative pair
    Individuals related by blood, each of whom is affected with the same trait. Examples are affected sibling, cousin, and avuncular pairs.
    See also: avuncular relationship
    Aggregation technique
    A technique used in model organism studies in which embryos at the 8-cell stage of development are pushed together to yield a single embryo (used as an alternative to microinjection).
    See also: model organisms
    Allele
    Alternative form of a genetic locus; a single allele for each locus is inherited from each parent (e.g., at a locus for eye color the allele might result in blue or brown eyes).
    See also: locus,gene expression
    Allogeneic
    Variation in alleles among members of the same species.
    Alternative splicing
    Different ways of combining a gene's exons to make variants of the complete protein
    Amino acid
    Any of a class of 20 molecules that are combined to form proteins in living things. The sequence of amino acids in a protein and hence protein function are determined by the genetic code.
    Amplification
    An increase in the number of copies of a specific DNA fragment; can be in vivo or in vitro.
    See also: cloning,polymerase chain reaction
    Animal model
    See: model organisms
    Annotation
    Adding pertinent information such as gene coded for, amino acid sequence, or other commentary to the database entry of raw sequence of DNA bases.
    See also: bioinformatics
    Anticipation
    Each generation of offspring has increased severity of a genetic disorder; e.g., a grandchild may have earlier onset and more severe symptoms than the parent, who had earlier onset than the grandparent.
    See also: additive genetic effects,complex trait
    Antisense
    Nucleic acid that has a sequence exactly opposite to an mRNA molecule made by the body; binds to the mRNA molecule to prevent a protein from being made.
    See also: transcription
    Apoptosis
    Programmed cell death, the body's normal method of disposing of damaged, unwanted, or unneeded cells.
    See also: cell
    Arrayed library
    Individual primary recombinant clones (hosted in phage, cosmid, YAC, or other vector) that are placed in two-dimensional arrays in microtiter dishes. Each primary clone can be identified by the identity of the plate and the clone location (row and column) on that plate. Arrayed libraries of clones can be used for many applications, including screening for a specific gene or genomic region of interest.
    See also: library,genomic library,gene chip technology
    Assembly
    Putting sequenced fragments of DNA into their correct chromosomal positions.
    Autoradiography
    A technique that uses X-ray film to visualize radioactively labeled molecules or fragments of molecules; used in analyzing length and number of DNA fragments after they are separated by gel electrophoresis.
    Autosomal dominant
    A gene on one of the non-sex chromosomes that is always expressed, even if only one copy is present. The chance of passing the gene to offspring is 50% for each pregnancy.
    See also: autosome,dominant,gene
    Autosome
    A chromosome not involved in sex determination. The diploid human genome consists of a total of 46 chromosomes: 22 pairs of autosomes, and 1 pair of sex chromosomes (the X and Y chromosomes).
    See also: sex chromosome
    Avuncular relationship
    The genetic relationship between nieces and nephews and their aunts and uncles.

    Return to Top

    B

    Backcross
    A cross between an animal that is heterozygous for alleles obtained from two parental strains and a second animal from one of those parental strains. Also used to describe the breeding protocol of an outcross followed by a backcross.
    See also: model organisms
    Bacterial artificial chromosome (BAC)
    A vector used to clone DNA fragments (100- to 300-kb insert size; average, 150 kb) in Escherichia coli cells. Based on naturally occurring F-factor plasmid found in the bacterium E. coli.
    See also: cloning vector
    Bacteriophage
    See: phage
    Base
    One of the molecules that form DNA and RNA molecules.
    See also: nucleotide,base pair,base sequence
    Base pair (bp)
    Two nitrogenous bases (adenine and thymine or guanine and cytosine) held together by weak bonds. Two strands of DNA are held together in the shape of a double helix by the bonds between base pairs.
    Base sequence
    The order of nucleotide bases in a DNA molecule; determines structure of proteins encoded by that DNA.
    Base sequence analysis
    A method, sometimes automated, for determining the base sequence.
    Behavioral genetics
    The study of genes that may influence behavior.
    Bioinformatics
    The science of managing and analyzing biological data using advanced computing techniques. Especially important in analyzing genomic research data.
    See also: informatics
    Bioremediation
    The use of biological organisms such as plants or microbes to aid in removing hazardous substances from an area.
    Biotechnology
    A set of biological techniques developed through basic research and now applied to research and product development. In particular, biotechnology refers to the use by industry of recombinant DNA, cell fusion, and new bioprocessing techniques.
    Birth defect
    Any harmful trait, physical or biochemical, present at birth, whether a result of a genetic mutation or some other nongenetic factor.
    See also: congenital,gene,mutation,syndrome
    BLAST
    A computer program that identifies homologous (similar) genes in different organisms, such as human, fruit fly, or nematode.

    Return to Top

    C

    Cancer
    Diseases in which abnormal cells divide and grow unchecked. Cancer can spread from its original site to other parts of the body and can be fatal.
    See also: hereditary cancer,sporadic cancer
    Candidate gene
    A gene located in a chromosome region suspected of being involved in a disease.
    See also: positional cloning,protein
    Capillary array
    Gel-filled silica capillaries used to separate fragments for DNA sequencing. The small diameter of the capillaries permit the application of higher electric fields, providing high speed, high throughput separations that are significantly faster than traditional slab gels.
    Carcinogen
    Something which causes cancer to occur by causing changes in a cell's DNA.
    See also: mutagene
    Carrier
    An individual who possesses an unexpressed, recessive trait.
    cDNA library
    A collection of DNA sequences that code for genes. The sequences are generated in the laboratory from mRNA sequences.
    See also: messenger RNA
    Cell
    The basic unit of any living organism that carries on the biochemical processes of life.
    See also: genome,nucleus
    Centimorgan (cM)
    A unit of measure of recombination frequency. One centimorgan is equal to a 1% chance that a marker at one genetic locus will be separated from a marker at a second locus due to crossing over in a single generation. In human beings, one centimorgan is equivalent, on average, to one million base pairs.
    See also: megabase
    Centromere
    A specialized chromosome region to which spindle fibers attach during cell division.
    Chimera (pl. chimaera)
    An organism that contains cells or tissues with a different genotype. These can be mutated cells of the host organism or cells from a different organism or species.
    Chimeraplasty
    An experimental targeted repair process in which a desirable sequence of DNA is combined with RNA to form a chimeraplast. These molecules bind selectively to the target DNA. Once bound, the chimeraplast activates a naturally occurring gene-correcting mechanism. Does not use viral or other conventional gene-delivery vectors.
    See also: gene therapy,cloning vector
    Chloroplast chromosome
    Circular DNA found in the photosynthesizing organelle (chloroplast) of plants instead of the cell nucleus where most genetic material is located.
    Chromomere
    One of the serially aligned beads or granules of a eukaryotic chromosome, resulting from local coiling of a continuous DNA thread.
    Chromosomal deletion
    The loss of part of a chromosome's DNA.
    Chromosomal inversion
    Chromosome segments that have been turned 180 degrees. The gene sequence for the segment is reversed with respect to the rest of the chromosome.
    Chromosome
    The self-replicating genetic structure of cells containing the cellular DNA that bears in its nucleotide sequence the linear array of genes. In prokaryotes, chromosomal DNA is circular, and the entire genome is carried on one chromosome. Eukaryotic genomes consist of a number of chromosomes whose DNA is associated with different kinds of proteins.
    Chromosome painting
    Attachment of certain fluorescent dyes to targeted parts of the chromosome. Used as a diagnositic for particular diseases, e.g. types of leukemia.
    Chromosome region p
    A designation for the short arm of a chromosome.
    Chromosome region q
    A designation for the long arm of a chromosome.
    Clone
    An exact copy made of biological material such as a DNA segment (e.g., a gene or other region), a whole cell, or a complete organism.
    Clone bank
    See: genomic library
    Cloning
    Using specialized DNA technology to produce multiple, exact copies of a single gene or other segment of DNA to obtain enough material for further study. This process, used by researchers in the Human Genome Project, is referred to as cloning DNA.  The resulting cloned (copied) collections of DNA molecules are called clone libraries. A second type of cloning exploits the natural process of cell division to make many copies of an entire cell. The genetic makeup of these cloned cells, called a cell line, is identical to the original cell. A third type of cloning produces complete, genetically identical animals such as the famous Scottish sheep, Dolly. 
    See also: cloning vector
    Cloning vector
    DNA molecule originating from a virus, a plasmid, or the cell of a higher organism into which another DNA fragment of appropriate size can be integrated without loss of the vector's capacity for self-replication; vectors introduce foreign DNA into host cells, where the DNA can be reproduced in large quantities. Examples are plasmids, cosmids, and yeast artificial chromosomes; vectors are often recombinant molecules containing DNA sequences from several sources.
    Code
    See: genetic code
    Codominance
    Situation in which two different alleles for a genetic trait are both expressed.
    See also: autosomal dominant,recessive gene
    Codon
    See: genetic code
    Coisogenic or congenic
    Nearly identical strains of an organism; they vary at only a single locus.
    Comparative genomics
    The study of human genetics by comparisons with model organisms such as mice, the fruit fly, and the bacterium E. coli.
    Complementary DNA (cDNA)
    DNA that is synthesized in the laboratory from a messenger RNA template.
    Complementary sequence
    Nucleic acid base sequence that can form a double-stranded structure with another DNA fragment by following base-pairing rules (A pairs with T and C with G). The complementary sequence to GTAC for example, is CATG.
    Complex trait
    Trait that has a genetic component that does not follow strict Mendelian inheritance. May involve the interaction of two or more genes or gene-environment interactions.
    See also: Mendelian inheritance,additive genetic effects
    Computational biology
    See: bioinformatics
    Confidentiality
    In genetics, the expectation that genetic material and the information gained from testing that material will not be available without the donor's consent.
    Congenital
    Any trait present at birth, whether the result of a genetic or nongenetic factor.
    See also: birth defect
    Conserved sequence
    A base sequence in a DNA molecule (or an amino acid sequence in a protein) that has remained essentially unchanged throughout evolution.
    Constitutive ablation
    Gene expression that results in cell death.
    Contig
    Group of cloned (copied) pieces of DNA representing overlapping regions of a particular chromosome.
    Contig map
    A map depicting the relative order of a linked library of overlapping clones representing a complete chromosomal segment.
    Cosmid
    Artificially constructed cloning vector containing the cos gene of phage lambda. Cosmids can be packaged in lambda phage particles for infection into E. coli; this permits cloning of larger DNA fragments (up to 45kb) than can be introduced into bacterial hosts in plasmid vectors.
    Crossing over
    The breaking during meiosis of one maternal and one paternal chromosome, the exchange of corresponding sections of DNA, and the rejoining of the chromosomes. This process can result in an exchange of alleles between chromosomes.
    See also: recombination
    Cytogenetics
    The study of the physical appearance of chromosomes.
    See also: karyotype
    Cytological band
    An area of the chromosome that stains differently from areas around it.
    See also: cytological map
    Cytological map
    A type of chromosome map whereby genes are located on the basis of cytological findings obtained with the aid of chromosome mutations.
    Cytoplasmic (uniparental) inheritance
    See: cytoplasmic trait
    Cytoplasmic trait
    A genetic characteristic in which the genes are found outside the nucleus, in chloroplasts or mitochondria. Results in offspring inheriting genetic material from only one parent.
    Cytosine (C)
    A nitrogenous base, one member of the base pair GC (guanine and cytosine) in DNA.
    See also: base pair,nucleotide

    Return to Top

    D

    Data warehouse
    A collection of databases, data tables, and mechanisms to access the data on a single subject.
    Deletion
    A loss of part of the DNA from a chromosome; can lead to a disease or abnormality.
    See also: chromosome,mutation
    Deletion map
    A description of a specific chromosome that uses defined mutations --specific deleted areas in the genome-- as 'biochemical signposts,' or markers for specific areas.
    Deoxyribonucleotide
    See: nucleotide
    Deoxyribose
    A type of sugar that is one component of DNA (deoxyribonucleic acid).
    Diploid
    A full set of genetic material consisting of paired chromosomes, one from each parental set. Most animal cells except the gametes have a diploid set of chromosomes. The diploid human genome has 46 chromosomes.
    See also: haploid
    Directed evolution
    A laboratory process used on isolated molecules or microbes to cause mutations and identify subsequent adaptations to novel environments.
    Directed mutagenesis
    Alteration of DNA at a specific site and its reinsertion into an organism to study any effects of the change.
    Directed sequencing
    Successively sequencing DNA from adjacent stretches of chromosome.
    Disease-associated genes
    Alleles carrying particular DNA sequences associated with the presence of disease.
    DNA (deoxyribonucleic acid)
    The molecule that encodes genetic information. DNA is a double-stranded molecule held together by weak bonds between base pairs of nucleotides. The four nucleotides in DNA contain the bases adenine (A), guanine (G), cytosine (C), and thymine (T). In nature, base pairs form only between A and T and between G and C; thus the base sequence of each single strand can be deduced from that of its partner.
    DNA bank
    A service that stores DNA extracted from blood samples or other human tissue.
    DNA probe
    See: probe
    DNA repair genes
    Genes encoding proteins that correct errors in DNA sequencing.
    DNA replication
    The use of existing DNA as a template for the synthesis of new DNA strands. In humans and other eukaryotes, replication occurs in the cell nucleus.
    DNA sequence
    The relative order of base pairs, whether in a DNA fragment, gene, chromosome, or an entire genome.
    See also: base sequence analysis
    Domain
    A discrete portion of a protein with its own function. The combination of domains in a single protein determines its overall function.
    Dominant
    An allele that is almost always expressed, even if only one copy is present.
    See also: gene,genome
    Double helix
    The twisted-ladder shape that two linear strands of DNA assume when complementary nucleotides on opposing strands bond together.
    Draft sequence
    The sequence generated by the HGP as of June 2000 that, while incomplete, offers a virtual road map to an estimated 95% of all human genes. Draft sequence data are mostly in the form of 10,000 base pair-sized fragments whose approximate chromosomal locations are known.
    See also: sequencing,finished DNA sequence,working draft DNA sequence

    Return to Top

    E

    Electrophoresis
    A method of separating large molecules (such as DNA fragments or proteins) from a mixture of similar molecules. An electric current is passed through a medium containing the mixture, and each kind of molecule travels through the medium at a different rate, depending on its electrical charge and size. Agarose and acrylamide gels are the media commonly used for electrophoresis of proteins and nucleic acids.
    Electroporation
    A process using high-voltage current to make cell membranes permeable to allow the introduction of new DNA; commonly used in recombinant DNA technology.
    See also: transfection
    Embryonic stem (ES) cells
    An embryonic cell that can replicate indefinitely, transform into other types of cells, and serve as a continuous source of new cells.
    Endonuclease
    See: restriction enzyme
    Enzyme
    A protein that acts as a catalyst, speeding the rate at which a biochemical reaction proceeds but not altering the direction or nature of the reaction.
    Epistasis
    One gene interfers with or prevents the expression of another gene located at a different locus.
    Escherichia coli
    Common bacterium that has been studied intensively by geneticists because of its small genome size, normal lack of pathogenicity, and ease of growth in the laboratory.
    Eugenics
    The study of improving a species by artificial selection; usually refers to the selective breeding of humans.
    Eukaryote
    Cell or organism with membrane-bound, structurally discrete nucleus and other well-developed subcellular compartments. Eukaryotes include all organisms except viruses, bacteria, and bluegreen algae.
    See also: prokaryote,chromosome.
    Evolutionarily conserved
    See: conserved sequence
    Exogenous DNA
    DNA originating outside an organism that has been introducted into the organism.
    Exon
    The protein-coding DNA sequence of a gene.
    See also: intron
    Exonuclease
    An enzyme that cleaves nucleotides sequentially from free ends of a linear nucleic acid substrate.
    Expressed gene
    See: gene expression
    Expressed sequence tag (EST)
    A short strand of DNA that is a part of a cDNA molecule and can act as identifier of a gene. Used in locating and mapping genes.
    See also: cDNA,sequence tagged site

    Return to Top

    F

    Filial generation (F1, F2)
    Each generation of offspring in a breeding program, designated F1, F2, etc.
    Fingerprinting
    In genetics, the identification of multiple specific alleles on a person's DNA to produce a unique identifier for that person.
    See also: forensics
    Finished DNA Sequence
    High-quality, low error, gap-free DNA sequence of the human genome. Achieving this ultimate 2003 HGP goal requires additional sequencing to close gaps, reduce ambiguities, and allow for only a single error every 10,000 bases, the agreed-upon standard for HGP finished sequence.
    See also: sequencing,draft sequence
    Flow cytometry
    Analysis of biological material by detection of the light-absorbing or fluorescing properties of cells or subcellular fractions (i.e., chromosomes) passing in a narrow stream through a laser beam. An absorbance or fluorescence profile of the sample is produced. Automated sorting devices, used to fractionate samples, sort successive droplets of the analyzed stream into different fractions depending on the fluorescence emitted by each droplet.
    Flow karyotyping
    Use of flow cytometry to analyze and separate chromosomes according to their DNA content.
    Fluorescence in situ hybridization (FISH)
    A physical mapping approach that uses fluorescein tags to detect hybridization of probes with metaphase chromosomes and with the less-condensed somatic interphase chromatin.
    Forensics
    The use of DNA for identification. Some examples of DNA use are to establish paternity in child support cases; establish the presence of a suspect at a crime scene, and identify accident victims.
    Fraternal twin
    Siblings born at the same time as the result of fertilization of two ova by two sperm. They share the same genetic relationship to each other as any other siblings.
    See also: identical twin
    Full gene sequence
    The complete order of bases in a gene. This order determines which protein a gene will produce.
    Functional genomics
    The study of genes, their resulting proteins, and the role played by the proteins the body's biochemical processes.

    Return to Top

    G

    Gamete
    Mature male or female reproductive cell (sperm or ovum) with a haploid set of chromosomes (23 for humans).
    GC-rich area
    Many DNA sequences carry long stretches of repeated G and C which often indicate a gene-rich region.
    Gel electrophoresis
    See: electrophoresis
    Gene
    The fundamental physical and functional unit of heredity. A gene is an ordered sequence of nucleotides located in a particular position on a particular chromosome that encodes a specific functional product (i.e., a protein or RNA molecule).
    See also: gene expression
    Gene amplification
    Repeated copying of a piece of DNA; a characteristic of tumor cells.
    See also: gene,oncogene
    Gene chip technology
    Development of cDNA microarrays from a large number of genes. Used to monitor and measure changes in gene expression for each gene represented on the chip.
    Gene expression
    The process by which a gene's coded information is converted into the structures present and operating in the cell. Expressed genes include those that are transcribed into mRNA and then translated into protein and those that are transcribed into RNA but not translated into protein (e.g., transfer and ribosomal RNAs).
    Gene family
    Group of closely related genes that make similar products.
    Gene library
    See: genomic library
    Gene mapping
    Determination of the relative positions of genes on a DNA molecule (chromosome or plasmid) and of the distance, in linkage units or physical units, between them.
    Gene pool
    All the variations of genes in a species.
    See also: allele,gene,polymorphism
    Gene prediction
    Predictions of possible genes made by a computer program based on how well a stretch of DNA sequence matches known gene sequences
    Gene product
    The biochemical material, either RNA or protein, resulting from expression of a gene. The amount of gene product is used to measure how active a gene is; abnormal amounts can be correlated with disease-causing alleles.
    Gene testing
    See: genetic testing,genetic screening
    Gene therapy
    An experimental procedure aimed at replacing, manipulating, or supplementing nonfunctional or misfunctioning genes with healthy genes.
    See also: gene,inherit,somatic cell gene therapy,germ line gene therapy
    Gene transfer
    Incorporation of new DNA into and organism's cells, usually by a vector such as a modified virus. Used in gene therapy.
    See also: mutation,gene therapy,vector
    Genetic code
    The sequence of nucleotides, coded in triplets (codons) along the mRNA, that determines the sequence of amino acids in protein synthesis. A gene's DNA sequence can be used to predict the mRNA sequence, and the genetic code can in turn be used to predict the amino acid sequence.
    Genetic counseling
    Provides patients and their families with education and information about genetic-related conditions and helps them make informed decisions.
    Genetic discrimination
    Prejudice against those who have or are likely to develop an inherited disorder.
    Genetic engineering
    Altering the genetic material of cells or organisms to enable them to make new substances or perform new functions.
    Genetic engineering technology
    See: recombinant DNA technology
    Genetic illness
    Sickness, physical disability, or other disorder resulting from the inheritance of one or more deleterious alleles.
    Genetic informatics
    See: bioinformatics
    Genetic map
    See: linkage map
    Genetic marker
    A gene or other identifiable portion of DNA whose inheritance can be followed.
    See also: chromosome,DNA,gene,inherit
    Genetic material
    See: genome
    Genetic mosaic
    An organism in which different cells contain different genetic sequence. This can be the result of a mutation during development or fusion of embryos at an early developmental stage.
    Genetic polymorphism
    Difference in DNA sequence among individuals, groups, or populations (e.g., genes for blue eyes versus brown eyes).
    Genetic predisposition
    Susceptibility to a genetic disease. May or may not result in actual development of the disease.
    Genetic screening
    Testing a group of people to identify individuals at high risk of having or passing on a specific genetic disorder.
    Genetic testing
    Analyzing an individual's genetic material to determine predisposition to a particular health condition or to confirm a diagnosis of genetic disease.
    Genetics
    The study of inheritance patterns of specific traits.
    Genome
    All the genetic material in the chromosomes of a particular organism; its size is generally given as its total number of base pairs.
    Genome project
    Research and technology-development effort aimed at mapping and sequencing the genome of human beings and certain model organisms.
    See also: Human Genome Initiative
    Genomic library
    A collection of clones made from a set of randomly generated overlapping DNA fragments that represent the entire genome of an organism.
    See also: library,arrayed library
    Genomic sequence
    See: DNA
    Genomics
    The study of genes and their function.
    Genotype
    The genetic constitution of an organism, as distinguished from its physical appearance (its phenotype).
    Germ cell
    Sperm and egg cells and their precursors. Germ cells are haploid and have only one set of chromosomes (23 in all), while all other cells have two copies (46 in all).
    Germ line
    The continuation of a set of genetic information from one generation to the next.
    See also: inherit
    Germ line gene therapy
    An experimental process of inserting genes into germ cells or fertilized eggs to cause a genetic change that can be passed on to offspring. May be used to alleviate effects associated with a genetic disease.
    See also: genomics,somatic cell gene therapy.
    Germ line genetic mutation
    See: mutation
    Guanine (G)
    A nitrogenous base, one member of the base pair GC (guanine and cytosine) in DNA.
    See also: base pair,nucleotide
    Gyandromorph
    Organisms that have both male and female cells and therefore express both male and female characteristics.

    Return to Top

    H

    Haploid
    A single set of chromosomes (half the full set of genetic material) present in the egg and sperm cells of animals and in the egg and pollen cells of plants. Human beings have 23 chromosomes in their reproductive cells.
    See also: diploid
    Haplotype
    A way of denoting the collective genotype of a number of closely linked loci on a chromosome.
    Hemizygous
    Having only one copy of a particular gene. For example, in humans, males are hemizygous for genes found on the Y chromosome.
    Hereditary cancer
    Cancer that occurs due to the inheritance of an altered gene within a family.
    See also: sporadic cancer
    Heterozygosity
    The presence of different alleles at one or more loci on homologous chromosomes.
    Heterozygote
    See: heterozygosity
    Highly conserved sequence
    DNA sequence that is very similar across several different types of organisms.
    See also: gene,mutation
    High-throughput sequencing
    A fast method of determining the order of bases in DNA.
    See also: sequencing
    Homeobox
    A short stretch of nucleotides whose base sequence is virtually identical in all the genes that contain it. Homeoboxes have been found in many organisms from fruit flies to human beings. In the fruit fly, a homeobox appears to determine when particular groups of genes are expressed during development.
    Homolog
    A member of a chromosome pair in diploid organisms or a gene that has the same origin and functions in two or more species.
    Homologous chromosome
    Chromosome containing the same linear gene sequences as another, each derived from one parent.
    Homologous recombination
    Swapping of DNA fragments between paired chromosomes.
    Homology
    Similarity in DNA or protein sequences between individuals of the same species or among different species.
    Homozygote
    An organism that has two identical alleles of a gene.
    See also: heterozygote
    Homozygous
    See: homozygote
    Human artificial chromosome (HAC)
    A vector used to hold large DNA fragments.
    See also: chromosome,DNA
    Human gene therapy
    See: gene therapy
    Human Genome Initiative
    Collective name for several projects begun in 1986 by DOE to create an ordered set of DNA segments from known chromosomal locations, develop new computational methods for analyzing genetic map and DNA sequence data, and develop new techniques and instruments for detecting and analyzing DNA. This DOE initiative is now known as the Human Genome Program. The joint national effort, led by DOE and NIH, is known as the Human Genome Project.
    Human Genome Project (HGP)
    Formerly titled Human Genome Initiative.
    See also: Human Genome Initiative
    Hybrid
    The offspring of genetically different parents.
    See also: heterozygote
    Hybridization
    The process of joining two complementary strands of DNA or one each of DNA and RNA to form a double-stranded molecule.

    Return to Top

    I

    Identical twin
    Twins produced by the division of a single zygote; both have identical genotypes.
    See also: fraternal twin
    Immunotherapy
    Using the immune system to treat disease, for example, in the development of vaccines. May also refer to the therapy of diseases caused by the immune system.
    See also: cancer
    Imprinting
    A phenomenon in which the disease phenotype depends on which parent passed on the disease gene. For instance, both Prader-Willi and Angelman syndromes are inherited when the same part of chromosome 15 is missing. When the father's complement of 15 is missing, the child has Prader-Willi, but when the mother's complement of 15 is missing, the child has Angelman syndrome.
    In situ hybridization
    Use of a DNA or RNA probe to detect the presence of the complementary DNA sequence in cloned bacterial or cultured eukaryotic cells.
    In vitro
    Studies performed outside a living organism such as in a laboratory.
    In vivo
    Studies carried out in living organisms.
    Independent assortment
    During meiosis each of the two copies of a gene is distributed to the germ cells independently of the distribution of other genes.
    See also: linkage
    Informatics
    See: bioinformatics
    Informed consent
    An individual willingly agrees to participate in an activity after first being advised of the risks and benefits.
    See also: privacy
    Inherit
    In genetics, to receive genetic material from parents through biological processes.
    Inherited
    See: inherit
    Insertion
    A chromosome abnormality in which a piece of DNA is incorporated into a gene and thereby disrupts the gene's normal function.
    See also: chromosome,DNA,gene,mutation
    Insertional mutation
    See: insertion
    Intellectual property rights
    Patents, copyrights, and trademarks.
    See also: patent
    Interference
    One crossover event inhibits the chances of another crossover event. Also known as positive interference. Negative interference increases the chance of a second crossover.
    See also: crossing over
    Interphase
    The period in the cell cycle when DNA is replicated in the nucleus; followed by mitosis.
    Intron
    DNA sequence that interrupts the protein-coding sequence of a gene; an intron is transcribed into RNA but is cut out of the message before it is translated into protein.
    See also: exon
    Isoenzyme
    An enzyme performing the same function as another enzyme but having a different set of amino acids. The two enzymes may function at different speeds.

    Return to Top

    J

    Junk DNA
    Stretches of DNA that do not code for genes; most of the genome consists of so-called junk DNA which may have regulatory and other functions. Also called non-coding DNA.

    Return to Top

    K

    Karyotype
    A photomicrograph of an individual's chromosomes arranged in a standard format showing the number, size, and shape of each chromosome type; used in low-resolution physical mapping to correlate gross chromosomal abnormalities with the characteristics of specific diseases.
    Kilobase (kb)
    Unit of length for DNA fragments equal to 1000 nucleotides.
    Knockout
    Deactivation of specific genes; used in laboratory organisms to study gene function.
    See also: gene,locus,model organisms

    Return to Top

    L

    Library
    An unordered collection of clones (i.e., cloned DNA from a particular organism) whose relationship to each other can be established by physical mapping.
    See also: genomic library,arrayed library
    Linkage
    The proximity of two or more markers (e.g., genes, RFLP markers) on a chromosome; the closer the markers, the lower the probability that they will be separated during DNA repair or replication processes (binary fission in prokaryotes, mitosis or meiosis in eukaryotes), and hence the greater the probability that they will be inherited together.
    Linkage disequilibrium
    Where alleles occur together more often than can be accounted for by chance. Indicates that the two alleles are physically close on the DNA strand.
    See also: Mendelian inheritance
    Linkage map
    A map of the relative positions of genetic loci on a chromosome, determined on the basis of how often the loci are inherited together. Distance is measured in centimorgans (cM).
    Localize
    Determination of the original position (locus) of a gene or other marker on a chromosome.
    Locus (pl. loci)
    The position on a chromosome of a gene or other chromosome marker; also, the DNA at that position. The use of locus is sometimes restricted to mean expressed DNA regions.
    See also: gene expression
    Long-Range Restriction Mapping
    Restriction enzymes are proteins that cut DNA at precise locations. Restriction maps depict the chromosomal positions of restriction-enzyme cutting sites. These are used as biochemical "signposts," or markers of specific areas along the chromosomes. The map will detail the positions where the DNA molecule is cut by particular restriction enzymes.

    Return to Top

    M

    Macrorestriction map
    Map depicting the order of and distance between sites at which restriction enzymes cleave chromosomes.
    Mapping
    See: gene mapping,linkage map,physical map
    Mapping population
    The group of related organisms used in constructing a genetic map.
    Marker
    See: genetic marker
    Mass spectrometry
    An instrument used to identify chemicals in a substance by their mass and charge.
    Megabase (Mb)
    Unit of length for DNA fragments equal to 1 million nucleotides and roughly equal to 1 cM.
    See also: centimorgan
    Meiosis
    The process of two consecutive cell divisions in the diploid progenitors of sex cells. Meiosis results in four rather than two daughter cells, each with a haploid set of chromosomes.
    See also: mitosis
    Mendelian inheritance
    One method in which genetic traits are passed from parents to offspring. Named for Gregor Mendel, who first studied and recognized the existence of genes and this method of inheritance.
    See also: autosomal dominant,recessive gene,sex-linked
    Messenger RNA (mRNA)
    RNA that serves as a template for protein synthesis.
    See also: genetic code
    Metaphase
    A stage in mitosis or meiosis during which the chromosomes are aligned along the equatorial plane of the cell.
    Microarray
    Sets of miniaturized chemical reaction areas that may also be used to test DNA fragments, antibodies, or proteins.
    Microbial genetics
    The study of genes and gene function in bacteria, archaea, and other microorganisms. Often used in research in the fields of bioremediation, alternative energy, and disease prevention.
    See also: model organisms,biotechnology,bioremediation
    Microinjection
    A technique for introducing a solution of DNA into a cell using a fine microcapillary pipet.
    Micronuclei
    Chromosome fragments that are not incorporated into the nucleus at cell division.
    Mitochondrial DNA
    The genetic material found in mitochondria, the organelles that generate energy for the cell. Not inherited in the same fashion as nucleic DNA.
    See also: cell,DNA,genome,nucleus
    Mitosis
    The process of nuclear division in cells that produces daughter cells that are genetically identical to each other and to the parent cell.
    See also: meiosis
    Model organisms
    A laboratory animal or other organism useful for research.
    Modeling
    The use of statistical analysis, computer analysis, or model organisms to predict outcomes of research.
    Molecular biology
    The study of the structure, function, and makeup of biologically important molecules.
    Molecular farming
    The development of transgenic animals to produce human proteins for medical use.
    Molecular genetics
    The study of macromolecules important in biological inheritance.
    Molecular medicine
    The treatment of injury or disease at the molecular level. Examples include the use of DNA-based diagnostic tests or medicine derived from DNA sequence information.
    Monogenic disorder
    A disorder caused by mutation of a single gene.
    See also: mutation,polygenic disorder
    Monogenic inheritance
    See: monogenic disorder
    Monosomy
    Possessing only one copy of a particular chromosome instead of the normal two copies.
    See also: cell,chromosome,gene expression,trisomy
    Morbid map
    A diagram showing the chromosomal location of genes associated with disease.
    Mouse model
    See: model organisms
    Multifactorial or multigenic disorder
    See: polygenic disorder
    Multiplexing
    A laboratory approach that performs multiple sets of reactions in parallel (simultaneously); greatly increasing speed and throughput.
    Murine
    Organism in the genus Mus. A rat or mouse.
    Mutagen
    An agent that causes a permanent genetic change in a cell. Does not include changes occurring during normal genetic recombination.
    Mutagenicity
    The capacity of a chemical or physical agent to cause permanent genetic alterations.
    See also: somatic cell genetic mutation
    Mutation
    Any heritable change in DNA sequence.
    See also: polymorphism

    Return to Top

    N

    Nitrogenous base
    A nitrogen-containing molecule having the chemical properties of a base. DNA contains the nitrogenous bases adenine (A), guanine (G), cytosine (C), and thymine (T).
    See also: DNA
    Northern blot
    A gel-based laboratory procedure that locates mRNA sequences on a gel that are complementary to a piece of DNA used as a probe.
    See also: DNA,library
    Nuclear transfer
    A laboratory procedure in which a cell's nucleus is removed and placed into an oocyte with its own nucleus removed so the genetic information from the donor nucleus controls the resulting cell. Such cells can be induced to form embryos. This process was used to create the cloned sheep "Dolly".
    See also: cloning
    Nucleic acid
    A large molecule composed of nucleotide subunits.
    See also: DNA
    Nucleolar organizing region
    A part of the chromosome containing rRNA genes.
    Nucleotide
    A subunit of DNA or RNA consisting of a nitrogenous base (adenine, guanine, thymine, or cytosine in DNA; adenine, guanine, uracil, or cytosine in RNA), a phosphate molecule, and a sugar molecule (deoxyribose in DNA and ribose in RNA). Thousands of nucleotides are linked to form a DNA or RNA molecule.
    See also: DNA,base pair,RNA
    Nucleus
    The cellular organelle in eukaryotes that contains most of the genetic material.

    Return to Top

    O

    Oligo
    See: oligonucleotide
    Oligogenic
    A phenotypic trait produced by two or more genes working together.
    See also: polygenic disorder
    Oligonucleotide
    A molecule usually composed of 25 or fewer nucleotides; used as a DNA synthesis primer.
    See also: nucleotide
    Oncogene
    A gene, one or more forms of which is associated with cancer. Many oncogenes are involved, directly or indirectly, in controlling the rate of cell growth.
    Open reading frame (ORF)
    The sequence of DNA or RNA located between the start-code sequence (initiation codon) and the stop-code sequence (termination codon).
    Operon
    A set of genes transcribed under the control of an operator gene.
    Overlapping clones
    See: genomic library

    Return to Top

    P

    P1-derived artificial chromosome (PAC)
    One type of vector used to clone DNA fragments (100- to 300-kb insert size; average, 150 kb) in Escherichia coli cells. Based on bacteriophage (a virus) P1 genome.
    See also: cloning vector
    Patent
    In genetics, conferring the right or title to genes, gene variations, or identifiable portions of sequenced genetic material to an individual or organization.
    See also: gene
    Pedigree
    A family tree diagram that shows how a particular genetic trait or disease has been inherited.
    See also: inherit
    Penetrance
    The probability of a gene or genetic trait being expressed. "Complete" penetrance means the gene or genes for a trait are expressed in all the population who have the genes. "Incomplete" penetrance means the genetic trait is expressed in only part of the population. The percent penetrance also may change with the age range of the population.
    Peptide
    Two or more amino acids joined by a bond called a "peptide bond."
    See also: polypeptide
    Phage
    A virus for which the natural host is a bacterial cell.
    Pharmacogenomics
    The study of the interaction of an individual's genetic makeup and response to a drug.
    Phenocopy
    A trait not caused by inheritance of a gene but appears to be identical to a genetic trait.
    Phenotype
    The physical characteristics of an organism or the presence of a disease that may or may not be genetic.
    See also: genotype
    Physical map
    A map of the locations of identifiable landmarks on DNA (e.g., restriction-enzyme cutting sites, genes), regardless of inheritance. Distance is measured in base pairs. For the human genome, the lowest-resolution physical map is the banding patterns on the 24 different chromosomes; the highest-resolution map is the complete nucleotide sequence of the chromosomes.
    Plasmid
    Autonomously replicating extra-chromosomal circular DNA molecules, distinct from the normal bacterial genome and nonessential for cell survival under nonselective conditions. Some plasmids are capable of integrating into the host genome. A number of artificially constructed plasmids are used as cloning vectors.
    Pleiotropy
    One gene that causes many different physical traits such as multiple disease symptoms.
    Pluripotency
    The potential of a cell to develop into more than one type of mature cell, depending on environment.
    Polygenic disorder
    Genetic disorder resulting from the combined action of alleles of more than one gene (e.g., heart disease, diabetes, and some cancers). Although such disorders are inherited, they depend on the simultaneous presence of several alleles; thus the hereditary patterns usually are more complex than those of single-gene disorders.
    See also: single-gene disorder
    Polymerase chain reaction (PCR)
    A method for amplifying a DNA base sequence using a heat-stable polymerase and two 20-base primers, one complementary to the (+) strand at one end of the sequence to be amplified and one complementary to the (-) strand at the other end. Because the newly synthesized DNA strands can subsequently serve as additional templates for the same primer sequences, successive rounds of primer annealing, strand elongation, and dissociation produce rapid and highly specific amplification of the desired sequence. PCR also can be used to detect the existence of the defined sequence in a DNA sample.
    Polymerase, DNA or RNA
    Enzyme that catalyzes the synthesis of nucleic acids on preexisting nucleic acid templates, assembling RNA from ribonucleotides or DNA from deoxyribonucleotides.
    Polymorphism
    Difference in DNA sequence among individuals that may underlie differences in health. Genetic variations occurring in more than 1% of a population would be considered useful polymorphisms for genetic linkage analysis.
    See also: mutation
    Polypeptide
    A protein or part of a protein made of a chain of amino acids joined by a peptide bond.
    Population genetics
    The study of variation in genes among a group of individuals.
    Positional cloning
    A technique used to identify genes, usually those that are associated with diseases, based on their location on a chromosome.
    Premature chromosome condensation (PCC)
    A method of studying chromosomes in the interphase stage of the cell cycle.
    Primer
    Short preexisting polynucleotide chain to which new deoxyribonucleotides can be added by DNA polymerase.
    Privacy
    In genetics, the right of people to restrict access to their genetic information.
    Probe
    Single-stranded DNA or RNA molecules of specific base sequence, labeled either radioactively or immunologically, that are used to detect the complementary base sequence by hybridization.
    Prokaryote
    Cell or organism lacking a membrane-bound, structurally discrete nucleus and other subcellular compartments. Bacteria are examples of prokaryotes.
    See also: chromosome,eukaryote
    Promoter
    A DNA site to which RNA polymerase will bind and initiate transcription.
    Pronucleus
    The nucleus of a sperm or egg prior to fertilization.
    See also: nucleus,transgenic
    Protein
    A large molecule composed of one or more chains of amino acids in a specific order; the order is determined by the base sequence of nucleotides in the gene that codes for the protein. Proteins are required for the structure, function, and regulation of the body's cells, tissues, and organs; and each protein has unique functions. Examples are hormones, enzymes, and antibodies.
    Proteome
    Proteins expressed by a cell or organ at a particular time and under specific conditions.
    Proteomics
    The study of the full set of proteins encoded by a genome.
    Pseudogene
    A sequence of DNA similar to a gene but nonfunctional; probably the remnant of a once-functional gene that accumulated mutations.
    Purine
    A nitrogen-containing, double-ring, basic compound that occurs in nucleic acids. The purines in DNA and RNA are adenine and guanine.
    See also: base pair
    Pyrimidine
    A nitrogen-containing, single-ring, basic compound that occurs in nucleic acids. The pyrimidines in DNA are cytosine and thymine; in RNA, cytosine and uracil.
    See also: base pair

    Return to Top

    R

    Radiation hybrid
    A hybrid cell containing small fragments of irradiated human chromosomes. Maps of irradiation sites on chromosomes for the human, rat, mouse, and other genomes provide important markers, allowing the construction of very precise STS maps indispensable to studying multifactorial diseases.
    See also: sequence tagged site
    Rare-cutter enzyme
    See: restriction-enzyme cutting site
    Recessive gene
    A gene which will be expressed only if there are 2 identical copies or, for a male, if one copy is present on the X chromosome.
    Reciprocal translocation
    When a pair of chromosomes exchange exactly the same length and area of DNA. Results in a shuffling of genes.
    Recombinant clone
    Clone containing recombinant DNA molecules.
    See also: recombinant DNA technology
    Recombinant DNA molecules
    A combination of DNA molecules of different origin that are joined using recombinant DNA technologies.
    Recombinant DNA technology
    Procedure used to join together DNA segments in a cell-free system (an environment outside a cell or organism). Under appropriate conditions, a recombinant DNA molecule can enter a cell and replicate there, either autonomously or after it has become integrated into a cellular chromosome.
    Recombination
    The process by which progeny derive a combination of genes different from that of either parent. In higher organisms, this can occur by crossing over.
    See also: crossing over,mutation
    Regulatory region or sequence
    A DNA base sequence that controls gene expression.
    Repetitive DNA
    Sequences of varying lengths that occur in multiple copies in the genome; it represents much of the human genome.
    Reporter gene
    See: marker
    Resolution
    Degree of molecular detail on a physical map of DNA, ranging from low to high.
    Restriction enzyme, endonuclease
    A protein that recognizes specific, short nucleotide sequences and cuts DNA at those sites. Bacteria contain over 400 such enzymes that recognize and cut more than 100 different DNA sequences.
    See also: restriction enzyme cutting site
    Restriction fragment length polymorphism (RFLP)
    Variation between individuals in DNA fragment sizes cut by specific restriction enzymes; polymorphic sequences that result in RFLPs are used as markers on both physical maps and genetic linkage maps. RFLPs usually are caused by mutation at a cutting site.
    See also: marker,polymorphism
    Restriction-enzyme cutting site
    A specific nucleotide sequence of DNA at which a particular restriction enzyme cuts the DNA. Some sites occur frequently in DNA (e.g., every several hundred base pairs); others much less frequently (rare-cutter; e.g., every 10,000 base pairs).
    Retroviral infection
    The presence of retroviral vectors, such as some viruses, which use their recombinant DNA to insert their genetic material into the chromosomes of the host's cells. The virus is then propogated by the host cell.
    Reverse transcriptase
    An enzyme used by retroviruses to form a complementary DNA sequence (cDNA) from their RNA. The resulting DNA is then inserted into the chromosome of the host cell.
    Ribonucleotide
    See: nucleotide
    Ribose
    The five-carbon sugar that serves as a component of RNA.
    See also: ribonucleic acid,deoxyribose
    Ribosomal RNA (rRNA)
    A class of RNA found in the ribosomes of cells.
    Ribosomes
    Small cellular components composed of specialized ribosomal RNA and protein; site of protein synthesis.
    See also: RNA
    Risk communication
    In genetics, a process in which a genetic counselor or other medical professional interprets genetic test results and advises patients of the consequences for them and their offspring.
    RNA (Ribonucleic acid)
    A chemical found in the nucleus and cytoplasm of cells; it plays an important role in protein synthesis and other chemical activities of the cell. The structure of RNA is similar to that of DNA. There are several classes of RNA molecules, including messenger RNA, transfer RNA, ribosomal RNA, and other small RNAs, each serving a different purpose.

    Return to Top

    S

    Sanger sequencing
    A widely used method of determining the order of bases in DNA.
    See also: sequencing,shotgun sequencing
    Satellite
    A chromosomal segment that branches off from the rest of the chromosome but is still connected by a thin filament or stalk.
    Scaffold
    In genomic mapping, a series of contigs that are in the right order but not necessarily connected in one continuous stretch of sequence.
    Segregation
    The normal biological process whereby the two pieces of a chromosome pair are separated during meiosis and randomly distributed to the germ cells.
    Sequence
    See: base sequence
    Sequence assembly
    A process whereby the order of multiple sequenced DNA fragments is determined.
    Sequence tagged site (STS)
    Short (200 to 500 base pairs) DNA sequence that has a single occurrence in the human genome and whose location and base sequence are known. Detectable by polymerase chain reaction, STSs are useful for localizing and orienting the mapping and sequence data reported from many different laboratories and serve as landmarks on the developing physical map of the human genome. Expressed sequence tags (ESTs) are STSs derived from cDNAs.
    Sequencing
    Determination of the order of nucleotides (base sequences) in a DNA or RNA molecule or the order of amino acids in a protein.
    Sequencing technology
    The instrumentation and procedures used to determine the order of nucleotides in DNA.
    Sex chromosome
    The X or Y chromosome in human beings that determines the sex of an individual. Females have two X chromosomes in diploid cells; males have an X and a Y chromosome. The sex chromosomes comprise the 23rd chromosome pair in a karyotype.
    See also: autosome
    Sex-linked
    Traits or diseases associated with the X or Y chromosome; generally seen in males.
    See also: gene,mutation,sex chromosome
    Shotgun method
    Sequencing method that involves randomly sequenced cloned pieces of the genome, with no foreknowledge of where the piece originally came from. This can be contrasted with "directed" strategies, in which pieces of DNA from known chromosomal locations are sequenced. Because there are advantages to both strategies, researchers use both random (or shotgun) and directed strategies in combination to sequence the human genome.
    See also: library,genomic library
    Single nucleotide polymorphism (SNP)
    DNA sequence variations that occur when a single nucleotide (A, T, C, or G) in the genome sequence is altered.
    See also: mutation,polymorphism,single-gene disorder
    Single-gene disorder
    Hereditary disorder caused by a mutant allele of a single gene (e.g., Duchenne muscular dystrophy, retinoblastoma, sickle cell disease).
    See also: polygenic disorders
    Somatic cell
    Any cell in the body except gametes and their precursors.
    See also: gamete
    Somatic cell gene therapy
    Incorporating new genetic material into cells for therapeutic purposes. The new genetic material cannot be passed to offspring.
    See also: gene therapy
    Somatic cell genetic mutation
    A change in the genetic structure that is neither inherited nor passed to offspring. Also called acquired mutations.
    See also: germ line genetic mutation
    Southern blotting
    Transfer by absorption of DNA fragments separated in electrophoretic gels to membrane filters for detection of specific base sequences by radio-labeled complementary probes.
    Spectral karyotype (SKY)
    A graphic of all an organism's chromosomes, each labeled with a different color. Useful for identifying chromosomal abnormalities.
    See also: chromosome
    Splice site
    Location in the DNA sequence where RNA removes the noncoding areas to form a continuous gene transcript for translation into a protein.
    Sporadic cancer
    Cancer that occurs randomly and is not inherited from parents. Caused by DNA changes in one cell that grows and divides, spreading throughout the body.
    See also: hereditary cancer
    Stem cell
    Undifferentiated, primitive cells in the bone marrow that have the ability both to multiply and to differentiate into specific blood cells.
    Structural genomics
    The effort to determine the 3D structures of large numbers of proteins using both experimental techniques and computer simulation
    Substitution
    In genetics, a type of mutation due to replacement of one nucleotide in a DNA sequence by another nucleotide or replacement of one amino acid in a protein by another amino acid.
    See also: mutation
    Suppressor gene
    A gene that can suppress the action of another gene.
    Syndrome
    The group or recognizable pattern of symptoms or abnormalities that indicate a particular trait or disease.
    Syngeneic
    Genetically identical members of the same species.
    Synteny
    Genes occurring in the same order on chromosomes of different species.
    See also: linkage,conserved sequence

    Return to Top

    T

    Tandem repeat sequences
    Multiple copies of the same base sequence on a chromosome; used as markers in physical mapping.
    See also: physical map
    Targeted mutagenesis
    Deliberate change in the genetic structure directed at a specific site on the chromosome. Used in research to determine the targeted region's function.
    See also: mutation,polymorphism
    Technology transfer
    The process of transferring scientific findings from research laboratories to the commercial sector.
    Telomerase
    The enzyme that directs the replication of telomeres.
    Telomere
    The end of a chromosome. This specialized structure is involved in the replication and stability of linear DNA molecules.
    See also: DNA replication
    Teratogenic
    Substances such as chemicals or radiation that cause abnormal development of a embryo.
    See also: mutatgen
    Thymine (T)
    A nitrogenous base, one member of the base pair AT (adenine-thymine).
    See also: base pair,nucleotide
    Toxicogenomics
    The study of how genomes respond to environmental stressors or toxicants. Combines genome-wide mRNA expression profiling with protein expression patterns using bioinformatics to understand the role of gene-environment interactions in disease and dysfunction.
    Transcription
    The synthesis of an RNA copy from a sequence of DNA (a gene); the first step in gene expression.
    See also: translation
    Transcription factor
    A protein that binds to regulatory regions and helps control gene expression.
    Transcriptome
    The full complement of activated genes, mRNAs, or transcripts in a particular tissue at a particular time
    Transfection
    The introduction of foreign DNA into a host cell.
    See also: cloning vector,gene therapy
    Transfer RNA (tRNA)
    A class of RNA having structures with triplet nucleotide sequences that are complementary to the triplet nucleotide coding sequences of mRNA. The role of tRNAs in protein synthesis is to bond with amino acids and transfer them to the ribosomes, where proteins are assembled according to the genetic code carried by mRNA.
    Transformation
    A process by which the genetic material carried by an individual cell is altered by incorporation of exogenous DNA into its genome.
    Transgenic
    An experimentally produced organism in which DNA has been artificially introduced and incorporated into the organism's germ line.
    See also: cell,DNA,gene,nucleus,germ line
    Translation
    The process in which the genetic code carried by mRNA directs the synthesis of proteins from amino acids.
    See also: transcription
    Translocation
    A mutation in which a large segment of one chromosome breaks off and attaches to another chromosome.
    See also: mutation
    Transposable element
    A class of DNA sequences that can move from one chromosomal site to another.
    Trisomy
    Possessing three copies of a particular chromosome instead of the normal two copies.
    See also: cell,gene,gene expression,chromosome

    Return to Top

    U

    Uracil
    A nitrogenous base normally found in RNA but not DNA; uracil is capable of forming a base pair with adenine.
    See also: base pair,nucleotide

    Return to Top

    V

    Vector
    See: cloning vector
    Virus
    A noncellular biological entity that can reproduce only within a host cell. Viruses consist of nucleic acid covered by protein; some animal viruses are also surrounded by membrane. Inside the infected cell, the virus uses the synthetic capability of the host to produce progeny virus.
    See also: cloning vector

    Return to Top

    W

    Western blot
    A technique used to identify and locate proteins based on their ability to bind to specific antibodies.
    See also: DNA,Northern blot,protein,RNA,Southern blotting
    Wild type
    The form of an organism that occurs most frequently in nature.
    Working Draft DNA Sequence
    See: Draft DNA Sequence

    Return to Top

    X

    X chromosome
    One of the two sex chromosomes, X and Y.
    See also: Y chromosome,sex chromosome
    Xenograft
    Tissue or organs from an individual of one species transplanted into or grafted onto an organism of another species, genus, or family. A common example is the use of pig heart valves in humans.

    Return to Top

    Y

    Y chromosome
    One of the two sex chromosomes, X and Y.
    See also: X chromosome,sex chromosome
    Yeast artificial chromosome (YAC)
    Constructed from yeast DNA, it is a vector used to clone large DNA fragments.
    See also: cloning vector,cosmid

    Return to Top

    Z

    Zinc-finger protein
    A secondary feature of some proteins containing a zinc atom; a DNA-binding protein.

    Return to Top

    Updated 15-Apr-03
    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section1/genome/5.txt0000600000175000017500000001564210246051200025344 0ustar madduckmadduckrestindex format: html page-title: Beyond the Human Genome Project -- What's Next? crumb: What's Next ? /restindex
    Beyond the Human Genome Project -- What's Next?

    Genomics and Its Impact on Science and Society: The Human Genome Project and Beyond

    Genome Sequences Launch a New Level of Scientific Challenges

    Building a “Systems Level” View of Life
    The DNA sequences generated in hundreds of genome projects now provide scientists with the “parts lists” containing instructions for how an organism builds, operates, maintains, and reproduces itself while responding to various environmental conditions. But we still have very little knowledge of how cells use this information to “come alive.” The functions of most genes remain unknown. Nor do we understand how genes and the proteins they encode interact with each other and with the environment. If we are to realize the potential of the genome projects, with far-ranging applications to such diverse fields as medicine, energy, and the environment, we must obtain this new level of knowledge.

    One of the greatest impacts of having whole-genome sequences and powerful new genomic technologies may be an entirely new approach to conducting biological research. In the past, researchers studied one or a few genes or proteins at a time. Because life doesn’t operate in such isolation, this inherently provided incomplete—and often inaccurate—views. Researchers now can approach questions systematically and on a much grander scale. They can study all the genes expressed in a particular environment or all the gene products in a specific tissue, organ, or tumor. Other analyses will focus on how tens of thousands of genes and proteins work together in interconnected networks to orchestrate the chemistry of life—a new field called “systems biology” (see “Genomes to Life”).

    Charting Human Variation
    Slight variations in our DNA sequences can have a major impact on whether or not we develop a disease and on our responses to such environmental factors as infectious microbes, toxins, and drugs. One of the most common types of sequence variation is the single nucleotide polymorphism (SNP). SNPs are sites in the human genome where individuals differ in their DNA sequence, often by a single base. For example, one person might have the DNA base A where another might have C, and so on. Scientists believe the human genome has at least 10 million SNPs, and they are generating different types of maps of these sites, which can occur both in genes and noncoding regions.

    Sets of SNPs on the same chromosome are inherited in blocks (haplotypes). In 2002 a consortium of researchers from six countries established a 3-year effort to construct a map of the patterns of SNPs that occur across populations in Africa, Asia, and the United States. Researchers hope that dramatically decreasing the number of individual SNPs to be scanned will provide a shortcut for tracking down the DNA regions associated with common complex diseases such as cancer, heart disease, diabetes, and some forms of mental illness. The new map also may be useful in understanding how genetic variation contributes to responses to environmental factors.

    The online presentation of this publication is a special feature of the Human Genome Project Information Web site.
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/template.txt0000600000175000017500000001276310425411107024022 0ustar madduckmadduck <% title %> <# if 'gallery' in plugins: print '' #>
    Voidspace

    rest2web: Building Websites Across the Known Universe

    Return to Top
    Part of the rest2web Docs
    Page last modified <% modtime %>.

    <# div = '
    ' for page in sidebar(indextree, div=div): val = page['crumb'] link = page['target'] if page['title']: url_class = ' class="sidie2title"' else: url_class = '' print '%s

    ' % (url_class, link, val) #>

    Powered by Python

    Support This Project

    Site Built with rest2web


    Site Built with rest2web SourceForge.net Logo Certified Open Source

    Python on Voidspace

    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section6/0000700000175000017500000000000010644674642023207 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section6/index.txt0000600000175000017500000000032710414273736025055 0ustar madduckmadduckrestindex crumb: Pruned Section /restindex =============================== Oh Dear - It's All Gone Wrong =============================== This subdirectory has been pruned, and so *shouldn't* appear anywhere. rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section6/__prune__0000600000175000017500000000000010327117674025042 0ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/index.txt0000600000175000017500000000243410335344157023322 0ustar madduckmadduckrestindex format: html page-title: The Big Index crumb: Test Site link-title: Test and Example Site template-encoding: utf8 page-description: This website tests all the features of **rest2web**. If it builds correctly then all is well. It also serves as an illustration of rest2web, including some of the more obscure options and capabilities. /description /restindex uservalues name = Fuzzyman paragraph = """Those are all the sections in this example site.""" /uservalues

    The Main Index Page

    Welcome to something.

    This is the test site for rest2web. It tests most of the features of rest2web. It also illustrates most aspects of the restindex and embedded_code.

    These Are the Sections

    So enjoy....<% name %>

    <# print_details(default_section, subsection_title='

    These are the Sections

    ') #>

    <% paragraph %>

    We also have Section 5. This isn't included, so I've added a link especially.

    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section5/0000700000175000017500000000000010644674642023206 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section5/subby/0000700000175000017500000000000010644674642024332 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section5/subby/index.txt0000600000175000017500000000050410414273736026175 0ustar madduckmadduckrestindex crumb: Subby /restindex ============================ A Subsection for Section 5 ============================ .. class:: indexblock I don't have much of a life of my own. I only exist to be a sub-section to `Section 5 <../index.shtml>`_. .. Can we use ``thispage`` to find a link to the parent page ? rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section5/index.txt0000600000175000017500000000345610414273736025062 0ustar madduckmadduckrestindex crumb: Section 5 file-extension: shtml include: No file: index.txt file: ../template.txt /restindex =========================== The Incredible Section 5 =========================== .. class:: intro Welcome to section five. As you might have gathered, this section is built - but not included in the indexes. This little intro section is written in `reST `_. It uses the ``.. class::`` directive to apply a style to the paragraph. .. class:: index-table +-----------------------+--------------------+ | A Table of Resources | +=======================+====================+ | `Text Source`_ | `Template Source`_ | +-----------------------+--------------------+ | | | +-----------------------+--------------------+ A table, drawn in reStructured Text. The text files are copied across using the ``file`` keyword - in the restindex. .. _Text Source: index.txt .. _Template Source: template.txt .. class:: indexblock This is another paragraph with a style applied. I happen to think it's very stylish indeed. I stole the {acro;CSS} for this from a nice chap called `Ian Bicking `_. You can visit my subsection if you want : .. raw:: html
    <# import urllib pageblock = '''\ %s

    %s

    ''' pages = sections[None]['pages'] # everything in default section in this index thepages = [] for page in pages: ref = pageblock % (urllib.quote(page['target']), page['link-title'], page['page-description']) thepages.append(ref) print '\n'.join(thepages) #>
    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/0000700000175000017500000000000010644674642023203 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/article3.txt0000600000175000017500000000016210246051200025424 0ustar madduckmadduckrestindex /restindex ============= Article 3 ============= This article has the smallest restindex possible !.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/hidden.txt0000600000175000017500000000065710311246051025165 0ustar madduckmadduckrestindex include: no crumb: Hidden /restindex ===================== The Hidden Page ===================== There's not a lot to see here, which is probably why it wasn't included in the index. It illustrates that you can have **rest2web** build pages, including navigation links, but not include them in the indexes. It doesn't appear in the sidebar either. This may or may not be a good thing of course {sm;:grin:}. rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/differentfolder/0000700000175000017500000000000010644674642026345 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/differentfolder/target.txt0000600000175000017500000000060210246051200030345 0ustar madduckmadduckrestindex page-title: Target Page crumb: Target page-description: This page is buried quite deeply. /description /restindex ================================ Testing, Testing, 1-2-3 ================================ Please bear with us, this is testing targets in subsections. Mainly testing that the index page above renders us with the correct link.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/index.txt0000600000175000017500000000216510247534203025044 0ustar madduckmadduckrestindex format: html page-title: Section 2 /restindex

    An Index Page for Section 2

    Welcome to something.

    These Are the Articles

    So enjoy....

    <# import urllib pagelist = '''\
      %s
    ''' pageblock = '''\
  • %s

    %s

  • ''' pages = sections[None]['pages'] # everything in default section in this index thepages = [] for page in pages: a_page = pageblock % (page['target'], page['link-title'], page['page-description']) if type(a_page) is unicode: a_page = a_page.encode('utf8') thepages.append(a_page) thepages = '\n'.join(thepages) this_sect = pagelist % thepages print this_sect #>

    Find the hidden page.

    Here is a link to a page that isn't included in the index.

    rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/subsection/0000700000175000017500000000000010644674642025361 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/subsection/index.txt0000600000175000017500000000010210246051200027175 0ustar madduckmadduckrestindex index-file: ../differentfolder/target.txt /restindexrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/article2.txt0000600000175000017500000000022310246051200025421 0ustar madduckmadduckrestindex link-title: Another Article /restindex ============= Article 2 ============= This is another article about nothing in particular.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/section2/article1.txt0000600000175000017500000000021210246051200025416 0ustar madduckmadduckrestindex link-title: An Article /restindex ============= An Article ============= This is an article about nothing in particular.rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/uservalues/0000700000175000017500000000000010644674642023653 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/uservalues/index.txt0000600000175000017500000000075610425411107025513 0ustar madduckmadduckrestindex page-title: Test of Uservalues /restindex uservalues test1: """A value in *ReST*.""" test2: A value in HTML test3: Never inserted. /uservalues ==================== Test of Uservalues ==================== This is a test of uservalues. The following paragraph is in ReStructuredText. <* test1 *> The next one is in HTML : {esc;test2} I wonder if it works. {sm;:-)} This one will be escaped by ``docutils``, and so not executed. <% test3 %> rest2web-0.5.2~alpha+svn-r248.orig/docs/test_site/uservalues/page.txt0000600000175000017500000000203210425411107025305 0ustar madduckmadduckrestindex page-title: Test of Uservalues crumb: Uservalues /restindex uservalues test1: """A value in *ReST*.""" test2: A value in HTML test3: Never inserted. /uservalues ========================== Testing Uservalues Again ========================== .. contents:: This is a test of uservalues. It tests the two different templating styles for inserting uservalues into a page. You can insert single values, or multiple blocks of code. Testing Single Values ===================== This is a test of uservalues. The following paragraph is in ReStructuredText. <* test1 *> The next one is in HTML : {esc;test2} I wonder if it works. {sm;:-)} This one will be escaped by ``docutils``, and so not executed. <% test3 %> Testing Multiple Code Blocks ============================ <$ print 'A comment in **ReST**.' $> <$ print try: print uservalue1 print print uservalue2 + '. {:-)}' except NameError: print 'Failed to find the uservalues. {sm;:-(}' $> rest2web-0.5.2~alpha+svn-r248.orig/docs/the_restindex.txt0000600000175000017500000004175410513534132023055 0ustar madduckmadduckrestindex crumb: restindex # we can't call the source file restindex.txt ! target: restindex.html page-description: An explanation of the options and default values in the restindex. /description /restindex =============== The restindex =============== -------------------------------------------- Controlling rest2web Through the restindex -------------------------------------------- .. contents:: The restindex Introduction ============ reST format alone doesn't tell us everything we need in order to build the pages. It will generate a title from the top headline - but it won't tell us things like which template file to use, what text to use as a description of the page in the index, etc. For this I've added something called a *restindex*, which is a simple list of keywords and values to go at the start of every page you want rest2web to render for you. There is another set of values you can add at the start of your files. These are *uservalues* and they allow you to add your own values into the namespace when templating. See the uservalues_ page for the details. The Master restindex ==================== This shows every option in the **restindex** - with an example value. In practise you only need to ever *use* values that are different from the defaults. For a good example see the tutorial_. :: restindex # this is a comment include: yes format: html file-extension: shtml template: template.txt template-encoding: utf8 index-file: index.txt section: section name link-title: This is the text to use in the link page-title: We *usually* only need a title for HTML page-description: this is a description It can go across several line And even be indented a bit. It will be interpreted as *reST*. /description crumb: Short Name build: yes initialheaderlevel: 1 tags: keyword1, keyword2, keyword3 file: filename1 file: filename2 file: filename3 target: thisfile.html encoding: utf8 output-encoding: utf8 sectionlist: section-name1, section-name2, section-name3 section-pages: section-name1, page1, page2, page3 section-title: section-name1, title section-description: section-name1 This is also a description. /description section-pages: , page1, page2, page3 section-title: , Default Section Title # note the leading ',' to specify the default section section-description: Default section description. /description /restindex Every **rest2web** source file must start with a 'restindex' chunk. This tells rest2web how to render the page. If a text file doesn't start with a restindex, it won't be processed. To qualify as a restindex - the first non-empty line of the file must be **restindex**. Subsequent lines that start with a '#' are comments. Each line is in the format - ``keyword: value``, except for multiline values. Currently this means only the ``description:`` type keywords. If a keyword is missing, or the value is blank, then the default will be used. The restindex is used for two purposes. 1) For setting values for individual pages. In this case the index is at the start of the page that is being processed. Only certain values are relevant. 2) In the index page, the restindex can set options for a whole directory. Restindex and Uservalues in a ReST Comment ------------------------------------------ Since rest2web 0.5.0, you can put the restindex (and `uservalues `_) inside a ReST comment. That means that your source documents can contain a restindex *and* still be valid ReStructured Text documents : :: .. This is a ReST Comment This page is copyright by me, 1941 restindex format: html page-title: This is a Phantasmagorical Page /restindex uservalues site_title: My Great Site copyright: A Copyright Message /uservalues Due to technical limitations (read laziness), the restindex and the uservalues *must* be the last part of the comment. {sm;:-)} The Keywords ============ The keywords are as follows. [#]_ : #. **include** - include this file in the index ? Should be yes or no. If no, the page is still generated from the template - but not included in the index page. This value applies to both normal files and index files. Default value is yes. #. **format:** - so far this can either be 'rest' or 'html'. It would be theoretically possible to allow other markup formats like 'sextile', or 'textile'. This is the markup format of the page. If it is 'rest' then docutils is used to generate the html (and usually the page title). The page contents are processed for embedded code, along with the templates (this allows you to use the templating in your indexes, using the ``.. raw::`` directive you can even do this in reST documents). The default value for this is 'rest'. #. **file-extension:** - what file extension to use for this file. You don't need to specify the '.'. Normal values would be *html*, *htm*, *shtml*, or *shtm*. In 'index.txt' it can apply to a whole directory. The default value for this is 'html'. (Grr... I'd prefer it to be 'shtml', but there you go). #. **template:** - this is the file to use as the template file. Relative locations will be relative to this directory of course. The template is processed for each page - and the body inserted, title created, dynamic elements are processed etc. See note on `Handling Paths`_. In an 'index.txt' this can specify the template file for the whole directory (and directories below it as well). The default value for this is 'template.txt'. If none is specified in 'index.txt' and 'template.txt' doesn't exist, then 'template.txt' in the directory above will be tried. (moving up directories until one is found) #. **template-encoding:** - This is the encoding of the specified template file. If an encoding isn't supplied then it will be guessed. #. **index-file:** - this specifies which page to use as the index file. (This file is used as the source to *build* the index file and as the target to link to in the index for the next level up). If 'index.txt' has this value, then all other values will be read from the restindex of the specified file. See note on `Handling Paths`_. It only has any meaning in the 'index.txt'. It can be used to specify an alternative page as being the main index page for this directory. The index page in the next level up will link to the target of the specified file. Default value is 'index.txt'. #. **section:** - This specifies what section this page should appear in, in the relevant index page. For 'index.txt', the relevant index page is the next level up. The section is the section this index page will appear in, in *the index above* ! Got it ? (So for the 'root index' this value will never be used). The default value is for a page to be in the ``None`` section. #. **link-title:** - this is the text used in the *link* to this page from the index page. In an 'index.txt', this will be the link to the index from the next level up. The page title will be uased as the default value, if none is specified. #. **page-title:** - this is the page title. You should specify the ``page-title`` for index pages *and* for html pages. If the page body is in rest, this value overrides any autogenerated value. In an 'index.txt' this has the same meaning as in ordinary pages. There is no default value for this *FIXME: could use the filename ?* #. **page-description:** - This marks the start of the description, which actually starts on the next line. The description is the text used along with the link to this page. Currently description format is 'rest' only. If you want to include HTML you can always use the 'raw' directive ! The page description can go over several lines - it terminates with '/description' on a newline. Uniform indentation (spaces) that occurs on *every* line, will be removed before processing. In an 'index.txt', this is the description used in the next level up. The default value is '' (no description). #. **crumb:** - this is a short name for the page. It will be used for generating the 'breadcrumb' navigation links. For index pages, this name will form a link back to the page. The default value is to use the page title [#]_. (Which will usually be too long). For index pages (or if no page title is available), the filename is used as the default crumb instead. #. **target:** - This specifies the target file. This will be the filename of the 'built' file, and the filename linked to in the index. If this keyword is used, the 'file-extension' keyword is ignored. (So a file extension must be included in the value for this keyword). The target can be a relative path to the file. See note on `Handling Paths`_. This value applies equally to 'index.txt' as other files. The default value is the filename, minus '.txt', plus the value of the 'file-extension' keyword. #. **build:** - This should be 'yes' or 'no'. This allows you to specify that a file is not to be built, but should be included in the index. This allows you to include prebuilt files in the index. In conjunction with the target keyword, it can be used to link to files in other locations. This value applies equally to 'index.txt' as other files. The default value is 'yes'. #. **initialheaderlevel** - An integer value. If your page format is ReST, then this value is passed to docutils when the page is built. This specifies the starting level (``h1``, ``h2`` etc) for headers in the page. This value applies equally to 'index.txt' as other files. The default value is '1'. #. **encoding** - this is the encoding of the file. The value must be a recognised python standard encoding. This value applies equally to an index file. The default value is to ignore encoding and leave the 'subtools' (docutils) to guess encoding. #. **output-encoding** - this is the encoding the page is written out as. The ``output-encoding`` ought to match the encoding used for the template ! The value must be a recognised python standard encoding, or one of two special values. **unicode** means don't encode the page - but present it to the template as a unicode string. **None** means use the encoding the page was originally encoded with. This value applies equally to an index file. Setting it in an index file will set this value as the default for the whole directory *and* any subdirectories. (Unless an alternative is explicitly specified). The default value is 'None', which encodes the page using the encoding the page was originally encoded with. If a value is specified in any of the index pages above this page, then that will be used *instead* of the default. .. warning:: Not including this keyword *is not* the same as including the keyword with no value. Not including it will cause the default for this directory to be used. Including it, but leaving it empty is the same as specifying ``None``. #. **sectionlist:** - This is a list of different sections for an index page. Links to pages in this directory appear in these different sections. The names should be unique as they will be used as "id's" to reference the section in the index. This value *only* has meaning in an index page. The default value is to have a single section referenced as 'default' if it isn't named. #. **section-title:** - This is how you specify the title for each section. The first value is the nameof the section, followed by the title to be used for the section. This can be made into a link back up to a section table at the top of the index page. You can have multiple section titles, one per section. This value *only* has meaning in an index page. The default value is to use the section name as the title. (In 'Title-Case'). #. **section-pages** - This specifies the *order* that pages appear in the section, for example in sidebars and in the ``sections`` data structure (used by the ``print_details`` `standard function`_). The first value is the section name (or a leading comma for the default section). You use the filename (minus the '.txt') to specify a page. You don't have to list all the pages - but including a page that doesn't exist will raise an error. The pages you specify will appear in that order, followed by the rest of the pages in an arbitrary order. If you want to specify a subdirectory then use the directory name followed by 'index'. For example - ``subdirectory/index``. This value *only* has meaning in an index page. The default value is for pages to be in an arbitrary order. #. **section-description:** - This is a description used for this section. It will appear above the links to the pages in the section. Other details are the same as the *page-description*. This value *only* has meaning in an index page. The default value is '' (no description). #. **tags** - This option takes a list of keywords that applies to the page. You can then use this list in your template and pages. (E.g. for the ``meta type="keywords"`` data). This value applies equally to an index file. The default value is for it to be an empty list. #. **file** - This option specifies an additonal file to copy from the source directory to the same directory as the target file. You can use this keyword multiple times in the restindex. This value applies equally to an index file. The default is for no additional files to be copied. #. **plugins** - A list of plugins that should be applied to the page. See the plugins_ page for more details. Building the Index ================== The contents of a directory will only be built if it has an 'index.txt' file. This doesn't mean that the main file needs to be called 'index.html' or similar. There are two ways of changing this. First - the 'index.txt' file can contain the content, but have a *target* value set. This sets the filename that the built file will be saved as. Alternatively, 'index.txt' can have an *index-file* value. This will specify an alternative file that is to be used as the index page. In this case *all* the other values in 'index.txt' will be ignored and the values from the specified file will be used. This means the restindex will be very short : :: restindex index-file: anotherfile.txt /restindex Handling Paths ============== Windows, Linux, and the Mac, all handle paths differently. rest2web uses the file structure on disk to represent the structure of the website [#]_. This means we are using the same relative file paths to represent the file path on the local computer (on which rest2web is running) and the links within the website. Because the different OS's represent filepaths differently - we have a conflict between specifing file paths and urls. A few of the restindex keywords expect a path. Specifically ``template``, ``index-file``, and ``target``. ``template`` is always a filepath - because it specifies a file which is built into other pages. This means it can be specified as an absolute location - or using a native, relative file path. For the other two values, ``index-file`` and ``target``, rest2web expects the path to be a *relative path, that follows the Unix path conventions*. This greatly simplifies our path handling routines. restindex.txt ============= You may be using rest2web to only generate some of the pages in your website. In this case it can be useful to provide a restindex for pages that rest2web *isn't* building. This enables rest2web to include them in indexes etc. You can easily doing this by setting the ``build: No`` option in the restindex. If you have several pages like this in a directory it can be a nuisance to maintain all the files for them. rest2web allows you to put several restindexes in a file called 'restindex.txt'. See the `Special Files`_ page for more details. As a summary - every restindex in 'restindex.txt' will be assumed to have ``build: No`` set, but you must specify a target - or all the ouput files will be named 'restindex.html' {sm;:badgrin:} ------------ Footnotes ========= .. [#] If you see **#** by every entry in this list (in the output html) rather than a numbered list, you need to update your version of docutils. (0.3.8 or more recent required) .. [#] For index pages we may not pick up on the default of using the title - this is because we don't generate the page when reading the defaults. Index pages **need** crumbs ! .. [#] Although you can override this using the *'target'* keyword. .. _Special Files: special_files.html .. _uservalues: reference/uservalues.html .. _tutorial: tutorial.html .. _standard function: functions.html .. _plugins: reference/plugins.html rest2web-0.5.2~alpha+svn-r248.orig/docs/example_function.txt0000600000175000017500000000124310311246051023531 0ustar madduckmadduck# # syntax coloring try: import colorize as col except ImportError: def colorize(filename): raise ImportError, 'Importing colorize.py failed.' else: def colorize(filename): """ format a python script as html Using the appropriate css it will be nicely colored. Needs the colorize.py module. """ fullname = os.path.join(filename) f = open(fullname, 'r') data = f.read() f.close() p = col.Parser(data) p.format(None, None) src = p.getvalue() return src.replace('\n', '
    \n').replace(' ', '  ') # to avoid having to use
    ..
    rest2web-0.5.2~alpha+svn-r248.orig/docs/templating.txt0000600000175000017500000004375610530171334022360 0ustar  madduckmadduckrestindex
        crumb: Templates
        link-title: Templates
        page-description:
            A description of templating, and the special values available in the
            namespace.
        /description
    /restindex
    
    ==========================
     Templating with rest2web
    ==========================
    --------------------------
     Using rest2web Templates
    --------------------------
    
    
    .. contents::
    
    
    Introduction
    ============
    
    **rest2web** templates are rendered using a python module called ``embedded_code`` from the firedrop2_ blog tool. It is aptly named; because it allows you to embed chunks of Python_ code into text files. Wherever there is a chunk of Python in the template, this is executed - and the output is put into the text. This means that if you are a Python programmer, the templating system is *very* simple. {sm;:-)}
    
    Even if you've never used Python before, the system is very easy to learn. Have a look at `the tutorial`_, which will get you up and started very quickly. Of course, once you start using Python, it won't be long before you're addicted. {sm;:wink:}
    
    This page describes the templating system - and the *special values* that you use with it. These values can be used in your {acro;HTML} templates *and* your pages.
    
    
    The Templates
    =============
    
    The point of templating is to dynamically insert values into generated pages. The **rest2web** templating system can be used to insert special values into your templates (which go into every page that uses that template) - or into individual pages.
    
    There are two sets of tags to do this, depending on whether you want to insert HTML or ReST into your templates. You would only insert ReST into your *pages*, rather than into your templates. This would usually be done in conjunction with `uservalues `_.
    
    Inserting HTML
    --------------
    
    There are two sets of tags to use with template values. The simplest is to insert a single name between template tags. This is evaluated and the value inserted into the page. This looks like -  ``<% name %>`` - where *name* is our special value.
    
    The second way allows you to execute blocks of code. Chunks of code can be inserted between ``<# ... #>`` tags. Anything that is printed [#]_ will be put into the page. The code must be valid python, which includes obeying Python's rules about indentation. Here's a quick example : ::
    
        <# 
            import urllib
            blank = ' '
            row = '%s%s'
            entry = '%s'
            
            # Get a list of the sections *excluding* the default section
            sectlist = [(urllib.quote(section), section.title()) 
                            for section in sections if section is not None]
            
            index = 0
            while index < len(sectlist):
                entry_1 = entry % sectlist[index]
                if index == len(sectlist)-1:
                    entry_2 = blank
                else:
                    entry_2 = entry % sectlist[index+1]
                print row % (entry_1, entry_2) 
                index += 2   
        #>
    
    In case you can't work it out, this chunk of code uses ``urllib.quote`` to make section names safe for inclusion in a {acro;URL} link. It then inserts a link to each section in an anchor tag (*entry*) in a table row (*row*).
    
    ``embedded_code`` will automatically remove any (uniform) excess indentation - this means the whole chunk of code can be indented to fit in with your page structure.
    
    The code chunks are all executed inside a **namespace**. This determines what variables (names) are available to you to use. All the code in a page is executed inside the same namespace - this means that changes you make in a chunk of code at the start of a page will be visible to later chunks of code. This can be useful - but it also means you shouldn't mangle variables you intend to use later.
    
    There are a set of `standard functions`_ available to you to perform common tasks. Printing indexes, breadcrumbs, and sidebars can all be done with single line commands.. {sm;:-o}
    
    
    Inserting ReST
    --------------
    
    The basic principle for inserting ReST into your pages (before they are turned
    into HTML) is exactly the same as inserting HTML into your template pages.
    
    The difference is, that you use different tag styles. For single values use :
    
        ``{lt}* ... *>``
        
    For chunks of embedded code use :
    
        ``{lt}$ ... $>``
        
    
    The List of Values
    ==================
    
    So the only thing remaining is for you to know what special values are available. Below is the full list of *normally available values*. You can also add your own values to the namespace, using uservalues_ [#]_ :
    
    #. *title*     - title of the page being rendered.
    
    #. *body*      - full body of the page.
    
    #. *breadcrumbs* - this is a list of tuples that represents the navigation trail for this page. 
    
        Each tuple is ``(crumb_link, crumb)``. ``crumb_link`` is a relative link from the document being rendered to the target of the crumb. This means the crumb links will still work when the site is viewed from the filesytem, or moved around in a website. 
        
        You can use a chunk of code to insert these into links : ::
        
            <#
                # this is the breadcrumbs code
                # breadcrumbs is a list of tuples
                anchor = '%s'
                divider = ' > '
                index = -1
                while index < len(breadcrumbs)-2:
                    index += 1
                    entry = breadcrumbs[index]
                    print (anchor % entry),
                    print divider,
                if breadcrumbs:
                    print breadcrumbs[-1][1]    # not as a link
            #>  
            
        An alternative way of doing your breadcrumbs, used in the example site, is available as a `standard function`_ **print_crumbs**. It makes each 'crumb' a list item - '
  • ..
  • '. CSS is then used to show this as a horizontal list, without bullet decoration. Simply put ``<# print_crumbs(breadcrumbs) #>`` in your template to call it. See the `standard functions`_ page for more details. #. *sections* - This is a data structure with information about all the pages in the current directory. It is a dictionary with all the sections. Each key is a section name (the default section is keyed by ``None`` - ``sections[None]``). Each section is also a dictionary. Each section dictionary has the following keys : * title * description * pages - this is a list of dictionaries. Each dictionary represents a page. Each page dictionary has the following keys : * **target** - the page location * **section** - which section it is in * **link-title** - the title to use in the link to the page * **page-description** - a description of the page * **crumb** - the crumb (very short name) for that page * **namespace** - the namespace for the page (a dictionary). Values will be encoded with the ``final_encoding`` for *this page*. If the page isn't being built then this value will be ``None``. * **subdir** - does this page represent a subsection ? (it will be the index page for a subdirectory if this value is ``True``) The values 'crumb', 'link-title', 'section', and 'page-description', will all be encoded using the ``final_encoding`` for the *page that is accessing them*. This means it is safe to use them directly in your template without worry about the encoding of the page they come from. This is not true for values in the 'namespace' of each page. ``sections`` has a method for sorting the pages contained in all the section dictionaries. By default the pages are sorted according to the ``section-pages`` in the restindex. If you don't use section-pages, the ordering is arbitrary. The ``sortpages`` method allows you to sort the pages by ``link-title`` (alphabetical) or pass in a custom function to do the sorting. (The same sort of function you would pass to the Python ``sort`` method of lists.) .. raw:: html {+coloring} sections.sortpages(sortorder, section=True) {-coloring} ``sortorder`` can either be the string ``link-title`` or a custom function. By default, calling ``sortpages`` will sort all the sections. Alternatively you can pass in a string (or None), to specify which section to sort. Don't forget that there will always be a section with the key ``None`` - this is the default section. If you're not using sections, all the pages will be in this section. If every page is in a named section then this section will be empty - but it will still exist. This means that if you loop over all the sections (``sections.keys()``) one of them will be ``None``. You can use the 'sections' value to automatically construct things like table of links, like the example at the start of this page. There is also a `standard function`_ called ``minibar`` available to you. This is typically used to create a sidebar that contains links to all the pages in the section. You can call it by putting ``<# minibar(sections) #>`` in your template. See the page on `standard functions`_ for more details (and other parameters you can pass to it). Note that the 'sections' value has *references* to the namespace of each page (in it's 'pages' value) - not a separate copy. That means that if your template changes anything in the pages, this change is likely to affect the rendering of other pages. See the *minibar* function in the `standard functions`_, for one way of handling this value. #. *default_section* - This is the default section from the ``sections`` value. It is *exactly* the same as ``sections[None]``. #. *pagename* - the filename of the page (not the full path). #. *pagepath* - full path from top level to current document. #. *encoding* - the original encoding supplied or guessed for the document #. *output_encoding* - the output encoding specified for this page. It can be a specific encoding, or ``none``, or ``unicode`` (both text values). See the docs on the restindex_. #. *final_encoding* - This is the final encoding used to encode the output of the page. It may be ``None`` if the value for *output_encoding* was ``unicode``. (In which case the final encoding will follow the template encoding). #. *path_to_root* - path from current document to top level site directory (ends in '/'). This can be **very useful** for providing paths to resources without having to use an absolute URL. For example; if your images folder is in your top level directory you could use ```` instead of ````. This means the reference resolves correctly when viewed from the file system. #. *sectionlist* - The list of subsections in this directory. #. *rest_dict* - only present if the page generated from reST content, else ``None``. This is the full dictionary of parts returned by the docutils ``html_parts`` function. #. *doc* - everything that has been printed so far (only available for ``<# ... #>`` type code). This is a ``StringIO`` instance. #. *stdout* - the *real* ``sys.stdout``. Can be used if you want your code to print a traceback to the shell (only available for ``<# ... #>`` type code). #. *modified* - the time the page was last modified, as a number of seconds since the *epoch* [#]_ #. *modtime* - a string representing the time the page was last modified, created using ``time.ctime(modified)``. #. *template_file* - a path to the file we are using as the template for generating this page. #. *template_encoding* - the encoding used to decode the template file. #. *indexpage* - the information dictionary for the index page for this section. This page *isn't* included in the ``sections`` value, so it is available separately. #. *indextree* - ``indextree`` is a nested data structure representing the pages that have been rendered so far. This will include all the pages and directories *above* the one currently being rendered. ``indextree`` itself is the top level index page (the 'root' of your site). It has members which represent all the other pages and directories. Each member is a page. If a page is an index page, it may itself have members that are pages in that directory. Get all that. {sm;:-)} You can use this data-structure to build sidebars and menus etc. See the *sidebar* function in the `standard functions`_ for a simple example of using ``indextree`` to do this. For full details on this complicated value, see the indextree_ page. #. *thispage* - This is a reference into the ``indextree`` data structure. It just points to the current page. It would allow you to create menus from the 'bottom up'. For example you could create a navigation trail from the current page back to the main index, rather than the other way round. Note that if the current page has 'include' set to 'No', then *thispage* will be ``None``, because the current page isn't in *indextree*. #. *sidebar* - A `standard function`_ for printing sidebars. In fact it is a generator that yields pages one at a time. For details on how to use it see the page on `standard functions`_. #. *minibar* - Another `standard function`_ for printing simpler sidebars. It prints a list of all the pages in the current section. For details on how to use it see the page on `standard functions`_. #. *print_crumbs* - This is a `standard function`_ for printing navigation trails, known as breadcrumbs. For details on how to use it see the page on `standard functions`_. #. *print_details* - This is a `standard function`_ for printing indexes. #. *section_contents* - This is a `standard function`_ that returns a list of tuples about pages in a specific section. The data it returns is easier to use than the data contained in ``sections['section_name']['pages']``. #. *Processor* - The actual processor object that is doing the work. Be careful with it {sm;:grin:}. A few potentially useful attributes are ``Processor.dir``, ``Processor.dir_as_list``, and ``Processor.dir_as_url`` [#]_. #. *page_description* - A string with the description of the current page. Can be useful if you only want to write it in one place. {sm;:-)} #. *include* - This is a `standard function`_ for including files within templates. These can be nested (included files can include other files). Files are first looked for in the directory being processed, if it isn't found the parent directory is tried, and then its parent etc. Included files *will* be processed, so they can include embedded code tags. #. *globalValues* - This is a dictionary of globalValues that will be available to all your pages. Initially it is empty, but anything you put in here will be available in all your pages. You can use this to share information between pages, or setup global values. If you do this in your template, you can check if the values exist already and only set them up once. #. *current_dir* - The current directory being rendered, relative to the top level directory. You can turn this into an absolute path using ``os.path.join(os.getcwd(), current_dir)``. #. *source_file* - The source filepath for the page. #. *target_dir* - The target file directory (as an absolute file path) being rendered into. Note: if the file has a target specified it may not be put in this directory. Use ``os.path.dirname(target_file)`` instead. #. *full_page_url* - The full url (starting with '/') for the current page. Using this means that your pages may not work from the filesystem. {sm;:-)} #. *target_file* - The full output filepath of the page being rendered. Pages and templates are passed through ``embedded_code``. This means that if you have HTML pages you can embed code in them. In reST pages you ought to be able to use the *raw* directive to embed code chunks. This is primarily useful for index pages, where you want to use embedded code to dynamically generate the *body* of the page - depending on what sections and links are available. The Template File ================= You can specify a template file for every page and index - using the ``template`` value in the restindex. If none is specified it first tries ``template.txt`` in **that** [#]_ directory first (even if the index is in an alternative directory using ``index-file``). If a template file is specified in the index-file, then the location must be specified relative to the directory the index-file is in. If no template file is specified, and there is no 'template.txt' in the same directory as the index file, then the template for the directory above will be used. This means that if you have a 'template.txt' in your root directory it can be used for the whole site. Note About Macros ================= There is another way of calling simple functions from within templates. These are called macros. Macros allow you to use a shorthand for often-used text or HTML. Included is an example macro file (and supporting modules), It has macros for inserting smilies, colorizing Python files, acronyms, etc. You will need to modify it for your own sites, by putting the correct paths into the source code at least. See the page on macros_ for more details. ---------------- Footnotes ========= .. [#] Or written to ``stdout``. .. [#] You can also specify 'global uservalues' at the command line or in the config file. These will be available to every page. .. [#] See the Python ``time`` module. http://docs.python.org/lib/module-time.html .. [#] These are different ways of representing the directory being rendered. .. [#] By *that directory* - I mean the directory that the ``index.txt`` file is located in, and where probably most of the content will be located. .. _firedrop2: .. _firedrop: http://www.voidspace.org.uk/python/firedrop2/ .. _standard functions: .. _standard function: functions.html .. _the tutorial: tutorial.html .. _macros: macros.html .. _restindex: restindex.html .. _python: http://www.python.org .. _indextree: reference/indextree.html .. _uservalues: reference/uservalues.html rest2web-0.5.2~alpha+svn-r248.orig/docs/functions.txt0000600000175000017500000004231710604744342022223 0ustar madduckmadduckrestindex crumb: Functions page-description: The standard functions for printing indexes, navigation trails, and sidebars. /description /restindex ========================== The Standard Functions ========================== --------------------------- The Functions in rest2web --------------------------- .. contents:: Functions Introduction ============ {emo;python} **rest2web** provides varous data structures for use in your templates. These can be used to create navigation trails and simple or complex sidebars. It is probable that most people using **rest2web** will want to create similarly constructed results - even if the visual appearance differs. Included in the namespace the templates are rendered in, are several standard functions that do just this. The Functions ============= The standard functions are defined in the file *functions.py*, in the rest2web directory. They are also good examples of how to use the data structures. If you find yourself regularly defining and using *different* functions then `let me know`_. I can include them in this file. [#]_ See the templating_ and indextree_ page for more details of the values used by the standard functions. These functions all have sensible defaults - but can be controlled by passing in keyword arguments. This means you only have to pass in arguments that you need to change from the default. You include them in the template between ``<# .... #>`` style tags. For example, you can print the standard *breadcrumbs* trail for a page using : :: <# print_crumbs(breadcrumbs) #> If you wanted all the elements to be in bold you could change the ``item`` keyword : :: <# print_crumbs(breadcrumbs, item='
  • %s
  • ') #> Abetter way of doing that would be through {acro;CSS} of course, but you get the idea. {sm;:-p} print_details ------------- This function provides an ultra easy way of printing index pages. It will print a list of all the pages in a section - with a link and the description. You can configure the {acro;HTML} used - and whether or not the description is included. .. raw:: html {+coloring} print_details(section, page_title='

    Pages

    ', subsection_title='

    Sub Sections

    ', item_wrapper = '
      %s
    ', item='
  • %s
  • ', link='%s', description='%s', do_description=True, split=True, do_pages=True, do_subsections=True ): {-coloring} This function is a quick way of printing all the pages and sub-sections in a section. You can use it without having to understand the ``sections`` data structure. It prints a menu of all pages (links and descriptions) and sub-sections. It has sensible defaults - and is configurable in terms of the HTML used. Including the description of each page is optional. You can also elect to do pages and subsections combined, separately, or just one or the other. Like the other function it needs "%s" in some of the values - which are filled in automatically. The default layout looks like : ::

    Page Title

    ---> ``page_title``
      ---> ``item_wrapper`` along with the corresponding ``
    ``. This wraps all the links.
  • ---> ``item`` along with the corresponding ``
  • ``. This wraps each item - whether or not it includes the description. Link Title ---> ``link``. This contains the URL *and* the link title. Page description. ---> ``description`` . Optional, controlled by ``do_description`` This is then repeated for the subsections. If ``split`` is ``False`` then all the pages and subsections are combined as the pages. The options are : * ``page_title = '

    Pages

    '`` This is the title line printed before the pages. * ``subsection_title = '

    Sub Sections

    '`` This is the title line printed before the Sub Sections. * ``item_wrapper = '
      %s
    '`` This is the wrapper around all the links. It needs one "%s" in it. * ``item = '
  • %s
  • '`` This is the wrapper around each link. It needs one "%s" in it. * ``link = '%s'`` This is the link. It needs two "%s" in it. * ``description = '%s'`` This is for the description and is put immediately after the link. It needs one "%s" in it. (You could put a tags around the description to style them separately. E.g. ``description = '' {-coloring} The above code prints the pages and subsections for every section. Each section is in it's own ``div`` with the section name as a title. If you want to wrap pages and subsections in their own ``div`` (so you need them outputting separately), you could do : ::
    <# print_details(default_section, do_subsections=False) #>
    <# print_details(sections[None], do_pages=False) #>
    section_contents ---------------- ``section_contents`` makes it easier to access the information in the ``sections`` data structure. You pass it an individual section (i.e. ``sections['section_name']``) and it returns a list of the pages and subsections. Usually it is only this information that you want. :: section_contents(section, split=True): Passed in a section - this function returns the pages and subsections. Each page (or subsection) is returned as a tuple : ``(url, link title, description)`` The urllib is escaped for putting straight into the link. If split is ``True`` (the default) this function returns a list of pages and a list of subsections. If split is ``False`` it just returns a single list. An example use of this function might be : .. raw:: html {+coloring} pageblock = '''\
  • %s

    %s

  • ''' # just use the default section pages, subsections = section_contents(sections[None]) # first print the pages if pages: print '

    Pages

    ' print '
      ' for page in pages: print pageblock % page print '
    ' # next - the subsections if subsections: print '

    Subsections

    ' print '
      ' for page in subsections: print pageblock % page print '
    ' {-coloring} Another example that only uses the link and link title for each page : .. raw:: html {+coloring} # Get all the pages in the default section # as a single list pages = section_contents(sections[None], split=False) link = '%s' for page in pages: url = page[0] title = page[1] # we don't use page[2] which is the description print link % (url, title) print '
    ' {-coloring} You can actually achieve the same as the above examples by using the print_details_ function. print_crumbs ------------- The ``print_crumbs`` function provides a way of easily adding a navigation trail to your website. It uses the ``breadcrumbs`` value. In it's simplest form you put ``<# print_crumbs(breadcrumbs) #>`` in your template. It also takes other values which define how it prints the trail, and the dividers between the links. Here is the function description, which explains how to use it : .. raw:: html {+coloring} def print_crumbs( breadcrumbs, item = '
  • %s
  • ', anchor = '%s', divider = '>', ): {-coloring} A function to print the breadcrumbs (navigation) trail for a page. The idea is that all the index pages above the current page are shown as links. There are dividers in between, and the crumb of the current page is shown (but not as a link). You pass in the breadcrumbs values. It needs an item value with one ``'%s'`` place holder. Every link and divider (and the last value) is put into this item. The default value is ``'
  • %s
  • '``. It needs an anchor value with two ``'%s'`` placeholders. Into this are inserted the link and the 'crumb'. The default value is ``'%s'`` The last crumb is printed without the use of the ``'anchor'`` value. It also needs a divider which is printed between the crumbs. The default value is ``'>'`` By default this function uses list items to display the crumbs. You should surround it using something like : :: '
      ...
    ' Then use css rules like the following to format the display of the crumbs : :: #crumbs { background-color:#c99; padding:5px; text-align:center; font-size:15pt; font-weight:bold; } #crumbs ul { margin:0; padding:0 } #crumbs li { display:inline; list-style:none; margin:0; padding:5px; } So to display the breadcrumbs trail without using list items, you could put the following in your template : :: <# # item no longer uses ``
  • `` item = '%s' print_crumbs(breadcrumbs, item=item) #> If you specify ``dividers=None``, then no dividers at all will be printed. minibar ------- This function prints a simple sidebar that shows links to all the pages in the current directory. It uses the ``sections`` value to get it's information. It *doesn't* print a link to the index page in a directory. Here is the function definition : .. raw:: html {+coloring} minibar(sections, item = '
  • %s
  • ', intro = '

    Pages

    ', subsect=True, subintro = '

    Sub Sections

    ', liststart = '
      ', listend = '
    ', displayval = 'link-title' ): {-coloring} This function prints an alternative sidebar to the 'sidebar' function. It uses the ``'sections'`` value rather than indextree and only goes through the pages in the current directory. It can optionally differentiate between pages that are themselves 'subsections' (index pages for sections below) and ordinary pages. You need to pass in the 'sections' value, as well as any of the following optional keyword arguments : If ``'subsect'`` is ``True`` (the default) then minibar divides pages into ordinary pages and 'subsections'. Otherwise they're all pages. If there are any pages then the value 'intro' is printed. Default is ``'

    Pages

    '``. Then liststart is printed. Default is ``'
      '``. The for each page the following is printed : :: item % (page['target'], page[displayval]) The default for item is ``'
    • %s
    • '`` The default for displayval is ``'link-title'``. (It should be one of the values stored in each page. An alternative value would be ``'crumb'``). Then listend is printed. Default is ``'
    '``. If there are any subsections (and ``'subsect'`` is ``True``) then value ``'subintro'`` is printed. Default is ``'

    Sub Sections

    '`` Then the same sequence as for pages is printed : :: list start, the page links, listend Note: it doesn't include a link to the index page in a section. You will need to include this yourself separately. To print a link to the index page *and* all the pages in the section you can use something like the following in your template : :: <# print '

    Main Page

    ' % indexpage['target'] minibar(sections) #> It uses the ``indexpage`` value as well as the ``minibar`` function. sidebar ------- This is slightly different to the other functions. It can be used to produce sidebars with links to all the pages in the sections above the current page. It is actually a 'generator' rather than a function. This means you iterate over it and it yields pages one at a time. It works from the top level down, and it wraps each section in a 'div' block. This allows you to visually display the nested nature of the sections. ``sidebar`` in it's defualt behaviour does this by indenting the sections 10 pixels each time : .. raw:: html {+coloring} sidebar(thetree, div='
    ', undiv='
    ', cmp=None): {-coloring} A generator for dealing with the ``'indextree'`` of pages and indexes. It is recursive for handling the nested 'branches' of the tree. It goes through all the pages from the top level of the tree provided. It yields pages one at a time - index page first. (It sets ``page['title'] = True`` on index pages - otherwise ``page['title'] = False``. This allows you to display them differently). Before moving down the tree it prints a value called ``'div'`` after finishing a branch it prints 'undiv'. Default value for div is ``'
    '``. Default value for undiv is ``'
    '``. You would typically use this in a template with something like : .. raw:: html {+coloring} for page in sidebar(indextree): val = page['crumb'] link = page['target'] if page['title']: print '
    %s' \ % (link, val) else: print '
    %s' % (link, val) {-coloring} It might be more sensible to pass in a div with a named class. This makes it easier to control the style through CSS. For example ``for page in sidebar(indextree, ' ___> Only present with ``wrapper_class``. This is then repeated for the subsections. If ``split`` is ``False`` then all the pages and subsections are combined as the pages. The options are : page_title = '

    Pages

    ' This is the title line printed before the pages. subsection_title = '

    Sub Sections

    ' This is the title line printed before the Sub Sections. item_wrapper = '
      %s
    ' This is the wrapper around all the links. It needs one "%s" in it. item = '
  • %s
  • ' This is the wrapper around each link. It needs one "%s" in it. link = '%s' This is the link. It needs two "%s" in it. description = '%s' This is for the description and is put immediately after the link. It needs one "%s" in it. You could put a tags around the description to style them separately. E.g. ``description = '
    ' % wrapper_class if split: pages, subsections = section_contents(section, split=True) else: subsections = [] pages = section_contents(section, split=False) # if pages and do_pages: print page_title entries = [] for page in pages: val = link % (page[0], page[1]) if do_description: val += description % page[2] entries.append(item % val) print item_wrapper % ('\n').join(entries) # if subsections and do_subsections: print subsection_title entries = [] for page in subsections: val = link % (page[0], page[1]) if do_description: val += description % page[2] entries.append(item % val) print item_wrapper % ('\n').join(entries) if wrapper_class: print '
    ' def include(filename, alternative=None): """ Include a file in the page or template. If the filename is not an absolute or relative path (just a filename), walk up the directory tree looking for a file to include. It returns the file as text or raises an ``IOError``. It takes an optional ``alternative`` keyword, which will be returned if the file is not found. """ curdir = namespace['current_dir'] path, _ = os.path.split(filename) if not path and '\\' not in filename and '/' not in filename: while True: newname = os.path.normpath(os.path.join(os.getcwd(), curdir, filename)) if os.path.isfile(newname): filename = newname break curdir, _ = os.path.split(curdir) if not curdir: break elif '\\' in filename or '/' in filename: filename = os.path.normpath(os.path.join(os.getcwd(), curdir, filename)) if not os.path.isfile(filename) and alternative is not None: return alternative return open(filename).read() """ CHANGELOG ========= 2006/08/04 ``print_crumbs`` can now take a ``None`` value for 'divider'. ``include`` function can take an optional 'alternative' argument. 2006/07/30 Added include function. 2006/01/26 Added ``wrapper_class`` to ``print_details``. Added formattime function. 2006/01/13 Removed the two ``
    `` from ``listend`` in minibar. 2005/10/22 Added ``print_details`` and ``section_contents`` functions. 2005/10/16 Change to the ``print_crumbs`` function. It now takes an item value. 2005/10/13 'sections' is now an ordered dictionary - so minibar doesn't need to explicitly use the sequence attribute any more. 2005/07/23 Minibar now follows the 'sequence' attribute where relevant. 2005/06/18 Changes by Nicola Larosa Code cleanup lines shortened comments on line above code empty comments in empty lines 2005/05/28 First version - extracted from the templates. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/modules/0000700000175000017500000000000010644674643021730 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/rest2web/modules/__init__.py0000600000175000017500000000000010467726552024031 0ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/rest2web/modules/smiley.py0000600000175000017500000001362610467726552023616 0ustar madduckmadduck#!/usr/bin/python -u """ smiley.py from http://www.la-la.com As amended by Michael Foord This version is edited to work with the Voidspace Guestbook http://www.voidspace.org.uk/python/guestbook.html """ ## smiley.py ## v0.1.0, 2005\03\31 ## A smiley library handler for phpbb style smiley paks ## Copyright (C) 2005 Mark Andrews ## E-mail : mark AT la-la DOT com ## Website: http://la-la.com ## ## For Kim for bringing me so much laughter and smiles ## ## This software is licensed under the terms of the BSD license. ## Basically you're free to copy, modify, distribute and relicense it, ## So long as you keep a copy of the license with it. ## ## Requires path.py from http://www.jorendorff.com/articles/python/path/ import re from rest2web.modules.path import path repak = re.compile(r'^(.*?)=\+:(.*?)=\+:(.*)') resmile = re.compile(r'.*(:[^:]+:).*') reno = re.compile(r'.*(.*') reyes = re.compile(r'.*(|
    ).*') _default_pak = ''' biggrin.gif=+:Very Happy=+::D biggrin.gif=+:Very Happy=+::-D biggrin.gif=+:Very Happy=+::grin: biggrin.gif=+:Very Happy=+::biggrin: smile.gif=+:Smile=+::) smile.gif=+:Smile=+::-) smile.gif=+:Smile=+::smile: sad.gif=+:Sad=+::( sad.gif=+:Sad=+::-( sad.gif=+:Sad=+::sad: surprised.gif=+:Surprised=+::o surprised.gif=+:Surprised=+::-o surprised.gif=+:Surprised=+::eek: shock.gif=+:Shock=+::shock: confused.gif=+:Confused=+::? confused.gif=+:Confused=+::-? confused.gif=+:Confused=+::???: cool.gif=+:Cool=+:8) cool.gif=+:Cool=+:8-) cool.gif=+:Cool=+::cool: lol.gif=+:Laughing=+::lol: mad.gif=+:Mad=+::x mad.gif=+:Mad=+::-X mad.gif=+:Mad=+::mad: razz.gif=+:Razz=+::p razz.gif=+:Razz=+::-p razz.gif=+:Razz=+::razz: redface.gif=+:Embarassed=+::oops: cry.gif=+:Crying or Very sad=+::cry: evil.gif=+:Evil or Very Mad=+::evil: badgrin.gif=+:Bad Grin=+::badgrin: rolleyes.gif=+:Rolling Eyes=+::roll: wink.gif=+:Wink=+:;) wink.gif=+:Wink=+:;-) wink.gif=+:Wink=+::wink: exclaim.gif=+:Exclamation=+::!: question.gif=+:Question=+::?: idea.gif=+:Idea=+::idea: arrow.gif=+:Arrow=+::arrow: neutral.gif=+:Neutral=+::| neutral.gif=+:Neutral=+::-| neutral.gif=+:Neutral=+::neutral: doubt.gif=+:Doubt=+::doubt:'''[1:].splitlines() class lib(object): def __init__(self, source = None, url=None, replace=False, safe=False): self.sources = [] self.clear() self.load(source, url, replace) def clear(self): self.smilies = {} def load(self, source, url=None, replace=False, safe=False): """ Loads phpbb style smiley paks from directory source. Image locations are prepended with url. With replace set to true, later smilies with same key will overwrite original. With safe set to true, only smilies with :smile: style keys will be included. """ if url is None: url = '/' elif url[len(url)-1] != '/': url = url + '/' # if source is not None: p = path(source) for fn in p.files('*.pak'): f = fn.open() for l in f: self._parse_pak_line(l, url, safe, replace) f.close() else: for l in _default_pak: self._parse_pak_line(l, url, safe, replace) self.sources.append(source) def _parse_pak_line(self, l, url, safe, replace): s = repak.match(l) if s: key = s.groups(0)[2].strip() if not safe or resmile.match(key): if replace or not self.smilies.has_key(key): self.smilies[key] = [url + s.groups(0)[0], s.groups(0)[1]] def parsetoken(self, t, nest=0): """Recursive routine to replace :smile: tags""" r = resmile.match(t) if r: rn = r.groups(0)[0] if self.smilies.has_key(rn): rt = self.get_tag(self.smilies[rn][0], self.smilies[rn][1]) t = re.sub(re.escape(rn), rt, t) t = self.parsetoken(t, nest) else: # not a known smiley, hide and try again nest = nest + 1 rt = 'smileynest' + str(nest) t = re.sub(re.escape(rn[:len(rn)-2]), rt, t) t = self.parsetoken(t, nest) t = re.sub(rt, rn[:len(rn)-2], t) return t def makehappy(self, text): """ Replace all smiley codes in text with their image links and return the updated text. A :-) style smilie next to other characters will not be replaced. :smile: ones will. Tabs and multiple spaces will be replaced by a single space.
     and  sections
            are ignored, though the start and end tags should be on individual
            lines for clean results.
            """
            buf = text.split('\n')
            text = ''
            process = True
            for l in buf:
                tokens = l.split(' ')
                if reno.match(l):
                    process = False
                if reyes.match(l):
                    process = True
                if process:
                    tl = ''
                    for t in tokens:
                        if self.smilies.has_key(t):
                            t = self.get_tag(
                                self.smilies[t][0],
                                self.smilies[t][1])
                        else:
                            t = self.parsetoken(t)
                        tl = tl + ' ' + t
                    # leading space gained on first token
                    text = text + tl[1:] + '\n'
                else:
                    # pass code and pre sections unmodified
                    text = text + l
            return text
    
    
        def get_tag(self, image, alt):
            """
            Override this if you want to add class information etc to the image
            tags
            """
            tag = '' + alt + ''
            return tag
    
    """
    
    CHANGELOG
    
    2006/08/13
    Added the default pak.
    Removed unused method.
    
    """
    rest2web-0.5.2~alpha+svn-r248.orig/rest2web/modules/path.py0000600000175000017500000006404110311246051023220 0ustar  madduckmadduck""" path.py - An object representing a path to a file or directory.
    
    Example:
    
    from path import path
    d = path('/home/guido/bin')
    for f in d.files('*.py'):
        f.chmod(0755)
    
    This module requires Python 2.2 or later.
    
    
    URL:     http://www.jorendorff.com/articles/python/path
    Author:  Jason Orendorff  (and others - see the url!)
    Date:    7 Mar 2004
    """
    
    
    # TODO
    #   - Bug in write_text().  It doesn't support Universal newline mode.
    #   - Better error message in listdir() when self isn't a
    #     directory. (On Windows, the error message really sucks.)
    #   - Make sure everything has a good docstring.
    #   - Add methods for regex find and replace.
    #   - guess_content_type() method?
    #   - Perhaps support arguments to touch().
    #   - Could add split() and join() methods that generate warnings.
    #   - Note:  __add__() technically has a bug, I think, where
    #     it doesn't play nice with other types that implement
    #     __radd__().  Test this.
    
    from __future__ import generators
    
    import sys, os, fnmatch, glob, shutil, codecs
    
    __version__ = '2.0.4'
    __all__ = ['path']
    
    # Pre-2.3 support.  Are unicode filenames supported?
    _base = str
    try:
        if os.path.supports_unicode_filenames:
            _base = unicode
    except AttributeError:
        pass
    
    # Pre-2.3 workaround for basestring.
    try:
        basestring
    except NameError:
        basestring = (str, unicode)
    
    # Universal newline support
    _textmode = 'r'
    if hasattr(file, 'newlines'):
        _textmode = 'U'
    
    
    class path(_base):
        """ Represents a filesystem path.
    
        For documentation on individual methods, consult their
        counterparts in os.path.
        """
    
        # --- Special Python methods.
    
        def __repr__(self):
            return 'path(%s)' % _base.__repr__(self)
    
        # Adding a path and a string yields a path.
        def __add__(self, more):
            return path(_base(self) + more)
    
        def __radd__(self, other):
            return path(other + _base(self))
    
        # The / operator joins paths.
        def __div__(self, rel):
            """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
    
            Join two path components, adding a separator character if
            needed.
            """
            return path(os.path.join(self, rel))
    
        # Make the / operator work even when true division is enabled.
        __truediv__ = __div__
    
        def getcwd():
            """ Return the current working directory as a path object. """
            return path(os.getcwd())
        getcwd = staticmethod(getcwd)
    
    
        # --- Operations on path strings.
    
        def abspath(self):       return path(os.path.abspath(self))
        def normcase(self):      return path(os.path.normcase(self))
        def normpath(self):      return path(os.path.normpath(self))
        def realpath(self):      return path(os.path.realpath(self))
        def expanduser(self):    return path(os.path.expanduser(self))
        def expandvars(self):    return path(os.path.expandvars(self))
        def dirname(self):       return path(os.path.dirname(self))
        basename = os.path.basename
    
        def expand(self):
            """ Clean up a filename by calling expandvars(),
            expanduser(), and normpath() on it.
    
            This is commonly everything needed to clean up a filename
            read from a configuration file, for example.
            """
            return self.expandvars().expanduser().normpath()
    
        def _get_namebase(self):
            base, ext = os.path.splitext(self.name)
            return base
    
        def _get_ext(self):
            f, ext = os.path.splitext(_base(self))
            return ext
    
        def _get_drive(self):
            drive, r = os.path.splitdrive(self)
            return path(drive)
    
        parent = property(
            dirname, None, None,
            """ This path's parent directory, as a new path object.
    
            For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
            """)
    
        name = property(
            basename, None, None,
            """ The name of this file or directory without the full path.
    
            For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
            """)
    
        namebase = property(
            _get_namebase, None, None,
            """ The same as path.name, but with one file extension stripped off.
    
            For example, path('/home/guido/python.tar.gz').name     == 'python.tar.gz',
            but          path('/home/guido/python.tar.gz').namebase == 'python.tar'
            """)
    
        ext = property(
            _get_ext, None, None,
            """ The file extension, for example '.py'. """)
    
        drive = property(
            _get_drive, None, None,
            """ The drive specifier, for example 'C:'.
            This is always empty on systems that don't use drive specifiers.
            """)
    
        def splitpath(self):
            """ p.splitpath() -> Return (p.parent, p.name). """
            parent, child = os.path.split(self)
            return path(parent), child
    
        def splitdrive(self):
            """ p.splitdrive() -> Return (p.drive, ).
    
            Split the drive specifier from this path.  If there is
            no drive specifier, p.drive is empty, so the return value
            is simply (path(''), p).  This is always the case on Unix.
            """
            drive, rel = os.path.splitdrive(self)
            return path(drive), rel
    
        def splitext(self):
            """ p.splitext() -> Return (p.stripext(), p.ext).
    
            Split the filename extension from this path and return
            the two parts.  Either part may be empty.
    
            The extension is everything from '.' to the end of the
            last path segment.  This has the property that if
            (a, b) == p.splitext(), then a + b == p.
            """
            filename, ext = os.path.splitext(self)
            return path(filename), ext
    
        def stripext(self):
            """ p.stripext() -> Remove one file extension from the path.
    
            For example, path('/home/guido/python.tar.gz').stripext()
            returns path('/home/guido/python.tar').
            """
            return self.splitext()[0]
    
        if hasattr(os.path, 'splitunc'):
            def splitunc(self):
                unc, rest = os.path.splitunc(self)
                return path(unc), rest
    
            def _get_uncshare(self):
                unc, r = os.path.splitunc(self)
                return path(unc)
    
            uncshare = property(
                _get_uncshare, None, None,
                """ The UNC mount point for this path.
                This is empty for paths on local drives. """)
    
        def joinpath(self, *args):
            """ Join two or more path components, adding a separator
            character (os.sep) if needed.  Returns a new path
            object.
            """
            return path(os.path.join(self, *args))
    
        def splitall(self):
            """ Return a list of the path components in this path.
    
            The first item in the list will be a path.  Its value will be
            either os.curdir, os.pardir, empty, or the root directory of
            this path (for example, '/' or 'C:\\').  The other items in
            the list will be strings.
    
            path.path.joinpath(*result) will yield the original path.
            """
            parts = []
            loc = self
            while loc != os.curdir and loc != os.pardir:
                prev = loc
                loc, child = prev.splitpath()
                if loc == prev:
                    break
                parts.append(child)
            parts.append(loc)
            parts.reverse()
            return parts
    
        def relpath(self):
            """ Return this path as a relative path,
            based from the current working directory.
            """
            cwd = path(os.getcwd())
            return cwd.relpathto(self)
    
        def relpathto(self, dest):
            """ Return a relative path from self to dest.
    
            If there is no relative path from self to dest, for example if
            they reside on different drives in Windows, then this returns
            dest.abspath().
            """
            origin = self.abspath()
            dest = path(dest).abspath()
    
            orig_list = origin.normcase().splitall()
            # Don't normcase dest!  We want to preserve the case.
            dest_list = dest.splitall()
    
            if orig_list[0] != os.path.normcase(dest_list[0]):
                # Can't get here from there.
                return dest
    
            # Find the location where the two paths start to differ.
            i = 0
            for start_seg, dest_seg in zip(orig_list, dest_list):
                if start_seg != os.path.normcase(dest_seg):
                    break
                i += 1
    
            # Now i is the point where the two paths diverge.
            # Need a certain number of "os.pardir"s to work up
            # from the origin to the point of divergence.
            segments = [os.pardir] * (len(orig_list) - i)
            # Need to add the diverging part of dest_list.
            segments += dest_list[i:]
            if len(segments) == 0:
                # If they happen to be identical, use os.curdir.
                return path(os.curdir)
            else:
                return path(os.path.join(*segments))
    
    
        # --- Listing, searching, walking, and matching
    
        def listdir(self, pattern=None):
            """ D.listdir() -> List of items in this directory.
    
            Use D.files() or D.dirs() instead if you want a listing
            of just files or just subdirectories.
    
            The elements of the list are path objects.
    
            With the optional 'pattern' argument, this only lists
            items whose names match the given pattern.
            """
            names = os.listdir(self)
            if pattern is not None:
                names = fnmatch.filter(names, pattern)
            return [self / child for child in names]
    
        def dirs(self, pattern=None):
            """ D.dirs() -> List of this directory's subdirectories.
    
            The elements of the list are path objects.
            This does not walk recursively into subdirectories
            (but see path.walkdirs).
    
            With the optional 'pattern' argument, this only lists
            directories whose names match the given pattern.  For
            example, d.dirs('build-*').
            """
            return [p for p in self.listdir(pattern) if p.isdir()]
    
        def files(self, pattern=None):
            """ D.files() -> List of the files in this directory.
    
            The elements of the list are path objects.
            This does not walk into subdirectories (see path.walkfiles).
    
            With the optional 'pattern' argument, this only lists files
            whose names match the given pattern.  For example,
            d.files('*.pyc').
            """
            
            return [p for p in self.listdir(pattern) if p.isfile()]
    
        def walk(self, pattern=None):
            """ D.walk() -> iterator over files and subdirs, recursively.
    
            The iterator yields path objects naming each child item of
            this directory and its descendants.  This requires that
            D.isdir().
    
            This performs a depth-first traversal of the directory tree.
            Each directory is returned just before all its children.
            """
            for child in self.listdir():
                if pattern is None or child.fnmatch(pattern):
                    yield child
                if child.isdir():
                    for item in child.walk(pattern):
                        yield item
    
        def walkdirs(self, pattern=None):
            """ D.walkdirs() -> iterator over subdirs, recursively.
    
            With the optional 'pattern' argument, this yields only
            directories whose names match the given pattern.  For
            example, mydir.walkdirs('*test') yields only directories
            with names ending in 'test'.
            """
            for child in self.dirs():
                if pattern is None or child.fnmatch(pattern):
                    yield child
                for subsubdir in child.walkdirs(pattern):
                    yield subsubdir
    
        def walkfiles(self, pattern=None):
            """ D.walkfiles() -> iterator over files in D, recursively.
    
            The optional argument, pattern, limits the results to files
            with names that match the pattern.  For example,
            mydir.walkfiles('*.tmp') yields only files with the .tmp
            extension.
            """
            for child in self.listdir():
                if child.isfile():
                    if pattern is None or child.fnmatch(pattern):
                        yield child
                elif child.isdir():
                    for f in child.walkfiles(pattern):
                        yield f
    
        def fnmatch(self, pattern):
            """ Return True if self.name matches the given pattern.
    
            pattern - A filename pattern with wildcards,
                for example '*.py'.
            """
            return fnmatch.fnmatch(self.name, pattern)
    
        def glob(self, pattern):
            """ Return a list of path objects that match the pattern.
    
            pattern - a path relative to this directory, with wildcards.
    
            For example, path('/users').glob('*/bin/*') returns a list
            of all the files users have in their bin directories.
            """
            return map(path, glob.glob(_base(self / pattern)))
    
    
        # --- Reading or writing an entire file at once.
    
        def open(self, mode='r'):
            """ Open this file.  Return a file object. """
            return file(self, mode)
    
        def bytes(self):
            """ Open this file, read all bytes, return them as a string. """
            f = self.open('rb')
            try:
                return f.read()
            finally:
                f.close()
    
        def write_bytes(self, bytes, append=False):
            """ Open this file and write the given bytes to it.
    
            Default behavior is to overwrite any existing file.
            Call this with write_bytes(bytes, append=True) to append instead.
            """
            if append:
                mode = 'ab'
            else:
                mode = 'wb'
            f = self.open(mode)
            try:
                f.write(bytes)
            finally:
                f.close()
    
        def text(self, encoding=None, errors='strict'):
            """ Open this file, read it in, return the content as a string.
    
            This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
            are automatically translated to '\n'.
    
            Optional arguments:
    
            encoding - The Unicode encoding (or character set) of
                the file.  If present, the content of the file is
                decoded and returned as a unicode object; otherwise
                it is returned as an 8-bit str.
            errors - How to handle Unicode errors; see help(str.decode)
                for the options.  Default is 'strict'.
            """
            if encoding is None:
                # 8-bit
                f = self.open(_textmode)
                try:
                    return f.read()
                finally:
                    f.close()
            else:
                # Unicode
                f = codecs.open(self, 'r', encoding, errors)
                # (Note - Can't use 'U' mode here, since codecs.open
                # doesn't support 'U' mode, even in Python 2.3.)
                try:
                    t = f.read()
                finally:
                    f.close()
                return (t.replace(u'\r\n', u'\n')
                         .replace(u'\r\x85', u'\n')
                         .replace(u'\r', u'\n')
                         .replace(u'\x85', u'\n')
                         .replace(u'\u2028', u'\n'))
    
        def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
            """ Write the given text to this file.
    
            The default behavior is to overwrite any existing file;
            to append instead, use the 'append=True' keyword argument.
    
            There are two differences between path.write_text() and
            path.write_bytes(): newline handling and Unicode handling.
            See below.
    
            Parameters:
    
              - text - str/unicode - The text to be written.
    
              - encoding - str - The Unicode encoding that will be used.
                This is ignored if 'text' isn't a Unicode string.
    
              - errors - str - How to handle Unicode encoding errors.
                Default is 'strict'.  See help(unicode.encode) for the
                options.  This is ignored if 'text' isn't a Unicode
                string.
    
              - linesep - keyword argument - str/unicode - The sequence of
                characters to be used to mark end-of-line.  The default is
                os.linesep.  You can also specify None; this means to
                leave all newlines as they are in 'text'.
    
              - append - keyword argument - bool - Specifies what to do if
                the file already exists (True: append to the end of it;
                False: overwrite it.)  The default is False.
    
    
            --- Newline handling.
    
            write_text() converts all standard end-of-line sequences
            ('\n', '\r', and '\r\n') to your platform's default end-of-line
            sequence (see os.linesep; on Windows, for example, the
            end-of-line marker is '\r\n').
    
            If you don't like your platform's default, you can override it
            using the 'linesep=' keyword argument.  If you specifically want
            write_text() to preserve the newlines as-is, use 'linesep=None'.
    
            This applies to Unicode text the same as to 8-bit text, except
            there are three additional standard Unicode end-of-line sequences:
            u'\x85', u'\r\x85', and u'\u2028'.
    
            (This is slightly different from when you open a file for
            writing with fopen(filename, "w") in C or file(filename, 'w')
            in Python.)
    
    
            --- Unicode
    
            If 'text' isn't Unicode, then apart from newline handling, the
            bytes are written verbatim to the file.  The 'encoding' and
            'errors' arguments are not used and must be omitted.
    
            If 'text' is Unicode, it is first converted to bytes using the
            specified 'encoding' (or the default encoding if 'encoding'
            isn't specified).  The 'errors' argument applies only to this
            conversion.
    
            """
            if isinstance(text, unicode):
                if linesep is not None:
                    # Convert all standard end-of-line sequences to
                    # ordinary newline characters.
                    text = (text.replace(u'\r\n', u'\n')
                                .replace(u'\r\x85', u'\n')
                                .replace(u'\r', u'\n')
                                .replace(u'\x85', u'\n')
                                .replace(u'\u2028', u'\n'))
                    text = text.replace(u'\n', linesep)
                if encoding is None:
                    encoding = sys.getdefaultencoding()
                bytes = text.encode(encoding, errors)
            else:
                # It is an error to specify an encoding if 'text' is
                # an 8-bit string.
                assert encoding is None
    
                if linesep is not None:
                    text = (text.replace('\r\n', '\n')
                                .replace('\r', '\n'))
                    bytes = text.replace('\n', linesep)
    
            self.write_bytes(bytes, append)
    
        def lines(self, encoding=None, errors='strict', retain=True):
            """ Open this file, read all lines, return them in a list.
    
            Optional arguments:
                encoding - The Unicode encoding (or character set) of
                    the file.  The default is None, meaning the content
                    of the file is read as 8-bit characters and returned
                    as a list of (non-Unicode) str objects.
                errors - How to handle Unicode errors; see help(str.decode)
                    for the options.  Default is 'strict'
                retain - If true, retain newline characters; but all newline
                    character combinations ('\r', '\n', '\r\n') are
                    translated to '\n'.  If false, newline characters are
                    stripped off.  Default is True.
    
            This uses 'U' mode in Python 2.3 and later.
            """
            if encoding is None and retain:
                f = self.open(_textmode)
                try:
                    return f.readlines()
                finally:
                    f.close()
            else:
                return self.text(encoding, errors).splitlines(retain)
    
        def write_lines(self, lines, encoding=None, errors='strict',
                        linesep=os.linesep, append=False):
            """ Write the given lines of text to this file.
    
            By default this overwrites any existing file at this path.
    
            This puts a platform-specific newline sequence on every line.
            See 'linesep' below.
    
            lines - A list of strings.
    
            encoding - A Unicode encoding to use.  This applies only if
                'lines' contains any Unicode strings.
    
            errors - How to handle errors in Unicode encoding.  This
                also applies only to Unicode strings.
    
            linesep - The desired line-ending.  This line-ending is
                applied to every line.  If a line already has any
                standard line ending ('\r', '\n', '\r\n', u'\x85',
                u'\r\x85', u'\u2028'), that will be stripped off and
                this will be used instead.  The default is os.linesep,
                which is platform-dependent ('\r\n' on Windows, '\n' on
                Unix, etc.)  Specify None to write the lines as-is,
                like file.writelines().
    
            Use the keyword argument append=True to append lines to the
            file.  The default is to overwrite the file.  Warning:
            When you use this with Unicode data, if the encoding of the
            existing data in the file is different from the encoding
            you specify with the encoding= parameter, the result is
            mixed-encoding data, which can really confuse someone trying
            to read the file later.
            """
            if append:
                mode = 'ab'
            else:
                mode = 'wb'
            f = self.open(mode)
            try:
                for line in lines:
                    isUnicode = isinstance(line, unicode)
                    if linesep is not None:
                        # Strip off any existing line-end and add the
                        # specified linesep string.
                        if isUnicode:
                            if line[-2:] in (u'\r\n', u'\x0d\x85'):
                                line = line[:-2]
                            elif line[-1:] in (u'\r', u'\n',
                                               u'\x85', u'\u2028'):
                                line = line[:-1]
                        else:
                            if line[-2:] == '\r\n':
                                line = line[:-2]
                            elif line[-1:] in ('\r', '\n'):
                                line = line[:-1]
                        line += linesep
                    if isUnicode:
                        if encoding is None:
                            encoding = sys.getdefaultencoding()
                        line = line.encode(encoding, errors)
                    f.write(line)
            finally:
                f.close()
    
    
        # --- Methods for querying the filesystem.
    
        exists = os.path.exists
        isabs = os.path.isabs
        isdir = os.path.isdir
        isfile = os.path.isfile
        islink = os.path.islink
        ismount = os.path.ismount
    
        if hasattr(os.path, 'samefile'):
            samefile = os.path.samefile
    
        getatime = os.path.getatime
        atime = property(
            getatime, None, None,
            """ Last access time of the file. """)
    
        getmtime = os.path.getmtime
        mtime = property(
            getmtime, None, None,
            """ Last-modified time of the file. """)
    
        if hasattr(os.path, 'getctime'):
            getctime = os.path.getctime
            ctime = property(
                getctime, None, None,
                """ Creation time of the file. """)
    
        getsize = os.path.getsize
        size = property(
            getsize, None, None,
            """ Size of the file, in bytes. """)
    
        if hasattr(os, 'access'):
            def access(self, mode):
                """ Return true if current user has access to this path.
    
                mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
                """
                return os.access(self, mode)
    
        def stat(self):
            """ Perform a stat() system call on this path. """
            return os.stat(self)
    
        def lstat(self):
            """ Like path.stat(), but do not follow symbolic links. """
            return os.lstat(self)
    
        if hasattr(os, 'statvfs'):
            def statvfs(self):
                """ Perform a statvfs() system call on this path. """
                return os.statvfs(self)
    
        if hasattr(os, 'pathconf'):
            def pathconf(self, name):
                return os.pathconf(self, name)
    
    
        # --- Modifying operations on files and directories
    
        def utime(self, times):
            """ Set the access and modified times of this file. """
            os.utime(self, times)
    
        def chmod(self, mode):
            os.chmod(self, mode)
    
        if hasattr(os, 'chown'):
            def chown(self, uid, gid):
                os.chown(self, uid, gid)
    
        def rename(self, new):
            os.rename(self, new)
    
        def renames(self, new):
            os.renames(self, new)
    
    
        # --- Create/delete operations on directories
    
        def mkdir(self, mode=0777):
            os.mkdir(self, mode)
    
        def makedirs(self, mode=0777):
            os.makedirs(self, mode)
    
        def rmdir(self):
            os.rmdir(self)
    
        def removedirs(self):
            os.removedirs(self)
    
    
        # --- Modifying operations on files
    
        def touch(self):
            """ Set the access/modified times of this file to the current time.
            Create the file if it does not exist.
            """
            fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
            os.close(fd)
            os.utime(self, None)
    
        def remove(self):
            os.remove(self)
    
        def unlink(self):
            os.unlink(self)
    
    
        # --- Links
    
        if hasattr(os, 'link'):
            def link(self, newpath):
                """ Create a hard link at 'newpath', pointing to this file. """
                os.link(self, newpath)
    
        if hasattr(os, 'symlink'):
            def symlink(self, newlink):
                """ Create a symbolic link at 'newlink', pointing here. """
                os.symlink(self, newlink)
    
        if hasattr(os, 'readlink'):
            def readlink(self):
                """ Return the path to which this symbolic link points.
    
                The result may be an absolute or a relative path.
                """
                return path(os.readlink(self))
    
            def readlinkabs(self):
                """ Return the path to which this symbolic link points.
    
                The result is always an absolute path.
                """
                p = self.readlink()
                if p.isabs():
                    return p
                else:
                    return (self.parent / p).abspath()
    
    
        # --- High-level functions from shutil
    
        copyfile = shutil.copyfile
        copymode = shutil.copymode
        copystat = shutil.copystat
        copy = shutil.copy
        copy2 = shutil.copy2
        copytree = shutil.copytree
        if hasattr(shutil, 'move'):
            move = shutil.move
        rmtree = shutil.rmtree
    
    
        # --- Special stuff from os
    
        if hasattr(os, 'chroot'):
            def chroot(self):
                os.chroot(self)
    
        if hasattr(os, 'startfile'):
            def startfile(self):
                os.startfile(self)
    
    rest2web-0.5.2~alpha+svn-r248.orig/rest2web/modules/colorize.py0000600000175000017500000001006110311246051024103 0ustar  madduckmadduck"""
        MoinMoin - Python Source Parser
    
        Created 2001 by Juergen Hermann 
        Subject to the Activestate Python Cookbook License
        
        Original source: 
            http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
        Modifications by: 
            Hans Nowak, Michael Foord
    """
    
    # Imports
    import cgi, StringIO, keyword, token, tokenize
    
    
    #############################################################################
    ### Python Source Parser (does Hilighting)
    #############################################################################
    
    _KEYWORD = token.NT_OFFSET + 1
    _TEXT    = token.NT_OFFSET + 2
    
    # define these in a stylesheet
    _tags = {
        token.NUMBER: 'pynumber',
        token.OP: 'pyoperator',
        token.STRING: 'pystring',
        tokenize.COMMENT: 'pycomment',
        tokenize.ERRORTOKEN: 'pyerror',
        _KEYWORD: 'pykeyword',
        _TEXT: 'pytext',
    }
    
    
    class Parser:
        """
            Send colored python source.
        """
    
        def __init__(self, raw):
            """ Store the source text.
            """
            self.out = StringIO.StringIO()
            self.raw = raw.expandtabs().strip()
    
        def getvalue(self):
            return self.out.getvalue()
    
        def format(self, formatter, form):
            """ Parse and send the colored source.
            """
            # store line offsets in self.lines
            self.lines = [0, 0]
            pos = 0
            while 1:
                pos = self.raw.find('\n', pos) + 1
                if not pos: break
                self.lines.append(pos)
            self.lines.append(len(self.raw))
            #
            # parse the source and write it
            self.pos = 0
            text = StringIO.StringIO(self.raw)
            self.out.write('
    ') try: tokenize.tokenize(text.readline, self) except tokenize.TokenError, ex: msg = ex[0] line = ex[1][0] print >> self.out, ("

    ERROR: %s

    %s" % (msg, self.raw[self.lines[line]:])) self.out.write('
    ') def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line): """ Token handler. """ if 0: print "type", toktype, token.tok_name[toktype], "text", toktext, print "start", srow,scol, "end", erow,ecol, "
    " # # calculate new positions oldpos = self.pos newpos = self.lines[srow] + scol self.pos = newpos + len(toktext) # # handle newlines if toktype in [token.NEWLINE, tokenize.NL]: print >> self.out, "" return # # send the original whitespace, if needed if newpos > oldpos: self.out.write(self.raw[oldpos:newpos]) # # skip indenting tokens if toktype in [token.INDENT, token.DEDENT]: self.pos = newpos return # # map token type to a color group if token.LPAR <= toktype and toktype <= token.OP: toktype = token.OP elif toktype == token.NAME and keyword.iskeyword(toktext): toktype = _KEYWORD style = _tags.get(toktype, _tags[_TEXT]) # # send text self.out.write('' % (style,)) self.out.write(cgi.escape(toktext)) self.out.write('') if __name__ == "__main__": import os, sys # open own source source = open(sys.argv[0]).read() # # write colorized version to "python.html" p = Parser(source) p.format(None, None) f = open('python.html', 'wt') f.write(p.getvalue()) f.close() # # load HTML page into browser if os.name == "nt": os.system("explorer python.html") else: # XXXX some assumption... os.system("netscape python.html &") """ 2005/06/20 Changed license text to Activestate Python Cookbook 2005/06/18 Changes by Nicola Larosa Code cleanup lines shortened empty comments in empty lines usage of string module removed 2005/03/24 Changes by Michael Foord ``
     
    `` changed to ``
    `` cStringIO changed to StringIO for unicode compatibility. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/printing.py0000600000175000017500000000441510514020175022447 0ustar madduckmadduck# printing.py # Subversion Details # $LastChangedDate: 2006-01-05 12:06:07 +0000 (Thu, 05 Jan 2006) $ # $LastChangedBy: fuzzyman $ # $HeadURL: https://svn.rest2web.python-hosting.com/trunk/rest2web/printing.py $ # $LastChangedRevision: 155 $ # Helper functions for rest2web # Primarily functions that handle the docutils/encodings stuff # http://www.voidspace.org.uk/python/rest2web/ # Copyright Michael Foord, 2004 - 2006. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # rest2web mailing list. # https://lists.sourceforge.net/lists/listinfo/rest2web-develop # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk """ This module provides two print functions. If StandOut is in use, then ``standout`` should be used, otherwise ``standard`` should be used. If you use ``standout`` you can pass in a verbosity level for the current message. (This parameter will be ignored by the ``standard`` function.) It also defines four constants for message priority levels : OVERRIDE WARN ACTION INFO The VERBOSITY dictionary maps different verbosity levels (integers) to the right setting to pass to StandOut. """ import sys __all__ = ( 'WARN', 'ACTION', 'INFO', 'OVERRIDE', 'standard', 'standout', 'VERBOSITY', ) OVERRIDE = 9 WARN = 7 ACTION = 3 INFO = 1 # maps verbosity level to the right setting THRESHOLD = { # verbose (default) 0: 1, # Actions & Warnings 1: 3, # Warnings Only 2: 7, } def out(line, level=INFO, newline=True): """Print a line if StandOut is enabled.""" global ERROR_STATUS if level == WARN: ERROR_STATUS += 1 stream = sys.stderr else: stream = sys.stdout stream.write(line, level) if newline: stream.write('\n', level) else: stream.write(' ', level) ERROR_STATUS = 0 """ TODO ==== Find a way of using this from within plugins. CHANGELOG ========= 2006/08/21 ---------- Changed VERBOSITY to THRESHOLD. Changed standout to out. This is support for standout version 3. 2006/04/05 ---------- First implementation. """rest2web-0.5.2~alpha+svn-r248.orig/rest2web/command_line.py0000600000175000017500000001217110514020175023240 0ustar madduckmadduck# command_line.py # Subversion Details # $LastChangedDate: 2006-02-18 23:57:27 +0000 (Sat, 18 Feb 2006) $ # $LastChangedBy: aji $ # $HeadURL: https://svn.rest2web.python-hosting.com/trunk/rest2web/restprocessor.py $ # $LastChangedRevision: 158 $ # Handle command line options for rest2web # http://www.voidspace.org.uk/python/rest2web/ # Copyright Michael Foord, 2004 - 2006. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # rest2web mailing list. # http://lists.sourceforge.net/lists/listinfo/rest2web-develop # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk """ A module that handles the command line arguments for rest2web. For Python 2.2 it only retrieves the first argument as the config file. For Python 2.3 and above it handles various options. Run ``r2w.py --help`` for a list of the options. """ import os import sys from rest2web import __version__ try: from optparse import OptionParser except ImportError: OptionParser = None __all__ = ('handle_args',) DEFAULTS = { # cfg_file not here, default supplied in r2w.py 'verbosity': 0, 'template': None, 'uservalues': [], 'force': False, 'nopause': False, 'skiperrors': False, } usage = "%prog [options] config_file" def handle_args(): """Handle the command line options for rest2web.""" if not OptionParser: # Python 2.2 options = dict(DEFAULTS) if len(sys.argv) > 1: options['cfg_file'] = sys.argv[1] return options return handle_optparse() def handle_optparse(): """Handle arguments using optparse.""" parser = OptionParser(usage=usage, version="restweb %s" % __version__) # # Verbosity options parser.add_option('-v', dest='verbosity', help='Verbose output (default).', action="store_const", const=0) parser.add_option('-a', dest='verbosity', help='Display warnings & ' 'actions only.', action="store_const", const=1) parser.add_option('-w', dest='verbosity', help='Display warnings only.', action="store_const", const=2) # # Template File parser.add_option('-t', '--template-file', dest='template', help='Specify a template file. (Overrides first template.)') # # Uservalues parser.add_option('-u', '--uservalues', dest='uservalues', action='append', help='Global uservalues for the site, in the form "name=value".') # # Force parser.add_option("-f", '--force', action="store_true", dest="force", help="Force site without indexes, restindexes or template.") # # No pause parser.add_option("-n", '--nopause', action="store_true", dest="nopause", help="Do not pause after processing (overrides setting in config file).") # # Skip errors parser.add_option("-s", '--skiperrors', action="store_true", dest="skiperrors", help="Skip errors (continue processing).") # parser.set_defaults(**DEFAULTS) # (option_obj, args) = parser.parse_args() # # Build the options dictionary options = {} options['verbosity'] = option_obj.verbosity if option_obj.template: options['template'] = os.path.abspath(option_obj.template) else: options['template'] = None # enc = 'ascii' if option_obj.uservalues: if hasattr(sys.stdin, 'encoding') and sys.stdin.encoding: enc = sys.stdin.encoding # options['uservalues'] = get_uservalues(option_obj.uservalues, enc) options['force'] = option_obj.force options['nopause'] = option_obj.nopause options['skiperrors'] = option_obj.skiperrors # # Sort the arguments if args: if len(args) > 1: # This quits if the wrong number of args are passed. parser.error("Incorrect number of arguments.") options['cfg_file'] = args[0] return options def get_uservalues(vals, enc): """Retrieve and decode the uservalues.""" out = {} for v in vals: try: k, e = v.split('=', 1) except ValueError: # uservalue in incorrect format print 'Uservalue "%s" is incorrect format.' % v print 'Must be "name=value".' sys.exit(1) try: out[k] = unicode(e, enc) except UnicodeDecodeError: print 'Failed to decode uservalues to unicode.' print 'Using encoding "%s".' % enc sys.exit(1) return out if __name__ == '__main__': # test code sys.argv = [__file__, '-v', 'pot'] print handle_optparse() # sys.argv = [__file__, '-a', 'fish'] print handle_optparse() # sys.argv = [__file__, '-w', 'kettle'] print handle_optparse() # sys.argv = [__file__, '--help'] print handle_optparse() # # Should fail ! sys.argv = [__file__, '-w', 'fish', 'kettle'] print handle_optparse() """ TODO ==== CHANGELOG ========= 2006/08/06 ---------- Added support for command line 'nopause'. 2006/04/17 ---------- Implemented. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/embedded_code.py0000600000175000017500000001735510530147410023347 0ustar madduckmadduck# embedded_code.py # Subversion Details # $LastChangedDate: 2006-11-19 22:39:20 +0100 (Sun, 19 Nov 2006) $ # $LastChangedBy: fuzzyman $ # $HeadURL: https://svn.rest2web.python-hosting.com/trunk/rest2web/embedded_code.py $ # $LastChangedRevision: 226 $ # The templating engine for rest2web # http://www.voidspace.org.uk/python/rest2web # Adapted from Firedrop2 by Hans Nowak # http://zephyrfalcon.org # Copyright Michael Foord & Hans Nowak, 2004 & 2005. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates, and support, please join the # Pythonutils mailing list. # http://groups.google.com/group/pythonutils/ # Comments, suggestions, and bug reports, welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk __all__ = ( 'render', 'render_well', 'render_value_eval', 'render_value_exec', 'get_indentation', 'replace_separators', 'align_multiline_code', ) import re import StringIO import sys import traceback _generic = "\<\%c.*?\%c\>" _markers = [] _uservalues_markers = [] _MAX = 100 def _build_generic(char, uservalues=False): if len(char) != 1: raise ValueError("Can only build parsers for a single character.") pattern = _generic % (char, char) if not uservalues: _markers.append('<%s' % char) else: _uservalues_markers.append('<%s' % char) return re.compile(pattern, re.MULTILINE|re.DOTALL) re_code_userval_eval = _build_generic('*', True) re_code_userval_exec = _build_generic('$', True) re_code_eval = _build_generic('%') re_code_exec = _build_generic('#') def render(template, namespace, uservalues=False, final_encoding=None): """Render all embedded code, both single value and multiline code.""" if not uservalues: regexes = (re_code_exec, re_code_eval) else: regexes = (re_code_userval_exec, re_code_userval_eval) # for (fun, regex) in zip((render_value_exec, render_value_eval), regexes): occs = re.findall(regex, template) for occ in occs: value = fun(occ, namespace) # FIXME: Should this be restricted to a single replace for each run # through ? if isinstance(value, unicode) and final_encoding: value = value.encode(final_encoding) template = template.replace(occ, value) return template def render_well(template, namespace, uservalues=False, final_encoding=None): if uservalues: markers = _uservalues_markers else: markers = _markers i = 0 while i < _MAX: # Max 100 iterations i += 1 template = render(template, namespace, uservalues,final_encoding=final_encoding) # evaluates to False if no markers found if not [True for m in markers if m in template]: break return template def render_value_eval(code, namespace): """ Render code, in the form of a string "<% something %>", to its value by evaluating (gasp!) it.""" code = code[2:-2].strip() try: value = eval(code, namespace) except Exception, e: e.namespace = namespace raise return "%s" % (value,) def render_value_exec(code, namespace): """ A bit more complex than render_value_eval. We redirect sys.stdout to a StringIO, so anything written to (the fake) sys.stdout, or printed, is caught by it and placed in HTML instead. Name 'doc' is available as a shorthand for sys.stdout. Note that multi-line code should start on a separate line: <# print this print that #> """ s = StringIO.StringIO() oldstdout, sys.stdout = sys.stdout, s # code = restyle_code(code[2:-2].strip()) # remove <# #> code = code[2:-2] code = replace_separators(code) code = align_multiline_code(code) if not code.endswith('\n'): code += '\n' # try: codeobj = compile(code, '', 'exec') except Exception, e: print >> sys.stderr, "Error in compiling template code." print >> sys.stderr, str(code) raise e # namespace["doc"] = sys.stdout # for printing tracebacks etc namespace['stdout'] = oldstdout try: try: exec codeobj in namespace except Exception, e: # FIXME: provide better traceback information here. # like line-number or context # print >> sys.stderr, "-----An error occurred:-----" # traceback.print_exc() # print >> sys.stderr, "code:", `code` # print >> sys.stderr, "----------------------------" e.namespace = namespace raise finally: sys.stdout = oldstdout # return s.getvalue() # for some reason, \r\n as line ending is not acceptable for exec... # so we convert that: def replace_separators(code): """Replace \r\n and \r line separators with \n.""" return code.replace('\r\n', '\n').replace('\r', '\n') def align_multiline_code(code): """ Align multi-line code so Python can execute it without running into inconsistent indentation. """ lines = code.split("\n") # # if there's only one line, strip it if len(lines) == 1: lines[0] =lines[0].strip() # # dedent as much as possible so at least some lines end up at position 0 ##indent = find_common_indentation(lines) common_indent = 9999 for i in range(len(lines)): line = lines[i] if not line.strip(): continue lines[i] = line.replace('\t', ' ') common_indent = min(get_indentation(line), common_indent) for i in range(len(lines)): lines[i] = lines[i][common_indent:] # # glue everything back together and hope for the best ;-) return "\n".join(lines) def find_common_indentation(lines): """ Find the minimum indentation that all lines have. Ignore empty lines. """ common_indent = 9999 for line in lines: if not line.strip(): # ignore empty lines continue indent = get_indentation(line) if indent < common_indent: common_indent = indent return common_indent def get_indentation(line): """Get a string's indentation.""" indent = 0 for c in line: if c == " ": indent += 1 else: break return indent if __name__ == "__main__": # code = """\ Hi! My name is <% name %>. Pleased to meet ya, I'm a funky creature. Let's print some numbers, eh? <# for i in range(5): print i #> <% signature %> """ # name = "Hans" signature = "--\nhans@nowak.com" # print render(code, globals()) """ TODO ==== Mixed tab/space indentation breaks this. CHANGELOG ========= 2006/08/06 Exec done before eval in render_well. 2006/04/15 Added the handling of uservalues in ``render``. Generalised the ``render`` function and added the ``_build_generic`` function for creating the various regular expressions. 2005/06/18 Changes by Nicola Larosa Code cleanup lines shortened comments on line above code empty comments in empty lines 2005/06/06 Removed extraneous print statement. 2005/05/15 Added 'stdout' to the namespace in ``render_value_exec`` We compile the code to a codeobj in ``render_value_exec`` Replaced use of the string module with string methods. TODO/ISSUES Can we improve the error messages ? (e.g. the line in the template they occur in ?) We *could* use ``eval`` in ``render_value_exec``, (for single line statements only) and so eliminate the distinction between '<# ... #>' and '<% .... %>' (This would prevent us from exec'ing single line fucntion calls though) Should tabsize be a config option ? (in ``get_indentation``) """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/textmacros.py0000600000175000017500000001566510425411107023017 0ustar madduckmadduck# textmacros.py # Subversion Details # $LastChangedDate: 2006-05-01 15:51:03 +0200 (Mon, 01 May 2006) $ # $LastChangedBy: fuzzyman $ # $HeadURL: https://svn.rest2web.python-hosting.com/trunk/rest2web/textmacros.py $ # $LastChangedRevision: 172 $ # Used in rest2web # http://www.voidspace.org.uk/python/rest2web # By Hans Nowak # http://zephyrfalcon.org # (amended by Michael Foord) # TODO: Allow {-} as closing macro. This is less clear but more concise. # Copyright Michael Foord & Hans Nowak, 2004 - 2006. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # rest2web mailing list. # http://lists.sourceforge.net/lists/listinfo/rest2web-develop # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk import re __version__ = "1.0" class MacroError(Exception): pass class UnbalancedMacroError(MacroError): pass re_macro = re.compile("(\{[+-]?\w+.*?})") class BaseMacro: def open(self, *args): pass def close(self, *args): pass class TextMacros: def __init__(self, namespace = None): if namespace is None: namespace = {} self.namespace = namespace def find_macros(self, text): """ Find macros in text. Return a list of tuples (start, stop, text). """ matches = [] for m in re_macro.finditer(text): t = (m.start(), m.end(), m.groups()[0]) # # if the macro is not in the namespace, we ignore it macroname = t[2][1:-1].split(';')[0] if macroname.startswith("+") or macroname.startswith("-"): macroname = macroname[1:] if self.namespace.has_key(macroname): matches.append(t) # return matches def split_text(self, text, matchlist): """ Split text in chunks, based on list of macros returned by find_macros. Return a list of tuples (type, text) where type can be 'text' or 'macro'. """ parts = [] index = 0 for (begin, end, macro) in matchlist: if begin > index: chunk = text[index:begin] parts.append(("text", chunk)) chunk = text[begin:end] parts.append(("macro", chunk)) index = end if text[index:]: parts.append(("text", text[index:])) return parts def build_match_tree(self, parts): """ Build a tree from the parts returned by split_text. Returns a list of items. Nested lists are supposed to start and end with an opening/closing macro. "Singular" macros are allowed inside lists as well. """ stacks = [[]] for part in parts: type, text = part if type == 'text': stacks[-1].append(part) elif type == 'macro': if text.startswith('{+'): stacks.append([]) stacks[-1].append(part) elif text.startswith('{-'): startmacro = stacks[-1][0][1] # closing macro should be the same as opening macro if text[2:] != startmacro[2:]: raise UnbalancedMacroError, part stacks[-1].append(part) z = stacks.pop() stacks[-1].append(z) else: # it's a normal macro stacks[-1].append(part) else: raise MacroError, "Unknown type: %s" % (part,) # if len(stacks) > 1: raise UnbalancedMacroError, stacks[-1] # return stacks[0] def expand_tree(self, tree): """ Expand a tree as returned by build_match_tree. Returns a string with macros expanded if possible. Any nested macros are expanded first, then passed up the evaluation tree as strings. """ parts = [] for elem in tree: if isinstance(elem, list): assert len(elem) >= 2 assert elem[0][0] == elem[-1][0] == 'macro' assert elem[0][1][2:] == elem[-1][1][2:] # text between + and -, expanded z = self.expand_tree(elem[1:-1]) expanded = self.eval_macro(elem[0][1], z) parts.append(expanded) elif isinstance(elem, tuple): type, text = elem if type == 'text': parts.append(text) elif type == 'macro': assert not (text.startswith("{+") or text.startswith("{-")) expanded = self.eval_macro(text) parts.append(expanded) else: raise MacroError, elem else: raise MacroError, elem # return ''.join(parts) def eval_macro(self, macrotext, spanned_text=""): """ Evaluate a macro. is a string like '+upper' or 'url'. Curly braces are allowed around it. """ original_macrotext = macrotext if macrotext.startswith("{") and macrotext.endswith("}"): macrotext = macrotext[1:-1] parts = macrotext.split(";") # args may be empty name, args = parts[0], parts[1:] # # get the name of the function/object that we're going to call fname = name if name.startswith("+") or name.startswith("-"): fname = name[1:] args.append(spanned_text) # add enclosed text as last parameter # try: f = self.namespace[fname] except KeyError: return original_macrotext # if macro not found, return the literal text # this MAY change later, or might be set as an option # if fname == name: # a "singular" macro (no + or -) if callable(f): return str(f(*args)) else: return str(f) # for string literals etc elif name.startswith("+"): return str(f().open(*args)) elif name.startswith("-"): return str(f().close(*args)) # # toplevel method def expand(self, text): """ Return text with macros expanded. """ matches = self.find_macros(text) parts = self.split_text(text, matches) tree = self.build_match_tree(parts) return self.expand_tree(tree) def replace_all(text, macro_ns, ignore_errors = 0): tm = TextMacros(macro_ns) return tm.expand(text) """ CHANGELOG 2005/06/18 Changes by Nicola Larosa Code cleanup lines shortened comments on line above code empty comments in empty lines 2005/05/30 Removed use of string module. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/plugins/0000700000175000017500000000000010644674643021741 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/rest2web/plugins/__init__.py0000600000175000017500000000000010311246051024015 0ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/rest2web/plugins/gallery.py0000600000175000017500000003164410472416145023752 0ustar madduckmadduck# Subversion Details # $LastChangedDate: 2005-06-21 09:07:43 +0100 (Tue, 21 Jun 2005) $ # $LastChangedBy: fuzzyman $ # $HeadURL: https://svn.rest2web.python-hosting.com/trunk/rest2web/restindex.py $ # $LastChangedRevision: 36 $ # The plugin that generates static gallery pages. # http://www.voidspace.org.uk/python/rest2web # Copyright Michael Foord, 2005. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # rest2web mailing list. # https://lists.sourceforge.net/lists/listinfo/rest2web-develop # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk """ Build a single html gallery page from a directory of images. It generates a single index page, and saves individual html files for each image. Requires PIL, pythonutils, and rest2web to be installed """ import os from posixpath import join as posixjoin from copy import deepcopy from rest2web.pythonutils.urlpath import relpathto, pathjoin from rest2web.pythonutils.configobj import ConfigObj from rest2web.pythonutils.cgiutils import replace # image imports try: import Image except ImportError: raise ImportError('Importing PIL - Python Imaging Library - failed.') image_types = ('.bmp', '.png', '.gif', '.ico', '.jpg', '.ecw', '.emf', '.fsh', '.jpm', '.ldf', '.lwf', '.pcx', '.pbm', '.pgm', '.ppm', '.raw', '.tga', '.tif') class Plugin(object): def __init__(self, processor): self.processor = processor # XXXX global plugins will probably need access # XXXX to the initital config file here def page(self, filepath, target, restindex, uservalues): param_list = ['thumb_size', 'gallery_dir', 'gallery_url', 'data_file', 'page_template', 'entry_template', 'gallery_mode'] for entry in param_list: if entry not in uservalues: raise ValueError, ('Missing value required by Gallery - "%s"' % entry) # make uservalues a copy, *not* a reference params = deepcopy(uservalues) params['thumb_size'] = params['thumb_size'].strip().split(',') # # this is how we know we're coming from the Plugin # rather than running standalone params['gallery_page'] = None # # make the values in the uservalues # relative to the right place params['gallery_dir'] = os.path.join(self.processor.dir, params['gallery_dir']) params['data_file'] = os.path.join(self.processor.dir, params['data_file']) params['page_template'] = os.path.join(self.processor.dir, params['page_template']) params['entry_template'] = os.path.join(self.processor.dir, params['entry_template']) # gall_url = posixjoin('/', self.processor.dir_as_url, target) html_url = posixjoin(pathjoin(gall_url, params['gallery_url']), 'html/') params['path_back'] = relpathto('/', html_url, gall_url) ## print target, gall_url, html_url, params['path_back'] data = get_info(params) # return {'gallery': make_pages(params, data)} def get_info(config): """Gets all the data for the gallery and builds thumbnails.""" # config data # x, y thumb_size = [int(val) for val in config['thumb_size']] gallery_dir = config['gallery_dir'] thumb_dir = os.path.join(gallery_dir, 'thumbnails') thumb_prefix = 'tn_' data_file = config['data_file'] # # get the image data file # this will create it if it doesn't yet exist try: # for backwards compatibility with ConfigObj3 data = ConfigObj(data_file, flatfile=False) except TypeError: # no such thing as a flatfile in ConfigObj 4 data = ConfigObj(data_file) # # if we are in mode 2 then just return the data file if config['gallery_mode'] == '2': return data # for image in data.keys(): # check all the files exist if not (os.path.isfile(os.path.join(gallery_dir, image)) and os.path.splitext(image)[1].lower() in image_types): # remove any missing one del data[image] # if not os.path.isdir(thumb_dir): os.makedirs(thumb_dir) # the_list = os.listdir(gallery_dir) for image in the_list: path = os.path.join(gallery_dir, image) if os.path.isdir(path): continue name, ext = os.path.splitext(image) if ext.lower() not in image_types: continue im = Image.open(path) size = im.size try: this = data[image] except KeyError: # this image isn't in the file data[image] = {} this = data[image] this['title'] = name.replace('_', ' ').title() this['description'] = '' this['size'] = size this['filesize'] = os.path.getsize(path) else: # we do have the filename # but has the image changed ? x = int(this['size'][0]) y = int(this['size'][1]) if not (x, y) == size: # the image has changed since last time (size is different) this['size'] = size this['filesize'] = os.path.getsize(path) # # always regenerate thumb nails thumb_name = os.path.join(thumb_dir, thumb_prefix + name + ext) im.thumbnail(thumb_size) try: im.save(thumb_name) except IOError: # FIXME: This causes us to skip animated jpgs del data[image] else: this['thumb_size'] = im.size this['thumbnail'] = thumb_prefix + name + ext # # save image data data.write() return data def make_pages(config, data): """Create the html pages for the individual files.""" # config data # x, y thumb_size = [int(val) for val in config['thumb_size']] gallery_dir = config['gallery_dir'] thumb_prefix = 'tn_' entry = open(config['entry_template']).read() page = open(config['page_template']).read() gallery_page = config['gallery_page'] # gallery_url = config['gallery_url'] thumb_url = posixjoin(gallery_url, 'thumbnails') html_url = posixjoin(gallery_url, 'html') html_dir = os.path.join(gallery_dir, 'html') # if not os.path.isdir(html_dir): os.makedirs(html_dir) # main_out = [] i = -1 image_list = data.keys() length = len(image_list) right_image = right_name = right_link = right_thumb = '' right_thumb_width = right_thumb_height = right_title = '' right_description = None while i < length: i += 1 if i != 0: left_image = image left_name = name left_link = link left_thumb = thumb left_thumb_width = thumb_width left_thumb_height = thumb_height left_title = title image = right_image name = right_name link = right_link thumb = right_thumb thumb_width = right_thumb_width thumb_height = right_thumb_height title = right_title description = right_description # if i != length: right_name = image_list[i] right_image = data[right_name] # XXXX how do we create the filename # XXXX always a straight '.html' ? right_link = os.path.splitext(right_name)[0] + '.html' right_thumb = right_image['thumbnail'] right_thumb_width = str(right_image['thumb_size'][0]) right_thumb_height = str(right_image['thumb_size'][1]) right_title = right_image['title'] # if i == 0: continue # width = str(image['size'][0]) height = str(image['size'][1]) # # generate the entry for the main page replace_dict = {} replace_dict['**link**'] = posixjoin(html_url, link) replace_dict['**thumb**'] = posixjoin(thumb_url, thumb) replace_dict['**width**'] = str(thumb_width) replace_dict['**height**'] = str(thumb_height) replace_dict['**title**'] = title main_out.append(replace(entry, replace_dict)) # # next we need to build the individual page replace_dict = {} replace_dict['**title**'] = title if i != 1: # not the first image replace_dict[''] = '' replace_dict['**linkleft**'] = left_link replace_dict['**thumbleft**'] = posixjoin('../thumbnails/', left_thumb) replace_dict['**widthleft**'] = left_thumb_width replace_dict['**heightleft**'] = left_thumb_height replace_dict['**titleleft**'] = left_title # XXXX replace_dict['**linkgallery**'] = config['path_back'] if i != length: # not the last image replace_dict[''] = '' replace_dict['**linkright**'] = right_link replace_dict['**thumbright**'] = posixjoin('../thumbnails/', right_thumb) replace_dict['**widthright**'] = right_thumb_width replace_dict['**heightright**'] = right_thumb_height replace_dict['**titleright**'] = right_title # replace_dict['**image**'] = posixjoin('../', name) replace_dict['**widthmain**'] = width replace_dict['**heightmain**'] = height replace_dict['**description**'] = image['description'] # make the substitutions in the page this_page = replace(page, replace_dict) page_path = os.path.join(html_dir, os.path.splitext(name)[0] + '.html') open(page_path, 'w').write(this_page) # if gallery_page: # are we running as a standalone ? gallery_template = open(config['gallery_template']).read() main_page = gallery_template.replace('**gallery**', ''.join(main_out)) open(gallery_page, 'w').write(main_page) else: # or called from the plugin return ''.join(main_out) ########################################################### message = '''gallery.py by Michael Foord Written for rest2web - http://www.voidspace.org.uk/python/rest2web gallery.py config_name''' DEBUG = 0 if __name__ == '__main__': import sys if len(sys.argv) > 1: config_name = sys.argv[1] if not os.path.isfile(config_name): print 'File Error "%s" not found.' sys.exit(1) else: config_name = 'gallery.ini' if not os.path.isfile(config_name) and not DEBUG: sys.exit() # get the image config file # this will create it if it doesn't yet exist config = ConfigObj(config_name, file_error=DEBUG) if DEBUG: # default settings for an empty (new) config config['thumb_size'] = 150, 150 config['gallery_dir'] = 'gallery' config['gallery_url'] = 'gallery' config['data_file'] = 'gallery_data.ini' config['page_template'] = 'page.html' config['entry_template'] = 'entry.html' config['gallery_template'] = 'gallery.html' config['gallery_page'] = 'test.html' config['gallery_mode'] = '2' # # FIXME: we *assume* the gallery dir is a single directory down # so the html file is then two directories down # need a better way of calculating path back config['path_back'] = posixjoin('../../', config['gallery_page']) data = get_info(config) # we have now created all the thumbnails and saved the config file make_pages(config, data) # """ ISSUES ====== Two images with the same filename but different extensions will be given the same html page name (and so screw things up). TODO ==== Documenting Testing as a standalone app 'path_back' needs sorting for standalone app Encoding needs sorting Ought to be able to emit html in a 'rest compatible' way.. Raise an error if the scanned directory doesn't exist ``<% path_to_root %>`` (etc) not expanded in gallery files. Is there a work-around ? CHANGELOG ========= 2006/04/03 ---------- Skip files that cause an ``IOError`` when we write out the thumbnail. This skips animated jpg images. 2005/12/10 ---------- Changed urlpath import. Defined image_types. 2005/09/04 ---------- We always regenerate thumbnails now. 2005/08/08 ---------- Bug fix - now gets all images (was mising the first one). 2005/07/31 ---------- It works ! """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/0000700000175000017500000000000010644674643022662 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/standout.py0000600000175000017500000002073310472435430025070 0ustar madduckmadduck# standout.py # v3.0.0 # A flexible object to redirect standard output and standard error # Allows logging to a file and to set a level of verbosity # Copyright (C) 2006 Michael Foord # E-mail: fuzzyman AT voidspace DOT org DOT uk # StandOut 3 # http://www.voidspace.org.uk/python/standout.html # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # For information about bugfixes, updates and support, please join the # Pythonutils mailing list: # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. import sys __version__ = '3.0.0' __docformat__ = "restructuredtext en" __all__ = ( 'StandOut', ) class StandOut(object): def __init__(self, logfile=None, logmode="w", stdout=True, stderr=True, errPrefix='[err] ', priority=5, threshold=None, errThreshold=None, outThreshold=None, errLogfileThreshold=None, outLogfileThreshold=None, outStreamThreshold=None, errStreamThreshold=None, unbuffered=False): self.__stdout = None self.__stderr = None self.logfile = logfile self._logfile = None self._logmode = logmode self.__outputStream = None self.__errorStream = None self.__priority = priority _outStreamThreshold = 5 _errStreamThreshold = 0 _outLogfileThreshold = 5 _errLogfileThreshold = 0 if threshold is not None: _outStreamThreshold = _errStreamThreshold = _outLogfileThreshold = _errLogfileThreshold = threshold if outThreshold is not None: _outStreamThreshold = _outLogfileThreshold = outThreshold if errThreshold is not None: errStreamThreshold = errLogfileThreshold = errThreshold if outStreamThreshold is not None: _outStreamThreshold = outStreamThreshold if errStreamThreshold is not None: _errStreamThreshold = errStreamThreshold if outLogfileThreshold is not None: _outLogfileThreshold = outLogfileThreshold if errLogfileThreshold is not None: _errLogfileThreshold = errLogfileThreshold if logfile: self._logfile = open(self.logfile, self._logmode) if stdout: self.__stdout = sys.stdout self.__outputStream = _Stream(sys.stdout, self._logfile, priority=priority, streamThreshold=_outStreamThreshold, logfileThreshold=_outLogfileThreshold, unbuffered=unbuffered) sys.stdout = self.__outputStream if stderr: self.__errorStream = _Stream(sys.stderr, self._logfile, prefix=errPrefix, priority=priority, streamThreshold=_errStreamThreshold, logfileThreshold=_errLogfileThreshold, unbuffered=unbuffered) self.__stderr = sys.stderr sys.stderr = self.__errorStream def __setPriority(self, value): self.__priority = value if self.__errorStream: self.__errorStream._priority = value if self.__outputStream: self.__outputStream._priority = value priority = property(lambda self: self.__priority, __setPriority) def __getThreshold(self): if self.errThreshold == self.outThreshold: return self.errThreshold return -1 def __setThreshold(self, value): if self.__errorStream: self.__errorStream._streamThreshold = value self.__errorStream._logfileThreshold = value if self.__outputStream: self.__outputStream._streamThreshold = value self.__outputStream._logfileThreshold = value threshold = property(__getThreshold, __setThreshold) def __setErrThreshold(self, value): if self.__errorStream: self.__errorStream._streamThreshold = value self.__errorStream._logfileThreshold = value def __getErrThreshold(self): if self.__errorStream and (self.__errorStream._streamThreshold == self.__errorStream._logfileThreshold): return self.__errorStream._streamThreshold return -1 errThreshold = property(__getErrThreshold, __setErrThreshold) def __setOutThreshold(self, value): if self.__outputStream: self.__outputStream._streamThreshold = value self.__outputStream._logfileThreshold = value def __getOutThreshold(self): if self.__outputStream and (self.__outputStream._streamThreshold == self.__outputStream._logfileThreshold): return self.__outputStream._streamThreshold return -1 outThreshold = property(__getOutThreshold, __setOutThreshold) def __setErrStreamThreshold(self, value): if self.__errorStream: self.__errorStream._streamThreshold = value def __getErrStreamThreshold(self): if self.__errorStream: return self.__errorStream._streamThreshold return -1 errStreamThreshold = property(__getErrStreamThreshold, __setErrStreamThreshold) def __setErrLogfileThreshold(self, value): if self.__errorStream: self.__errorStream._logfileThreshold = value def __getErrLogfileThreshold(self): if self._logfile and self.__errorStream: return self.__errorStream._logfileThreshold return -1 errLogfileThreshold = property(__getErrLogfileThreshold, __setErrLogfileThreshold) def __setOutStreamThreshold(self, value): if self.__outputStream: self.__outputStream._streamThreshold = value def __getOutStreamThreshold(self): if self.__outputStream: return self.__outputStream._streamThreshold return -1 outStreamThreshold = property(__getOutStreamThreshold, __setOutStreamThreshold) def __setOutLogfileThreshold(self, value): if self.__outputStream: self.__outputStream._logfileThreshold = value def __getOutLogfileThreshold(self): if self._logfile and self.__outputStream: return self.__outputStream._logfileThreshold return -1 outLogfileThreshold = property(__getOutLogfileThreshold, __setOutLogfileThreshold) def close(self): if self.__stdout: sys.stdout = self.__stdout if self.__stderr: sys.stderr = self.__stderr if self._logfile: self._logfile.close() class _Stream(object): def __init__(self, stream, outfile=None, prefix=None, priority=5, streamThreshold=5, logfileThreshold=5, unbuffered=False): self._stream = stream self._outfile = outfile self._prefix = prefix self._done_newline = True self._priority = priority self._streamThreshold = streamThreshold self._logfileThreshold = logfileThreshold self._unbuffered = unbuffered def write(self, data, priority=None): if not data: # avoid writing the prefix for null input return if priority is None: priority = self._priority if self._prefix is not None: # Need every newline (including the first) to start with the prefix # but not print the prefix for a newline if it is the last character # instead do it on the next print if self._done_newline: data = self._prefix + data self._done_newline = False terminated = False if data[-1] == '\n': terminated = True data = data[:-1] self._done_newline = True data = data.replace('\n', '\n' + self._prefix) if terminated: data = data + '\n' if priority >= self._streamThreshold: self._stream.write(data) if self._unbuffered: self._stream.flush() if self._outfile is not None and priority >= self._logfileThreshold: self._outfile.write(data) def writelines(self, inLines): self.write(''.join(inLines)) def __getattr__(self, attribute): # doesn't proxy attributes shread by this class and the underlying stream # this affects next and some double underscore attributes try: return self.__dict__[attribute] except KeyError: return getattr(self._stream, attribute) rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/cgiutils.py0000600000175000017500000004707410414273736025066 0ustar madduckmadduck# Version 0.3.5 # 2005/11/26 # Copyright Michael Foord 2004 & 2005 # cgiutils.py # Functions and constants useful for working with CGIs # http://www.voidspace.org.uk/python/modules.shtml # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the Pythonutils mailing list. # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk import os import sys __version__ = '0.3.5' __all__ = ( 'serverline', 'SENDMAIL', 'validchars', 'alphanums', 'getrequest', 'getform', 'getall', 'isblank', 'formencode', 'formdecode', 'mailme', 'sendmailme', 'createhtmlmail', 'environdata', 'validemail', 'cgiprint', 'ucgiprint', 'replace', 'error', 'makeindexline', 'istrue', 'randomstring', 'blacklisted', '__version__', ) serverline = "Content-Type: text/html" # A common location of sendmail on servers SENDMAIL = "/usr/sbin/sendmail" validchars = 'abcdefghijklmnopqrstuvwxyz0123456789!-_*' alphanums = 'abcdefghijklmnopqrstuvwxyz0123456789' ####################################################### # Some functions for dealing with CGI forms (instances of FieldStorage) def getrequest(valuelist=None, nolist=False): """ Initialise the ``FieldStorage`` and return the specified list of values as a dictionary. If you don't specify a list of values, then *all* values will be returned. If you set ``nolist`` to ``True`` then any parameters supplied as lists will only have their first entry returned. """ import cgi form = cgi.FieldStorage() if valuelist is not None: return getform(valuelist, form, nolist=nolist) else: return getall(form, nolist=nolist) def getform(valuelist, theform, notpresent='', nolist=False): """ This function, given a CGI form, extracts the data from it, based on valuelist passed in. Any non-present values are set to '' - although this can be changed. It also takes a keyword argument 'nolist'. If this is True list values only return their first value. Returns a dictionary. """ data = {} for field in valuelist: if not theform.has_key(field): data[field] = notpresent else: if not isinstance(theform[field], list): data[field] = theform[field].value else: if not nolist: # allows for list type values data[field] = [x.value for x in theform[field]] else: # just fetch the first item data[field] = theform[field][0].value return data def getall(theform, nolist=False): """ Passed a form (FieldStorage instance) return all the values. This doesn't take into account file uploads. Also accepts the 'nolist' keyword argument as ``getform``. Returns a dictionary. """ data = {} for field in theform.keys(): if not isinstance(theform[field], list): data[field] = theform[field].value else: if not nolist: # allows for list type values data[field] = [x.value for x in theform[field]] else: # just fetch the first item data[field] = theform[field][0].value return data def isblank(indict): """ Passed an indict of values it checks if any of the values are set. Returns ``True`` if every member of the indict is empty (evaluates as False). I use it on a form processed with getform to tell if my CGI has been activated without any values. """ return not [val for val in indict.values() if val] def formencode(theform): """ A version that turns a cgi form into a single string. It only handles single and list values, not multipart. This allows the contents of a form requested to be encoded into a single value as part of another request. """ from urllib import urlencode, quote_plus return quote_plus(urlencode(getall(theform))) def formdecode(thestring): """Decode a single string back into a form like dictionary.""" from cgi import parse_qs from urllib import unquote_plus return parse_qs(unquote_plus(thestring), True) ############################################################# # Functions for handling emails # # Use mailme for sending email - specify a path to sendmail *or* a host, port etc (optionally username) def mailme(to_email, msg, email_subject=None, from_email=None, host='localhost', port=25, username=None, password=None, html=True, sendmail=None): """ This function will send an email using ``sendmail`` or ``smtplib``, depending on what parameters you pass it. If you want to use ``sendmail`` to send the email then set ``sendmail='/path/to/sendmail'``. (The ``SENDMAIL`` value from Constants_ often works). If you aren't using sendmail then you will need to set ``host`` and ``port`` to the correct values. If your server requires authentication then you'll need to supply the correct ``username`` and ``password``. ``to_email`` can be a single email address, *or* a list of addresses. ``mailme`` *assumes* you are sending an html email created by ``createhtmlmail``. If this isn't the case then set ``html=False``. Some servers won't let you send a message without supplying a ``from_email``. """ if sendmail is not None: # use sendmailme if specified return sendmailme(to_email, msg, email_subject, from_email, html, sendmail) if not isinstance(to_email, list): # if we have a single email then change it into a list to_email = [to_email] # import smtplib # head = "To: %s\r\n" % ','.join(to_email) if from_email is not None: head += ('From: %s\r\n' % from_email) # subject is in the body of an html email if not html and email_subject is not None: head += ("Subject: %s\r\n\r\n" % email_subject) msg = head + msg # server = smtplib.SMTP(host, port) if username: server.login(username, password) server.sendmail(from_email, to_email, msg) server.quit() def sendmailme(to_email, msg, email_subject=None, from_email=None, html=True, sendmail=SENDMAIL): """ Quick and dirty, pipe a message to sendmail. Can only work on UNIX type systems with sendmail. Will need the path to sendmail - defaults to the 'SENDMAIL' constant. ``to_email`` can be a single email address, *or* a list of addresses. *Assumes* you are sending an html email created by ``createhtmlmail``. If this isn't the case then set ``html=False``. """ if not isinstance(to_email, list): to_email = [to_email] o = os.popen("%s -t" % sendmail,"w") o.write("To: %s\r\n" % ','.join(to_email)) if from_email: o.write("From: %s\r\n" % from_email) if not html and email_subject: o.write("Subject: %s\r\n" % email_subject) o.write("\r\n") o.write("%s\r\n" % msg) o.close() def createhtmlmail(subject, html, text=None): """ Create a mime-message that will render as HTML or text as appropriate. If no text is supplied we use htmllib to guess a text rendering. (so html needs to be well formed) Adapted from recipe 13.5 from Python Cookbook 2 """ import MimeWriter, mimetools, StringIO if text is None: # produce an approximate text from the HTML input import htmllib import formatter textout = StringIO.StringIO() formtext = formatter.AbstractFormatter(formatter.DumbWriter(textout)) parser = htmllib.HTMLParser(formtext) parser.feed(html) parser.close() text = textout.getvalue() del textout, formtext, parser out = StringIO.StringIO() # output buffer for our message htmlin = StringIO.StringIO(html) # input buffer for the HTML txtin = StringIO.StringIO(text) # input buffer for the plain text writer = MimeWriter.MimeWriter(out) # Set up some basic headers. Place subject here because smtplib.sendmail # expects it to be in the message, as relevant RFCs prescribe. writer.addheader("Subject", subject) writer.addheader("MIME-Version", "1.0") # Start the multipart section of the message. Multipart/alternative seems # to work better on some MUAs than multipart/mixed. writer.startmultipartbody("alternative") writer.flushheaders() # the plain-text section: just copied through, assuming iso-8859-1 # XXXX always true ? subpart = writer.nextpart() pout = subpart.startbody("text/plain", [("charset", 'iso-8859-l')]) pout.write(txtin.read()) txtin.close() # the HTML subpart of the message: quoted-printable, just in case subpart = writer.nextpart() subpart.addheader("Content-Transfer-Encoding", "quoted-printable") pout = subpart.startbody("text/html", [("charset", 'us-ascii')]) mimetools.encode(htmlin, pout, 'quoted-printable') htmlin.close() # You're done; close your writer and return the message as a string writer.lastpart() msg = out.getvalue() out.close() return msg def environdata(): """Returns some data about the CGI environment, in a way that can be mailed.""" ENVIRONLIST = [ 'REQUEST_URI','HTTP_USER_AGENT','REMOTE_ADDR','HTTP_FROM','REMOTE_HOST','REMOTE_PORT','SERVER_SOFTWARE','HTTP_REFERER','REMOTE_IDENT','REMOTE_USER','QUERY_STRING','DATE_LOCAL' ] # XXX put this in template ?? environs = [] environs.append("\n\n---------------------------------------\n") for x in ENVIRONLIST: if os.environ.has_key(x): environs.append("%s: %s\n" % (x, os.environ[x])) environs.append("---------------------------------------\n") return ''.join(environs) def validemail(email): """ A quick function to do a basic email validation. Returns False or the email address. """ if ' ' in email: return False dot = email.rfind('.') at = email.find('@') if dot == -1 or at < 1 or at > dot: return False return email ########################################################## def error(errorval=''): """The generic error function.""" print serverline print print '''An Error Has Occurred

    Very Sorry

    An Error Has Occurred

    ''' if errorval: print '

    %s

    ' % errorval print '
    ' sys.exit() ######################################################### def makeindexline(url, startpage, total, numonpage=10, pagesonscreen=5): """ Make a menu line for a given number of inputs, with a certain number per page. Will look something like : :: First Previous 22 23 24 25 26 27 28 29 30 31 32 Next Last Each number or word will be a link to the relevant page. url should be in the format : ``'%s'`` - it will have the two ``%s`` values filled in by the function. The url will automatically be put between ```` tags. Your script needs to accepts a parameter ``start`` telling it which page to display. ``startpage`` is the page actually being viewed - which won't be a link. ``total`` is the number of total inputs. ``numonpage`` is the number of inputs per page - this tells makeindexline how many pages to divide the total into. The links shown will be some before startpage and some after. The amount of pages links are shown for is ``pagesonscreen``. (The actual total number shown will be *2 \* pagesonscreen + 1*). The indexes generated are *a bit* like the ones created by google. Unlike google however, next and previous jump you into the *middle* of the next set of links. i.e. If you are on page 27 next will take you to 33 and previous to 21. (assuming pagesonscreen is 5). This makes it possible to jump more quickly through a lot of links. Also - the current page will always be in the center of the index. (So you never *need* Next just to get to the next page). """ b = '%s' url = b % url outlist = [] last = '' next = '' numpages = total//numonpage if total%numonpage: numpages += 1 if startpage - pagesonscreen > 1: outlist.append(url % (1, 'First')) outlist.append(' ') outlist.append(url % (startpage-pagesonscreen-1, 'Previous')) outlist.append(' ') index = max(startpage - pagesonscreen, 1) end = min(startpage+pagesonscreen, numpages) while index <= end: if index == startpage: outlist.append(b % startpage) else: outlist.append(url % (index, index)) index += 1 outlist.append(' ') if (startpage+pagesonscreen) < numpages: outlist.append(url % (startpage+pagesonscreen+1, 'Next')) outlist.append(' ') outlist.append(url % (numpages, 'Last')) # return ' '.join(outlist) ###################################### def istrue(value): """ Accepts a string as input. If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns ``True``. If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns ``False``. ``istrue`` is not case sensitive. Any other input will raise a ``KeyError``. """ return { 'yes': True, 'no': False, 'on': True, 'off': False, '1': True, '0': False, 'true': True, 'false': False, }[value.lower()] def randomstring(length): """ Return a random string of length 'length'. The string is comprised only of numbers and lowercase letters. """ import random outstring = [] while length > 0: length -= 1 outstring.append(alphanums[int(random.random()*36)]) return ''.join(outstring) ################################## def cgiprint(inline='', unbuff=False, line_end='\r\n'): """ Print to the ``stdout``. Set ``unbuff=True`` to flush the buffer after every write. It prints the inline you send it, followed by the ``line_end``. By default this is ``\r\n`` - which is the standard specified by the RFC for http headers. """ sys.stdout.write(inline) sys.stdout.write(line_end) if unbuff: sys.stdout.flush() def ucgiprint(inline='', unbuff=False, encoding='UTF-8', line_end='\r\n'): """ A unicode version of ``cgiprint``. It allows you to store everything in your script as unicode and just do your encoding in one place. Print to the ``stdout``. Set ``unbuff=True`` to flush the buffer after every write. It prints the inline you send it, followed by the ``line_end``. By default this is ``\r\n`` - which is the standard specified by the RFC for http headers. ``inline`` should be a unicode string. ``encoding`` is the encoding used to encode ``inline`` to a byte-string. It defaults to ``UTF-8``, set it to ``None`` if you pass in ``inline`` as a byte string rather than a unicode string. """ if encoding: inline = inline.encode(encoding) # don't need to encode the line endings sys.stdout.write(inline) sys.stdout.write(line_end) if unbuff: sys.stdout.flush() def replace(instring, indict): """ This function provides a simple but effective template system for your html pages. Effectively it is a convenient way of doing multiple replaces in a single string. Takes a string and a dictionary of replacements. This function goes through the string and replaces every occurrence of every dicitionary key with it's value. ``indict`` can also be a list of tuples instead of a dictionary (or anything accepted by the dict function). """ indict = dict(indict) if len(indict) > 40: regex = re.compile("(%s)" % "|".join(map(re.escape, indict.keys()))) # For each match, look-up corresponding value in dictionary return regex.sub(lambda mo: indict[mo.string[mo.start():mo.end()]], instring) for key in indict: instring = instring.replace(key, indict[key]) return instring ############################ def blacklisted(ip, DNSBL_HOST='sbl-xbl.spamhaus.org'): """ Returns ``True`` if ip address is a blacklisted IP (i.e. from a spammer). ip can also be a domain name - this raises ``socket.gaierror`` if the ip is a domain name that cannot be resolved. The DNS blacklist host (``DNSBL_HOST``) defaults to *sbl-xbl.spamhaus.org*. Other ones you could use include : - 'relays.ordb.org' - 'dns.rfc-ignorant.org' - 'postmaster.rfc-ignorant.org' - 'http.dnsbl.sorbs.net' - 'misc.dnsbl.sorbs.net' - 'spam.dnsbl.sorbs.net' - 'bl.spamcop.net' Useful for vetting user added information posted to web applications. - 'relays.ordb.org' - 'dns.rfc-ignorant.org' - 'postmaster.rfc-ignorant.org' - 'http.dnsbl.sorbs.net' - 'misc.dnsbl.sorbs.net' - 'spam.dnsbl.sorbs.net' - 'bl.spamcop.net' .. note:: Another, possibly more effective, way of coping with spam input to web applications is to use the `Akismet Web Service `_. For this you can use the `Python Akismet API Interface `_. """ # turn '1.2.3.4' into '4.3.2.1.sbl-xbl.spamhaus.org' import socket # convert domain name to IP # raises an error if domain name can't be resolved ip = socket.gethostbyname(ip) iplist = ip.split('.') iplist.reverse() ip = '%s.%s' % ('.'.join(iplist), DNSBL_HOST) try: socket.gethostbyname(ip) return True except socket.gaierror: return False ############################ if __name__ == '__main__': print 'No tests yet - sorry' """ TODO/ISSUES =========== The indexes generated by makeindexline use next to jump 10 pages. This is different to what people will expect if they are used to the 'Google' type index lines. createhtmlmail assumes iso-8859-1 input encoding for the html email functions to support 'cc' and 'bcc' Need doctests Changelog ========= 2005/11/26 Version 0.3.5 ----------------------------- Add the ``blacklisted`` function. Added ``__version__`` 2005/10/29 Version 0.3.4 ----------------------------- Shortened ``isblank``. 2005/09/21 Version 0.3.3 ----------------------------- Fixed bug in ``getall``. Fixed bug in ``getrequest``. 2005/08/27 Version 0.3.2 ----------------------------- Large dictionary replaces use a regex approach. 2005/08/20 Version 0.3.1 ----------------------------- Improved istrue function. Added __all__. Various other code/doc improvements. 2005/04/07 Version 0.3.0 ----------------------------- Changed the email functions, this may break things (but it's better this way) Added createhtmlemail, removed loginmailme mailme is now a wrapper for sendmailme, mailme, *and* the old loginmailme 2005/03/20 Version 0.2.0 ----------------------------- Added ucgiprint and replace. 2005/02/18 Version 0.1.0 ----------------------------- The first numbered version. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/__init__.py0000600000175000017500000000200510414273736024763 0ustar madduckmadduck# ``__init__.py`` # The package file for the Pythonutils module # http://www.voidspace.org.uk/python/pythonutils.html # Copyright Michael Foord and Nicola Larosa, 2004 & 2005. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # Pythonutils mailing list. # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk # this wrapper imports all the pythonutils modules # *except* cgiutils and validate """ This makes most of the pythonutils objects available from the ``pythonutils`` namespace. It doesn't import cgiutils or validate. """ from listquote import * from configobj import * from pathutils import * from standout import * from urlpath import * from odict import * __version__ = '0.2.5' """ CHANGELOG 2005/09/02 ---------- Added ``__version__`` """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/configobj.py0000600000175000017500000024452010536615641025176 0ustar madduckmadduck# configobj.py # A config file reader/writer that supports nested sections in config files. # Copyright (C) 2005-2006 Michael Foord, Nicola Larosa # E-mail: fuzzyman AT voidspace DOT org DOT uk # nico AT tekNico DOT net # ConfigObj 4 # http://www.voidspace.org.uk/python/configobj.html # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # For information about bugfixes, updates and support, please join the # ConfigObj mailing list: # http://lists.sourceforge.net/lists/listinfo/configobj-develop # Comments, suggestions and bug reports welcome. from __future__ import generators import sys INTP_VER = sys.version_info[:2] if INTP_VER < (2, 2): raise RuntimeError("Python v.2.2 or later needed") import os, re import compiler from types import StringTypes from warnings import warn try: from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE except ImportError: # Python 2.2 does not have these # UTF-8 BOM_UTF8 = '\xef\xbb\xbf' # UTF-16, little endian BOM_UTF16_LE = '\xff\xfe' # UTF-16, big endian BOM_UTF16_BE = '\xfe\xff' if sys.byteorder == 'little': # UTF-16, native endianness BOM_UTF16 = BOM_UTF16_LE else: # UTF-16, native endianness BOM_UTF16 = BOM_UTF16_BE # A dictionary mapping BOM to # the encoding to decode with, and what to set the # encoding attribute to. BOMS = { BOM_UTF8: ('utf_8', None), BOM_UTF16_BE: ('utf16_be', 'utf_16'), BOM_UTF16_LE: ('utf16_le', 'utf_16'), BOM_UTF16: ('utf_16', 'utf_16'), } # All legal variants of the BOM codecs. # TODO: the list of aliases is not meant to be exhaustive, is there a # better way ? BOM_LIST = { 'utf_16': 'utf_16', 'u16': 'utf_16', 'utf16': 'utf_16', 'utf-16': 'utf_16', 'utf16_be': 'utf16_be', 'utf_16_be': 'utf16_be', 'utf-16be': 'utf16_be', 'utf16_le': 'utf16_le', 'utf_16_le': 'utf16_le', 'utf-16le': 'utf16_le', 'utf_8': 'utf_8', 'u8': 'utf_8', 'utf': 'utf_8', 'utf8': 'utf_8', 'utf-8': 'utf_8', } # Map of encodings to the BOM to write. BOM_SET = { 'utf_8': BOM_UTF8, 'utf_16': BOM_UTF16, 'utf16_be': BOM_UTF16_BE, 'utf16_le': BOM_UTF16_LE, None: BOM_UTF8 } try: from validate import VdtMissingValue except ImportError: VdtMissingValue = None try: enumerate except NameError: def enumerate(obj): """enumerate for Python 2.2.""" i = -1 for item in obj: i += 1 yield i, item try: True, False except NameError: True, False = 1, 0 __version__ = '4.3.3-alpha2' __revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $' __docformat__ = "restructuredtext en" # NOTE: Does it make sense to have the following in __all__ ? # NOTE: DEFAULT_INDENT_TYPE, NUM_INDENT_SPACES, MAX_INTERPOL_DEPTH # NOTE: If used via ``from configobj import...`` # NOTE: They are effectively read only __all__ = ( '__version__', 'DEFAULT_INDENT_TYPE', 'DEFAULT_INTERPOLATION', 'NUM_INDENT_SPACES', 'MAX_INTERPOL_DEPTH', 'ConfigObjError', 'NestingError', 'ParseError', 'DuplicateError', 'ConfigspecError', 'ConfigObj', 'SimpleVal', 'InterpolationError', 'InterpolationLoopError', 'MissingInterpolationOption', 'RepeatSectionError', 'UnreprError', 'UnknownType', '__docformat__', 'flatten_errors', ) DEFAULT_INTERPOLATION = 'configparser' DEFAULT_INDENT_TYPE = ' ' NUM_INDENT_SPACES = 4 MAX_INTERPOL_DEPTH = 10 OPTION_DEFAULTS = { 'interpolation': True, 'raise_errors': False, 'list_values': True, 'create_empty': False, 'file_error': False, 'configspec': None, 'stringify': True, # option may be set to one of ('', ' ', '\t') 'indent_type': None, 'encoding': None, 'default_encoding': None, 'unrepr': False, 'write_empty_values': False, } def getObj(s): s = "a=" + s p = compiler.parse(s) return p.getChildren()[1].getChildren()[0].getChildren()[1] class UnknownType(Exception): pass class Builder: def build(self, o): m = getattr(self, 'build_' + o.__class__.__name__, None) if m is None: raise UnknownType(o.__class__.__name__) return m(o) def build_List(self, o): return map(self.build, o.getChildren()) def build_Const(self, o): return o.value def build_Dict(self, o): d = {} i = iter(map(self.build, o.getChildren())) for el in i: d[el] = i.next() return d def build_Tuple(self, o): return tuple(self.build_List(o)) def build_Name(self, o): if o.name == 'None': return None if o.name == 'True': return True if o.name == 'False': return False # An undefinted Name raise UnknownType('Undefined Name') def build_Add(self, o): real, imag = map(self.build_Const, o.getChildren()) try: real = float(real) except TypeError: raise UnknownType('Add') if not isinstance(imag, complex) or imag.real != 0.0: raise UnknownType('Add') return real+imag def build_Getattr(self, o): parent = self.build(o.expr) return getattr(parent, o.attrname) def build_UnarySub(self, o): return -self.build_Const(o.getChildren()[0]) def build_UnaryAdd(self, o): return self.build_Const(o.getChildren()[0]) def unrepr(s): if not s: return s return Builder().build(getObj(s)) def _splitlines(instring): """Split a string on lines, without losing line endings or truncating.""" class ConfigObjError(SyntaxError): """ This is the base class for all errors that ConfigObj raises. It is a subclass of SyntaxError. """ def __init__(self, message='', line_number=None, line=''): self.line = line self.line_number = line_number self.message = message SyntaxError.__init__(self, message) class NestingError(ConfigObjError): """ This error indicates a level of nesting that doesn't match. """ class ParseError(ConfigObjError): """ This error indicates that a line is badly written. It is neither a valid ``key = value`` line, nor a valid section marker line. """ class DuplicateError(ConfigObjError): """ The keyword or section specified already exists. """ class ConfigspecError(ConfigObjError): """ An error occured whilst parsing a configspec. """ class InterpolationError(ConfigObjError): """Base class for the two interpolation errors.""" class InterpolationLoopError(InterpolationError): """Maximum interpolation depth exceeded in string interpolation.""" def __init__(self, option): InterpolationError.__init__( self, 'interpolation loop detected in value "%s".' % option) class RepeatSectionError(ConfigObjError): """ This error indicates additional sections in a section with a ``__many__`` (repeated) section. """ class MissingInterpolationOption(InterpolationError): """A value specified for interpolation was missing.""" def __init__(self, option): InterpolationError.__init__( self, 'missing option "%s" in interpolation.' % option) class UnreprError(ConfigObjError): """An error parsing in unrepr mode.""" class InterpolationEngine(object): """ A helper class to help perform string interpolation. This class is an abstract base class; its descendants perform the actual work. """ # compiled regexp to use in self.interpolate() _KEYCRE = re.compile(r"%\(([^)]*)\)s") def __init__(self, section): # the Section instance that "owns" this engine self.section = section def interpolate(self, key, value): def recursive_interpolate(key, value, section, backtrail): """The function that does the actual work. ``value``: the string we're trying to interpolate. ``section``: the section in which that string was found ``backtrail``: a dict to keep track of where we've been, to detect and prevent infinite recursion loops This is similar to a depth-first-search algorithm. """ # Have we been here already? if backtrail.has_key((key, section.name)): # Yes - infinite loop detected raise InterpolationLoopError(key) # Place a marker on our backtrail so we won't come back here again backtrail[(key, section.name)] = 1 # Now start the actual work match = self._KEYCRE.search(value) while match: # The actual parsing of the match is implementation-dependent, # so delegate to our helper function k, v, s = self._parse_match(match) if k is None: # That's the signal that no further interpolation is needed replacement = v else: # Further interpolation may be needed to obtain final value replacement = recursive_interpolate(k, v, s, backtrail) # Replace the matched string with its final value start, end = match.span() value = ''.join((value[:start], replacement, value[end:])) new_search_start = start + len(replacement) # Pick up the next interpolation key, if any, for next time # through the while loop match = self._KEYCRE.search(value, new_search_start) # Now safe to come back here again; remove marker from backtrail del backtrail[(key, section.name)] return value # Back in interpolate(), all we have to do is kick off the recursive # function with appropriate starting values value = recursive_interpolate(key, value, self.section, {}) return value def _fetch(self, key): """Helper function to fetch values from owning section. Returns a 2-tuple: the value, and the section where it was found. """ # switch off interpolation before we try and fetch anything ! save_interp = self.section.main.interpolation self.section.main.interpolation = False # Start at section that "owns" this InterpolationEngine current_section = self.section while True: # try the current section first val = current_section.get(key) if val is not None: break # try "DEFAULT" next val = current_section.get('DEFAULT', {}).get(key) if val is not None: break # move up to parent and try again # top-level's parent is itself if current_section.parent is current_section: # reached top level, time to give up break current_section = current_section.parent # restore interpolation to previous value before returning self.section.main.interpolation = save_interp if val is None: raise MissingInterpolationOption(key) return val, current_section def _parse_match(self, match): """Implementation-dependent helper function. Will be passed a match object corresponding to the interpolation key we just found (e.g., "%(foo)s" or "$foo"). Should look up that key in the appropriate config file section (using the ``_fetch()`` helper function) and return a 3-tuple: (key, value, section) ``key`` is the name of the key we're looking for ``value`` is the value found for that key ``section`` is a reference to the section where it was found ``key`` and ``section`` should be None if no further interpolation should be performed on the resulting value (e.g., if we interpolated "$$" and returned "$"). """ raise NotImplementedError class ConfigParserInterpolation(InterpolationEngine): """Behaves like ConfigParser.""" _KEYCRE = re.compile(r"%\(([^)]*)\)s") def _parse_match(self, match): key = match.group(1) value, section = self._fetch(key) return key, value, section class TemplateInterpolation(InterpolationEngine): """Behaves like string.Template.""" _delimiter = '$' _KEYCRE = re.compile(r""" \$(?: (?P\$) | # Two $ signs (?P[_a-z][_a-z0-9]*) | # $name format {(?P[^}]*)} # ${name} format ) """, re.IGNORECASE | re.VERBOSE) def _parse_match(self, match): # Valid name (in or out of braces): fetch value from section key = match.group('named') or match.group('braced') if key is not None: value, section = self._fetch(key) return key, value, section # Escaped delimiter (e.g., $$): return single delimiter if match.group('escaped') is not None: # Return None for key and section to indicate it's time to stop return None, self._delimiter, None # Anything else: ignore completely, just return it unchanged return None, match.group(), None interpolation_engines = { 'configparser': ConfigParserInterpolation, 'template': TemplateInterpolation, } class Section(dict): """ A dictionary-like object that represents a section in a config file. It does string interpolation if the 'interpolation' attribute of the 'main' object is set to True. Interpolation is tried first from this object, then from the 'DEFAULT' section of this object, next from the parent and its 'DEFAULT' section, and so on until the main object is reached. A Section will behave like an ordered dictionary - following the order of the ``scalars`` and ``sections`` attributes. You can use this to change the order of members. Iteration follows the order: scalars, then sections. """ def __init__(self, parent, depth, main, indict=None, name=None): """ * parent is the section above * depth is the depth level of this section * main is the main ConfigObj * indict is a dictionary to initialise the section with """ if indict is None: indict = {} dict.__init__(self) # used for nesting level *and* interpolation self.parent = parent # used for the interpolation attribute self.main = main # level of nesting depth of this Section self.depth = depth # the sequence of scalar values in this Section self.scalars = [] # the sequence of sections in this Section self.sections = [] # purely for information self.name = name # for comments :-) self.comments = {} self.inline_comments = {} # for the configspec self.configspec = {} self._order = [] self._configspec_comments = {} self._configspec_inline_comments = {} self._cs_section_comments = {} self._cs_section_inline_comments = {} # for defaults self.defaults = [] # # we do this explicitly so that __setitem__ is used properly # (rather than just passing to ``dict.__init__``) for entry in indict: self[entry] = indict[entry] def _interpolate(self, key, value): try: # do we already have an interpolation engine? engine = self._interpolation_engine except AttributeError: # not yet: first time running _interpolate(), so pick the engine name = self.main.interpolation if name == True: # note that "if name:" would be incorrect here # backwards-compatibility: interpolation=True means use default name = DEFAULT_INTERPOLATION name = name.lower() # so that "Template", "template", etc. all work class_ = interpolation_engines.get(name, None) if class_ is None: # invalid value for self.main.interpolation self.main.interpolation = False return value else: # save reference to engine so we don't have to do this again engine = self._interpolation_engine = class_(self) # let the engine do the actual work return engine.interpolate(key, value) def __getitem__(self, key): """Fetch the item and do string interpolation.""" val = dict.__getitem__(self, key) if self.main.interpolation and isinstance(val, StringTypes): return self._interpolate(key, val) return val def __setitem__(self, key, value, unrepr=False): """ Correctly set a value. Making dictionary values Section instances. (We have to special case 'Section' instances - which are also dicts) Keys must be strings. Values need only be strings (or lists of strings) if ``main.stringify`` is set. `unrepr`` must be set when setting a value to a dictionary, without creating a new sub-section. """ if not isinstance(key, StringTypes): raise ValueError, 'The key "%s" is not a string.' % key # add the comment if not self.comments.has_key(key): self.comments[key] = [] self.inline_comments[key] = '' # remove the entry from defaults if key in self.defaults: self.defaults.remove(key) # if isinstance(value, Section): if not self.has_key(key): self.sections.append(key) dict.__setitem__(self, key, value) elif isinstance(value, dict) and not unrepr: # First create the new depth level, # then create the section if not self.has_key(key): self.sections.append(key) new_depth = self.depth + 1 dict.__setitem__( self, key, Section( self, new_depth, self.main, indict=value, name=key)) else: if not self.has_key(key): self.scalars.append(key) if not self.main.stringify: if isinstance(value, StringTypes): pass elif isinstance(value, (list, tuple)): for entry in value: if not isinstance(entry, StringTypes): raise TypeError, ( 'Value is not a string "%s".' % entry) else: raise TypeError, 'Value is not a string "%s".' % value dict.__setitem__(self, key, value) def __delitem__(self, key): """Remove items from the sequence when deleting.""" dict. __delitem__(self, key) if key in self.scalars: self.scalars.remove(key) else: self.sections.remove(key) del self.comments[key] del self.inline_comments[key] def get(self, key, default=None): """A version of ``get`` that doesn't bypass string interpolation.""" try: return self[key] except KeyError: return default def update(self, indict): """ A version of update that uses our ``__setitem__``. """ for entry in indict: self[entry] = indict[entry] def pop(self, key, *args): """ """ val = dict.pop(self, key, *args) if key in self.scalars: del self.comments[key] del self.inline_comments[key] self.scalars.remove(key) elif key in self.sections: del self.comments[key] del self.inline_comments[key] self.sections.remove(key) if self.main.interpolation and isinstance(val, StringTypes): return self._interpolate(key, val) return val def popitem(self): """Pops the first (key,val)""" sequence = (self.scalars + self.sections) if not sequence: raise KeyError, ": 'popitem(): dictionary is empty'" key = sequence[0] val = self[key] del self[key] return key, val def clear(self): """ A version of clear that also affects scalars/sections Also clears comments and configspec. Leaves other attributes alone : depth/main/parent are not affected """ dict.clear(self) self.scalars = [] self.sections = [] self.comments = {} self.inline_comments = {} self.configspec = {} def setdefault(self, key, default=None): """A version of setdefault that sets sequence if appropriate.""" try: return self[key] except KeyError: self[key] = default return self[key] def items(self): """ """ return zip((self.scalars + self.sections), self.values()) def keys(self): """ """ return (self.scalars + self.sections) def values(self): """ """ return [self[key] for key in (self.scalars + self.sections)] def iteritems(self): """ """ return iter(self.items()) def iterkeys(self): """ """ return iter((self.scalars + self.sections)) __iter__ = iterkeys def itervalues(self): """ """ return iter(self.values()) def __repr__(self): return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) for key in (self.scalars + self.sections)]) __str__ = __repr__ # Extra methods - not in a normal dictionary def dict(self): """ Return a deepcopy of self as a dictionary. All members that are ``Section`` instances are recursively turned to ordinary dictionaries - by calling their ``dict`` method. >>> n = a.dict() >>> n == a 1 >>> n is a 0 """ newdict = {} for entry in self: this_entry = self[entry] if isinstance(this_entry, Section): this_entry = this_entry.dict() elif isinstance(this_entry, list): # create a copy rather than a reference this_entry = list(this_entry) elif isinstance(this_entry, tuple): # create a copy rather than a reference this_entry = tuple(this_entry) newdict[entry] = this_entry return newdict def merge(self, indict): """ A recursive update - useful for merging config files. >>> a = '''[section1] ... option1 = True ... [[subsection]] ... more_options = False ... # end of file'''.splitlines() >>> b = '''# File is user.ini ... [section1] ... option1 = False ... # end of file'''.splitlines() >>> c1 = ConfigObj(b) >>> c2 = ConfigObj(a) >>> c2.merge(c1) >>> c2 {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}} """ for key, val in indict.items(): if (key in self and isinstance(self[key], dict) and isinstance(val, dict)): self[key].merge(val) else: self[key] = val def rename(self, oldkey, newkey): """ Change a keyname to another, without changing position in sequence. Implemented so that transformations can be made on keys, as well as on values. (used by encode and decode) Also renames comments. """ if oldkey in self.scalars: the_list = self.scalars elif oldkey in self.sections: the_list = self.sections else: raise KeyError, 'Key "%s" not found.' % oldkey pos = the_list.index(oldkey) # val = self[oldkey] dict.__delitem__(self, oldkey) dict.__setitem__(self, newkey, val) the_list.remove(oldkey) the_list.insert(pos, newkey) comm = self.comments[oldkey] inline_comment = self.inline_comments[oldkey] del self.comments[oldkey] del self.inline_comments[oldkey] self.comments[newkey] = comm self.inline_comments[newkey] = inline_comment def walk(self, function, raise_errors=True, call_on_sections=False, **keywargs): """ Walk every member and call a function on the keyword and value. Return a dictionary of the return values If the function raises an exception, raise the errror unless ``raise_errors=False``, in which case set the return value to ``False``. Any unrecognised keyword arguments you pass to walk, will be pased on to the function you pass in. Note: if ``call_on_sections`` is ``True`` then - on encountering a subsection, *first* the function is called for the *whole* subsection, and then recurses into it's members. This means your function must be able to handle strings, dictionaries and lists. This allows you to change the key of subsections as well as for ordinary members. The return value when called on the whole subsection has to be discarded. See the encode and decode methods for examples, including functions. .. caution:: You can use ``walk`` to transform the names of members of a section but you mustn't add or delete members. >>> config = '''[XXXXsection] ... XXXXkey = XXXXvalue'''.splitlines() >>> cfg = ConfigObj(config) >>> cfg {'XXXXsection': {'XXXXkey': 'XXXXvalue'}} >>> def transform(section, key): ... val = section[key] ... newkey = key.replace('XXXX', 'CLIENT1') ... section.rename(key, newkey) ... if isinstance(val, (tuple, list, dict)): ... pass ... else: ... val = val.replace('XXXX', 'CLIENT1') ... section[newkey] = val >>> cfg.walk(transform, call_on_sections=True) {'CLIENT1section': {'CLIENT1key': None}} >>> cfg {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}} """ out = {} # scalars first for i in range(len(self.scalars)): entry = self.scalars[i] try: val = function(self, entry, **keywargs) # bound again in case name has changed entry = self.scalars[i] out[entry] = val except Exception: if raise_errors: raise else: entry = self.scalars[i] out[entry] = False # then sections for i in range(len(self.sections)): entry = self.sections[i] if call_on_sections: try: function(self, entry, **keywargs) except Exception: if raise_errors: raise else: entry = self.sections[i] out[entry] = False # bound again in case name has changed entry = self.sections[i] # previous result is discarded out[entry] = self[entry].walk( function, raise_errors=raise_errors, call_on_sections=call_on_sections, **keywargs) return out def decode(self, encoding): """ Decode all strings and values to unicode, using the specified encoding. Works with subsections and list values. Uses the ``walk`` method. Testing ``encode`` and ``decode``. >>> m = ConfigObj(a) >>> m.decode('ascii') >>> def testuni(val): ... for entry in val: ... if not isinstance(entry, unicode): ... print >> sys.stderr, type(entry) ... raise AssertionError, 'decode failed.' ... if isinstance(val[entry], dict): ... testuni(val[entry]) ... elif not isinstance(val[entry], unicode): ... raise AssertionError, 'decode failed.' >>> testuni(m) >>> m.encode('ascii') >>> a == m 1 """ warn('use of ``decode`` is deprecated.', DeprecationWarning) def decode(section, key, encoding=encoding, warn=True): """ """ val = section[key] if isinstance(val, (list, tuple)): newval = [] for entry in val: newval.append(entry.decode(encoding)) elif isinstance(val, dict): newval = val else: newval = val.decode(encoding) newkey = key.decode(encoding) section.rename(key, newkey) section[newkey] = newval # using ``call_on_sections`` allows us to modify section names self.walk(decode, call_on_sections=True) def encode(self, encoding): """ Encode all strings and values from unicode, using the specified encoding. Works with subsections and list values. Uses the ``walk`` method. """ warn('use of ``encode`` is deprecated.', DeprecationWarning) def encode(section, key, encoding=encoding): """ """ val = section[key] if isinstance(val, (list, tuple)): newval = [] for entry in val: newval.append(entry.encode(encoding)) elif isinstance(val, dict): newval = val else: newval = val.encode(encoding) newkey = key.encode(encoding) section.rename(key, newkey) section[newkey] = newval self.walk(encode, call_on_sections=True) def istrue(self, key): """A deprecated version of ``as_bool``.""" warn('use of ``istrue`` is deprecated. Use ``as_bool`` method ' 'instead.', DeprecationWarning) return self.as_bool(key) def as_bool(self, key): """ Accepts a key as input. The corresponding value must be a string or the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to retain compatibility with Python 2.2. If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns ``True``. If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns ``False``. ``as_bool`` is not case sensitive. Any other input will raise a ``ValueError``. >>> a = ConfigObj() >>> a['a'] = 'fish' >>> a.as_bool('a') Traceback (most recent call last): ValueError: Value "fish" is neither True nor False >>> a['b'] = 'True' >>> a.as_bool('b') 1 >>> a['b'] = 'off' >>> a.as_bool('b') 0 """ val = self[key] if val == True: return True elif val == False: return False else: try: if not isinstance(val, StringTypes): raise KeyError else: return self.main._bools[val.lower()] except KeyError: raise ValueError('Value "%s" is neither True nor False' % val) def as_int(self, key): """ A convenience method which coerces the specified value to an integer. If the value is an invalid literal for ``int``, a ``ValueError`` will be raised. >>> a = ConfigObj() >>> a['a'] = 'fish' >>> a.as_int('a') Traceback (most recent call last): ValueError: invalid literal for int(): fish >>> a['b'] = '1' >>> a.as_int('b') 1 >>> a['b'] = '3.2' >>> a.as_int('b') Traceback (most recent call last): ValueError: invalid literal for int(): 3.2 """ return int(self[key]) def as_float(self, key): """ A convenience method which coerces the specified value to a float. If the value is an invalid literal for ``float``, a ``ValueError`` will be raised. >>> a = ConfigObj() >>> a['a'] = 'fish' >>> a.as_float('a') Traceback (most recent call last): ValueError: invalid literal for float(): fish >>> a['b'] = '1' >>> a.as_float('b') 1.0 >>> a['b'] = '3.2' >>> a.as_float('b') 3.2000000000000002 """ return float(self[key]) class ConfigObj(Section): """An object to read, create, and write config files.""" _keyword = re.compile(r'''^ # line start (\s*) # indentation ( # keyword (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'"=].*?) # no quotes ) \s*=\s* # divider (.*) # value (including list values and comments) $ # line end ''', re.VERBOSE) _sectionmarker = re.compile(r'''^ (\s*) # 1: indentation ((?:\[\s*)+) # 2: section marker open ( # 3: section name open (?:"\s*\S.*?\s*")| # at least one non-space with double quotes (?:'\s*\S.*?\s*')| # at least one non-space with single quotes (?:[^'"\s].*?) # at least one non-space unquoted ) # section name close ((?:\s*\])+) # 4: section marker close \s*(\#.*)? # 5: optional comment $''', re.VERBOSE) # this regexp pulls list values out as a single string # or single values and comments # FIXME: this regex adds a '' to the end of comma terminated lists # workaround in ``_handle_value`` _valueexp = re.compile(r'''^ (?: (?: ( (?: (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\#][^,\#]*?) # unquoted ) \s*,\s* # comma )* # match all list items ending in a comma (if any) ) ( (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\#\s][^,]*?)| # unquoted (?:(? 1: msg = ("Parsing failed with several errors.\nFirst error %s" % info) error = ConfigObjError(msg) else: error = self._errors[0] # set the errors attribute; it's a list of tuples: # (error_type, message, line_number) error.errors = self._errors # set the config attribute error.config = self raise error # delete private attributes del self._errors # if defaults['configspec'] is None: self.configspec = None else: self._handle_configspec(defaults['configspec']) def __repr__(self): return 'ConfigObj({%s})' % ', '.join( [('%s: %s' % (repr(key), repr(self[key]))) for key in (self.scalars + self.sections)]) def _handle_bom(self, infile): """ Handle any BOM, and decode if necessary. If an encoding is specified, that *must* be used - but the BOM should still be removed (and the BOM attribute set). (If the encoding is wrongly specified, then a BOM for an alternative encoding won't be discovered or removed.) If an encoding is not specified, UTF8 or UTF16 BOM will be detected and removed. The BOM attribute will be set. UTF16 will be decoded to unicode. NOTE: This method must not be called with an empty ``infile``. Specifying the *wrong* encoding is likely to cause a ``UnicodeDecodeError``. ``infile`` must always be returned as a list of lines, but may be passed in as a single string. """ if ((self.encoding is not None) and (self.encoding.lower() not in BOM_LIST)): # No need to check for a BOM # the encoding specified doesn't have one # just decode return self._decode(infile, self.encoding) # if isinstance(infile, (list, tuple)): line = infile[0] else: line = infile if self.encoding is not None: # encoding explicitly supplied # And it could have an associated BOM # TODO: if encoding is just UTF16 - we ought to check for both # TODO: big endian and little endian versions. enc = BOM_LIST[self.encoding.lower()] if enc == 'utf_16': # For UTF16 we try big endian and little endian for BOM, (encoding, final_encoding) in BOMS.items(): if not final_encoding: # skip UTF8 continue if infile.startswith(BOM): ### BOM discovered ##self.BOM = True # Don't need to remove BOM return self._decode(infile, encoding) # # If we get this far, will *probably* raise a DecodeError # As it doesn't appear to start with a BOM return self._decode(infile, self.encoding) # # Must be UTF8 BOM = BOM_SET[enc] if not line.startswith(BOM): return self._decode(infile, self.encoding) # newline = line[len(BOM):] # # BOM removed if isinstance(infile, (list, tuple)): infile[0] = newline else: infile = newline self.BOM = True return self._decode(infile, self.encoding) # # No encoding specified - so we need to check for UTF8/UTF16 for BOM, (encoding, final_encoding) in BOMS.items(): if not line.startswith(BOM): continue else: # BOM discovered self.encoding = final_encoding if not final_encoding: self.BOM = True # UTF8 # remove BOM newline = line[len(BOM):] if isinstance(infile, (list, tuple)): infile[0] = newline else: infile = newline # UTF8 - don't decode if isinstance(infile, StringTypes): return infile.splitlines(True) else: return infile # UTF16 - have to decode return self._decode(infile, encoding) # # No BOM discovered and no encoding specified, just return if isinstance(infile, StringTypes): # infile read from a file will be a single string return infile.splitlines(True) else: return infile def _a_to_u(self, string): """Decode ascii strings to unicode if a self.encoding is specified.""" if not self.encoding: return string else: return string.decode('ascii') def _decode(self, infile, encoding): """ Decode infile to unicode. Using the specified encoding. if is a string, it also needs converting to a list. """ if isinstance(infile, StringTypes): # can't be unicode # NOTE: Could raise a ``UnicodeDecodeError`` return infile.decode(encoding).splitlines(True) for i, line in enumerate(infile): if not isinstance(line, unicode): # NOTE: The isinstance test here handles mixed lists of unicode/string # NOTE: But the decode will break on any non-string values # NOTE: Or could raise a ``UnicodeDecodeError`` infile[i] = line.decode(encoding) return infile def _decode_element(self, line): """Decode element to unicode if necessary.""" if not self.encoding: return line if isinstance(line, str) and self.default_encoding: return line.decode(self.default_encoding) return line def _str(self, value): """ Used by ``stringify`` within validate, to turn non-string values into strings. """ if not isinstance(value, StringTypes): return str(value) else: return value def _parse(self, infile): """Actually parse the config file.""" temp_list_values = self.list_values if self.unrepr: self.list_values = False comment_list = [] done_start = False this_section = self maxline = len(infile) - 1 cur_index = -1 reset_comment = False while cur_index < maxline: if reset_comment: comment_list = [] cur_index += 1 line = infile[cur_index] sline = line.strip() # do we have anything on the line ? if not sline or sline.startswith('#'): reset_comment = False comment_list.append(line) continue if not done_start: # preserve initial comment self.initial_comment = comment_list comment_list = [] done_start = True reset_comment = True # first we check if it's a section marker mat = self._sectionmarker.match(line) if mat is not None: # is a section line (indent, sect_open, sect_name, sect_close, comment) = ( mat.groups()) if indent and (self.indent_type is None): self.indent_type = indent[0] cur_depth = sect_open.count('[') if cur_depth != sect_close.count(']'): self._handle_error( "Cannot compute the section depth at line %s.", NestingError, infile, cur_index) continue # if cur_depth < this_section.depth: # the new section is dropping back to a previous level try: parent = self._match_depth( this_section, cur_depth).parent except SyntaxError: self._handle_error( "Cannot compute nesting level at line %s.", NestingError, infile, cur_index) continue elif cur_depth == this_section.depth: # the new section is a sibling of the current section parent = this_section.parent elif cur_depth == this_section.depth + 1: # the new section is a child the current section parent = this_section else: self._handle_error( "Section too nested at line %s.", NestingError, infile, cur_index) # sect_name = self._unquote(sect_name) if parent.has_key(sect_name): self._handle_error( 'Duplicate section name at line %s.', DuplicateError, infile, cur_index) continue # create the new section this_section = Section( parent, cur_depth, self, name=sect_name) parent[sect_name] = this_section parent.inline_comments[sect_name] = comment parent.comments[sect_name] = comment_list continue # # it's not a section marker, # so it should be a valid ``key = value`` line mat = self._keyword.match(line) if mat is None: # it neither matched as a keyword # or a section marker self._handle_error( 'Invalid line at line "%s".', ParseError, infile, cur_index) else: # is a keyword value # value will include any inline comment (indent, key, value) = mat.groups() if indent and (self.indent_type is None): self.indent_type = indent[0] # check for a multiline value if value[:3] in ['"""', "'''"]: try: (value, comment, cur_index) = self._multiline( value, infile, cur_index, maxline) except SyntaxError: self._handle_error( 'Parse error in value at line %s.', ParseError, infile, cur_index) continue else: if self.unrepr: comment = '' try: value = unrepr(value) except Exception, e: if type(e) == UnknownType: msg = 'Unknown name or type in value at line %s.' else: msg = 'Parse error in value at line %s.' self._handle_error(msg, UnreprError, infile, cur_index) continue else: if self.unrepr: comment = '' try: value = unrepr(value) except Exception, e: if isinstance(e, UnknownType): msg = 'Unknown name or type in value at line %s.' else: msg = 'Parse error in value at line %s.' self._handle_error(msg, UnreprError, infile, cur_index) continue else: # extract comment and lists try: (value, comment) = self._handle_value(value) except SyntaxError: self._handle_error( 'Parse error in value at line %s.', ParseError, infile, cur_index) continue # key = self._unquote(key) if this_section.has_key(key): self._handle_error( 'Duplicate keyword name at line %s.', DuplicateError, infile, cur_index) continue # add the key. # we set unrepr because if we have got this far we will never # be creating a new section this_section.__setitem__(key, value, unrepr=True) this_section.inline_comments[key] = comment this_section.comments[key] = comment_list continue # if self.indent_type is None: # no indentation used, set the type accordingly self.indent_type = '' # if self._terminated: comment_list.append('') # preserve the final comment if not self and not self.initial_comment: self.initial_comment = comment_list elif not reset_comment: self.final_comment = comment_list self.list_values = temp_list_values def _match_depth(self, sect, depth): """ Given a section and a depth level, walk back through the sections parents to see if the depth level matches a previous section. Return a reference to the right section, or raise a SyntaxError. """ while depth < sect.depth: if sect is sect.parent: # we've reached the top level already raise SyntaxError sect = sect.parent if sect.depth == depth: return sect # shouldn't get here raise SyntaxError def _handle_error(self, text, ErrorClass, infile, cur_index): """ Handle an error according to the error settings. Either raise the error or store it. The error will have occured at ``cur_index`` """ line = infile[cur_index] cur_index += 1 message = text % cur_index error = ErrorClass(message, cur_index, line) if self.raise_errors: # raise the error - parsing stops here raise error # store the error # reraise when parsing has finished self._errors.append(error) def _unquote(self, value): """Return an unquoted version of a value""" if (value[0] == value[-1]) and (value[0] in ('"', "'")): value = value[1:-1] return value def _quote(self, value, multiline=True): """ Return a safely quoted version of a value. Raise a ConfigObjError if the value cannot be safely quoted. If multiline is ``True`` (default) then use triple quotes if necessary. Don't quote values that don't need it. Recursively quote members of a list and return a comma joined list. Multiline is ``False`` for lists. Obey list syntax for empty and single member lists. If ``list_values=False`` then the value is only quoted if it contains a ``\n`` (is multiline). If ``write_empty_values`` is set, and the value is an empty string, it won't be quoted. """ if multiline and self.write_empty_values and value == '': # Only if multiline is set, so that it is used for values not # keys, and not values that are part of a list return '' if multiline and isinstance(value, (list, tuple)): if not value: return ',' elif len(value) == 1: return self._quote(value[0], multiline=False) + ',' return ', '.join([self._quote(val, multiline=False) for val in value]) if not isinstance(value, StringTypes): if self.stringify: value = str(value) else: raise TypeError, 'Value "%s" is not a string.' % value squot = "'%s'" dquot = '"%s"' noquot = "%s" wspace_plus = ' \r\t\n\v\t\'"' tsquot = '"""%s"""' tdquot = "'''%s'''" if not value: return '""' if (not self.list_values and '\n' not in value) or not (multiline and ((("'" in value) and ('"' in value)) or ('\n' in value))): if not self.list_values: # we don't quote if ``list_values=False`` quot = noquot # for normal values either single or double quotes will do elif '\n' in value: # will only happen if multiline is off - e.g. '\n' in key raise ConfigObjError, ('Value "%s" cannot be safely quoted.' % value) elif ((value[0] not in wspace_plus) and (value[-1] not in wspace_plus) and (',' not in value)): quot = noquot else: if ("'" in value) and ('"' in value): raise ConfigObjError, ( 'Value "%s" cannot be safely quoted.' % value) elif '"' in value: quot = squot else: quot = dquot else: # if value has '\n' or "'" *and* '"', it will need triple quotes if (value.find('"""') != -1) and (value.find("'''") != -1): raise ConfigObjError, ( 'Value "%s" cannot be safely quoted.' % value) if value.find('"""') == -1: quot = tdquot else: quot = tsquot return quot % value def _handle_value(self, value): """ Given a value string, unquote, remove comment, handle lists. (including empty and single member lists) """ # do we look for lists in values ? if not self.list_values: mat = self._nolistvalue.match(value) if mat is None: raise SyntaxError # NOTE: we don't unquote here return mat.groups() # mat = self._valueexp.match(value) if mat is None: # the value is badly constructed, probably badly quoted, # or an invalid list raise SyntaxError (list_values, single, empty_list, comment) = mat.groups() if (list_values == '') and (single is None): # change this if you want to accept empty values raise SyntaxError # NOTE: note there is no error handling from here if the regex # is wrong: then incorrect values will slip through if empty_list is not None: # the single comma - meaning an empty list return ([], comment) if single is not None: # handle empty values if list_values and not single: # FIXME: the '' is a workaround because our regex now matches # '' at the end of a list if it has a trailing comma single = None else: single = single or '""' single = self._unquote(single) if list_values == '': # not a list value return (single, comment) the_list = self._listvalueexp.findall(list_values) the_list = [self._unquote(val) for val in the_list] if single is not None: the_list += [single] return (the_list, comment) def _multiline(self, value, infile, cur_index, maxline): """Extract the value, where we are in a multiline situation.""" quot = value[:3] newvalue = value[3:] single_line = self._triple_quote[quot][0] multi_line = self._triple_quote[quot][1] mat = single_line.match(value) if mat is not None: retval = list(mat.groups()) retval.append(cur_index) return retval elif newvalue.find(quot) != -1: # somehow the triple quote is missing raise SyntaxError # while cur_index < maxline: cur_index += 1 newvalue += '\n' line = infile[cur_index] if line.find(quot) == -1: newvalue += line else: # end of multiline, process it break else: # we've got to the end of the config, oops... raise SyntaxError mat = multi_line.match(line) if mat is None: # a badly formed line raise SyntaxError (value, comment) = mat.groups() return (newvalue + value, comment, cur_index) def _handle_configspec(self, configspec): """Parse the configspec.""" # FIXME: Should we check that the configspec was created with the # correct settings ? (i.e. ``list_values=False``) if not isinstance(configspec, ConfigObj): try: configspec = ConfigObj( configspec, raise_errors=True, file_error=True, list_values=False) except ConfigObjError, e: # FIXME: Should these errors have a reference # to the already parsed ConfigObj ? raise ConfigspecError('Parsing configspec failed: %s' % e) except IOError, e: raise IOError('Reading configspec failed: %s' % e) self._set_configspec_value(configspec, self) def _set_configspec_value(self, configspec, section): """Used to recursively set configspec values.""" if '__many__' in configspec.sections: section.configspec['__many__'] = configspec['__many__'] if len(configspec.sections) > 1: # FIXME: can we supply any useful information here ? raise RepeatSectionError if hasattr(configspec, 'initial_comment'): section._configspec_initial_comment = configspec.initial_comment section._configspec_final_comment = configspec.final_comment section._configspec_encoding = configspec.encoding section._configspec_BOM = configspec.BOM section._configspec_newlines = configspec.newlines section._configspec_indent_type = configspec.indent_type for entry in configspec.scalars: section._configspec_comments[entry] = configspec.comments[entry] section._configspec_inline_comments[entry] = ( configspec.inline_comments[entry]) section.configspec[entry] = configspec[entry] section._order.append(entry) for entry in configspec.sections: if entry == '__many__': continue section._cs_section_comments[entry] = configspec.comments[entry] section._cs_section_inline_comments[entry] = ( configspec.inline_comments[entry]) if not section.has_key(entry): section[entry] = {} self._set_configspec_value(configspec[entry], section[entry]) def _handle_repeat(self, section, configspec): """Dynamically assign configspec for repeated section.""" try: section_keys = configspec.sections scalar_keys = configspec.scalars except AttributeError: section_keys = [entry for entry in configspec if isinstance(configspec[entry], dict)] scalar_keys = [entry for entry in configspec if not isinstance(configspec[entry], dict)] if '__many__' in section_keys and len(section_keys) > 1: # FIXME: can we supply any useful information here ? raise RepeatSectionError scalars = {} sections = {} for entry in scalar_keys: val = configspec[entry] scalars[entry] = val for entry in section_keys: val = configspec[entry] if entry == '__many__': scalars[entry] = val continue sections[entry] = val # section.configspec = scalars for entry in sections: if not section.has_key(entry): section[entry] = {} self._handle_repeat(section[entry], sections[entry]) def _write_line(self, indent_string, entry, this_entry, comment): """Write an individual line, for the write method""" # NOTE: the calls to self._quote here handles non-StringType values. if not self.unrepr: val = self._decode_element(self._quote(this_entry)) else: val = repr(this_entry) return '%s%s%s%s%s' % ( indent_string, self._decode_element(self._quote(entry, multiline=False)), self._a_to_u(' = '), val, self._decode_element(comment)) def _write_marker(self, indent_string, depth, entry, comment): """Write a section marker line""" return '%s%s%s%s%s' % ( indent_string, self._a_to_u('[' * depth), self._quote(self._decode_element(entry), multiline=False), self._a_to_u(']' * depth), self._decode_element(comment)) def _handle_comment(self, comment): """Deal with a comment.""" if not comment: return '' if self.indent_type == '\t': start = self._a_to_u('\t') else: start = self._a_to_u(' ' * NUM_INDENT_SPACES) if not comment.startswith('#'): start += _a_to_u('# ') return (start + comment) def _compute_indent_string(self, depth): """ Compute the indent string, according to current indent_type and depth """ if self.indent_type == '': # no indentation at all return '' if self.indent_type == '\t': return '\t' * depth if self.indent_type == ' ': return ' ' * NUM_INDENT_SPACES * depth raise SyntaxError # Public methods def write(self, outfile=None, section=None): """ Write the current ConfigObj as a file tekNico: FIXME: use StringIO instead of real files >>> filename = a.filename >>> a.filename = 'test.ini' >>> a.write() >>> a.filename = filename >>> a == ConfigObj('test.ini', raise_errors=True) 1 """ if self.indent_type is None: # this can be true if initialised from a dictionary self.indent_type = DEFAULT_INDENT_TYPE # out = [] cs = self._a_to_u('#') csp = self._a_to_u('# ') if section is None: int_val = self.interpolation self.interpolation = False section = self for line in self.initial_comment: line = self._decode_element(line) stripped_line = line.strip() if stripped_line and not stripped_line.startswith(cs): line = csp + line out.append(line) # indent_string = self._a_to_u( self._compute_indent_string(section.depth)) for entry in (section.scalars + section.sections): if entry in section.defaults: # don't write out default values continue for comment_line in section.comments[entry]: comment_line = self._decode_element(comment_line.lstrip()) if comment_line and not comment_line.startswith(cs): comment_line = csp + comment_line out.append(indent_string + comment_line) this_entry = section[entry] comment = self._handle_comment(section.inline_comments[entry]) # if isinstance(this_entry, dict): # a section out.append(self._write_marker( indent_string, this_entry.depth, entry, comment)) out.extend(self.write(section=this_entry)) else: out.append(self._write_line( indent_string, entry, this_entry, comment)) # if section is self: for line in self.final_comment: line = self._decode_element(line) stripped_line = line.strip() if stripped_line and not stripped_line.startswith(cs): line = csp + line out.append(line) self.interpolation = int_val # if section is not self: return out # if (self.filename is None) and (outfile is None): # output a list of lines # might need to encode # NOTE: This will *screw* UTF16, each line will start with the BOM if self.encoding: out = [l.encode(self.encoding) for l in out] if (self.BOM and ((self.encoding is None) or (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): # Add the UTF8 BOM if not out: out.append('') out[0] = BOM_UTF8 + out[0] return out # # Turn the list to a string, joined with correct newlines output = (self._a_to_u(self.newlines or os.linesep) ).join(out) if self.encoding: output = output.encode(self.encoding) if (self.BOM and ((self.encoding is None) or (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): # Add the UTF8 BOM output = BOM_UTF8 + output if outfile is not None: outfile.write(output) else: h = open(self.filename, 'wb') h.write(output) h.close() def validate(self, validator, preserve_errors=False, copy=False, section=None): """ Test the ConfigObj against a configspec. It uses the ``validator`` object from *validate.py*. To run ``validate`` on the current ConfigObj, call: :: test = config.validate(validator) (Normally having previously passed in the configspec when the ConfigObj was created - you can dynamically assign a dictionary of checks to the ``configspec`` attribute of a section though). It returns ``True`` if everything passes, or a dictionary of pass/fails (True/False). If every member of a subsection passes, it will just have the value ``True``. (It also returns ``False`` if all members fail). In addition, it converts the values from strings to their native types if their checks pass (and ``stringify`` is set). If ``preserve_errors`` is ``True`` (``False`` is default) then instead of a marking a fail with a ``False``, it will preserve the actual exception object. This can contain info about the reason for failure. For example the ``VdtValueTooSmallError`` indeicates that the value supplied was too small. If a value (or section) is missing it will still be marked as ``False``. You must have the validate module to use ``preserve_errors=True``. You can then use the ``flatten_errors`` function to turn your nested results dictionary into a flattened list of failures - useful for displaying meaningful error messages. """ if section is None: if self.configspec is None: raise ValueError, 'No configspec supplied.' if preserve_errors: if VdtMissingValue is None: raise ImportError('Missing validate module.') section = self # spec_section = section.configspec if copy and hasattr(section, '_configspec_initial_comment'): section.initial_comment = section._configspec_initial_comment section.final_comment = section._configspec_final_comment section.encoding = section._configspec_encoding section.BOM = section._configspec_BOM section.newlines = section._configspec_newlines section.indent_type = section._configspec_indent_type if '__many__' in section.configspec: many = spec_section['__many__'] # dynamically assign the configspecs # for the sections below for entry in section.sections: self._handle_repeat(section[entry], many) # out = {} ret_true = True ret_false = True order = [k for k in section._order if k in spec_section] order += [k for k in spec_section if k not in order] for entry in order: if entry == '__many__': continue if (not entry in section.scalars) or (entry in section.defaults): # missing entries # or entries from defaults missing = True val = None if copy and not entry in section.scalars: # copy comments section.comments[entry] = ( section._configspec_comments.get(entry, [])) section.inline_comments[entry] = ( section._configspec_inline_comments.get(entry, '')) # else: missing = False val = section[entry] try: check = validator.check(spec_section[entry], val, missing=missing ) except validator.baseErrorClass, e: if not preserve_errors or isinstance(e, VdtMissingValue): out[entry] = False else: # preserve the error out[entry] = e ret_false = False ret_true = False else: ret_false = False out[entry] = True if self.stringify or missing: # if we are doing type conversion # or the value is a supplied default if not self.stringify: if isinstance(check, (list, tuple)): # preserve lists check = [self._str(item) for item in check] elif missing and check is None: # convert the None from a default to a '' check = '' else: check = self._str(check) if (check != val) or missing: section[entry] = check if not copy and missing and entry not in section.defaults: section.defaults.append(entry) # # Missing sections will have been created as empty ones when the # configspec was read. for entry in section.sections: # FIXME: this means DEFAULT is not copied in copy mode if section is self and entry == 'DEFAULT': continue if copy: section.comments[entry] = section._cs_section_comments[entry] section.inline_comments[entry] = ( section._cs_section_inline_comments[entry]) check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry]) out[entry] = check if check == False: ret_true = False elif check == True: ret_false = False else: ret_true = False ret_false = False # if ret_true: return True elif ret_false: return False else: return out class SimpleVal(object): """ A simple validator. Can be used to check that all members expected are present. To use it, provide a configspec with all your members in (the value given will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` method of your ``ConfigObj``. ``validate`` will return ``True`` if all members are present, or a dictionary with True/False meaning present/missing. (Whole missing sections will be replaced with ``False``) """ def __init__(self): self.baseErrorClass = ConfigObjError def check(self, check, member, missing=False): """A dummy check method, always returns the value unchanged.""" if missing: raise self.baseErrorClass return member # Check / processing functions for options def flatten_errors(cfg, res, levels=None, results=None): """ An example function that will turn a nested dictionary of results (as returned by ``ConfigObj.validate``) into a flat list. ``cfg`` is the ConfigObj instance being checked, ``res`` is the results dictionary returned by ``validate``. (This is a recursive function, so you shouldn't use the ``levels`` or ``results`` arguments - they are used by the function. Returns a list of keys that failed. Each member of the list is a tuple : :: ([list of sections...], key, result) If ``validate`` was called with ``preserve_errors=False`` (the default) then ``result`` will always be ``False``. *list of sections* is a flattened list of sections that the key was found in. If the section was missing then key will be ``None``. If the value (or section) was missing then ``result`` will be ``False``. If ``validate`` was called with ``preserve_errors=True`` and a value was present, but failed the check, then ``result`` will be the exception object returned. You can use this as a string that describes the failure. For example *The value "3" is of the wrong type*. >>> import validate >>> vtor = validate.Validator() >>> my_ini = ''' ... option1 = True ... [section1] ... option1 = True ... [section2] ... another_option = Probably ... [section3] ... another_option = True ... [[section3b]] ... value = 3 ... value2 = a ... value3 = 11 ... ''' >>> my_cfg = ''' ... option1 = boolean() ... option2 = boolean() ... option3 = boolean(default=Bad_value) ... [section1] ... option1 = boolean() ... option2 = boolean() ... option3 = boolean(default=Bad_value) ... [section2] ... another_option = boolean() ... [section3] ... another_option = boolean() ... [[section3b]] ... value = integer ... value2 = integer ... value3 = integer(0, 10) ... [[[section3b-sub]]] ... value = string ... [section4] ... another_option = boolean() ... ''' >>> cs = my_cfg.split('\\n') >>> ini = my_ini.split('\\n') >>> cfg = ConfigObj(ini, configspec=cs) >>> res = cfg.validate(vtor, preserve_errors=True) >>> errors = [] >>> for entry in flatten_errors(cfg, res): ... section_list, key, error = entry ... section_list.insert(0, '[root]') ... if key is not None: ... section_list.append(key) ... else: ... section_list.append('[missing]') ... section_string = ', '.join(section_list) ... errors.append((section_string, ' = ', error)) >>> errors.sort() >>> for entry in errors: ... print entry[0], entry[1], (entry[2] or 0) [root], option2 = 0 [root], option3 = the value "Bad_value" is of the wrong type. [root], section1, option2 = 0 [root], section1, option3 = the value "Bad_value" is of the wrong type. [root], section2, another_option = the value "Probably" is of the wrong type. [root], section3, section3b, section3b-sub, [missing] = 0 [root], section3, section3b, value2 = the value "a" is of the wrong type. [root], section3, section3b, value3 = the value "11" is too big. [root], section4, [missing] = 0 """ if levels is None: # first time called levels = [] results = [] if res is True: return results if res is False: results.append((levels[:], None, False)) if levels: levels.pop() return results for (key, val) in res.items(): if val == True: continue if isinstance(cfg.get(key), dict): # Go down one level levels.append(key) flatten_errors(cfg[key], val, levels, results) continue results.append((levels[:], key, val)) # # Go up one level if levels: levels.pop() # return results """*A programming language is a medium of expression.* - Paul Graham""" rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/urlpath.py0000600000175000017500000001721010357205657024712 0ustar madduckmadduck# urlpath.py # 0.1.0 # 2005/08/20 # Functions that handle url paths. # Part of Pythonutils # http://www.voidspace.org.uk/python/pythonutils.html # Copyright Michael Foord, 2004 & 2005. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # Pythonutils mailing list. # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk import posixpath import os from urllib import url2pathname, pathname2url __all__ = [ 'nativejoin', 'pathjoin', 'relpathto', 'tslash', 'relpath' ] def pathjoin(base, *paths): """ Join paths to a base, observing pardir. If base doesn't *end* with '/' we assume it's a file rather than a directory. (so we get rid of it) """ # XXXX will posixpath.join do all this anyway? if base and not base.endswith('/'): # get rid of the filename base = '/'.join(base.split('/')[:-1]) base = tslash(base) path = (base,) + paths return posixpath.normpath(posixpath.join(*path)) def nativejoin(base, path): """ Joins two paths - returning a native file path. Given a base path and a relative location, (in posix format) return a file path in a (relatively) OS native way. """ return url2pathname(pathjoin(base, path)) def relpathto(thisdir, origin, dest): """ Given two paths relative to a directory, work out a path from origin to destination. Assumes UNIX/URL type relative paths. If origin doesn't *end* with '/' we assume it's a file rather than a directory. If the same paths are passed in : if the path ends with ('/') then we return '' else we return the last part of the path (presumably a filename) If thisdir doesn't start with '/' then we add one (this makes the top level of thisdir our root directory) """ orig_thisdir = thisdir if not thisdir.startswith('/'): thisdir = '/' + thisdir orig_abs = posixpath.normpath(posixpath.join(thisdir, origin)) dest_abs = posixpath.normpath(posixpath.join(thisdir, dest)) if origin.endswith('/') and not orig_abs.endswith('/'): orig_abs = orig_abs + '/' if dest.endswith('/') and not dest_abs.endswith('/'): dest_abs = dest_abs + '/' # print orig_abs, dest_abs # # if the first item is a filename, we want to get rid of it orig_list = orig_abs.split('/')[:-1] dest_list = dest_abs.split('/') # print orig_list, dest_list if orig_list[0] != dest_list[0]: # can't get here from there # XXXX raise exception? return dest # # find the location where the two paths start to differ. i = 0 for start_seg, dest_seg in zip(orig_list, dest_list): if start_seg != dest_seg: break i += 1 # # now i is the point where the two paths diverge; # need a certain number of "os.pardir"s to work up # from the origin to the point of divergence. segments = ['..'] * (len(orig_list) - i) # need to add the diverging part of dest_list. segments += dest_list[i:] if len(segments) == 0: # if they happen to be identical paths # identical directories if dest.endswith('/'): return '' # just the filename - the last part of dest return dest_list[-1] else: return '/'.join(segments) def relpath(origin, dest): """Given two absolute paths, work out a path from origin to destination. Assumes UNIX/URL type relative paths. If origin doesn't *end* with '/' we assume it's a file rather than a directory. If the same paths are passed in : if the path ends with ('/') then we return '' else we return the last part of the path (presumably a filename) If origin or dest don't start with '/' then we add it. We are *assuming* relative paths on the same device (i.e. same top level directory) """ if not origin.startswith('/'): origin = '/' + origin if not dest.startswith('/'): dest = '/' + dest # # if the first item is a filename, we want to get rid of it orig_list = origin.split('/')[:-1] dest_list = dest.split('/') # # find the location where the two paths start to differ. i = 0 for start_seg, dest_seg in zip(orig_list, dest_list): if start_seg != dest_seg: break i += 1 # now i is the point where the two paths diverge. # need a certain number of "os.pardir"s to work up # from the origin to the point of divergence. segments = ['..'] * (len(orig_list) - i) # need to add the diverging part of dest_list. segments += dest_list[i:] if len(segments) == 0: # if they happen to be identical paths # identical directories if dest.endswith('/'): return '' # just the filename - the last part of dest return dest_list[-1] else: return '/'.join(segments) def tslash(apath): """Add a trailing slash to a path if it needs one. Doesn't use os.sep because you end up jiggered on windoze - when you want separators for URLs. """ if (apath and apath != '.' and not apath.endswith('/') and not apath.endswith('\\')): return apath + '/' else: return apath ############################################## def testJoin(): thelist = [ ('/', 'fish.html'), ('/dir/dir/', '../file'), ('dir/dir/', '../file'), ('dir/dir/', '../../file'), ('dir/dir/', '../../../file'), ('/dir/dir/', '../notherdir/file'), ('/dir/dir/', '../../notherdir/file'), ('dir/dir/', '../../notherdir/file'), ('dir/dir/', '../../../notherdir/file'), ('', '../path'), ] for entry in thelist: print entry, ' :: ', pathjoin(*entry) print entry, ' :: ', nativejoin(*entry) print '\n' def testRelpathto(): thedir = '//toplevel/dirone/dirtwo/dirthree' thelist = [ ('file1.html', 'file2.html'), ('file1.html', '../file2.html'), ('../file1.html', '../file2.html'), ('../file1.html', 'file2.html'), ('../fish1/fish2/', '../../sub1/sub2/'), ('../fish1/fish2/', 'sub1/sub2'), ('../../../fish1/fish2/', 'sub1/sub2/'), ('../../../fish1/fish2/', 'sub1/sub2/file1.html'), ] for orig, dest in thelist: print '(%s, %s) : ' % (orig, dest), relpathto(thedir, orig, dest) def testRelpathto2(): thedir = 'section3/' thelist = [ ('../archive/strangeindex1.html', 'article2.html'), ] for orig, dest in thelist: answer = relpathto(thedir, orig, dest) print '(%s, %s) : ' % (orig, dest), answer def testRelpath(): thelist = [ ('/hello/fish/', 'bungles'), ] for orig, dest in thelist: answer = relpath(orig, dest) print '(%s, %s) : ' % (orig, dest), answer if __name__ == '__main__': testJoin() testRelpathto() testRelpath() # testRelpathto2() """ TODO ==== More comprehensive tests. CHANGELOG 2005/07/31 Can now pass mulitple args to ``pathjoin``. Finalised as version 0.1.0 2005/06/18 Changes by Nicola Larosa Code cleanup lines shortened comments on line above code empty comments in empty lines 2005/05/28 Added relpath to __all__ TODO Move into pythonutils relpathto could call relpath (and so be shorter) nativejoin could accept multiple paths Could tslash be more elegant ? """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/listquote.py0000600000175000017500000007234210357205657025273 0ustar madduckmadduck# 2005/08/28 # v1.4.0 # listquote.py # Lists 'n' Quotes # Handling lists and quoted strings # Can be used for parsing/creating lists - or lines in a CSV file # And also quoting or unquoting elements. # Homepage : http://www.voidspace.org.uk/python/modules.shtml # Copyright Michael Foord, 2004 & 2005. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the Pythonutils mailing list. # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk """ Having written modules to handle turning a string representation of a list back into a list (including nested lists) and also a very simple CSV parser, I realised I needed a more solid set of functions for handling lists (comma delimited lines) and quoting/unquoting elements of lists. The test stuff provides useful examples of how the functions work. """ # Pre-2.3 workaround for basestring. try: basestring except NameError: basestring = (str, unicode) import re inquotes = re.compile(r'''\s*(".*?"|'.*?')(.*)''') badchars = re.compile(r'''^[^'," \[\]\(\)#]+$''') ##commented_line = re.compile(r'''\s*([^#]*)\s*(#.*)''') paramfinder = re.compile(r'''(?:'.*?')|(?:".*?")|(?:[^'",\s][^,]*)''') unquoted = re.compile(r''' ([^\#,"'\(\)\[\]][^\#,\]\)]*) # value \s* # whitespace - XXX not caught ([\#,\)\]].*)? # rest of the line $''', re.VERBOSE) __all__ = [ 'elem_quote', 'unquote', 'ListQuoteError', 'QuoteError', 'UnQuoteError', 'BadLineError', 'CommentError', 'quote_escape', 'quote_unescape', 'simplelist', 'LineParser', 'lineparse', 'csvread', 'csvwrite', 'list_stringify', 'makelist' ] class ListQuoteError(SyntaxError): """Base class for errors raised by the listquote module.""" class QuoteError(ListQuoteError): """This value can't be quoted.""" class UnQuoteError(ListQuoteError): """The value is badly quoted.""" class BadLineError(ListQuoteError): """A line is badly built.""" class CommentError(BadLineError): """A line contains a disallowed comment.""" class CSVError(ListQuoteError): """The CSV File contained errors.""" ################################################################# # functions for quoting and unquoting def elem_quote(member, nonquote=True, stringify=False, encoding=None): """ Simple method to add the most appropriate quote to an element - either single quotes or double quotes. If member contains ``\n`` a ``QuoteError`` is raised - multiline values can't be quoted by elem_quote. If ``nonquote`` is set to ``True`` (the default), then if member contains none of ``'," []()#;`` then it isn't quoted at all. If member contains both single quotes *and* double quotes then all double quotes (``"``) will be escaped as ``&mjf-quot;`` and member will then be quoted with double quotes. If ``stringify`` is set to ``True`` (the default is ``False``) then non string (unicode or byte-string) values will be first converted to strings using the ``str`` function. Otherwise elem_quote raises a ``TypeError``. If ``encoding`` is not ``None`` and member is a byte string, then it will be decoded into unicode using this encoding. >>> elem_quote('hello') 'hello' >>> elem_quote('hello', nonquote=False) '"hello"' >>> elem_quote('"hello"') '\\'"hello"\\'' >>> elem_quote(3) Traceback (most recent call last): TypeError: Can only quote strings. "3" >>> elem_quote(3, stringify=True) '3' >>> elem_quote('hello', encoding='ascii') u'hello' >>> elem_quote('\\n') Traceback (most recent call last): QuoteError: Multiline values can't be quoted. " " """ if not isinstance(member, basestring): if stringify: member = str(member) else: # FIXME: is this the appropriate error message ? raise TypeError('Can only quote strings. "%s"' % str(member)) if encoding and isinstance(member, str): # from string to unicode member = unicode(member, encoding) if '\n' in member: raise QuoteError('Multiline values can\'t be quoted.\n"%s"' % str(member)) # if nonquote and badchars.match(member) is not None: return member # this ordering of tests determines which quote character will be used in # preference - here we have \" first... elif member.find('"') == -1: return '"%s"' % member # but we will use either... which may not suit some people elif member.find("'") == -1: return "'%s'" % member else: raise QuoteError('Value can\'t be quoted : "%s"' % member) def unquote(inline, fullquote=True, retain=False): """ Unquote a value. If the value isn't quoted it returns the value. If the value is badly quoted it raises ``UnQuoteError``. If retain is ``True`` (default is ``False``) then the quotes are left around the value (but leading or trailing whitespace will have been removed). If fullquote is ``False`` (default is ``True``) then unquote will only unquote the first part of the ``inline``. If there is anything after the quoted element, this will be returned as well (instead of raising an error). In this case the return value is ``(value, rest)``. >>> unquote('hello') 'hello' >>> unquote('"hello"') 'hello' >>> unquote('"hello') Traceback (most recent call last): UnQuoteError: Value is badly quoted: ""hello" >>> unquote('"hello" fish') Traceback (most recent call last): UnQuoteError: Value is badly quoted: ""hello" fish" >>> unquote("'hello'", retain=True) "'hello'" >>> unquote('"hello" fish', fullquote=False) ('hello', ' fish') """ mat = inquotes.match(inline) if mat is None: if inline.strip()[0] not in '\'\"': # not quoted return inline else: # badly quoted raise UnQuoteError('Value is badly quoted: "%s"' % inline) quoted, rest = mat.groups() if fullquote and rest.strip(): # badly quoted raise UnQuoteError('Value is badly quoted: "%s"' % inline) if not retain: quoted = quoted[1:-1] if not fullquote: return quoted, rest else: return quoted def quote_escape(value, lf='&mjf-lf;', quot='&mjf-quot;'): """ Escape a string so that it can safely be quoted. You should use this if the value to be quoted *may* contain line-feeds or both single quotes and double quotes. If the value contains ``\n`` then it will be escaped using ``lf``. By default this is ``&mjf-lf;``. If the value contains single quotes *and* double quotes, then all double quotes will be escaped using ``quot``. By default this is ``&mjf-quot;``. >>> quote_escape('hello') 'hello' >>> quote_escape('hello\\n') 'hello&mjf-lf;' >>> quote_escape('hello"') 'hello"' >>> quote_escape('hello"\\'') "hello&mjf-quot;'" >>> quote_escape('hello"\\'\\n', '&fish;', '&wobble;') "hello&wobble;'&fish;" """ if '\n' in value: value = value.replace('\n', lf) if '\'' in value and '\"' in value: value = value.replace('"', quot) return value def quote_unescape(value, lf='&mjf-lf;', quot='&mjf-quot;'): """ Unescape a string escaped by ``quote_escape``. If it was escaped using anything other than the defaults for ``lf`` and ``quot`` you must pass them to this function. >>> quote_unescape("hello&wobble;'&fish;", '&fish;', '&wobble;') 'hello"\\'\\n' >>> quote_unescape('hello') 'hello' >>> quote_unescape('hello&mjf-lf;') 'hello\\n' >>> quote_unescape("'hello'") "'hello'" >>> quote_unescape('hello"') 'hello"' >>> quote_unescape("hello&mjf-quot;'") 'hello"\\'' >>> quote_unescape("hello&wobble;'&fish;", '&fish;', '&wobble;') 'hello"\\'\\n' """ return value.replace(lf, '\n').replace(quot, '"') def simplelist(inline): """ Parse a string to a list. A simple regex that extracts quoted items from a list. It retains quotes around elements. (So unquote each element) >>> simplelist('''hello, goodbye, 'title', "name", "I can't"''') ['hello', 'goodbye', "'title'", '"name"', '"I can\\'t"'] FIXME: This doesn't work fully (allows some badly formed lists): e.g. >>> simplelist('hello, fish, "wobble" bottom hooray') ['hello', 'fish', '"wobble"', 'bottom hooray'] """ return paramfinder.findall(inline) ############################################## # LineParser - a multi purpose line parser # handles lines with comma seperated values on it, followed by a comment # correctly handles quoting # *and* can handle nested lists - marked between '[...]' or '(...)' # See the docstring for how this works # by default it returns a (list, comment) tuple ! # There are several keyword arguments that control how LineParser works. class LineParser(object): """An object to parse nested lists from strings.""" liststart = { '[' : ']', '(' : ')' } quotes = ['\'', '"'] def __init__(self, options=None, **keywargs): """Initialise the LineParser.""" self.reset(options, **keywargs) def reset(self, options=None, **keywargs): """Reset the parser with the specified options.""" if options is None: options = {} options.update(keywargs) # defaults = { 'recursive': True, 'comment': True, 'retain': False, 'force_list': False, 'csv': False } defaults.update(options) if defaults['csv']: defaults.update({ 'recursive': False, 'force_list': True, 'comment': False, }) # check all the options are valid for entry in defaults.keys(): if entry not in ['comment', 'retain', 'csv', 'recursive', 'force_list']: raise TypeError, ("'%s' is an invalid keyword argument for " "this function" % entry) # self.recursive = defaults['recursive'] self.comment = defaults['comment'] self.retain = defaults['retain'] self.force_list = defaults['force_list'] def feed(self, inline, endchar=None): """ Parse a single line (or fragment). Uses the options set in the parser object. Can parse lists - including nested lists. (If ``recursive`` is ``False`` then nested lists will cause a ``BadLineError``). Return value depends on options. If ``comment`` is ``False`` it returns ``outvalue`` If ``comment`` is ``True`` it returns ``(outvalue, comment)``. (Even if comment is just ``''``). If ``force_list`` is ``False`` then ``outvalue`` may be a list or a single item. If ``force_list`` is ``True`` then ``outvalue`` will always be a list - even if it has just one member. List syntax : * Comma separated lines ``a, b, c, d`` * Lists can optionally be between square or ordinary brackets - ``[a, b, c, d]`` - ``(a, b, c, d)`` * Nested lists *must* be between brackets - ``a, [a, b, c, d], c`` * A single element list can be shown by a trailing quote - ``a,`` * An empty list is shown by ``()`` or ``[]`` Elements can be quoted with single or double quotes (but can't contain both). The line can optionally end with a comment (preeded by a '#'). This depends on the ``comment`` attribute. If the line is badly built then this method will raise one of : :: CommentError, BadLineError, UnQuoteError Using the ``csv`` option is the same as setting : :: 'recursive': False 'force_list': True 'comment': False """ # preserve the original line # for error messages if endchar is None: self.origline = inline inline = inline.lstrip() # outlist = [] comma_needed = False found_comma = False while inline: # NOTE: this sort of operation would be quicker # with lists - but then can't use regexes thischar = inline[0] if thischar == '#': # reached a comment # end of the line... break # if thischar == endchar: return outlist, inline[1:] # if comma_needed: if thischar == ',': inline = inline[1:].lstrip() comma_needed = False found_comma = True continue raise BadLineError('Line is badly built :\n%s' % self.origline) # try: # the character that marks the end of the list listend = self.liststart[thischar] except KeyError: pass else: if not self.recursive and endchar is not None: raise BadLineError('Line is badly built :\n%s' % self.origline) newlist, inline = self.feed(inline[1:], endchar=listend) outlist.append(newlist) inline = inline.lstrip() comma_needed = True continue # if thischar in self.quotes: # this might raise an error # FIXME: trap the error and raise a more appropriate one ? element, inline = unquote(inline, fullquote=False, retain=self.retain) inline = inline.lstrip() outlist.append(element) comma_needed = True continue # # must be an unquoted element mat = unquoted.match(inline) if mat is not None: # FIXME: if the regex was better we wouldn't need an rstrip element = mat.group(1).rstrip() # group 2 will be ``None`` if we reach the end of the line inline = mat.group(2) or '' outlist.append(element) comma_needed = True continue # or it's a badly built line raise BadLineError('Line is badly built :\n%s' % self.origline) # # if we've been called recursively # we shouldn't have got this far if endchar is not None: raise BadLineError('Line is badly built :\n%s' % self.origline) # if not found_comma: # if we didn't find a comma # the value could be a nested list if outlist: outlist = outlist[0] else: outlist = '' if self.force_list and not isinstance(outlist, list): if outlist: outlist = [outlist] else: outlist = [] if not self.comment: if inline: raise CommentError('Comment not allowed :\n%s' % self.origline) return outlist return outlist, inline def lineparse(inline, options=None, **keywargs): """ A compatibility function that mimics the old lineparse. Also more convenient for single line use. Note: It still uses the new ``LineParser`` - and so takes the same keyword arguments as that. >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''') (['hello', 'goodbye', "I can't do that", 'You "can" !'], '# a comment') >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''', comment=False) Traceback (most recent call last): CommentError: Comment not allowed : "hello", 'goodbye', "I can't do that", 'You "can" !' # a comment >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''', recursive=False) (['hello', 'goodbye', "I can't do that", 'You "can" !'], '# a comment') >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''', csv=True) Traceback (most recent call last): CommentError: Comment not allowed : "hello", 'goodbye', "I can't do that", 'You "can" !' # a comment >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' ''', comment=False) ['hello', 'goodbye', "I can't do that", 'You "can" !'] >>> lineparse('') ('', '') >>> lineparse('', force_list=True) ([], '') >>> lineparse('[]') ([], '') >>> lineparse('()') ([], '') >>> lineparse('()', force_list=True) ([], '') >>> lineparse('1,') (['1'], '') >>> lineparse('"Yo"') ('Yo', '') >>> lineparse('"Yo"', force_list=True) (['Yo'], '') >>> lineparse('''h, i, j, (h, i, ['hello', "f"], [], ([]),), k''') (['h', 'i', 'j', ['h', 'i', ['hello', 'f'], [], [[]]], 'k'], '') >>> lineparse('''h, i, j, (h, i, ['hello', "f"], [], ([]),), k''', recursive=False) Traceback (most recent call last): BadLineError: Line is badly built : h, i, j, (h, i, ['hello', "f"], [], ([]),), k >>> lineparse('fish#dog') ('fish', '#dog') >>> lineparse('"fish"#dog') ('fish', '#dog') >>> lineparse('(((())))') ([[[[]]]], '') >>> lineparse('((((,))))') Traceback (most recent call last): BadLineError: Line is badly built : ((((,)))) >>> lineparse('hi, ()') (['hi', []], '') >>> lineparse('"hello", "",') (['hello', ''], '') >>> lineparse('"hello", ,') Traceback (most recent call last): BadLineError: Line is badly built : "hello", , >>> lineparse('"hello", ["hi", ""], ""') (['hello', ['hi', ''], ''], '') >>> lineparse('''"member 1", "member 2", ["nest 1", ("nest 2", 'nest 2b', ['nest 3', 'value'], nest 2c), nest1b]''') (['member 1', 'member 2', ['nest 1', ['nest 2', 'nest 2b', ['nest 3', 'value'], 'nest 2c'], 'nest1b']], '') >>> lineparse('''"member 1", "member 2", ["nest 1", ("nest 2", 'nest 2b', ['nest 3', 'value'], nest 2c), nest1b]]''') Traceback (most recent call last): BadLineError: Line is badly built : "member 1", "member 2", ["nest 1", ("nest 2", 'nest 2b', ['nest 3', 'value'], nest 2c), nest1b]] """ p = LineParser(options, **keywargs) return p.feed(inline) ############################################################################ # a couple of functions to help build lists def list_stringify(inlist): """ Recursively rebuilds a list - making sure all the members are strings. Can take any iterable or a sequence as the argument and always returns a list. Useful before writing out lists. Used by makelist if stringify is set. Uses the ``str`` function for stringification. Every element will be a string or a unicode object. Doesn't handle decoding strings into unicode objects (or vice-versa). >>> list_stringify([2, 2, 2, 2, (3, 3, 2.9)]) ['2', '2', '2', '2', ['3', '3', '2.9']] >>> list_stringify(None) Traceback (most recent call last): TypeError: iteration over non-sequence >>> list_stringify([]) [] FIXME: can receive any iterable - e.g. a sequence >>> list_stringify('') [] >>> list_stringify('Hello There') ['H', 'e', 'l', 'l', 'o', ' ', 'T', 'h', 'e', 'r', 'e'] """ outlist = [] for item in inlist: if not isinstance(item, (tuple, list)): if not isinstance(item, basestring): item = str(item) else: item = list_stringify(item) outlist.append(item) return outlist def makelist(inlist, listchar='', stringify=False, escape=False, encoding=None): """ Given a list - turn it into a string that represents that list. (Suitable for parsing by ``LineParser``). listchar should be ``'['``, ``'('`` or ``''``. This is the type of bracket used to enclose the list. (``''`` meaning no bracket of course). If you have nested lists and listchar is ``''``, makelist will automatically use ``'['`` for the nested lists. If stringify is ``True`` (default is ``False``) makelist will stringify the inlist first (using ``list_stringify``). If ``escape`` is ``True`` (default is ``False``) makelist will call ``quote_escape`` on each element before passing them to ``elem_quote`` to be quoted. If encoding keyword is not ``None``, all strings are decoded to unicode with the specified encoding. Each item will then be a unicode object instead of a string. >>> makelist([]) '[]' >>> makelist(['a', 'b', 'I can\\'t do it', 'Yes you "can" !']) 'a, b, "I can\\'t do it", \\'Yes you "can" !\\'' >>> makelist([3, 4, 5, [6, 7, 8]], stringify=True) '3, 4, 5, [6, 7, 8]' >>> makelist([3, 4, 5, [6, 7, 8]]) Traceback (most recent call last): TypeError: Can only quote strings. "3" >>> makelist(['a', 'b', 'c', ('d', 'e'), ('f', 'g')], listchar='(') '(a, b, c, (d, e), (f, g))' >>> makelist(['hi\\n', 'Quote "heck\\''], escape=True) 'hi&mjf-lf;, "Quote &mjf-quot;heck\\'"' >>> makelist(['a', 'b', 'c', ('d', 'e'), ('f', 'g')], encoding='UTF8') u'a, b, c, [d, e], [f, g]' """ if stringify: inlist = list_stringify(inlist) listdict = {'[' : '[%s]', '(' : '(%s)', '' : '%s'} outline = [] # this makes '[' the default for empty or single value lists if len(inlist) < 2: listchar = listchar or '[' for item in inlist: if not isinstance(item, (list, tuple)): if escape: item = quote_escape(item) outline.append(elem_quote(item, encoding=encoding)) else: # recursive for nested lists outline.append(makelist(item, listchar or '[', stringify, escape, encoding)) return listdict[listchar] % (', '.join(outline)) ############################################################################ # CSV functions # csvread, csvwrite def csvread(infile): """ Given an infile as an iterable, return the CSV as a list of lists. infile can be an open file object or a list of lines. If any of the lines are badly built then a ``CSVError`` will be raised. This has a ``csv`` attribute - which is a reference to the parsed CSV. Every line that couldn't be parsed will have ``[]`` for it's entry. The error *also* has an ``errors`` attribute. This is a list of all the errors raised. Error in this will have an ``index`` attribute, which is the line number, and a ``line`` attribute - which is the actual line that caused the error. Example of usage : .. raw:: html {+coloring} handle = open(filename) # remove the trailing '\n' from each line the_file = [line.rstrip('\n') for line in handle.readlines()] csv = csvread(the_file) {-coloring} >>> a = '''"object 1", 'object 2', object 3 ... test 1 , "test 2" ,'test 3' ... 'obj 1',obj 2,"obj 3"''' >>> csvread(a.splitlines()) [['object 1', 'object 2', 'object 3'], ['test 1', 'test 2', 'test 3'], ['obj 1', 'obj 2', 'obj 3']] >>> csvread(['object 1,']) [['object 1']] >>> try: ... csvread(['object 1, "hello', 'object 1, # a comment in a csv ?']) ... except CSVError, e: ... for entry in e.errors: ... print entry.index, entry 0 Value is badly quoted: ""hello" 1 Comment not allowed : object 1, # a comment in a csv ? """ out_csv = [] errors = [] index = -1 p = LineParser(csv=True) for line in infile: index += 1 try: values = p.feed(line) except ListQuoteError, e: values = [] e.line = line e.index = index errors.append(e) # out_csv.append(values) # if errors: e = CSVError("Parsing CSV failed. See 'errors' attribute.") e.csv = out_csv e.errors = errors raise e return out_csv def csvwrite(inlist, stringify=False): """ Given a list of lists it turns each entry into a line in a CSV. (Given a list of lists it returns a list of strings). The lines will *not* be ``\n`` terminated. Set stringify to ``True`` (default is ``False``) to convert entries to strings before creating the line. If stringify is ``False`` then any non string value will raise a ``TypeError``. Every member will be quoted using ``elem_quote``, but no escaping is done. Example of usage : .. raw:: html {+coloring} # escape each entry in each line (optional) for index in range(len(the_list)): the_list[index] = [quote_escape(val) for val in the_list[index]] # the_file = csvwrite(the_list) # add a '\n' to each line - ready to write to file the_file = [line + '\n' for line in the_file] {-coloring} >>> csvwrite([['object 1', 'object 2', 'object 3'], ['test 1', 'test 2', 'test 3'], ['obj 1', 'obj 2', 'obj 3']]) ['"object 1", "object 2", "object 3"', '"test 1", "test 2", "test 3"', '"obj 1", "obj 2", "obj 3"'] >>> csvwrite([[3, 3, 3]]) Traceback (most recent call last): TypeError: Can only quote strings. "3" >>> csvwrite([[3, 3, 3]], True) ['3, 3, 3'] """ out_list = [] for entry in inlist: if stringify: new_entry = [] for val in entry: if not isinstance(val, basestring): val = str(val) new_entry.append(val) entry = new_entry this_line = ', '.join([elem_quote(val) for val in entry]) out_list.append(this_line) return out_list ############################################################################ def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() """ ISSUES/TODO =========== Fix bug in simplelist Triple quote multiline values ? Doesn't allow Python style string escaping (but has '&mjf-quot;' and '&mjf-lf;'). Uses both \' and \" as quotes and sometimes doesn't quote at all - see elem_quote - may not *always* be compatible with other programs. Allow space seperated lists ? e.g. 10 5 100 20 Lineparser could create tuples. Allow ',' as an empty list ? CHANGELOG ========= 2005/08/28 - Version 1.4.0 -------------------------- * Greater use of regular expressions for added speed * Re-implemented ``lineparse`` as the ``LineParser`` object * Added doctests * Custom exceptions * Changed the behaviour of ``csvread`` and ``csvwrite`` * Removed the CSV ``compare`` function and the ``uncomment`` function * Only ``'#'`` allowed for comments * ``elem_quote`` raises exceptions * Changed behaviour of ``unquote`` * Added ``quote_escape`` and ``quote_unescape`` * Removed the ``uni_conv`` option in the CSV functions .. note:: These changes are quite extensive. If any of them cause you problems then let me know. I can provide a workaround in the next release. 2005/06/01 Version 1.3.0 Fixed bug in lineparse handling of empty list members. Thnks to bug report and fix by Par Pandit The 'unquote' function is now regex based. (bugfix it now doesn't return a tuple if fullquote is 0) Added the simplelist regex/function. elem_quote and uncomment use a regex for clarity and speed. Added a bunch of asserts to the tests. 2005/03/07 Version 1.2.1 makelist improved - better handling of empty or single member lists 2005/02/23 Version 1.2.0 Added uncomment for ConfigObj 3.3.0 Optimised unquote - not a character by character search any more. lineparse does full '&mjf..;' escape conversions - even when unquote isn't used makelist and elem_quote takes an 'encoding' keyword for string members to be used to decode strigns to unicode optimised makelist (including a minor bugfix) Change to lineparse - it wouldn't allow '[' or '(' inside elements unless they were quoted. 2004/12/04 Version 1.1.2 Changed the license (*again* - now OSI compatible). Empty values are now quoted by elem_quote. 30-08-04 Version 1.1.1 Removed the unicode hammer in csvread. Improved docs. 16-08-04 Version 1.1.0 Added handling for non-string elements in elem_quote (optional). Replaced some old += with lists and ''.join() for speed improvements... Using basestring and hasattr('__getitem__') tests instead of isinstance(list) and str in a couple of places. Changed license text. Made the tests useful. 19-06-04 Version 1.0.0 Seems to work ok. A worthy successor to listparse and csv_s - although not as elegant as it could be. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/validate.py0000600000175000017500000013007110425411107025006 0ustar madduckmadduck# validate.py # A Validator object # Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa # E-mail: fuzzyman AT voidspace DOT org DOT uk # mark AT la-la DOT com # nico AT tekNico DOT net # This software is licensed under the terms of the BSD license. # http://www.voidspace.org.uk/python/license.shtml # Basically you're free to copy, modify, distribute and relicense it, # So long as you keep a copy of the license with it. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # For information about bugfixes, updates and support, please join the # ConfigObj mailing list: # http://lists.sourceforge.net/lists/listinfo/configobj-develop # Comments, suggestions and bug reports welcome. """ The Validator object is used to check that supplied values conform to a specification. The value can be supplied as a string - e.g. from a config file. In this case the check will also *convert* the value to the required type. This allows you to add validation as a transparent layer to access data stored as strings. The validation checks that the data is correct *and* converts it to the expected type. Some standard checks are provided for basic data types. Additional checks are easy to write. They can be provided when the ``Validator`` is instantiated or added afterwards. The standard functions work with the following basic data types : * integers * floats * booleans * strings * ip_addr plus lists of these datatypes Adding additional checks is done through coding simple functions. The full set of standard checks are : * 'integer': matches integer values (including negative) Takes optional 'min' and 'max' arguments : :: integer() integer(3, 9) # any value from 3 to 9 integer(min=0) # any positive value integer(max=9) * 'float': matches float values Has the same parameters as the integer check. * 'boolean': matches boolean values - ``True`` or ``False`` Acceptable string values for True are : true, on, yes, 1 Acceptable string values for False are : false, off, no, 0 Any other value raises an error. * 'ip_addr': matches an Internet Protocol address, v.4, represented by a dotted-quad string, i.e. '1.2.3.4'. * 'string': matches any string. Takes optional keyword args 'min' and 'max' to specify min and max lengths of the string. * 'list': matches any list. Takes optional keyword args 'min', and 'max' to specify min and max sizes of the list. * 'int_list': Matches a list of integers. Takes the same arguments as list. * 'float_list': Matches a list of floats. Takes the same arguments as list. * 'bool_list': Matches a list of boolean values. Takes the same arguments as list. * 'ip_addr_list': Matches a list of IP addresses. Takes the same arguments as list. * 'string_list': Matches a list of strings. Takes the same arguments as list. * 'mixed_list': Matches a list with different types in specific positions. List size must match the number of arguments. Each position can be one of : 'integer', 'float', 'ip_addr', 'string', 'boolean' So to specify a list with two strings followed by two integers, you write the check as : :: mixed_list('string', 'string', 'integer', 'integer') * 'pass': This check matches everything ! It never fails and the value is unchanged. It is also the default if no check is specified. * 'option': This check matches any from a list of options. You specify this check with : :: option('option 1', 'option 2', 'option 3') You can supply a default value (returned if no value is supplied) using the default keyword argument. You specify a list argument for default using a list constructor syntax in the check : :: checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) A badly formatted set of arguments will raise a ``VdtParamError``. """ __docformat__ = "restructuredtext en" __version__ = '0.2.2' __revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $' __all__ = ( '__version__', 'dottedQuadToNum', 'numToDottedQuad', 'ValidateError', 'VdtUnknownCheckError', 'VdtParamError', 'VdtTypeError', 'VdtValueError', 'VdtValueTooSmallError', 'VdtValueTooBigError', 'VdtValueTooShortError', 'VdtValueTooLongError', 'VdtMissingValue', 'Validator', 'is_integer', 'is_float', 'is_bool', 'is_list', 'is_ip_addr', 'is_string', 'is_int_list', 'is_bool_list', 'is_float_list', 'is_string_list', 'is_ip_addr_list', 'is_mixed_list', 'is_option', '__docformat__', ) import sys INTP_VER = sys.version_info[:2] if INTP_VER < (2, 2): raise RuntimeError("Python v.2.2 or later needed") import re StringTypes = (str, unicode) _list_arg = re.compile(r''' (?: ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( ( (?: \s* (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s\)][^,\)]*?) # unquoted ) \s*,\s* )* (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s\)][^,\)]*?) # unquoted )? # last one ) \) ) ''', re.VERBOSE) # two groups _list_members = re.compile(r''' ( (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s=][^,=]*?) # unquoted ) (?: (?:\s*,\s*)|(?:\s*$) # comma ) ''', re.VERBOSE) # one group _paramstring = r''' (?: ( (?: [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( (?: \s* (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s\)][^,\)]*?) # unquoted ) \s*,\s* )* (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s\)][^,\)]*?) # unquoted )? # last one \) )| (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s=][^,=]*?)| # unquoted (?: # keyword argument [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\s=][^,=]*?) # unquoted ) ) ) ) (?: (?:\s*,\s*)|(?:\s*$) # comma ) ) ''' _matchstring = '^%s*' % _paramstring # Python pre 2.2.1 doesn't have bool try: bool except NameError: def bool(val): """Simple boolean equivalent function. """ if val: return 1 else: return 0 def dottedQuadToNum(ip): """ Convert decimal dotted quad string to long integer >>> dottedQuadToNum('1 ') 1L >>> dottedQuadToNum(' 1.2') 16777218L >>> dottedQuadToNum(' 1.2.3 ') 16908291L >>> dottedQuadToNum('1.2.3.4') 16909060L >>> dottedQuadToNum('1.2.3. 4') Traceback (most recent call last): ValueError: Not a good dotted-quad IP: 1.2.3. 4 >>> dottedQuadToNum('255.255.255.255') 4294967295L >>> dottedQuadToNum('255.255.255.256') Traceback (most recent call last): ValueError: Not a good dotted-quad IP: 255.255.255.256 """ # import here to avoid it when ip_addr values are not used import socket, struct try: return struct.unpack('!L', socket.inet_aton(ip.strip()))[0] except socket.error: # bug in inet_aton, corrected in Python 2.3 if ip.strip() == '255.255.255.255': return 0xFFFFFFFFL else: raise ValueError('Not a good dotted-quad IP: %s' % ip) return def numToDottedQuad(num): """ Convert long int to dotted quad string >>> numToDottedQuad(-1L) Traceback (most recent call last): ValueError: Not a good numeric IP: -1 >>> numToDottedQuad(1L) '0.0.0.1' >>> numToDottedQuad(16777218L) '1.0.0.2' >>> numToDottedQuad(16908291L) '1.2.0.3' >>> numToDottedQuad(16909060L) '1.2.3.4' >>> numToDottedQuad(4294967295L) '255.255.255.255' >>> numToDottedQuad(4294967296L) Traceback (most recent call last): ValueError: Not a good numeric IP: 4294967296 """ # import here to avoid it when ip_addr values are not used import socket, struct # no need to intercept here, 4294967295L is fine try: return socket.inet_ntoa( struct.pack('!L', long(num))) except (socket.error, struct.error, OverflowError): raise ValueError('Not a good numeric IP: %s' % num) class ValidateError(Exception): """ This error indicates that the check failed. It can be the base class for more specific errors. Any check function that fails ought to raise this error. (or a subclass) >>> raise ValidateError Traceback (most recent call last): ValidateError """ class VdtMissingValue(ValidateError): """No value was supplied to a check that needed one.""" class VdtUnknownCheckError(ValidateError): """An unknown check function was requested""" def __init__(self, value): """ >>> raise VdtUnknownCheckError('yoda') Traceback (most recent call last): VdtUnknownCheckError: the check "yoda" is unknown. """ ValidateError.__init__( self, 'the check "%s" is unknown.' % value) class VdtParamError(SyntaxError): """An incorrect parameter was passed""" def __init__(self, name, value): """ >>> raise VdtParamError('yoda', 'jedi') Traceback (most recent call last): VdtParamError: passed an incorrect value "jedi" for parameter "yoda". """ SyntaxError.__init__( self, 'passed an incorrect value "%s" for parameter "%s".' % ( value, name)) class VdtTypeError(ValidateError): """The value supplied was of the wrong type""" def __init__(self, value): """ >>> raise VdtTypeError('jedi') Traceback (most recent call last): VdtTypeError: the value "jedi" is of the wrong type. """ ValidateError.__init__( self, 'the value "%s" is of the wrong type.' % value) class VdtValueError(ValidateError): """ The value supplied was of the correct type, but was not an allowed value. """ def __init__(self, value): """ >>> raise VdtValueError('jedi') Traceback (most recent call last): VdtValueError: the value "jedi" is unacceptable. """ ValidateError.__init__( self, 'the value "%s" is unacceptable.' % value) class VdtValueTooSmallError(VdtValueError): """The value supplied was of the correct type, but was too small.""" def __init__(self, value): """ >>> raise VdtValueTooSmallError('0') Traceback (most recent call last): VdtValueTooSmallError: the value "0" is too small. """ ValidateError.__init__( self, 'the value "%s" is too small.' % value) class VdtValueTooBigError(VdtValueError): """The value supplied was of the correct type, but was too big.""" def __init__(self, value): """ >>> raise VdtValueTooBigError('1') Traceback (most recent call last): VdtValueTooBigError: the value "1" is too big. """ ValidateError.__init__( self, 'the value "%s" is too big.' % value) class VdtValueTooShortError(VdtValueError): """The value supplied was of the correct type, but was too short.""" def __init__(self, value): """ >>> raise VdtValueTooShortError('jed') Traceback (most recent call last): VdtValueTooShortError: the value "jed" is too short. """ ValidateError.__init__( self, 'the value "%s" is too short.' % (value,)) class VdtValueTooLongError(VdtValueError): """The value supplied was of the correct type, but was too long.""" def __init__(self, value): """ >>> raise VdtValueTooLongError('jedie') Traceback (most recent call last): VdtValueTooLongError: the value "jedie" is too long. """ ValidateError.__init__( self, 'the value "%s" is too long.' % (value,)) class Validator(object): """ Validator is an object that allows you to register a set of 'checks'. These checks take input and test that it conforms to the check. This can also involve converting the value from a string into the correct datatype. The ``check`` method takes an input string which configures which check is to be used and applies that check to a supplied value. An example input string would be: 'int_range(param1, param2)' You would then provide something like: >>> def int_range_check(value, min, max): ... # turn min and max from strings to integers ... min = int(min) ... max = int(max) ... # check that value is of the correct type. ... # possible valid inputs are integers or strings ... # that represent integers ... if not isinstance(value, (int, long, StringTypes)): ... raise VdtTypeError(value) ... elif isinstance(value, StringTypes): ... # if we are given a string ... # attempt to convert to an integer ... try: ... value = int(value) ... except ValueError: ... raise VdtValueError(value) ... # check the value is between our constraints ... if not min <= value: ... raise VdtValueTooSmallError(value) ... if not value <= max: ... raise VdtValueTooBigError(value) ... return value >>> fdict = {'int_range': int_range_check} >>> vtr1 = Validator(fdict) >>> vtr1.check('int_range(20, 40)', '30') 30 >>> vtr1.check('int_range(20, 40)', '60') Traceback (most recent call last): VdtValueTooBigError: the value "60" is too big. New functions can be added with : :: >>> vtr2 = Validator() >>> vtr2.functions['int_range'] = int_range_check Or by passing in a dictionary of functions when Validator is instantiated. Your functions *can* use keyword arguments, but the first argument should always be 'value'. If the function doesn't take additional arguments, the parentheses are optional in the check. It can be written with either of : :: keyword = function_name keyword = function_name() The first program to utilise Validator() was Michael Foord's ConfigObj, an alternative to ConfigParser which supports lists and can validate a config file using a config schema. For more details on using Validator with ConfigObj see: http://www.voidspace.org.uk/python/configobj.html """ # this regex does the initial parsing of the checks _func_re = re.compile(r'(.+?)\((.*)\)') # this regex takes apart keyword arguments _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$') # this regex finds keyword=list(....) type values _list_arg = _list_arg # this regex takes individual values out of lists - in one pass _list_members = _list_members # These regexes check a set of arguments for validity # and then pull the members out _paramfinder = re.compile(_paramstring, re.VERBOSE) _matchfinder = re.compile(_matchstring, re.VERBOSE) def __init__(self, functions=None): """ >>> vtri = Validator() """ self.functions = { '': self._pass, 'integer': is_integer, 'float': is_float, 'boolean': is_bool, 'ip_addr': is_ip_addr, 'string': is_string, 'list': is_list, 'int_list': is_int_list, 'float_list': is_float_list, 'bool_list': is_bool_list, 'ip_addr_list': is_ip_addr_list, 'string_list': is_string_list, 'mixed_list': is_mixed_list, 'pass': self._pass, 'option': is_option, } if functions is not None: self.functions.update(functions) # tekNico: for use by ConfigObj self.baseErrorClass = ValidateError def check(self, check, value, missing=False): """ Usage: check(check, value) Arguments: check: string representing check to apply (including arguments) value: object to be checked Returns value, converted to correct type if necessary If the check fails, raises a ``ValidateError`` subclass. >>> vtor.check('yoda', '') Traceback (most recent call last): VdtUnknownCheckError: the check "yoda" is unknown. >>> vtor.check('yoda()', '') Traceback (most recent call last): VdtUnknownCheckError: the check "yoda" is unknown. """ fun_match = self._func_re.match(check) if fun_match: fun_name = fun_match.group(1) arg_string = fun_match.group(2) arg_match = self._matchfinder.match(arg_string) if arg_match is None: # Bad syntax raise VdtParamError fun_args = [] fun_kwargs = {} # pull out args of group 2 for arg in self._paramfinder.findall(arg_string): # args may need whitespace removing (before removing quotes) arg = arg.strip() listmatch = self._list_arg.match(arg) if listmatch: key, val = self._list_handle(listmatch) fun_kwargs[key] = val continue keymatch = self._key_arg.match(arg) if keymatch: val = self._unquote(keymatch.group(2)) fun_kwargs[keymatch.group(1)] = val continue # fun_args.append(self._unquote(arg)) else: # allows for function names without (args) (fun_name, fun_args, fun_kwargs) = (check, (), {}) # if missing: try: value = fun_kwargs['default'] except KeyError: raise VdtMissingValue if value == 'None': value = None if value is None: return None # tekNico: default must be deleted if the value is specified too, # otherwise the check function will get a spurious "default" keyword arg try: del fun_kwargs['default'] except KeyError: pass try: fun = self.functions[fun_name] except KeyError: raise VdtUnknownCheckError(fun_name) else: ## print fun_args ## print fun_kwargs return fun(value, *fun_args, **fun_kwargs) def _unquote(self, val): """Unquote a value if necessary.""" if (len(val) > 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): val = val[1:-1] return val def _list_handle(self, listmatch): """Take apart a ``keyword=list('val, 'val')`` type string.""" out = [] name = listmatch.group(1) args = listmatch.group(2) for arg in self._list_members.findall(args): out.append(self._unquote(arg)) return name, out def _pass(self, value): """ Dummy check that always passes >>> vtor.check('', 0) 0 >>> vtor.check('', '0') '0' """ return value def _is_num_param(names, values, to_float=False): """ Return numbers from inputs or raise VdtParamError. Lets ``None`` pass through. Pass in keyword argument ``to_float=True`` to use float for the conversion rather than int. >>> _is_num_param(('', ''), (0, 1.0)) [0, 1] >>> _is_num_param(('', ''), (0, 1.0), to_float=True) [0.0, 1.0] >>> _is_num_param(('a'), ('a')) Traceback (most recent call last): VdtParamError: passed an incorrect value "a" for parameter "a". """ fun = to_float and float or int out_params = [] for (name, val) in zip(names, values): if val is None: out_params.append(val) elif isinstance(val, (int, long, float, StringTypes)): try: out_params.append(fun(val)) except ValueError, e: raise VdtParamError(name, val) else: raise VdtParamError(name, val) return out_params # built in checks # you can override these by setting the appropriate name # in Validator.functions # note: if the params are specified wrongly in your input string, # you will also raise errors. def is_integer(value, min=None, max=None): """ A check that tests that a given value is an integer (int, or long) and optionally, between bounds. A negative value is accepted, while a float will fail. If the value is a string, then the conversion is done - if possible. Otherwise a VdtError is raised. >>> vtor.check('integer', '-1') -1 >>> vtor.check('integer', '0') 0 >>> vtor.check('integer', 9) 9 >>> vtor.check('integer', 'a') Traceback (most recent call last): VdtTypeError: the value "a" is of the wrong type. >>> vtor.check('integer', '2.2') Traceback (most recent call last): VdtTypeError: the value "2.2" is of the wrong type. >>> vtor.check('integer(10)', '20') 20 >>> vtor.check('integer(max=20)', '15') 15 >>> vtor.check('integer(10)', '9') Traceback (most recent call last): VdtValueTooSmallError: the value "9" is too small. >>> vtor.check('integer(10)', 9) Traceback (most recent call last): VdtValueTooSmallError: the value "9" is too small. >>> vtor.check('integer(max=20)', '35') Traceback (most recent call last): VdtValueTooBigError: the value "35" is too big. >>> vtor.check('integer(max=20)', 35) Traceback (most recent call last): VdtValueTooBigError: the value "35" is too big. >>> vtor.check('integer(0, 9)', False) 0 """ # print value, type(value) (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) if not isinstance(value, (int, long, StringTypes)): raise VdtTypeError(value) if isinstance(value, StringTypes): # if it's a string - does it represent an integer ? try: value = int(value) except ValueError: raise VdtTypeError(value) if (min_val is not None) and (value < min_val): raise VdtValueTooSmallError(value) if (max_val is not None) and (value > max_val): raise VdtValueTooBigError(value) return value def is_float(value, min=None, max=None): """ A check that tests that a given value is a float (an integer will be accepted), and optionally - that it is between bounds. If the value is a string, then the conversion is done - if possible. Otherwise a VdtError is raised. This can accept negative values. >>> vtor.check('float', '2') 2.0 From now on we multiply the value to avoid comparing decimals >>> vtor.check('float', '-6.8') * 10 -68.0 >>> vtor.check('float', '12.2') * 10 122.0 >>> vtor.check('float', 8.4) * 10 84.0 >>> vtor.check('float', 'a') Traceback (most recent call last): VdtTypeError: the value "a" is of the wrong type. >>> vtor.check('float(10.1)', '10.2') * 10 102.0 >>> vtor.check('float(max=20.2)', '15.1') * 10 151.0 >>> vtor.check('float(10.0)', '9.0') Traceback (most recent call last): VdtValueTooSmallError: the value "9.0" is too small. >>> vtor.check('float(max=20.0)', '35.0') Traceback (most recent call last): VdtValueTooBigError: the value "35.0" is too big. """ (min_val, max_val) = _is_num_param( ('min', 'max'), (min, max), to_float=True) if not isinstance(value, (int, long, float, StringTypes)): raise VdtTypeError(value) if not isinstance(value, float): # if it's a string - does it represent a float ? try: value = float(value) except ValueError: raise VdtTypeError(value) if (min_val is not None) and (value < min_val): raise VdtValueTooSmallError(value) if (max_val is not None) and (value > max_val): raise VdtValueTooBigError(value) return value bool_dict = { True: True, 'on': True, '1': True, 'true': True, 'yes': True, False: False, 'off': False, '0': False, 'false': False, 'no': False, } def is_bool(value): """ Check if the value represents a boolean. >>> vtor.check('boolean', 0) 0 >>> vtor.check('boolean', False) 0 >>> vtor.check('boolean', '0') 0 >>> vtor.check('boolean', 'off') 0 >>> vtor.check('boolean', 'false') 0 >>> vtor.check('boolean', 'no') 0 >>> vtor.check('boolean', 'nO') 0 >>> vtor.check('boolean', 'NO') 0 >>> vtor.check('boolean', 1) 1 >>> vtor.check('boolean', True) 1 >>> vtor.check('boolean', '1') 1 >>> vtor.check('boolean', 'on') 1 >>> vtor.check('boolean', 'true') 1 >>> vtor.check('boolean', 'yes') 1 >>> vtor.check('boolean', 'Yes') 1 >>> vtor.check('boolean', 'YES') 1 >>> vtor.check('boolean', '') Traceback (most recent call last): VdtTypeError: the value "" is of the wrong type. >>> vtor.check('boolean', 'up') Traceback (most recent call last): VdtTypeError: the value "up" is of the wrong type. """ if isinstance(value, StringTypes): try: return bool_dict[value.lower()] except KeyError: raise VdtTypeError(value) # we do an equality test rather than an identity test # this ensures Python 2.2 compatibilty # and allows 0 and 1 to represent True and False if value == False: return False elif value == True: return True else: raise VdtTypeError(value) def is_ip_addr(value): """ Check that the supplied value is an Internet Protocol address, v.4, represented by a dotted-quad string, i.e. '1.2.3.4'. >>> vtor.check('ip_addr', '1 ') '1' >>> vtor.check('ip_addr', ' 1.2') '1.2' >>> vtor.check('ip_addr', ' 1.2.3 ') '1.2.3' >>> vtor.check('ip_addr', '1.2.3.4') '1.2.3.4' >>> vtor.check('ip_addr', '0.0.0.0') '0.0.0.0' >>> vtor.check('ip_addr', '255.255.255.255') '255.255.255.255' >>> vtor.check('ip_addr', '255.255.255.256') Traceback (most recent call last): VdtValueError: the value "255.255.255.256" is unacceptable. >>> vtor.check('ip_addr', '1.2.3.4.5') Traceback (most recent call last): VdtValueError: the value "1.2.3.4.5" is unacceptable. >>> vtor.check('ip_addr', '1.2.3. 4') Traceback (most recent call last): VdtValueError: the value "1.2.3. 4" is unacceptable. >>> vtor.check('ip_addr', 0) Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. """ if not isinstance(value, StringTypes): raise VdtTypeError(value) value = value.strip() try: dottedQuadToNum(value) except ValueError: raise VdtValueError(value) return value def is_list(value, min=None, max=None): """ Check that the value is a list of values. You can optionally specify the minimum and maximum number of members. It does no check on list members. >>> vtor.check('list', ()) () >>> vtor.check('list', []) [] >>> vtor.check('list', (1, 2)) (1, 2) >>> vtor.check('list', [1, 2]) [1, 2] >>> vtor.check('list', '12') '12' >>> vtor.check('list(3)', (1, 2)) Traceback (most recent call last): VdtValueTooShortError: the value "(1, 2)" is too short. >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) Traceback (most recent call last): VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) (1, 2, 3, 4) >>> vtor.check('list', 0) Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. """ (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) try: num_members = len(value) except TypeError: raise VdtTypeError(value) if min_len is not None and num_members < min_len: raise VdtValueTooShortError(value) if max_len is not None and num_members > max_len: raise VdtValueTooLongError(value) return value def is_string(value, min=None, max=None): """ Check that the supplied value is a string. You can optionally specify the minimum and maximum number of members. >>> vtor.check('string', '0') '0' >>> vtor.check('string', 0) Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. >>> vtor.check('string(2)', '12') '12' >>> vtor.check('string(2)', '1') Traceback (most recent call last): VdtValueTooShortError: the value "1" is too short. >>> vtor.check('string(min=2, max=3)', '123') '123' >>> vtor.check('string(min=2, max=3)', '1234') Traceback (most recent call last): VdtValueTooLongError: the value "1234" is too long. """ if not isinstance(value, StringTypes): raise VdtTypeError(value) (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) try: num_members = len(value) except TypeError: raise VdtTypeError(value) if min_len is not None and num_members < min_len: raise VdtValueTooShortError(value) if max_len is not None and num_members > max_len: raise VdtValueTooLongError(value) return value def is_int_list(value, min=None, max=None): """ Check that the value is a list of integers. You can optionally specify the minimum and maximum number of members. Each list member is checked that it is an integer. >>> vtor.check('int_list', ()) [] >>> vtor.check('int_list', []) [] >>> vtor.check('int_list', (1, 2)) [1, 2] >>> vtor.check('int_list', [1, 2]) [1, 2] >>> vtor.check('int_list', [1, 'a']) Traceback (most recent call last): VdtTypeError: the value "a" is of the wrong type. """ return [is_integer(mem) for mem in is_list(value, min, max)] def is_bool_list(value, min=None, max=None): """ Check that the value is a list of booleans. You can optionally specify the minimum and maximum number of members. Each list member is checked that it is a boolean. >>> vtor.check('bool_list', ()) [] >>> vtor.check('bool_list', []) [] >>> check_res = vtor.check('bool_list', (True, False)) >>> check_res == [True, False] 1 >>> check_res = vtor.check('bool_list', [True, False]) >>> check_res == [True, False] 1 >>> vtor.check('bool_list', [True, 'a']) Traceback (most recent call last): VdtTypeError: the value "a" is of the wrong type. """ return [is_bool(mem) for mem in is_list(value, min, max)] def is_float_list(value, min=None, max=None): """ Check that the value is a list of floats. You can optionally specify the minimum and maximum number of members. Each list member is checked that it is a float. >>> vtor.check('float_list', ()) [] >>> vtor.check('float_list', []) [] >>> vtor.check('float_list', (1, 2.0)) [1.0, 2.0] >>> vtor.check('float_list', [1, 2.0]) [1.0, 2.0] >>> vtor.check('float_list', [1, 'a']) Traceback (most recent call last): VdtTypeError: the value "a" is of the wrong type. """ return [is_float(mem) for mem in is_list(value, min, max)] def is_string_list(value, min=None, max=None): """ Check that the value is a list of strings. You can optionally specify the minimum and maximum number of members. Each list member is checked that it is a string. >>> vtor.check('string_list', ()) [] >>> vtor.check('string_list', []) [] >>> vtor.check('string_list', ('a', 'b')) ['a', 'b'] >>> vtor.check('string_list', ['a', 1]) Traceback (most recent call last): VdtTypeError: the value "1" is of the wrong type. >>> vtor.check('string_list', 'hello') Traceback (most recent call last): VdtTypeError: the value "hello" is of the wrong type. """ if isinstance(value, StringTypes): raise VdtTypeError(value) return [is_string(mem) for mem in is_list(value, min, max)] def is_ip_addr_list(value, min=None, max=None): """ Check that the value is a list of IP addresses. You can optionally specify the minimum and maximum number of members. Each list member is checked that it is an IP address. >>> vtor.check('ip_addr_list', ()) [] >>> vtor.check('ip_addr_list', []) [] >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) ['1.2.3.4', '5.6.7.8'] >>> vtor.check('ip_addr_list', ['a']) Traceback (most recent call last): VdtValueError: the value "a" is unacceptable. """ return [is_ip_addr(mem) for mem in is_list(value, min, max)] fun_dict = { 'integer': is_integer, 'float': is_float, 'ip_addr': is_ip_addr, 'string': is_string, 'boolean': is_bool, } def is_mixed_list(value, *args): """ Check that the value is a list. Allow specifying the type of each member. Work on lists of specific lengths. You specify each member as a positional argument specifying type Each type should be one of the following strings : 'integer', 'float', 'ip_addr', 'string', 'boolean' So you can specify a list of two strings, followed by two integers as : mixed_list('string', 'string', 'integer', 'integer') The length of the list must match the number of positional arguments you supply. >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] 1 >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] 1 >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) Traceback (most recent call last): VdtTypeError: the value "b" is of the wrong type. >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) Traceback (most recent call last): VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) Traceback (most recent call last): VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. >>> vtor.check(mix_str, 0) Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. This test requires an elaborate setup, because of a change in error string output from the interpreter between Python 2.2 and 2.3 . >>> res_seq = ( ... 'passed an incorrect value "', ... 'yoda', ... '" for parameter "mixed_list".', ... ) >>> if INTP_VER == (2, 2): ... res_str = "".join(res_seq) ... else: ... res_str = "'".join(res_seq) >>> try: ... vtor.check('mixed_list("yoda")', ('a')) ... except VdtParamError, err: ... str(err) == res_str 1 """ try: length = len(value) except TypeError: raise VdtTypeError(value) if length < len(args): raise VdtValueTooShortError(value) elif length > len(args): raise VdtValueTooLongError(value) try: return [fun_dict[arg](val) for arg, val in zip(args, value)] except KeyError, e: raise VdtParamError('mixed_list', e) def is_option(value, *options): """ This check matches the value to any of a set of options. >>> vtor.check('option("yoda", "jedi")', 'yoda') 'yoda' >>> vtor.check('option("yoda", "jedi")', 'jed') Traceback (most recent call last): VdtValueError: the value "jed" is unacceptable. >>> vtor.check('option("yoda", "jedi")', 0) Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. """ if not isinstance(value, StringTypes): raise VdtTypeError(value) if not value in options: raise VdtValueError(value) return value def _test(value, *args, **keywargs): """ A function that exists for test purposes. >>> checks = [ ... '3, 6, min=1, max=3, test=list(a, b, c)', ... '3', ... '3, 6', ... '3,', ... 'min=1, test="a b c"', ... 'min=5, test="a, b, c"', ... 'min=1, max=3, test="a, b, c"', ... 'min=-100, test=-99', ... 'min=1, max=3', ... '3, 6, test="36"', ... '3, 6, test="a, b, c"', ... '3, max=3, test=list("a", "b", "c")', ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', ... "test='x=fish(3)'", ... ] >>> v = Validator({'test': _test}) >>> for entry in checks: ... print v.check(('test(%s)' % entry), 3) (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) (3, ('3',), {}) (3, ('3', '6'), {}) (3, ('3',), {}) (3, (), {'test': 'a b c', 'min': '1'}) (3, (), {'test': 'a, b, c', 'min': '5'}) (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) (3, (), {'test': '-99', 'min': '-100'}) (3, (), {'max': '3', 'min': '1'}) (3, ('3', '6'), {'test': '36'}) (3, ('3', '6'), {'test': 'a, b, c'}) (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) (3, (), {'test': 'x=fish(3)'}) """ return (value, args, keywargs) if __name__ == '__main__': # run the code tests in doctest format import doctest m = sys.modules.get('__main__') globs = m.__dict__.copy() globs.update({ 'INTP_VER': INTP_VER, 'vtor': Validator(), }) doctest.testmod(m, globs=globs) """ TODO ==== Consider which parts of the regex stuff to put back in Can we implement a timestamp datatype ? (check DateUtil module) ISSUES ====== If we could pull tuples out of arguments, it would be easier to specify arguments for 'mixed_lists'. CHANGELOG ========= 2006/04/23 ---------- Addressed bug where a string would pass the ``is_list`` test. (Thanks to Konrad Wojas.) 2005/12/16 ---------- Fixed bug so we can handle keyword argument values with commas. We now use a list constructor for passing list values to keyword arguments (including ``default``) : :: default=list("val", "val", "val") Added the ``_test`` test. {sm;:-)} 0.2.1 2005/12/12 ---------- Moved a function call outside a try...except block. 2005/08/25 ---------- Most errors now prefixed ``Vdt`` ``VdtParamError`` no longer derives from ``VdtError`` Finalised as version 0.2.0 2005/08/21 ---------- By Nicola Larosa Removed the "length" argument for lists and strings, and related tests 2005/08/16 ---------- By Nicola Larosa Deleted the "none" and "multiple" types and checks Added the None value for all types in Validation.check 2005/08/14 ---------- By Michael Foord Removed timestamp. By Nicola Larosa Fixed bug in Validator.check: when a value that has a default is also specified in the config file, the default must be deleted from fun_kwargs anyway, otherwise the check function will get a spurious "default" keyword argument Added "ip_addr_list" check 2005/08/13 ---------- By Nicola Larosa Updated comments at top 2005/08/11 ---------- By Nicola Larosa Added test for interpreter version: raises RuntimeError if earlier than 2.2 Fixed last is_mixed_list test to work on Python 2.2 too 2005/08/10 ---------- By Nicola Larosa Restored Python2.2 compatibility by avoiding usage of dict.pop 2005/08/07 ---------- By Nicola Larosa Adjusted doctests for Python 2.2.3 compatibility, one test still fails for trivial reasons (string output delimiters) 2005/08/05 ---------- By Michael Foord Added __version__, __all__, and __docformat__ Replaced ``basestring`` with ``types.StringTypes`` 2005/07/28 ---------- By Nicola Larosa Reformatted final docstring in ReST format, indented it for easier folding 2005/07/20 ---------- By Nicola Larosa Added an 'ip_addr' IPv4 address value check, with tests Updated the tests for mixed_list to include IP addresses Changed all references to value "tests" into value "checks", including the main Validator method, and all code tests 2005/07/19 ---------- By Nicola Larosa Added even more code tests Refined the mixed_list check 2005/07/18 ---------- By Nicola Larosa Introduced more VdtValueError subclasses Collapsed the ``_function_test`` and ``_function_parse`` methods into the ``check`` one Refined the value checks, using the new VdtValueError subclasses Changed "is_string" to use "is_list" Added many more code tests Changed the "bool" value type to "boolean" Some more code cleanup 2005/07/17 ---------- By Nicola Larosa Code tests converted to doctest format and placed in the respective docstrings, so they are automatically checked, and easier to update Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and "max_val", to avoid shadowing the builtin functions (but left function parameters alone) Uniformed value check function names to is_* convention ``date`` type name changed to ``timestamp`` Avoided some code duplication in list check functions Some more code cleanup 2005/07/09 ---------- Recoded the standard functions 2005/07/08 ---------- Improved paramfinder regex Ripped out all the regex stuff, checks, and the example functions (to be replaced !) 2005/07/06 ---------- By Nicola Larosa Code cleanup """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/pathutils.py0000600000175000017500000004631510532056464025253 0ustar madduckmadduck# 2005/12/06 # Version 0.2.4 # pathutils.py # Functions useful for working with files and paths. # http://www.voidspace.org.uk/python/recipebook.shtml#utils # Copyright Michael Foord 2004 # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the Pythonutils mailing list. # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk from __future__ import generators """ This module contains convenience functions for working with files and paths. """ __version__ = '0.2.4' import os import sys import time __all__ = ( 'readlines', 'writelines', 'readbinary', 'writebinary', 'readfile', 'writefile', 'tslash', 'relpath', 'splitall', 'walkfiles', 'walkdirs', 'walkemptydirs', 'formatbytes', 'fullcopy', 'import_path', 'onerror', 'get_main_dir', 'main_is_frozen', 'Lock', 'LockError', 'LockFile', '__version__', ) ###################################### # Functions to read and write files in text and binary mode. def readlines(filename): """Passed a filename, it reads it, and returns a list of lines. (Read in text mode)""" filehandle = open(filename, 'r') outfile = filehandle.readlines() filehandle.close() return outfile def writelines(filename, infile, newline=False): """ Given a filename and a list of lines it writes the file. (In text mode) If ``newline`` is ``True`` (default is ``False``) it adds a newline to each line. """ filehandle = open(filename, 'w') if newline: infile = [line + '\n' for line in infile] filehandle.writelines(infile) filehandle.close() def readbinary(filename): """Given a filename, read a file in binary mode. It returns a single string.""" filehandle = open(filename, 'rb') thisfile = filehandle.read() filehandle.close() return thisfile def writebinary(filename, infile): """Given a filename and a string, write the file in binary mode. """ filehandle = open(filename, 'wb') filehandle.write(infile) filehandle.close() def readfile(filename): """Given a filename, read a file in text mode. It returns a single string.""" filehandle = open(filename, 'r') outfile = filehandle.read() filehandle.close() return outfile def writefile(filename, infile): """Given a filename and a string, write the file in text mode.""" filehandle = open(filename, 'w') filehandle.write(infile) filehandle.close() #################################################################### # Some functions for dealing with paths def tslash(apath): """ Add a trailing slash (``/``) to a path if it lacks one. It doesn't use ``os.sep`` because you end up in trouble on windoze, when you want separators for URLs. """ if apath and apath != '.' and not apath.endswith('/') and not apath.endswith('\\'): return apath + '/' else: return apath def relpath(origin, dest): """ Return the relative path between origin and dest. If it's not possible return dest. If they are identical return ``os.curdir`` Adapted from `path.py `_ by Jason Orendorff. """ origin = os.path.abspath(origin).replace('\\', '/') dest = os.path.abspath(dest).replace('\\', '/') # orig_list = splitall(os.path.normcase(origin)) # Don't normcase dest! We want to preserve the case. dest_list = splitall(dest) # if orig_list[0] != os.path.normcase(dest_list[0]): # Can't get here from there. return dest # # Find the location where the two paths start to differ. i = 0 for start_seg, dest_seg in zip(orig_list, dest_list): if start_seg != os.path.normcase(dest_seg): break i += 1 # # Now i is the point where the two paths diverge. # Need a certain number of "os.pardir"s to work up # from the origin to the point of divergence. segments = [os.pardir] * (len(orig_list) - i) # Need to add the diverging part of dest_list. segments += dest_list[i:] if len(segments) == 0: # If they happen to be identical, use os.curdir. return os.curdir else: return os.path.join(*segments).replace('\\', '/') def splitall(loc): """ Return a list of the path components in loc. (Used by relpath_). The first item in the list will be either ``os.curdir``, ``os.pardir``, empty, or the root directory of loc (for example, ``/`` or ``C:\\). The other items in the list will be strings. Adapted from *path.py* by Jason Orendorff. """ parts = [] while loc != os.curdir and loc != os.pardir: prev = loc loc, child = os.path.split(prev) if loc == prev: break parts.append(child) parts.append(loc) parts.reverse() return parts ####################################################################### # a pre 2.3 walkfiles function - adapted from the path module by Jason Orendorff join = os.path.join isdir = os.path.isdir isfile = os.path.isfile def walkfiles(thisdir): """ walkfiles(D) -> iterator over files in D, recursively. Yields full file paths. Adapted from path.py by Jason Orendorff. """ for child in os.listdir(thisdir): thischild = join(thisdir, child) if isfile(thischild): yield thischild elif isdir(thischild): for f in walkfiles(thischild): yield f def walkdirs(thisdir): """ Walk through all the subdirectories in a tree. Recursively yields directory names (full paths). """ for child in os.listdir(thisdir): thischild = join(thisdir, child) if isfile(thischild): continue elif isdir(thischild): for f in walkdirs(thischild): yield f yield thischild def walkemptydirs(thisdir): """ Recursively yield names of *empty* directories. These are the only paths omitted when using ``walkfiles``. """ if not os.listdir(thisdir): # if the directory is empty.. then yield it yield thisdir for child in os.listdir(thisdir): thischild = join(thisdir, child) if isdir(thischild): for emptydir in walkemptydirs(thischild): yield emptydir ############################################################### # formatbytes takes a filesize (as returned by os.getsize() ) # and formats it for display in one of two ways !! def formatbytes(sizeint, configdict=None, **configs): """ Given a file size as an integer, return a nicely formatted string that represents the size. Has various options to control it's output. You can pass in a dictionary of arguments or keyword arguments. Keyword arguments override the dictionary and there are sensible defaults for options you don't set. Options and defaults are as follows : * ``forcekb = False`` - If set this forces the output to be in terms of kilobytes and bytes only. * ``largestonly = True`` - If set, instead of outputting ``1 Mbytes, 307 Kbytes, 478 bytes`` it outputs using only the largest denominator - e.g. ``1.3 Mbytes`` or ``17.2 Kbytes`` * ``kiloname = 'Kbytes'`` - The string to use for kilobytes * ``meganame = 'Mbytes'`` - The string to use for Megabytes * ``bytename = 'bytes'`` - The string to use for bytes * ``nospace = True`` - If set it outputs ``1Mbytes, 307Kbytes``, notice there is no space. Example outputs : :: 19Mbytes, 75Kbytes, 255bytes 2Kbytes, 0bytes 23.8Mbytes .. note:: It currently uses the plural form even for singular. """ defaultconfigs = { 'forcekb' : False, 'largestonly' : True, 'kiloname' : 'Kbytes', 'meganame' : 'Mbytes', 'bytename' : 'bytes', 'nospace' : True} if configdict is None: configdict = {} for entry in configs: # keyword parameters override the dictionary passed in configdict[entry] = configs[entry] # for keyword in defaultconfigs: if not configdict.has_key(keyword): configdict[keyword] = defaultconfigs[keyword] # if configdict['nospace']: space = '' else: space = ' ' # mb, kb, rb = bytedivider(sizeint) if configdict['largestonly']: if mb and not configdict['forcekb']: return stringround(mb, kb)+ space + configdict['meganame'] elif kb or configdict['forcekb']: if mb and configdict['forcekb']: kb += 1024*mb return stringround(kb, rb) + space+ configdict['kiloname'] else: return str(rb) + space + configdict['bytename'] else: outstr = '' if mb and not configdict['forcekb']: outstr = str(mb) + space + configdict['meganame'] +', ' if kb or configdict['forcekb'] or mb: if configdict['forcekb']: kb += 1024*mb outstr += str(kb) + space + configdict['kiloname'] +', ' return outstr + str(rb) + space + configdict['bytename'] def stringround(main, rest): """ Given a file size in either (mb, kb) or (kb, bytes) - round it appropriately. """ # divide an int by a float... get a float value = main + rest/1024.0 return str(round(value, 1)) def bytedivider(nbytes): """ Given an integer (probably a long integer returned by os.getsize() ) it returns a tuple of (megabytes, kilobytes, bytes). This can be more easily converted into a formatted string to display the size of the file. """ mb, remainder = divmod(nbytes, 1048576) kb, rb = divmod(remainder, 1024) return (mb, kb, rb) ######################################## def fullcopy(src, dst): """ Copy file from src to dst. If the dst directory doesn't exist, we will attempt to create it using makedirs. """ import shutil if not os.path.isdir(os.path.dirname(dst)): os.makedirs(os.path.dirname(dst)) shutil.copy(src, dst) ####################################### def import_path(fullpath, strict=True): """ Import a file from the full path. Allows you to import from anywhere, something ``__import__`` does not do. If strict is ``True`` (the default), raise an ``ImportError`` if the module is found in the "wrong" directory. Taken from firedrop2_ by `Hans Nowak`_ .. _firedrop2: http://www.voidspace.org.uk/python/firedrop2/ .. _Hans Nowak: http://zephyrfalcon.org """ path, filename = os.path.split(fullpath) filename, ext = os.path.splitext(filename) sys.path.insert(0, path) try: module = __import__(filename) except ImportError: del sys.path[0] raise del sys.path[0] # if strict: path = os.path.split(module.__file__)[0] # FIXME: doesn't *startswith* allow room for errors ? if not fullpath.startswith(path): raise ImportError, "Module '%s' found, but not in '%s'" % ( filename, fullpath) # return module ############################################################################## # These functions get us our directory name # Even if py2exe or another freeze tool has been used def main_is_frozen(): """Return ``True`` if we're running from a frozen program.""" import imp return ( # new py2exe hasattr(sys, "frozen") or # tools/freeze imp.is_frozen("__main__")) def get_main_dir(): """Return the script directory - whether we're frozen or not.""" if main_is_frozen(): return os.path.abspath(os.path.dirname(sys.executable)) return os.path.abspath(os.path.dirname(sys.argv[0])) ############################## def onerror(func, path, exc_info): """ Error handler for ``shutil.rmtree``. If the error is due to an access error (read only file) it attempts to add write permission and then retries. If the error is for another reason it re-raises the error. Usage : ``shutil.rmtree(path, onerror=onerror)`` """ import stat if not os.access(path, os.W_OK): # Is the error an access error ? os.chmod(path, stat.S_IWUSR) func(path) else: raise ########################################################## # A set of object for providing simple, cross-platform file locking class LockError(IOError): """The generic error for locking - it is a subclass of ``IOError``.""" class Lock(object): """A simple file lock, compatible with windows and Unixes.""" def __init__(self, filename, timeout=5, step=0.1): """ Create a ``Lock`` object on file ``filename`` ``timeout`` is the time in seconds to wait before timing out, when attempting to acquire the lock. ``step`` is the number of seconds to wait in between each attempt to acquire the lock. """ self.timeout = timeout self.step = step self.filename = filename self.locked = False def lock(self, force=True): """ Lock the file for access by creating a directory of the same name (plus a trailing underscore). The file is only locked if you use this class to acquire the lock before accessing. If ``force`` is ``True`` (the default), then on timeout we forcibly acquire the lock. If ``force`` is ``False``, then on timeout a ``LockError`` is raised. """ if self.locked: raise LockError('%s is already locked' % self.filename) t = 0 name = self._mungedname() while t < self.timeout: t += self.step try: if os.apth.isdir(name): raise os.error else: os.mkdir() except os.error, err: time.sleep(self.step) else: self.locked = True return if force: self.locked = True else: raise LockError('Failed to acquire lock on %s' % self.filename) def unlock(self, ignore=True): """ Release the lock. If ``ignore`` is ``True`` and removing the lock directory fails, then the error is surpressed. (This may happen if the lock was acquired via a timeout.) """ if not self.locked: raise LockError('%s is not locked' % self.filename) self.locked = False try: os.rmdir(self._mungedname()) except os.error, err: if not ignore: raise LockError('unlocking appeared to fail - %s' % self.filename) def _mungedname(self): """ Override this in a subclass if you want to change the way ``Lock`` creates the directory name. """ return self.filename + '_' def __del__(self): """Auto unlock when object is deleted.""" if self.locked: self.unlock() class LockFile(Lock): """ A file like object with an exclusive lock, whilst it is open. The lock is provided by the ``Lock`` class, which creates a directory with the same name as the file (plus a trailing underscore), to indicate that the file is locked. This is simple and cross platform, with some limitations : * Unusual process termination could result in the directory being left. * The process acquiring the lock must have permission to create a directory in the same location as the file. * It only locks the file against other processes that attempt to acquire a lock using ``LockFile`` or ``Lock``. """ def __init__(self, filename, mode='r', bufsize=-1, timeout=5, step=0.1, force=True): """ Create a file like object that is locked (using the ``Lock`` class) until it is closed. The file is only locked against another process that attempts to acquire a lock using ``Lock`` (or ``LockFile``). The lock is released automatically when the file is closed. The filename, mode and bufsize arguments have the same meaning as for the built in function ``open``. The timeout and step arguments have the same meaning as for a ``Lock`` object. The force argument has the same meaning as for the ``Lock.lock`` method. A ``LockFile`` object has all the normal ``file`` methods and attributes. """ Lock.__init__(self, filename, timeout, step) # may raise an error if lock is ``False`` self.lock(force) # may also raise an error self._file = open(filename, mode, bufsize) def close(self, ignore=True): """ close the file and release the lock. ignore has the same meaning as for ``Lock.unlock`` """ self._file.close() self.unlock(ignore) def __getattr__(self, name): """delegate appropriate method/attribute calls to the file.""" if name not in self.__dict__: return getattr(self._file, name) else: return self.__dict__[self, name] def __setattr__(self, name, value): """Only allow attribute setting that don't clash with the file.""" if not '_file' in self.__dict__: Lock.__setattr__(self, name, value) elif hasattr(self._file, name): return setattr(self._file, name, value) else: Lock.__setattr__(self, name, value) def __del__(self): """Auto unlock (and close file) when object is deleted.""" if self.locked: self.unlock() self._file.close() """ Changelog ========= 2005/02/03 ---------- Added a workaround in ``Lock`` for operating systems that don't raise ``os.error`` when attempting to create a directory that already exists. 2005/12/06 Version 0.2.4 ----------------------------- Fixed bug in ``onerror``. (Missing stat import) 2005/11/26 Version 0.2.3 ----------------------------- Added ``Lock``, ``LockError``, and ``LockFile`` Added ``__version__`` 2005/11/13 Version 0.2.2 ----------------------------- Added the py2exe support functions. Added ``onerror``. 2005/08/28 Version 0.2.1 ----------------------------- * Added ``import_path`` * Added ``__all__`` * Code cleanup 2005/06/01 Version 0.2.0 ----------------------------- Added ``walkdirs`` generator. 2005/03/11 Version 0.1.1 ----------------------------- Added rounding to ``formatbytes`` and improved ``bytedivider`` with ``divmod``. Now explicit keyword parameters override the ``configdict`` in ``formatbytes``. 2005/02/18 Version 0.1.0 ----------------------------- The first numbered version. """ rest2web-0.5.2~alpha+svn-r248.orig/rest2web/pythonutils/odict.py0000600000175000017500000013272010536615641024336 0ustar madduckmadduck# odict.py # An Ordered Dictionary object # Copyright (C) 2005 Nicola Larosa, Michael Foord # E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk # This software is licensed under the terms of the BSD license. # http://www.voidspace.org.uk/python/license.shtml # Basically you're free to copy, modify, distribute and relicense it, # So long as you keep a copy of the license with it. # Documentation at http://www.voidspace.org.uk/python/odict.html # For information about bugfixes, updates and support, please join the # Pythonutils mailing list: # http://groups.google.com/group/pythonutils/ # Comments, suggestions and bug reports welcome. """A dict that keeps keys in insertion order""" from __future__ import generators __author__ = ('Nicola Larosa ,' 'Michael Foord ') __docformat__ = "restructuredtext en" __revision__ = '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $' __version__ = '0.2.2' __all__ = ['OrderedDict', 'SequenceOrderedDict'] import sys INTP_VER = sys.version_info[:2] if INTP_VER < (2, 2): raise RuntimeError("Python v.2.2 or later required") import types, warnings class OrderedDict(dict): """ A class of dictionary that keeps the insertion order of keys. All appropriate methods return keys, items, or values in an ordered way. All normal dictionary methods are available. Update and comparison is restricted to other OrderedDict objects. Various sequence methods are available, including the ability to explicitly mutate the key ordering. __contains__ tests: >>> d = OrderedDict(((1, 3),)) >>> 1 in d 1 >>> 4 in d 0 __getitem__ tests: >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] 1 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] Traceback (most recent call last): KeyError: 4 __len__ tests: >>> len(OrderedDict()) 0 >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) 3 get tests: >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.get(1) 3 >>> d.get(4) is None 1 >>> d.get(4, 5) 5 >>> d OrderedDict([(1, 3), (3, 2), (2, 1)]) has_key tests: >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.has_key(1) 1 >>> d.has_key(4) 0 """ def __init__(self, init_val=(), strict=False): """ Create a new ordered dictionary. Cannot init from a normal dict, nor from kwargs, since items order is undefined in those cases. If the ``strict`` keyword argument is ``True`` (``False`` is the default) then when doing slice assignment - the ``OrderedDict`` you are assigning from *must not* contain any keys in the remaining dict. >>> OrderedDict() OrderedDict([]) >>> OrderedDict({1: 1}) Traceback (most recent call last): TypeError: undefined order, cannot get items from dict >>> OrderedDict({1: 1}.items()) OrderedDict([(1, 1)]) >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d OrderedDict([(1, 3), (3, 2), (2, 1)]) >>> OrderedDict(d) OrderedDict([(1, 3), (3, 2), (2, 1)]) """ self.strict = strict dict.__init__(self) if isinstance(init_val, OrderedDict): self._sequence = init_val.keys() dict.update(self, init_val) elif isinstance(init_val, dict): # we lose compatibility with other ordered dict types this way raise TypeError('undefined order, cannot get items from dict') else: self._sequence = [] self.update(init_val) ### Special methods ### def __delitem__(self, key): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> del d[3] >>> d OrderedDict([(1, 3), (2, 1)]) >>> del d[3] Traceback (most recent call last): KeyError: 3 >>> d[3] = 2 >>> d OrderedDict([(1, 3), (2, 1), (3, 2)]) >>> del d[0:1] >>> d OrderedDict([(2, 1), (3, 2)]) """ if isinstance(key, types.SliceType): # FIXME: efficiency? keys = self._sequence[key] for entry in keys: dict.__delitem__(self, entry) del self._sequence[key] else: # do the dict.__delitem__ *first* as it raises # the more appropriate error dict.__delitem__(self, key) self._sequence.remove(key) def __eq__(self, other): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d == OrderedDict(d) True >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) False >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) False >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) False >>> d == dict(d) False >>> d == False False """ if isinstance(other, OrderedDict): # FIXME: efficiency? # Generate both item lists for each compare return (self.items() == other.items()) else: return False def __lt__(self, other): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) >>> c < d True >>> d < c False >>> d < dict(c) Traceback (most recent call last): TypeError: Can only compare with other OrderedDicts """ if not isinstance(other, OrderedDict): raise TypeError('Can only compare with other OrderedDicts') # FIXME: efficiency? # Generate both item lists for each compare return (self.items() < other.items()) def __le__(self, other): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) >>> e = OrderedDict(d) >>> c <= d True >>> d <= c False >>> d <= dict(c) Traceback (most recent call last): TypeError: Can only compare with other OrderedDicts >>> d <= e True """ if not isinstance(other, OrderedDict): raise TypeError('Can only compare with other OrderedDicts') # FIXME: efficiency? # Generate both item lists for each compare return (self.items() <= other.items()) def __ne__(self, other): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d != OrderedDict(d) False >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) True >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) True >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) False >>> d != dict(d) True >>> d != False True """ if isinstance(other, OrderedDict): # FIXME: efficiency? # Generate both item lists for each compare return not (self.items() == other.items()) else: return True def __gt__(self, other): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) >>> d > c True >>> c > d False >>> d > dict(c) Traceback (most recent call last): TypeError: Can only compare with other OrderedDicts """ if not isinstance(other, OrderedDict): raise TypeError('Can only compare with other OrderedDicts') # FIXME: efficiency? # Generate both item lists for each compare return (self.items() > other.items()) def __ge__(self, other): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) >>> e = OrderedDict(d) >>> c >= d False >>> d >= c True >>> d >= dict(c) Traceback (most recent call last): TypeError: Can only compare with other OrderedDicts >>> e >= d True """ if not isinstance(other, OrderedDict): raise TypeError('Can only compare with other OrderedDicts') # FIXME: efficiency? # Generate both item lists for each compare return (self.items() >= other.items()) def __repr__(self): """ Used for __repr__ and __str__ >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) >>> r1 "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) >>> r2 "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) True >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) True """ return '%s([%s])' % (self.__class__.__name__, ', '.join( ['(%r, %r)' % (key, self[key]) for key in self._sequence])) def __setitem__(self, key, val): """ Allows slice assignment, so long as the slice is an OrderedDict >>> d = OrderedDict() >>> d['a'] = 'b' >>> d['b'] = 'a' >>> d[3] = 12 >>> d OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) >>> d OrderedDict([(1, 2), (2, 3), (3, 4)]) >>> d[::2] = OrderedDict(((7, 8), (9, 10))) >>> d OrderedDict([(7, 8), (2, 3), (9, 10)]) >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) >>> d OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) >>> d OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) >>> a[3] = 4 >>> a OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) Traceback (most recent call last): ValueError: slice assignment must be from unique keys >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) >>> a[3] = 4 >>> a OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> a OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> d[:1] = 3 Traceback (most recent call last): TypeError: slice assignment requires an OrderedDict >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> d[:1] = OrderedDict([(9, 8)]) >>> d OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) """ if isinstance(key, types.SliceType): if not isinstance(val, OrderedDict): # FIXME: allow a list of tuples? raise TypeError('slice assignment requires an OrderedDict') keys = self._sequence[key] # NOTE: Could use ``range(*key.indices(len(self._sequence)))`` indexes = range(len(self._sequence))[key] if key.step is None: # NOTE: new slice may not be the same size as the one being # overwritten ! # NOTE: What is the algorithm for an impossible slice? # e.g. d[5:3] pos = key.start or 0 del self[key] newkeys = val.keys() for k in newkeys: if k in self: if self.strict: raise ValueError('slice assignment must be from ' 'unique keys') else: # NOTE: This removes duplicate keys *first* # so start position might have changed? del self[k] self._sequence = (self._sequence[:pos] + newkeys + self._sequence[pos:]) dict.update(self, val) else: # extended slice - length of new slice must be the same # as the one being replaced if len(keys) != len(val): raise ValueError('attempt to assign sequence of size %s ' 'to extended slice of size %s' % (len(val), len(keys))) # FIXME: efficiency? del self[key] item_list = zip(indexes, val.items()) # smallest indexes first - higher indexes not guaranteed to # exist item_list.sort() for pos, (newkey, newval) in item_list: if self.strict and newkey in self: raise ValueError('slice assignment must be from unique' ' keys') self.insert(pos, newkey, newval) else: if key not in self: self._sequence.append(key) dict.__setitem__(self, key, val) def __getitem__(self, key): """ Allows slicing. Returns an OrderedDict if you slice. >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) >>> b[::-1] OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) >>> b[2:5] OrderedDict([(5, 2), (4, 3), (3, 4)]) >>> type(b[2:4]) """ if isinstance(key, types.SliceType): # FIXME: does this raise the error we want? keys = self._sequence[key] # FIXME: efficiency? return OrderedDict([(entry, self[entry]) for entry in keys]) else: return dict.__getitem__(self, key) __str__ = __repr__ def __setattr__(self, name, value): """ Implemented so that accesses to ``sequence`` raise a warning and are diverted to the new ``setkeys`` method. """ if name == 'sequence': warnings.warn('Use of the sequence attribute is deprecated.' ' Use the keys method instead.', DeprecationWarning) # NOTE: doesn't return anything self.setkeys(value) else: # FIXME: do we want to allow arbitrary setting of attributes? # Or do we want to manage it? object.__setattr__(self, name, value) def __getattr__(self, name): """ Implemented so that access to ``sequence`` raises a warning. >>> d = OrderedDict() >>> d.sequence [] """ if name == 'sequence': warnings.warn('Use of the sequence attribute is deprecated.' ' Use the keys method instead.', DeprecationWarning) # NOTE: Still (currently) returns a direct reference. Need to # because code that uses sequence will expect to be able to # mutate it in place. return self._sequence else: # raise the appropriate error raise AttributeError("OrderedDict has no '%s' attribute" % name) def __deepcopy__(self, memo): """ To allow deepcopy to work with OrderedDict. >>> from copy import deepcopy >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) >>> a['test'] = {} >>> b = deepcopy(a) >>> b == a True >>> b is a False >>> a['test'] is b['test'] False """ from copy import deepcopy return self.__class__(deepcopy(self.items(), memo), self.strict) ### Read-only methods ### def copy(self): """ >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() OrderedDict([(1, 3), (3, 2), (2, 1)]) """ return OrderedDict(self) def items(self): """ ``items`` returns a list of tuples representing all the ``(key, value)`` pairs in the dictionary. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.items() [(1, 3), (3, 2), (2, 1)] >>> d.clear() >>> d.items() [] """ return zip(self._sequence, self.values()) def keys(self): """ Return a list of keys in the ``OrderedDict``. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.keys() [1, 3, 2] """ return self._sequence[:] def values(self, values=None): """ Return a list of all the values in the OrderedDict. Optionally you can pass in a list of values, which will replace the current list. The value list must be the same len as the OrderedDict. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.values() [3, 2, 1] """ return [self[key] for key in self._sequence] def iteritems(self): """ >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() >>> ii.next() (1, 3) >>> ii.next() (3, 2) >>> ii.next() (2, 1) >>> ii.next() Traceback (most recent call last): StopIteration """ def make_iter(self=self): keys = self.iterkeys() while True: key = keys.next() yield (key, self[key]) return make_iter() def iterkeys(self): """ >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() >>> ii.next() 1 >>> ii.next() 3 >>> ii.next() 2 >>> ii.next() Traceback (most recent call last): StopIteration """ return iter(self._sequence) __iter__ = iterkeys def itervalues(self): """ >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() >>> iv.next() 3 >>> iv.next() 2 >>> iv.next() 1 >>> iv.next() Traceback (most recent call last): StopIteration """ def make_iter(self=self): keys = self.iterkeys() while True: yield self[keys.next()] return make_iter() ### Read-write methods ### def clear(self): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.clear() >>> d OrderedDict([]) """ dict.clear(self) self._sequence = [] def pop(self, key, *args): """ No dict.pop in Python 2.2, gotta reimplement it >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.pop(3) 2 >>> d OrderedDict([(1, 3), (2, 1)]) >>> d.pop(4) Traceback (most recent call last): KeyError: 4 >>> d.pop(4, 0) 0 >>> d.pop(4, 0, 1) Traceback (most recent call last): TypeError: pop expected at most 2 arguments, got 3 """ if len(args) > 1: raise TypeError, ('pop expected at most 2 arguments, got %s' % (len(args) + 1)) if key in self: val = self[key] del self[key] else: try: val = args[0] except IndexError: raise KeyError(key) return val def popitem(self, i=-1): """ Delete and return an item specified by index, not a random one as in dict. The index is -1 by default (the last item). >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.popitem() (2, 1) >>> d OrderedDict([(1, 3), (3, 2)]) >>> d.popitem(0) (1, 3) >>> OrderedDict().popitem() Traceback (most recent call last): KeyError: 'popitem(): dictionary is empty' >>> d.popitem(2) Traceback (most recent call last): IndexError: popitem(): index 2 not valid """ if not self._sequence: raise KeyError('popitem(): dictionary is empty') try: key = self._sequence[i] except IndexError: raise IndexError('popitem(): index %s not valid' % i) return (key, self.pop(key)) def setdefault(self, key, defval = None): """ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.setdefault(1) 3 >>> d.setdefault(4) is None True >>> d OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) >>> d.setdefault(5, 0) 0 >>> d OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) """ if key in self: return self[key] else: self[key] = defval return defval def update(self, from_od): """ Update from another OrderedDict or sequence of (key, value) pairs >>> d = OrderedDict(((1, 0), (0, 1))) >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) >>> d OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)]) >>> d.update({4: 4}) Traceback (most recent call last): TypeError: undefined order, cannot get items from dict >>> d.update((4, 4)) Traceback (most recent call last): TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence """ if isinstance(from_od, OrderedDict): for key, val in from_od.items(): self[key] = val elif isinstance(from_od, dict): # we lose compatibility with other ordered dict types this way raise TypeError('undefined order, cannot get items from dict') else: # FIXME: efficiency? # sequence of 2-item sequences, or error for item in from_od: try: key, val = item except TypeError: raise TypeError('cannot convert dictionary update' ' sequence element "%s" to a 2-item sequence' % item) self[key] = val def rename(self, old_key, new_key): """ Rename the key for a given value, without modifying sequence order. For the case where new_key already exists this raise an exception, since if new_key exists, it is ambiguous as to what happens to the associated values, and the position of new_key in the sequence. >>> od = OrderedDict() >>> od['a'] = 1 >>> od['b'] = 2 >>> od.items() [('a', 1), ('b', 2)] >>> od.rename('b', 'c') >>> od.items() [('a', 1), ('c', 2)] >>> od.rename('c', 'a') Traceback (most recent call last): ValueError: New key already exists: 'a' >>> od.rename('d', 'b') Traceback (most recent call last): KeyError: 'd' """ if new_key == old_key: # no-op return if new_key in self: raise ValueError("New key already exists: %r" % new_key) # rename sequence entry value = self[old_key] old_idx = self._sequence.index(old_key) self._sequence[old_idx] = new_key # rename internal dict entry dict.__delitem__(self, old_key) dict.__setitem__(self, new_key, value) def setitems(self, items): """ This method allows you to set the items in the dict. It takes a list of tuples - of the same sort returned by the ``items`` method. >>> d = OrderedDict() >>> d.setitems(((3, 1), (2, 3), (1, 2))) >>> d OrderedDict([(3, 1), (2, 3), (1, 2)]) """ self.clear() # FIXME: this allows you to pass in an OrderedDict as well :-) self.update(items) def setkeys(self, keys): """ ``setkeys`` all ows you to pass in a new list of keys which will replace the current set. This must contain the same set of keys, but need not be in the same order. If you pass in new keys that don't match, a ``KeyError`` will be raised. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.keys() [1, 3, 2] >>> d.setkeys((1, 2, 3)) >>> d OrderedDict([(1, 3), (2, 1), (3, 2)]) >>> d.setkeys(['a', 'b', 'c']) Traceback (most recent call last): KeyError: 'Keylist is not the same as current keylist.' """ # FIXME: Efficiency? (use set for Python 2.4 :-) # NOTE: list(keys) rather than keys[:] because keys[:] returns # a tuple, if keys is a tuple. kcopy = list(keys) kcopy.sort() self._sequence.sort() if kcopy != self._sequence: raise KeyError('Keylist is not the same as current keylist.') # NOTE: This makes the _sequence attribute a new object, instead # of changing it in place. # FIXME: efficiency? self._sequence = list(keys) def setvalues(self, values): """ You can pass in a list of values, which will replace the current list. The value list must be the same len as the OrderedDict. (Or a ``ValueError`` is raised.) >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.setvalues((1, 2, 3)) >>> d OrderedDict([(1, 1), (3, 2), (2, 3)]) >>> d.setvalues([6]) Traceback (most recent call last): ValueError: Value list is not the same length as the OrderedDict. """ if len(values) != len(self): # FIXME: correct error to raise? raise ValueError('Value list is not the same length as the ' 'OrderedDict.') self.update(zip(self, values)) ### Sequence Methods ### def index(self, key): """ Return the position of the specified key in the OrderedDict. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.index(3) 1 >>> d.index(4) Traceback (most recent call last): ValueError: list.index(x): x not in list """ return self._sequence.index(key) def insert(self, index, key, value): """ Takes ``index``, ``key``, and ``value`` as arguments. Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in the OrderedDict. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.insert(0, 4, 0) >>> d OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) >>> d.insert(0, 2, 1) >>> d OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) >>> d.insert(8, 8, 1) >>> d OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) """ if key in self: # FIXME: efficiency? del self[key] self._sequence.insert(index, key) dict.__setitem__(self, key, value) def reverse(self): """ Reverse the order of the OrderedDict. >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) >>> d.reverse() >>> d OrderedDict([(2, 1), (3, 2), (1, 3)]) """ self._sequence.reverse() def sort(self, *args, **kwargs): """ Sort the key order in the OrderedDict. This method takes the same arguments as the ``list.sort`` method on your version of Python. >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) >>> d.sort() >>> d OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) """ self._sequence.sort(*args, **kwargs) class Keys(object): # FIXME: should this object be a subclass of list? """ Custom object for accessing the keys of an OrderedDict. Can be called like the normal ``OrderedDict.keys`` method, but also supports indexing and sequence methods. """ def __init__(self, main): self._main = main def __call__(self): """Pretend to be the keys method.""" return self._main._keys() def __getitem__(self, index): """Fetch the key at position i.""" # NOTE: this automatically supports slicing :-) return self._main._sequence[index] def __setitem__(self, index, name): """ You cannot assign to keys, but you can do slice assignment to re-order them. You can only do slice assignment if the new set of keys is a reordering of the original set. """ if isinstance(index, types.SliceType): # FIXME: efficiency? # check length is the same indexes = range(len(self._main._sequence))[index] if len(indexes) != len(name): raise ValueError('attempt to assign sequence of size %s ' 'to slice of size %s' % (len(name), len(indexes))) # check they are the same keys # FIXME: Use set old_keys = self._main._sequence[index] new_keys = list(name) old_keys.sort() new_keys.sort() if old_keys != new_keys: raise KeyError('Keylist is not the same as current keylist.') orig_vals = [self._main[k] for k in name] del self._main[index] vals = zip(indexes, name, orig_vals) vals.sort() for i, k, v in vals: if self._main.strict and k in self._main: raise ValueError('slice assignment must be from ' 'unique keys') self._main.insert(i, k, v) else: raise ValueError('Cannot assign to keys') ### following methods pinched from UserList and adapted ### def __repr__(self): return repr(self._main._sequence) # FIXME: do we need to check if we are comparing with another ``Keys`` # object? (like the __cast method of UserList) def __lt__(self, other): return self._main._sequence < other def __le__(self, other): return self._main._sequence <= other def __eq__(self, other): return self._main._sequence == other def __ne__(self, other): return self._main._sequence != other def __gt__(self, other): return self._main._sequence > other def __ge__(self, other): return self._main._sequence >= other # FIXME: do we need __cmp__ as well as rich comparisons? def __cmp__(self, other): return cmp(self._main._sequence, other) def __contains__(self, item): return item in self._main._sequence def __len__(self): return len(self._main._sequence) def __iter__(self): return self._main.iterkeys() def count(self, item): return self._main._sequence.count(item) def index(self, item, *args): return self._main._sequence.index(item, *args) def reverse(self): self._main._sequence.reverse() def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds) def __mul__(self, n): return self._main._sequence*n __rmul__ = __mul__ def __add__(self, other): return self._main._sequence + other def __radd__(self, other): return other + self._main._sequence ## following methods not implemented for keys ## def __delitem__(self, i): raise TypeError('Can\'t delete items from keys') def __iadd__(self, other): raise TypeError('Can\'t add in place to keys') def __imul__(self, n): raise TypeError('Can\'t multiply keys in place') def append(self, item): raise TypeError('Can\'t append items to keys') def insert(self, i, item): raise TypeError('Can\'t insert items into keys') def pop(self, i=-1): raise TypeError('Can\'t pop items from keys') def remove(self, item): raise TypeError('Can\'t remove items from keys') def extend(self, other): raise TypeError('Can\'t extend keys') class Items(object): """ Custom object for accessing the items of an OrderedDict. Can be called like the normal ``OrderedDict.items`` method, but also supports indexing and sequence methods. """ def __init__(self, main): self._main = main def __call__(self): """Pretend to be the items method.""" return self._main._items() def __getitem__(self, index): """Fetch the item at position i.""" if isinstance(index, types.SliceType): # fetching a slice returns an OrderedDict return self._main[index].items() key = self._main._sequence[index] return (key, self._main[key]) def __setitem__(self, index, item): """Set item at position i to item.""" if isinstance(index, types.SliceType): # NOTE: item must be an iterable (list of tuples) self._main[index] = OrderedDict(item) else: # FIXME: Does this raise a sensible error? orig = self._main.keys[index] key, value = item if self._main.strict and key in self and (key != orig): raise ValueError('slice assignment must be from ' 'unique keys') # delete the current one del self._main[self._main._sequence[index]] self._main.insert(index, key, value) def __delitem__(self, i): """Delete the item at position i.""" key = self._main._sequence[i] if isinstance(i, types.SliceType): for k in key: # FIXME: efficiency? del self._main[k] else: del self._main[key] ### following methods pinched from UserList and adapted ### def __repr__(self): return repr(self._main.items()) # FIXME: do we need to check if we are comparing with another ``Items`` # object? (like the __cast method of UserList) def __lt__(self, other): return self._main.items() < other def __le__(self, other): return self._main.items() <= other def __eq__(self, other): return self._main.items() == other def __ne__(self, other): return self._main.items() != other def __gt__(self, other): return self._main.items() > other def __ge__(self, other): return self._main.items() >= other def __cmp__(self, other): return cmp(self._main.items(), other) def __contains__(self, item): return item in self._main.items() def __len__(self): return len(self._main._sequence) # easier :-) def __iter__(self): return self._main.iteritems() def count(self, item): return self._main.items().count(item) def index(self, item, *args): return self._main.items().index(item, *args) def reverse(self): self._main.reverse() def sort(self, *args, **kwds): self._main.sort(*args, **kwds) def __mul__(self, n): return self._main.items()*n __rmul__ = __mul__ def __add__(self, other): return self._main.items() + other def __radd__(self, other): return other + self._main.items() def append(self, item): """Add an item to the end.""" # FIXME: this is only append if the key isn't already present key, value = item self._main[key] = value def insert(self, i, item): key, value = item self._main.insert(i, key, value) def pop(self, i=-1): key = self._main._sequence[i] return (key, self._main.pop(key)) def remove(self, item): key, value = item try: assert value == self._main[key] except (KeyError, AssertionError): raise ValueError('ValueError: list.remove(x): x not in list') else: del self._main[key] def extend(self, other): # FIXME: is only a true extend if none of the keys already present for item in other: key, value = item self._main[key] = value def __iadd__(self, other): self.extend(other) ## following methods not implemented for items ## def __imul__(self, n): raise TypeError('Can\'t multiply items in place') class Values(object): """ Custom object for accessing the values of an OrderedDict. Can be called like the normal ``OrderedDict.values`` method, but also supports indexing and sequence methods. """ def __init__(self, main): self._main = main def __call__(self): """Pretend to be the values method.""" return self._main._values() def __getitem__(self, index): """Fetch the value at position i.""" if isinstance(index, types.SliceType): return [self._main[key] for key in self._main._sequence[index]] else: return self._main[self._main._sequence[index]] def __setitem__(self, index, value): """ Set the value at position i to value. You can only do slice assignment to values if you supply a sequence of equal length to the slice you are replacing. """ if isinstance(index, types.SliceType): keys = self._main._sequence[index] if len(keys) != len(value): raise ValueError('attempt to assign sequence of size %s ' 'to slice of size %s' % (len(name), len(keys))) # FIXME: efficiency? Would be better to calculate the indexes # directly from the slice object # NOTE: the new keys can collide with existing keys (or even # contain duplicates) - these will overwrite for key, val in zip(keys, value): self._main[key] = val else: self._main[self._main._sequence[index]] = value ### following methods pinched from UserList and adapted ### def __repr__(self): return repr(self._main.values()) # FIXME: do we need to check if we are comparing with another ``Values`` # object? (like the __cast method of UserList) def __lt__(self, other): return self._main.values() < other def __le__(self, other): return self._main.values() <= other def __eq__(self, other): return self._main.values() == other def __ne__(self, other): return self._main.values() != other def __gt__(self, other): return self._main.values() > other def __ge__(self, other): return self._main.values() >= other def __cmp__(self, other): return cmp(self._main.values(), other) def __contains__(self, item): return item in self._main.values() def __len__(self): return len(self._main._sequence) # easier :-) def __iter__(self): return self._main.itervalues() def count(self, item): return self._main.values().count(item) def index(self, item, *args): return self._main.values().index(item, *args) def reverse(self): """Reverse the values""" vals = self._main.values() vals.reverse() # FIXME: efficiency self[:] = vals def sort(self, *args, **kwds): """Sort the values.""" vals = self._main.values() vals.sort(*args, **kwds) self[:] = vals def __mul__(self, n): return self._main.values()*n __rmul__ = __mul__ def __add__(self, other): return self._main.values() + other def __radd__(self, other): return other + self._main.values() ## following methods not implemented for values ## def __delitem__(self, i): raise TypeError('Can\'t delete items from values') def __iadd__(self, other): raise TypeError('Can\'t add in place to values') def __imul__(self, n): raise TypeError('Can\'t multiply values in place') def append(self, item): raise TypeError('Can\'t append items to values') def insert(self, i, item): raise TypeError('Can\'t insert items into values') def pop(self, i=-1): raise TypeError('Can\'t pop items from values') def remove(self, item): raise TypeError('Can\'t remove items from values') def extend(self, other): raise TypeError('Can\'t extend values') class SequenceOrderedDict(OrderedDict): """ Experimental version of OrderedDict that has a custom object for ``keys``, ``values``, and ``items``. These are callable sequence objects that work as methods, or can be manipulated directly as sequences. Test for ``keys``, ``items`` and ``values``. >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) >>> d SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) >>> d.keys [1, 2, 3] >>> d.keys() [1, 2, 3] >>> d.setkeys((3, 2, 1)) >>> d SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) >>> d.setkeys((1, 2, 3)) >>> d.keys[0] 1 >>> d.keys[:] [1, 2, 3] >>> d.keys[-1] 3 >>> d.keys[-2] 2 >>> d.keys[0:2] = [2, 1] >>> d SequenceOrderedDict([(2, 3), (1, 2), (3, 4)]) >>> d.keys.reverse() >>> d.keys [3, 1, 2] >>> d.keys = [1, 2, 3] >>> d SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) >>> d.keys = [3, 1, 2] >>> d SequenceOrderedDict([(3, 4), (1, 2), (2, 3)]) >>> a = SequenceOrderedDict() >>> b = SequenceOrderedDict() >>> a.keys == b.keys 1 >>> a['a'] = 3 >>> a.keys == b.keys 0 >>> b['a'] = 3 >>> a.keys == b.keys 1 >>> b['b'] = 3 >>> a.keys == b.keys 0 >>> a.keys > b.keys 0 >>> a.keys < b.keys 1 >>> 'a' in a.keys 1 >>> len(b.keys) 2 >>> 'c' in d.keys 0 >>> 1 in d.keys 1 >>> [v for v in d.keys] [3, 1, 2] >>> d.keys.sort() >>> d.keys [1, 2, 3] >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True) >>> d.keys[::-1] = [1, 2, 3] >>> d SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) >>> d.keys[:2] [3, 2] >>> d.keys[:2] = [1, 3] Traceback (most recent call last): KeyError: 'Keylist is not the same as current keylist.' >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) >>> d SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) >>> d.values [2, 3, 4] >>> d.values() [2, 3, 4] >>> d.setvalues((4, 3, 2)) >>> d SequenceOrderedDict([(1, 4), (2, 3), (3, 2)]) >>> d.values[::-1] [2, 3, 4] >>> d.values[0] 4 >>> d.values[-2] 3 >>> del d.values[0] Traceback (most recent call last): TypeError: Can't delete items from values >>> d.values[::2] = [2, 4] >>> d SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) >>> 7 in d.values 0 >>> len(d.values) 3 >>> [val for val in d.values] [2, 3, 4] >>> d.values[-1] = 2 >>> d.values.count(2) 2 >>> d.values.index(2) 0 >>> d.values[-1] = 7 >>> d.values [2, 3, 7] >>> d.values.reverse() >>> d.values [7, 3, 2] >>> d.values.sort() >>> d.values [2, 3, 7] >>> d.values.append('anything') Traceback (most recent call last): TypeError: Can't append items to values >>> d.values = (1, 2, 3) >>> d SequenceOrderedDict([(1, 1), (2, 2), (3, 3)]) >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) >>> d SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) >>> d.items() [(1, 2), (2, 3), (3, 4)] >>> d.setitems([(3, 4), (2 ,3), (1, 2)]) >>> d SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) >>> d.items[0] (3, 4) >>> d.items[:-1] [(3, 4), (2, 3)] >>> d.items[1] = (6, 3) >>> d.items [(3, 4), (6, 3), (1, 2)] >>> d.items[1:2] = [(9, 9)] >>> d SequenceOrderedDict([(3, 4), (9, 9), (1, 2)]) >>> del d.items[1:2] >>> d SequenceOrderedDict([(3, 4), (1, 2)]) >>> (3, 4) in d.items 1 >>> (4, 3) in d.items 0 >>> len(d.items) 2 >>> [v for v in d.items] [(3, 4), (1, 2)] >>> d.items.count((3, 4)) 1 >>> d.items.index((1, 2)) 1 >>> d.items.index((2, 1)) Traceback (most recent call last): ValueError: list.index(x): x not in list >>> d.items.reverse() >>> d.items [(1, 2), (3, 4)] >>> d.items.reverse() >>> d.items.sort() >>> d.items [(1, 2), (3, 4)] >>> d.items.append((5, 6)) >>> d.items [(1, 2), (3, 4), (5, 6)] >>> d.items.insert(0, (0, 0)) >>> d.items [(0, 0), (1, 2), (3, 4), (5, 6)] >>> d.items.insert(-1, (7, 8)) >>> d.items [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)] >>> d.items.pop() (5, 6) >>> d.items [(0, 0), (1, 2), (3, 4), (7, 8)] >>> d.items.remove((1, 2)) >>> d.items [(0, 0), (3, 4), (7, 8)] >>> d.items.extend([(1, 2), (5, 6)]) >>> d.items [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)] """ def __init__(self, init_val=(), strict=True): OrderedDict.__init__(self, init_val, strict=strict) self._keys = self.keys self._values = self.values self._items = self.items self.keys = Keys(self) self.values = Values(self) self.items = Items(self) self._att_dict = { 'keys': self.setkeys, 'items': self.setitems, 'values': self.setvalues, } def __setattr__(self, name, value): """Protect keys, items, and values.""" if not '_att_dict' in self.__dict__: object.__setattr__(self, name, value) else: try: fun = self._att_dict[name] except KeyError: OrderedDict.__setattr__(self, name, value) else: fun(value) if __name__ == '__main__': if INTP_VER < (2, 3): raise RuntimeError("Tests require Python v.2.3 or later") # turn off warnings for tests warnings.filterwarnings('ignore') # run the code tests in doctest format import doctest m = sys.modules.get('__main__') globs = m.__dict__.copy() globs.update({ 'INTP_VER': INTP_VER, }) doctest.testmod(m, globs=globs) rest2web-0.5.2~alpha+svn-r248.orig/rest2web/restprocessor.py0000600000175000017500000021722210575567255023562 0ustar madduckmadduck# restprocessor.py # Subversion Details # $LastChangedDate: 2007-03-13 19:34:21 +0100 (Tue, 13 Mar 2007) $ # $LastChangedBy: fuzzyman $ # $HeadURL: https://svn.rest2web.python-hosting.com/trunk/rest2web/restprocessor.py $ # $LastChangedRevision: 236 $ # The processor object (etc) for rest2web # This builds the pages and indexes from content and templates. # http://www.voidspace.org.uk/python/rest2web/ # Copyright Michael Foord, 2004 - 2006. # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # For information about bugfixes, updates and support, please join the # rest2web mailing list. # http://lists.sourceforge.net/lists/listinfo/rest2web-develop # Comments, suggestions and bug reports welcome. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # E-mail fuzzyman@voidspace.org.uk import glob import os import sys import time import datetime from copy import deepcopy from urllib import url2pathname from shutil import copy2 from traceback import print_exc from posixpath import split as posixsplit from StringIO import StringIO from rest2web import functions from rest2web import textmacros from rest2web.restutils import * from rest2web.restindex import read_restindex, default_keywords, default_restindex from rest2web.restindex import parse_user_values from rest2web.embedded_code import render_well from rest2web.printing import WARN, ACTION, INFO, out from rest2web import printing from docutils import ApplicationError from rest2web.pythonutils.odict import OrderedDict from rest2web.pythonutils.pathutils import import_path from rest2web.pythonutils.urlpath import pathjoin, relpathto, tslash from rest2web.pythonutils.cgiutils import istrue import rest2web.defaultmacros as macros class rest2webError(Exception): """ """ class NoRestIndex(Exception): """ """ isfile = os.path.isfile isdir = os.path.isdir split = os.path.split dirname = os.path.dirname splitext = os.path.splitext abspath = os.path.abspath def join(*args): return os.path.normpath(os.path.join(*args)) # We assume os.path.splitext is cross platform :-) __all__ = ('Processor', 'handle_sections', 'rest2webError', 'set_print') globalValues = {} try: sorted except NameError: def sorted(inlist): newlist = list(inlist) newlist.sort() return newlist class SectionDict(OrderedDict): def sortpages(self, sortorder, section=True): if sortorder == 'link-title': sortorder = lambda x, y : cmp(x['link-title'], y['link-title']) if section == True: sections = self.values() else: sections = [self[section]] for section in sections: section['pages'].sort(sortorder) ########################################## def add_modules(ns): """Add a couple of modules to a namespace.""" ns['os'] = os ns['sys'] = sys ns['globalValues'] = globalValues ########################################## class Processor(object): """A processor object for building pages from content and templates.""" def __init__(self, options, config): """Initialise the processor.""" self.dir = os.curdir self.dir_as_list = [] self.dir_as_url = '/' self.crumbdict = {} self.indextree = {} self.breadcrumbs = [] self.templatedict = {} self.outputencoding = {} self.plugins = {} self.force = False self._template_files = {} self.skip_html = False self.last_error = None # self.get_config(config) self.handle_options(options) if self.force: # load the default index file out('Force on - loading default index and template.', INFO) def_dir = config['defaults_directory'] self._default_index = open(join(def_dir, 'index.txt')).read() self._default_template = join(def_dir, 'template.txt') self.skip_html = istrue(config.get('skip_html', 'False')) # # FIXME: This is hardwired self._top_level_name = 'Site' def get_config(self, config): """Handle configuration options from config file.""" start_directory = os.path.expanduser(config['start_directory']) target_directory = os.path.expanduser(config['target_directory']) compare_directory = os.path.expanduser(config['compare_directory']) or target_directory macrofile = os.path.expanduser(config['macros']) debug = istrue(config.get('DEBUG', 'False')) uservalues = config.get('uservalues') if uservalues is None: uservalues = {} else: uservalues = uservalues.dict() try: # FIXME: allow a 'guess' value ? encoding = uservalues.pop('__encoding__') except KeyError: encoding = 'ascii' # FIXME: WIll this choke on list and dict (subsection) values ? uservalues = decode(uservalues, encoding) ## hash = config.get('hash') or None # self.start = abspath(start_directory) self.target = abspath(target_directory) self.compare = abspath(compare_directory) self.defaults = deepcopy(default_restindex) self.macrofile = macrofile self.config = config self.uservalues = uservalues or {} self.debug = debug self.macro_paths = { 'smiley_directory': None, 'smiley_url': '<% path_to_root %>images/smilies/', 'emoticon_url': '<% path_to_root %>images/', } self.macro_paths.update(config.get('Macro Paths', {})) self.macro_paths['smiley_directory'] = self.macro_paths['smiley_directory'] or None self.force = istrue(config.get('force', 'False')) or istrue(config.get('Force', 'False')) self.skiperrors = istrue(config.get('skiperrors', 'False')) self.promote_headers = istrue(config.get('promote_headers', 'True')) def handle_options(self, options): """Handle command line optins.""" self.override_template = options['template'] if options['uservalues']: # Command line uservalues override config file ones self.uservalues.update(options['uservalues']) # Set self.force if set at the command line self.force = self.force or options['force'] self.skiperrors = self.skiperrors or options['skiperrors'] def setmacros(self, macrofile): """ Set the macro related attributes. Import the macros module if one is specified. """ self.macros = macros.__dict__ self.macros.update(self.macro_paths) self.macrodir = None if not macrofile: self.macros['acronyms'] = self.macros['default_acronyms'] return elif not isfile(macrofile): raise RuntimeError('macros file can\'t be found "%s".' % macrofile) else: try: mod = import_path(abspath(macrofile)) except ImportError: # Should this be a fatal error ? raise RuntimeError('Error importing macros file "%s".' % macrofile) else: self.macros.update(mod.__dict__) self.macros['default_acronyms'].update(self.macros.get('acronyms', {})) self.macrodir = abspath(dirname(macrofile)) def execute_safely(self, function, *args, **keywargs): try: val = function(*args, **keywargs) except KeyboardInterrupt: raise except Exception, e: if not self.skiperrors: raise # print any error without bombing out # (so we can display it, then close our files nicely) f = StringIO() print_exc(file=f) # write error to sys.stderr rather than printing to sys.stdout self.last_error = f.getvalue() printing.ERROR_STATUS += 1 return False else: self.last_error = None return val def output_error(self, msg): out(msg + '\n' + self.last_error + '\n', WARN) def copy_file(self, entry, filename, target, index=False): """Copy files from the ``file`` keyword.""" entry = os.path.expanduser(entry) if not index: src = join(self.dir, dirname(filename), entry) else: # The directory will already have been added for an index src = join(dirname(filename), entry) if not os.path.isfile(src): out(('File "%s" referenced by file keyword missing.' % entry), WARN) return # target_file_name = url2pathname(target) targetfile = join(self.target, self.dir, target_file_name) dest = join(dirname(targetfile), split(entry)[1]) comparefile = join(self.compare, self.dir, target_file_name) cmp = join(dirname(comparefile), split(entry)[1]) if comparefiles(src, cmp): out('File "%s" identical, skipping.' % entry, INFO) return if not isdir(dirname(dest)): os.makedirs(dirname(dest)) out('Copying "%s".' % entry, ACTION) copy2(src, dest) def copy_filemask(self, filemasks, nofiles, filename, target, index=False): # below needs testing thoroughly :-) if not index: srcdir = join(self.dir, dirname(filename)) else: srcdir = dirname(filename) # target_file_name = url2pathname(target) targetfile = join(self.target, self.dir, target_file_name) target_dir = dirname(targetfile) # nofiles = [os.path.abspath(os.path.join(srcdir, os.path.expanduser(pathName))) for pathName in nofiles] for mask in filemasks: mask = os.path.abspath(os.path.join(srcdir, os.path.expanduser(mask))) files = [entry for entry in glob.glob(mask) if entry not in nofiles] # for entry in files: dest = join(target_dir, split(entry)[1]) comparefile = join(self.compare, self.dir, target_file_name) cmp = join(dirname(comparefile), split(entry)[1]) if comparefiles(src, cmp): out('File "%s" identical, skipping.' % entry, INFO) else: if not isdir(dirname(dest)): os.makedirs(dirname(dest)) out('Copying "%s".' % entry, ACTION) copy2(src, dest) def walk(self): """ Walk the directory tree from the start directory. And build the site as we go. """ # # The macro file needs to be located *before* we change directory # (it might be a relative path) self.setmacros(self.macrofile) # # we'll move to 'start_directory' # all future URLs will be relative to this old_dir = abspath(os.getcwd()) os.chdir(self.start) # # start in the 'root' directory # (We treat directories as lists, making it easier # to go up and down the tree, turning it into # a file path or a URL path as we go - so the root directory # is represented as an empty list) dir_stack = [ [] ] out('Starting in "%s" directory.' % self.start, INFO) # # we walk the directory tree - processing a directory at a time count = 0 toplevel = True while dir_stack: self.changed = False out('', INFO) # next directory to do self.dir_as_list = dir_stack.pop() # # a file path - relative to root directory self.dir = os.sep.join(self.dir_as_list) # # a url path using '/', ending with '/' self.dir_as_url = tslash('/'.join(self.dir_as_list)) # out('Processing "%s" directory.' % (self.dir or 'root'), INFO) # # details from every page that will appear in the index self.this_section = [] # contents of this directory dir_list = os.listdir(self.dir or os.curdir) # if '__prune__' in dir_list: continue # if not dir_list: # Skip empty directories continue # if self.skip_html and not self.dir: dir_list = [entry for entry in dir_list if not entry.lower() == 'html'] # if ('index.txt' not in dir_list) and not self.force: # handle directories that we don't process # subdirectories are added to the dir_stack dir_stack += self.emptydir(dir_list) continue # # set the default values for this directory # these can be set in the restindex of the index page. # remove the index page from the list # (the index page *might* not be 'index.txt') # In the process it retrieves a lot of the info about the # index page. # FIXME: If an index file has no restindex - this call bombs out # should it be a warning instead ? errorcheck = self.execute_safely(self.setdefaults, dir_list, toplevel) if errorcheck == False: self.output_error('Error in processing index details of directory: "%s"' % self.dir) continue # # if restindex.txt exists it has special meaning # it is a series of restindexes for pages # we're not building, but are including in the indexes if 'restindex.txt' in dir_list: dir_list.remove('restindex.txt') thisentry = join(self.dir, 'restindex.txt') out('Reading "%s".' % thisentry, INFO) thefile = open(thisentry).read() while thefile: # read the top restindex def ProcessNextRestindex(thefile): restindex, thefile = read_restindex(thefile.split('\n')) if restindex is None: return None # # we never build from 'restindex.txt' restindex['build'] = 'no' data = self.process(restindex, '', 'restindex.txt') self.this_section.append(data) return thefile # thefile = self.execute_safely(ProcessNextRestindex, thefile) if thefile == False: self.output_error('Error in processing the file: "%s"' % thisentry) break if thefile is None: break # count += 1 # # process every file in this directory for entry in dir_list: # skip '.svn' directories if entry == '.svn': continue # a file path thisentry = join(self.dir, entry) subdir = False # we only process files that end with '.txt' if isfile(thisentry) and not entry.endswith('.txt'): # FIXME: allow '.rst' files ? continue elif not isfile(thisentry) and not isdir(thisentry): # can this happen ? # not on windows ! continue elif isdir(thisentry): # skip pruned subdirectories if isfile(join(thisentry, '__prune__')): continue # creates a *new* list and adds it to dir_stack dir_stack.append(self.dir_as_list + [entry]) # # we only need to process subdirs with an 'index.txt' # to include them in the index if needed thisentry = join(thisentry, 'index.txt') if not isfile(thisentry) and not self.force: continue else: subdir = True entry = join(entry, 'index.txt') else: # we only add to the count the *files* in # the directory count += 1 # out('Reading "%s".' % thisentry, INFO) # def ProcessFile(): try: restindex, content, filename = self.get_real_restindex( entry, subdir=subdir) except NoRestIndex: # there is nothing to process return True else: data = self.process(restindex, content, filename, subdir=subdir) # add this page to the current section self.this_section.append(data) return True # errorcheck = self.execute_safely(ProcessFile) if errorcheck == False: self.output_error('Error in processing the file: "%s"' % thisentry) continue # out('Processing indexfile.', INFO) # build the index page with details from ``setdefaults`` count += 1 toplevel = False # errorcheck = self.execute_safely(self.processindex) if errorcheck == False: self.output_error('Error in processing the index file for directory: "%s"' % self.dir) continue # then build the pages # errorcheck = self.execute_safely(self.buildsection) if errorcheck == False: self.output_error('Error whilst building pages for directory: "%s"' % self.dir) continue # # restore previous directory os.chdir(old_dir) return count def emptydir(self, dir_list): """ If a directory has no "index.txt" there are certain things to be taken care of - so that subdirectories can find the right crumb for example. Returns any sub-directories which should be scanned. This sets appropriate values in : templatedict outputencoding crumbdict indextree """ dir_stack = [] # we don't need to do this directory - FIXME: case issues? # but we need to make sure all the subdirectories get scanned # FIXME: do we really want to recurse into subdirectories ? for entry in dir_list: # ignore the repository directories if entry == '.svn': continue if isdir(join(self.dir, entry)): # We want to add any subdirectories to the stack # creates a *new* list and adds it to dir_stack dir_stack.append(self.dir_as_list + [entry]) # if not self.dir: # if 'template.txt' doesn't exist it is handled elsewhere self.templatedict[self.dir] = ( abspath('template.txt'), None) self.outputencoding[self.dir] = 'None' else: self.templatedict[self.dir] = self.templatedict[ dirname(self.dir)] self.outputencoding[self.dir] = self.outputencoding[ dirname(self.dir)] # no crumb for the current directory self.crumbdict[self.dir] = None self.indextree[self.dir] = None # return dir_stack def provide_default_index(self, filename=None): """Provide defaults for a missing index page.""" if filename is None: page_title = split(self.dir.rstrip('/\\'))[1] else: page_title = splitext(split(filename)[1])[0].replace('_', ' ').title() content = self._default_index restindex = { 'page-title': 'Index for %s' % (page_title or self._top_level_name), 'format': 'rest' } return restindex, content def setdefaults(self, thelist, toplevel): """ Set the default values for this directory. This reads default values from 'index.txt'. *Some* of these may be used for other entries in the directory. (For example members of 'default_keywords', the 'crumb', the template used for this directory, etc) This method is called once per directory, before any of the files are processed. It does the following things: remove 'index.txt' *and* the real index file from ``thelist`` read the restindex from the index file set the relevant default values for the directory set the crumb values for the directory (which will be the same for every file in the directory) set the default template for this directory set the default output encoding for this directory returns the information it has obtained about the index page: (index_page, indexfile, restindex, content, target, encoding, final_encoding) Because we resolve crumbs here we save them as unicode. This means we have to detect encoding of the index page. """ # first we must get the appropriate restindex # this means finding the right index file indexfile = join(self.dir, 'index.txt') if 'index.txt' in thelist: thelist.remove('index.txt') restindex, content = read_restindex(open(indexfile)) else: # FIXME - provide defaults restindex, content = self.provide_default_index() # FIXME: validate the returned restindex for sanity ? if restindex is None: if self.force: restindex, _ = self.provide_default_index() else: # FIXME: Should this be a warning ? (Probably is a fatal error.) raise rest2webError('index file has no restindex !') # # the restindex may specify an alternative file as the index file index_page = restindex.get('index-file', 'index.txt') if index_page != 'index.txt': # it *might* not be in this directory (usually will be though) if index_page in thelist: # FIXME: what if they use some syntax like './filename' ? thelist.remove(index_page) # turn the unix relative path into a native path indexfile = join(self.dir, url2pathname(index_page)) # the real restindex for this directory # FIXME: trap potential IOError here ? restindex, content = read_restindex(open(indexfile)) if restindex is None: if self.force: restindex, _ = self.provide_default_index(indexfile) else: # FIXME: Should this be a warning ? (Probably is a fatal error.) raise rest2webError('index file has no restindex !') # # retrieve any uservalues from after the restindex uservalues, content = parse_user_values( content.split('\n'), dirname(indexfile)) uservalues = uservalues or {} # # indexfile is absolute file path # index_page is relative location in unix/url format native_index_dir = dirname(indexfile) # # set some default values for the whole directory for entry in default_keywords: # if the index page sets a default # otherwise use the one from default_restindex self.defaults[entry] = restindex.get( entry, default_restindex[entry]) # # Next we must find the template for this directory # this will be relative to index_dir # (if it is specified in the index file) # FIXME: seeing as we are reading the template file - we could cache it if toplevel and self.override_template: # NOTE: Override first template if specified at the command line template = self.override_template else: template = restindex.get('template') if template: # a template was specified temp_filename = join(native_index_dir, template) template_encoding = restindex.get('template-encoding') if not isfile(temp_filename): # template file specified but not found raise IOError('Template file - "%s" not found.' % temp_filename) if not template_encoding: template_encoding = guess_encoding( open(temp_filename).read())[1] else: # no template file specified # try 'template.txt' in this directory first temp_filename = join(self.dir, 'template.txt') if self.force and not isfile(temp_filename): if not dirname(self.dir) in self.templatedict: temp_filename = self._default_template if not isfile(temp_filename) and self.dir: # 'template.txt' doesn't exist # we'll use the template file # used by the directory above (temp_filename, template_encoding) = self.templatedict[ dirname(self.dir)] if template_encoding is None: template_encoding = guess_encoding( open(temp_filename).read())[1] if not isfile(temp_filename): # XXXX is this possible ? # (possibly if we don't build anything in the root # directory and a placeholder is inserted by # ``emptydir``) raise IOError('No template file found.') self.templatedict[dirname(self.dir)] = ( temp_filename, template_encoding) elif not isfile(temp_filename): # this is the root directory # and no 'template.txt' raise IOError('No template file found.') else: # specified template file exists template_encoding = restindex.get('template-encoding') if not template_encoding: template_encoding = guess_encoding( open(temp_filename).read())[1] # # set the template file we finally used for this directory # which will be the default for other pages in this directory # and subdirectories self.templatedict[self.dir] = ( abspath(temp_filename), template_encoding) # # the path to the file we're trying to build # will be relative to the index file target = restindex.get('target') # # if one isn't specified then we autocreate the name # using the 'file-extension' value from the restindex if not target: fileext = (self.defaults['file-extension'] or default_restindex['file-extension']) if not fileext.startswith('.'): fileext = '.' + fileext filename = posixsplit(index_page)[1] target_filename = splitext(filename)[0] + fileext # handle case when we have an index_page in another directory target = pathjoin(index_page, target_filename) else: target = pathjoin(index_page, target) # web location # # detect the encoding, so that we can find the crumb # and decode to unicode self.guessed = False encoding = restindex.get('encoding') if not encoding: encoding = guess_encoding(content)[1] self.guessed = True # # FIXME: uservalues from the page currently override the globals # is this the right way round ? global_uservals = enc_uni_dict(self.uservalues, encoding) global_uservals.update(uservalues) uservalues = global_uservals # # Plugins # XXXX should deal with encoding # XXXX indexing will need content # XXXX also global plugins for plugin in restindex.get('plugins', []): uservalues.update(self.do_plugin(plugin, indexfile, target, restindex, uservalues)) # crumblink = pathjoin(self.dir_as_url, target) crumb = restindex.get('crumb') if not crumb: # FIXME: would need to generate the page content (if rest) # to get title, which ought to be the default def_crumb = splitext(split(target)[1])[0].title() crumb = restindex.get('page-title', def_crumb) # unicode self.crumbdict[self.dir] = (crumblink, crumb.decode(encoding)) # # next generate the breadcrumbs for this directory # they will be the same for each file in the directory # we step up from the current directory, up to the root directory # getting the crumb for each step self.breadcrumbs = [] crumbling = self.dir while crumbling: thiscrumb = self.crumbdict.get(crumbling) if thiscrumb is not None: self.breadcrumbs.append(thiscrumb) crumbling = dirname(crumbling) rootcrumb = self.crumbdict.get('') if (rootcrumb is not None) and (rootcrumb not in self.breadcrumbs): self.breadcrumbs.append(rootcrumb) self.breadcrumbs.reverse() # # decode uservalues to unicode uservalues = uni_dict(uservalues, encoding) # # set the output encoding for this directory out_enc = restindex.get('output-encoding') if out_enc is None and self.dir: out_enc = self.outputencoding[dirname(self.dir)] elif out_enc is None: # the default out_enc = 'None' self.outputencoding[self.dir] = out_enc # # seeing as we've done all the work - we'll calculate # the final_encoding the index will use if out_enc.lower() == 'none': final_encoding = encoding elif out_enc == 'unicode': final_encoding = None else: final_encoding = out_enc if final_encoding and final_encoding.lower() == 'utf8': final_encoding = 'utf-8' # self.section_pages = restindex.get('section-pages', {}) for entry in self.section_pages: if entry not in restindex.get('sectionlist', []): if entry is None: # don't need to explicitly defien the default section continue raise rest2webError('section-pages value defined, that isn\'t' ' in the sectionlist - "%s"' % entry) # # having worked all this out, we might as well keep it around self.index_page = (index_page, indexfile, restindex, uservalues, content, target, encoding, final_encoding) return True def get_real_restindex(self, filename, subdir=False): """ Get the parsed restindex for a directory. This includes following the 'index-file' value for a subdirectory we are including in a section. Returns: restindex, content, filename (or None if there is no restindex) """ filepath = join(self.dir, filename) # this raises restindexError # a subclass of SyntaxError if not isfile(filepath) and self.force and subdir: restindex = { 'page-title': 'Index for %s' % splitext(split(filename)[0])[0] } content = '' else: restindex, content = read_restindex(open(filepath)) if restindex is None: # no restindex, no process :-) if not self.force or filename == 'template.txt': raise NoRestIndex else: restindex = {} # FIXME: validate the returned restindex for sanity # # filename is either a file only # or if ``subdir=True`` is a 'directory/filename' joined using # join # # need to special case ``subdir=True`` in case ``indexfile`` is set!!! # index-file value is specified as a relative unix path, # relative to self.dir if subdir: real_index = restindex.get('index-file') if real_index: thedir = dirname(filename) native_real_index = url2pathname(real_index) indexfile = join( self.start, self.dir, thedir, native_real_index) # the real restindex for this directory restindex, content = read_restindex(open(indexfile)) if restindex is None: restindex, _ = self.provide_default_index(indexfile) restindex['build'] = 'no' # this is now unix (URL) format filename = pathjoin(thedir + '/', real_index) else: # turn native to url format filename = '/'.join(split(filename)) # if not istrue(restindex.get('include', 'yes')): # shortcut if it's a subdir and we don't need to include it raise NoRestIndex # return (restindex, content, filename) # # FIXME: what if build *and* include are set to No ? # FIXME: we could short circuit this ? - but it's a dumb set of options ! def process(self, restindex, content, filename, subdir=False): """ Process a page - ready to be built. Return the data structure to add to the current section. If the file is an index file from a subdirectory then set ``subdir = True`` Most of the job is building the right values to put in the namespace for when rendering the final page, and also the entry in the index. A lot of this is dictated by the restindex. Some text is saved as unicode. This means that when the details of this page are referenced by other pages (i.e. building sidebars) they can be converted to the encoding expected by the other page. Other text will be stored in the ``final_encoding`` expected by the page when it is rendered. """ filepath = join(self.dir, filename) name = splitext(filename)[0] # # fill in any values missing from the restindex # but we need to preserve the *original* file-extension orig_file_ext = restindex.get('file-extension') for entry in self.defaults: if not restindex.has_key(entry): # use copy to avoid getting references restindex[entry] = deepcopy(self.defaults[entry]) # encoding = restindex['encoding'] if not encoding: encoding = guess_encoding(content)[1] out('Guessing encoding. We guessed: %s'% encoding, INFO) # # a relative url path target = restindex['target'] if not target: fileext = restindex['file-extension'] if subdir and not orig_file_ext: fileext = default_restindex['file-extension'] if not fileext.startswith('.'): fileext = '.' + fileext target = splitext(filename)[0] + fileext else: target = pathjoin(filename, target) # pagepath = pathjoin(self.dir_as_url, target) # (orig_uservalues, content) = parse_user_values( content.split('\n'), join(self.dir, dirname(filename))) orig_uservalues = orig_uservalues or {} # global_uservals = enc_uni_dict(self.uservalues, encoding) global_uservals.update(orig_uservalues) orig_uservalues = global_uservals # # Plugins # FIXME: should deal with encoding # FIXME: indexing will need content passing to plugin if not subdir: for plugin in restindex['plugins']: orig_uservalues.update(self.do_plugin(plugin, filepath, target, restindex, orig_uservalues)) # also copy files (from restindex) into same dir as target for entry in restindex['file']: self.copy_file(entry, filename, target) ##self.copy_filemask(restindex['filemask'], restindex['nofile'], filename, target) # # FIXME: what about values that are lists/dictionaries ? uservalues = uni_dict(orig_uservalues, encoding) # # build the body of the page # even if subdir is True we still build to obtain page title # FIXME: can we cache this - wasteful ? rest_dict = None title = restindex['page-title'] if title: title = unicode(title, encoding) # if we have any uservalues # render them into content # which *may* have come from a different file # if the 'body' value was used # NOTE: we have to *assume* that the uservalues (and restindex !) # NOTE: are the same encoding as the content if uservalues and istrue(restindex['build']): # both in original encoding, so should be ok ? # FIXME: Should we do this for subdir ? # NOTE: orig_uservalues not reused, so ok to pollute the dictionary # by rendering in it as a namespace. add_modules(orig_uservalues) content = render_well(content, orig_uservalues, uservalues=True) if restindex['format'] == 'rest': # extract title, subtitle # FIXME: shouldn't try to build here # FIXME: and should provide a better default title try: doctitle = 1 if 'page-title' in restindex and not self.promote_headers: doctitle = 0 entry = html_parts( content, source_path=filename, input_encoding=encoding, initial_header_level=int(restindex['initialheaderlevel']), doctitle=doctitle) except ApplicationError: if istrue(restindex['build']): raise entry = { 'title': '', 'html_body': '', } # FIXME: defaults to '' in the absence of a title title = title or entry['title'] or '' # extract the body body = entry['html_body'] rest_dict = entry else: body = unicode(content, encoding) # # We need the crumb in unicode # if we haven't got one - we'll use the title crumb = restindex['crumb'] if not crumb: unicrumb = title else: unicrumb = unicode(crumb, encoding) # get the tags tags = [unicode(tag, encoding) for tag in restindex['tags']] # # processed as reST page_description = description_rest( restindex['page-description'], encoding) # # We use link title or title or crumb # FIXME: is this documented ? # could be filename if neither title nor crumb exists link_title = (unicode(restindex['link-title'], encoding) or title or unicrumb) # # can be None section = restindex['section'] if section is not None: section = unicode(section, encoding) # # body, title, page_description, link_title, crumb # are now unicode strings output_encoding = restindex.get( 'outputencoding', self.outputencoding[self.dir]) lower_enc = output_encoding.lower() final_encoding = None if lower_enc == 'none': final_encoding = encoding elif lower_enc != 'unicode': # 'unicode' means leave as unicode final_encoding = output_encoding if final_encoding.lower() == 'utf8': final_encoding = 'utf-8' # # we now encode the body, title, and crumb as per the choice # in the restindex; if final_encoding is None, the encode function # leaves it as unicode body = encode(body, final_encoding) title = encode(title, final_encoding) crumb = encode(unicrumb, final_encoding) # targetfile = join(self.target, self.dir, url2pathname(target)) file_dict = { 'source_file': join(os.getcwd(), filepath), 'current_dir': self.dir, 'target_dir': join(os.getcwd(), self.target, self.dir), 'full_page_url': pagepath, 'target_file': join(os.getcwd(), targetfile), } namespace = None # if we're building the page # we need to create the namespace if not subdir and istrue(restindex['build']): # sort breadcrumbs including encoding appropriately # (from the unicode they are stored with) breadcrumbs = [] for crumbpath, crumb_title in self.breadcrumbs: crumbpath = relpathto( self.dir_as_url, target, '/' + crumbpath) crumb_title = encode(crumb_title, final_encoding) breadcrumbs.append((crumbpath, crumb_title)) # filename rather than full pagepath breadcrumbs.append((posixsplit(pagepath)[1], crumb)) # # create the namespace we build the template in # title, body, breadcrumbs, indexdata, pagepath, pagename, # encoding, anything in namespace becomes directly a variable # when rendering the page # namespace = {} namespace['title'] = title namespace['breadcrumbs'] = breadcrumbs namespace['tags'] = [encode(tag, final_encoding) for tag in tags] # not yet ! # namespace['sections'] = None namespace['pagename'] = split(target)[1] # full path namespace['pagepath'] = pagepath namespace['encoding'] = encoding namespace['output_encoding'] = output_encoding namespace['final_encoding'] = final_encoding # target, relative to index page namespace['target'] = target namespace['path_to_root'] = relpathto( self.dir_as_url, target, '/') # a copy rather than a reference! should be [] for a normal page?? namespace['sectionlist'] = list(restindex['sectionlist']) # will be None for a page not in reST format namespace['rest_dict'] = rest_dict # careful now ! namespace['Processor'] = self namespace['body'] = body namespace['modified'] = os.path.getmtime(filepath) namespace['modtime'] = time.ctime(namespace['modified']) namespace['modtimeiso'] = datetime.datetime.fromtimestamp( namespace['modified']).isoformat() namespace['plugins'] = restindex['plugins'] namespace['page_description'] = encode(page_description, final_encoding) # template = restindex['template'] if template: temp_filename = join(self.dir, template) if not isfile(temp_filename): raise IOError('Template file - "%s" not found.' % temp_filename) template_encoding = restindex['template-encoding'] if not template_encoding: template_encoding = guess_encoding( open(temp_filename).read())[1] else: (temp_filename, template_encoding) = self.templatedict[ self.dir] namespace['template_file'] = temp_filename namespace['template_encoding'] = template_encoding namespace.update(file_dict) # # anything in index is available as a member of the page dictionary index = {'target': target } index['subdir'] = subdir index['section'] = section index['link-title'] = link_title index['page-description'] = page_description index['crumb'] = unicrumb index['namespace'] = namespace index['build'] = not subdir and istrue(restindex['build']) index['include'] = istrue(restindex['include']) index['filename'] = filename index['index'] = False index['uservalues'] = enc_uni_dict(uservalues, final_encoding) index['name'] = name index['restindex'] = restindex index.update(file_dict) # 'link-title', 'page-description', 'crumb', and 'section' are all unicode # FIXME: should target be ? (unicode filesystems ??) # return index def processindex(self): """ Process the index page for a directory. Most of the work has been done by ``setdefaults``, so we just need to put the namespace (etc) together. This will also establish some data that is going to be made available to all the pages in their namespaces, *including* building the sections data structure. """ # unpack data from setdefaults (index_page, filename, restindex, uservalues, content, target, encoding, final_encoding) = self.index_page # # fill in any values missing from the restindex for entry in self.defaults: if not restindex.has_key(entry): restindex[entry] = deepcopy(self.defaults[entry]) # if self.guessed: # we had to guess the encoding, let's admit it out('Guessing encoding. We guessed: %s' % encoding, INFO) # # copy files (from restindex) into same dir as target for entry in restindex['file']: self.copy_file(entry, filename, target, index=True) ##self.copy_filemask(restindex['filemask'], restindex['nofile'], filename, target, index=True) # # a list of encoded strings becomes a list of unicode strings! sectionlist = [sec is None or unicode(sec, encoding) for sec in restindex['sectionlist']] # this expression swaps None for True listswap(sectionlist, True, None) # # a dictionary of unicode strings sectiontitles = uni_dict(restindex['section-title'], encoding) # a dictionary of encoded strings! sectiondescriptions = restindex['section-description'] # indexes = self.this_section def_section = { 'title': sectiontitles.get(None, ''), 'description': description_rest( sectiondescriptions.get(entry, ''), encoding), 'pages': [], 'section-pages': [] } indextree_sections = { None: (def_section['title'], def_section['description'])} # None is default section - means you don't *need* to use sections # everything with no section specified will go into sections[None] self.sections = {None: def_section } # # might be None for entry in sectionlist: self.sections[entry] = {} if entry is None: entry_title = '' else: entry_title = entry.title() self.sections[entry]['title'] = sectiontitles.get(entry, entry_title) # process as reST self.sections[entry]['description'] = description_rest( sectiondescriptions.get(entry, ''), encoding) self.sections[entry]['pages'] = [] indextree_sections[entry] = ( self.sections[entry]['title'], self.sections[entry]['description']) # for page in indexes: if not page['include']: # the page is not passed in 'sections' continue section = page['section'] try: self.sections[section]['pages'].append(page) except KeyError: raise KeyError('Page "%s" claims to be in section "%s",' ' which doesn\'t exist.' % (page['filename'], section)) # # sections is now a dictionary with an entry per section # (including the default section - keyed by None) # each section dictionary has: # unicode section title and unicode section description (or '') # a list of all the pages in each each section # where each page is the entry returned by ``process`` # # next make sure that the pages in each section are ordered as # per 'section-pages' (if specified) self.order_pages(self.sections) # namespace = None crumb = restindex['crumb'] # establish a default crumb if all else fails unicrumb = u'' if crumb: unicrumb = unicode(crumb, encoding) page_description = description_rest( restindex['page-description'], encoding) # processed as reST link_title = unicode(restindex['link-title'], encoding) # title = restindex['page-title'] if title: title = unicode(restindex['page-title'], encoding) # # can be None section = restindex['section'] if section is not None: section = unicode(section, encoding) # targetfile = join(self.target, self.dir, url2pathname(target)) file_dict = { 'source_file': join(os.getcwd(), join(self.dir, url2pathname(index_page))), 'current_dir': self.dir, 'target_dir': join(os.getcwd(), self.target, self.dir), 'full_page_url': '/' + pathjoin(self.dir_as_url, target), 'target_file': join(os.getcwd(), targetfile), } if istrue(restindex['build']): # build the body of the page rest_dict = None # if we have any uservalues # render them into content # FIXME: we have to *assume* that the uservalues # FIXME: are the same encoding as the content if uservalues: # need to re-encode uservalues # NOTE: orig_uservalues not reused, so ok to pollute the # dictionary by rendering in it as a namespace. orig_uservalues = enc_uni_dict(uservalues, encoding) add_modules(orig_uservalues) content = render_well(content, orig_uservalues, uservalues=True) if restindex['format'] == 'rest': # extract title, subtitle doctitle = 1 if 'page-title' in restindex and not self.promote_headers: doctitle = 0 entry = html_parts(content, source_path = filename, input_encoding = encoding, initial_header_level=int(restindex['initialheaderlevel']), doctitle=doctitle) # FIXME: defaults to '' in the absence of a title title = title or entry['title'] or '' # extract the body body = entry['html_body'] rest_dict = entry else: body = unicode(content, encoding) # if not crumb: unicrumb = title # # FIXME: is this documented? Could be filename if neither title # nor crumb exists link_title = link_title or title or unicrumb # # we now encode the body, title, and crumb as per the choice # in the restindex body = encode(body, final_encoding) title = encode(title, final_encoding) # pagepath = pathjoin(self.dir_as_url, target) breadcrumbs = [] for crumbpath, crumb_title in self.breadcrumbs: crumbpath = relpathto( self.dir_as_url, target, '/' + crumbpath) crumb_title = encode(crumb_title, final_encoding) breadcrumbs.append((crumbpath, crumb_title)) # the previously created crumb *might* be wrong... # (because we didn't render content to get the title) # correcting it here is *inconsistent* of course ## breadcrumbs[-1] = (pagepath, crumb) # # create the namespace we build the template in # title, body, breadcrumbs, indexdata, pagepath, pagename, # encoding, anything in namespace becomes directly a variable # namespace = {} namespace['title'] = title namespace['body'] = None namespace['breadcrumbs'] = breadcrumbs namespace['tags'] = [encode(unicode(tag, encoding), final_encoding) for tag in restindex['tags']] namespace['pagename'] = split(target)[1] namespace['pagepath'] = pagepath namespace['encoding'] = encoding namespace['output_encoding'] = self.outputencoding[self.dir] namespace['final_encoding'] = final_encoding namespace['path_to_root'] = relpathto( self.dir_as_url, target, '/') namespace['rest_dict'] = rest_dict # careful now! :-) namespace['Processor'] = self namespace['body'] = body try: namespace['modified'] = os.path.getmtime(filename) except (IOError, OSError): # happens when creating a default index namespace['modified'] = time.time() namespace['modtimeiso'] = datetime.datetime.fromtimestamp( namespace['modified']).isoformat() namespace['modtime'] = time.ctime(namespace['modified']) namespace['plugins'] = restindex['plugins'] namespace['page_description'] = encode(page_description, final_encoding) # # for an index - template is always worked out in getdefaults temp_filename, template_encoding = self.templatedict[self.dir] namespace['template_file'] = temp_filename namespace['template_encoding'] = template_encoding namespace['_dir_as_url'] = self.dir_as_url namespace.update(file_dict) else: link_title = link_title or title or unicrumb # the_index_page = { 'target': target, # unicode :-) 'crumb': self.crumbdict[self.dir][1], 'namespace': namespace, 'link-title': link_title, 'section': section, 'subdir': False, 'page-description': page_description, 'filename': filename, 'name': splitext(filename)[0], 'include': istrue(restindex['include']), 'index': True, 'uservalues': enc_uni_dict(uservalues, final_encoding), 'restindex': restindex, } the_index_page.update(file_dict) # XXXX a magic section ?? self.sections['__index__'] = { 'title': '', 'description': '', 'pages': [the_index_page], } # # add the index page to the current section indexes.append(the_index_page) # add the whole directory to the indextree structure # addtree has to properly preserve encodings and relative URLs self.addtree(indexes, indextree_sections, sectionlist) self.sectionlist = sectionlist def buildsection(self): """ Actually build and save the pages for the current directory. This means put the content into the templates, including processing any embedded code. It must handle all the relative locations and different encodings properly. (Particularly for the indextree data structure). """ # # when building each page, # get the template filename from the namespace # insert sections into the namespace # render the body # deal with macros # for page in self.this_section: if page['namespace'] is None: # not building continue # namespace = page['namespace'] target = page['target'] # temp_filename = namespace['template_file'] template_encoding = namespace['template_encoding'] final_encoding = namespace['final_encoding'] # # get the template if not temp_filename in self._template_files: temp_file = open(temp_filename).read() self._template_files[temp_filename] = temp_file else: temp_file = self._template_files[temp_filename] # # decode temp_file = unicode(temp_file, template_encoding) # encode temp_file = encode(temp_file, final_encoding) # filename = namespace['pagepath'] # the_sections = handle_sections( self.sections, final_encoding, self.dir_as_url, target) # # FIXME: hack - retrieve the indexpage and remove it # from the_sections __index__ = the_sections['__index__']['pages'][0] del the_sections['__index__'] # namespace['sections'] = SectionDict(the_sections.items()) namespace['default_section'] = namespace['sections'][None] namespace['indexpage'] = __index__ namespace['sectionlist'] = [encode(mem, final_encoding) for mem in self.sectionlist] # # set the key order of the 'sections' dictionary # This assumes that the sectionlist from the restindex is # either complete or empty. # FIXME: hack - add the default section if None not in namespace['sectionlist']: if not the_sections[None]['pages']: # if there are no pages in the default section del namespace['sections'][None] else: namespace['sectionlist'].append(None) namespace['sections'].setkeys(namespace['sectionlist'] or namespace['sections'].keys()) # indextree, thispage = self.buildtree(page, final_encoding) namespace['indextree'] = indextree namespace['thispage'] = thispage # target_file_name = url2pathname(target) targetfile = join(self.target, self.dir, target_file_name) out('Building %s' % filename, INFO) # # add the functions and uservalues for entry in functions.__all__: namespace[entry] = getattr(functions, entry) uservalues = page['uservalues'] namespace.update(uservalues) # functions._set_uservalues(namespace, uservalues) # # render macros if self.macrodir is not None: cur_dir = os.getcwd() # change directory to the directory of the macros file os.chdir(self.macrodir) try: self.macros['set_uservalues'](namespace, uservalues) except KeyError: pass namespace['body'] = textmacros.replace_all( namespace['body'], self.macros, 1) if self.macrodir is not None: os.chdir(cur_dir) # if self.debug: # DEBUG mode def exit(): sys.exit() namespace['exit'] = exit namespace['local_vars'] = locals() namespace['self'] = self namespace['temp_file'] = temp_file interactive(namespace) add_modules(namespace) thepage = render_well(temp_file, namespace, final_encoding=final_encoding) # # if final_encoding was ``None`` thepage is now unicode # so we need to re-encode if type(thepage) is unicode: thepage = thepage.encode(template_encoding) # FIXME: why template_encoding ? (can't leave it as unicode though) # targetdir = dirname(targetfile) if not isdir(targetdir): out('Creating Directory : %s' % targetdir, ACTION) os.makedirs(targetdir) # # compare compfile = join(self.compare, self.dir, target_file_name) same = False if isfile(compfile): # True or False same = (thepage == open(compfile, 'r').read()) if same is False: out('Writing %s' % target, ACTION) open(targetfile, 'w').write(thepage) else: out('Skipping "%s". Identical file exists.' % target, INFO) def addtree(self, indexes, sections, sectionlist): """Add the indextree entry for the current directory.""" secs = {} this_sect = {} for page in indexes: if not page['include'] and not page['index']: # FIXME: what if we don't 'include' the index page? # FIXME: possible I guess? continue # newpage = { } newpage['subdir'] = page['subdir'] # absolute path to this file newpage['target'] = pathjoin(self.dir_as_url, page['target']) newpage['section'] = page['section'] newpage['link-title'] = page['link-title'] newpage['crumb'] = page['crumb'] newpage['page-description'] = page['page-description'] newpage['name'] = page['name'] # FIXME: Warning, encoding isn't handled for entry in ('restindex', 'uservalues', 'source_file', 'current_dir', 'target_dir', 'full_page_url', 'target_file'): newpage[entry] = page[entry] # if not page['index']: if secs.has_key(page['section']): secs[page['section']].append(newpage) else: secs[page['section']] = [newpage] else: this_sect.update(newpage) # Order of pages in this list should follow # sectionlist and section-pages pages = [] for sec in sectionlist or [None]: section = secs.get(sec, []) if sec in self.section_pages: sec_list = self.section_pages[sec] these_pages = {} for page in section: these_pages[page['name']] = page for entry in sec_list: pages.append(these_pages.pop(entry)) pages += these_pages.values() else: pages += section # this_sect['pages'] = pages this_sect['sections'] = sections this_sect['sectionlist'] = sectionlist this_sect['dir_as_url'] = self.dir_as_url # # this_sect now represents the index page for this self.indextree[self.dir] = this_sect def buildtree(self, cur_page, encoding): """ Given the current page return the indextree structure and thispage pointer. """ # abs location of cur_page cur_loc = pathjoin(self.dir_as_url, cur_page['target']) # branches = [] thedir = self.dir while thedir: this_index = self.indextree[thedir] if this_index is not None: branches.insert(0, this_index) thedir = dirname(thedir) branchroot = self.indextree.get('') if branchroot is not None and branchroot not in branches: branches.insert(0, branchroot) # newlist = [] i = -1 # start at top level, work our way down the tree while i < len(branches) - 1: i += 1 branch = branches[i] # handle the basics this_sect = sort_page(branch, encoding) this_sect['subdir'] = branch['subdir'] # relative path to this file this_sect['target'] = relpathto( self.dir_as_url, cur_loc, branch['target']) this_sect['thispage'] = False # sectionlist = this_sect['sectionlist'] = [encode(mem, encoding) for mem in branch['sectionlist']] sections = {} for entry in branch['sections']: title, desc = branch['sections'][entry] sections[encode(entry, encoding)] = ( encode(title, encoding), encode(desc, encoding)) this_sect['sections'] = sections # pages = [] for page in branch['pages']: newpage = sort_page(page, encoding) newpage['subdir'] = page['subdir'] newpage['sectionlist'] = sectionlist newpage['sections'] = sections newpage['parent'] = this_sect # relative path to this file newpage['target'] = relpathto( self.dir_as_url, cur_loc, page['target']) newpage['thispage'] = False if not newpage['subdir']: newpage['pages'] = None else: newpage['pages'] = [] pages.append(newpage) this_sect['pages'] = pages # if i > 0: thepages = branches[i-1]['pages'] if not thepages: # either None or [] - parent directory claims # to have no pages # FIXME: Should this be INFO or WARN ? out('No pages in parent section.', INFO) newlist[i-1]['pages'] = [this_sect] else: for num in range(len(thepages)): if thepages[num]['target'] == branch['target']: del newlist[i-1]['pages'][num] newlist[i-1]['pages'].insert(num, this_sect) break else: newlist[i-1]['pages'].append(this_sect) this_sect['parent'] = newlist[i-1] else: this_sect['parent'] = None # newlist.append(this_sect) # for page in newlist[-1]['pages']: if split(cur_page['target'])[1] == page['target']: thispage = page page['thispage'] = True break else: # for index pages if split(cur_page['target'])[1] == newlist[-1]['target']: thispage = newlist[-1] newlist[-1]['thispage'] = True else: # for pages that have ``include: no`` thispage = None return newlist[0], thispage def do_plugin(self, plugin, filename, target, restindex, uservalues): """Run a plugin - first checking we can/have loaded it.""" if not plugin in self.plugins: try: p = __import__('rest2web.plugins', globals(), locals(), [plugin]) p = getattr(p, plugin) except (ImportError, AttributeError): raise ImportError('Plugin "%s" doesn\'t exist or has no' ' Plugin class' % plugin) self.plugins[plugin] = p.Plugin(self) return self.plugins[plugin].page(filename, target, restindex, uservalues) def order_pages(self, sections): """ Order the pages in each section as 'section-pages' (if specified). Additionally, create a list of all the pages in order. """ self.page_index = [] for sec in self.section_pages: section = sections[sec] sec_list = self.section_pages[sec] pages = {} for page in section['pages']: pages[page['name']] = page section['pages'] = [] for entry in sec_list: try: p = pages.pop(entry) except KeyError: sec = sec or 'Default Section' raise rest2webError('Page specified in "section-pages" ' 'that doesn\'t exist. Section "%s", ' 'page "%s"' % (sec, entry)) else: section['pages'].append(p) self.page_index.append(p) remaining = pages.values() section['pages'] += remaining self.page_index += remaining def sort_page(inpage, encoding): """ A specialised function for ``buildtree`` that encodes specific members of page dictionaries """ outpage = {} entries = ['section', 'link-title', 'crumb', 'page-description'] for entry in entries: outpage[entry] = encode(inpage[entry], encoding) # FIXME: Warning, encoding isn't handled for entry in ('restindex', 'uservalues', 'source_file', 'current_dir', 'target_dir', 'full_page_url', 'target_file'): outpage[entry] = inpage[entry] return outpage def handle_sections(sections, encoding, cur_loc, target): """ Given a 'sections' datastructure appropriately encode. The 'sections' data structure is a dictionary of dictionaries. All the members are unicode strings, or lists of unicode strings... etc. This function returns a new data structure with all the unicode turned to encoded strings. (unless encoding is None) This *also* handles making the targets to all of the pages relative to the right location. """ # FIXME: could this use sort_page as well? out_dict = {} for entry in sections: if entry is not None: # could do try..except here... ? enc_entry = encode(entry, encoding) else: enc_entry = None this_sect = sections[entry] new_sect = {} new_sect['title'] = encode(this_sect['title'], encoding) new_sect['description'] = encode(this_sect['description'], encoding) new_sect['pages'] = [] thepages = this_sect['pages'] for page in thepages: newpage = {} # sort out relativity newpage['target'] = relpathto(cur_loc, target, page['target']) newpage['section'] = enc_entry newpage['link-title'] = encode(page['link-title'], encoding) newpage['page-description'] = encode( page['page-description'], encoding) newpage['crumb'] = encode(page['crumb'], encoding) # no conversion - just a reference newpage['namespace'] = page['namespace'] newpage['subdir'] = page['subdir'] # FIXME: Warning, encoding isn't handled for entry in ('restindex', 'uservalues', 'source_file', 'current_dir', 'target_dir', 'full_page_url', 'target_file'): newpage[entry] = page[entry] new_sect['pages'].append(newpage) out_dict[enc_entry] = new_sect return out_dict rest2web-0.5.2~alpha+svn-r248.orig/gallery_test.ini0000600000175000017500000000063210311246051021700 0ustar madduckmadduck# Attempt to use psyco ? psyco = True # pause after building ? pause = True # the root directory start_directory = 'gallery_test' # the directory to generate files in target_directory = 'docs_html/gallery_test' # directory to compare against (defaults to target_directory) compare_directory = '' # file to log output to (if any) log_file = 'log.txt' # file containing macros (if any) macros = 'macros.py'rest2web-0.5.2~alpha+svn-r248.orig/docs_html/0000700000175000017500000000000010644674644020500 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/stylesheets/0000700000175000017500000000000010644674644023054 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/stylesheets/pysrc.css0000600000175000017500000000061610416212052024705 0ustar madduckmadduck.pysrc { border: #c0c0ff 2px dotted; padding:10px; font-weight: normal; background: #e0e0ff; margin: 20px; padding:10px; } .pykeyword { font-weight: bold; color: orange; } .pystring { color: green } .pycomment { color: red } .pynumber { color:purple; } .pyoperator { color:purple; } .pytext { color:black; } .pyerror { font-weight: bold; color: red; }rest2web-0.5.2~alpha+svn-r248.orig/docs_html/stylesheets/voidspace_docutils2.css0000600000175000017500000000467610416212052027524 0ustar madduckmadduck/* :Authors: Ian Bicking, Michael Foord :Contact: fuzzyman@voidspace.org.uk :Date: 2006/04/01 :Version: 0.2.0 :Copyright: This stylesheet has been placed in the public domain. Stylesheet for Docutils. Based on ``blue_box.css`` by Ian Bicking and ``default.css`` revision 3442 Optimised for rest2web - http://www.voidspace.org.uk/python/rest2web/ */ @import url(default.css); em, i { /* Typically serif fonts have much nicer italics */ font-family: Times New Roman, Times, serif; } a.target { color: blue; } a.toc-backref { text-decoration: none; color: black; } a.toc-backref:hover { background-color: inherit; } a:hover { background-color: #cccccc; } div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { background-color: #cccccc; padding: 3px; width: 80%; } div.admonition p.admonition-title, div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { text-align: center; background-color: #999999; display: block; margin: 0; } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title { color: #cc0000; font-family: sans-serif; text-align: center; background-color: #999999; display: block; margin: 0; } h1, h2, h3, h4, h5, h6 { font-family: Helvetica, Arial, sans-serif; border: thin solid black; /* This makes the borders rounded on Mozilla, which pleases me */ -moz-border-radius: 8px; padding: 2px; /* Padding used to be 4px, changed by Justin */ } h1 { background-color: #72739A; color: #000000; } h1 a.toc-backref { color: #ffffff; } h3, h4, h5, h6 { background-color: #cccccc; color: #000000; } h2 a.toc-backref, h3 a.toc-backref, h4 a.toc-backref, h5 a.toc-backref, h6 a.toc-backref { color: #555555; } h1.title { text-align: center; background-color: #444499; color: #eeeeee; border: medium solid black; -moz-border-radius: 20px; } table.footnote { padding-left: 0.5ex; } table.citation { padding-left: 0.5ex } pre.literal-block, pre.doctest-block { border: thin black solid; padding: 5px; } .image { border-style : solid; border-width : 2px; } img.align-center { display: block; margin: auto; text-align: center; margin: 10px; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { font-size: 100%; } code, tt { color: #000066; } rest2web-0.5.2~alpha+svn-r248.orig/docs_html/stylesheets/default.css0000600000175000017500000001226110414273736025206 0ustar madduckmadduck/* :Author: David Goodger :Contact: goodger@users.sourceforge.net :Date: $Date: 2006-04-03 21:30:38 +0200 (Mon, 03 Apr 2006) $ :Version: $Revision: 163 $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. */ /* "! important" is used here to override other ``margin-top`` and ``margin-bottom`` styles that are later in the stylesheet or more specific. See http://www.w3.org/TR/CSS1#the-cascade */ .first { margin-top: 0 ! important } .last, .with-subtitle { margin-bottom: 0 ! important } .hidden { display: none } a.toc-backref { text-decoration: none ; color: black } blockquote.epigraph { margin: 2em 5em ; } dl.docutils dd { margin-bottom: 0.5em } /* Uncomment (and remove this text!) to get bold-faced definition list terms dl.docutils dt { font-weight: bold } */ div.abstract { margin: 2em 5em } div.abstract p.topic-title { font-weight: bold ; text-align: center } div.admonition, div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { margin: 2em ; border: medium outset ; padding: 1em } div.admonition p.admonition-title, div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title { color: red ; font-weight: bold ; font-family: sans-serif } /* Uncomment (and remove this text!) to get reduced vertical space in compound paragraphs. div.compound .compound-first, div.compound .compound-middle { margin-bottom: 0.5em } div.compound .compound-last, div.compound .compound-middle { margin-top: 0.5em } */ div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic } div.dedication p.topic-title { font-weight: bold ; font-style: normal } div.figure { margin-left: 2em } div.footer, div.header { clear: both; font-size: smaller } div.line-block { display: block ; margin-top: 1em ; margin-bottom: 1em } div.line-block div.line-block { margin-top: 0 ; margin-bottom: 0 ; margin-left: 1.5em } div.sidebar { margin-left: 1em ; border: medium outset ; padding: 1em ; background-color: #ffffee ; width: 40% ; float: right ; clear: right } div.sidebar p.rubric { font-family: sans-serif ; font-size: medium } div.system-messages { margin: 5em } div.system-messages h1 { color: red } div.system-message { border: medium outset ; padding: 1em } div.system-message p.system-message-title { color: red ; font-weight: bold } div.topic { margin: 2em } h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { margin-top: 0.4em } h1.title { text-align: center } h2.subtitle { text-align: center } hr.docutils { width: 75% } img.align-left { clear: left } img.align-right { clear: right } img.borderless { border: 0 } ol.simple, ul.simple { margin-bottom: 1em } ol.arabic { list-style: decimal } ol.loweralpha { list-style: lower-alpha } ol.upperalpha { list-style: upper-alpha } ol.lowerroman { list-style: lower-roman } ol.upperroman { list-style: upper-roman } p.attribution { text-align: right ; margin-left: 50% } p.caption { font-style: italic } p.credits { font-style: italic ; font-size: smaller } p.label { white-space: nowrap } p.rubric { font-weight: bold ; font-size: larger ; color: maroon ; text-align: center } p.sidebar-title { font-family: sans-serif ; font-weight: bold ; font-size: larger } p.sidebar-subtitle { font-family: sans-serif ; font-weight: bold } p.topic-title { font-weight: bold } pre.address { margin-bottom: 0 ; margin-top: 0 ; font-family: serif ; font-size: 100% } pre.line-block { font-family: serif ; font-size: 100% } pre.literal-block, pre.doctest-block { margin-left: 2em ; margin-right: 2em ; background-color: #eeeeee } span.classifier { font-family: sans-serif ; font-style: oblique } span.classifier-delimiter { font-family: sans-serif ; font-weight: bold } span.interpreted { font-family: sans-serif } span.option { white-space: nowrap } /* span.pre { white-space: pre } */ span.problematic { color: red } span.section-subtitle { /* font-size relative to parent (h1..h6 element) */ font-size: 80% } table.citation { border-left: solid thin gray } table.docinfo { margin: 2em 4em } table.docutils { margin-top: 0.5em ; margin-bottom: 0.5em } table.footnote { border-left: solid thin black } table.docutils td, table.docutils th, table.docinfo td, table.docinfo th { padding-left: 0.5em ; padding-right: 0.5em ; vertical-align: top } table.docutils th.field-name, table.docinfo th.docinfo-name { font-weight: bold ; text-align: left ; white-space: nowrap ; padding-left: 0 } h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { font-size: 100% } tt.docutils { background-color: #eeeeee } ul.auto-toc { list-style-type: none } rest2web-0.5.2~alpha+svn-r248.orig/docs_html/stylesheets/rest2web.css0000600000175000017500000000775310416212052025313 0ustar madduckmadduck/* :Authors: Michael Foord, Justin Fleming :Contact: fuzzyman@voidspace.org.uk :Date: 2006/04/01 :Copyright: Copyright Voidspace.org.uk, 2006 :License: BSD Revised License Stylesheet for rest2web - http://www.voidspace.org.uk/python/rest2web/ */ @import url(voidspace_docutils2.css); @import url(pysrc.css); h2 { background-color: #CCCCCC; color: #000000; } p.headertitle { color:#999999; font-size:1.2em; } a.image-reference:visited { color: #999999; } body { font-size: 62.5%; margin: 0em auto; padding: 0em; font-family: Verdana,Tahoma, Arial,sans-serif; } #wrap { width: 760px; margin: 0em auto; } img { border:0; } /**************/ /* HEADER */ /**************/ #header-section { padding:0; margin: 0; margin-bottom:1em; margin-top:0.5em; text-align:center; } #header { clear: both; margin: 0em 0em 0.5em 0em; border-top: solid 0.1em rgb(175,175,175); border-bottom: solid 0.1em rgb(175,175,175); background-color: rgb(235,235,235); text-transform: uppercase; line-height: 2.0em; height: 2.0em; color: rgb(50,50,50); } #header ul { margin: 0em; padding: 0em; list-style: none; font-weight: bold; font-size: 1.0em; } #header li { float: left; white-space: nowrap; } #header li a { display: block; padding: 0em 1.0em 0em 1.0em; background-color: rgb(235,235,235); text-decoration: none; color: rgb(50,50,50); } #header a:hover { background-color: rgb(220,220,220); text-transform: uppercase; text-decoration: none; color: rgb(50,50,50); } #header .selected { padding: 0em 0.5em 0em 0.5em; border-right: solid 0.1em rgb(175,175,175); background-color: rgb(220,220,220); color: rgb(50,50,50); } * html #header a {width:1%;} /***********************/ /*** LEFT COLUMN ***/ /***********************/ #left-column { width: 152px; vertical-align: top; background-color: rgb(255,255,255); font-size: 1.1em; color: rgb(50,50,50); } #left-navheader-first { display: block; margin: 1.0em 0em 0em 0em; padding: 0.3em; border-left: solid 0.5em rgb(235,235,235); background-color: rgb(220,220,220); text-transform: uppercase; text-decoration: none; font-weight: bold; color: rgb(50,50,50); text-align:center; } #sidie ul { width: 95%; padding: 0em; margin: 0em; list-style: none; } #sidie li { margin: 0em 0em 0em 0em; } .left-navheader { display: block; margin: 1.0em 0em 0.5em 0em; padding: 0.3em 0.7em 0.2em 0.4em; border-left: solid 0.5em rgb(235,235,235); background-color: rgb(220,220,220); text-transform: uppercase; text-decoration: none; font-weight: bold; color: rgb(50,50,50); } #sidie li a { display: block; padding: 0.3em 0.7em 0.2em 1.2em; border-left: solid 0.5em rgb(235,235,235); border-top: solid 0.1em rgb(200,200,200); background-color: rgb(235,235,235); text-decoration: none; font-weight: normal; color: rgb(50,50,50); height: 1.3em; } #sidie li a:hover { border-left: solid 0.5em rgb(175,175,175); background-color: rgb(220,220,220); text-decoration: none; color: rgb(50,50,50); } .sidieimg { text-align: center; margin-top: 10px; vertical-align: middle; } /*************************/ #sidie2 a { text-decoration:none; } .sidie2title { margin:0; font-weight:bold; text-transform:uppercase; } #sidie2 p { margin:0; } #sidie2 p a:hover { background-color: rgb(220,220,220); } .sidie2indent { margin-left:1.5em; margin-top:0.5em; } /*************************/ /*************************/ /*** MIDDLE COLUMN ***/ /*************************/ /* Sections */ /************/ #middle-column { width: 592px; background-color: rgb(255,255,255); color: rgb(100,100,100); line-height: 1.3em; font-size: 1.2em; } #middle-column h1 {padding: 6px;} /*******************/ /*** FOOTER ***/ /*******************/ #footer { clear: both; height: 2.5em; margin: 1.0em 0em 1.0em 0em; padding: 0.25em 0em 0.3em 0em; border-top: solid 0.1em rgb(150,150,150); border-bottom: solid 0.1em rgb(150,150,100); background-color: rgb(220,220,220); text-align: center; color: rgb(100,100,100); font-size: 1.0em; } rest2web-0.5.2~alpha+svn-r248.orig/docs_html/tutorial_site/0000700000175000017500000000000010644674644023367 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/tutorial_site/stylesheets/0000700000175000017500000000000010644674644025743 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/tutorial_site/stylesheets/test.css0000600000175000017500000000605510416212052027416 0ustar madduckmadduck/*css file for Voidspace content pages. http://www.voidspace.org.uk css source based on the 2 column layout from : http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/ */ @import url('pysrc.css'); p, td, li, ul, ol, h1, h2, h3, h4, h5, h6 { font-family: Georgia, "New Century Schoolbook", Times, serif; } body { margin:0px; padding:0; background-color:#A7A09A; color:#000; min-width:850px; } #main { background-color:#FFFFE0; padding: 10px; margin: auto; width: 90%; font-size: 12pt; height: 100%; } #main h2,#main h3,#main p,#main h4 { padding:0 10px; } #main a { font-weight:bold; border-bottom: 1px dotted black; text-decoration: none; } #main h2 a {border-bottom: none;} #main h3 a {border-bottom: none;} #main h4 a {border-bottom: none;} #main a.toc-backref {border-bottom: none;} /* void1 and void2 are the text elements of the header */ #header { background-color: #318; text-align: center; padding: 10px; color: silver; font-family: Helvetica, Arial, sans-serif; } #header img { display: block; margin: 15px auto 10px; } #void1 { font-size:13pt; font-weight:bold; } #void2 { font-size:9pt; font-weight:normal; } a:hover {background:yellow;} a[name]:hover {background: none;} #nav { background-color:#c99; padding:5px; text-align:center; font-size:15pt; font-weight:bold; } #nav ul { margin:0; padding:0 } #nav li { display:inline; list-style:none; margin:0; padding:5px; } #header a:hover{background:none;} .title { text-align: center; padding: 15px; max-width: 80%; margin: 20px auto; } .title h1 { font-size:30pt; } .title img { display: block; margin: 20px auto; } .intro { border:medium outset; border-color:blue; margin-left:5em; margin-right:5em; background-color:#ddd; } .sitemap { padding-top: 20px; text-align:center; } .sitemap th { font-size:24pt; } .sitemap td { font-weight:bold; padding:5px; } .sitemap table { margin-left: auto; margin-right: auto; } .displaybox { width: 65%; margin-left: auto; margin-right: auto; border-color:#191970; border-width:medium; background-color:#F5F5F5; border-style:solid; padding:5px; } .displaybox h3 { font-size:24pt; text-align:center; font-weight:bold; } .indexblock { border: thick solid; border-color:blue; margin:2em; background-color:#edd; padding: 10px; -moz-border-radius: 10px; } .indexblock table { margin: auto; padding: 10px; } .end { margin: auto; 20px; padding: 10px; text-align: center; } .sidie h3 { margin: 10px; } .sidie li { font-weight: bold; } hr {text-align: center;} .zentrum { margin:auto; text-align:center; width:80%; padding:10px; } .ex { background-color: #FFFFFF; margin: 10px; padding: 5px; border: 2px; border-color: #FFFF00; border-style: dashed; width: 65% } rest2web-0.5.2~alpha+svn-r248.orig/docs_html/tutorial_site/stylesheets/rest.css0000600000175000017500000000361010416212052027406 0ustar madduckmadduck/* :Authors: Ian Bicking, Michael Foord :Contact: fuzzyman@voidspace.org.uk :Date: 2005/08/26 :Version: 0.2.0 :Copyright: This stylesheet has been placed in the public domain. rest.css - used for rest2web documents Content rendered by docutils as part of a website http://www.voidspace.org.uk/python/rest2web/ Stylesheet for Docutils. Based on ``blue_box.css`` by Ian Bicking and ``default.css`` revision 3442 */ @import url(default.css); em, i { /* Typically serif fonts have much nicer italics */ font-family: Times New Roman, Times, serif; } a.target { color: blue; } a.toc-backref { text-decoration: none; color: black; } a.toc-backref:hover { background: none ! important } div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { background-color: #cccccc; padding: 3px; width: 80%; } div.admonition p.admonition-title, div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { text-align: center; background-color: #999999; display: block; margin: 0; } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title { color: #cc0000; font-family: sans-serif; text-align: center; background-color: #999999; display: block; margin: 0; } h3 a.toc-backref, h4 a.toc-backref, h5 a.toc-backref, h6 a.toc-backref { color: #000000; } table.footnote { padding-left: 0.5ex; } table.citation { padding-left: 0.5ex } pre.literal-block, pre.doctest-block { border: thin black solid; padding: 5px; } .image img { border-style : solid; border-width : 2px; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { font-size: 100%; } code, tt { color: #000066; } span.pre { white-space: normal } /* When set to 'pre' it breaks my page layout on IE */ rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/0000700000175000017500000000000010644674645023177 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/gallery/0000700000175000017500000000000010644674644024635 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/gallery/hst_carina.jpg0000600000175000017500000020250510276336034027445 0ustar madduckmadduckJFIFtPhotoshop 3.08BIMxHH@ Rc(hh @d'0- 8BIM y y8BIM x8BIM8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM@@8BIM8BIM8BIM 7pHP^JFIFHH&File written by Adobe Photoshop 5.2Adobed            Hp"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?`~Z71 =洘0'.3$GDA>~Z\z)a'! lmK([~S+X_O9 P2zr3r9ѕv6U!PxAo;={}. -QnEͭ~r\`qHۡkN={Dž1׈}cTt4m^sH5pE|6>Y][kZOӎ鰱{D}\{RɢI>%}scV^|F_9Wyپ!9MsS#*KZ= }ޜO旸7:pBϠ؏21_ÚڳF4H)XAy5{f㬸~v`=Bí6w~ygmqt8=WAZ7oI;zVt{C].1?E}~1ާ|{yahf5h,9߱!ik^9bvEawqokPu܋2%ō{6K cXb3 R:Uc-UccOMfdZ!kqhSs7/֮V]c%2yg5չboӓ0zyw݌ZqK߻nEVYUn1}/Buϫ&w }-߸gڻLE,AnwzMTkm`w~sOQMBK$I=FM!^m]n%Nt?O`x E d+2\DMisy\32U- մhA;=Vͦf~{NJkhu9bzs}U뱂Qۃu:^4a2vǧb63r~hwauC嗢kl%w[kYGYM$;k~\8Q+_XkQ7/.sKiG! >7kmgUM1 4D u4ր״F[c6r5Iaג ֻV^ݭ~Ζ@nۧ^|ChP׷?. \}Fs,,IiIy.k841Km6}DTHkǥcVw{\ЋfҏiM2OQ=06gWaaiI̭fe?"\@?0*YsM8!n&mlb$q/~{"՛+V(eg*W9mTڝ]: 7IR6\̇9Y k'Vkm;cٿ@('BOs[:7}ncYMkdiKq,̌/^2):ݷ]dc0=`-:gh769xIXV\pu=~a?R{{AĞJH,s|U>@ 2:cn8׵sȇ8-3[ [ipuXN'Mޝk엘iVkʮ+nCm$>},o_ e=+Dq~h+s1<2yN}=,?Bk}?O`g[ɭ4 k[Y;{>`T? vcU E6Wh<{uI:zm6K\lwfdڛSK]^ֆ58 iow' VHԴcGHUk^] A%iݘLJY0tM>e/vdsv{FB12߻O$4,$6a߻9s"-'M_}jqEŅ]I^Ciu$i-֗mF2"q&uTE~]:_@šuvҹ! moG_uG*,o>\Tcщݟ͑B@qk3 +7 -2tvQwڸ禿ȿr,hZ9whk;=0S߃-N<2h.i5p)eq}ggѝҙӋ] &K TߦNŞ-8w5Mk9}F 1Y*R#[+uy~/hm}?_Tyͱ+ jc7沺-/sGOE{nc"'^~0}]ܾ 5ݼ T:~6oX6~3oGF.$g|._}W/\k!o65cz7=h_@.c蹡ϧkי$ҙŧ}@oh$A e5nj$W_8BIMMosaicx7x7x7{W]`{W]`{W]`DUHDUHDUH!6}!6}!6}`z`z`zp&p&p&\n9\n9\n9   vJ|4vJ|4vJ|4LLLM~ M~ M~       $0$0$0%%%6a 6a 6a 8V8V8V8BIM&File written by Adobe Photoshop 5.2!AdobedC     C  6" %{}w?ɿL ߱w)|h^`Mg.U$ %fc 5Cs_heQê #^EL\\LW j͌`ɓMn3k;P hG8r[dño-vՊ1 ;M>zV[Yy 撮`iNkbT h5 gs$(5U Jb&|om45Ds}X>9xgZL-% R,c9_V4N)c֢r[XX@o V,z_dJ:U9|k c}X =lHۗР˱BZYh@Z^V2>$B`j[|6S sCf"Lְ|ѥ0a <qsO:08\ l9*dc{ UԬfPξH/@RIQOE˳O%t a2"Z b! 4QHKb]f@(簜hн,A"qR>YtZAEb/xK-q_Rh ^P[|R%ērKhNO-@}eqk[t՗|Wč'K>n֋PyT35Q&UK'EB4o7O8,Zjmջ[壯>NGDWo iڵ=EO_i<ģkc3=S<{R2Ca?3TZ~õT_[9Z3ײ$SpԳjj/ծ=%VRêY aE!:ZT^{X ~Dig)bɍ"|!ZM$Xbx8 Ɩ}A^uMB9=,mx<!C?Y9LQx ؅qfQHi^ qv+`b\MAjjNWĹUaXD2bc4ղȑLwm uqJ-ʼ6!//Hc 1-zo`YNkǜtQ貿QPCH(*aoڿdk$Qt{WZf؀2VjPC*1{v"B&+50JJp>z+K4ҠD$2ڱx{,,{i)1>[R/,^̚r]j,&3ؖ**9I&UD7a-^.Pt5ȩY/ri+Mί?س`/?~^s^fFڥwgʫDW!RX\R euq .CiܯDin|/rly,1D;;qf'hJ!x[P!hg^yGA*I q䑉+s3pE/T1Ut˨6Ć)J-ô籞#~tI`!Zf+,W3ɾ{}L{2Ko"˲x5l]h uƟ gX3`V";qI\b_V3g>=\_,HCY0,  &L/&j2O0ҡ/Rd~cR{< ]P bțf'4$myRF؎_ZSW"Dh3r tI3.Yl=(+BK@-7xI],zxIY׋q.DExE׳ hOF%]V2I-MjSs:B!kzzm%d|XsbVw:\!_)DޡX;Y[Q@HjmY*@.˼ nM==( <8IZk:5$YgMz':k-YFnȀ?_%pZqw _UnP[ $ 2Zh̜vOf7K_]| $@ֵbz*؋T1~xwuE% ؄qh={Mp 2Λ5۞Px"w4 pvN^^☃3ް)kRhϘ<̘j2N"CP/P6`;0SwY`V> 5Ay\ҸhYPuV KpM)6f+oOs' AzT~2?#,< 62d RݛE+ԱIsh1Ǒg ${42bdH6x8_(*՚,B)F)bM/HșIVSDbs%IT[a/LY:3W8M>}쨛R6ƕR}h6sR 7/x E =fzx-uN$1 62M+vX}QB|4uM藙< ep:Oo\bs7g/54lmψ3~a7*bI uVRh<6:u) TB⬣Po=C "Jnus qVk-%Pc2Y4`1 $nuq{YW]pNje 02!?.KzO?Qg^q>06֎^kyyT,J"}yC{ܗwC'=ΪٺD{/k~!9Oz^[&=Xz(Д7=SDn^Vz)!"#$123Bα9W1L@@:{u !|%? wO1,_`Bpe_>#۝gUk|oJ)xHP# Z>+~liNz@%θJ-t{>9A6(CGu'\X^tM1@BLP&,ǠDg' `1 EL(nrqi|Tgx^e06~`9*f}Hi xEip$qHWB9@aGbmc%I-'hV ϱShDDbeAY*V8; n>2Ǘ !eS\9Q| L %8 $3< %3i sqfg^gbZT/X9}P(Lp.'NV)$W0&'+kuP笷+1' k^@ՒkۋY ȧ"ߎ.KA65vH&mcq_ȇMٚd'r?M=Xn 9Q0h+lPd> Y]&X?Y6>CYv/ FWT13L'`bUbPDUNI"|\+(KVUױw^up֒7ʶ,e*L`9Cq-29*_S& /#EJWٕ/V)X6 v;@͕ثzjRm{R@}fЋA\WBXp1}4b=7L :n''jN5C†Ǣw/yT;a" T&Zt5 |;YwĂ De\E Aж,45?1=V #8"%+H$lB+2h]!c BSzIvp3*IFomiLf.v0!!)o5Y׀nEZ;F%η|!j6w+` )s bI+}fa8}$0O4c7lg>>j+Wo>H%E@:ZMm o۷+Wd[4lj#4,@yXK@8E$6Mfkuzp6BZ. *BLTcݧF/0N &S8x703>ŔgԎT Gc5 5c`텡?u?ʕ*^uX5k,n^Iٮ$U竡ùNRwl^ImU`H}XWYDH*^lʜNIW /QU*EJX+n쌒^ڡ3$"2#.#a p*}or c@-BNqv3uֆȷAbo)V^h+/7Z>߬Qi;Ki_eK3܊DKSaV5Fteh &[/ oh[Qu=p@Z8gWu%MԤ5kń4ĺNP]r]]_Lx#_rNj`un$0H1Yb6'DbGh&6W̏lx͍+לze)E|OTjӝS m-Z Mlѳ2BփU/[.]i}v>rN Go)z}TWlh6x@~]Ȁ&Vҕl|T9Ptٻ-S徺k K8Zvu$$ۮMc*X-l_4!T9׮8d*0%S-z մE*6&q>?sK|Mka51Ť7tk_6%b;N輧'N+adqv8 13XFD8ƿӱ+MБl1_L39z knXvd>2'`;嚷"-/aObEpkjЫ?XU m+Rk*6nJ%m$2Cgս&,xم,-j|ńؠ~V̈́I5PҔ֎Ĭ&a86?Z>c5_#GECz3?+/|4@x){URszBm g2Z[W7[Ml{YRٮ6,k7RaՇ5V8J⬶j$:ؐnolf27d؉Á+ݛ  SI7k#ՆAV#;E?hu0?-uU"L'=U׿@,ZsjcD<UI@x!ș#mU&JvaǔuYֶ ^1\Kzfz-ծc5FMvҝ/jԄq,feE, rGa.Q7^iYVR]nvjuH%^N"!ئB;BP4?n\d-8L(JKP*ZuS_O-^Lɲ lWW6vS+0<햔)OIkvzLl$Yl^~GkkX|+԰}8;nzCV0*'7eU+W*{KJqFcu?i[_IH95,s5l\BGYL4;yejpDEY)j)ӵ䍳6m4U1-Qov.3VU[nf>VvF|~-ZA[-X>T6k46- G,2V6-0fՖjه]:,=_Y /eʚWkX2b[W -aC03i;v^YHW&qh کЪ!67S "/iٟ{_$m >&թ^淽HŒX~A0f7fFa.y~&vsz-wg6WCl5VJjZ[ӉAUq *Y'JI5xLt('wW 9Txv>2E2ȞrO&Ujei-mBcTn_r̻+Kk8U*\NSF)س8 ۡ_jF҈P2U.b*`ٗX֕1ѬjUo "Jj׾)\ʍ׿uTzyZr]ptv Z8a {0lVoa!10%=ʐ柁q @r1ѿyWz[f"l$]%WWRӬ[m }imbN˂'Yք6weֻm̤ukٺfOv0w?WV;>BFsZXmzzvfiZwm#[*I5 _Kkđ?I <9PQx?5Ì[~| CuL']B7FբTߪ\*pxI4LNbe!:9µ|3}#d6O_EĥTM7{FM%ase譇ӰLqf:*u[kŧx6,s[b#~YStw 2 p]בm+oRоFM-FUhm56(C /01ϴ#`]%6OArIs9&S?,;Eۖ..Sڰ~f)餛$VfK|XgsTvmzXv%חa;:qP6iK W Arm3Kln;i5sǭXCrV4~Û\eZT}bk1"UPLGd0̖k7kzav+"<gFe%2RCߨm3wJkLiY ?CT9BV5V4$e`{V6HVdmXjv]jTTK͒WKUa]S%YVCOE1aKJWm%vm3`Fy;g$[zj2rp2 O+,[>5u$Nn~\b}dISp9b^(cˆr'ps~l5vŚr*y.bbC%S.͂ co5.q}eݪ̓>@u[BXֶĹ|ɸks:OC 3+Lsϡ̨.gi z[/g˓* ZW¿3ZDl]P@m>T Xt[jˮXe Wi$,]|@ls6Oڜfx3.fIwO`~!䫺MC"&\װv/ד@ilbB"\ubcŐͪq?XxJҪ FzW~N_BďcX&ǔMSRRIe3*K,[r&5cf͍,m$9{ ؊ 4b`ٱ!!* =8fT?a$3:gN%nϗד;aa٠<4+s60\FNIp1_i Dc+- 8F6W!\,Bc3yV d^{,oǑH-e~vlK+5?ʤ}%z Q|MopF)y"rW[gΩO#0x2J n Y 8G`_Ib<ƻT V'DX8[mϩ׫U<`PhzV0I1p)}!$ acن=}l\ٯ|a-d lO^ۣ\JKj  &#::>b_n:ƒ8DDo}=266-$I2`^H9fw^ pkhr.0DLvjgPh.uzȲ"Rb^<:%^5z2m6(uRM]ו'>P2`LWh` lD[E.HXa͇3hjmm-'V.ȞnhF9yydTzkj,"LKe1 G ez8*^VhܯInQ"hp]*ɦۍ\uzVjt"#ޢFDv%/fK`<;H&ge:k/|gdo6# Kc[ISʊD ʱ̟_cT.s'RRk[=,9-a ʩE5(OkN]MqVL2;uPw ̓b2NH>gf\wY3Wfs0&qk go2jþbm[g`7jıəϐ,oeR%F)dE^ol 3_k!,ICL2nI]d"̰ 0Ecf0IԬeq"P0M4,Ud}!%{g%">9 $NG JG,b! av.y^\Iɝ\YxԇHYJ4_*Vl"K 1^& #ǯ$,_r*zأ&7A쩲ҰPLL6(εIKW'uؕ*V"q+ jƒel_cLSMP5Sa;#?ZǞV it>)%3 SB J>%jDBe;rؙј& 2m˓ GA۔Vڛ^g񿦺YXc5A:{btIɡfPȤ|TXg|2V#CXNߴ?,kDNs`qdr9d|mVcJVH/~(u%D?_ 쫙3#]ӟ "U ֘IS _ar˵6De*Z0fIRd=8! S3d ;bS9יhJ_BhObI}c]KiO޶/DT2>WSe}zmPk7\1J AD*"L*Ճ25Uצa+fsĶق"#-E\FW(Qsۈ`K!" Կ5!1A"Q2a#q3BR4?n<|{ÜN`̯0_WIxL~1|zax6`^gL>&+'զE4J$N"P>jN">O[ʶ|ʝE%X WnrTc9T\:f1[0TF@Vzz>b/RyOzQ:dC`%zv%6qT.ဿ}VcBqdϩXLOs_3k[ oqY鷟2Hs38R -qkP ۃ)9smw.:ϥr,j:c+jf=R@LVV89W+Vq+Uw,B?^ 2rb OT%`2ޢ010F! v398K N{|N)"ӏs)t,4/hjġqkQeZ&s_a>c[ d66!ܱT$Qpp`K)KB18yU*s=b\Ta3`H>ceNf8aN]"3s/"4gp]Ng#!t?0T@X"7TFܢ  ff]*3 v>&,fODĺxr="XOS3UX.`gS@24Mm }DZ:ιL/CJ_s ӯ3':Ͽiuj/M@x 2a{c9 YeYzԲYu> p%ɝrD sÀ7(/g+LzkwQq1NEˍro o혏 83>y ?ys{ S a{Ewzz 7@ V1řq?O3SnDZ΅`vm|K=q}JZD?4N@c>HAC-5-Y7+9Sd~ iY[&#t0ɫ0"ee]jrXˆD]@qc>f;E\ rr"g!~aOQ0[Wۈ~L6՚ ̭sqak]ϧ 0ϭbL~1AVcVLS5THQ zU]qz`@VND{4Zn^CxM|Vg.ae3jT0gApШ ˸֪,X yb>0`$r%̶΢vG(0f5D?yIȃ|kpfx1w'&K -W `Dǃ<~ l]eV!? s/mKkOك2/zju0gu_7;u%aLΐ``2xOɜa3VSd*0^ıєw3O : Dc0;Em`LF$=!1A"Qa2q#BR3br$C ?w 67X/NˡI^>°P)04W?DDT8N">)j*2o4N5] JIkXH8+in@vӔ\G0|+;,)~ 6ե/ZEVy*\+.CVKB֢DToP4CC$%EQ[FB+]YvZ}B=siBaX >˚*Asd0V,~ /]wڣKI٨e1tyQW\/kfx?|;O_ Ec OvV zS2JeNi:}Ӭu{SAVҩpR (m]R R>\ (jx]&W/"KfJp|=ַHVOЄ\>e/֏uf-^ncY9C/uHDc3Qsl;鴸nwwyxy nM3PU1 (Ǩ9cHoV"p櫛Y΅zgdҺ59_)U3W]Ԩ,=§F;o%?GkʇBgU{FP0 PNZ:W=Q nZG!frƁyV5V[-={$J?Rhg|AlƐЍ+R˥@Ģ>.ЯUAZwED!^-36h%.7 *oK\u+ e]\wJ4$Ggtyopĵ%mnPnq+p0*otʂ9Z,elʻmѷ`T;m$7+lZguk4`eX #=2&JnsTI+$yXlQ6f*)iU5Hʧ6U2GXtZp9/eKOQq&Ou̫қQ+ZQn팦Тq8F ujicQ^zLߊqzM~.!7HDYfCWSZۣ33uqSM_hEO \Q6ęQMe&F%YR*t;LjT|.ӳQ.v^{.CNW-Y4vPԬ Rܗi.hth⋹d( N@A#BJ<.h0Gu<;Kc]#Y:.&\)Tdk34AN:OeWUh};B.DU@`dp yCLLvNlOW5\=w!r.ժ0 +N.9Lnܩ:X|J•W= \zH 3_!6XPԙ_FAӺZt=ܬ.i|Vu=(p)e5f5@N2k nOa)h,PﺽImV>{Yn95AʿFv?Ъ&K[nck ɵ 8p&Mn%EK\F> WƩ=TYӰ# 4Gu#EWn΃T9M*t4>d+U|Nݔ=o>-ĞE?KJ8%ՖD.d}Sա(:FW6gSK|!VƏKVf8y wP0CvC@ (hWQ}C>UN m6ފnIq-}:alôҺ.t?r8IuZ=见;ɄB) +9&pS`܍8_7[%-n!pۡ tR :΍, Txv cAӯOb4j$M0tNl}aMmpEI^֧aiU_|olEYv2=kYR8/qM7uP9Q +=jOhjT 5= Cu:_mJgb7m0~@xu2ve X>52=>}]-CΩ) մaګ(Tk=!{L{kH<8iQЫ/g&P[T]OjZ)t-iLoMt5T7B/ix jsU4º>tf U pXv\Yqvu*iqPæ5>Pkb Dsͳ9FDcN灓2XKgu {;Z o-hC?Bfʔc+uGWtqR˕~Qkh ~Qm;i `ՍgpP?~iSMU$P軇|K p]TuϹAOq!?m|\61 n}k)e8: *w_z&Ĺ>fFF> c!6*F2PoD-{\Tf鱙0`d/.=,ZFT^Ѧų`+Gew5ǃݕN=}JY`ݗx(ҩG6മuf-nZ mk yV22TUv|+Ӟ맾Hag躣¼:#SQȐH_QӷQLS~2zʧ˚D HcZ~.8>Hܳ\l3i8psWMCprO[``u,d; ʔzm@Ν7:77c+ʘ.p 43Ӣ*iKPM0ONob7QkxB蘖S'Ui^I"~HkLSMT\UZ[D*Z1,y> XP \OVY;G~{7⡝sK] ^KB A~dTFԤž%諸+6@};x~2\<́ Q-0e:\O9seNjzLhq'SPr\;x\?_*ł9TZOs m0OytCo-m»\:-# ^K~en YHC`hpk@YM u] ƪ-_ȻH]o=d)Z=6\ g`*V۸k[ƹ]GL\2>#M")9fҚ:\A$2pSI'#+IOtNn@vWӀּ̋N\N|K7p)(ȝFɵ87 쮷VR k?WUi++FT䀋3ձN%K\N*QM6mk8@ΩX}Nqh+'r7t5"5@\XJ>Ɔ׿u*G?D3'R}7mgFNre5LPiʟQ'R10n4USX M؝#UtXwuF,1uW}]rk\`:r.ku4ܶ=J*S8p) |#mh\F,{-!Pt],tʵԬ-3S#8Sb%Q (|'LL5Ӯ1~ Sd#\Fa.1պ4Ůn<R%j|8-kJ7ސNST Sʂ4T}pNw4w׬"K` 0 s[YM1ƻ~ƀa nSAS=N:vHI-EptBc pL*L P[ #W {jj9{>#2q!W}PSRT+{={(8di? kש̺o,O6_ `:*N;?u,hO Z-W.kDUuhf۫Z|JgisǡLHPr:Fs*qt@R|m|W^|[tsdxs-:D+I'evwFiVTS!~}?)M)zEL "e]H8: +FF*vVsO6`M:BwAdwǔ-Ǻ+W0B\km0OP+"ya Bc`4h"Щnw-9:ﺉz VӊCޠOʸ.̬+]C#ʥMEhj_cu`@O}'ܷ9.vB5xդq"G؄*8i9\M Wq|ɑ:#l+Us@7}<'!mIsD"K 2.º`5ʚ=tZf*\{'Oq4^ֶLKI[<.]'Z_M"GM/_e(=DLի! ڔ~8&tBB$63(8JVˤ.R}ʽ۲TY(GWY343F>u1&%rDc6VcTŤ0:qUme7KѨM0Uu}92h?Re6ZGQ>Jv#<ë](YSgZ'e53= ywW6e'ۍVs\'yF9d:_.sizf"'q.{~$n+^:gIiTtt=S'AF ņS^tKT#e C0%r94˫ EkJ2U>(7r]u8L?Ul ѹt?}2v\gR McCgqL0 uB:.D"(IV.Cd@̍dzf%{/tT99S6}mG .1e%2C#Z^^`{m\tW $Ȩ&򮽢M!r_x^W4fˢ+T(e`.PP)ϹY\PN؉uD.cRaM uht8nBR6:nUu_l!UqLIotmiqu9셿N?{o( 4:WrUYy%&ӋT$RO϶uɓNuk=ХmOݔƊgLs?ʇ7~G5p.i9c\3="4x*&xSRc^jcB:1~"Dgc8h%Zêʐ.Ps5;##:tdwV"s= 0N? }/"%M><KƉouOt/|( $NJh{KJd~t24K ֟RuwpSm8#]>Ϥ郙QTg!s8@iaFԼjaAq=749*AiMuAn ԘR'5B[KAjdh[B'eY%s)qԸ~\dh9?#KFp@NB}*în@4貦Tt/s`n;S}!O ӝJ5TkZަ?\'̆eI 'T'`У$'Tӕ %|RӺShpԜwZ\>렘ѱ*bGj UNXŊK}ڠЧuq8mB/ U1\[::t$)˻+ks~p7V2ԫa*F<Ȃ⺋QRNi9vQ ]C e}WIVA8h{yNwW+W>6:Ux$ > rIO4]o qG! *h:P.|6lhµ[[0d@ϔk*ҪXeF9s\JE B#xN-f ֛l=YO3 )LwI*|ޒTL{˩MSj=s%rElYHNzA!sjLvhH'YO[Q́ʸK |Xp6̲=WR:D+#R -1Ae%vzzc1m%[D /ѪȃiLh' 6G4*%xW^[M-'j쬝._>4º 4NrVCPBTU$\S (#lc9WӷӞw0q_ :N=&CD)˗JAQu9Z{GwAx5(Y+j]N7 (pL|R1s#L%SpJ{ikJ-#+lavB=CwpHqӘムWwu=|E$Yd@µu]-\(# sEm-6 VV] ۓs]셌2pQJ4D5.R/h#ԙGDk0>z4JTS졍h+5ZPjWoYj:!n#Wgsh}CMWt [ gLIL *FYǺm:ߨYe!gESuq@(NZ몸oWR Y6,ir|9sMuR4J"SS`!p."#2Ldۺ*p*mSھk?x]!TWIb=տeeG}1+ >Rc4:5Gȸ|^KƄP空J$wV5%rst[Hdi4rx 9Z)YZ n?O&0o2+X| 8v0SBmnflU t7ءUeckX<\?*__IE+80U6]-uh  E"qB~ 'iD+ MMѵlJ-t !+4QM匉*+.>AC*K!ZBÜ=]ƞ ˙Q9X׺*iA/OJ$RL*xG4=$lɦi-Y\ )ds&W1U4+rц-BxVU}8zT +XW9~T+=(NCc:c@ew =M0Jy;Oڠ5K{䮆 L܇6=;NO:2tcFSYMejɎynVfcd)TTEÒցlfgnXY*5Z]+q!AlrЮsl65Mu^*`./#.c3:f@\5I?fz@S .U[u8T:3A^'vR:UϒICQ${V >\W_9T4o['B-6Npld%DR/QQ0I!a+eJ ZZJ?d9A2X!w4 S#X#]QBEs&Gc>=fi#]JqBs ڀC!^ +\jh~es,.kOc@F6?n`e0h:+l1#]MN|f;}1M&U>R&1m;FATQ :<>TBNS`#%+^vVPGeer@X;]ot_pӯaL"1#e{:F 5¹ǗMy&x,?R3@⃁00[4]AC),q:yµDh?/nUluuaԄ[f逰(vE +ޚcNez5EW2f AҬۃdxj&iEt3]H69؞Rp>-eoFgIV[PЈDʊq\]d)XZR;+Wi++Sc:lWS=LNeQ!ì(dt/1;NrNSjs[nD*u8l~S5xU3v-#Qi9 х?d*[:i֡H3(:;)HA `4\p\\H j&FuʰjU(HA_JA(Э'e'+]QکkTmv*ZmsW+rrW-˥+cTkpSp8ȪAl}\L2?ne=؅>IMeAA1uRj7\gt@XiX TG 9?xVAnl F&!1AQaq?!y(< mTӝ6s|O,>3\WG^)VO2& oŪz\|ں\/TD4#U}aEec11k UU=L,.)ȹOw,O5+A䗛)eN22eÈS29^b Qg-OaqŦ,脗%^ÿys GS.a&eE0D@kҦZrqS=۾A˷;Lq%|Xn^f&3 )OJT^XA 2~14KJՐfr^yͺ_Q3Sf@ lrm.>à0D2-QVڈ7 Z*r"P:V>Ťɹb %cwP^6?ķȴ\A9sgHS! R4ʰnhkF=b{<ķ+lGhnPk8KN :Yd@UFje4LSbt^ [V :,3!f|Pyr]$1 )qnv|QJhkT6Nk<n꼡+X/Ի̩Y [rq}GyBJA@Ls2nz/~ U :.KÇ>p!%E=IP1d0~&R0RƉ2uģw15( VS5PH-k)^Ai@wƖ# Ξ m_`JYOwAwRwWi7mKEdaW'8LPVtzAm%]T⋄BV) ˘PP)^3^fV>e2Sص&A:ӏ_-T,v*{. Xa(xa97m z:t#q. Ʌ}y2iک,yE DoԽl]ho0AC~[W]w*#[C7֡mdY)~XMB46ʳ)C2iŐ 8}F2¥Σ!_QiGC0Jc^ kٺ}CVs|DՋd}VG% - ESV1J5-dB^wr*c8uT5lPs3!}FtAxEkM_zWLYs(0PdVora]Q@"A)[v=x'-!dSJ{ i?0ܫ^5Blƺ m3%P_~K#elqSLP+F圏*-p$(MgJ|Re MN&y`1S+Bպ̜ZnDYxΔuXW[Atp\U}o, rm$ y5ei\v %4032`: j1fG?eChuF+ϩHw73ƜCJ*- ʢW j̹?+HE`~V~cfs [eOL0ZJ*ԡ!N)/f8+1|Rj*mz:A,ck`ܠ;Nf"V6ELڅj!`dR7`Fe4`m{bi)!5ۄ;y }eF-X*]e:Y{Jaф[H?!|.l|C us#`HU6C&M!xa{@)RWXIHlPζ/%K#6QR)|@EQw*ac7QBj/0OF0xdQft΃6 <++$_)~c" ^[[#~2iurW6ibm1sKn a5`So`렠;;Ņ1#[[җ3HKs4Fw7V7h2b8_$ ZH,ɾ+<e1\pj1.i) 403ZZCn!@4+X[\.f}Np\M]QyG_ .33bV[ i+⛗GH5eO;,ǹUA@So3Dj[`y.W8Q_B.y (J c3fl5p@6߹{@ɨTÎA-)TЕoXaX!!m4CH c$..eN: [Wo)V=n“3.(]'w4g[f^<@kE-9 ;"R*!=FZ#ŭ W$L\fR-@X\Hn@hk|B% rGC+s̷*'嗜MQ})-X,x3@#Ee_fC!&~g*7nC"Mho8_$Q7f3rیdR0*hTsVs mksR(yecmWcziVleS- XcSS,Nlk{2Psrh\bV5#͓? g27̭sJ.mQQ|YkOn Eٚce&\~g$Nw >]Jfל 7( tLepYP΅֌Y m rkxy)hs+ư4\~J)-0/ sV<2%՝nb`_ EÃ#4uwydPځwPxoKgb8Q>r/b,D$dE8Z*Jyc cԻz)н2,™L;-2]7I[& bUpejI<'(2sȱXoEٷ L-iMc5> iA\c26|+O2 [2B޲˧4E&z\X dEc9jrWP\c&,3 ,EUpX4Φ*qs(՜\3XhO:`cDQiɋT&ɩQj {VVA^YCR\VKauab!Jgh o!*pGRZx҅'t#YD XHvugt>#ތN]tlЮ-srk2ĪP-qZu&s)q3sħP4o57RilWίc%]?@= % W`fdkqpߑey;Y3|-"ji}N8s*6LTTU@Ѐ";Vp3[k~&ۦUH9zR bW@|6zG0#a08W@t98Kw,бr Hw Z@"bfls#+b`̓ ].u+\ 9tlׯܻVUE2)6Ѻl_2%֤ˊY,9FTɲI SF\(P/_ #዗&Pw JP`SUeŻfYCc̉PͭXv8iMppdQ</"o'=ʪ2 :{s0كULPMTylFBbZNȼ-&;(kGtߘvX}CaeAH8`Y\ ĶюEZ?`_&aaīr{[ԷAU300 MF 2ӂځ9fQڰ@@j.o@nn-fA03+Y}ëpӄ`EYM|%0)~EJ|b4d9Ce/]< f}[o+䁁nb7 /6\1:Z=b{3U%ǣ;c)Bгd g-y2D:*P#H4j)h*C (a,X &ph]ٙdojfZSaHVf4U6:ŧ<̐Hx/Rl[XAǠ6YLl˺<̅T;sAD'U)͸MȱԵmvڣ?Bk  Lq,AZU|B{@G_qĥUFn(Q>.:-0X/>ImXNִ `,y&B6w;y$4Z=Y3OD\ g5޻jQ.̲i5Qڿsitt' 򝘆vŧR'p8b}~ #GNZbYr%g]pr_Iq(crcm417r]u"i1U0;`:f" qUDbXāO(Ԗpj&?nQg4[f\U_1 ɚbFR ..q.+mLJ+{)'ǸJu9RsB#^qÄ_,}E&IC c }ǝA#V婿Y̡n n^hkLbDU`UFȄ+ xGƐzհ@ޜAC@>6ecуѢg(6#ىYCMa,1,SKq/lqMו%M41d:jc~> y u00Dgg(f`f߄h^ 9;MfX^"$JH+JZd&=}j"6cj5PDjmw<:Eg5q^zornEFWIWVqQx@a1 NDd$jNJ8ݸ* _'= fU&V1 FħJ#@#iȰq/YhP>G:hØ:TLj"մ1yyZ`*[X9fb ߎ<)@Z%  eNK-1&v5/ej6c?.QLB8sEo 9C/`  r[ꓩ1U*=9-lG><_4ax ;esp1E@b(˶|#4Bs6i6יH88P_f>Qq!Uy mIJΉWCJmYֳUq)D9d˙U Ļu*V-`Q}*;+##9f⯱_Y艳Ms Gtپk0(_܋^2gD9e"~mQ-jo[#D.)f5y}Uy(&uYNevɎ|!dcb Mzak9񁇚.*4 [.XT4;o#Q%3kipLdSLdD"y}ƵU|̅P l3$",al9a%aN5-\b MC6V%Xު.+v| 2;)& WuQ,5mc R oW@=z$h์n0 q8#ȉlF;m|oKS& ZPyx_(3' _ePa.p;՚a@_2zV'vaƟ m)4tRJT`ཾ V5L{}2qU#C-݅G5U X%# Z``RJwkt B&:H*7 }NeZ7ZNpӥ +W+OeܴP+r)_ D8C/ kc-v\hbլ-uXġp ͽw[;f;Fr̎5-Z>P%mCb\+1QJKTnZ{/PEpFH, cBB5)_l!h#`A:2_a NffU~&f2/R{-|.b6nC(u̡PtpYW 1snhW,P q0^yA`6Egf;>bFUz)eB5ɾ7ħ/k\[Q3s:DbeWM:f &uhV,k Į̿JIamlN. 9UμB~JT/ÃN)@aʅz8@_8>,z.\3)τc)&įgh+ 0̑:j凔<Kr_멢4UTD0GY5 \%,E~u(^] F h1˼BYwP0Ӯsm}2eqb+V$88,K|7vȷù4pYL-Mned}56M퐨duDDP»+ ƅW O wr\or7`9fǚu+^H*ԻQ62^ɌLf~aa6szc;w h1F%qktp#P-Txe6-U?E c1n. @u ^ʯ/Q:ĸ#NJ`~+I R@ Դ'뉒L~*&iUPB+EіhX8 U*TJX¢m 69~W4 /ufe2(806TqT|$i/Tn.0eœFC]xEjS#_'dqxo)+ CQϴfԶ6!F:H>*HXDŒLE^LjOu0'V6XaX " nә[䏱P`㍋~jުWRʁq+qڍnW4⪫#^ˮL񙾭ʍ=8ƓWW24b2IJ $(1XrnZ~s)]"f <448^K~x:4:L#P3c**"|[_Xw~yl zL0ñWm- ܂J~V >rs3z?+)Wd0c'$'c(X=MlsMiPdH6&<-)\~tuv{_:nkr~!Pc8*w8E@Zυid]{@BϽ&k5Iᝑ]PJ7h?S&YE<2xZKDWK3b}.rOU_5|+EkЃ)0h1ЬPKo11?grF9n7 ?`g8Ċbt j; !;խ6XS@"UFx;ۃSHA9}׮i6?pqs(@u_'&!1AQaq?iSUd\w~QYzՇ+n*kk j08(24@h nxbQ[눉['qkJ+i=A UIDžyc\n";̡-!OD5;iqi7 m[J5=k"P 1Ɍfiyg\G-\=dՇۙYpڗmcU8|XnzsT tP[KV9 t]5tfRԧs\L˖.!p#`"L)LQh@ZN(wy L1hf")_~r(sjr*Yjx [ J8p%OeV [Ns~G(#r 3ZW6kHD$ x"w(."Rوcz`(ï}ծ]SL#]>~ܖ[sKp# u(2'~#'z>N.}b s,5 hM2w9BPr3[=HPw3 u̠",@h+^wD-iꞺl1WCZ!lЀŏ^ {uLWH{̊]3ҳe'S A~ao@1]PEKQ\;kɧ01}?0b;&g)`A,izbM3%U} X#߇~|˼58]]|14]2D^>*YCy`bTn!iSxz 7SbUDV(/z|sUww*/0RT ˅~$ j=U ^ r[bb¶1P ~E@+ TIJ˪% ~=HSΌzfW-|}\#{T\7g/HZ_ P\J1ZCêW]?u6Vt~v/4XfvsjѽxW0PϙSw~Ѷ{R»c)3agYh9y3/h/GՁb#bO\A0WeL|VЁl˿iUL<[gz2^Yn:_*(na7_t{5uk ˴SOa4xV:.ξ^q1yRy y~`RńS "u0faa%1:7y mejަZu 5?bgWcixkZ玓,׏ ]{Exnaƾ19Gÿ^5!KzCG^JDzE"jο9-e￸C:SQQ+JrJĵCf!A_k%U@EŠ<`V\c׿XH0|V|",U_V{KZ?ys”KRX 8C6O 6iO:;U6m\-Ѭw0S,w#ykxZi,NDFc,X|pbFI`emYCotc<,Ɗ1qmDv;i;V5̝2y g@W[;Kj[B _'?* jBPV5uo6XψPϹ(-՛03]>nH S7`w^ϜWkM HG@+yF]b5W늂7n}@\9K:3[ ?L,1}cQ q0)}a6\ Xzo|fYAk4=˒pKԔIoD+131pO礷ںeBqL|Oʢ{Lu]uWX_ H[Xc|/gǶ ZYtAE$h(/9vPW`4|yܧ^=Sk?*+Nk, vq;j4Á/禛YZlhIyF² hJ%z܋24۬ 4Cf e}=paRr??g2BL{Dmw=Soa1.ssH&WSBM ,|0nk}g~ v>#+:j8u: JgDnu@Lk*++5Gq!`YhJ/';C][y&5(Ĺ/,Õ1UøѲj0z%?pf<Գ]/s۽UJqFzosH-,. K3B(קS-g"en>+~GB0y[Eƽ,43=LD1̅TfTmdbvZC^W)N_X;i^(+NcɚȺ6n 9:]KmG.Oc׭Ҍp_GLEÁֽr2FOstU-܆?,w{,k߶Р2] g* ;S{i~`y)Xa/:|̮_WҏIurNs'j(uuW/Y~VFWS ^aj&zm *B 1]pF=)Լ9z~|}q|b:H}:CZ"Ӏ*qH eZ+Ǥt5~풍r&j0Zj2a0.LauDabc >jx &۸.Ĩ1Usq?P@}ezk~=!O-{qˈʛ W7Gu0Grm)].b1fb`ՍoF9樓iDSwr4;Ms9_vЖ!|Wy8z`{GuoX`F@QoEnk-P XHxIrNBS}̪jxJ՚hdd''ET\M>%ӆ"]%oA[_ele u3%ߚmm\ju#i 5Ӝc ѰJU5 ZxJŏ?\h0<9hNu~"4~\l2`38|@.R%VSxt1|~K (2(/ï>` ;߽m.+.Ji KJSk[JU E@?t+ƣS_V"F<bmzWErv˫z2̰g;A9~̴E]z2d Mf F'~% bDVbPIm*Y\0..ږViЀs oA0M5ætii VYy9K##LcxCX.`Ѵ2zKA Ee/JeZr-w, ;̤Ux QU̖@rF1V5e`"":ah'iakcChb-) ;#}'xNkܲ ͮSaSL.ȇ]bİ6NǃErzFKP4B@%**# F+&*3/ba+'S^=5|dnCBPZQVlCkb˫1 "\U-Dm4b%j"4+J"Uc[af)6 Wzd2@SQ(ԬDcpD%!1AQaq?EBcMbP)%|ڧqg*]g M^>xtfxP񯌹j˧{ZP[ᎃAI?)}yoZ+x`쟌uto ܤeD[ұE_dk( jU -X4`qL!)DxිJҍ.ab&>י2:Oz=fȕn=*!})E fz8XV++tFi<ߌhlHMr+Hyҿ?yva$  $Wce;qsn^ms424N4]∠r-P< h@4nCCEYx5$4S~"Œo|&*^w8;%:ɏ2F+˂Q75l4 R:EW>6aRA;|b%ӽ}`YZz;TV(/Fjx6s!IM+\d:_G4!SWfn: ]z6 ,*TNz?<`dQ*gyEK >%BYucʝ%n“sDI &1@B `F#&ȚkzW6%x>sOKN~2K&~뉐?BR;Ǝ'@5g?Xu`ui׼:TxPSA`/)nx4 ^\˃QA*퉒XW7MS1*ھzi<|t1M6/'S~.(MRrD@V$R)Sn]pqLLdЬGX"JssD-]Qq?&pf _;w9¢N tt_ q#|yqg xIw2PMx2;^Byi~ist"2ڼ6q]>t(qEK1GnlAt/{|&8Yn9N&Nɧ#MmV&8 5<{8Íxd\=l /))i~^74y]8RPզJ8 >˚av!??y<<{lH:2` Q^馈.2ٯ2R C.*c6˾'iTlg޽kp/;FsRcA#! Ca9wyX|Z^4ga*GGͨtgy0{U۽x..Ů{h.H}>``ӞJw<唐5LxI|aD^5p(΍'^̟>`2&A:>3bPH4GIV#Ǭ Yh#E]s.Fاqr .S8Gi_ᛁ$=6ӓCs<0hZ"̨B; #\LR|m zFc g~Aް-lw?>03Pm>g_y(;_h;ZUc #ʨ]:pCe=~=\Q6=޵|}NĴ-mC+F2]WAj4/ ^i?08kI{OxHV#NShSTgZxP9>]l0SsW7!"`qfn^w'9GЍ8@Hh(/z 4'BmojA8âD?jQZ4geƴȀC:Vj3vDUp@$fRcCϙ~0%F;/EAE}WtlD( 5ƶ:=sL MqX #xtq=A:INS bgT'ǜ*''*Wa}6詚҆a Eۊh=%o`M8h||IFF kV͚Fxo xu˗.s=W.*od^p#t9%Ku|s!{5\ۆ޴51 ד^7`J튲I}}cyG!&~11x[j4e\شs=4*91>'D"~=COYFyۅboPHkU}4XP^dy粧@UiO]fB]3h\ut1>?.}c,M +.#]#M J[6$񇪺 w; iR ]ƓXC 4e9a/ΌY^9]_RC=Jvh<]:7PA7G8 $50 -P z{JqLiGHSP"sMT$^c0MktA ܀"?ESy/13@xηZ֚%_˗WA]AX*֜"?A-%5DpcɛxUkTW2m0EVZzT ZH3: {'p`V:qSaSw^ (4XHx1`̢YANӋ1"*ߎ ?=a؇{yy4jSl&E7V>٫Fu%Pk$ZzgU"@w>L+@S2ȁt8P8?3+ݙCT)wlت4gI!;,$X_8x964 SY ۃ nur%Q#.$5>]ก;|srw}a BLCr)0p*zlKثxM0mۅa$2)nj`A,(>ta(n _ ~- N9S{xy'Nz @HN4:YoT*9o$$ZۣKPh|mHxߌYTK659bP)k,]CUx}*awY$s4`ЧDw+Ʉ0+Oo%f `ACNqD!4槱Ȇ![6d@.V{TO)A!1V[3 xڍ;QV&xQmj0o嵢_~ŗCj3zq ~pǜeja8QDj*u0θ(o& pļƼ DqBt; <GCL*_l]s€lX '= *Lr.Î; ewd{0=9&}c->Eۻ%=bRFri{=׬Y^>XjZy[p ]󏮢^90Bex?[Ό`=󖺲lFU9-%fK4n-O)y|lE\d#7_G"}$p Ug88ȒSzjͪT`89={0ꦷxPDE}qxL~./keGA¨@(9Y8XOX$@ QwBNSu;F k(>񣯩~"j_r(oOɼ ݭ0f bDy6(W)V+Lڠ(&숴F5=Q]VmiWBˈxzͿEy{蘿R#pw)G[(Ssz߻4[3+*5^VsF]<2B=s0P>/2kZDaq2 vӐxo}jW/8IWPKG'2"|wTқ4*/ֹBƒhPkEth#O-euQXqɨ٤w}_X)aٱ0 S\e4)߁~w|1IHSӗj3f]> 4JŨ ]0'{?XHŜv %z,J0?Y>ش]7m~_Xx Q П%|m^f"$5IϜL4gkJkP-X`zƃss_.G(5|e;7E eFm4# Ŭŵ h,(=#Uq3`twȍԉyPӾƲyPrx6#/)yUưT) iC904~FrH & /phnQ z8G`sk ަ:n֍!G+ |YhU[, .\{ o]"8βp3To)}s&B@ ۈ0wnВ],?uo hŎ7=E\ o6[0ABp};+Qۗ|`6ς{'z!ޞRW6|2VA[l0Ŋ+HK=; `>chaJui䁺+[ȯ|Z*1>+_9EZ&B gZ<%D6SgAu3H 61FODbQA `,Jݤ{%mm ˁ5P0 뫊^7N}W|h!Itc )5pi߰;r06(dD1Z'?vB; 8?i#ۮLԯwH*q=_ <Ϗ=f(|Ke:IGeG$tB\+ˠۆ!%CyBNc1橤nQ1Sk$naY9 6ͨ [ŤhwK[mYJQ|]+{ۨb'~81!qǪ}ؿ 8U='. c4b3^#<[q( yQv'1]ɨy,x:O;IӅ~Nmur^jbݖ_ 8*&@z]GaO HAjo[H;tO721 RuM+>1ćs) /6t(Q:B^C7Ved9Э8 X `jG1tw5 NmoZ^Շ1bXCHiy+5(b&db Zp!VϬQEv)=#fSx}֤"7w%jwADMLDvqל`ċb@o5j (mz1oQ th'{Cm- GdR ۃ+gzAqIEKC|TGKRL<<r;xcsM64wu[Vy[w}]Ei]].=UiKVJj9 PaB*T $a1xHN)+p],v<&ciۀ!alޞ?%wW` PmPˑ)"šh1 @ qv~iO|j]‘PҌ/mp>Mߔ: !5,P4&`A4qFxܾu`w?@Ѵ QbRyȪ^OkRn˄];]2AS~'`1ݸ \bE!y;wa%ExA;t(uGAsH!KIƉ@xo)Xg(k6'1^Jhj,]s%69 Uq΅ń҄pBoWJٽl^H YoZǢ rd6,i\ʱ& }T Qxme/F4DmK? 9+=D)7ې ̮ҷH/EZk;d" CJY5PF%~8ϽkY(T0jtl ٮjiĀ @׍.a@!f7Uu'~Mcry8 Dwc3xAH4uv1H{Kǡ+kp"Hx_­; [t h운CO5 !4+_}iXC3v lF qFՕnd<:! s_;:2Zv Es=cPpoB5*nFkJ /ih&$(k0n IىMuZ l@z˱ЫM[J[3E/9ɖאve0:lK"QHPu*c"3NSXRUE!|8a G_s%B#g z@w"4(Bh9i88(ARSorV _ AxEl7Ur1ӊ~ Y_ ݼ`@;W(=c7қ4IGtw>'%I dʿ5vpt1?8 0pZwHHsw6*ޣk_f߼*T|~N#y-9( O.2rpqyP!K[%\|$+*'Q`xӈKJ1 ϷƱpN6_l#tczLNkCp.r9 o5}sw8n*G04F=4=F;a`S!J@}*& q>Y(4&Pc&.Q{NCntNjZtwu2VXb~'¤-^n|ͽ UN! N>MI [4KOȸ6k[&Ou;$**[ Tt#en0.غG]뼠ٿqB(yQ&Ox"92)†b ~t٨.A,xO9OPD6;Z~g& G@ix fO|`sj!b,96x;uop RJsxXRψ>/rY Ds <)y<Э[;A?gP ҺxqT:WdEh>Y dI\Cn8()7 'sF4[A.n؋RעmOhֶ1CmtPrM$}L RVoQy>.V7ZW;ۄgI^mMiuR@JIG1@tK.u ƀk|a>9Xg31$)ޅHUj 托y p%bzйTMAָ<B$N }xXIӽPzJ@88׻F2+A>|8ij w*瓼pZՔܠ8\qTAw㎽dXY}. |[Q TB Gg{5:N_s1y!_yxv([sFtJ:hv8G @4{"F蘂-ܓ0*oRI򦷃Q.H.9Ul=s~^hZ=U|ֿ66'tnL#H'+\ O%ۯ</< '\9 '8#%O`1%nCb="~L%%o$KeEjbR[95'wK/\et [4~0V.ZkcZ:iAXRݙp$ yWh`׌_Ur ,i8IF҆Ôb{@8ŋD` ~8#@P2+œ izF70d8e(廫12g"()%Nx~4Ձ 1Xb7^?yj=8:"dm`x =k$ժA#P%(.BϞ0 8.%$~c6GYR|xlVMq5A6] 79Htc(.ixfJ5˫|M7|:|BQn[>TUAEKv7\os]siF7Á)*E7( Ǽ:-$| ڰל)J{/Шy!%z8[*'W-Fp wTLwWP 4XД?#[)4i)@'PpT!'_W` ` ~MYQ@]E~[ϬzG!o0\c{عW|!mS+'D?$zJk&?w蚬-@ x,i1p~ 㼀&>S0Z'~p{e{sc^WjNJgw)+%PSOaHipa_\N m|d AӱĞP5zySE!>\C<b1 - 1AT=X [?bb:iXz.2tnf Jb.mJ,MW͔!)0%yՙCO&5A+sbNzh9.d( 8oww6[ yʴH">Quu;¤TUtZ]$6X2*!Z`C%lX*tF:#gxBcCL-L;b5B wP<ݠvRʷ׸$Q7~MP)+wx\pZ_J6߷Y|K[gA6cm0χJCnȉnaOMh5=xW_nAY>ܧ ;Jo kJQKL`)x7T6Im~Ib sMT([YjGB}rGYER0ӹx}3"d/GGu5nD]hG\N7XJ'Q/\2< hI}N} h> liԶ+ #hnY/z<4~uŸ[[67F:qAXXH&e3MCvaB74[.P.1H]O?&;@!u&L M"W,5=; J׺Ɗr1a $%FHUrCd,xrDy{vMB\Tq}(!hn3ɱ4YK\O`iX-&2(~D}b`f 笨UW{b G.bH.XPx˅D<9A^q-u;4 t^3~0EJXaiv q9yق1c|sH9 RM DV-b Vh ] i>rF-}<⍃^CwX^hlycG.?S#?!0uTFd$4 Pb "6mo]e4;`LXɱk FK&AGIhqO hoǏ.\{łg{W] @U^`@+sT4{C_04<@;7/B7^/V)bND=ѩ]_>Ph`$C|8G#1:-lֺ1WpFDyPx~YlĆܚ:kzd_=[Bb8#9@.抢;KD@t =# gY;nRx=~>E*vD9~p p &$qP/~­j@"4碽b ecҟlMJß@q*%,xJ֜,358zO?xj.xby iIq-P-L޹}=w;\ɎȳSŵɐ] qk֪J=g07d# Ej#E&Kw 'i9rAOp?8@'}8 #× JNC(ٺs9]5$>Er@ >@d4c4`GțthO Wj_Ο V/ÔB1Z7\_+A@6qKPl1# sǯ<,_~9ׯ:;_buu֓̐Q$oʘX7~>3CZ ~a%=sZȇ0TMp3MoC:ɀM H5% Mxfg 5e*B `ib S +v/@" W<K(0!ѡEŧ'pdDbGT&&1!RWVgK.? 3j  oI>~1KJnLhU:vf$7H'Y Zy~qDt;R|aH #[3ZvgZ22 FU$c yùnK=UߚfFoO-"gSmNQo%!EiiSUޏ q{ =u3] ˆW~H|0,bНVB?^0b79 q\q h-v-y!`"M>0Sߠy;YjDxWoUf~f]h7kdo^1 uu %x|oIa=$0R_xy´ މ5<ҠRp*DӃD \gcMvg3K%Kf bh")8Ԫ󬽪> &<;NA`_ghS"%۟wj@D'xQE֩ߌ`Wb*/@ SD+6LM!8\1%m1ÁDwJkMSpƹ2 η6U&9<99j_pUR_sNAsrZ>ɇJӬai⾑d ~֯~j2l;,~\\Mj85y_>r#;lY,^i5 =bD>+3˧IzQK鸏I1_^7k{=([@Cl'p3#5+W9Qomwyz?)ةNovjq7K![:O2H$Q ~ck:L:_`  1j<S;IwWz5Vj8͎ 19zVF -xy'9w[nҼCsWg0!KKR5!gi򠕤 ?O9c,Uct~ŋB=SDo~z/eV/Dn\+`;ZiX hG=3^q'0?R+TilhX%̳]H 4$2Fzv#>'ӭsMn$I7 \.:|x׸SRq<eyV7c59Y+pm;OAVVY+0 Q2yհC qyGW1z%DmczQCݔxúw&ݷ9$UfTϮҝ,LhE۹NS$co,w{[ãk8J&#rkR2q_v^/uMmϓsj `z^Q&72@C@s־":Ubx{Juzɳ.m^[)DsErYH'<18B:~C޾K\XQYxP0GwG_]O|P9`qQRU?VTtqsmHDbdu_?m,63u%w:g*׫,BVsXKRc1}]OneГ4)޻C N9*nj:8Z+k'Ujs q@xo4[| v'kcQzgx 9jŭn̥R&ұ}gNIo0!ʿp(89ǶkBHW{3HpyJar~jytfyNS>@*2I㠥0y\KIg#<9j4v6ZN [n8yiޥI,-VTRMfp2GNpV@l8+؊hUZtbnHrr8GnH ` FPWhbU/F_pƎ cК5R=?-/s%4hcKVS6k0ϭ_YOW@$bR $!C+^ F?m#V?¥4 ƙh?c[:0Ɖ 3Nod[:v{Dؔ~g䪶 cd4,y+4=Kۆ "%*rOԛ=Photoshop 3.08BIMxHH(FG(HH(d'@8BIMHH8BIM8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIMAdobedC     C  0" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?NU௃,Y|M ;faa^>mydyݏ*P|x?dBS*)t p4cs53n֫u# <[iZ^b\۪rnAV#w)*NUQewo$ڿރ;߉q7t.7|/;י33M/,Y1ɤH,MpO? &s+7toCi*dd0gU.jS]͏:y ;'ª_íw dǰ|Nxw.Wߓ\t56)*~Lw*̏>NR뮽uwLr?|LM]v^B [ߊC^kkNNlC:(lъ!w/Zڭ:U΍ͫIlOj `"1/l3W :SV]{Ykg{vWVW &v\O1cʪ?c%1tUWI2αp*l%IO'?\~>gq41nd}C gںsq Zi "4wm H4wڦVk_.CxJ6;p| 3^I7}9 ֦LG9g;Tۿ8 &s7uN÷^%~=%.ykl&K|ΈHdHϭCy %ai;P7 WJ)]k~]cў8/?U1i/,zXc2>L;t]|-4UWuz.Nj8]3?>s9]7 7uWZ/~_48ڞ>`E YUIG>>Q^5ޙ{-B|lUG5YQUZ[F>Ww۟^s9]7 7ug>||G񆹦#fe8eGRAX|MM]۟^ȿO"oȿO"oY:uw/tUӾ:8[x_¨vaUF~"ƨhCF~"ƨ'bFGܓh <~?.|F"?c#i]>ǝQ^?c#jSdi,:67-E _P #h$uEz'2f:˿4ȟ?8&t.Yv}źG 7jZ~(쟲/`~! zS-?`VgI$t;p(_T<d_zo0|4 9$ x;;KMVN{{iyK4+*+㤛]wƹ)CDeVSֵ`*CRnVj׶{릝G{]IZ;tf6 Ls*uf1d"b19n9m=I N9j񞘾oo-_ 3"G J95zN)u[_'Eꆪ<媱$vrʲ(*Gz恤^kW7 &8 x-7"[>ib Hʨ⫕*jJZ5DմE]&MV5\kʫ @Hyc9 чb}) n)COU ;jSN6R#) 0 ϡ[3mMm"B2ےTC`#PyE9C<x(q\, fL"$I 'wNy%i瑙3O֙$5-Olt %ŝPn]c02m^>\&{7n U U9JKަ!xᾰ\wtZ)*'f,`@9{N:\]B=[XW6n|g6cѴ5bÚLZW NYcP*Dh*IBWSExF-Ȯj6>,$4s^߳\9ƝjW*]B}y֍JHTtޏװt1;jM- ZBLTAđg2"H{[Ŀf5 [>p89ulc#ҵ]Lf`23\ғץFmQY=߷;9wo\q3Qy5`HK`ej%ЌYpF8UԞvąqqx͞.3*^09}l}53oM-#j 4hzե{J5f`o$z7g B#yrߐ8ZLpA#9Hdt9F$9ޡJڵ̗ڶ5R` rxusTz,4͖ڪG(yhFUPiϕ=mxTU.%%ڼ Ҭa{Pk,̀{u6ߗVҹ[Xb, pNRz?}JdH fI *KYQ+Hzֺ?>&|7_ [hRh| |rM[MuZZ˭Uke{.Ok| i [Ke4C%&o0m\lNk;m$ǽkh>P?Q^:W-už-"K+8HH9ڠ:M0:“R$l91 Vm๼=M-WPT3Akj @LŁD{*,{I?˸[ ~g䚝M:+IY '@;6sIC׊^.z챉>۴05Gk6p($xI>&o@k~cZnkɒFYתg#`Z_ \ԁ<9Dl9)8ҦW454RCQ|r*[I07ҖQ"L-|.@tEq223YZZ&[zVq3sŽxx6GAeC˫Vs$5@ r7Dح$D,>`_?^r /NDO2%PKt:+=OTMYO=!eOi[_ M/p5 g˒}s^൅+b$^Xh-'ѿs1id_2Y*kE5+@69=׾x~mB|H`:N_YOgD*6-a1 Q_=W3h)nNys ArwA JUˠ];*)=~u_fv9Z5G7ӧ.cg&h;GJfvC5Z]G1,{FQJnTFǙl5k֏tE' #ڢmt96Fj+fṨnPÅf=X²J\%I{[9_I7&<q\& Ȯps^WZ #5L®FW$?z0Gw#uVԮ';XYd$Q HUTYH?c,Z6(dDB dwzG?ߊ7kULg5#4Mr^Gef#,+wlQH[*pwm%סzRjRdzLBrWv3W<_(UIE;KY#VIE\$N7y${5 tw-$RIig%jBb@ϧ>o+7քc4߀FHrn<3ilfwċp#a9z%& VpLgr̸'yX-Um]BW({'+g,].V.Viwt՛6"sMJ.%}x>#y爉@cL=? ]>? 4H~w5[ w8S郑WC^(ONTդht2l=E*r.>,gooVWz/'_.k}$+#8zu<,A|>p*prXFz]O%>qeJOe[䷽o-QĀЊkj߂ahRc&@8ؠg`>u)ozWo}-kF/+te7nkVW^m .'ibhש2 m{vPxͼn{Xg'}3^sgZ5zVh ͼI9aX1SA⾯)Q)N7E۝x8ÕOy!!2 $f?:J])$f 1F]I{]XYW$f<-i֗R<7k[%Sez񲫒Qq Q2*եՕ7[Fǭc+Qw za*|9=v.Xl)8k&ԡ7D)ߑݷ8usҳL"?Z$m6ȬG/a*O N]zܚR6g^[̷N*_V|e nW1]I[I2iB$ 3ֽ8)-YGiFٵp@N zKyy ?m3$K F5@Tds֒e-8][WE,8K|V_FKioC=b-=5L7 'P ^@'1֪2nFU(ӧStʴ+ӧk_c_&8n\q#5[#v`DQ[MGXGZ49Hozo#y?c*vi5AJ7^4ZGk$ }.bL)@Q~U{+J+Kzfjejeo"V%aa#TD2F nLdz)ȧNbSJ6MumoZX+X1o(n+/sޛ gISMSW:dν!eeʀ:v#{W)O4Xd2ls$XS5~ "tyU@$O0x"|7??^X[]}tH@8?885iC8ovn;T)C7$zc+J-ờn}""H Gs_A uG"(%UAUCq^^\jvrQ4͂U*ѫV>}f['*Dž-rN:t8(Y/__;e񱤥ۦ=o}R8jK(t6mu#fBxcǟξx3ZġFfK"<ԕOSV|?|~-%K=DP܌N:r:_OB922wM+ut='h<+Zҍͣ}W>`>SvJdSU5׼5ᗂeސkPcAFq:o4h p3}o %! !>zޅsV <;8'18/Rg%_|EcgX(i'Z@);09$ A'\w;+&O t[Fc^]^%0r98^6Ɩ] OF|HOֺreJIhߑ*RM:++ڧt}fcw\dWi נ\15?Yߙ /PyϽ}Aq!{tײ8=JaLU[7ż7sJԯfZI+`cVJM4f"*~c_sJ䯲?0Juy.ܺio3.72AvlF3;Si۹ITw>k^&V98,q##(\bGRD{m~zfY^zEm:2c$aH5cQծo-`ZO&7cyc,ㆧJ3,f.6jqMy+$ڹ 6a8jE AKzJ=jaq/݊fg+/Ar`9=kp h8YHtuyTro䗋5vqP;X.a2~^.Z'UpwZH̭M|[Vd7UZfۊ|n {|MѳVp-dW|(OItҢrӭ?d\jk]/nˏk$zb~H@>bw?k<:3xgU?򯣔_Wg8%0'Զ|@">YghLy]" 7󁌎YIE.|`%WۭFzv4>ЭIŤϭinevlNIGHעMxWMƫ:J7@ִSHGQu4̦ @ |$r0+鯀g5: ޯ ͑f'ʎ)I|R;۽|լ{yo>#R-8Sxsq;׾f"1FJ@cg+lKi~u-ewrHD$`p_aX/G-啥` fiB"02zcs_dS*Ui+k}^UZnNtO3:񙕴%KH ^}3|8@@wm*wOaWx?l O<5e_FH4idcd* ^l#j>4id^{s;?P>@ `_e 9{&bV);;7l\M*|*Z=7|J3kz4Q[\Y5)9= zORU 'L̶7:NzG;\X4`;{GN{'O{{KHgiA,""Ol{/Y/mc"nPrnUֱ\=U?bR9P_owug6M( ({=ؚe]BVE wHA縯FOlkO}ĉlxs\8 M,C%]=m7Nܟ [qV/nݬ߇]V(xH줞1]2O0>W!2W57ҍ$ۙUkNjN_V3J/]jz畁UtYW8Rk>>#sP_>]k&,mW!=e4Ҽ!H(9||C`MgO75Pc+[%<kFO [X<v9m-aJ;+\2:+܂(bWM\^:Ks @ևtyA$NZM,FcgRDk6x;O>kWE儥r3WVo&}-zΚ #Gݵ3na=:QxBXʺB1pG\q|0p3gF18gy5z\K,0+ADX=ժ`8B:WUwg8/8[F$Nz=BT}EA[`qPIs| y>(8h?Y+J!1sGNsT.|MuKg<5.Jo]QnK0E<vsbTh>5u9R\*Y|Q&F$@(93\02)$ zm@Gki`i-c|2rD#Xݞq? =ًiGZWb;F-ڥPr8 Ng7VO[c˸d z9֤Ҳ\;<Q~7h,l|#OgyޕbɸRwBLlCZn{uz]_j67<5nZM VqTӖ^|FDh"BߖnxZ"Iy5gW^J6?z_O^.>(d}EcgRB+^ Ětۀ2 Z,)Msu[38q?=V=;yZ3o'$ uZ]\l6yϯFTy+[}ED~_># Dk߶UKg1k@KOCuY^N#_5$9|x$e@'zWQxh˓& u6-%ũ+7+Zϟ̲YhZ2]؞U9-0e>iVN w/a'pwO-U'-16U LRI|H>E V-,x-Ʊ{.zᦞBA+y _isK$)\ANuaR(nA|-Ч(tYn6?x^  񽥜rC/>bY#LG^NbЯh h߰aڽ 40(˟*>Oqҿsé^.WOx|~6a%x??t +KԦo 3|YmxÚ=?˲I!.-_q>|-wRwZ$MFe־'+P3x"WJ,U#~k`Cߎk׻ZjNnZSRW~GV:.+3]EK{ޥwrU; ǥzޏI/t I <1Ҿ?`šh_Eͤ iZ ^O5IX.r=_`ĩU>Y;kk}U잟6>|).5 hU vݘUI*P^7&Y4=+1u c%H`ڱn0:x^.|),VƂ-sLBӜdJZuvz0J8Ri'emmk"|HHק2pyeNO$\OMmw֏.oi R, c\v= Լ)k˭iR-(|#nǮ+-_~#ռޡyy mn ́,PW9n(QsS'ʢޗI;ɮz5eʰuvwgê]QQM{QaX>Orx;ǚW>yg$H3#oRG̠d"d^?k^ kk[GSucj_Z;SF4kM)kcc%~ϳݜ9ҿ<@*Pmi{zI[>-pZmʷ <5j%͢[5"U 8_w'iW)nE'=_ xnWӭekv}FClH)3 ``b:muˍfK1EK'QJ$x%&RMGK궒z\-fa)÷Vy #'}"EA 4}6y'zwqȗƒKHi:w\>onEpUdȯL%5-:P+2ZBpv qj[MkZ,43S5gdK VE 9I W!Tc֢1ؓڽq:?%Ĉ:&7vG/#4MgO75z|+ I(m$aO1^/3G?ssVLd/ E9;7өb;ms]ap̓V~a$ ?aFF|]{>S^9WL?H3O1R{WUY2FzVɭ}.Ag?xD'm2 y4, y cj'Ajս4;fcҾzEWFIE۳~2<&@*^~`01++e +|Ó>%̻lʃƕ%c %7̫ZGw|80F'$aRN17Mp*9D1N?洮Xmu Ŗ O ZӒZNIZ/^_i,,6[iT8Ie=?i{\-FYL@0Au7 ,rN ^*aT 4MvdP׫SxKbdY6:pGj眣͡htxݟC-&6߼wҒAkK\(b[Y HU2*zr*ƛs ,PzT~Ln| ӟ]Qv>r_]*0=+׬M:hK.Tr~ls W c^gve H!n&g'%xFB ho jny3|,BV<MytcF5.v񾖾)b0 UuI~RSqӚܖ˩󶧦G [6/c)D)X(Z7H-g'`zUh6{׭9 1N ,]Om*114Yu#=*$t9GZ@O^pk^G/ۮxj@2GAOD1ZL3صH%TIdʍ"0wU8t\j2jhI!>vuS)9/eWHU-{L űWoxU-aFfL?Zu0fyxQWR\3,=G4m|NCJhbB>Rrb<;S'HN7яQ^1RM3:'Mz }a LxlYPbvu-=ܦ!㿭j#_Zd~&n_K?#.>IUTNO8>X=?fhm֫bSN0k|-~>.oeݧkx~> ,Z5PA=$+9֪^Mv? I|-iosExbyf#8_^~˿ b-Ϧ\J;>I|1W~7_m΃yh֚Kf+7 ¸y#™H^ i("ѡ-wΊE['cqpH|&>]4W-uM{hҷFGXPTp>9yQMU晥tו$FHPz&Jn_~g(%693y%|4L,%nbS oB|ç^m7-|U4_OPy;T3a4x_B(:RKg Yu'Sspkf]ODM74`#LͶ 0ۉlP{wqѣK,_~W~}*J.w}Uw#&o>mefK&u$ep};f>6^{e:r7StH,tO뺓ϧveC4aAmT[/m usF9ƒ_PSG (1^vv*ʴg:kI_KyiUAvBI^ߥ|3gvz>q5>̄"%W~_|%4^M{K l[xd2|k$;%b |ʹu=z80Q7f-gx:%%tߥ࿉Kg]NR7Zqk5[!oq"d[7qv~-|o6ki%c.֯|h-f \O<~6EOض 2)ۙoI fROLY4?kŤReMn3!% *N0HȭXc[)d>tal׸M1Vi{V/j&u® &O"`=jIyp"R6 mݑF46Fn. cU,tŖ >ݢ4# 㜳1=h:{j&+Sߖ'A'dJev-:gU+IFC'8Dl-on% +ciI}m}%<,em߫g1\ݺ2/UI`)?tW^>kollGC{W>+}wOuGȊiF`NAn}ҽ3Xٔ45% cnӃj\oVbZ/}n7T;S-g^! ,nH p=*Te6HGhmcȑIU#2kO bkh7Q q' d8n$V~ kyS4VU mFνwW ˥Rq{ZۮTÈJ8R\RnO5OZs*Ϋ= @893|E{]ټPZΰvW?c\V,DFy5F4!@5-C_0c|!ov֚~=ͱrSsc \3T\Z룺5g#x+|4T6ꮾ|DM0nCM: qў!#>f>^Hr*8ztzϵoxzfF! OƾEO nG#R}@K}5,8Z5$Bn 6+G_Cu%̬F}z\Wjl6`LpPB[GI98^k^R+hbBWMÓ\m".%aVe`9y_CU>75SFgX2!8+n7e=3]nr< o-XW?/s_KI+W$Sws ZhyQm 0EjxߏZ`ѿ۪^낉O~҃f9n__AzLtI~_.''h=hHKŒ 7ZWj$ۑ{czl'MrןZˡ,>I/k^v#Olwx<%}/_-ʌɼ0x~~sm w3,vq9$'ʾW?-YHEh " ;ϵ|'؈Tm뮻N8e^}L.=\x[pOqaLs%-9Kx77V޻w[6 8 W)3Z_'A֬ԒV , myۀ@W0|A~WbnI π16Ún]??sMdR}֒}OGu*nCyYC$[SX3K|${:fH6&E@~)?/8|}+K= N('xǯj?g:h & N@r3%eGIe+>˫ Zܾ4N>yiC"@QG=1һ=GCּG) C8Xb9n5x+v(]Nm- s ֍c]T>[kXvm`/zg#4l_p6┽e.o0_!؂p ?u ?g5*hzMI?ǧpZéKhKq .e=a+O^2oy d7:Uv,f\G9zum]jӵ]V VhսZ>Yᵅ/$M\qAs_)EAqs_S~^-n^t[UE=\sW?G@x鏨=_]^륕pr~_3BXv^FKVep$tƫ0°wZȳQ0ڻ-?5[fr23@#`2cM$хyr5j+*:3L1 O>/\Wz\l[ci:g%W0Emp)ǹakYA6i;1kyKOH[iHP7cfnIzWZ7>-ȒF޵-!˷=#KJjI9ǭc|HS#~D$5`+9 \)+[ƱNGQ^N"6v?C+RfÌ=Q=|8,:tP8FR#=TMQr}/-0od ]_z<9&Fw{$ouom|գIYF[R }Vʹם-Hp?35=,<*y-SCK^Cx"=[wV֋RD _S."7~s\fM2}э+n浾isn|`J1t?w~TUmYzs_PiePAƙ/_[Q/ d+{zO5iM{l{rjԇSOYDC&s)*n*j!UJ{'/,͝HR3ǿj= :ZC(>ƺ)cYQHN3u^L#0u\ps4y6^65;?2uBF݉~q^WڼWagN,gOS~u_~)A˟['PE9kіYTGE{/C̸]'ʖV}OZxWJ5(cxa>FP)W>jv~( O"?=vZ$~nj/wFn#FH(G$ÿu3'goAXϠ6ҾҩJ2w?Lת΍Khkk껝nr%kH%]GqMTOًkiD6WJz=qX>'i-4IB0J:fO$?Ylb\|ē<V"R:j:mr mXho$W*Io-v gPS1Bu lr09%2`Cn} 5"K0KA3ukj4W2OUxՄ)x-u/?k(ӣ|>98#1'00-U'!D6qӧ^gO21QO_37壅z}8b&_$H'aYL nf{VƧxSN>7D\BRY4e*}W}ɴg 𭟉kkݙ&hvnXv7+E@!9}&)ғVu?}JE^D$܉lʙ`/C: ?mw6a{eh.1.ܐ0#7Vǀg];_Ğ")m\meoB=t$ɯ\$MB^ꄚ1ZN$Zۣvv4mBM:?༙r?ھBKo@W2ۇFQ^[߅,xzlVxKqae&rko0l4Y$oo+!bnW8RI}}?[~j$Vvy*3'r*k6(|Kž̷pyq{%miNdi?tq^yF^iVkbd)Y\[. !$Ӡ+VMՉ8zF8Uj! `gzEꕼsߩ*P<4UҶ\iw |*=d9VO`z2>|aO5v}Su8 <"@2;:o Gω5_-l/$~A=*3 =ejOZw/ڽ_x DHY#1 BX0=+7W _~MK Y|F?Z[ 0XޥFfKݻ:+-6[EtWWkk~4#O7:ƕ'fJQ٘OjjjZ8XA#Pskoz4uoZ/dKa:$O_!떒Y]=;\>ٯ᪞ڛ+'-)Amɗiɓ#U˶T sZ;1wG3F_:S6o_ 6675Jv׷.i$(G6|xK<BM+ISԅ$U?kxYY}&ɥHk+2v>IU"hN)ɦFphnʼ<TR98ڣϥXQAμ$vͫM}lPq޵-obcǩ},7ZV r{kS>.cXje??Z۶7 O6$Y<m}wl yUzyҿoE9<Z6Cp k⸙69Z۱DYElkέMGc2\= -ebb*ƑTy؞I?ԍ`SJv?5ysNR0!Ft+ofs(If䫌kFesr#P)޸WF_'P2b$L[,*3W* L,t:/m쉶[4iukC3ҹ{+[F9EM=Y. ̗D6э/'j澇UiUJӏ[+$+tR[<Һ̋0$(fI8? UGOŖ'Ӡkk>f{d9FS__aΟA9єҺ4DET;u9~cO6R$~XVDVz}9eAڽO~*` r:r0k/ugӡGX$#%=9_9G()i>gamFjkw1"E-3Fё#AVbNyKy/g0b9'1Ӯ}+.uhv[@JgiGU9Oz,IQi=]m>]3o^dI.:~$*N`sKq י=/1}?x}bs;ɅR[+`]Q*̐c J8jq^ ]-Rʖb@yE %m?#gzv^CG+%H7]SO]7HGfL󞇑ӯXkze.֓=qjF){+~m֯rO|Y晴(Hщ=OT4uHZf‚98)eK!PTԜ÷ֲA.f`(CoY6Ѿh" m ɒBT+^Ydڡ6 5cva{wzX$k.{W ]pڤly8ǥrZC'Ws #e=YQ{+2)$~1:.Qk~H^Lǐ+cŒI'wnnTBa'8#=1;'h7U'͋WJϕxo#ᖫc:h0jֿ9,ǔ"1\pq5.ex?MƝfh?fQYh jN?c4 U_x1wew_xRb.jeQWֆ#Cݫ$5&}Sg? /..at}sx{/k<8ݶJ7 C+f^$/􋷷hRQЩ2T+tmBÒ[w: `!'a힝kfR'/yiՇRG'5?i񲡴ϸvd"`SjX4߉YxVe5,϶˩<8;=_I>ihw{sݐy{wƞmx7c `mc߶i{Zͫu+eGABGO$'}Oo8/\xTvZ٫:B1e< x'ᶇ~𭵭^.V3&zf~ U.t6-P ge.F=duR[hm_G ={vG+kB.N0S$z|>7K j^RvkdE}}:D> è&}=o ԏ1xTII4 ߻};YxM75xw^ԥ[NYkۄrs߁Sm[VkI.\nm#̗뎵ܔTmK BNzۯ鯙6ͽε=ҢyJrrsNh!4éI_/j2BӚ/٧υ7ZFa:njOn<1-0JyWЃZe6&T.klwOnyVO׳j$~-;]3ݵ&hƐIs=/ee 185SZ14OQñFO BN՝ެ|LxӬ|)iO7u#FIJ2O\q_Gi3$vK& $g;t f'26Б lcks5?_kSK tK. '|&f| &(ߙskGvfYn"3 ZOoPJMbl{ eP@ݜ@5Ž FmQ3_@|C2߳XD<--!$gߵy'*Ֆ0Zsd׿ΆMuiy{6N=9}s*|QO~Ӈ  =3Ь<637.NO9݂d;W1ekbմ˫7PKŻ3@+ 256隁2Qjo>: [\j~|b^%Vzj<(ubꚞ_MݽĆI癉y1$:';h?9;#lu^+fIuق~O/ 3D2##)W0z拼$7܊2XcPRB.Vh(iIKO\ԚѶ<8&f@1گAsN=AZ=: -=N'hr03W0qswv8ZvW.@b5V#œ=ÿYᏅ"ME'7s6%/*r$oNlNA xu{3z1\W((mϲGa+J+Gb !!=G]*|6ځ"Mj {(7Cپx}Onի|V߹llu^?q[:V%EyA"4S460N2 o\,)^V%H^Q P NZ| ZʷpF cR)1>29ozֳ!y=};c!ʏ~z zQlO U ǭz~YjWty \Fc; >'ݛnƲ~R@+pzq5~ ڌRI|N8 1{& ĺ]35Q9|=/`I rO=_ƺ'[S, sYecc aYc; qU|+ǫVWu`?jrv8R{ H֯;mxXW$y ֽe"[*d1]l7=E8Ş UUI8,U&Xf22ng< $[0Ž;-JZ{RaI1#gW^:~e1J=־V,#ݺJ9ywE*knD13LWBZc ?-6KSLzhb\hA=rx[wakH;F+-pb!C.*9W-;1-kTxا'8<xj2Z';[mujaIʌL-vrNNkAE㟄~=<9EE4Ƭ0]##Һ߅(n5#f> hRf1!+[_3kʠtyaD)!q$urG7gGm:AJ鞝Œz,׺LJͿv$5xwvZV1owm<ӊ/ºvot`kbtrF9Ǯy|ey)^KOM~󪜩ەS> WMwȒ;6 ؃2r{}:Wן ZW5w ^ ۀ9a+ڴz#up۾OZvŊjtOOW|#wcosj[63$\Yg\W9:ĥ$6;WO8n[>-'o­CPt|(ɜ}?to7NŽUf [y9x߇|} 1];G.uu5jϼ,-2γw6WY|>DvCj0 `x1^+o vڒC&7H?^0wGkOF>u4}~V9]{{cJJm^I{`]_wOiwm%y+mIv #/ zW8ڹUGr>5-?"ҭ%ѐ4C$6puLܺ|kU_m?%+{KlQ$W֦ZpNc\t殷>-LfTiLdK8[+x剄86zt #8G2V@0_;~ɑfSSv>*>.ۍ'acF '#%zU{,A;.6qā=*-ܙ-WS? \: ;U|dg81jjܒ=PkYL98WEҼ&܌2Ja> DWvWknJt${֌2n )IAּP=iڼ!-y>(b)u7jFEs?PҮEzz׏A36}+_ߑ^F?JY+o*Tϧ|YckW 9![ֽ+M50H_<:W_Me0gVQ+~.s#\Fx"1 G3>IsR}-|Y_>tGS5ʴV?,Xl$YZW1:ו[|MIlKvwc#}kA{;jN@<=#jMM&g jMZIau =o! %gk+Y3Lhb!dύmAWxǺa5"=]}`1ӯwɒx+1 =ZLX ^).}?̲E26Iw2cLoZ#"}k?a,׶m̧0L3u?֪zX|$Oyf512EF:EX]g^!|Ð=?AYvwڵċ!"Jk׿]-HF%k~{ciW7}uq?I^9Y8.SR0#'׭eiLE۞ uZhס5s9]\zWG??rY<;v8V͢Q\sPCl:z}Wpq%¸UķUʦ2+?a˥ẒJG ӷ;Y7kUQ]On8dW\:c#^f:sE0O~.UI?+͕VS{q_s++CD]!I' 3[;Vlm&۹rz??_x76KZT KxʫVRTFuT[mw_k5W=¾8dzG &96Eƽ%φuYxw$f)ҵ˝6fBO^9z3nmήbѤYe'?βyR÷OT޺Z+ (~v?A|OAgΛ[̆NkԾw0x,r >ȪcD\@fz߅~xg/.eQ{q4;9ӥ\o.Rhu5kq{m29v2 qBJ9kE~>⽓nzw_Ui#{ӮA9븱 [&:K_䃍{ t53|FV%Y,Ljr;S-|Sm}/z,WPK5I;u 0FTgjNiU[jjz|҆_ _5[A|wIy :J;GO/#svZs8'|9xV U-nYQ$v/' I=en'/54M%I]zui~k]cqwQIrZ޾Kyoִ!5H/5]8^i9;A9'ՇjƾE%: Or-(d7t ʲ`gEbD/IuoA͑LHd+xZ|5&]m-RZHEP9xYLy`+`TfϏ|'%m,?=>jq~Z~Lɥty-Q] ( 4F*okZ^͖Z9h"y^gHԜ"@YjG!4 6nC˅:/(=tMgGF';មZQد" Om;_EsXfjy].y"Xd ̻M͝GXүk.vWg/YMjm:ͿzJ rR얚 %TnuԐD2`bUI>nrs$GIH)S5ԦGaqQFKVh O̓R|~cUp)z uďO`jiԔ$Oo&JƎO9]^mkپTŷsĬUf`%rsC%a2ktL673]Pڞ% ʃZ&J̪[f@j3Xй`+*ZwO4#<[t>.MF61˲svS$,9뻞Q͍L|;WboXxԟV2n*7Ǧ1ռ8QE뎄-j1hvז$.Sw+'Cַ:UJngA!Kr<:9&֊cUҪFAltL퍣5OVnޖzDޅy$EWI_ &y$k8@ ҲuˈXF2k Ixڕo6XTVWq {W58cEײ0t]? jvGtH{V샮M)b/EpA}gXԟ=Í PZ'<1wuo{'( }+lo$gà0ˑ<x5%cv^h>I4RWH渹{2b^AI.f(&-@]>92ټq[ Ypǧj ^/Es% Ẅ́I!|t`E/HeF3+[mS:N~U_ kY%U~K Qx5x+Ş.Ov`\[шNBÐ=O^LV/5L6R{R{ϴuڹ#^ U /gޛɻkRK{gV?tzmPN[a3FMVah? m/#yybI?0ƞeKK8M+nߩ4M77G+q 9HV!T[}6Mc T@UW8%G_tͩh:6'\)|Ic/04,k#~lkK1*ӛZeGBkEyl>'Qs^.M8#po3FS7LI 2x6j.fZ?+$?^c-kSӢ~o$w2EnAMrΝTO+^"S0kfTǡ imz[_M3+⎹:Ƨy<0It#`Uu|_?R./2j[fbDiI =Nk(Rvny+ʥV=rO4%VjȱHq+8?j'*GV>g+l,q|NA%=+7R\-9e!4tWMGZk{O2"2( 9K}FѣWy<;VTHfxO1SyF e>=g4o~ri~#_ɨGf~׬q5[Ήz漹BtPӏP?0?5wrv-FmYWFGJb1ފM€zLReREn$[Jn =)K4#\Ji\+J>5v[C=k9ԊZ|INJ7xJIV;YﻑҴԞ@%`pI}Ictݫ(j}>V׳m}f39g~Nz,ps_'å;yUCR{ D9YnC$ pp%[wpu]+fk_nhYGž{B;6kalɸwo3N)E-,}Rqf +*ۄ2FÞA8P09Wd)[^2TT5ܻj¬ʵ: k9VOSwR>0 |ՖnK]W|vwO'NOX5籹T 2G N>(ߵ셮tau^s 5l|BEׄ[UдeZ%_1'I$oP⦌'^_OBQ?+Lf*C_ R]bRG#dy)iUI}?JqLokUi,:e9 Zk=+$jKu-IYB\0d3#xsV~#xuE:& ۠Ӡ.;@;VZR2@O5UWQvMYK}R~݉(kFBcӷis#בJ+#fEs[DL=BRha6qY.(x5-dl;@֥&_UXM&2`pC^ڳ\CurV^xT3 @l`*Dkv9Hu::%(~ooUQW_sGRjVShEmW_5ok޽ꎐo1ą$~]kRy،cxgMg>ll -Fv{rX22H ?*Xx*%Ҽ 1܇ ~}Ef;zXMJ!KyKos`crGTFsRt5tɫ%杦1Y[#\=I,ATQZ]ww-/܇wH#ҽOGٮ 5BܞN:zש|i>O54oZ姆7{_N&&w$9톅uXE-k3`w2NRhZE'%ŽmW¯F98'&p{APzy'pm朶yZ ,p+Na0/А[B\u"f zT alg$z6cjqW"dT 1~aG'T|L9$*y.=Oa4cN.i_[,{>3w3H"*Hs|F]0yC!-.jsLÁ+Ν9`d8bEtZz]R95׳R+>>i~*~xV]J~mor$"7wFy q^_>ήyIJ2(1ɨǶEvз߉e[(yf$NF oKYLX]G"K$[=E:Lȡy$ ^e\ V\g|Zq$GSb1'˨'pN;ø]c|r{*?~='SQ+JK-oHb-"cbB̌ u57xV?QkJɻodڻu}BUVާ< e8WwF'{.W&+;gP[[T)Hw(=N^w<p6[Qe* N1VGb1,fҕn7}.s><O\iܘdHܣma 8=Ee\hFo E{_n=J\28&]=dZ޷o~ui?2c֩c10}z}SE6 a׭aj7>xK[M_H0_ QWr {{oq7(L3>Γ22_9_x+Go?hWZtشlc2\0lqF,{X9o|0O54{@b5(mqI#V:Ҍi:U[庽[y.m9~8Z_ u_[EkK74`DB ':f\X[4󢒒FyI3v~CI[U.ﯼAew$Wvo6C*7CK۝F[Yij9$$̛kikk~nNYz͌=(v$nzB[&] t=ub0".UYpJC_~8˫um#׋?xMy+3^cř?S,sց꘭=៉o^F jܾ!h/66.@8wx*=]cW- SxSL7Z֯J<Dn+)C W{ZߕkBɩ9kxƚE?5 [V֢G/DvV;vǭk┇x=|;amE.S0v#(_?,gq%{jfYVsRz-|e.csIwkċ9[HѮ^PNvPofZoDQoeP q{Ozv ;X+!&1^:i##֏jV[_C:Um+:Z9|89VJs'iRv}}JNqKU5 >x'мGX?<; ۗ,ϑEsמ `V5hTJV՚z]mfRqkSеYm.[t*cΈ$ʪOPsךuCss# QGn *ycO$ hѼOm_eg9a6{p*F;Yr921Imx7V֭𞙩5Sj0k][χbQ@ֱٴH\eJ6N!g-[TGm^; ?)'8M'.3`=XCJ=q?zErRҐ M ?W|g~=`-cO75J?kx+((EIe)p9v=+ .#kR7䎕as$i$ˈ̏s'źij<#qVcUAS {A4S2@Z:@W9Y"9$5mNRuڽ7fRV>"3Z>١6 )zTW8>\QR+m0b}>"[DNJTG,v&~}m}ki\2M;͌.kWώ,nEDYOoz֑(4`GQSg極IU>kpBY==kQ] y_]yR>,/e*/u\Arrj wPK-ѣ,:u\W o /EcW,W}Vtft7:0Dެ?SXh'{E nHc 63F3ֶM4\#zVjrHs}.V3uX}-r_:5֡4%5õ]-`r)`A jQv 5ŀ^n^ vhIOJS>/S=mz£:\+ax uֿ!6.c.ZKvD|KymSVMz;x¶Z|(`c(^ې0U?G)x>.DNR;卷"PWr>~#^XmS $~ֆ-4G5O&r5[b}HFk5Zj6vm9)s]{ek+n9(Yy ȏKkhZg;ʂ<3U>z?k 0T)?k!?"ԤVXG#Fd:6:tz?gK|Nv?LE4d~vCjEPsu~p/2T $?:M#M/{bK3xT9'KK{^7XhnqOx3H#IgylzbmrXcJOt:]erַ4ם3k "p9 [W7h6~b1*4[x)!*!}rTTtF77lkFTSКoلʁڦrOҺRU>ht$~X}XԴ1A;U@1[CK81քڬXA,Kc XYU`vYw,[eA7SUrf F3_OO4`{VDeL-=qT4mL[SIyˈ&Eygi 4 Ϩ]b'lL8M<Ρm1XH^I]F7mxd{5q0Km:zf(.Ú!=ku-Qk Xcڊ"\Br%"gt<%Z{*PՏ8_=HLY]w㯎a~(^Ek}Cm3k�Y$*\}ooW _AW'/QK{yxƑlvM_S7lKqqrCwH;o(٦(Ez'ٗ ÿxhcO-%3.6a3mY/'7 u_^|ֵjQj)=17Sz".ETt~T?$(Onѵ(<[9>5m}YLEK1D};j]g ~|9|']j{=CŴ96K,y ـPBc PEPEPEPEPEPEPEPEPz|?|^/V>V;{]C |Yߞ8&>4A?>,Џi\*Yn*1I|YqERug)/ҿU\+$"bMd.p=쟵Oq"l?~(_VT_b2|=AlxIuSP{Kx--gLŘɯKFTޫ>8cm?rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/gallery/apollo08_earthrise.jpg0000600000175000017500000010651610276336034031043 0ustar madduckmadduckJFIFLJFXX AppleMark            }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz=P! ?$@h9n@$.MޔF@i%FV6@gjZ(l?C@ zb&7#%+>-~+GTH伱nm#M(ȡgsK1 UE$ك¼LXJwnx74?vGK*rAra'w] `%X0V`."QW:0㈦C &6X3Ez6Ƕ:n]j:~|k6(!ʃ?9䌌uϚuG>' T}ϱ^:DX#\Yky_*խ$\aPy.1G XO֪K[D<&ai5ؠҷ?C\P86 ~xN(O8JYD31l>}>kx =*O+##~{1 "Mjs >$iVt5u_Y>bԵn+Nh#VX[D*Z (Ƽ p1~>v`K&?LwV nQڿL#W{rj a3^DiA"_8%PzgBD-`D^9FBׯ>]:йmÀNzp+H֒VD}nf[|\ӭ ,}NgY7 _$odX{,?J1WZW\:!p0vԷ5q#%e,_CEZGw\Z4W-*h1?^@ . *29PL1yFۛO4ۼ_}(?V8m%x=Gz!kV.S%vx"aN:Ą9cnA$Gݫ,̠*# SkE\̛ZM*Izwk*Y}m( ?}=H^7c AppleMark Photoshop 3.08BIMC     C  k" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?B8}+IZe4?^q,+^,fA=qҏ>?EDc|dv?SA#Et#L l'F=A #`4X=p>›7heoQ4/ڟ5ӱpcӰ}M2\ R 'Ɛ4,R١"-Ԡc!#McsIDzrs4onPĹNxL%'p.0qQzJ<1$IbdQ4(b8MYހƀr4L=8yp(CruϔLPb9:K;q׽+M#d:F 8QR 积)eQ!p)icLtfl:PzΤsIOHއcx9%$\-5آGlR$3mBy4)6s@ GZ(/M4fڅ4 r]D@" .9qSs`N;҇BH Ԁ}0(m ij#3  ~b0Ha2#>SRCa&DZ-rÏ\ NZcK(p`ڟ_d(>`ҕQ8h/OPA&O04m_8cK+@$RO$tIj73L\n q@$ m/|3`f0Yv%t4>9dbÜSݜ6Қ*8(DFfpqzXcqFfTbYQ7 Қf+(Fep0Gtwg9RT,)1SFx'9@(֝I `#1QΜn)np:8AݜZijp7Ȭ6қTN?Y\*'Njd1fWOQq隯z&p@ 0O!ֲ#exiKZ\XI+I ,(Q\dQEIpy7Oa֖Vu#i'.NDb˸RES@ 7Pw<$c Tybj8,I"Bx.TzV@:h=uOzO5TY715d!@=-ɜn =@} .>n{ԪI-QREPHA${Z(99V#FPi(#u-4*\֛$\''Fi$*gj|0ˁq{N,/AJZJcm{4Mj<{ӨXc$up3L&sޕX?(4.q !Қ~UiTbLg2փ袊D:A u52$E=e?"7 F~\`S_8#hV,[x_Ҥ l?27r=I!)f .;PHk$#3 -ǯc@|M;ЎDX+ɤ @ cmN})``~XǑ@%ۥ++.$$`sPFA# 0=(YCpF:Tk ⠑rL̖i9>+QUKr95'~MZO_?lOڣnVy1zR82^c#֊IrAj)I]A<*|$6}Sv}%OI\ci(4R` VR;bTG$jB\ a1N'm3qڝ |9Z<E1NR4IM2 JuD)'u@S:JXҢ1Kp9>5paڀֈ)zTP&_Nwf/!}zr۰1@Ymp+?j!b?Y~B-mך@B{+inWXKh TsVMɳ=Ӫ>Qw *G=Ou IRsR$7wMEFaA;"ʓJv g9gbۻ) ւe= IadHUKnRqICLlLR= I?b0yOzl, '&4yك)`GΑTb+p;P#)i#uK7^=}*HHS1nE8+ycƚN%4qOBH08H {))[gpNqM~eQJ}A=Ԍp=qLPH9=rrڱ6kf:=̨p:SLe3RK2I>CM)PhCZ1i[g'' ǭ2LEh1֔Si0=; yJ лZ@h%;ǥILz)E*Oz EPEPQO,TP&eg'F<3.zZĸ#qqLGrךՐ'CIEz?2J(zRR1gb>VQE#7)\0棚vF؊ {fx0#P+Tc(-Pc4. 3R7C.TS gN9¨zO X,OҖTg`BP*t:UZ VU<`AHrsN Nd݌u%Iǭ`nH1sZFrtr=E%%(j!={6}==F2jTRnT (hie=9 ֛* j)*0zQ[qC=j0LcR=I9عU'N V6@`˸Λ 1NU؁sjm'/Ҹ%1ӹH̘uz`,3юjDZ[_'h`1 >BE_wIchvSӽ>I^Kr O4N1JԟL21ʒv-ۨWqh`^I\gM9DǗ/QR*MqCǓn7zɴEN(%O=80q(Ǧ)*ƐV1i &1 Kg4y00{gy+ zҘwǒ<ɦ6}:2iȥXqNCҗAGZFvUtԡr9TOJ?֟Py 1NBJZvpzԵ T?N7WkfQU p :+a&Yt?dX0?Zd !Mf@8U`WCgҖ@h !go;P()oSQ܍{S|ܨ[9L=E( 5 @1/OZT""C. 遚FUX4`qKè~S8ʤce =6lvH7-"2(~hX1=dqMY OLsB{X f;F9iܛzum */(ّGԅ<=9:b0intvǨ*ݎEUl(Rz B\]YX@WRd&0x1ʁJr#4yXJE]w(8uv#q$zMsӌR;ۄ%ܹ]ŏAI2Mj2["H㸦o1=fdVTt͑A8q fU^iC4kq~~Q=>P7"1րr3Q* Olax+j@=yةEAp3NnQm݀p{rpsH8ǯ4#0*}%~ )2ni[WҙB2){i$1֑hv^>)b̃jtJ~O e 4;d.HJ X\sK,l@~A.R8ژ^9%**=XJ62c£xny$*˝ˏƣ:AQf_2f*OZc2:;k۝?&#)8QCV*dK#BtPF!Mff8r 0{QE&FqJZkrlb8_SL˓_zD(I-|ٻ9f $qO vibJkYo#cVĀdHBGzR:x8sG#p'̀C)>b߹֘E$C{T֙1rjjHB$8ȠŏIΔle>J͎Q+m ?N*) 1V983Cz3ALSAcp#R0 1S/4|*ǥIçJ8d(Tz*Rd{P00M\tܨ>آC&'ld.$M>#ğVxv))g{8l"5~Bo@N7z# n1j~&."1iz%(Tg}~?i߂4s.nuO>U!ϒײy2Q zcՏ!X,bi {<7ŎOxo n|=guBikp{A7K{ua/,⽜ ԟ*ߚM;J?b!Gv=S[{q~-n滒:zf 8C|~ ~^y]uȰffp{s0CG n[=SR'텲j{}Nr˟.+X3^7u<>K|fihMB-04 pT ھth`#5`G;uk3G83Ϊ{Znį 5 B;?7%׬ٌbBUVxj$WU2LYv_>w< \nQun@ܕ ϯcF_0~>.фVX"fm*7N3^ͥ "xcD&lHYTː :WEu=6RUXȷ˞T^1+ _Oi_'cS]k>XF"Cc 9;ydx6JGvZQMN 9/O:C+9&6bP͎zjPbrj4Pd{ӆr&)|CY @A|9x^M6Gղaܠ9"=WSN1@]Ѥvǭ'?U ׏ֺ*,S9Ɣ\8?2fZwp?Z$l`4 .H48'`~"m€ϙ5VMٞiܖiw ȠIX쁰ɓJuG&۽?u~؞ Oş|Qi%}{i-S(2K f5# +$ U\>OQv~?S쫏u/n ekHzG>(#cwgiz7q.?`i_'o_Mtka[mIB< i/x u]se-tV~ɾfB~GT[ ""lӕ~y^&vdKZ]d>/+KL|Q>)2A|s Uf%'~Cy?aχ_M}_|)MYB6s]5Ŧ,`HId`u/ |$>O ?u:.y{^˩hEiav}?Q?h}ً>&o_m+SQLi>k^|g+ռ;iHк1UW0z+OM^vuB# Pys#>w b%6faz*pNޤA($:d'94 ˃`U gX{E1>P&|ǧ/!Rs >҂pjBd$)dzG#F?ց7rE` R">F`:3EfJQL p3QM~@mfxo=X-nY>v'ُA iv9cb`. T`$crW})h}SiT.[44,Wi?JJCӋB!9Rwjkc&.՛T `2O'-"jRy>6L[FԱ(RjV3lwHmQ^<˶)X @HOQE#ЩfX%2WTsAQǡᗌ-ҧWlDc$w +wr_ 鲛p%"H*7;WFz;cJ4OHx714eK%c+r On>d×Gub}t?Vtc [aWI OtԵ_jZjɽ NRc 28P1_3PiXG'ԑRFo,~jC.&oiExw>=qNi:%./ǟjZs$j+p#/hsF=OQ+ohyh#mrW3xģ\xχ7O庥t D1Kp#> xUiMSc6^ 6Ihlõm[fP wJ澍֦ }:v^ɧggu{? Z3H?IiF.6ܑqpCI:(N2GWvkE| asS}Gė>ɵ]F(1pi#OFz~ ~sqSU)0JcA {@OhUϸ4I?I 9?M:F+eM?3NpH(0`3ґG^i].TrjR`/5e w-M\l2?MpϮ?ϥ8ww( #ҕh#}ii8gl0\u[vx)9MNȢNR)%A'- aEQfA&aҐTW_q~U vČGz팊Ur=~VjRZ2\[)G#^O_ⷁ,Ѵ-=oTu+(1M1=Ozע Z-l=*;}W#j7ZR]@ ;M~lײxNhnvxP]ɜ)$z9x7@8P񴺜ղ5zmj>Έ>-'}%O iw#I#UUe1з>|ºVhIphY| _+t{-VTÜ׭;a'Ո, kj\OǷc,>[<O՟P}Ɵ|CA٦cznOUk#N"o-ZRBʜO $spl~1S^ˡuߎ9ۨx,iU̄~˫vа Rkryi}@Jes.{)~PNsX1fj)7/J>B0{f#Xp<~4Q@9)}Tb|ygVc䑎=icF=i-Y Ҋ(:/uDa7/m0rv)ң>61M2k)$^#У|sM y#I֥*"EMşjdbIrzPqIiҡJeF;O`㩩VS”<&7~D8ؿ  Px9+HE( _ȧ8 4DZΘȉ'Ƞd`PZZ(eI!C4 WY㞵50Ci/GB,Jdg8yUK`"Y :SH+I4@ xii$gcBzUe9+2cڥ!b'FXo,n*f1*6/QFR/JPGoӔ\=(=?v[ i[?hfUD!sllsN 8B1RU+85ÃjvA$u6I-x0I#=j^iJێ}iKO\⛀08v cc9N BJvQ&H`$7\ss{q nWbH>-F&L13sKz7dP HYI=#Rr==hGf(OS)&JqW.ibd7pJ@0S.?={R EK@]ﴞ8.TaӽNMP3o );d`mz( 'hk+Êfam]sNգbkWY d=3T!u*g=jYPn҃0(:'Q4=@ayQϨ)U *9X n]zdX?4jwLSdX?4` (#GV `O֬I *4%V\?xz$Moґ"A23`aw6p=&0wٴz}1!~}}E%#d'ҥ;,FhM0e 23LmN;JzTw?lwa*ʬr}*Xcb0S<M^K??*aWjMj7K??g)%z nviI<:Ӽn펕PܹSN=?W  ~,,B8-l863Ao1^F\G֧DXZ& qLZE)7) VȢE7.άXn; c#җt$ $.ΤTp|N?J#6~@Hbz Z(HzHRGhPLRzTaDˍ@X~p6oJH!db!`*m 1ҟ@4p)sԌN暸=)b C 00zdYҖWe!PO99啘QN6$O}Z ͳ/='8r;wrGY>@2Nϱ5P M*+H%@?|6OZn e\(,T}޹u$x\7@Zdd`<=iDfٴ;jƱG=OQ͆aC;u4k*֕ 8mRjnnOGNQ)'vESjQE>Esrm-81Nw$"#6;RJG-,pq:wV۴sڒIE1N@ȤBPֆ!>c׊$XBnicLuNgCYXm*$te 9Ʃ>^7+Fju_k֤fqjUڗ+ǝch(L0 tWQ Van݌m9H|!8514FU8DGrLZr<zAKPc-.1Rk:hp?E 0( АnN:R q2{S 0R??Józʂ@#dP򥐪aS[B6E8( 5w߷;L_ @)KAEx;zFH)CjRy*?*6GOBy] M9`z eH!b8Fmۻ5a_.~ M9UeA-P0FZF* zj2bTzRU }A5yG⦇r[eεv dhgzjHǍG~!7f7ҔzR1t=jL77c#"m,ҷ x\ZB2ACMXLS/vVh9AWrqzRsևH`tɧ2q&ICaؒnݩ ҥpE"?_KSvEdt#\`u8 B`)Mۇ^u#8R}Bo09Gj=~Ͽy;S)b<;kJ`8EUAF8H O4u yN9msװRvB}-E8o jb'h!|Gs\'I2^ChH`q*ѷHavl~x?goiq4Y k,A}sھOu?M g4 1'ᰌxP3ڬUW;NT 'p/faO 8ɸ4}'J'OƼ[kWKzG1Ko T6^)r b)m^jf PJ5_E'õALQOJ[7Ov>}E<8y}_eca!_?DGpw}^ iu'|U闑в7RWLѩAL\ #sMGepqHHq5v3['udwdg♖\ =zu}C$N.=Ob@;zS^bzW\JQr'ڕ0+-I)#CRs5!oM&brI:*oBz 2<}jE!Am 4ۈdyXEI'wm8N;I$6mubKdsOMZaY6u=i#Ǖ4ߧoPQHdܪ1S?*i\T+m4d0-Mq*7hV Hq{v"6%a &1İ)` JYXL~Qcue p)P Rlu E`BNoqT/4H *PIr=(h`~YBSHCnNFeN b PTnS&J}$ 5o6QIcxsTwqҁ*py8dw+v9Cn9 hk 8!GZxu%0tЀu4ݬ qr5t$.;+$x;nXѐ9V;b!YI(9<ƚc="th(&*è 2~Wy=GE"씆Rp2;RG > QJ3mZSJ_]4Wl:[@ zM}ft?ZI2ctxk6:w&=k!2 <W6wL7z}rur$s~u gIzIem8u ˀn?v-`)k*k~ m=GU&6'}sWݽPGgܡŅ qQgo yg m?JHZt;0}G/b4|H/)8`VDF#tW@OLl+ĿI&'[_JttZӖTW O:TsE EW7n2կ[~%\!k}B#7? oyI)?7i{)U{krKNsZPG30x?XrA*cd W4]oJf]WC-YG#=Z+QMt! 2TQM,Ks7} 3J֦<~ H)Bv3ڂwd̪v.H~GHxarw ||犱iI}Km4g),2`q$`<Әp#_j52Tq'$'ڙnABA#ޟL#RNWE~'b@Qޖ~#QE!xϸXX4ЧCa@Y$*z h Ɇ=ii 3 Hd]ɒ>NOqZ y?ICjUp>QPHe<˜ٳxLv G'@̎h4LW\Fѫ[ z`kji O5JB;~Ӄk`;φ!.&Gb$m'Sg"m?Q5O)8WIֹeɥ&!|@県`nt;O3"UQ3{VGPuQYR)zǀauZ=i؀-痖'$\%(%[öH=c8_0ZWKo+^*ȗ$Hx9fo^=sr ȠnϹtREԼ=q \qN16C' M/CcVgPpwl9A?Svm/Q3I ٞ0H9uI4U7pL JN=Dzte6 q>*dT?];Ph]VGi%*S#F:[[fex)AvR/LybH%;\չ~'iZZѭ lΤciK]ئνOxq(r9-YI%,$mĀ>Nimn;^RpNJSk{,Ur2j Zw}o-Zn,aEҜ*Fq׭7(bT*M;6?cI<.RBܼ?ak.g. `"9l|wSҴ#$2}ZLn#@,xdB0p7k<7_{ڏˁ,x;Ij7o5+mXK%{Lg''n&屇d:^4UDffT6|sEejv~"4ZD&pr +4;WC4Rg+ԑڻ-Q˨ep60!2ݞV˖R ZkWdr^5`U@Y\OBj'(UR\x'lĆݞHZ4?1ҍ8-^elsڶR[ܚ(qӹͨ-?u_NQDD_?:cs\?`_QWqaL}N ¿NuP̹}1M?duT5gǭ+-F)[ s[&_v3\ s[ *7 4ɼy1|iy @`Br{Ԭ\ZK_[=qmvB2wc,lGі=E'8q0*+CY&$T q נ.uXJ]x4RjOwQP\Ǹc N}ӕضܯF?~x¿lG~xB6Z;i#'wweMCuO f>aእ4O~o(ð޾_$?ƭZ|o׹-Ee~9M⹭W ?piz:]tn5oK~մZd5:Y:?wZ}_CJFiL6x??a_7?m:C^\2z|BUՉU{$$cހ흹^ǿ^_O>CNia3rzÏc ?J^}h%iGWT*9{m!#q"ȻJڒ5‘՘@zDcbˎ P֟fnPFIKHHHz 6ps\` ֢ۗ9jN.Qejdq8诋,mرq^5K/ ^$B]q?}_[aR6]Owg]צsfyw4v=J3yaNv>jO}dO+r!. 11߈T]sݕO1^6dmq7d!KzQjtOmWZKLC"{k>^^#KzݳEm9k-*|lQ9OjKۭ9v(#z\Z-[qVVٓN)qolG#!Ack./9,[Y\ap9 _jGi%bMs*\vh<2z!/mt2s$`z=qS K P /),>aI ?cU.#׶is ;gr'` Yꆒ{G}ushȻ&Y )nYżom(VB:`~\xfY2 `t '&#T-8_*%ԔBy[IZ9ncrBzU-uE{wqF׊Dڔ*!N*֕Pko, EA==kX3S ݴ26ƎC5宖)h̅6'˒H\Һr 8xqP1w H0Y23X:Ƥ-IVT ~}hmRkc浺8<>p#{XiKzhɎ+IRHLk|;sIڮF"KMVHb@dYãud6Pӯz5׆c[U^5, \5jkvxm*"zt^WcDľn-洴ְ76eyRwn6Sn!ȲDK#h^'qrcI+ ]׵e O kpMH,c"ƫp7D Ak"֪^OOƌh-%E[ {U;W;oK34i?ѭGrP[0H",``cxU[lM,"OWo#!9@o ݪHZ?9#DNʼv1v==ޕS<CƯ*mgӜ{-BTe5kޱwiexګ/-e-mǎ5ېkT!z6;kk8SHׯF o$DoMx+hw,*r9㿵)NNNQ i]sX) #ƺccc`'ۡh>VחPmks1 ,;c[:oďqZ}Jݤe3@]m+ NMqOj:\wP0 ;0z&|Ejkx-l^M;=_$5p8'x–z$寮Xx%LP0ڰ$'ҵMic@dzⴴ7mb-m;8a֡VM_0=otfZ&RK Z8$ŻW SiΈmf Ծ ӾZX%-&xn~WWvPBtmM#D;ves+$ex:Ż2G!-gqTcBlHb9b۽֧>m[Yn;Y<';Qu=6& 넒0\猞OJ9mJ5, /,N/ jI.gVּK_r_mWÚGtEg*xZڪ\#sg<סxK,VVlio# NqiO\.Mv>E\}w[}JՑlt!Ie>q^G~V™nc5q5IHFgɧ~ 5>>'b}3bxWtkn] !G Tsֻ*9ԧY;3U ÷-jI m*F5k7|9+÷|BȷO\ =YjwO, G\`~4K.7L?`Y29* &?){v$.I#y#AGOA+⟆v|?i]I(Y$Ĝ0^U?cf-ڟsZ,FI*-b~~=_AX/ŭ$'|mB- }xbf Vc Z?+lMZѫ}ݰߨ)OP `a!8\g'?qU]5h_6C/7'yJ|ohEt[茡{`B;׽Rtϝ dv*Oh^E=Ȱ݃|t5Ǫz:?hX^uf#9]>-w`Uo*OI8_JM ]WEN,,h;  .'c𿈛M8$18i8 KOctW5U7Zx!ˏ |?ỂoH8s;O;ૉG5b`/lF; g-:DW)9j`ma^'kUqE3`G0XjΥC\O>f`3W<3#&5vw/Wĭu\E\ԐO.[u;>*ITFͷzkLo Z}IV>48\@4nypZu''=W5QK-&ݼd*ةE4u{'CeRxcS]g +E%^O!Uc2HõUm;N"i"6 *ڻ񤗺+]P@Xۇv;_Z]Ӟֆ}:gp6ej{x'~qk$m-i(xo\k񥮊kLHMYgC}TӥO"Xv9֩sda(N:wšTz[Y<'AeQ֮X*fx͗X#vyp~Mq gPd 8oYxm{uHk3_J#)F+Y$$WjM'i-mޠ犎d-J7mj \h^"sL|-m-Nw5bKIo0wָitA{ ܤe[Y;gڥ욭Žݦ FCK2REB0^ҳ]궞vK4@:}U^x#[ܯԚˊ[9%UZ #x$Wti丒vL^+R݌jz|^_KT,XJIN0Xct«R*||e|.t&g)̃"B+y:$$kgc_63N?h4$E Bw?'RMqi%owrAZ\R5{1D!YMJXF;^G9^5-Bodi5æI۬2'e # @x[ŵ"7L|X0p>,߇\XH[s?AyiJK-YDх*eBQww*KdbC 8={U|Gc$ 2ElC(x=?Mv#V$ƹkz-6^E|߇ͪ'#h`a jAm}_RP#)ON+eSY<ɚ G҉H_(9dj&o-H%F&@:m5;IfeCROOzʷ/:1NNUuMwvm B"v| ]BszⲵK;ޢzD)g?1k>\'pMľpY:޵2 j.}]99<ڸ%(#Kc}mKXU>g g;#}^# 5R70PjZhLdKf7g[yq6-^)K*18qԭIqM[Y^dpӭQ4z:|BJʊ r:W5{sIÿjS (麫% ]B|I<;|CH죋P'Iyc-dpWs̓=Ae&o)6 cOy+5=(\E&>d3׏j/4VcQ&(Lgڍ{N.#"qٍw ۥ}>crkcN] t%iyi4!vҳN_ƗO|d-](.b‚G'5mv^Zskkslz(WNqO潌E52,] >A5ks5\Om#3 N-ݮfxx:-@O+]ZViN l'9q)[o4YuqlDv}ӃVmuZ]ju@vC]XIEn Y0XQebn]D1Y0HҲ%tq_'I?|qjMmnl_Zkү.⼎6૎9ΑiϧG6M͟3\'#MԊj<NۼY.-ϷSҩIc]@r[BryF޼UF;k-`N&4Pij֭j0l/>/-r1z~u䯮rabasZV<,;cuaBrx?DR_ HUZw< Syl叨 dcQt{]MI,qk5%;{ =;wOhs@뤴C*8 ޮ0Z2qI"9t]R]rBDpzuj^oo#f)LAVC ZOK\^]KnGlޘDiު0dPg6ct|/MKS]>I?$ =@ ÷7S׮xUf${zV))jԔ4GY-׸7iSխ&R>e#$?GoK;#15͕⥄*3(\g&+cS'|=𽾦t[qi:|]TS>r|4&LMB/#X]j:wxP_Iu u-fL:-Q^jbZ-7 I_'|x%(BEʪ77@8n.S Nq[Ǡ[AtX. 8ߥ7:e庍KLQo yUyh+f L v6r8~q?9);}+TYxbm:QO ?Znp-$ET'mdD#b_}UQ U@x>_z\S)vEHdE;P,|{vPjߴxUdsoJBcYjs K gLc97sеk nQ-:QGe۷xcZM[ 8`zVo ޱ7x% 0;k|q^=+>IClSf߈A4ڧjS#rJqZBm 4I,Oںf{S>U˱'j,l1B,p6}9&I.9Jm?ຎ.Tm{i27r9ҬUTXX*q~ 3M+OYI`mdns@s6zqsn%z[eKBwܮӟRj7Rq srԝ:m0OuqS?JCg 1q+Qv ,z`Y7W6w:%ťđI&9#rpGJj|u?.S'IE^nS<w,C|Hd>=9zYC<`6:OH=zbkJ,LCڮkVVvQ,qPe=ΛMe5įo$sgIڇg6}rFq)H9#טZ$~$ʀֺ Ę7QR*q얆BFsMtP; 秴:{+3)-<^K][͍8_0Sk); :쭢B#ZڭZcZ@gYZ~J.LU$dd#Iy5Es+xF^BU]61sWrȐ 0wvsWOjrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/gallery/gal_africa.jpg0000600000175000017500000012414010276336034027400 0ustar madduckmadduckJFIF!JFXX AppleMark            }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz<P! ? ( w,.Tsn.; )Q@*!Td'yJϱQS 6 Ok˰Ni- R{--a%)@@GܛO 0 3xB/Vkys wݷ{3t?}\:</-oq_Ker[ g !9u?PH>oEШyR +o>[ CAzW_g4tGLҺlAi9#<6[ :)>0~ZbM?×ӬUB[&̙$,c˂c'?:~՞7WP)ms7f)dbO$L)S)/i%ӥδBޟ{W3Ν(5|Ц*.vWPн捊7W&oMӪ՞Ik;+3 ('Nqn$0\LT Xs31Ձ\7Q]8Chnxf;obE 'v0~'@BVnA,o$&^8bмQcuqkW<; IͿ˂Y9*:A=_E>M j*ҟ?f'_ diڣs !da'~; k+_%k-in߀щ'#"Iv>7͂bsb\Z ID_ï ƭ_jG6Cm]EN Wlk ֫n_XZEy (^o'¹Z38tہ}V~Yh3 {Dx$T(?c־{jֺ^xƪ ˋVh`vt\`z*`=0/:^JKso]+^Ar<'WvWFN(Zw7 g,Һo/"iTD9+۟jׄ|NHU jM:;h~il7eG_B+谙M4G FTѾ0#ԝ?vmnod q^O xSLckkf fX֌FY01~j:յN"by#5\j闶QhB=8 pG{tʬIvGCF+G5i|O{^D&Eq䒙[ xs6V++C|3Sq<`^ᯆސu}+º gʸS2/S0jψRW0EH!k%|Y8$g#;IiKEH8h[O3ߌ7i_۟NQӬ-R9y䌲nq_]xq8O0A{d\ux>xR4`wtX b_~_Ὲq4|/Y㹌BR=zx2gs,=9JWDVG㿅_%MvN6~mCNwc2g\j,\w67)wi#U pxn>$/f[[(M[iQpH9j5ڇY'm& x!@R1ZK CNRy-y?⎛m&{F j:Mna5.Vh'}w ;C4k%2[\#Z㑵Ox3 e{P~vvj'}=ּo`KߡSִ~W_5fFcim/m,w)1֟twֲC*UzWrfZӺٜ4Ոh( dQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEdM  n*ala4]"tZ-Z4U=qtSu+ u&'<&'h#1W9#ӊ=:A沼?t;1U0#=?Ʒ>%k, *$=+aBǨ8˱BzWiYb]H< N =yI̪m;guu DPXU3?."Xh Y\`y|R@ǿSWč[ɡ^ m"H֗+6 rIp+4SvKPeA,!9f+lj|jd<4%t+oUzV/!yrn,%UHn1{JֻnSWY|8e@ {M 3˨~*x#W&kdkzgYr\b|:=ޝ4PɁ}'?J,x=#2xI-/crWIԂe`p+/~z׳:tW~!Ys0ŚS28-e I T'(+C85/]v<Ư,Zf lK8(ve.vW^e)pdxFq5wO]?~xRV}b*<7VqxᏖ WG>u-*N>3qI.mo`Gkf$+$0Ͼkѭ*|ZKc||6;sc?rs*1:95W>~Ӛ}[{;[o2ĺmiS gnwڹ]ѮIm;3ӽ|a;a[DO ZMyk0@ H@y^F?!x\pm瓈΄>_Jb_"ER((((((((((((((((('i8r}[%p Zt ۙ#Y1"z~" UZWc)= rh[J3$iuFu6|SxySrZZ!UDfpxv=jq_Zȩw(==0**nV=<ikm:Zg 3[cVҧ:-"UrzJf[\hdLI'7^/\о\xGYy*tYݕ2@R 4ƻ5֧K]k663<ב`ƊI廌| h)s{[XVa$vFiw#N<xYIi/z!v4霏/_GOGF'+ivڷnF`q*N)bPvGxcKDvao.v\j7Ƭ 9)Ğ o)tYZi󎿭u7-Skº6m^mi3@E#JHR{zN[E.$hLͫT M8~a_b*JXtTj_KO]Y߳ſWO*ρ..˦N;Lpn8#^g7m~+{֡)t' uSU#ZjdeN/$sI̛S;AoAyĻKϊWS&de3fPxU^=+r>~ڮJt%S{W}]OX+Ta3%w< աӵ(IvxC8A 񜗉o|.$9h>PIAqY!.wk:C%plgBi.st$cDžp<bT 2[ҥ6d ̽k/^*M7 /:6E[cZ>"֒,_eI)Ԟ?LU)P]^miyPݴdo-9L  V}{UT֏t}=S4{3M>mEd)%աx;wp{.kL#wZcI1$bdFsa/ڦAi2Cwo<-|'c!L!UM[kpƒ<*4.QKn/^zW-y-~hSn \~xu5k(׉"ȍ%6Qzf Z|zu_ۼB<$3r$^{/xkO֬̄y'lq 3_=xH}+w:E'_)!2r~`zzWeg8Y=x" YpGWG/ڏ/}v[S`eieWU;qL yKOXCI;.B"spGQ\Y[VRihUOZGT|E+u?ZJ6`)QEx'-may%jK3I= a 0<vQEQEQEQEQEQEQEQEQEQEQEQEQEgN7 JNѓUh#B"Ojڅ?i0TNӝٶ I<»"7Z"TA)bĜg׶+-OH28v:~˨8ݞ@Efa }rsҾ) 6h~ƥ임=OxRYM=䒻 UR tZo>u#ZRr|qUleT+?;Xk$2݀>T3kѦU%ߧ=SwHg?jog4ϩ^Go/2Ƹanc95Qi|#xb՝C&KQԐ*!<~埳QmqOP_V|[[~&Oʺ>J͒ąvD5*J5kφS敮cj |ĉ= dwofk)ǎ5\Xxno i?ku  #, +_gׄ_|!-+Eݬj[47,#Gy> \.9Ҟ/%]CFòũ麌ʠ䬐lFwX\z^ERPq+L@o?1Y{?[m.#ۤ,T?5ꟳׂ- v&3S[2^&VQ+Ќ߲Osga}t-qn 2\[MҀ$P#,nl:TyXB꒶=>FGO׋b_>&ifwqW_$\H^ID[cIgޯ)o/K3xs6kZU^%C5H2 ~x,~|K<+}D٢MV%|8{r ؟ =(|dZC5 v0&=enAA`^- ѥRUSc:8Q?hے{+xk?}? |o5mE>d1A*^#;4ACi|~0xOV֯|-_"Lf|UB<8؜:^ዝECyՄ1-dj0#vrVl~!GS>iFYIaH(\XL}|\e̒lv%E]/u]??4߄<_4|k_iesuh4R>$ kl'RO |{/ŧFml#Ko\EjR?1e% `PNk{K[P-䶱{MiN6hœ*!,\eg j>Yy 񥦕}#VѢX$pj$\HC(TtwKZh&կ7Oك ⫕xo!%e%I$NIWi+Oik~'9ԤivvIhJ,e1F=rv_]B?ڎ.̍cC_"(eye$R C|Zߊuj ]WU׭mBRfP00F4o~stBjfooe3|_<iwAcmzbd}o2v,X`ISX&x*!y-YTc#*G~~ԿoM4>k?je폏5lN'uy/?Q|g\ KQdږƛrc)(W$t\EJ_~iɄkJ Tڳ6MOT-zF[߆XP1piSOt<tH>_%-[{+nd rI_5w676-  )UYcE r3͆w{K~2ӔyO~P^I 7w`n} e ?[~X!QF_霋iiu/ ƺ66Ps$x ?Oo{xOkwÒ9B|i7K;/sSIٶF/Yx w'- m6EsF/w}?y5&lx=oNvpA^~h$CW7 &M?l~m=E|Uj>׮t-VfH*A޿?Lc)?y-?Ϣ VXic:(9B((((((((((((( )v)QH"gX+@8cjkwGҮm6g_Bg8?5gQ鿭IVDEwl-"4'{ua;NtIŬŶ'V֓V4;iX,:~< [ž!Im:Eآ݉c f N*W+aTϫvҠ-<+V8qj6[w,@Hpqk🁼G?cͮ ╥Ἅ"@\|&~[68 WW!\n{WG?V'Tsk}&kM5V s~]{?Aӧ;{C?fiWe AI"R2rYs_bVW^d̿cg]ĐTی9iƲxG<-Ol"Qix5 -ٝfܴى& F:9(oe~L xUx#BmGIL5H@х| Qkڍ"mBf$  yg?ic㯋|\-ơȭh\9sG!99 >*CkZOgqEz#EܶpZ2r#F'$+VeM=˧_ڮn>uw|MQ/Ӵ=:mSWҤeHC3[!K'(vHO_&Ѽ!_4C ڼvW7WLa/l$J Cn#84~zwDZ=!-3[)%RfK:}[Zx3W_ڗPsTj|)s*ٽ>//CK ihNsc0:.T|wڧGb㭧>Щgw|aO%^+vi,Vʞ|A߉rKu^3&-1Rع+gvQ~-w3±|G7i0uu[3kOk7r-Wx|?sTaI%xǟ5K-{A?dդO ;mtZbwaإ Ai[P75 H^9#+y>\ʎyvo @~o?*Oۗ;p[g4oi> .X݋?>HIp;qi\0AkO]Xk @ᡑ\N>O.Qi_ß_4*e2xN]>G,>akڷWh1,XHeUSW$f1G5⟴'F6Kҭ⌵O-@sT]OIE|?o xwŭ7o[i>!C&G_mG\_5wt܎"Pq}M>awԾ8^֮X5V-$I:W?(?kMA>P%dXՔgZǙɩ88FMr׫NU4nZ_{Ze/|(-֯duӤ-xWANw)¾}+;^u_ QNZݯ_v(R~lE9?KV6дȭ$u v"`O(3X_Gğxq$^ju"\oqee+0"`Q\敩AJ68a1m=g Ҿ"{Qo|'VQђ6i$PeH,ra][? χ~ue;ۣ}o':oeߙ/o|4`}>K'QQ9dS_Aj_Gq|J߼ /OvLeݯ^mij~¸O^I@|uiEIZWʱ#ir(TRUh+IZ2Cx$KxTVǍ7y_\YpG z+(oo?=((((((((((((((SnQ4C怣5ige !+iPWq8U bxkS3mUV.xeNKE̅MsO(6&^o(9+ĊXy_Z $'x$9H.@iظLGu=z&̑}$C=2{VD#Ե8⳾-!޶X{Ҿ!mK0 Z`BofރM" uͽm4ihUGHKC H<)^7wO;FԦ}Z)cGWXcnf}ExG(|M_=[/p89[~?,(wv#sG~RJ\*#&_tzuTח{o])_ZiwIKy-g!dl} pkо~?M535?ص>)/;-] >zXZ[þ Dm{kMr$.|dM+V+|lmͨx34{i UF ?nIpJNnkl:uuK_C?coKC>j;:whIĊ0Hbq_||GΑ{⏇'N5S$ǪhDeU4ƼO>~#Tvw`5)6ܤw𡉲`lw(O]ޡkⵊiFL*kzׂSZn> bڒ~~ mZfOk)-nm:3; ]e m z-_m hw^AsKI)B71 Zo YX6fB=fX.DžY2X_|iW섿~/~$ҭ_GW[][DA$fA*YXW5r9JZKj5XpVwWGcod,uX~jҺ AU7ie[[K?k:z77sCEl.A@TH'_l'>;j^Qu&{% fy1;z!|oc~7Fx-oc/0bBdWF?.xMFǡȽbzg5CÓ&Mib[=eh#6[r /Ҿ!nvoG 4,qͺs 1_? w[o6iz-&kie.֮IDb(1W~.~*AY>-}34d3rdDmxL>*K] Z8/?QOᦃ)k~7Qx{I-[E6IpFvj/ /_-1/|ki" x/.XQƀ>VbW85ZeƟz>"څ>`dMW2ǽ|3Rdۏ\{y/[{ S&3`3NX(ۛn u,V779AY_U i8PG×sJcD_" yEI˥]5c+K\CZ&ti9U BInNk/ #RӾ1W-n@R>uc6G;|ןǏ!&EFjZČ{Qy(=}W&zjJ|Qo*E7%C;f;l]ӧ}o™sy9-X.mN[ctKrm&[i& A#W+,|-ԭOs}SPt۳&c `lVLNFIרԿw"3H6-Σg!WrA׫azgV<3P?|AN@>i wmDi(qs>vZވ#`FIQwrao6<>WJbe͖bU6|:/;CTfL/%\Rc2I+Vz߷лԊz~;?Ѩc3?_8i-[Y $! U0];Mpk~sk6zeOİiHAfb%YKRjo4; u/Wi֭*GF@z+:]3": ζqwȤ3*+W'<9Z&\]q-Ӯ"I*#Yd>xJzFZt{]i ZȡuDV]CvEIח=b#{>1o%kOAQ5kS,vpqឣŨxO|i8q]%XvRIڮpE|qva?KA{;AŻc<8-p+|q{k:]׎nhUt[vE8\oF,־WFrHKungsd||;m~"~ϟYNӮSo[/1? o-[ǛO^ l_]AqkpN"RBnVW~aKCxӡokm-#ڑEɹ&_ kQDq.8\W !iߗ rK9ȯ+-QMMZJj;'K#9d}.l>x''4o5[Yo{\EDs#$:m,l]+SpxEU%O[*r6R7z~R~5[,E$Jng#w42,LS]4qi;+-g((QEQEQEQEQEQEQEQEQEQEQEQEQE8PK*0s]Hؚmhk^5Fm7I'|:~-kVV@g|dcҹXK̑mgx{׶wŚ= C/$N^ 2gWxߥadsǞ(WC^H"ѲELqALះEHWRiw2#  Or)_;o ϥOؙZE"p1w#b_~~ WΑgAKl~BzGj+!oD5G!]0;?~#|#m7z hPS3~q;O |K[طPZL̶ q.ėKlpeBp3B5K|=~WаG;uEnx%\`9 pʴZK_cϜ⮭zC4ccu߅*Uk }Fd KD$an/%m^;u?ًGWEK&}IhF%D46#ce*G*kqYy8P>q gscI\k὚梭Ut!SKZk~tkϿ{?fox4^H$-ĒN,.zIFNU bh??g*}/ǡk^ eqy;I; ޾/Iſ |ugsڅ=,Xuo* Wٗ߶GĿT~.CxW =WVH0 xߕUl㟭tjN-z;'ݕΚ{Qxnhvb.4R{:( dys'O:rk<]Y5 pp3~>񯅼CKXk-:F{}_}-Qcd$||2ƥbǹ[E@#1U[Tq9(F}.q~G>M[ /T\\_nrX!E;A*>[E6Bм'zfse NwXd^B)M ohn(EM.Jl{I#oghI l?x?3go 蚦<Y\>Ď&ÄLp*ԥ?g/ݳ']:p,LVzl]>XZn-"q=?ڭ) єm!qxXⰎ^JFx>.ʢTV/W|q=jU|oLuz2l#j7.Ukg oKJkKd6Y%dĉIؓ__u|FWEuXd[S;ܑcwd?o x߫jE5?1w,-9zƴPZu~iۥW]~&d4`W/s $`b(((((((((((((!@u[;aU!-pkV3BTw 5eQ.ettaJ2:Λyyڲ)3cJ؊O^MwV?ݵd4@ 3zנx"Vhyоve=vj𗀵sVHd[#Pģ- 9|`9}pPv_c:7KSDžc{񾻨6ј|_\#$k蟂~1kկ-omKpbҬoDVo.2ۓbu?6>v\/b -Ē9SvfᏀxW inĬH(ܮ2qUwE}e Fu{֛J*W[_Mgx]ev߁píyOƻO}.N{,84+t#~H#rp1۞Oj_W ~Ӻ^7q.f61 |$׿1[j?5+Sm/E·zs.UpA W9RZ2ZtoC i$5^[]Mr ^0"p*+0grW>:<=:, \ *IL+i[v1ɬ_4^$UIm8]!N۹ۀzg WF<:2;2Pc'1HF!sT;n[=P#떟 ~"6J"JԖݯn{H8ʓ#U |q@glMwP.-eHLj +۲3n rTRմ;UѢ.{#+y ǎwھ=N'kpGj,2@Xi%h}DɈ;uo?TZ.=7Iz8|s|?æ2xm fVRpX8>wt5 v+;wf{V*A{PnZ>#[kB𝏎u3qb6 ºJ3;z_9|c{xdW~U)WqF+3>*P{_Ez0pW+Koɣ%/MH}_Ikձbx~|-5~+v7c5Mdi(ҕ9Υ7Gg}{[G/|E4ysO]ad"檶+FwhsFS߿u>?fg?e) (ּX3iPTeG ʫcUi>Vn Z^߉Tl/h߳/FA|jxkY_ RHWˑNDkM xXizm& ew?܀|FC+ȯ><|2/jM8Asⴎƾde@){,k#E~3"vJ?j~D2[_Y 2,˵c'/K|Oglդh%cw-[(^:Vt*S'(^_i-ދ2r{][n޿WOSK_!Gѐ=O y&Em;ϩ |#Ű|(eٝ?TkUyꄆ>_AP@'5CM_+ 客q(`Pq3{$:Slnom1B1[gg !~mi:6ӘUb5p%#9X6G2h ۷Y^ťn}-dFsםAax4"o&Вw&>)XJJ(((((((((((((Tº–bo&+Gu^SY7F* $P8rEbRnuz_cTT2Ǟy~^;{WsO ZZiz_yHmx,Wp# rXK*y9@@WW2i4+=+N~A_t+Rw%4N Z73\#Lax; .xE{9W t|K BehHY(@<kƼu_?5bKx7/ۉXq!]//uKF_.&Ɏvaq:d+JZT ~-_sx$hmG[+%oa2m}#okzF5R[x=B Vb2< qZ?> Ҽ3x~QT"f'nŊs W#⿃.~st|=V>%;F 1zᒨSJ _O)Y#s}^AyIe!U g| lg~7 X_ٳu6uho& ʑW8a5⯂4'ѵX3)[^=ժo>Cl&5#$O{9gOGW?Κo2/m4S%GPc.*7c":oDѻmt5ɇE/>y.|3ykJ΂1 pXN M}3eGgx5vxw[ 2w O|C 1~Ǎ^m$m溔0-P,-s_Od??kzw!},xgEd [h$8V[R3kkM/gϣ]g#F 9#d$, Q!E~9^ /3t::7UqNK7,U\Wg[U3X%ѵFᶖGI 4dDVJOYG>I_uOZtXut-H6ød 8=X$ݿ :jS|^I_| Jѿl_\|mdž6>q-s؏Y=;˩KP9I"(#uZ-lHgte#u9I= x÷q|?n+yS,_:p b 9̾ۯ;Լo^Az,<WRAF1\2G3H"UDb!A7Mkwe~=(:}{y;ޛv߰7{6O|6Ѿ xwMVKSyBcvm|τa ڿ/PL qnۑ#tS,`kٿ.[w+Bnuu 4@yry7U~b yƯٳ_!|4 7Zd3HYNֽ4J4:NC? _OSᆟ/:n-5dmT}w"9ne]Ye}rC*_Wǟ-_@ƛe/{ImeRXe#ȸ%nܫ| Wïv~0~FrKmjKg{\黋GI*#8|J_վ0~:c]R\\)2Kcz;v#e2((s\^:o~kja~4Z}akIqAeťHX[Ř#'!G^6aM9_sfp*B#~}/O>$'>!OQӴ oW6k1j́њ>W'|W 7jZ$z$h匴gnv', r+8{K[]W] MӢixk[Ծ =K.>+h-}j(Bm0ݰ;5 NiZ52G%.ʼnoyGĵK= flDsI9,MmJOOI'4S.{ρc{]%>5%-auVuDٶ3FMHbK,/|k ȷ@03Ԩ#S{[s>k6×7Pj!#$n\IRX-F|ʿ{f0QN7nޱ^{(rǕ#k\GNmFHȌ 9 5ύ|q~2T!]l'(6t1<5tٷk؉Pwᷝ*Pz-n4ǥi WݳqaZNmm4VT5=.Po-2RJ;92J_<3~ݬ ow] I&thp&83^g;GQA5X,U%.>uR:r8s(TM]^Rj^Z^j[huXmӡk2{h$ ,%AWLNѮJYEA,I1]Yrb/?ƟSǺ?oZ]lqmk,w'+tu?'eg\C6Irh*" e!v^"ǧ+7^f/|}gk?V^Z3\\Mq7?'F+?=4~ų[vϬ͆}XHd8#Q `a ArὭINז;\0EdO~?'>| _~G'l/DLIO_|'_:- {Nm>'A #ȑ2" {'tm?I4v[6 2[’Cu Vm?>&~/Fdߥγ>"[k $@:3.n3{迫 ay%t-o^=]oe3m5Pcacg$+E}~Pڛqn8f[Yt ɤODb8"cU$!߸iڟ-zg6Hgᘮ5] ǹ8L`wȯ%?SvѴǹYI}$r`-[bpˎsUnT=y>^55,V"-(nZ{;|Ik5>݈ M7˷% ;@@9Xh K| ۽]/NK _nGi=g-ρ4Z7~4ξ(?E,쭴E<*h6_.Mc q ? {oe<:UKW=#߳wf}?6:YxFgHեWxYw s~. ij^j0[s 6oG ֽ,ML9ݯ^·*eMiu쏅 IgWԅܺ{|A泞@>^vWQwƻċM>Qs6x#Όg\XY \ߋl=)qzb}PFqg=p5<-u14נ_\[i& fm %N+HF*1^W-y^I\䴓^;'ĚLx>g̰"N ney\k|`T뺬ھ2YāX:W><#Vr1x{G7:1L7+ sͯQ?4a34Ob11MwչqҿmN;QAj+AER((((((((((((m³cߥEN)rtݾ~xϡ@7Ld[{r鷅Kjo/ǘ!u`O*yym׍Y|9ysͭ7o bз$n k_dv6]19R9I*NTվxP>*5٬<44HWehP0qⳜWqMt#N+_ŧ_ॿ7-EJA>`v&ܱYvE*C={sǿm]+Y|/m2v@-QA:3㪃NG`G-I %$hܩ $kuܹ#?$q^{W񕞩wZLZ2BűK- szfShԚ4? mi [du Xbl72@98ࡿj~.  iǫmh"6ڌ#2d2 |]4-fR{uxj]JyuURtvqg鯅 |A/\~Oei$0,Qa+0gTm饻_6{_σ?|k~)n]m:J2(`|0ݭ{ٳ߄ڇ~$k4wڼr9ۃ+GOH#g׺}|>!7/D@@>#~7Se?v$e]~?QtwWŋ|AIOujkq#WdL*W vIx{ĺ͵=ݮub-\e|no1HcGZ~Zh>qNx^ɾ(/4˸jx^=k?7,Z/ 8~`}hH|5/Ǵ)徵֭] _9Ljd iKAb|9w??_m+F^E14 !\jTVF'J*noշb>JQw'}1Oڳ_aٯwLj igw4$`Arǥ㘼e;jP;TK%!(2_S/>9?o?i]x<9.:k{p9.vQ,0j/zglI%`HC**^XeY*UhJմxk} TB3`3i'e?>1F9HtX+ȧmbێ5%u8>g{3%RJѻ>m?qjkm4n`~UxWW2]\s#erֽ#㏋6^d:}Oi;? 3=k8*hE'o>9(b(((((((((((((>SN ݣ]_6œ o=pV`3j:RJxu 司J*EҚv}Bz\~k^]YxR iEkFۃ4 YՇ9SuyCqY1C"^3$% \rkφ~tfGbΗ?;) Wu+[/ et+\5ݡnS vT:'qU/=^~K%1{&cX%k;kӻAq8׾~7HůeE2#\x|Yof$V̳^[N6 Rw\ f/fH 2NOjIʛm\Lӥd{&x:kr@آtt#jARGpl_tu/ğkOkUѴ+v'O@vzzvX^[H.,VUUUrBG\ڻ_-Z.Kӵgb(HV9$u\4VҎ˽Ua'<|w=yw߉Y"74Ԯ󟳪;pzq\USy~тKbo[⾡w7=]fh& ze4J7\[ɲ@9v נ%ҿo_( |QsA,7'|o_Z͹*O ȗ$q_b?O$}gϑug҄XwJ7e]KOዝFkKo̭nqs@!z 㾇Uh_4Kq$ahЎH$~-~ʞ4 MXꓩ 4FeCHܒ QTGZ.Un|o_» ?E o<OXiP'v0 e, |Sö|E31Ë}'V̦kyĊ c XckړӾ ~|/|k2^ o[HGِ*txrA!vO&GΈz|h!~Ǿ2~:Ë95zfT~vIo-n%gDrc b|[߅~!|pܶqx_HZ@g ibI$>$ִsm4vk%ЙJ륖߆͟ |RѾn|5HUHP VX2nSϢ?hk‰t+Tysi[k_Mi KRgr6E}ceuAlF!orn`<-!ޒ]w1^iO^L~ úZNiڡ[.dmwd.A 7.z=Uvӭ6|'&42de\V "Ӽ/>G>yӬ.&"Aۊ|t?lZ?Yx{ ;?z`FV$gq'ھMa |;{k4d+:#5V=+Z<,kUk46N=[__RߍOqׯΪ[VBK#Lˌ3E|^ Vե{@iU8@cHoHR濠J+?F|[g1"-u4]"AZ@ t2~ÿςklcGh&UTbaH i;+/v7v]ec3 ÿ 5=᷁wMHU\T8р]0-}S (kߴOb:Oi -.<e2PUcv1xm5zaYtUio0vInxmȯlgMGg]#62aiё#G $vC*p'W}:f֭]EZߢg:R?e }.Ҋ%ߡll*xG,]-t (-6< |?#4'[9PN.v9=޻ _txbRךR~s@7`<⋟~+C-ʶVYw{U`ݞ<׵').L㴞֩PI<+Lf*7:7E6AEW0Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@n޺/km2$pv<~=+*eFksZUeIKYgCA? N@ǿQW?6-.Re s*J(`˗I&?-kS; TGs{3PY)WYʂ9H#Z Qcۯӡ H}Ogj:t7W:Xdrq8'39maYL$JCH'rdC#Ԋo'W{r7^¾ Jo_v}84Ӽ"WVOW]_D2%,;׮W+nc.U,PFA=װ6Z%y4go*I{1=65P> skƚm;U7l#fmc|/ZZ>צ5-;J.et* HlZyV~# 2\#1")IF(hYceWS^77Kʧ.A4_#wltϽx|_֥𞤓^-M,/Up,rWySRM(GQh*K?MW3] <ͮ]&4| HRG͚_U/#|\o/W6/,QhVXec +.@5k|,>2~zxFϓTwT($ל2?b&ï*xE4? ֆ-*t$ eI?{_e#ti{^ߋMGxB1EtGWZYi_7ètOk*Fmv|$j;J'* :Zwkglu6bk TѬqYD +Mꠑk "e(Ӧߑ:q<]w^ <3˨i^V\pSAzWߴG xǺΓ~$^ֿ̖ <"TQm7NP/= އuéi?-͍ʛ 2G*ׇ1xF4gJҴD*(QG0^&V|oz Ћ7oGࢿZ>O~a+ڭ*5b1X9#1??Q|'iۘvۡȅ?4^[7۰-#F'Up8wij>"|O \ky Ήs<bM_ ΞEm#RK$crX/zԷ׼]?!\Zݿ.-/~/k5zX,nu<|\wٖ{P'.g'O~+ukc> QGeQ'k㸋4K`;.wߪ[.K/MZ+o (L((((((((?J((((((((}i絺W>[+f9 hV*9Rx=J ^.8==kO;s2boj29:!i{?^}yiu"ff>?#{osnm4c{1zw~yKF$x ypAvtf{3_uCx TvP2'$'&-[} ] yKxeW,pp~c9^4Fh#S-NAUENYբkR4͜o,n| H-ݴAץz߄ןz^+h]$2e3Q۵|ψr٘ca$.1ݎ}r2NO^/K̥ & $،1ּ Vc=%Yǥj͵$XW89Z׍`v^e0m `$oC1]7u4v2H‚g88 &C~:}C7DmK&!#d͂`݌s_BL3sORDkW/5I 9ֻ7?:NyfCy#LIKw"*4AaA'|o^|PK-G=KlF2m\w5Sɤ޾vvMFOFΌJm.?):ֽk{Yٿ$7EWƑ*ğtGg/ ~ۿ5+VvP^Xc.X);Q+ٷо ⿌ vC!iRĪ@Tz+FY %oPʈHLWe[YW;6Kݽ>5?i.h|/i58_\0 ׹x7Shӵd`6|c 908rk粕<ɀȮClBxЏJg{!pt2"īgv3F_^ܩ7xq ͭ^7nF xaDSg$ 3q ^OC Br? O&޴6Lx$ #+pkO2L<%Ȧbw7@y$ 蚥Q{穇RWu`EqH; +־늉~K$l.I^^+~, vZ1 W>:\[_tXrn B +g$':/Gז0Ud,Hp6'#>ƶءt2l NF:+= ㉗Rlv6ݰnRqGC>4[Cef~YT"0dc#sk&FE+lS씮l;H}|xW<#rj>UHO7D FNw+r+0?<'9m}VMMUj+膪BSQW]OaK >0]|'Iw]=J!Sq y?x2Go>b}:$fɊG|ɂc,OzWn~sO|U\>IѰ<Gj#tF#́H #vo Z KLdɐG^@en"v|\:_YeȮIZ]t=KZݒWI$/tԩ+=x^|S4-b6K'+C_^}Gy5B:7agtMueކ9/@#NT(#95 u.^ T~/#mPbV@N!9'3=g#.SR>o9W焿i3JQzHM¢G7L)#9һ-|r WE>sK ZV~.E>)lbW9n5o> Þ:Nj53E{#2ge 2C_ jo7v9#6FT Fv\ iݽmj&nH\2SE莚X)5o[ߴ=/QExzRmwjdh 5mnONOQ_O}b\ 4xUpIUv eoq3_3 =l&K/ më($gps봌^/ ?nզj!zϽgcO(Õ{klz%銥I?]$~VԼGe_jƈTK #t>7V|)ŚEG3b=Bkƿ> |A7+MK1*g}+g-II`0Խ3+]2X ~=hmA|u;-V1HR@dו<#v$IE|?4U9z%1rwaEW ]nqPQEQEQEQEQEQEQEQEQEQEQEQE`4SYUVFEڼt'PEPEPEPEPEPEPEPEPFOP^A&W-|[;>-ycgQ]i|kƛGEo_ͿLwO|_x Mb#sTVڙ>*E}[ye͂]iu˓5nmN3f4*I"6 Hv\rKsUQљ~ɚI|Oo: ի0}Y p?Q?妞?D9{|*^iI{oܶUċt*oU nFm!0Oa3p:Zj--BOcjdMݥڋ%`yVR*WC~Z }%VUhG*p5\-Etl4ˉ#_0 7Qo[I-nxlڲq8H~&tz66"tkHo/l! ̰lf?Qɢ^)ɛzu VQ[֧}kڦ8~0Gj: RkAf0, 3t^* h6{Qp8oj2ŧ)+$v3w x}rxۨ-o,I;IxwHRImm&M[GpX} Vm9#3-unl&|#s~/VclaǢޱN75l-ne2+,@3].:yY{ZBsjY.%+ &i]!wp)Uln4jֱGg<-H2-'OI2ض&rݢWPGς˅'u/5hg|U2.{ys- ED) Uw6Փ'nocᾩ-v쭆Dfy@biO<$S;ǩ+!0dh@6޴)%uUu_\!@7 ``nNN2Wg;Þ<*T,kGtkD>r\HyI*X+sjh3R{yVWl!(E$W'@Xt=ZYl/h*umX@re.W sg=*߉?hU4x[ 8ČHA`[ )5(ğGE!"e}2 ˈ׀W$k>~w]GV^Py#2i#&s5ߴ4{ xWUvKأHѢG+1$@n[tk/YI4W}WA9B")8ּˡo wM,kʰy^nU\FTarNׇ+(p7@n$)eKaGB159[?^ tVՅ  ` yj ~x[:- AmIeE`*$L4U@H&3u?+,lwG F;yUqؿεxc[Ғ7+~⹻]upw:J9)SJ; oVtXI9X i;ֹOCZJc܎ AppleMark Photoshop 3.08BIMC     C  i" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?.v*2{S|/;-LW{PpYBypy8#p t?^4`IU8K@,CϠn^Iy›$#k″iOpvJیzv0U'z8U4 V  cÊ?*Wp0nm페Pc%QXgy Bn>)?9Ucˀ8E G&Q@owj7Eev<Ñnrց, xzQE&#bF"U8,}_!}(!X5u w?)~S ;}޴;I*lt;H c~` n $)@ P a0@p(IdK!N,g&1V#M2>=8'?&]Sy\cStDɃ=E&Fq#ڬ]wfl1 `861B dǰ!Y68cJy%sG䩩൶!e >tV G;7,2riѴO90wJ\yf<=M*J_uSbRR.:vYPX|wVB1L*ivbQ3RrmX)gҤ 9.?Kts8Hp`<{q8l㏦h*HPfazZz#b$˘ZybO/M530-n:)ҫ@ܑ ͎ %#94i1?*hPc҉mY߻eN3O|ݙrITd3dBϥ0* 6=M sH8|m9TyM؏CHT2{ҥ1vrqQvxi$JT `|E! >~ ! sR0rGozqPz``):IX$eVciҀd=h$E_7i6A;R.Nu秡(-n8`ңN(uD ).##rlbq23_#];H~}/5g1H=SisA%)sޔU,0) ӜLXT?Z[Rd\֚>D`wʌu%Nr})'HXy)@=qڀ_ʒIMEǵ5QLLF;SÌ֡X\aGZS⓸;KUFr9}):9JGSTpZ^Ƙd֚Vs"|SGAlv\ dg}[PHřqS%$Ot lOZ i+8(zRFnAj_!=@"TـԿ?j0d+UJ&2RC]" 6dwazX;'>af d'#>`">"vdMj$Gbz?Jl@@?wF7 t4ޔGN"ʤ=3G V'-d?j'(縠 {Y2 4`==)3F84Ia`pI(;x>"ƒY6Kt{})w8I xʀyR"fc`:,'I$h1)IPO$pY)ܸ8\R|E+%!POyT(|!#zz`$y4 0iO?*s"Z: (P~{QFi(xr=p"*6x@c$Қ\#siǥD\y9J4`H@'mSڃGYwz; F$(lgl9:Nݑ!_)؟$o̷n֘⃐߻i_)N b`cx<OJD?v7F2ǮygA(PE#'%M-6Q?ʮ˰,AcܣM٩&@ʁO?*QwЭ[/e_֤$fN0)OzFSe H'Zg?$[FHmJ&MQl.fFep@`8֦b%>c=O\no}$\-%IeekApMbHk-kuQa޻}<طxw@fK)OB.8ϱ⼣?'/޹6ɧ w<2+#<]|[_"rCdv\毢x.nݢI^8D[R W";c<9?f׽Ѕ."[(9$C\mY Ў '\񷆜}4[ i%  f4VYsVE  27q⣚*8쟩k;n1̘ڻ gTx+NRO/&[^6V2WR|TL#ɶY`p1zVU1KMbiD m!K.rǁq/\ ?2%RG7c8Wc/4r_Ui!GMkKh;ws圎=7.SvW>IdYmƑa5+n>`ZZ,vm?>mUC#KSRI])\px:pUSntۖm泙O1BGLL@0ݻ"\dAiO']ՆmZ+)V>5+/]&õ-WBx56 Yb6jf#eoI@][⟁u ];G1' 5SVvs̳[7 I >fz͵cNcI1ꑣ?oxx! }:8.>6vZ4KmCD1r$FX85kıޞSn][E `sj.7.|?Ohv1TET'¨ks]>M1ukM5l 4?8c=:FojzmrjG˝'+/e#r;*K {=,C#y 8^j]Oq7Ⱊ]kN$عxr@튩`KWIs}XҺ]CGufFB C_Sy>x[Mk+f5[x >I4]CF9u>Ķ&ʴ/sG# _Dk?0E{(nx?p  0q^Mcwk~VI7eޛO=3ON^# m$UPsg/"~<ſo-qJ f7h gäkf0@ju#LO)e>#8TbpUUsK)˻# |7Z d_D礞bg-F.v,Έss_Ưh-ڦ!3uuojԦi0*i>2wW㾋n^#cȌH B>6kgjD(IIIׯHl8t=A;Y<}p6SK qS(Gub/|k_1Q5zfْxeW쎙ݧ. A\'9ڝݶh,}'W|C?p:i7OtRk޻&zr +lֿM|Y%Ⱦ9E> Z0mGKfRO'(puxCSι6!p>frװoIX5I/g*Tx"GI<)CS^`O(O:o'7úF!}]I \)޴s] MϟY ~p1BE 8~:|0'~Km>Fz+KHɗ |zjR\&Ġ2;LzS*<ݧ؅f} `>J~:PJj9Q҉՚QPJ3P?MHN$ր7 JGQ~*m9:P~I7s)1ҀI. 9>Scڠdrvh F\w4HRH qR}  9'u!YQ@3m鞆BKeNߥ$$1ɠ d OZ٢ D`!wvNi,s9@IS;^둏A̫3LdR'Ve3ڠ 7%EMcnVY]Gqlcݷy'xW|79.o#Ռż?&NЗO֚ʀ1͞S]7&fThCh=dNt^p2r)<Ṳ>tsı%qӊv-DN$\!9B~N㸷}KSHm%$VFh?Ef~eiS0&ېN5u{%>d6* HܞWڤZkvgrI2e cǭaj:1k(X`Rm+n@px\怬Lo7ںČa$gSlr 98ֽ*aW𥜳}@L ޭiM-$;>·u}iXiW-jr.uU69]~)GqWm@RTw6~m( rzVϟt\)XndrUyڀd)/yR}{XB1 gs.5[*zYD,a់{cIe_4iF:縮yS{yjVVnf́vkd[k;.";XsD5Q@;銿gF+= { o㱭&[*YK[ ^x#AcIU rz Z90L}irקr4;5 ܁Uی}ߜ?g`6^-m_D.?Ns]h)uOaF2;Ս";vke›YN;qTͣ:VLğN/W}U|=r"}u,C`לx Y |?XgW` *n?ڤ1,og|1+qlYc2W4q463M G˭1V6W?(># Q{ -1y2Bbr~GUpE~!X`x;VȲ&?r'ᏍcYfK=?1mS LSi4ʌ7k[?)^@T緽;,3"Bh;#=MH"_l7C?__j4t/[Y)(is0?_F?vַ(-UB$[%njI;EzW%.p;3IeET) ״ȼ?ax&gZ)<ξ,߯ٳW__WM(^;E6pWZIFo\ȑlW:x?,1OP+gÞ[bkֺ62&!RE%SKIJ? H>뽕XYM}!EBC͏5_i%XalO>ߟۇ^Eu[`~3Km XNG=_SM-||bI߂ ~|:$i6#r;+A;\s4h6x/o9?wɷ'i5g_Vڊ~OéFj1GH[QS`5^DS7n<UB?)!*aCסQ?F?ՇLܿ9!B;өG?N-վxW$Mx|FNH@^sO j7:}\4w|A_ƓKu<+o]LIRWNi:%c~㷢;"M/V]BR) ǧjw%Of9~w/°o$Y&uQ,\*SG/4V4%Qo9m #םx T)c zÕ~{qJdO&(<.1\a|3R?OmF;_& #hfڼcahj֗qޢX6Af¡U &U,s\i]x|F???y4Ax5כpJw"+OK$gPfgĚ2'sXm[: n]}zWmk2[ʍvr:g#uk{z7@P*7F3Zn;Ns<85[ gWB@jAOV+,H9;Eyq-ͭBD@#F s`m5kGg=ҁgҨ ^f[`C,;MFg7MQ0)xb eBTmQ\'Y7VXx^}v 2K5E ]4i;ΔbQu#B]V/n[\u vx(e1Ҽ6f5Y-̒bt1ycK74Ѻ${PI#|1ֻq[uΥ.=-60'#Es}}Vдm *z5k_Hylc<Z5_}i"ڥbTǩ{Q{iw7> 9mk>1M#C3׏|@+dֵyh!uKpͷ>6+i! S)I9UR:U lvP2߮*%(.< 0tppr Ef?%zVR2&,=knMcmHaEv䑜lܷvp_'xQB?*S'JuQNXsk%+Tǃ~ FӤy\kt?٪x" f(>`k5kZ6wd}Ҿ;Yޏ]$|d+oܖKS28 _IӹyR7׮.o~O-K>6ZڑK)9@=;۵-*f&>Z \z}XZ%d,ʒ \sӚS6{㮓WFM؞T0jGg`͖ݫi~~U|Șe ZSUҵX]Nn6ѩzW1i [ibGd8tGc/?[rHx7X'DD~TnAڴg5 yZn!]gמýsx_6WX^f60ny{?6XD#af/yYu\jv:+~xľ.́ Փ /YCK🈦2(@EtzC=e$1O)ss+<iV|[`!%UcOgVoyU[}R*3ˢ]6n\5 4/ۇ|Muy,auf9Cb/ e{w`Y03ţ@fඑ-c)# gթhif>}%uk(`!FTpy+<)MZu|\ݳQڹc׈S;\F+ '=x#ֹg]5)\]#W;XxB6j2Ylt%F@l] N:`=72ڸԮv+夅v13ן\u֡kڲٕRѬ# r/۹ZM$ixrGoRV9eRs+Kp~%Ԉ- ~cx QnݵR)*E<2Eq2 .8ޤ%>.}381*Ck~*֏`N5l >kSMD $0VWLoxPRZKdGH^vTu(#ӭ4Hj>n>Rv.m6mm5ŜauOU|ic4N `\Z  ({s#**("5yeRD{C +(?0l>jW"̨`+q z޳ud"uZ[}NR xpkzb[K֊~ϫ`~ecҼ_Umu)6p '([ȉvDI~U.I3x9|m3^$e|[rn}X!\O=3_ xsƏIFk.wF7gn|pm* f@6n+(##!ODן=AAF'ݏ_[2("EǙ*ޑ%>dg B ?ҹw9n..1̯jgUZ]Y^ZN5ysl[ze̷l\.[Uه3 0^*ճܛO*8@ZX{M5M4Gl}O4$kym/A <}j5p3522!IylքzF%Ҝ;?9}B #[AVJo.+%J?g2Th{3LOʟpĭ*@9ݓ~_Jז+VvR[ }OʎYԤ`!UIR OMoHbFws*Y4!=y 6|;W6`/@ͷ 6>jv.unV[l>/aiv{$i~IoEՉh\Mf$aZ8U/~u"wW ]ؠ22[5urpW4)$9j,AXs\ݲkk0h5P>iZ~ˊOp%ar΄,w qZ>*jjȺYRoA*irDnyjpϑy-j+ܖh#$ 4($jWe2b27ʽGA&wEݢWܶ{jiDQ6"Dk#޴,kafA.FG?kd6.J8l{DoL~OSQ7 x5XG,k"y+p$08mmR +bJkv2D2IHZm(;^'BUΓ,EleõO*kKXudʈ`bm]u=}1ޛh*@oi²K6Xq{Sesh`$mb8;b.`(.(d"e3Vx83۪ OR7ݬ7 \gs}"NzU4/ML("5g{Z8+IjYxķ2')s 5m|EL! ,pwaM0Iw憈|8WAq,wW@Ĭ3^vkr6!BX3į=O51VЭdMI꾿JϷin]/ˬ,I\69ȩ~fahcUUǸ4Y]b+8Rqcuh.gШE'IZ˹2RA y"=ԑ"\$iP3ߥTR{mliEqg~o lLr="4>$O 0Qcp &Wf{Y6eo\WY&# ڴQ|vGx91 ZMθ~\qJsSF= VbvpFl#@cK`}-eV2.G,qj9]YZycCSM`lp2[9銂0KAQR{*gհUF%\t2Km$UF\yw:mG'a%ڐ\GNGxc @#Iˮ1JEvMbY$v-wpړȻ'C uKDzHhYP I)~6)%wsTPpN cAꅄV|g8$)q=aXGbn5NC5CȾWMQ2#pzb/iI*dĭyiAs=Bdcr>h³"Ns,x-ښFV]}:t]¯~77G=CS$Sg(=6}W#jKt D-UҼ8< r}9 f9;l|K3^ GԮ]v0HTnP j^h1-Z2d] ,y1 ڿJ~?g[m>h/aƹpXBYuN'=gGui̓5Mpr@@rONo*?hF|A⿎ϳ(@VxbUyCkto{?h֚j*|Kc̒0Vݴ` `v{}<|\$u{}mUH ..fbय़T~ xƟ<k?7A񧊯 S#1)] m S( K L(Y'g|m2k7ƚً`8)&JJ1׍kS/|] 7`j3sCTl 9S_>LJ9/ 鍭]'U{gZXFShD0@#ФGl mpvpA:gk9ٽ cMFs}O`^-f2I pqQ$y*zk'PCFYI7\p:mm ti^%մ f˴@[uIj߽:*+ykUbR8DAF>nqJ6+ ]|ͫ1OI~-꡸e6̉/q/cDjR >NDs|aj.ljܤrKA5gcXolpŮVΑ' O M5`Fl=9~5=sy{mB1ŸO²9Ef5,KW<V2C41b0;v+`(5M"&hԜmhH%էSY \<Zu SG$r$V,\M,%W~u0s5h:b>.[mP}_R]yt˖7csֵ%$[[ΐ LD8=9] yEIe6m턓qm:_mOgO?"lOZ Ng{1ڳ5MGG(bӚMpGz++ג).%V+4U@}&;Mha7~VI |͋K}C;=iȓ4_gU_)D#0Js.ڹVÚ4sswl/`U-ˑxsJEX9B|?Ge<7 g-ugvUi =:ՙq>>as57ґ#C`Ӟu$qTmm@SLu$618x"*,r>'Pڥ-i"A-@): mZmyie-]ڊ)ʡFA$kʷ8z;o+-`?^iL i/LS%c#s-gixe G#aRp @;NM|13 AźH ">1nP>@c7&Pm;~ x</Ə^ KAY>t.@HeY\GepwиM#AEhzMj>%%,92gß&^\Hh:KKӤ/o~b/trf3@_NKo㟈q,]ω|'Yͺ9j?Qe6QGe&iL~m\ Iٴrz֒.ndiRM.?b:W..ۛ+>au$sFc<0%kQ]xf kh.$Id( :t~$_Ï5ږX?Ol40'cr#8߀^6}Ҟ0Dy|G!dqDŽYsMdYi^Wg"i&r0 !;qs|R- jPkfoճJ^N,kF`g̱Sp|W|?>5}^G<=S,#;ٚ g|CF~ /_z<RҼ1[ ("DynrT!eZ<%^Z~?q}\]xo᷍]fM?KI]SPk0ay d$MG>JWW6C$IsT/߼)4ћZ:O`KjêOy'FؤVPOc}{xV-ͽ1rHJJ ROp}kv3aH!_9_P|;ԗOoK?-dLZq,ЮO=0)|^;_nxvsws4J ;b@2ymCN;$qny ^ӯ.vVi@cf%@ [_]Mh",/"HAb}Y6o,o>Z6UʕzSQmT=F<$F?3Iӌ`VEFIJ3rOOҔgFhc 6A5=sE* oNzFNӧm2ަ16{UmkHu}% MLP1LǟJضmK!o+.pwo&0x$C e`z9aBN@lmfbH@$!۵% o4y0vӧ|M#m=KIU}F%dy#A4Q=A-ywbiA!✟ٿmyL9ݎ՝6C#٦;J9Cb٧ ΙY|cVU0c6ik[EKql=fAUܢm"2'$zǎym$ Ts'Au>4%ݿt. *7%7 $'YPc|H gp]Ē64na$d11$w6a4$z/P $Ҳ}cRo$P^!nTe,H=cTЙ'HlHH\v3w[xnYVw-6HR3X»I76S[FV@l%RJ,$''N? -Wx [{K% f >V-nKK?Olⷔh61qu߳.~:x:U4{GJ|(؜Jfq|+Oķo,l @дQG7anvlѲw;iRT/oGqYXڔmgaBƙ$`0T+=/J<+π>o iV s$65ԯ 4cFBJ"?׋ký=cj-1slBѱ xoMx?-5:ΕsM'1=ͦmky-W t YaحԔXUĪk]|Ou;/a\wJkhmeR UUdKp| Vx?+wޫnYQH~N>RIcW e@ Շ4%ey / xOsMk\}FO>G/3IOlٶ|ke&Tamg\W_?nπc$ޜ| hg۵^3AgڦM.wGݏpgᗌj /2zΥ"X\,t'D6Wt_:e^NJ'.DUԝ.jRI/Ч?ۻn㏃;ks^bR{?̅w|u׵Լ;j/)Gb!"Ts\W䧊`؏_=zWxɢ\Z*Isuhv/%K S4(؆BNI~|~LS>Ҿ+xK,\Cmwx I윥ʚ8nM/M߰ixrjMZ0E[HLj ~֞c_/nh-.Lh F2+_#kĚ/ߊ67Z5}{&MHh\[,ɴa&| 8kω~>\}N2Mqqy E#/&"?Z }VqRNp|%5_^5M xO-w; WV:\]lOp\ s/m׌?k1^emI.D +^wgo?I5)ּ/wC;Du{&㛟)RݤU2A$W> w|5ekE讖iS!%9`ч `kiKXEi)|+km qoco9,nZK0n!r3o? <?lj:mC*)d=iYV"w)F {m}VQy oxCCXrw3ck42 8d2ykWC O .Ffk tyc,IX~>e^u{e_0yXJIJBq!+ko_C|DVMl4ggY!r:5U%)饵=ux[a5hCAlɩ5^oo,1$~;~x^8ƣkl-CI- wAVO$ΥV8>1x_$UpzXLѮH13_3͎X3K~ӿǍ4x^Rk^#3I `Lw#pSR7Ԇ".5|" EG#K(cOqi3opuoǑnpA=0ECumLje5aKWgu0\I#5YA?ƵWGe 2U|VLvf̅!R8o_J^n11[J40@= LQ嶚7$#iQ"0yj &g n n.k/|r:E-Zy t%9W_zM?!'cBkK;x.$Ĭ"\89+\[*nyߧOΦ#VaK"FHcy5ZM*H,Ųu(dyc_4{̉%ss^8)h-%L'B bV$V6wћtڸ?Za-Jcz'=Lf$˨G-ݰA5I\c W jMCkY6RV#-Ʀ6Q CGy_߂˲z0 'FHtn\;GUOGnx_m?đArU}֭iI<sYQxKPT Myfu*3׏Ú}̢]Cq?4cXZ@W 8=jwVKdKv9ZٚPXcT`wz}KOkkUxl0KSWzRͣ6ƍ\iA1`cwKr%qDͨkv$t' sxH7[.V `:qjf?\KGFQX2rv#:omk;x W3`9v-Ծ4_L|@;,nB4.2Gj&ϧ.7mxe]e9<$zԷq L64#HW t#Qs6)Xci2;iJzj69'h_:"2HCgv4LyƳЍm>Kc9~:UdTNwЮ.m5 m?xqӵKEcEBwain9|)B𦍪~\QZ48@ |-':m䗷֍"yA幯u~WӼ)xF죰ֵ[[-uf15 *n#RBsZChPrM,pg ,ό@ֵ eLN|;yuHU Ȇh\'nF zĻ9WoZ/-M[WX`%7@ yg 1Y?:=֭UY\\xEiᇅwڕ.mbk73K9RT$ӔIw7ں?S(~ӟ <EW-t߆t֐LeYǒ0v 8j h~Ju7oͳ?J[7!(n1r ?0*~li|_<~ ?_giXM OP'ğgn~oyyVXhP;n;U~5~|-~hVZn kRZ{i5ǖvHY%L(w^pOxwY_GԾ#^>!_],1j2JgWy+gDBN7Hw4oi1I~&,nky TVgQ˘k9iJһI-~w^Sτ6keG;#L#xVd*x4XkNIGFDbԆFrAϭ}|sÚ?:i:Z_YVNGj!8n5o0?ֵ?m]$3 n떏4jV\ƭ P]H;7m>DQk`xKKVL|q !z.2A(l~$|k[ g x-ڍ9dG2#-ך]#oDgxB]Q|&hI@1 <(b;dxg5?|n%xZtnC-*O&塂$VD;9+_Y㟋V^2伱ipFlY\ZZ??w/Ʃi ?QlQL)Zʠc-Ko{<$brj$Aϻ|L4)/ZƳ,u- .uoHs&#0q]揯xsþM|=Xv_D66Y#"+sʫ3l 6}Jm3TmVk „n#7Ŀ~-񖥫ƞ#u[)%Ej2 hp: 3LQRG6wm>5t0&4Ayq r"#v0+ͧ-ēG ,WrpOҗIh8+; F 'Zѡv|My%5gd* RFѺ?E _'=/Cx_~--t ϵ,[Q$;V1 副~ | rѼEïO-x_&х֮JYY |w-D&1!|.x.+m ;6fU ;e@lZ'OXk WVџ/l "|2h֌S0Wc ;ϥVּkyk9.|I%NH(KTu{oT~'W_1K?GxSS1DRKHΈi-|1>kTl/ kq-\-I`FzW<;ǥNkEOm2!=+QQJ nH%*p:.KoIPȀKjvK툎BǶA9s'┚Uio1d?2Z ;7?jOHx]lIU<\-2!$ -g<:3˶(I=ZG4<$:G J9agp7*:TϦv*denŎaWJ)+|5^W! (K8l}n8>(3y,ǨҲ+9Eכ9Wy#pzfPdW൘ #D׉UIj[i,"̟*=zuO\O7O$/#J^ sC=&M?Ri l/bu p=S8LC+w2ʣ|q.Q_1AXo/Fo%|H7Kz[x-^$o 6蛛' "_@=:bő, +0%F |%xlІ=~86[+O|f[5@|zj協cFroagxT|R-EE]ƷkGaFa9`~Gc~K[fu$ѕ 8*qhcu+};+˵ 3 {gbHVt H R0Vڦf^9YaHާ~.~le/nNr'iB ڔ6ZE+C!0*ǠVln#54A3lB@jΉ5*( 54ځH0Ö^R ثwvhk sgstJu)F+1v{_M>HK$[qĐ*j)t'$%ik0V7Kg$vo6Z*(!ʌ~I,S Mms! ?x? FwOUu`*[a?OukXb[zqM$טﲑrKq+YX=z nk+}S`2$qi5671 i;%)?j|\[*źfb_9mǶyRר Ih`NA9yy0`c'Zri:2JLlp@yf U\#ly>vRj2VcI5K+R71F_8ґ{6i[Wcx4=:Xui)0^~zw7{^"}q] a2Σ{~ȉU/L{U-5{{;RXYI`<܊[yKĂ"iƘ<=_?d/itoYuC34p< 88<->4-[L`O4J,11(;UKI$g\"Os3R-Điծ&YaG2\ K/Z}cKO[Auh40Ĩ%w8.3y&-RKWVuk%cʪ&&9ڨge3U!J#˿o<|7}EkhZT5 ]=1d 2@}=@П ohڛ7GM;DqcqeG 8x[oNwv|mJӼIiZW4y݅F8Lu G/g|CEƗ%xkz5qh5lӸ7H ^r^F2Mu gSǏEoIm6F?l1"dMs1p#`WͿm⯎$-hoIm'OZZTj.0=1Z߄KĚ厡݌ŷ 8iÎzGS/*ӴVw4VFG7yrm\Fry➩c/ū^i&%e"&hABHy >QI95?F?dW.|'Lv޻`n aXԕrX~P+cI{?5uC?ñxbEБ I5O> P]<Ñ>XX'f|+V)~z-ïWxQUa2x!c J 5&I־#~$xB}F +K} ->qcG??_]5>3x\յ DbgHg|El-w~*m~/>w wǙ,6X 1kӿ?_?g6?xWw|{sv6:Oڞ泷Vuӊ1s\i7^~>6)K[,% KWG2$|q^mgLZb(< caPH犓D|+zӫcHbKq2MBM؛hqC_Q<$5m/1ͦb0B%Bvf&~*G4m sʏ޿x?j5x[zeRE=Ét(~6Gcֿahൎ( *%r} s)sA8:MUFQdˈ 5KpQץ49189v4I@tBmu ˚~@)?AYd "u `0=}*Fgkrŷ>@u_AKtCPA:'-,nFL&p392"?qafZdzL[ݏ>"71+\v`_"`lc#<_`ޯm~[ CXTȷjJ/D7敓~ 1P^]r4˿6 x*ybUZq>XLϷn?Z?w~Ŀ > |4M/}KԼs$[vvpnfqAZ7k+=~VrF\)H_Wr9ȯ?c _WǍboC^=-d:meH-d&,U#GuiVm@gZ%+C1QG /^᳌9A֤qiBn@mÍ/vsJqמ+9X Z}HNZFiOLUτ'fX_j( p/:[(WQB!1.XN9og 2]e#HcLu5dTѴ2 B:9"^C 4C}9"=ѴgK4Go\/K9Ǿ)|?2['12IJnY"{dx%o'xG.͜TuI`r0BGмq*pEg1;A@`c)$jAfC+1_L԰GYP &={{U+8/n,)g N0ӽKee^I, uUڀ'IKs"BfĊAn~5%Ƨ ZJ|s~T8jzDc7I9ǯzkay\d$iv_7Ȯ!@Q89lU@dQnI*=µKKm6=\[R[i,YJrw>KDq۷Iogf%eV;3r2 NѴMC5vKd^z}{U.領O.ɅԞ gucp)Jg/4 4~aPrI=-lNzLf2 2۽:JtFF<8:7nx]M+ib\j!UAPx\jK{Ӕ[KfYn$?sC Mt=GP> l0r2q_6z c['6$1sqn9\ #hEss=,PKaxBSjh*i>:dK), $yYor &$lJI>xJTȟf?UvJ B@<;y-ëG}QṼko!ivC#Lz?h?GھӚV-K{JH_ &1sӥm߉>x:7i4äq ii,XD[ 8|E*}7IE>񞩦Nְ#0 0%K t3Y>3^k 닝1 灌/nܒv|SW2ơ=oU|;=w.aX FL~`BjUw>"xa67wږw/}y$KDYA g3IvbO7nAj:U5 3C47I̻P]wPt++Lxq0KxH":n{eo^FVI7a!?nZO/exzWK'rp3F߶7Q/Vwߴ~g>Kv@d[3 E 7#K3n1dQ{b%Nf'"|YƏ#xAτu*Pԯ;DuN Vi\s_ [xM%ӼS0E˜(5)a$lˏğO'EQAVo_Z]_I]8 TY91Fa~iC4!$j3˝MGU*$bՌ%R[Fu~ oh4Zxjo}JmmhuUNc3V0ۖ6۷ZZ_| ƺDw6ȊOV֖,&3#9~>i:<_{VkyR{+覷.^K/.gKOd'(|VOV?f>Li 4̩%;&e;t o<-Mew06FpZQs@Yȯώ_~ğ > Ai6xKillch AN$?go?/짢xnyoBTKWV m]lJpVO[?<s=QaCJz~i3GpSq֭r=l8Cގ'emBѿkXx㾫ZYXRLIa %@ v.KW|x 4"|5[-#_;h6I`A5~:_wXe=*~}]RklBX|p?Vwk6CXIfL̲M&HdH]ШJ8|H5i?=CĚUJ5[I$*q!$WQ=<ß&Ƿa%K̶4eȜj  77߁~$jMkVҮu,%-,F\h&\Xx}CO} _cOf u~+jCĵVEX# N+߿߲Ox_k^3>XXE[X(U =M~oWooH>vG>2Ɋ}6jmxWž!C++kjvv:x cDFPU ;mT7B'H:v\c$'׀L_پf5!ia^!b[=%sb[Jܩi׆|u2/+|c;( H%Wtn ,2yI4&o`T9Dw҈n<%ǰ& :i~&xJjpiu62&idPAUfusWӊdώ_?T/|+|J̓O(Ī(jD ɱFN}=F?>XpE\>KU#h5ʙegu%Fcbݎ{ұMJasysn#.4T׵1t)Iiun@fVˍNQi-i|?JŽ7"ۅ5vny$vy5n̪Ť@r= #M2&K4ɺYZHك3&p*:wfDEN[z[Mm+Yki2:'um^3p vY%(|uz5+-Gᶗ>j:ЮLtNK5§sXr[1h-+᷆4hAx\þXbpUdX8&VHs}E#b]CXַ^յ; nd+G? 9V5 +y?Z[im[x59D͹UUb pk/j |/?3V=և}6+V{XH^]A%h,*t&%^߅ߵ_׵]kg>hF7?]4gRk;|үl-,+\0vgk^)~/x3QռyN&:P$M2wJp0#󯁿< ?Yu~6ޅer7Qa<+4̓.o#95.urah|ϥ?gմ?Y|Eo{bTM2*ms+hN᎕~ȟs|$|[V_37piAۣtBOmm+u/Z8?ًB}T5xN2u.Z+'?wV?'ycǑZ5Cp.l?,w^TPGĆo`¼WW&_s_gmC65ffO*g8g   2#s ˤiWM&UЬ4&Ԉ Y. ~KArFzӜ٢<KJt hj(Rִ-[aWSI 0s'|co@kOAu=Wԭ!2̑’) p8T~zΙx>Jۼ:0_~e$ƭ/ǟ}RѴ[zT ϗN'>6j~nh=4rZLsUx>ݸ 8ϯ?뗺\7nOo}u468k|[w?xg^W   ryJqQ5ymfEh$2=wźjzF rʋ#bHxQAa͜W:3%.A?02*=k/8kq㎯\^Ki&\Dlѕܩ,t+^:+(r_Q>1𕧀K tUq-TA)*@V 8&u/G =$^Vl_pmĦx ؃ďڛ?WZ/!kK0ژf&v-c$~;fcMJ'.g^53fgfYV a =~?OH>| kmÞݨB\NO+զl%I>JG9s=/(f~fY B̭lTyj:go(u{o{Sծ..xwۤKpvA1&M S_~ڦW|J HmQm||zYA,ijmQo?L?okƟ>/Pf5رNI'xq=9[F89`$<4'eaUW9 4Leeu/Ƞ$:2Pi"&xZ^}v]JGJy'\V4f9Bd2dcv֡mhҡ[^]k|hl-_#G4c^̷ws17 4-1g* i͛|R8,Qzn;=Gu,qò׊|o-7VQKHԵصMދ-0Snܱcנ|Y-?fgo:@Qk_Ŀ⎕Pɪd3!E)zw'T_<eoO(PA$`)k[g ]baWuDUYV+iijC|̰c櫄yͩQգ?f¯kK}3QIaW`@QTqM~<3<;OuʙH^(R$FE|{ ~%?>,izT1h{& $q%HNZ^' <)E>'K&}Ms~%;]) ླྀܹ4xy/)8n$WB_ > ϥjϫA5ΙqE 2- +k\-`|SmOQ_MӼ!f$HMPIʝ, F\ZV߅g]2[Y_>; :ANO0H##DLHZ9{ZVGпVX׉|Lk~+U)&YBj=\j'xƷV?2f-`KSPUS+ uw5c~1 8^?~M kK)kinD$\|x#ޅ ƞ,!&k{}t\RBnBeF }?hφ l7v:XÐEnB˰}IdPЮ:ƿj cc3@.kzZkZ%ɹY%26Jh/m͢Z~?Ft]GWQjv;- _,u>d''KTf-/>s>xLDL>԰,D*)u )IZERS K 𮿤I&kg^4:t #CoHr]ëMďi&ӵ/#4o iCOܻy2ĨrqT<b߄ A^Vmh:o/.1ku-l(1]UڗOŏ!Oλ]W4:mR^p8 9Rź󓋋ՙ%QY#м!R+~/ū_mVdܫ$c=+SΛc1_h.;m"⮞'?,JK;aaMRO +NZzxfƶl0T ;Ӟcuw#W nKӯi-Whddo >T:@x5*ZDܜ/+?gψ5]G(k%/ldG#BK %N+S㮟ac{]CFl{ibaK'RMP2ۂ{փ|;O)xƲwSj3SZ֮Ld/|y>a5Tm<{ǐh:~ /AiGեY^uR591`dnTiv>?m?۳w|J`KX)6ݭݒ[ͬqۀ˳b8,{fP')?iٺY~gt:KyY36"6;w/ vcT{5v-5qopȟm|eϋ#O]EeY[pEtRnʫ5G+VH`;n/ݯ~~Iм-;iR+(D+O2Wč ^Uெ?lh 5? q׉|Cjc2"n3|O+,go<pCiVR[is%eDrq}j>n~mB:i\Op(*E9 |{ԪԫJ\3[_5/uk0,wVWRAqkmoOP(2+ٽXk2h-RNdlRY$@j,"b^C g? 61]G\mu60rVUaQ5b'W=ITVW7^TًsYİ ++{Xmg #2Tc߃_|'Ad>3j3k2ʬWѬ7*ƆUOB3יZƯ+R"o@n'w0Z.d1˺O|C^Ox8 Mf֭}`o">mE e[y5ܨ0uW4Qo~>f|@Ju+5JmXb$7܎k?~? :4Fӣcf+\\tY9> +6;G"fGȈncn`&m6Y/7<}W,שF kl@ QrɻtiEl``iINn?3{NgTtZ6𽕻gm4crܒ1~tXk-?/a2 @2ڹ Vg7p66К~? b8cI6șIZDfmtjwK#Ƕ*晩GyvamÁw#dsZF+iMr\#Ghfsje:wQZ*a3$|5"\ 0% #ǨD 8UrX/o0"k[cL42~K7q]o??SB杯e\WM4!ec$6%2MsҮU`(ZD7SO=տটş)xzټMa4Dq{xO1dFe&M% 3YX^t1O-o.Y^K˥nj#4dcԐF1\tIL\i7:jwV 7XYH.f&6]x5>Tj,$wF;ωG֮|?iH-+]ZY,cF ןV5w]* 8I}M?kxzwMKHagĂ#+V4)@{ 1ʏW~3Xχ ѦL/#0Hܡ2kqQ iQ&+$KŸ|cO|M2XZi妒BF=[#2{Ws:}ױ ȣCݸ;W#~eXә|1OSkA`~B>WԸm9ahd_}j~3. ~4Ӗ>~ iMVNR7I+[&bMđ6f*[?xOƚ^ B8aaɽ>eFU!}DowleJIs@o+{9.NNvWrbfw{[Mn<k5{tuդvl:AXZaOԶ xZ{˫ՊBnu!o3!"DY`xW%G?5+_4{PxwlV(pb,}Ѧz-^˷?5#4)?wm.&KKk;)ɼ)<9t? oZ| e k%juqpwY`,r o'68<1bmA`[yr;qH&w/k__$|.^>j>dЍPȌGSo}׍j0j^![T˷'ϖrbwZSI%Ӕ'Bk};_ůZq&3ܴg-kQ7\WIqx->}Ѽ7xZ>ԵXCmsq6dȠ$wul2+ό|zyn߇|9/۬B k㳈鶌h[$#/;zm2_k}uyt=N`s;,4#c`uyTen;_Wu 3k^3Eg6%.*c Q̑v5_x u_>3s WO' 1.-"8dQ@ɐ_'Кş.K|;ӭ!VRQffSʸk<~^?.%X9=omxLKmVF QJ YJU|qGį[^|! KRK/T#>U !#;ݟGƽ+;+ZŽ1\Ghm Z[#BI /l?֓k#?Ԧ#[Аdޒ _O/M<_ѾKH40_\ƪ$p*jiJSI~O?'oxkׅUݴP3,%pc5?']!߲֟ Oѭ[d7ldk]H}͓ *Î+{úv~(mg~ 'ĿS-lR B b'M|xot/}|6мmC["];F#{HN|Vĸぎis&}o={C o <^xZm}<)yܼbF d3 ,DkbxO.~ܿsI=ڎ~$I [ cFpհ8W]O  x_kQAo&d[Xd. #9k c(?ߊ_Zh5 M# =E$w0!?rsZͶMuZ?!K>ZD:Nx\5ռFrf0;ÀyU?ji>ӵ:ᄨn.͑di 9)NW'׼)WũiVW# 6b_0A@F xW? W$0m*;["6w|W%!B.<MٿRkMA ۭΣos%M-(bʦbU둜W|Z=5sSi=λ>hi2 khن$:wZs@g _G-rD[K[tWyJx_fş _ _x^ӧZJ-m7G$c!AP ѫVKM-jDo%_d/fjw{GAu\ZiB \KrxWƝcx:~Wx> ? [slo%MX8ʒO9C=O:+{x仝c"+ 77Q&@jS[yLG8cs DŽeUq&1+ΒCm|FU[~yJp}Wh<-Q2LID{Ns }&@c8fL/ m'|9\[<[xc?JÝImV9 mց''mw+y!F%0zTҬC,p۩6An۶zcWakccdŦء#tLVG).nzq}ٌ4MgVmTg֝=֝ZbTp Wy;ɦY泬Y[Mu !Taq#;\l"ҼMnt䶒0'qHZ=-^p.ew?"  t; VatmfG bHE|`z[E[_k04$ʪU'h杘vW&ЈIHJuDž縏ƣ21 +}D 8u 5R+/F?֟P?>prcI9wgOR'W¶goR]ik7~ > ~1WbcߏR-K^_%x~ZN,o S?8&YIQ_[m{Z#׵k}GHs)dGqo?P Î)҃Hބu;_/wj+6u/ [jWR;.?xx9\_5-iut^6m:T]O:eQdb`2r+Oy|NGJU2wZ<%)l#K0ϧ$`/~ #o65}|@l֑iq2{+slN*yO+ۤ|Q^WyU t|7m5vӴYƙXcV#}9oHIKj5] z^?MQ֧](kUX1ϒA_?aoq់)yoCkdou!2wٜy[@^:|uⶥ}/"[[Y4jʑ'n/hizE6i޴ck8;\)\[J\'u_ ބVZL/$ Q x(¶I5/~f/mm_C#KjoʨZZ<f7;" ׋j&ռhx]i6:e̥kxB+K \#85T^VZiUIpI,A% s6w0u$}/e佹< /-tk8-5ud)9j~Բo_ƒ194 3FKJQ!HGEBK 0|U5-^G߉WZ—73\xdm/m6Q@ 8UvZ|[Qa^ c[д+}Kƺun,"^b+X^dy/2>V#*>ɢxU--ցR-ͪn) YF@M|i5|!eA~?<]Hj1GlA4I'eUѴOg>5oImz[^dk2_^Emll1; yw>&xVϥxu=(}sofbV+ 6sRN/3p |E?4KqKFnR+!O},F85aL #TS]\_:ڝ?NsW!JU#U>&DŽh^Q]^\H-dn3_/|y'K/Mao;;E!ޥ[+fy7ïxŶ>{uo?߾w̍64JWDnNۆE}C0|wz;-Ū2^j kuB8%S54:PibDx4rHJH` $WGZ1|%[\yaԤ3Uܵ(ӎ&kZ7|<🄼I Ne>CGUN1^1⯅A+x\A8 Jycڲ<^ZG4[40%eP?, WGz8~mNeovGDuڟ"U~bV#ErF>"&{&{ohIy%Ks$c OWG29X(o zígsoq'Bdc=HT6/wN,ʟt |9~ BImΟ֥;g;=;x?s>s=Oך<(YHد~ ?h?"Jmˁ_x6Ιi{WD1>NehwgWQI\mɥz >^+̧֖Om- YH"3dUem|&+>Z>cࣿ?g= _ƫ[յ-[ΎUDw3dk/s-8[[?S#EW_ꖟ ~s+Xqy}:u[3l|o_/k7>)&𔁊N@/3QaK9Go ί& F Q a׵ǎXվ(k z /ű||)wosTr&5%?T YI<=_{&[_5y6PpS7Zaޟlw}X]ڬXܭDJ,,`}ױk/ ~ uX jnƗ!ӠE9J`a_Q?8+dy7x>#n_STmm>deIC$2䑷)֡n[soލKR:QPFЦD^'"x{W 7+]׼-moUL|ʲu q__~-i? 4jIJGV#0pp ]'ǭZfkexcW@y@3nOXUȚW׍/x+ 1H׬^[h:g<k->/i=w-vk#ĻVf+d|Gwmkſocgg-D7CCV2̂c 9ۯ<[\krxtĐD(LJ?xDL3H}M|+^?xc7ċh:u4RM:YJUy5Uu59THNfk2ï:_|s /k> aa.lɪj/ʒ @|; wq_^Sbz;|B^"s/졐}Tp8k$S8=+Ϊܥzg㠒%uJޚS|zq֖2NrvC':T޽@Yroj||ӽVX w) wqsHl'2; G4ßZ\K띰Gk"(99#{g?dq7K6-ގO'yGďڳ "_KN}R i|ae#fT}V`G=-MIu=3o9i5A,"V!y@zR]@{cA~f {֤s5݌HMioefQ3OSЖѕ]dDx88Ɔ`k|?u-6? Ri-E՝դWJn<:!M|U[(gl/1z,򋋥R-,8}oKxfh^jk(\9ڷT`_ytɿ&,K;b.4-HirPU(ބ_Sx*Fе}{(o$H$9| ~|k Y5Hfm-N)Hy (1 gq㎿xq-῎k*@}HTۜY j_ >"-jzTq-ԬL0G#>BvNYs.uI+ݿN/  mEy,ʶ'S<8%y_,!h Rռ +D;2I$jyCdSg@U=T}Mw݁@s" |ox> 6%tOu:Lq1sʮUBhRmr\,_6Gl} ]$id2 n_4k~$ּY4c&ڇDy|${:kw|3j:޷+<Fm0=N+#˴g;{{o/1BbqX0f'$Η6+Ksd"-%Їz+p mLuwbh:i .v \3slh~Ѽ^aۍV^xm/2ɒTu_|AxB?d΢4Ҝ|fZ1u{q3K>{"A|T=ku}'V4 uf-k&(vy5p*.ڙ7 xrfr|- *$xƩw>_]ڝ@D)dld?dٯºvu[^iZ?aWfa8BZ5mc9|#NJ~ʺ7;ezZY`4Ywl"@[s?v+ş~!av>Njm?^!:PALy|[0mJqw.YM'}O+? OʮDwQw@ 'g~GNCku DiZV$6gl3JR2킣וEM3u<#}&Ckg$PA< BV2~Y>Ig~- 2t|y/$K/c.1dEi؊ORb{&$=.?gtgFV(KRVki2\kaHc7r^4k%"uR' '־e>vGQ׾$|_5M3H=.HլY#=-Cwk䟆>/WkkƊŞ(4q;;u<ǙAtI{6I$oNS_[x/INxT^숇9$Z `kYv0D&q:MǾ"_WZQm- AE&6'}YO3ioim-dKr#C )EwuF$/^';]zKAZeI/k+υ:F-͔Zn=ֲxlKp]Bp>4]r^(ž3Ejsy$ ;:I{u1mıV^+C Kh|#׾$AFχ!unD6Li"~w~Ae=Cagoַ>.tK[fx(U- $ *e'N:>'jIg~yпaF|n?j7w&(*p6+BWnNC+/ U9|O-g@Wt(uK!{n 2Q3ȯ+I^Khт0CVsJJ؛w`1K*w)eޚ fh \;RKXAR(Y6Tu,(drrif$u^]v9v)9[ E ! pqJP;R+\GS?aLFү5,#c9n% ISRIqI&0wwr1])Jm׌ 'k>s++Q{^]oMpYwj^69ɫZêiWvie qΫc}l*E ?:W4]^LE"|\6gZ֠B˶l%`pNA=3YqW$VZp{ Kі)fo0"n'ioirOye{ur^ s_hiϽcZχ|Gi7 Z0d5_ꑶR+u Q#np+[MЌ>|xY"21~7 Kj׏5',^3:FksYDe2X{y>"OVm'Q$jH^-ېn1Y{TI>4M:nd{ׇW:_ȺfC+;̶F>Ţòi6pAukp$ع`{%@:L-s{^Ks+HwRpJi?xKo |BԼ=-B;U\ov`lq\u_>R~<^9 R|)giVly2|ϛ\t9^?^1ֶ8Zd\Q7q«}O#nv}# ~Ѵeiw+Į³9[H~Y|w^?56IG]HNY"9h?x17Kxķ/j6A宯8Y$c<x戫$c5'-<Eo{۹o?{{q4hLɇ<a_|Rߊ4~Fo[֐,.Ean;]|[/k&' [<4nXuā>4#uz= o#q=aUkד _ k ׈9쥒PyJ }k_? A3Y*N,a_ņث@n+|9iiPCaLkV<1Oʎo#Zx^}VE"3F1 t GXbwl"$n_/6h"xH>xQNy쬉 i [b8F :N\e5sþ3:l~]xRMêEq){;ۋv\q%v\tڳx|\ixBuvjwR=DOά|2Xm7WzGlRsuYb?/n`=<ڜ[nI ](Hzn\+\՛g MDV3v|X wmi-a,e@Ya)#G>Zs˃yoxFbR[6-B˰$izGm%&X"$2tS; bt' xWR>{WV L!( e\m2.Kvi> HAxcM\Xl^[Bb?waQVtWoG5XPIJ4vNhB1DMޅƵkeiKȖʬ7,r8|B񟅏#Ҭtӡ\MgymVO-xr@8_ 2jqMitY1yShI9'? C> Ծ;;޷3:L:W($ږKq38^k~#;<7]" &rIؘqpxeœ' vGxsPo/>֋<(Oy"gY1.s9݌Wh_?eFMRJyy{{. w5]b ][9VMFvV/9K[ j6"x/<[@x[0𵟍Edi/h%8%o((s_b~˾$h/T4:aLMM6&]8'ڗO#(8,9_M?j~; e@XWL?74 zRōJkeM%[#3pHAQrz39xcĶ:~coK;*&p57U_'SoWvs5Pi[2wi@BH^\s.swp>Q 2yFM|6,ZNi.!Bh`soxkǟ|S/J'mp'-Q"Fg_0rp\ghдKVwzVu-i6E,2ynNj]KYK{|7DHyn,HFwZ@dknMNww*6xK:?i{_׊$.T!Br8 ޸[Zb/o:%|ykڅĢ:nH4`zuw+ jz[xpL:ՓZױ:#`WixGzxvmn. xA,dv&;sS?{j%)9{;a !Ě^/oEfwf(S 韘URM, 3wQHO9׼c{T ks :K\{hWo59Z=G6J߳TGƞ0%5u&2#$oHH:,jGj/^|t?;Y,⽼mN`w[;F$Cc(W:Ϳ 3kmfTcs[[kK+W3+\I"w\2?.~/\r[q<+y%h?\F~1wZ>YGg{OO+7_J=߁|A?4kĚuي[LfFD l @ dkOl4RfW*(2~oW !KT<v~$&e#ۻjsvDkS⻟'fkx?w$+_MHZ>M"JgmGžCnc5 Z[zg27׍{D:kmm[[[Zi\/,y}} > >&jZxn`RCEp[Gk|Io goIMt$s OP,R+9]y^[owsҬtkiO,[; tGe]u${5[YIE$4#1^Ǯς) >=\)C6ѝϷ9~|:x[E?jIL ַ\3E=<sCׅ׿iNs3\]ys4uD1mr>\|@WSlvԷ҈LcA I"o/O=wo{gkAl-p&AYHRw}ϑto:u{H|QKw;ַ%fwcyNJ3~o +O%sqya X.K xD#n1_ xo4vxR]5x+xl Ǝ$n\F_IPMܡch _Y.91kpo0LH+]$z_[nu(2.0<#i7>f;}m/C\0DэX# 0?i ߴg{ĺ+ \CiaN9վ2i/7Ɵ=s;e[H9iRPIy?*vʯQ_2\]^4K+Nei6NI$Z!ܼp(DRYTzRi^\#hPIܪgFjih^b.W'HȪ!=Kq#zܹ' ؘ0*2213L K98+G>#a qHp\=k!a<7R|g¾ ]7H^Uq>ak1b;+O͕#?Z@ҳI9cnO~N2 =*Ng_/_WQg6O4;[;Fi*so?cĒ,VO|.u]Mw;[/xxamH] ⯆>.k 1d=Y?_Ϫ-2;Ζ* ,Q@<ָ`GX%T,y$S߳ +RR]G]ná GŗͥYLP 됤})BZhWxºį[𶝨Y=<^1 Dtr;xR^_L6zv:ZǬx32!ڣi}sU߰j>o!W[kDvcj"y#4XeG;О~~LdGyK|JNJPѿ{3K9+et}QO#8ڼ\/OZXn oWN2vtbFOz??1.֗Zya[M)`J\ې$L|4,>w&$ h. :6Sni|X:ƫjM7OnFim<ȈX쉘p}S^"G_ nu;jgaaY m68$5i9~ks|:ߌ/.Ń̃vA;g+n;Cڬx++9;B /,xX&ӚE#{(.wvP>LX]?#|ofq3xk4=.RQ| 3Bfm޺cTjMKSି~ ct6~_|.d׵1 R\^FV8> gԍlT7ĭn&-2+x5 I&iByh]`p副ӟROƑ M.Cc ۛ2XH Iff=k,~W#]Gs屽t_#0.D9 =Jn;HLJ*?;?>&Scs ̱;(Whrm⾫p_?Vl߽ͪ; d8T5D>25亴Xn~txGm(??#ר~߰?a=]IMnމ kM;VBRuZ ,]P+qhqhWHͯ?h~ O֟5vsޮ12#o׆Sm=oWc =?~ ɣ|TWR6bpT1*zZgmSZn-V5'6αW.%g988_~(xj.c_Y5"ykX8YwAT$k!R5~ֱ}Jnm P °HϽ}/S]f?ؿw}Q]'t/GxSNE5}9Rh{Äsa'9 tYF ?x+^ 56m),O$*XU*$q.5h^+'ĭOV}Q\w,,~jAY=)|('Cy]:\4]>Bs+d9k ~7CCuZ}E&_Q) iƍ9h@plyOۓEƿwp)_3U6{{{++&(9 =_|e\O>Rݦ #d rnS?(_=?_Do5|SHcea`M4J WqEkw⟏ھeíȖWK%Ҵ8-ah*TcԚ_WD{Bg^a+P:ueFerEAvƾ˜?f+-Ԛgt&<֦ lwAM>a:^?~k>_΋ 6v>KK6;Dy n[rF]_ൟuS>"ƵF4hq 2Em\qaj!Z,Һ!>W+;%X&X-/EfX[\HIdK’:տv~ ϫ\i=Iy2+h cp+gۛo[|DNxjHgLyt*˃N+VUf8m#|\O#Cu.t#LWP6Kj$+fV"Mjuo!Iۧ'oWVc񇊵&mW^cJK?GŪop!b6B"$0oڎ~3]X֭=V u+i%-q$H-!Sj/k$R #^eo#..kf`HFb€887ݼR{>6jf=$W' \2e,y)XwOsˁ֩)(nSj[ڈWsMqx[֞]îַrH?x@a|L7>&Ѫ[IV-t˵©rO'5k޷,"E$ w FhKN'N[u[ yϜJH) MҔGmm 1uefc8/z3^?ӂMA!0t)˹9vv&Xq5B|0ǃ~j:0R-y)I$i$2<.[Jp?3WڽׇuZ 5)HlhO1kh:mS #QW+$_4=|מxO|u kV Hb;U@11L>4'R>3E^Zѻe6PG 9kq|3$G~/'r/ld{8ظ- nddGEzGï< Kgc'55s~uٳh̕*I`G$/kտl|7ҾxZݾc%Dqqystp 6>`66'7ßG^SP5\h6VW+6H9kY{|6u /uKltn5(PllW'OIPh6ٝ=KF Q[)>$^2bvewkƾ!xoV+ U \ƍKYt1,Zd: W/*ę@'2zV|MwT ԧޡ-$yу4#=ڵXGkNC7"0Ww7ϥ\]Ev: Pgne-~\s5ږO֬tȬ2&r2\K ~m2۷B.4xFg;x^8jχZtWǭwW|5e_Ϩè!i/ Ȳ`Q05\Տ]B+ u$tz`1,_0Nz <;Kw<>[ME^,M+W1wmzs^1l|5;ψ B=6+9'F4Q!,9 j8mNG/?hOƛ? SĺugKĠ\Ui+Iϡ>ֆ5/,F5AI '!0/u/ >#1ir5-;nveVP zMߎ߅z:M kM&tՖĬirҴT+$ާ~|bswG_^;ZvZnM4d\L8~:RᧅdEݿl O2'ŃMs_)KBoTFmD!-2!yN=ÖWm`ft(]p2ӃZ&mM˟O^+66w+ii*Gecǘ psn:Wjv_ xA6/'6f.SUH2n'kt} V%,;GWEtm,2ύ(мi|Gg"|%d ] ^`-۩#eݹM&R8v~T~(~ҿ5G6=Zo:Yg =XzG|α;"pna c~xE75Q|rUoIy*765c<ĺͫxm1haל֒F?g[[x>\7&D1K'h9#OO^1wD"~ q?~Ouկ)|@ m3kt1jl!)pAC| DƟ/}$L31V%*1rO;(V`&MֵM*[KQI"Flq W}-n呢p222A_߶'/s6[x?BYC^$j2B@1Q~_h#վxEzTx*@l0]c̻UlO&Ѓw54~@KumF p[ sO>0ã}zWxg u5G>%M4k(h2fCώ^%ž ӄr3 C9D"6ICR?r?h.";:7:24bxb2@G_qO{/j_a,ڝŚ:)*ϟ/*GJc.Dnwz@&9vZz3~ӿ~#o|5Þ-|l]6֡,9{7Q?hK)mM"( 13M? '*%֬gdRy'?Wt?߀NŬkӮnRAS6qֿG&` o~⣣wuvq1,g`I?u.NZ4j>E]VY-mV Z,rnQ%X3/mM쭾gqK1҇Ҽ!AbypI/R= }#/%tOo2 mk7zH s;6_C& De3>Ck8c-$PYjLivV\:vV:˕.ex"FsEXilĸ1ࢁp {q[221H ~qs:RVD#fܮu>Ec/*^-:8 Ax5k#w2*N DU9$ |3wq+Mt,-l&IA!k7}5bCls 5}kCľ$u7!(%U]9gZsHl}goį%VKi^qkn"VPJ{8qҷ%?<O^:'uMGYy,d~evGorACjmCh: ?J8MF DXq2/4!f5i亀+s+.p?.{}ik8/|4>%[,Klv gֆ3>*<-> 8tYedUHHd (`G" #]§‰~cRI!ѩ!ʂ]sk7J;qz<=^|[9IGTxo#5Y(O_/٫E&EE#jߺs,sŕSڡTdxÛ`)5׵cD_}O]41`g2v-B, Zfod1MjvF5SC05QR+[m#랫iڏ.?z!6beI,[A5-m^#mFqr냜Wi]u.Xjv"-,h98>;K릗#%V$֍$z:[xWgt%K8i|rqʾfO> < ~.ZKOj>b'Uh(X ڽGoTƯ|fo$' \#$-[k6w$ $R  xcXuWUbls'AA#oIMlJШ (KzuaYCiڴֶ@#6G"Ls]@XtV 'Ev8!'?эO;׼X**U!|AD7!~#º7t7VE'etXʲ'<;IwSKk^η$ńSxTp {.?oO-1BЫɽG:W 쳡K 1bM:S(!2W!䞘@Wg CJ?OFԓAk=}.+GE̵%J_Ȯ0[;j~ SWdPcG\[mnYH F݁9SzWu{}7ZmlbN;5Кy$%}Ы} jܶO U$ʎyg|*c6vW:_xQ5Nb{G{!÷ZGtj"YuY(bPIYaޫ~(/FG]G?%z,Z{ut:o,P\ѤJH$q1ɧ.ZGu-bɴɵ;+ PL pGz3#\Ok|u#?넟)H@Q!u YYjQC=Qu,2d(z2yZx{AP޳l,Iw3rZܽ5?t-y?뵷+IIZ:-civUeʩTCc Rm.4xO2FѢg;g99=*=Cץ5F_rHvG}^MV6B]^!Ev,x^z$1RCb~]v_>xNb?K };G]1E)NK:M3b0^kI @m Sk<o>CnіRY@1;sJ|)&_ֱ|Y"7ҟ&ӕXt,-eI^\YvsWh9\]gwzw=g87eI3"Oz++'J땷 RDs5+ŭ}.xFf-o9Hjkz([-Osx%xc2f10ݻ>@Z]˟B(+_/:?Wl9G ĉqW|K]&?._Wh6^jYg@jzͪ^CT^Oe_rɎ[♴k8wmՏz(>4ֳ-ץ;*ui%ˠF&7ҟx_X?gTiC7zψ=f(G5[GZlOkg]寅W;/1ߪ:^;9΃a+C'qZ迵5.$͛?qrڗCz-tohC7:-{^7^WRGޣ_aT6V7-a/:h~Y+MSP~"HaY*K7<ֽohq^]?ivCg֙x3ĶDrFj%K?i'1ϛ/?IyUhp;xixF’$ ;'Ҽڣ/Mao|IRk9~ʮv/!I+g6<>0;~Flma4Vuj_cH | >%0͊yIgZ-od߻Uj$-|Ua'4(c*kk,m~_j/y eltoƟxP6W/_(~Zϊ'Ggt{ɴi_a vdT.kךny%漼u?o?i-w_o4iRZ57TKϟZ\\5g Z,~}%C⣇ZT:^?fuj ͊Jm=7R+mf_'My#4Sy7%v:΃uxKjL ʋ2oI,; {GyRK,}M:?-䊀 ;HἻ|,E?(.HlSsRIy<:ƿ%r<9CIkɭgyT  Ǒ '@]XEE_s4 6?6WWQe7RRI//̖/ulQ|.<$#/<񖃣E3̓mmxw]$ i';TarI/x-ˮiORsq/5ůh_7lxs-Ѣ55/gPgwB:qb;X㹯;/ b{:hW9-!#$>-~6~"?c, ߴg,3M?yh^O_[~?u)|/`Ԧ/UayJѰSfcWźα>m2;UT47^VIW@4h 6o| ѥo,u\tW~e]:O54_kcZ]_$˵uWRYco B-vxVok\թ*ge_Jo:>uĿʺ K5b#˧1]Ih:e</*X(X*|Gy.4eMVκʖ rסh:;T*./u,ZY΀=/, Po\KF=6h~I?嬕h^9KH!tFѴmKZHy]ޗk _پe|>\7l W^tytWT/J >0}!o$9wf?l5KKcU-(~*ԌG~+ 3F7H~y9OZ~x- >yW/㴚 3A$}BFMZxOPH-d̲xo?ilu//zו=gV[?\G}$_)hž/:΍R.)bzm}Ǒ?K_*~߶? Լ|||yIy_ZſP'ƛMbAi_VKÝO'<2{7Û??t}6k6}Ե=osVG|gMhno&)+?ԦdqzW?7*;__~xŷngsyqE@-eY*"LrO~Uj]/RuG9W7PXIkG@tobmȎlUGTdRG ߼j4kȵI\~ֻxsMmcԯ>OXcRz֗bdmu4Q^?7kPyw$ֻZC%Gyvu {o4٤@/:tScR̯?|eEuK]tktk@Ug)^GLȏYYZI?!8?{_yikf/̆Ϟ9[ c#$="R-6[ McR-u_u(!W?k>MQ<]y cE,RfP\/Ѽ 5+om~]Z5~}3c'΢fy/+=(~,lv^-s^ilxsG5δn[ د>hna$$ʽK<767Yo]­g@6gtwioie׬KQMKM~9k߀^?"Og,y&CcoHo ^(mM|ξ-漵H?ᇀ|9,2.AJCŷvvp;?w@7 WY:7g^W=6I+ß-gWg^2MO>8NJK}ii Z0(߂>_NĺU6>Wd!|W⮥xVweԞlq:iHu?=𷅵{I$[%_O>$E-~~|UmWjB\WKk1I4??@^h߹wRBvSF]6Di-gxy'2"k]IYO FZ$>w<7Tk'_oXIggy׭-6Af9?ykytb/:HO^m[6.igyWyG A/೎ho jng QxÓjk:gMkEO狾_p &?$\ν+Z WQR#u.G~)kYMI*4|.XxGd k@x[nίqs6uix| #>kA%Ԓ]I,,nmY+]ߊůTïjOqzPϟ}C㖹i'Y+oA?~Ξ7/xr_\MxGAdsdTWYl[a7YVut ƆR-O6$^yOxRT}6TJf.핁M&\DeR~ep>#^iʰ͎_]WZŭguhܺ/&kj5 Y}8~xAߒ/נx7ųx_}O_;i~#Kh|$u_ |G Yzדc,sO7R׮g4~<[HlZV"Դ ?I= KKƥǨ%?/4,O'Ҿ*}4i%o]}$Z~g6$/?駗]5{&9zPX֗ß xM5Z"^}/ok8JyVq84 .FG XXI5土ju%lKTm|f'Wwui17K-.=KRI?ykX/ |L& ů=+G1jWZ |UoTB/YWMg^Լ. %U^߈:u(o>Kkpgδ/,6Qi^H/:Ζ4~}nxK޴}o%x/Cw}k%/e>7|*>G?zWZR 2Gr_ tfOW~/y&韕3Uɦk>U_֧>>V 5}_*@$WwyrI/~~^_kIq$/g }|Kg2dBc7_oM~,vW~uQϥ~s^O4 ;"m]Mw %Ie@Y٧ׅi|1$R,~e~Ğ$ҼG>4G?'?~nm/-jW|U_qmk^uyI'G^uz /3˷r-cT| -dk>=m64qILm5<0?gZ󯈿22dK'-|k'h "+Τ4gTiD$C-`I/LaYyˮ~=gMS%Ȩ?->ޞpW-zMIyg%hTѯgZ>&_lU>T#=AsukCַ?ږ_Ҽ[7׭lW]4Z }yTKu/\ƥ' Կ׆OZj)7,Kz^k{MK;'?s,9woVτit*Rb6zK,\ώ[Q{ÓtVɴ#Sy~uWʷ/ivM4r:|_ҵMcY"o#jxJ^ҵzi<~zel6<~5l?~~?+C/|ykI^jb_'Z%R˯~1x  XWAu?!޾xtV2~^ew^7,$)(_·L*c>TlXZE-?]1i e׈.RJj~Ai=*ڥ7SToqPS$2yJh-ԵX,j:XxR]cKƳѴ<5,sgSMC5~~l|k;;>Jn[_6_:o]¯yRoy(|Z/5(6G%w֩ 0%|/pڥ̎YWMF~O*My p}/g|1V]{i ;㷗e} ^i_m2YvH`j7&uy$o]k_XZך*e{˽zIZyu5l<YkWlKTeߛgRE>ygeyEo5O>u((ҾsQ_´Oa.ON|nj%QiS15=ڮR7gޯxNԦ.tkk5ԾT=k~AKH`'*Hmy >+?loh>CAyy7'4:~$ol#r {p"i ^l<Þ2f8UXxǜ;j#ÓKu$ڗ9768wT״5+9/3ʯ9u7{?:co@%Т ώK+W[/Y5DzZZy3o _麥ԗ^yoo/JIfZVVh6M $V~lp}2S& |axRԢȓzWKzWҤ$W\ĽzmmcQ_:?2;*oJA+K?^]ASRbjV~DVX|w? Oڥ4G5sY#ml-|:n! R IVzL31G7Kj_#Tx6h?8׵MLw/kēY鿻>#Y^΀<moEMl֭aد? YQ>v>צej]ij%|ĩ-u%?9ٴBȖu[~w>yךǖP<8u?uY<~-+wF>IپJu/4>gQ,/aOV6kTJ'ZE?/egX_%t^LRG>wQ>-ka쿶i>῟}O^k3hQJ?x5/4ohuILW=+B>kggO=bgT-y`ƥ}Qazu~g6xAKY<y\gHPU Z_?$w.Y*cq\"JOyjHuBHdHT~DZ΍._߱*,>^]EךuGTvs+sĿeg*g7<5#,%raY'Z^+F_6M%K\j{Yt[&Wր$ÓEa4yUn헛*?^=ZWuH!>x(y6p?Vo|esCiTÝ6+TԬKUˣ^iwLo-Y|'eYˤ~4aWSKR}M\̾u,U$-/㼆̒I{\>LQu]Ծl_cTڥԓ鿻Ok@YyPǿz>(\_lGsYD`IyHs:Mh冥&yg$|.Z6475/%~y^[㦓=xK?ZKGi"F&@q)%C7MSָ|apz5aYux,$r N=%`ww,{Jc?e!fG?q~/Ru5/ HB,Z9zpk~7C^ٷo4C>f7ޫ?ef?)kaeINKBv H3ӵ?aYtm>:i"ϔI/IH:X\/`Y9#O|-7iz׀𶡠]zcϬZ(TYb2nȻQx <oi60J"MUb6I;T~sK߇n[$Zm{+.Z͎f R#A [pL,rЧOc9-~9蟵Նeqo˹[r+dp1>΁)xPpIY]2 [%֪.oއƳZG?!(9hw IƉvb;? _xT׭5+}=Q[[IQmq(ྏql|29#s_<~߱w*xSUZ4:=/G;k096C|+z'w3ͫS֖s=W^2ռhWQd7+4թNYSkn} d%'jm:N̢G1 9x݋1$`1'" EPEPE$7E\Qԟji6쀎߃uojygEwk.F74 y|+gyAo>} JFXHɜ<5j[.]6f_x;UUPz| $+;&v/>?>$ڔlo}b=%`"V\Ƙ##{=$ׇtW^&dv]"G&F8P@J]'G*| iዦՀ[Ir+3W|Kk+m෴k{igdȀ#?0 xC.k515ƉZ[LI X 5+9&SGg/%%ği> <+aK9uw[=:я0hYH#>Wӗᦍ;i\C.sSa{ll)loR9 qJ7;|0<[|A/ WV/t2mH̳[܉uB#h_ >!i~ Umm>S{xo jvZH¸GTpuMѢaN$ұ5h_G5/Kֹе8cpH\{7Ï|=/NL'T_.sk󙼙Ioyù u8s5 ~ϟ~/}moŶt&1a>̱"ʏnY@<UE]Ķ:dž\?skGYm]xoP$+-Ϡj5v׿4zy0ow6x^^6կUƧ} UL -h ? ᆳ/8?O h> }3E~.]LK[g3ZR(ˇ_/7Q{ſN(d7mnu/ |pK1oD_ hshm5Ju}c?4]te~^ҥߵOc3m3tŚׇ&lVslE0btZ_c%uk=?WxƚGoQOk+/XXL_1xAܶ,鿴?~?:ul#/}@Um[ME6/-1Zh迳mN㍷f /|Yk7ƫãI2D%݅m^ȑ$ ުTVn\Vkx[Ϯ_EEN2_Vx9-Ԃ͖ ~bCmn 6 E!Q"FkԼ+7IO/LI% m;?f>%/h$%CxvUu!%2K-Zk2/ 0ҡԗR[;GK6X<=g*RQyhC{χ/&qufeKV%+1SUOu6ISQ5} "1G` 5zMu^ޙe]hZM+0q:yw_PWiΰNtynT.B]i?3MNnDS5֍wyu vuf*XIՙ[4f4ۻ}SQ{&R$i";\$d0HVPGg>6_j_j:[2 0=zz=5uE^7$>;^ PxpE]Crl{ުBA|a_-,fOU3TyNp"A\­b~=Ρ&Z =ApBAS ^⿂>="M QC$I$N x?ӊ+A(9<M"Las"P9;N8>ǽfIc7<:$h帼he_xa` n 7HZmu&ݶ"NIJCL(xtvַ7.l-ZLE 2pGj&9jy&ѓ`vV6ϙk#s835S>ۻ?c$ұ!KB~;4[Qs ڭ=) Jz_ɴr$ssTe^nhK3M8=? q,20N Vԝv*;Ⲧ!$- ]hFz ,BbӯJ:FlP#u.+G޴ag{,QB2W w f|K5KVI+ ;P.VX¸ʖFf+~%1$F2p3I_G>%yA跏Aү dF񏾎둃d s·nN5[BZd丑 +dFs7^zEm;AյӼG.U`/"||~mH1_H' |=wi% 1LȪ Aۚ?TllE^%%zc )]zLfYW> _CthK1^@%N7ɞTC +iK p[O&55+\_&Rb|x]2]@4V9nbf8]+= M2x#KS+#Q;Z鷏*Ytۆc}!LA(,b~Z&_u>!t]axn[K%RWQc~̟ ~EezK#G"W*{kO\$1C*QN\уK~?/?e?4ُ'jqa KKխf,a9fRCy%ھv74? ~>1&&oxO)"59$Hb+7#><:7{aeMzqK3Nff^DY>Qs9Otn_jzX URo@r|5/[K˵M CEsk:GƏjZqhxr>7i"6( 9>Otᵝti 2M*Vٶƿ2l$~:5ڞ] \|%u˃| 3$|>v!Rxo]Ϗ|_K]^FXCG Yėv*_gv <2[nivfsm~?-Sׂs&\-FM5YU#%YI\nV _W<1B|:ԵO > ڎeq,ђ%!d2m5__ /)\h_[cl7.q 0KL9ۀ(!.J{D'{;KK𭗄5 OFe ̊hcec98]g%~^6oWĿy>ķq\Fbx WA[ƛ5{tݝM[| NigM3~5 xO.Uamm5I%Ű&DUG;j"T?eOmO'e,ncX鷷u<0$SHFk߆$'Եk kÿm|;h%Ckukxd 4ͼ+[K3*+{~ Gω_oi em5X/$/ûInT{k$ R-M=tg:dZ9j7WE˒_i#ᶻ²^xZg/l/[6Y&hdhenJuweZݼ#b==Է+{ϠXޖK+Hco2 b<>LP<7)?V|Qi[7Mp\Xh+y\_æj̚E T~Ʃ?3mxgE%j _@U$2FuS٧j5WO oW񮹥^[j:q l3V%Fj)Wt/=eCaʞ3vǭ||oN⟇\:יwa4Dĝr9`9״h>GT񧉵.-D9u=Y0 vF9!bmhjR/;=ƒPWf%Wtv LG˼c3i67PcSsghLH!r+ L^ l48=6H898 wkm7zVYmo{v'.{ cpqU|匬|NWVugz n"Iu:%2D PV~5Lj7i$I5 ѓ.H(o@y|y7oTK._uEw$y۹U 0CiWk/OU'Xwi:Nr y zώ[ +}sFo6"s R9'>^ݔ(񓍧/⧏|aogu˨ءUV6!)3_hC.k^ɩ1HE}хm ۹g*yTդ&UFT*/̱\4&RLKqVѲrvMr)s2mut"t{>XF- # 2Fx8⶟{|3׬F$mCV6NU6qXAF( #MUe2]i-)ݹN9@ '?@ZIJؑ PE=we=>oa4ϦWq9JbE8} oYko;X@x% =nMT&1U}]&dw>_ےP6U^jJP.2fjPFFvF hs7Uu;k&z5BCܡA'r<+ѥ/yXMf|H>'--OmJ[8TI|H*{%e8kÉÝkZ(w)*j5L lWCa(U깣MIO=liJ?4oЮzm>@2HIo(-nPH< 3F䈅7Q9QZG۫W_nK߲?\Z/]iW(t}pH;M=5;Iw ]~9xï ?|Q>_ oqx|nޏwPS_1bl$q+9෌Z/xƶfxw7fpp%"X,x~˾/[|?YsNHRϹby,m#uwJ5mf_cy[}φ~*GǃI=Ǻ.!fe|I,9_b/?h|gO_Ѯf%2Ghncr9o'E?kz%狾1 -6`Ԯ4`J;:1y, ccoX<;m\Fr sL*2sʪוEgk~fz]|CoCBh|/I]cj~^ L~$6la٢lmn+=z7xğ?f~J)OXkzSEbk$m #;^-U7jφrDR>< ꋮO5[ꗐŲhcncb`.:e'?)|ht_߳<9ᎽOccqx"Tٙ)t[kkaKA07G&'wťh=Ρv׾w3X\Gb9\4cŕm( UqjjVwեo;hVylۏgم Dy\S!eۯ5 щ9A$yFLOP3_>~G~^YA0Y|Màx7 գAg4ЪAGmv E<^m/u_[R\outυ9)jR|_X?.=Ѡ]c]ޯ>,2A+@l'*gL-cR^Vv۫-lu~5|?i?|da;[_GZtiw[U.ys!cCWa(~?3/_k,Y%7_80 #||Lpƍgf0 Ү*{of'|Ye|Nao_h:}; SF߽o$-.̬M$Hvn?o1eM~5hI/?-S$Ӯeޗ\[ͽF0%sQJ5{9^xwkIZZ/zWqxKAҾ|kznxO? -M7jG đIsB9h Kػfړ~5-5#mXxO-|EqmeD@;qx=[<𖧨xZ CMZ[Y&M>KNSR?j|SLд_ l-+]w\!q$QW yPܘLIdYV#:{og~Թ&n%pg?dmO j-6oxZ DW)5[_yb2D Ymxf_1_Žė6jouH|2G?ԭ;m0xFźQg{Xݭ7J R>L,ҭn쿣|~''h>Oz>7'NQy4"B,R܂G |]~߀|*ڿ.eFzD7-xU<ڤ_|OAX<_c_,}xĖPލF՚V6ri=۹2,Vy|.~;*M + K=_:uι%ȦHKhX*GrOR"<ޞ܉&Տ(ZmfŅob PHQcd+ LW5};g x7y߄I|i-"v c s,~`ʎEl|@R5 _YG됋%ݧd ,9_ wIjuVr\7I*lV۸n9`0rz˯77,uգ5ݺG:Ial.v<} Ee6^Ockm'$`wWw)]趞ռ9YIy;k3D(Y cv@8GYZ}ܤAnBeH(SBkf񦆳g]:[ mVo&Me`HjTl dlxTt]V=6ാo 7 nE<2(O }tL4kˈKY>)=xQ`p3<]s ׋&{)W6P3#$]8cQ+mG%Ie+Y/+)\)9/&(MgR˙/|?{!Юm܈%2{GЊʲmO-02<]W#uXaikiZ\ Fwkv.ڳQ瞜2hRiٛ%J1`UnoHͰduP2<zZ' P9s%GץsAD=QjјUtr+Ι. ;OʺMVHR!B N3נ0^FXxQFD[h6#|¿ i_:7uLqbX몶N3ʾW' R XMZ,uyiMw d}TVGnU>\st4|/@}:5|f# { mg@ί=Fφ ^#nuwsG,`M Ѻ-|HhΉ0w;LZlu:HƟqf,ddMˍt>,xO8/mZP ~Zgn۝F_jWA[ wL|.o >jkε* ʙPnt&;tkYvf-p2<56ݴ^m<*ו᷊v_? D~5 >̉-MjQcAѲ:'-|iœ/| #-jl'ϊH-{>|;D\|7~iޛ3Qu|"dOYZPxyF!~?úlpk:FJ^y+e>`3 +6\#ԪRq?M{bz7τ_~I5O?<cĺ_9UO]P&* Zi kX.ag/ڧs9Ljl_NEC4&ݷqA &!H@WXH|7⸾xv/g]$+xUu[tbkYѦ W1Ld@Fcʉ~|o<pO,#/z^(Ji>{d dJ8Nd(Niɣᅤ >~̲i:\mgkwO4pZKKh-UHd a/i ?lj~м[ucy} #[!Yt@.Dѣ $cGEN:5h?<7m5&,jmrbS#iBKx+gMLuNJ#[wX%\iF[v^_HVcA4&*чo—8CLW)HWi8koIk Fꟴ'ϋHݫxZ^K>o*`VS$5m:Ȥ#/ =xT(E:X/ZB VBh&QGd$p3,lwXb'Zо$kwTRyٵ`W^mjzRgǯ| gM⯊~ڴzl5]兤0mmtM;YXW`~0G!M<_-oXyuO@nx úLn4&i)y.Loo$N1_ ?-9o1/}q?veދ &{Y~} I`\4mnaAڏO_Jƿh6^<)cXIK5ıeXϝ%glz=;+Zm摗4R>~g~n~#V[|q|EZA妞eae̶\FHˁ >4a-|dVÚgأg_ԴIaB*(;hU_~xGlC\?žvtV o*.M 9G BZz?o>Œ<7O:lKVU̺Mc9ӦBH%$;y0c웺Nvk[Ng?us-ϱ3id/|F5?"?^"ゥbfDjc)8о~^?3U;~&E7ūiVM(dPy##㟈x/oV].ޛdY7E-ִۏ?=c¿SHU_Ug_^>:xG/᧋qheGV5֗_In֒u ƑH |#|yQ3K⟂rؚs&_2~dt/DmPEhDdM˪BW/&Oj |Na.cW`[xoV@_"#[7D2AN'SNOuiZF4vKqod/ u^&:k*~Y^Xim,I-4}yO.~/uG[ xRy5#Դ&c-GWV(v?j/C)|[ׇ լ!u2cgXVbF6xd~w=xKѬxw_ ~$ٰKiG| {GAfcyXnG )#eZ˕[!$8? ]/q|!USWAut# $2 2Yyċ!HFBG?[hu7om:>c}UAդ6ZHb,lcXп> j?>"JO;p\O!X_޴j1Y@ #URA.ư -S7Uӷ%wG8HK[Pa*/OWdLvKƖt߆x/^OsT#\c5^ FILSgd.T` /w ijlixMSunWsX^OWj?<#/ xW6cm/\KmXKԃĻU vL$a ް|GwZ^Tek-rĆS$ "fUX>ee>mC|=hd1m H:Ҿ'hEiͪ}00 2pk3GZvlf֬)E¿%EK# )u/59uߋWw[] G[G}[>nA(M"/R{?hw7z$Vl$K$y6IuO,tԲK68H\;T{3XZc\- mʓ:h?CF~xDiRmh?y1,c!99F~Z t k;)$YBKk:9V2!_ yZ[o;w+9`]9M;G:Hmt}]|VG'3 !+j)Z5pDW z׈jW Ke޻/s3,./p$znU=tgUloF1ibjV\gG ^'c!A棊Iד@Kei[VCr922z5>%-u|k{$r 屵2p~l = ;]^E2^F`66٥°8Pˎ1C+T{TGp7`<5R-i"fU ar>Űqu~X2IW O汨5y_qkoIF5 c9/ק^kM!Id81'[Ĩ~7+1lǃWGcĥ]8ʜvHUMFt`pGQ~j6 7 h0W>o#̎nQҾeξYS;!E*RJ'v}< Gsׁٗ4_>-/ ^ÿgkkKȒHgä "J@?灬|wgmoB[kVSbUnEzq &+=>M:W*>#H~- xzI:?Ok{%h򮼩p8s8k~еogb|%Ni,>[&upyQd ]7"xS/[ }_P[yW&H3@8/pnFF̪kmeߌ~"@~%ZGqcKkxu$cҚXrrKݡaVS唴z &%Lu|[ÃSOhZ_K}Ũ~〰$Uq}Ư&_ #c\H{oq kupb72il{_? 'EŖsR<;W/>s EƂ,")v%MCD W~þ6~NkCx]JB$r,"x" biBUN]~[>GR\485[^,Nokm~-u(Q 0K&^cpA'??i_/:Ŗstw"􅷜yk4 \gWk|Do|Tm;Ev~c@hXNѥVYch|CG$p3j/G֞ǾYx1J,c.OG c"nɭ~3T5c/?$÷Z.֭w,w kKGvx$9Va$c?OtٳMԮ/:__.k-e?E?D*}a%,xʌ@zDѿmmQbO:ݮ/x:.ϣPvOQ70[ &}_<aoka¿.a:qeoouY"DmOˏ_J|Y/x^0[Wxvֺ'ڮ.$ӂ`Rl2/|nӼOȴ}gQv" j*2WfPQ&n]>${],vzt>[+g?`Ḛy@\2G [>QRԼRó^[1hx7z߉/W `l.4{i61¾܏<ZF v= fo.MϒO3^fzsmGM  YO )gYDk )WiCSWߊ 5]?in(%[9&Yc`"y9T34 hk|LjAacjg~Zf0Rc`[PIG5/|KW]gx^ /)ehwէ^&~38+TNVާN+jͽ/ſ:O}LU$ּ>mYDbh[q$1!E:eg|A&1&-Qd$8`v\J-s>+gCxqib\wW ~\2l^1^~w7|Y|+Il5ug#%4'+ sGKBguP|3#? *ژoma,1W@¢g+w7ZyG^6s3:jp9 ,66m*F1?<_ $|PKY:nnrMhu;K mE$̇tcr2V-[k⯍#TksٝBՆ%!D@RI|QsW?3qi{sqZo׾6KԆ{4PCrw;Nr 83gg%Q0%v S}O >i/3n9 -Hb:`J-I xpGz?dr6ӱZ Ŏ.$G#,;u=r+DnW™]SKgƎ]J"e6$ۜz_弶a4xF >W'eua]DeTO26W@|ヴ8%VG^M6./1AOROa]mE;cr.㒅FNzk>=h:"HY!d ʬ0~pls?oV=;&A&`$U+8 tǯh~#/>8i'tN%$d!N8PVtU;:`ukqހyDduG]}}PewH#Q^[AO_vb[K[]V@.~8ȯcF}MVFuHnR k-ݹfd}jO\V|ye~e5wSķ]D $c_Mr̥mX eH?{X#bCd15F !\rNZ!c;S 6b^wd`>] 9[(n:=̧ W?'/~"1m|Uq8KRzR]E4w:b%XzW$߲y[~b G_ƾEM_~UjdWYkv|+x_J[C{m@HlXp>.l7 x:7ƧpSx!vG]` ^'|O_?֋m/fmt66~|BxX4yفh5jQs˷Kn_.E'CY~#dCkxFdL[̲}#yL^h/4 JtWPG*nVdeۀ57;x?a:WxoZ4JmIH#ZM>? j-]\Ćo(PHWVYq$S$|~aA=gnK}mZY{H_"F9DGgkZvCf?C?f?u߀9wLv6r^G0c'|I)too^~JXu+]3IDX!TW0Ȯ!鿴lr|Bް%ͅƏG%qn"dRI6e#G[M,-In"tqd4R 20^ [[[rשI_a_ۗ_>:xk/%j\)[S#+%~|@eIeiueϒn5sĿmX\j<$S >yRHQ( I7[wAkM]+t,5 Y`")QЍƨIoO? I8^wk('? [j4ZZmcIwฅ iemcgt;gvO|@o|7%-#/{+8 {L.DqWa<i^8 KOaKhDh`XY|;o '+\3i 'MV )u}a %d$/?hڗᥟ:{uoxL D)C3:z8pOȄW~!L<]?ō\L6R14|o FE|CZIӣiG'c|]oV'Zvvko?DaR)46~>%].7^j!ؑF 13;W[_ gT gL|CymB4#74r-0YA^TG"#hem6 ;<=j}3^w^Λ GmKhM°](I(Xihǖto97ϧ9#ƯJoϊ<3g#wz+,rBHByM4O>0?e,Qhz^s@ >FXn sn`y|8xGZ֕Zφךޑ|m [IZNAʹR~P;_<e3,Dzm*Oit0lkz|hajqyĀ ~[z_v=tSݚg0 &-rKB$7)M %" XJꅔg_>/'`{EFZ]i:I僆),2靛졮XѾEks5׺\R |V3~U~{~/|`u촻jXF(g2NU g |kά EKl}s#xF<){Ib2LN3C!o-zQJ̓jo%.YPN$rUu=SΥQmpqp.6*ؠ jEl֑ç—>RAnF=?gG S1:x8bA3~͋t1W@z6kmix'7@QAznNײ)_Zme@2 }+ߏ:+^P*g \`GA.$ zWdK#xNQ/-ו1M6ۆWVwgⶣ7:6]sQ:߃YxQr N>a{ CyZ0w: 67bWGh>/>}OO&h\+f? xŸ>,HE-(vQDNB+U=ʭşik:ovgIq`"T`$V qy v5~4Yպ{f^s-tGEw~{d#۱fHl, <+ nLaќN2xּtZCf!rثRd2:r;jenuT@ccW-#] Qp+pc9$ SMv/̈褞O?Ji2t,Ŧ^4M^ fR2dc1#,3Q; D[Fgj6(F#_r^ލC]-.c;1I{ֿ;hM$q?}>O"F4PE>WsT+gOn;ÿ>0/ 3Rž+qMA]=0sn̬l09'w?_WFgßZe2ݵ،RjvAv*E¸xƼkI:xogzO ]I+1nm^Ke>MZkFL 4l .G5^ޟ!Nh?DC4I>Z?*S⯋|A@nm'HȮO67%IC]gm%Dž,,W=A \Ǩs d +dž_n+o_⯇S -^*񇈭α];b iU&R xºomO隞%&EVo,7 I1(uqir 6V s %Jɰo-#v^0YgjEkfLk OEB%q"UW-'j *ɨ쭮?#JQ-wF째cSR`%o.E6 s˸;JnIf'׹'owFSj-5Fx>.D3]# T8v9XFǚrGQ/j~д {,Qknw"eAnd {ĝzy1m|,3L,y7?͌\%2}gwaPS-0I[j-$[yuY%([.SyF =qSukaFྋׁS6ׅa/ԗQ+=JYLr*=`w5xG5K _^eέWۄM0]4JQ=t`fQӵ+F}-#ӋbUdGB8>1}/gg?Eksl^{f[ 3>ɉIT7|z1s ٽ?#d_Zw_/Eχhi 9C 0$ʁ5ĞLj<>.1IaNJI."f&1`pypO{i"#)h|e<=&Cß g{MŎMH.n3V-n|Y\R9W,[ |6QjUGwu .B6KNYnb c/O?Ru9ߚIm)˲1T K-}c]#G^5u/)"EocBNWGW鿴ibu"qNͱ+1g6̪Ur_y^4" N33;~bH9<xV 3y[Y8--8]׼5/?[ yH$ 2qLwŏh*%ޯe_ZI %0:nl(8lyZ|= sxٞQHB+d<>S@݆$d]MZ[8ah$9 88Tt;xdU#\Z޿Iw/<#JQc5ml0ڝ"TSr`S#};Za\\j:&fS)ى eۅUXQ~dODj%%k`K&2N;O~& 8g?to9HʛDR=1W4o%7A 4Akjߓoz~TvRv"Yo\+.s5_Q>#iM8Hv) /\ҿ9֍7;z^̜<ҍVY6#7] e i~JO_E?T$aO\M?GG㩧g֖s,ҳkO؏%|,<1P/%6澽BY-%Ibi*O͒ xe/>tXҟűhVЉSgOn(VLg'I&Oao\j7v?<[MZH][oYGy)#F  |Yr _+؟_ [_Zvn77%̊mn'g̑!|/.>a}ziQZO]x8s ߱G|swH.'o&"VWf=ϲ(s$YyT<<+o]i:ƏtI} 1oEFy8BXȯ۞> QѼsĺYnᇂT/&5g"ʬ8IT eG_')m]?L`WxHvf!o! ^%Ů%~$:5Ƣ/5In[8@u!!1G&E7.> > || |7?;HMB;st-<;X`x]?5ՖI-J[I,l1H4^l9e4li`1x_IIR/s/ueᮟͣ Z];P{K+: y_0obwչS$*OFuq zygua^Xͥy "D"J v+GM=G@BH*{2_No1N[+Ղh(sq|Qͫx:-"cgcs$*JoO |zhet W᭾qk:m䖋|V.b<qX+ iSiW YdVA?31ӞWzy2deRq~\ZF"DG (Eg8Kn|iaC^4wP֭f[LŠNJ+N _o!0J xFėg<|~E,l,G>D,I^K%Kwx:ی}_?/Oƪ\HX^DɼdRN ボW8V箦CQ.A개5P\(fǏ:קn8Iס9(<$ &iwcqi$3/kdZO~$M/\f3Ϗ8|Q--$/w{ QAEˈ$ rTg 2k__1QF7g~<_}j ]G=Ef+|ݎ vLP?ſ u*ڌ F"YeM(BH rs~2h_U>-׈M4}|\wbd<r[d3A"˦kgyzǵySwcxJzoyt hQI^]1Μ39XKx!BI9@S[29'8E]kD\AmU`sN֍HNGR#'8RpAz|;鷷ą=*jhKp|3yq|@|~e]hˋRGae73)8v0yp:tQIi:|fy:fX@=eSƗxQ2܂}A+>3|VI{ZbK[\[+ CrEyċ>#wZϪx-\#pNB\i9E4c)j;lvp14Uo82]]Cv>{/h<5ٯnN@H4Źe۝9T};/u\"2H-sn~^#HyEs#"'#9 yڽWAէ՞{FӃqĒLk{s2Q:W^j1l.D ׌~vZkw3/KRTut=^ߪi2\&CNn;殣&2[4)ud?qЂ?0HfUqֲfѵ37J#/Lu A#u\4dWW7.$^,?mTiςqT1? ?g??%+-Siv^ZQb[yvWͿ'ogKkx{=:ZՅhV[ m;v6X65/*^4_x/ ӡ49]`%`Ci|;'ďZuޕ{~"/6%Lȿ@~R\h{6k٭V|TM_/<9&tz=ޝ&tYђL}5|7E|վ? #ji_ 5O7d(+imRBr~Oƿo[uOf-M}6;ŤM1C!߆F3]w⟏|57~j~%[do3Q/DVHD3FU BTPIim_z{ny7>>h#~'Ti '<YFho(d]ؚ'd.H=쿨'~,_o !x_ú|_O;ay,G*F ֗r&Cp2k/t} ×Ok4s-K -I忘хG8/~񿀿g(okt|2o"?\:R Qad:0qF"NoZ_mu{=:jVܗQ[½wGᕽKmIag !Yb".f''>|tuۋ~:gReLE6_*:H v2OfaPߊ^-u֞ڎ|_?翗BZasުHuhȫޠ!<o3l5smrǦ;6зP+cfE6椣^OSի#ӼAaKy$ʟx"qcj|?ktؔ(cq׌`܃\#xǶZVZE-^Te+Zjm{)(W;Vï{I3<bC|nZc]lXvwWi5hG#~85107AǏzWu|.&׏-Ic!,WѓkCf2^8<.' k w:Z"-晭iM۾pqnc·tjFknlEIr@'-!z<8Net9i9o:<կu"5ē<" ce /㏈׼#qɣ=ؚSr$H${YA**S/' WҮ'cH[{'d| kZI7]\ 9ǝ$y' 2^t k^&N̼yyX`Hl#5|G_Wn"SFk2]ä gʊܴEqzV5_Zg4h͚]> wݗUmo8lFS͞Z^]iO^ժ<.m#r!ӮGCIj^o%PrFmub7F779HT>sφ2]xhu+uǗ$aUG CIy0ë^A-VQ}X .&JJWOv8%JQC |g;~ƒ\I4ÉMzEf4"OAl,[0S@a8Ծ-?>9xSbotI,o$~$%8AEl&fF 9>8|gqi-.飶&ڗ(|)  X(d$ _)|2|cU~8Xx_wC瘌$ܮ 7|C uѴ_OwYKT @.Fpǖ+5'Du[aq#Omvh-*iH)Rtp/M}ǽ4j?u_IEax 0eQ?xB66mȔG&9YWw`]ѬxLmb1+'" s)ïyπfHK]Yѡ*8dݮrգ+imgo:w?SFGx+62c|l_|&?].Ԋ b M9鑎k59j/m@ ,G<^kp;Lld[H$`\Qnvgg^ҧOK]£M}~;5<"@1ޤ{tkySiII7}m =銒Iu({Il!@Y־&E:k8Y=1x8ES4Hⶏ}"ANp V嵜Gsn#X6r?#޼X^i3nz*Ue;T#k |ŸB"Wp`zzX|-\OŗZ<[+{FYW hc_9q(.wG閛7}ǐ<=nWI|ǛGasU1wg?ao~?g?W@sxKȥ%ךR!"~;5>jvZ|;kfۍ#UDjS"*AC,o7&e=Y{iNw{z)@?oǂ|+kF<=_}e:$6Kc&!t*yoữY-׆#KAzg5t(-h1׀~şg2/k h֖Ϯk6-w_4O)ByvGs⏅ZlWr\/$e"2J+N~}_O_5O?XF\xs:OO1'"^(X3ltxlg8o'jOƯ> > izwk_yTdG:*Xd_:ѵM,kƫj6ON#O!ۤ8#NS]*Qw&FأTgv >^L;Z> H5%OXiZXk ]'-8]c9Pׄu\!Kf\QvP9R< qs r v;"|o?W.GՓRis)Agt(ݫ .ϔ=o~.-W]?RC@re}G]##'І){H˶"QG/~2vk[oH-,PRHvW'ss~ZoK_->$HU܉T \s$@硪tO↉x/177Ԧm2],; d㏚uK-wږIl"^Ɲx: G2=?YbF|o'xc7|OԴ[JKx4+Ec$9"{frQa)a=>L6i-Dbd+F<( `G^'¿ .`X]jz Vn^1Tn`c*O m^%ȡ8tվgSR=i-Y}_04 Hp GA zm׃_xOnbPi+U.TFȼ&AmX״?RAg5Us7j2^s\w麌:Ōm~m%E<.i Ax8֓uw=7{lO6nm_7Ikqzڍ$AlHg`P0MΉgr }m']?vB6F/r@'C◍tXcG xF)PYHO^_~ 3]-t ,2B4ȎYqzb>HVsFqh> i|Ano:t#6#uVt_~M׋-/xwP7ڵγ,ZCg†>i ?\;/u;"D|0Lz74>v|-RT+d`1㚥xŗxcY<1:s,Sۇe3v06>6si׳<[ǵП5OrSž1\E=Ǘ`3*~f`¶+>o߈5$Ih*##7cYF>PşhO xO4M'1&Dq]&.b%I` >վ++0vfU${\X֛I=Dp3>IK^zΙK7zſu&{5^m#j@n0x pq㟈^լ-xNdm 'mğN3[(uOi;֒~̋'(J|,.+^}ym'TO()V>C/kzN4ǫO[>wwW>DLDw"yܙ!tlTps_+6-"hlEkau\xg%9AH\ŅyAb+$p1* q^ EyR윮aC8X:t- ~ޛNW j ?d+ze񮇯jRNNc"2)7Q|{JRRI"S&!Oڧ# m;CJ%P3dR=bcδ2MΟum_2n%\#)"<8}Şo|a OrGkzl?%0RI kAW|Y D_Cy`&PC+8(^yZ7ϋRP/ĺmM. Tzauč_x[+rCsds^=߈e}[ bvp <7ˑ_ R~;D&, rcO<6V#+cw>$]Q%,-,&T㒬#A=6p`,yzzVUYM;k]7-elG$~8 71~'J>/$6':l ?-I_/o x6:,lu~q%t-:wf}'UGyT@AUO8|}No_^u t9mޞ3s `*}E#.n3KiXf1񪧙<Y)iaiF!BIBRMz/{dcm}u #)l~߆_~&hW&B( HG̕,\r~]M;_ ;UJdV^ Kq!o;`O-| E@nmeG}?<֚5Glclr1E%U}s3a"k6Y[\=@=h1(ue$r1rڇ]L5$fb U.A qH/.4kcܫ1Ïcs#NG.Tg $8WҬ^(h_k/3ɓLqz Ae߈<{wiF#Y0s3O< *ۯ爴t"[Msŷ-zG%dEl6I%}\c7 qt.leL`sH:)^d=\h _kP6.ņvU5;zSin 鄻"3F*W8 Xzq.ﵗ{9-*=no| ]焮GM/3j'^k' O`ڪGAj2FH\U x.Ὴ}^Э%PHJ>NҼ \ >Iwjͱ. ͕ct5=hg xŁe; xXK(cY WeIyk_|[aɤXd,-|λr0XMN]jԌWvvms$E~ё q4#ac)4Йn71nRPG{kUwR՚4W"Y|J`\Zͤbhn[[* A~,0|#%c$PD& @H 6qk?,ti&k_""rx+za5ǣ6zd-s6 q f.88r|K[xzi:\B+;W oJ0:'X8M<ķ%EY:Wp_AOv`V}%kwy{~ -8}rS^Ť]i׹9nˆf+SMn㮇|84m^Lgg2T H`#Ѻ ~'GsiINaqg G-aj+]l>$U`@ F$Y7=_<# iXDJř9;wvH5yԌ9qbj/G5(/u KĒ$[ad*݈}w(6x n|CZ?_6)[XiL~WfDGse&jROk*:&`(es }BKMZ,NՊnr)Q2 rg+Is6wzM&Iά<13;å5K $x9؜}px ӊ̊kx<~hpxt|yrH 3,Cu?X}ƣs|]̀7ʬ9ܪw6C 'sx. W5?)D"؀gֿ忍<=w|z\aKkP1>ی~vnHO%1d 0#ӧZs{*h.R_zY9:ݾ{S_Ξ?o!?Gυ>ksz֟]WFkد]f&HK /j|^+_>=^Q[kWq^oq"5\ 6>aXwğ g|!x=Wjʱ 91uN+_:ZnWS#x_/ ZD~*Y-nGS̫JrЧ_)]'fVtDgҞ:sq\i:Z _# RHqa;ח)>|h}}K"!ӬE -,r,äJrAZWBC։JQ[D| -7q)G sp(.[NiLv_NeVvit6=oۿ`wv}>?kM|Khzx4BgY 9KG1?iޡ /WM,FXhk}NAwo_ 8|; WvţxD-u/ӯ-S#\̑u`^a-7[ͤ|Lxǂ4!qѾʺHޮ1~WuM峂7^_OR0o<%1Wg~|@_w֞Z/1/A/1b͂_ \]J6X<[9Om-'IOs(udya;'E$gA|CԵjt ->i:,<ˣLT{Kp>r2UQ®HbCZǨF#Y>R[jwsc^3 cֹ8xJm˂yZ?c?z7>*MW54 (2,,QDrĻ6>L>_~Ța4i^,ZWరK3BP]ɴc֮-K2}xY98caB79iKz+- \9+m1޾J~tǵJ-"1߼;lv9SNJVwEixPj1WwVto,wAX*sN$jq]^m&eX`!#=GS;Gbku4Z @r6{xWxGuZ0[ou]VŨRI|vērkt%^zReދs?x}O.7냾q̽`x;`hgECѡ֭VIXʼS,&%\ ofe @lH'\6=k^k}> !rSr ekie&Hc '>JJ2wQݗՐE/˞;pFj<[g%a* \9ּдY۰FEܻNTfY); vkAW849+x(w\0ckqkR%Oz/Ƃ*Qtd##OVInDI %qWwݣ K jY78O8N1^WJi<c$4s\J$Dh'1ᵯZ~Kdz֖i7R3`U'-+kßOڵĿ<5G_ GkdQGz[8u#ɯC? xO ~2A>}Br#K W +N?ORhu^k=b7Io*6$`d'x3Q. ^"k.@Sob2ҼៅAl/.yAk Hy97otwZՖ(YQ|zK O Y:nz*7Iu?m_^o[7Qi}WP^/uN~ܿ~1Oo9>n|q0<ꢞc8uGJ79]/c{~z8Wz'3u$,@hu t?Jֳv-Z^R&676^#b.//#NmXk#η[:$)qe6r"B.T) >/C/x? _]ŪhrXjeIffS P1J2RQ(/Cfϥ~xokK񖟪XAkZO&]7QBʖ)_{,/856O|JY:UŌ'Vhd`74,Ix2s_'>/ |-[FvͥG; 1~Te iSa-s> j.vK * pu`VZm?:h>!>)\#H(v>elu s?m D\jl/ Iw~ #WɡUQMRUnj^,ӄ4`d0'h1 q|/RmԿU>6't۹bף-y*E,70Yp+>-|KյrO?5O^kSuK0G@s Qk|E,A^ޙB|CK#x$qc+m AB |HڔMnbeN~_ofgyf5h缊 "6JǟRzՊ [Kܪ!OvQ*)W=#y]9;7u+sPkE NџX\,$+)<ڡ_GNH뜞p/Pm_f:fov9Kә^khG{w:*pFx|B |'Ǯk !|yR{#IRҡ[b5ٳqǕІS9.Vf~oHJx)oS<{?N^4M~0Dn<0 |D`vD H 1RwW/>|`fOXW~"/u֍tiIlle2~aq0-&.^UP^(u&%[[Ď{krc׽e2ΉV> ! _7\Z9[dr9pA9%|{@‡×:.yaneM67cbD6󆍲Q\Beεݝe+O™|Ak$yo"H\@k~|.~Ҵ눴[\ ̑4fRd 2;gzWƟiWt"V h3弌3]AN^W6tmCֺۭYEXGLl|Cm8a]K|qZK >IbҼ^xФk3\X(?.kEuo%Z.ߛqp3s^]ᯄ7M~]WZM%Ũ &i!vcJ],^)3Ïee (gܭ8c(v9榧-iM#+ rn3tKFۏ;ʸ[NQ8 Πc'Z~uE˒sF W@=O`_j[..5GxԺ $Hvp2sh}ZՕu=FIEm7̮ d`: g,VɗK+u%ޛI#q]ѬnmtYсu l$2M|ks1N>lgիR!fʮy?<oK" F?%khaPzLk`$-bGec$ v#_?wW1|8tkǎ}{RĚ eVᇣJ?ν WN\8+&O˯7{#12J\ccyFKO~zfjRrIX\TtLS? ci)EvF;^m4D5gI=RHs\7I|`U;G'xi=x=dˣ5 ႩwBER[y`~߲?JOSm At|oio[);͙79бJh9I/r+WugO蟶O/F|?⻝^hw]oK{yȘf܌G>{ozOx:|S;rȻ7,8p=S?~5ƌt.s/,^J@್pb`8 |MNx߇ˏjO:6t f}J/W߽rw95 dIfĒKzסӳƭwC#d@p8v>jQ0ST_^</z` 6XF~q * ']73ռ<9 +B\+F?1b;\橩hzou1]D0 |dzJ\BRt0\W(>j冷.ɖ/Ir5K.@nh?o +~W6!F{{BgGt;aWێ }=:' K~uO i oG7"wy'RXPw__T wu? h0{T~FWkc6M2@Ncp&tpк~\|Jhe OUbmJً~+t{]QհCK&s+m  +' )ԿZ^WJoeE{K1)A}lNG#~doj41YOmEO-`!V*qa[w֞0AĂ;Vg{RLeŒT\XдwA/F1wuHv ;qf[۟M[N Tjmm$o<2f+[j̥77 zv[/g+VR.,L  k\.^z_-/ 7R\NzU/$̈́5$c>o͍ĿϛM"4VhI`N.Uqܜ?l&'IwDm m;$PN@.HΒ&$ʬCeSנ#1*S4excNtءӬc"hA? IU-l`pʹ1'<}kǟ/4 Yto )`Slg' I"'2F8p)xΕs&v 6죾Cѓ_Vw=]\;WbK9f'$^._WNM|vtyT{GE~d xF(-xGК7N;:EޛlG%XI~c; ,Ԅɨm}0,S_EocM濹KMeE`VpLDeS m G_g-NY<9A> ͥ,@r*#'x?O?_k./iR 68P[\CK)1(1vC9#,x^:|M!uY, ,) okr $y#PYR{zV~[x_ ӽĚ}[uefhXʾO? H&ICͨrgXj( x5 ӦׯygOJ4+c_w.ޠ(cuܙ66As?ş go8<%`|+ӡdY7{"8rBh7/zoDoW:nim2S ח"O$~vQcW?i"oyOZtE%z@KHZBjctXmE#F5.etvI~/o;$G[h25DPf8 g''>ÁLq*W~ ߆eϟ XE5GlцY  HI\n0W~FQ5$yUb$K,[ntd|f@[M׷+ #ƥݒH>|Iuܠ@/:烏} MW>WX?io>>𾗧xIk+wҜ:yI0̨7 Tߵ/z៌l-E o"ѮlWqKAgd* !۽ٴ{ey 6c lq bI}h8l?_ڏᇉ[ȓQTY;wx2A4rĉ NNYEMoOz+xC.߆Ю 혲F|:nASFk_(7gxKsjv\+q+ځ,3ܛ{x&H/Fgt9ȫ |O~? ,| E+,7f#a{<{'QDɀY] Ep }gCТ໵ŵΔ +6)o,^bn.wg>]:SM[|?OQ&xVk><֓ݶg #Ԓ;gI#×A* (8 }I?|ueCA.KFZ)ɗбfF%r28*qkH|i|kL'=;K5 Qm%mϝRCl~xGѾ'xGd%dQlVY#Ia NYo1Z-~V߁| ¾!~ 2v/v$:[2}UkY葆cdWBַ>@%O;N$ĖH9#ۊ{=z3ZJ5޻o-o/yP,k|IrCix 9]"{FKuWB@'@:W=IanFYE瑞8F=+(RkQD0i{v ԁb]~nI[ ~`8Tt';kSdbx8/% yڷZMt?CcZAuUP`}K3~.6 ִtֹ፬h.!wI# #A nl>g k+^ebEI*BɜNUNx1sĒGJj ײܸd#lsUSuwZN+M3y/m MevZ_>3Z\j@[FxrvK`7?i_s|0%1wr<-ko1nS.,`:h14ZnIeڤic2P( "V@'?< KkV4Q}s &7DHCd.2J7(f xa΍eu[yl|Ki5\\fm+ޯ-h*AqhdhY(x m/>W|>4\kBway,0GBHPFkS->&|] _叇.)*mw/ljۮi8OP;?e7wcJ1PM2u1Hn, ќx_¿<3o_~-uMQF bc20{kWxEܭ~߳~#AC.=NG៎5h7Me2ͦV8 Izyl_&>dׯd.)I+Sɱ+Zrſࢶ1‘i76ܳmw(/ʓFdJ?%yc|D/ $xZEͬl淒]E?z|Zoa>FZv"C,03Lѳ>G8X\+󵯥Kv5{z?~ ߊ~v!}4Zh!,3<Åtݎ>-bob2!BR!q[__O/hiv^+ woHGo6#HJ6Kg  O_ݭ-Ӵ?x6Q~ I-a#h%ׅatӿwٙT{#> m{kJ%Ve[}g"I#*IJ ޑfWӧn|KambX$u;y 2q-$/oxΗ//Yexw%6JT2IKEK'$mVLzO,/i?6Z]kwMsUɓo3˒^mJ*^N*ryFpxA<3kCe2MB_#SȬ-\FYN|M+W}ޕ5_+HN"eLY_ 5ŒIx0ZE.Z1jڔ VBg± F0%a~^,KuΕjm$zJJpպ[i̋Ë{ cTV;Oo,7!,YSs+ax .(\6֭YH70ܤssS:u7u##܊-dxX[bH́ajnlqr+bqMF-JaQ]3◀>J~! [kF2Y d(bgෂkKGL\$Hv*HS_w|P>>x4.Eƛ }2`0P1HAK@p|]ZE76Z GX *b>@`&+QXl\3\wxO]yŽK3[KdXYB)(##9~8.=.ϋmSᵒ-PFkx , y_!@|-σ7^&>ſ|y|[41{M'MWJ \ąܠ|B-蚵Rrswq?k2[K;tnow< ױ0}A&(N?|#Լ ?se xPbG] ۶aj~߳+GԬL]$G Y\Gqo"&$[]L>ţ _To^]#{ hРeGwRZ] }x|uǎ5 +fZ!ʍ?f8{_⏂|g|J[:G=ᨧIa}b_11;+$NG,t|acxVmm;NYR+5FXH ]4^djZ'G- Y ۰+̼W'g㯈ιw>`kD#,BqF: 'V-x Z^w_;c-<Eٽu~5? u{O:ƱxDQ🃴AfqFټ F猠Je͡_l/Z>. hYG$*sGě[$.'1*wB8q"|ww?h i {ggwH[kaFZEf &Jxn~sܜ,3t+:8O},ŶHlriQc6qg태㊧i<P}$ )DOq\ݦ& f)f$`r:V'ՙky -,@ o9mۊ9_گْS<| =>p8!"#g8'UxLЧV3k nocOKxHK?z.' c^92:kUQwCp__~()ۼ"4,(It3Ko*HGC@{I䵺')$r)VFyu?Z{_{--Ygf<I$Ih؋QVi֞ėho9 4ӺF}+O 7Ѿ~;EYe3+(]l۹v_L|wgzkZmߋh\Wk!gttP@K$ _?Mk>*j[2%ҾEΈY{QX*u#8$VgbckR+᎓y[A w4/^iTv2EEyoJT.v믹:<kUkiJO>$gq'pkh_{Ge?xZ} 4կ4+HQkpk7$5k#cu!I|ykrI@Gug@B$WrDR{~m&>ୟN-π 9g=총'>8lYTZ\,d9e1\95.O_R‹ Y5D2j/mw#ai|UҼAkgOLIa!ln驇E9KukS t}]חq OZY X~p0Xc?+A#m F37x;=Fq:qd\FVFOƇ $n~WEx+⯅tDtfUYYehW6Dq$Cvg\ӵSTu-: _JmF04`|q8&&+pU>&WZI3߾kw?&Ѽ? ιsY;dZ !"? 8+[x"T}]23DPH6~nOЈt_DK fWH㘃'A9ٝMN)z`NѰk=$0pQa'=_!Ջ)N++O 3W~ɚ\ڿ /ʇQnL,AIsarp9v_ZiHcFӴeDf%\?1SC9>0qsA.+>+i1ۭ#5Ih[(%hnX[Bf?PTW>>x-3VQwK{L -4RI,1 o|>._֖C as$$WTO/䌂@e W :n>i=n&(m' q#dśtA9+ǾWkoX9N˩_ko _ռ?|a{ Kc:r6x^ɺ aK설Rqg<(EZMp|[ =zX9U+>mӾk0%Y \hk(,y!vAl^Sfk7ZYNt"+Xf` ̛J8hO{9㎟I|3`V$Ђ`lđۤX|\ȚMfWO$к-h#bŷ\W+N_?hMh9>>4O/ڝޢ<~s[I*@rQp+.kcp-duGbV&@8Q]4,X]G#ȩsh2*6^A^{?ྞ5EˉTgYa1O<2%BUBײ-꺥,aQP$gWtit=]ikhuf2NN;zQ>#w#$[i1o EI1U5_* u{~ؠwTҾC^QInxusJwngcCi>!DGLZ1Q>LTTr5|5c(ƻidwdtZ:t )m/k= \ OYSX+Ie V/,>pe!>ϬX{8D |98q~ܰi:ᖶYoe [RXSF6|ibʞj%Q}[ظ8'D'?dW_ /|I[xK hby )g 1q;+k˟Mnma4!YVZ;DVw o~$xR ~smI1{4'$V3Fv&^_߆|#gR״ ƏWM&[1j? b"昼Zo^O,x>64-/QѠX;H!^Rw12SXZqk+o{kыcj C 滥0糴ִ? (Zq-/KI%rO?7%xU>I!ԼoVӎ U.` _?i~9}reм?-Y[;im4v#I_@ʿ_WϬy՗UtnaҬmG4+)',yv{MVg~$a/ڃ׌|}gqm}~bu X˶X " -ٳQ_4x;;xl$x~6䗴heLMVEc~)Mr]+f8%{ݴϰ9Q$l@pR)8 VmW|!u=Rkͭ塍w0RUT)@*T/ukvO_)JMg?k/\$ԯ5CukUVf}o<W4 x^Ѵ EX|FBh<m4fE$YIߌt#5L/?şmYjQ ;s!\vƪw>Ҡ3+~ |!XSƟZamq"ϴ9M2xIcQK/JZȉ"*Q,~wC5֟moP졕/|M6SClKKp#G ;p'56q'5gsOaOsxڦmhFXlWR*ĭՙ((FH/_x"2~i?n gĭ,o1|*x|9-[/%{xx=& p&>]טep٘> ~͞4/ANDUshf)rE0'?㪪b*pR~~~"^xԵ]uƷu(QXĥJLr<ϼO#Qtmk[>6}re+]¾t>Y;bsqӧ:(<ع'w My~?:R~4V 9L4AZBVP07ddfOuFq;Z8z"o6$+,,2NI*|%_⿊l5mbyi CkVVi[#ءcm ~B[_4{}3-LJ`%,́.^y2DÉ'$CRn寗O0HQk>MVV}N?.~nI+ϊ>'|B񿉢 %,vkmS޿b7|/5 ]^ il 3IdphɵXA8?LŞ>7~{_b{DFY" D~Fɯʳl +xMg qthE}eVؙ>0 Ro~ [[(tYOKtQ%;FWw jxƞWeMዛT`+@TxBߌ |M'S$;^n"iN[^uWĚOε[Tq>m{c P ^24Σ֖3<^-9]u%v*x~I]3_/Bt'ċxV-^JX`v&yzٺ[h^3L.-tɲ^*ȑ,p]G.j#\n2HWk O&K|@M>iDGZ:υI YN U-+4ܟ+Vi/}i>>-6|9? WMGWiH⍢]hZ'Hޯ=tz n.w^J- |-ašab#.ŒF$s1$T^3jMZ?ovPQU O+],ݫ\ C/ʬUw]oˎ*莗o/5? x7Nb,唃'r\zW؞𕆿Ȯ.d4s%۽ǘm}{&k*cq o_c #O)?c|j"O?&WLңcǛ'͌j^xΤdҥ 8+$QEbhQEQE:>(VڂNO4_h~ Nwi#~ir ofaYNr@aࠟfi7֬#ҵ/lt{*m..Keo5|H |2hM|Du}؋w#r* 8 sZ^#|wi|L~[LM~CO_Muu^@/V`=J\6$xYwUӵN)m$RK,%,d-+,D!B$U?WR4]YDU\r7 ++wFZmME~]X>_qm Eö!PU=R %OW}:hx%mJƱ[XӴyl|EyI2̖KyE9 +&%Pi9e>$[|I_v&Wq 8Չp03?bu[֗ooD:8o5 ntO BMI{ {:QѮ.^Sw1hHg[SDr ' |%DQR}6mCcmapr[v$i)ǒI B~U_e~CJѼi}a߇:x^̩5 -m06 Li)+=A|/KDҶgݮ췊P2AuGw>FKKͬ XEt?hgAoiVjiq]ArQ %cVpH hcK 6+zZߎ'Nco5 E>_ 5/YhOakxZ*H"]X:|K/ZԏdL..NڈT۰c+|{^ GW|_ EMJ4q7Mk{c799>Y|~m6uwcjEuHR,ʿfH|![CRx_/x}&OZInu(t"[ַc4hYvtۿT9?] ~.}. @[X7QX&fkILbL豸U۷'ٻ?ۆSx]rC5}2jek[`\'grgBjI좛 t/Z&ikL^iבr lnFcӃE}z.CJ.u8K_{ޕfiĚW~&1XӸS6VmXHy?xŴ?F45',&Y48bwHH$c{xŸzLG -/.T9eW69B,c ' /VX-m?zĜv_KG.Bxޖ=\{}| ُit 2Tc*&q%0Nw?7i{ڋP%.,VvhexV,OpAK nx{ƚln;s߬f^)LQbːPAGyKZxWƟρ"iշIl7*'_#|Qs'o4uAqskMkkcVYv| ע|EW]o6 vhC_itWw-(·Uy*y=^6Od-u1uԗ<՗{#% =+u]ԭ4!2\kkx"Y9TIʎkʾ2տh=F'Jt xfv"/+*i$ +f3n|u}ExY1ضZXƪ۪rlc-ך~Cn5]}9%ti[*8PQ6('ֽ ]<7*')-gg쬼9E] yVa14RDwp[ Rۃc1s_Q|._M ;O58ehaPwFҸeT̟1'c➝1xEK:9#ϳ_0Kdf/┞1۫4.ϵ2Q@TUxQSSZ45yEy+B~ u=!->$|JoLSm7ӳb?.6NV %ʼn* 4̫vO'c=/WV;[Iiq1 76,%ik¾ i?O4  ka#-9q V'M}FHZ 3rW;0Tp8nvc_"z7W/x{U4k4kupGq 3“T)//%V6g~:BI, 0XlA-|,|drO5j7d3;zq]ٸ6߹վO?gxg_vەޅgoCjnHH~prGec-x}&pU2QpH?ao^=??ZEMצTаQmov&vPQ ;.~xZ-hnDVww:EdG+E)$$U0+焩IT h|kþI.ڗ5=Ntg{%ͻȫP.>U.xS_$ǟlե,@o Hw<75|G#~C2! F5r)]~Ne/> Ӯ"|KXA$ihV,m] I'}NKN{+ecM9ٿ2Ηυ `>.;i2[qn׌ǴSH$C G:i:Ym/d[t0RApw8=9ojk#<7MISxs1%" f1#q8~5 [ח#ɖLʪQ{ϝm,G/\n6O 0Ϯ+<׺[aB$w&h<_M{ICNNaG㏈~zČHNp3|aG=:uph~J+'7^(HtOO+FRn>kw( *4kFnVvHPli&i )>N{fO|(}l6sqo}"xLi/ͩyRK;$ۻxwًE7? Z#ՂEq-=V8-!?( {dž~[x-֡uXZk HlPĮ# m*n&Nd`ӤIߏq^M_AF:Mq7$+i$;R A ӄ~x +z*t-c ? þ bA6ؗ&JDޒFԩp5a|\u?4_ڿhJMkI'6k\I4yWFX8 y=5 Iz/?jn3^zᕘ2(C\I8_h'x]Zeq$P1$zfv4?5o[K|)kkcaLUWCi-dLb!5>>~(#=e"|-oOi >Եo"[[fynG崎L1Vb]zTgd)kKm=~"pһ_kgO_GΡdrH?nR >0%χ+M/÷:bm>x o13Z 1۟Ii+]|SZ_ -1pmB,]-^NkAKks#{?]+J4.|CsksUx!I hCGeMۘK.tv4F|V-6) z'ZVkS,1[q$ ]cY^I*oxH5߈mCCׅnnOu)uo;߻vEV&:s"jK/_;jޚ_T̽švSPrB*v3̣zZt>+Z׏.c]:Sxƣs T;rxcs]U ({Wˢg~ɿ,%*Zh6p1$_G*eY?d՟zo/+bu?c9,+%Mcp7F`AcUI'GϚ2;I75k%>8aeDQƐ;a (|$a;RKx fLmfhCbG.$CHw50|Mix?//M4#-cu H+p%սv;+'v<[_~6ĿY-mRkgK&&D9Fؒm$A=OUBLk+K[T2 \|hY*e+ W^$??|!{["C=|?H.x m+;iӼWoz476768UA U<ݹtJ$zmUX9wV|o⹌6J񢙈D.=I߲'v}g$rk:m$MH#3Ɍd~(&H~0 A||>2ϤI4CDq4A,6+0 @fV>3OmேWÞQͺ]^9W)'{e  q-K)mvP9UO٫GQ9+Vi:w#\t텎E/ngP*&K$3i-STEo/g넲TaiU[*5:f[igӡqmp6[-8)+n99Z&~&?_Ksj1J2ݣogm pq\YfIՖ'v}-;ӡ gWR/~/fkP(P_Jd(rFI)>"h?荭3M.dRY턐6Y mkOcIi.ޭn5kq`w@U^1:%zE!dgPcS rA+(aū[{֔~Y3qw,LY'?}߇:h/'3hQKiQN芓*QXuUN"K?:t4e&y(*?g!&V|2iOV6>՞O5n8SɏW2NH3mR<^~[} BI͍yvH~%<1u}fh ¨g~cQ& VOR1Xi[|/t]m[O"0v/-o e5R <3AKYq97w;xVL ";3Ik^]]OG]g(>(~&U?oXiixֱDkekWdllMscuEQEQEQEN!󦁞__'o /ah3!񯃟&Z[XE e[ D,2 eCH?ikΙc75)-E.Q1[-B iG9 o~įG6mO,OqJ$Q%q&We),iwTkZ 7t{ \tM;Heү|Kngvn‰ k$P g(`ir^7kW}_^|5um_|yG1j6=;Zѭu5Kxy6ILƼ<.^յE].,V7|:]&SQte4 6  8dɎ fTD|+ǟ Oy-o/txt,mPk($Ø%ZG֐/D"vx rEk(H2JX DZ/ND? 5׉Hi-Ե,y?hDg9oUIӣ%n밝I'%~fv*~_j[ȏĎRf3ΊU n>c.@M/Dlcseዙ+D3`pUX6EZOW1?Or(fغ¹3[;>H|d989/>><&ŞKT>e(XagF$RMiKuݒӣ3OW1e nk-4 xfL9[B]VVHq?ਫ਼+~- *_Z}Y[-&IV6lbaFKWxC5<+x3F[G7#u9.դ;skݯEw;4}wQ ѩcry$ln]Ah+^z'w Ӈ>?j_WcգYc: {HK*y%Xo׾$xwv#ľ~"C, <*zw- +H9W_gۿ8MC\KKcm5;XJ |yS C*dmK/FUkwR\N-^o-3Gmb<Í Q^/fWş[Z-bbjgyf)\A ƿ |0t ~,⼺'(P`,਎Lx 8??>ˤC M7&8P#M=PA-z*2sz^ֺ&uM7df/z?6t/ hvv#V$q 2ː̡@ڬ9~^:&>4>P7ѻwQ\mB>X =Y _O~_gW=6H-;yca}>c7hd> to?ܧq,j 66poØ| +FsoKleCUT,]+ď j1w,#^Y_qMm=Y|{}YRy+yQExgQEQEQEQEQEQEI԰IuXdT%S=2{g0N@ P)__6z?, 6 Z0Eu  0Fp΀?@d Xx?3Ctkڎ-:S!sFXłhѦĀ\CmK@z"5M,h1'm\\Y3&8܂bf+VMOQ..5{[iqQUQYyWʲ2 Kh-Ӭ>{0e4|@?tU 1*r4aat]uR^}ߋy|A(S}7'/ʸ֝=b(T:L$)0Kf"k.:5xGSҦ/4ѵ5Ti$[>'~ݴZegH hhTķ) @"tB<l|m}&]ck70^ijnn`:`9 .'9e}ǿ\zd>'X|Wuɭ>i<}K76l*~s}/)? tˍ(/XRO1#~t;GKeɅ#&9#2_,8k,']S%ieE& _g(Aݚi񷉬 > v4 GXe/"TSM],h>YK|_p-o[M"k |>X"}.bpAUN.I&M6?}+>17A$Q]$eڮ&T Ttƚ͝mg)lT4?u,6G|3m}ŚGo x+OR[qd2+JCc;MkkZuG4_G'nDI|p*$%$a{&m{4IgF4gvyOڳ|!_Ty.4HT2U;ORq־aՄ7Fm'ȁC>[11$ڲZZ8w/uMSK1vɶD! .pk_O[ %gfg@pK9'qpJN;VK^[7CW?hs?n?cM-οJLt<7'`R["k=HJx6_0ʻn~@yhc/A~ 2\^RȂW2O+FN98F|Pu?&LEGg0Y]lR9Va98ka)M2_VnWi=*ˈ<(}RNԼL,v%@[IdixjQ{zsB._|mq5A"deb+ҵ|B]#AmVZ1i)Ô\r8+YE-/K[;{kSodG/ڬGUiז{[LimN F%ܸ?V 7tٮa[=^mvv]NF^8RG #QФmRYnl|ɲR1&_WSB9} P-Ztz%tַ#oFU:v_$6w[e-.k˫Me RC`&Gi/͜m7 wG_~GuՒJV)=ŝEx 4XrLU[CiY%v_E}/QCUy+kUG>|9`{[ko[Hv{1dx̻d.꬘-?o/u-yxOX-m_(v#r(i7)^q5ڋ9& MO,%Լy&j0\۬\Epvvtip.4!T|¿}V-4 Rb}9$ܻK!pFK3{.X5{]Rt!ФO_/ i&;$vk~$s>R93c{5؉+뢷MM $,< gዽnhȉ}mR‘ȭ)\N (O ψm54 n|[Mn5aF,퀠aEiz%Pmzf%E:\iocFE Slv+f8|OV/oK Fq?.u~)=T]It20'}kk S_.-#;t}7A[^]I*IdX-ܳ;z&n6yq 8=zrk'R> kd4z4Vkmkb9$bTyaA`W&#+Z޿qKF替yſfq 45(i THb;)Fuܘcpm ~>xzMh/5/3@?1w%q؏n9ӵ_ CKhV-&pdqPAbrj(Y?ECj05ņX]kzܧ't6yBnlmq^%jxAgozoy=ZuvoN>U/O3uy=pdĤ8Pz S?^2!UhZH|3D3ܡaa3ߵeଷ? dM/J'Tbc.i T2unkAҗ`Uzwons^(xT}gO6/ t2q$^SEԫRr#do8-2|@MPkQ]z k d88+VVeSHv2EPEPEPEPEPwG5Z~.& ~'mׇlc8ݫ򶲺N!(((N)Kt~٭eJ$7"L2 fSOC^YQ|e&ڄkލqFGɼ׆~¾N9Yhik:]i6Fh=ْʀYCr~{fo?x [=lO=VOA"D./)%vH|bE~ׁtUkB/[/^2Vs5BK=/^Oj-š I4da%7Cqos⮵R4NnT7dyh0EO3 .٩[Fx$TpN֏,4 ~0|D]+QO{=b*6DxՔU-vi?1W[]e u `QqD*`y?:HuMV~%JM:7m0k? {7Im}4FfV-4Dy;Cm HB5q!M͏]]3 ki.i @CBM?X.FӬu/[}(ٷK4d0Rʩe*N^v?.VuwgtVҕ:q[u9+ >j|?rIuGg$eW5?xKԼK: ' 4PjȞ#j&aP O6by?gM:;?m-5m-"{ClF<9(o y1y8 bBw~0^3 hm㵑%+e`sT&"6N]߿T[~x#v*յ+Z%|RD#{?('ǝ:Yx3:ϢhaLO[B[F0Ay\nwƟb~|;76F6m Adc?dOEşV-K[r&-x 86XZVז][zTjzCWl{wn-"h!Rr^+>9V|jO:Gm3^"2߶܈,& g ;~L~t'V 5lpc>?QjZo mfzF`~]|_{x]j%;w9;TgP8 /Nn^IzGG.VΦ?Sn:ť¿&$fCC'- =C2OTu&xTLe5}fK?HK1ǢF2#G7~gC GQErQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@=I#e=A,z0O|Yg[%Hɨ{d)ȞcU]çL k7싣O|Ey||K-嗄pYXÑ(VNH=+5 +7ֵ?_B Cxwn dWX<-,NQBZ>nZ*=ww|^e]cŚ|Q*~(>WzvR0fT ̚eRWhpO77mSĚ͍͵Ế[;H-%i DA TW'|Y^Z|+ W"kF$YR"P }U+vO߶WM.kVʑ&8,m0Sh޲ Sm|edWs}{K%=oPj —Gkegq⑬rO_M*+%~6_Ǐ_m߈;A+Zx:]3ȎPA)`2o9HWUygx\CG#86y&?@Px_+^x}m!q8%Y9،.$L) i7߄zv9;}D= Ym$( 1%Ug}Wz>^Zpq\I ({c$ 0&BAXQھ{E23,(?3MwС4Mm8V'hKS|{CQ,i gб#d?+ ns7|vSP~ʖs/Ge|1bhHM|lŤi" .R f;H-?_4\+Ǔ:igޭ&yM~awWK_WߴG=0뺶o6S׼AF'l\8 q IO9tߎoEçK6I,!I@i(/f^S7Mi:L U@k+q\AxЅܿ2=>IִO~~r ZVS’X" r?y XĞ&uۯx{)Pnys՞G%Th3VhP^mQ\EPEPEPEPEPEPEPEPEPEPEPEPNN6?kQ9Ga m<+k};Ëx$[w{.O^rcFx3ğu:v5- )8mAFz `߉gQxT5+ !'<ǎiw/s3{h|Z>,$/- Z#K?^jȏO_axJVVHqX_RJ;j}U_l|G~!}yT.>}1^W>+xV{CG`i6$c y 4zWĭ#а#džma`=G7ĊͻֵGrYb x]EoR+rM/~xfM%l~$׿tUm}u;ϾJʠҾ}i|+/o|P֒2tm-YY瑘vj+ؙ6v!~y?mf_쟆 <%{x,Xͨވd@$+!VVAqY~S Btz~Hl?H? ",IW_3Q۲}ǣG8%MQ\PQEQEQEQEQEQEQEQEQEQEQEQEQEgEQEOia8$HD.ǒ3@%r rti9(K;y֤). 85R۩JGTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQESM$t4rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/gallery/apollo_lem.jpg0000600000175000017500000024145410276336034027463 0ustar madduckmadduckJFIFJFXX AppleMark            }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?P! ?gTUTi8lsʒK!?⪼ώI=+=<"AI5* ~"4{RG1~@?fqV-̃ ߹m Z;5ȍz|Y 7ʎ ˊ"zwbIT(5\1_[i&4[# #/m9v( nqq,mofppB3TDؐ15~2:rFJr5ȍ:_ïxf[,:v guy[Il5wSP|nk z$2_E\(H}$$nj欌'Xɦӵ%gV d3es);x|1&48omñZ11ѐduu#KIZ飅Jr_ծx%'DZdž_'GnuKhnaSٕ(/.p9$dVG3j!'Mqh 4hۈp0 'F9qZJ _׊bB?h%95ƍ^IXg'N-y.Bi^c'$ B&w3!~b;yo _'v;N,=7WEV-'xdĒFa<!p3EUkiZ*ў/?]:~LW;vwgUcGVBqMm4;ufy kN[H$=*-,&H(IeW~=е xCv-𷍢R8E%bh.px3vk֪_lm2'K2®ʃv s@8yRޅ%Lɫ=~aΫ xxCd$nEy""Jf{>8*l|/_zD0ZCJ|/-ݻ,q RD*~ C6LK%zʹf,Т $mŕ0 n= W-J)$|gq}q6lʄ3e}y<ڳU+dڞvCNfНʤn—A3?aO:|4`i4kVNVrW)M`īv-2~N23vc^-Wl5.[AS LD73h޿d:ixtRs7qZ,/U4/jw_-gr<卿W=~io|-0;UZ,h=[LuPyR2+<[loYV4Y tGg*`UfDvs(9ݏLs4rFcwr_ؐ7k7;:}<:HG?T;Ua# QSϑeMя_Qy#e⯟CNB ,n&%q2?QY^sHA- zJQ AppleMark Photoshop 3.08BIMC     C  {" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{U[x ռ>lvݴv8zK!m&$ 2]N[ Y`ńn #,|r8#F1֊ʿl[ $ ۼX+4̞餲̶XT(m9 qdUqLm+<\&#̣k G{[1X=2$?JL"?0( ރKؿGpK(M v<ݓ*H^%T1 ݁sڥxMwQu! E|}yf*JЪ+ȥ|Yx 288vbs;1BS" 7HFd>84,{² B#F\rGg<}b.#[g9 [󃚖+ǂ%is =jtKi(CzY#d̮ILdpO?Zw],a1[`B% zLW2UG*8o@* O^:UfݭbA @> rcV,+UФd,'N3Q޴[Ȳ2HȲYApw+cECԻs|eFvû8 A 2r|ScȊȌљc*۔x{9/Q^y0HL{J1c85J #M%{Itdr`K|Ҡ19 n*w,6܇(nHL.}ꎟ噧3Y,#R7nR (y "m_mVQ/<vV"ϒ\3 lTCܔ_CBjHa,"-z_  55GX%u\G9 έ#-<$7猊P:IrnX'prrq&YW{zfv:e`ߟ8ԣԑQ4ϴ#1Ft>BaA/#S[NP@$CȬBF:pGq2\МƓH5ؿ<~]ʗ6׶.Br$SIq5YXȈۋ#':0=`U2ˈVbN:uǷ43FXq| *<Ғ0l!03n(g۬0YcxϦrqa$-Ee8a?T=ra5MLέ鹉d)O#i'3G7 ]$1nH]9'5Z-I"w FO'HiQ Wmɐ@c\b~t18 _v|v*E݀J8R6ʩ\Ek$ܛHd08UAQx6`") @I %]J\p챨68*wzUUKW ;r9cϰ{.a.-O3EF?@ 9/]VZ61yF=89JR/QGr916#eV͔]3+N:*w;[ΡrS#cuHQNFqڕv .Ok2G11p?<;f "u8#NF0=xmBVx4h**_D9!@F9 (34.O3iIadT-1~>a-BgXÉX3@ +>ԵM{ހ BִtGd]"ig RG+1At(i-7!A;G!OJ-bتƁR p[,p 횮6V`T )YoUb!7}2d J@灌\gDb$׷evSU.HR8jY{ %.w* *Fa+ <̭ JN>sWwN{VB!|)d$1֩+3IlVf1QY3*pĺ c Z޻o?qWrJCӌ*tݥtGB^f&9Asx⢟B=%ʓ;6 aW $T@B/&Pm+ C9z9qWP1D#S1SK4${DHolq(NBJQM 90RFYJcu ?2==EH.I&!|ē0%z('P$1yXD-6#el h\RȒ!ʕ~h!ML겳]˳fcfvʼnpps֫=e^p2!]+e?S]ΐDfenU3h4P\E3F1tnZņEH)Q#2*tP9ڴs~nee$ u#1-đ4WId kf yL*+FN-%;Nw !8ϩIb HCv RI8><ܾ/cĹ eK'5Jo*)6px?1NͿKk'* dzb{ a2+4aJrv mռqS"9bv71+sR322#i,X\}яz}\MzeH6>V44Y6!QlAf-[Ɲ)$lJz~N:sFlʎ|"k60A=*kY[h#%6`=qEH96 RHL,R%YARGנ"{igHc10(K#W"I㲎v/rKj>Ƽ HRL|2ws!f[49D8g׌ء&t#>`ެrN;&I98/>uW"NG:!gf劀O ޫ[<,n^Sкu6sϾ*r[HBIXQwئVhm0:dvpnA$QEqv@*E02=ϸ7P;Gt` =RAnIJ۪Y"#i凸5I}.A@0 g2}jhŅdd>xP}:q֙&<1ŧ9$) yӌVe_&)vA>yg ,; q$ج[xYN)BÁi<5$jcl"$u!t F$1YE؊DgFٗfb_ tG nl?y z>f'#e cQxdJQ!_0<(|a''}ȶHFph )*x:@2[Zf!|>esf5v7f)[Gz, NA``O#ZFQ+i;?0^jmi+Jb̶d铟֖V;/*;B*4o i=QcuAH3;F[EMs6rn>|ŅTør 4 @:l#w6:mpVq`Hx8 K]C܌i%fWty$eb璞TLpQ62Oc4nIM궫 nn%W 8$<Xo,H& rz-]h6.I~'uHYql+d uṄ;&FĒz30Cc#`LncE 2p=1ӵSUkylؠŐs֯]]5fx.B@' J/QVMlL@ɀF xqJm-Ŭb)w sH`xTd'r)S@GYD TCa@c$ 1ҒVMlu-Đy<`7u<`AQOsq2EndfL)sxQNΐȅH~f;#â $֠˖XH略ʂ9jf9v!ʒ!# 2MXQ"й۵:YDgU~?qSmE>eJۣ4ʤp UU[ YvyjĻ2 2I9\4h2 aL@5n\IrjL<Vx  x&wHb9ɽlr3cð;V/#ʕ K &[p*1 ̬e&I,hCq@8@.Əۂ:g֟iwGKsp j-ĒJbc_/ 19IH"iK[ Pv2} z4QE H!Ȩ'I$VhI+oP9NrF*kXU8P@6@,8.cd6[5(;0VS>]yo5Q؇~&[)\)hI!9t3&7qǖ>7=?jah"@ ^H؞0temss<}zڡ)?"h@c ՚-rةR#8=}B47: Ңd# JsqҫK-C$jl$`;{U6G$1%&I Ƞ,v-nR` u:cʧ;,fb1)xJ{q@8:K;bI E /CpyPdvx6@&w9UGF$*w򙑅\IrN0ڒ9BGiTă0!99ǽIA$QFoU؆ېpۭEsm, ynL_xnl(*9j݊h \ˁR.3bOf,olEϹvA yUniZ>7nm5"-1M'Mtp^yd9756Q[.8;pϥH[B̻wIB:<8PJ찍n(ϚFWlV^{$@jr2:߻ <ǥ:fy2 ByJ'8sVjdS#ClpcB>k[ A)8mX6~踲12oƠS;-J7qd{$A3 qZboa=͐a3'`vRlL&K~*-RPVKǕvqׅޗ*Y#yݏ9[{wm4 .cp1NӮiF7)jd]F *(@9{OJu]B@% Cp T|4w.@17K*8Az;y.Y7|7[po|b"={~è!qTr(YҢȅF '#{lfzW -ėI琫d`s~PxۢH'2fv Vbaʗx#@kbf&AΟum8^x1}BMAR 2` ;yBH=9<խbyو èsO~Im+XVb jC-2)cc9C"~|'9|Z+@R).} M&01 !UtN9<e)=&(Lۘ>3#')5r ){61 <20m,gV| ѷ  0IV`L`X ;X梖NmH'eM%I8AVF2%"gPc G\梛k,0Z7h.67` *[nJ~%o;a[yVPd}1*^ J ;Y,0'a,COZ 4H?*nI|Ӭ3I]شXy;p+סRڽbs4v˸rY> LYgn: @xo%Ж/$ld Ϯ84Iq-eo)F:`t6l;Eq$ RqHFy142b-u($c* 59[̆E6RAᱜӌTQ\I=y"Idb>뢒V+ˀ?|Xd0zHs%1c؈0!A\ƕo<ֹ+"XLw$Tqȯ" q Li8G0 c_?$. rA{cvw-ʩ~5>7q?Ubm4J%xvE:3%6shg'|Sc>q׊u^̒2 z8^ԠGXƱ=>R1ך wo)2+d1!nN“n*m ɍ{mb,y/ tۘ k#y72#:rew o `6h`3~fPcsE7_:$nXs 0GR;U 42]f-+  1sBJUI@04 sXȫbeHZk16L{2l!YQrʪHO8L'6f!мs&7,p99LJ $'z$U06Pp<̑:wO`sK}v#"gʆl=Fn֥4BYQݢD;UDS{Y#r 1|=҄򮈛$qp<8?T[DRF-ݢ`sG =%mu gM4gUPݜ*%Kvx䉰LJ"L_Ӯfn ;5}wvN C-K l;r)]=$drK : Ϡac|^WY KisEDMsc鏞"vx-Wc $F;pϥzWW7b|6d-$>䈷ל˧_3m-a$LK8և&8zZKs,Q(3"pIRs]O}nc*"0Y#wg[=Bh%;B(.x Z Z0@ (rT69u";hi-TPҳ'U Ns'ӃP ,s,FFPHK.@*@w RyRzDA%-X|N0C~V+ӏ$V[B:*ۤp@y,. ɧyQM"l|\2CU/ y@ro ÃLV9P~٭w6 8ީ kki.ʦ吧h?);r1u 4k1 VUi&)Cq G|z=f8Ǔ*r3G2Q׮*"/[p!9QHHVvEp n d)1$a  =9Vf *`qp e3{bFI8XHKc[| Y /7-/?6N[(LհE*N/8ցآS '@#9dzR[f"srD̸c>5 RA xeb[ri jXb0_-!f0+0C`M5(&SHw zg8Cq 6M%OnyꤳJy/7\g-7sqƊj&UBHYa:UnD6VAo慉b18dחR;UR6@$OLXhd>\;#ZfTgsy2i5+` ht9l~S mqf[Ɛym2B80Iʷ9HM&+;:ʠUI`pd*i,y$ $ Ƭ˜(;OC6䰎)|9Yu > 9c,-ބ6Ɠnnsn݈:D 7DJQbN2TzC]9](Hء$ۡ栝VDv0:{Ֆ,nw\|ǒ c' prћ&%ڥtEhp 1늎8.PFHbtSv#DAnPJ c<z{ȶ*]ؖ(@d(n ҧXYd;MD= (qvz㠦iL, ef$YG }*Xα$Yo `㸒B=ǷzYd{;R>s($HV+iQdkʳJ2T&(岿wfJE%0 qҬj[Kqhp7@I9=H9%0kcHsFx5j6$4R߮0z`j:lUibsO<>X0.q9Џ/qs/oڻөO>/K>Ӣ4Đ/i_e (`F+v_ aoybEV*/+U(Fv5 |Z4o۟kfȻ~ʷH9`]'acwPn;au}| =뿲/xRฝ[]exbtNˀYMG_^D.i60J6+FA!<_?R)o)m {O&w6EKxNPPBg2ᕟ~~?btY#غVDrLq/ٯ%8mJ4Kr]Km)]_⡞Hv`!yl$)o 4M  *'}12..DaG Aog?Ӊ."H 2Ss[tke0I#uMA{yvem7#gvO\ y[eaXЫd-tڂ>,!H!I̳P)hs&L}uc d!0K`` lեq=O-,I(wKsHNV=`vJ%եBjb 23LhgY LW(K Ap'=ZYrKu%DxFĘP@UÁBe"s.DL(#zf݆Y19`<@D{ vfC c gލfqO)2ڐ0fw=1Y3+f#3A؆qsPr}i 4P"9T,;M>o$;stGmkl,WhFLx:{y&,1T( jӰ`rʊ@sA ۭVIYnW| C&{ؚ@IVV9l}G$vNI19鎹5h  r tIa[l bf .XL[q-xCYS+l`HߞwqRR=,QBJH6 >ƒ<¶w(ct"Gml,r=&LB䜜tN+s6p yBV30@OAv0$c-ď1%?p6Lu5%昶peY1Gk tnVyQϖ``y"V[F+lUlTrp}Tqsu&[PQIicn$qfpPUA&"/ŷJrqP͝9dtgIX Ed#9<1v q;l5"Dʤ*l:؊ihcwxNĴq}܅*mYky hlQ׾Q,S.!E9?%[%3M[S4yq v >-bvE6"7>Rh(TDbWVp*_zr8̳yL.w`qϡi\Esj%?yOH#Ӟ*+Q+RfHYcm`xU“8jYkH M1W sv2@g*`trHBJr8[蠓b-XKŖmS۸x&UEkfwEyۄcT>O_SlZUbwV\wݦGQJxíKRo%]SM,q 4/.қwMf-8ܩ#,JUQ~`O##{w[3~ʺ62xľ7ݾIy6TKKc=䑠jM$\ypef9,T3u3"jȧoO<̌&BrR1Z:¶lA|ѵ |.6A8B֞~oXS#0yʱ.H$5ˬ 1'WA692sDK!"ZFꉍɄ+vǮ?1SZh[wFG* 1YPe$.ֹdXAL`TX4+ [plA`3r9:\[iV1|DVce$rX,h'x\[ڡ2Kg2%8?2Nr1Wl 鈬Q*(-+l)OWdDu܋9@6q:I#yGI0bPU}zRys,eq%O9 8qJqZK$w 6ݍ \I1SȂ݀Y_uOU~t _hw_*ϡ'>sQ2[k"L*]B9Dn", <'q3 e΁XVR9?ONX?Cn9$2M7 tin3#1Ãp2`px$rN{sU Ia`cd [王kD_6 yʕA6#v(`K ڹTHѶ[g]cU.ekSr$* ݍ (NA8oNTWw2[D+oL43*>a gP˩ϒ%x)L$^9vBZ=S|l|hZ}*92ʍLWG ŲC0qGT#'8=r1M}9 kiƮɤ|(չ.e,"8zr+KY V̲gmNA֟-zۣTW'-&I 8=F9RJvcf #gY6w9\*ٕIV&8QwDF9e)~\OIN"fuK"Q7AʳIci )]ѱ`@zSUxeeQ`˓};Ӯ"oco(@ c gR mG)frg8{v3k*M"2O$u=q{k6ʅ¢0*Ӯ=sژ=D{Am"[˃8yEBgZ Ȓ5]bpI-X皖;c-Hn㘫y۷#QCg#E@HnG*h"IIill#yW?1gy4QapBr>#9yupQKvA=a uXbPr7sZ+S_7υZNJ_&nN;@(9,Q63W3m朐.cq=#.#_L(ύeƝxAaHdg+\WyaԔasw' xJ?^7V<ֈuMž"["q;LTn:zuU+7>|nkS-3,|Ox6?ih-ܬ#Ĥs3/q ~1Uhvwƻ᫑sOM$\'X*c#$`k +'^ \v<3)Kc<a,ycҟ,emXDsJe%xLtՀd3Zմ 9DI(a LM"4I5;ҦtF9^C Ş[ R2?<ȑr<P;p+NX pB72r9O~jXyr FdE\qNGNAtD -m#=>G>|=zEŝ?{F|);үtO G_‗2k07[ [v#9>A \06nla#I*K(al짞o*(-M#3+ K1 :1־Em~%hoƍ#"yuRM pm4`XgV8)Vov|s,tROL3^_|{޿>x>&;v^g mǝ}] oǿvub(9 x$ Z@oOۏ'~?MwK>|EHT%}"oح"7*V}o'ᏉdO>nP Qx;9K)gWű#̅BvlhQ?g=>/𧎮-F7WRCj\t7j$aA)^!Cl^6< _C񽎡mecↇjV<61ꋫ?$Z>e~_Wmol9OK:|47oxPWC$1JDatBNCѝT%);.e*ߗJ."HLjrpĆd-Ss|=W$ٵ4JxI<eR 'a[n>!`6?x۳UI }{$sSdxvh#G$1##+$e2@>M-w+,RnJdX{0y|<@+$QaA=N=YaVi.Lݒ ކ){/,R2qrHQIg;E;"rHbJ8'=j1.%Vͱ b~n9Y*VhVK88p# .Qn`p7d@=;O2[𯊴ZBm(-#[GA"#r89c3M}`{QTpIܟ^:WmڻAGkv.5i_,Xn'w[M*{<ک )7e~ς?ho:2o5!ڡZy*hki jDVPIqmjz"ƫ3H \(?h3 K( =zH"I #!r8>ٮnYMHA@?iU9|fU'*dA9ljOfDKL8|dec?>=))Apmn(6n1P 3ӭu(,gTIe^.ʨX8ϯ2. vw˿ʻ\䃞:j2iILfDEF=w%S\.##.O9 䍝 ` g\c N[Ԓ2$9 } Qq>;HWDlj =Iږ-]VDHlwgԗFmos+õsoH >H~u o~sD!0UEcpGIvbL\`#H|ǹS%3leR$bȨ\0}:'H|RKTCx$SMkf[$x;g+)=QK}vž)bUp8b;xm݇ F/$drL#e%Qw,0ß:i-F ĿP^N@R4qmDA8œj.I47͕Y+g z)-<~M'_ .$]c_?{gmknK9pF˲`èpsP9_?U` 2#?9,m2`5 ϖU 9/j u]O(i!X,D);v ' )Pp{tWQ<+_&C^#?}2ǪGQ #)$e.3 IWJt>&j?nHNyosJQ#HURRJ`2굟+n"c"&,mb v ۰OhMpcY9$0wEl<¡OIdwYҾEw]gF/]YXs |C 1nx)Kk./ JfեYye[}]pH[Slv-庴] 2G-E\:3"` ,2PhFm"˟*U>O8>w |N-q' ծw{vh>o3d:ТŢ9fI|7gXm#gW3wp>c)Dυ]WWY*DLsFb.[^? 2i:[ڷ&uT&,{W4?gG=-GmxtyxU%fƬv#u##$M.߲6||4_?|>׺V f:|pXOrM*읰4eTWxoE[J_W}V8Kgq3_s"G$&47 #|/7Gɫ]eY!14/,nd `ⷂ~оٮW_x+aGRY,DDjV( NmF|v=,4kԦb_E=?xi(i}/BӾ~z7+cŢzmw->%m=NӧI#Ynfg_6qΞ*{2|E8u5~FgeO&oiߴWß.:W;Hum;}ƙ kW{4~\QQ$Yп[/qai/|O#_IۛhbaRܸ8<|j}M3Ė x|:攉G-I- C0/Ca:jOؓ~IWixS]ii֯o5˨[n@Do#'r\ }<+ /fݴmw>n/[U_ 8 ֭|5}@ҡ EWr[=ȯP'M+)O{?hǾkRӥZ} [,lX3ۥrV3x}0-"4V''-QRsWO3د D|Gq~zī? QMHP|IBKk'%g¯h5ޣzumfKw#"ʍ\~q~X?W𷆯5xg*c˶5i$a.~S'OGKnExz30$II!)x7>ky~g\;< b*g8-b|I Hz{Tvm r}(2ct G#/}PrT Py{bX ިc'I<|b_-)'z䒸 Ǹ$K(Xm΂V(.88pOO\D Qԫ/͞s֥J#K$F\Bon2jct+nV~X T=%6LI m-S$ :JA2@y8*CRc"3H#O-_8l/'rE$@9<%Οt.bc$9b1Y`^t6owT( P:DIgWf M%;g7  &{/%yijuٴq` r)oiga#P6pdq@Κj^건K,I&;w73X/%mwNSUVIKc*BЍ8i_ΚdVѢa  '#h*[wGE['!w$h { up \+)1*BH?MoZyW0T Lbq 6G3p)ږ@*[H- eEbd.a\TL)01Ip8l͜ ڝHQLrW>a=(#% [hJ$ey=VE Vp_1J~Gn-eT lE$0z㐤wD72Y2cC#xQ0;5睛yde CLL/Rr:1iQyJ\&Xn\KՖs*c3,Aq+[6cg$lS9=+W,XOq[sc,w*/ i By|8PsU[ĖEXl@Pێ}w~bR] Z8 @9c9d5ck!XX O1$:КXMc d BJp~l' rCiml&&&xi$$n(iHi 5×2 zӳ4NȊAr"vl,8OmVPF[f 1瓂)tU1Py3^7x 9l ;A<՘=d7!d?43,HsVwjܛ{sW:X-d'z- -X6ye#'9A%m r#͉ųVFhC+$q#ݖ#GNIP$K-L#r *vF׊w)rU}2F3h%#n YK$*!ڸIPssߒj%$_u#rpqt9#I2A\Gq%&CʊcU:`զfAygwmאNX"1#/ʁ'z2f3~7wOدǫz7$C\fPes)wv1Ȧ^$(@~HdfnY x'Q"[VƁj-Lok5y,nI ?mğ-~_{O}(c4r N _$Z\4$(LxÏz.?,鷷hťئ\$c ,Y{~j?Z_+^\vt>xcN-[XPDc\7Pxை4O[Crj:+#:gMtlD<؝Tۃ5_IVnen4h jѦf֐IJy#%eWFW\1ؓA?a'~nbѥ[hnYnV P4Fŧ$/E.c?ZW[t9-?jƜ֭ [[[}DhIi"H8/~ܿ¯~<}[_X]O|M:$-y%Hw P,-ş6CWbk:nxJ=5)LY[JX]2};Pc-ˈw)@bo߲366yXEvG wR4_ 뚖ak-Zkl)"[p>ŖG76yܺ|byPrB1WOgTӢYZ\Y[[;–(u.ńagx~զ#qeW]ٷ|wt;r]h2r]^(V{fu#:)zu4c:gߵ-/+7YHqYc[hw-'R >Yg7|[[|gtZ/f4I޲󿒤l8-_o<;mί1k&02xd>4s:^6mxbQ^]zPKnq9@C aRC~NMgEjo/ƏRh _Y4j.ĎeQ+eTGBp6ryn>:S~ >,lZVw}aH\_'w$ڳtω6Z?pmKX3pQ)YT 峇 ~϶= "C.4ǚwP. 'y<g&kVGJVCrqRvvIGVE?,o'폧[\}&LO1moI-#${'|_5@BlsDFd2qT4eA5+[9_ڷ/OY.VsE2y=FeT8Pݴk?j/gX~>h)қLQlӞTLCX,96?wɝ:n5)EKվXf#),5r+' ㌣``o,J$Tyql`yki?e_zoړ[rEăÞ7L]L"}I^i QcK1_N~~/|CN[[izFkq%-M'Vq n#SFc+ 'l-kn Vy{m't-͖P8'ݜIhB,xe' [Afdr;}u_}3H?n+}ZDf~MNy\H#<)!d~ߊ~пo'/]x[1_ۖ&+fԡX3$඾:%|_CցŬ]sr:mZ-,VbTmpqMK RJWK>kk {Ԅ$-/@.3ݺT!8.!"DjÃy?J7 6V%$XR=*3LNBirM\ ,qE6epA='?1ۻ$~DcrO W%"&e%e2q(ӌf%2iJ#B #Uoeta9< ģ/87QJˤjr;HA|UFMŤ9A%k`/i.b_1LMqQybNqéGaI eeڌH|Zyr$GG3>ȩѻYQ=I7TfMsre/(8Di\XxR"c}T-鞾rIkcU*_|DayjĜbJcQ$[*H#}"`l`׭K} pȻ7pL#c c#wAf4p#"388\n+|:dhn$*X1IRԁ`hdəp9@sޡ:uET؝SI' xNV[h1O:2>fys֖x'ZsB)}j%Kѵ5|mGu \3 w0 c lvR6apOO%Fy%$鋕ct%La%\@@=n=.غQM)c1ii~sCѾ֙a92]m/&FI9Ƕ34ՊћTWLad'jՉ˗>lq<<7';A;c+GrcR#:]Ax~/zeŠh֓},nn^U'E<1UM;}ouٳxX{ r\m|?'nnlu6[a.e3GG+kIZfYY!FD;2cOk,$=/LWӬ+8m(oIW|q$+"sWcg=|JN\t+BQ.sLؽn6N,0 @#[ t$jƺva /Nఇ &mcn=ņhsom_H[r!$Bl?McӢ8b(Dr:+$.6=AEbIAXaw WK}plֲo*!|_+bCQ}_| 6~(.l<5qpb[4o,DaVe$TWx_Ŏ~0\8n|=2y6ԝʾB7?0^] '6w8]zJ܄%rq5_j>/zޥ6]\DCIq$"P(2+P|Eu}"H5+FhɢyRsJCd⾛ ӡR&įtN]~O?A?_>x}w.NÚmAJo[-X␔#Wm~9o(??co.ui>̶q4HͤwyYs}O>'kO]h=~jVZ,բD`<*$QQOt'ᕶxnlc6Ԛ6 pv\9USwi*tiROPvtswc; cPNH > ZϨYTs ,$lo'.z_|3N&7圏w5d V7l '$Cko pF pniV1 2Cr9P}+?b3ҟ/[i^kxUbE'O"HǼ澩Enϊ'DAGϯK}O0~^jMX>Z˛ZEHt IBc_>%>*_m5xX&fXw(0#⢲o,Hv`=qV o=.ɺ /. 9zSnW_jsbi#o? :T $[:|crzt w+|9bcP h~~N;szT6GN 33Dx tC ] G9}yt9zUt(!  u=1WLcGR1;g$mprq1R%d@oH]cc<95Z;tWl3 }UBeg((x9J%m:$%9櫕=[$ K̡B1,0OQIxA4I.&20p<.¤m'NڨfNݼbQ _1Hܼ%@9ʯB6D2P9c^il?Ʌ*pT/9oN nyٛ*Sy?|}|<ºt?~]֐&q 4$tnf)J&=Fft+{ :>5]@a`ٹۂ[O ;N=?]M~²i b( *>jq—~$diK"- ZIA ^SB_>ƥ,\**iuEoo$΁hV6G6]|@#"YDRνv+9ÿ^7ՆÍ/Vz"Si-%HTIg`؜\w__|Yj9O%t_ A!Rwk 1ʤ,ȏ ?hωh>'4/zR2] 녒?>ErYK0'jδ[Vj2j8vuӹ p|p Уlr$}ٙeVFWmY lhV s.lȺX$P@JU3uUo{c+HOVi7᳼:$Ȃ.u8b$(yzҴZovGJP&RӯFொ3|"AōvO}cu8ZH|PL+ֿ-xQ!#M]GQt^8纛O+n鋛7f=[HIܬOLW鏏|ge֑j'^h(f/+H!%Z8?Q> *6nxwzp Aboy t?1*JpRVv5 ?A1e~OMJ?EtȺ5/ ]J{m)l*F$bD¿t+BH^1ei2!>D|E1uq[ Pe~aFKk;'u{enyM !K`c7G_I1/B~i-6_VϧhF혬oݬjYI(^/a/lC_,|Bվ':jϬ]^ji l('!PI9Fpk]Vwp[ qך8Ѭ!фeXbe8$2:V7.Bp،8lp1hyƤ 4,% w6xVQRBBK,Qa߳H;۸[& vDGMahRL&rB2xZBs"R9 p,d4lYDb) oi`N~ǥ-XnCInUl`89`qJnGDʌyq- W奫\ۅx#`Nx3׏)s ŰҡuQ! |Aisi5/L#@^9*>b[ 1SKRk eiEޱ(Mʬ2ɩـہ}woo-h#2*<tq5(Fc+ܔHG^E2 .vG<} XկmE]A{"Aucm<''Qw@ b܉ @IvW!UH"W$fR Ov]2FXgH(sK}9L6@IgB17bSZ7"WBvcXdFY`a0lD \Q;qq;]G&eb[8῝+ľHL4d3ӹ'r"-"B]wi< =i1$ 7!b@ls-pK;In3՗Aqږ|-QV@ u9{뻉ibWP|wW5bٯp6pH95LE5*_07 x?#RF@؎e@}Vf69?v {d ydx6FIrm;%Pa;Uaf@\e߿FѵA튚>]0wr}1S!7ch[)+0r*8mI.%dRYde?.==*;jI$fpYF|Ac'c gci^.VMbSzjwH͈9Ey d u*n`ci(4G+ sv䌒_vHb&?&V$AOG'3O!g'?M*alNPv 5/C!`#@O}ڝ qpG^j6Zz* 3O#9EfG<%acSrM[ž x_0k pAkʹM(23&t>*EӴt`kcqxdhՁ"582q` X{ww m~-WbHSe+x%N~P8Ԛ/y"b1iDaV?y@vm|xd<~-$ ^(ܾ͠d{oٱ&ojTF8J)sQO\=7-0\_jHٲhfUI2vY_/vQg>"CKVQsqpo29J\hؠj > /y'g&O!w@M xYrefe?s ?FW{?Þ3Syv{wg1H+Klrh1~neقE;}o t[Ε7OMQea@Ѵj%f&Gf_'džҡ?boEH j:3\z'WwRΨ+b1uj,:w~.?'Ywl#7kwZM=#{NjuCjvq>o9 vwcA¹fٳ^d/~h0]s:NӮuyp2z/Vj_ X<-ԼG-ƤA {, mRLDUk0֫dzzYpyaGz_/ك? h}_xOKRL~%xZH%YeGtB0ƿ9|u|TGI5}^}SWe73|(8\ c׿iύ#VXx`]WOHcެ-:BUX$\IMS"ܪlQ WM(Enx# We.U6A[#}0Lɦ0I`0q&ݾ_ a𤸎Hc[6ۢ%G*{j&vwR"?1@X 8➐ƳEw=۩PD&! $я¡ kWԪРdݒX`}j>YisY 2} !D*06Ү*W$ 9ϭ]ڲX,rngg0b-8ng7R,wu6@ d~#J#f^F:9Ru0A7c55prFqpIVvܷ`({de7&*>UAPsI$ѥR*FAICܥ:B"1c1=a=>Cm*ԅ\9 s@L}ևYL2^XWec l­hbԦInd'\&ߑQ)C0K s4j )xἎQo(ao(U7ef܊J[i ,P-4^CqxO+KiUQH #y c&HHf+!1 sVG>wyݕ1`R =GkamZIi n#PBC1n:[YEl,Fppy">Ie3v1Scf7)e8 :M rE Hwc>f)"eӦG愓qߥ6fB`T)pےC?-nMݩqD+&2qƑ$̎'P)T/d' ALCܛ«bO6lc.ytxt[r99cfL "!IQbOP;w^I,bHn]~χ'#,Xߚ[7wsD4h6㻀O9QQuJ8\U}~p KxW (0G%=1֠LaR8z:  ח&ydV23=OjFycn8aPw <<*:3o<9SN͑-b.fVFBo8p 9uQ }\FK\2l, ;95 Ɲ:Nr7 cHlK6QsJ|gOUY,#YJdt 5-֠^tI$d X1ӏWtCqэc'Y(SmZdoAsb&hgrg)mAcgjXWWChbY0 B`va֍y -r:6AH"YZ>ymBW$3y#dcNq׃Z~LY.e@ʭ$ $dJ&MnịM0E[Tȭ79"1{=JWK$Gy6iouG,(&inUv 'pk);x.oxU&6to 47I;*Z! %HwW̟g cᇈl#+ƾ觋HUkȢ "vE)~!Y!꺶-Z>#L6G%+!99ִZ!JWl_۫&h>v<=dzOtPUf OL4xǣ,w$T#&ēc7t*HP ǭ,N9s'G S=NsJitm. ě[˒e*('8:6ɩrl?y%yHl`ed' dHWIo/vYt:hW:}HEvH巍G-Epcfy$Gy!s˜)8^;DQ|_Dz*KyЫ&?1dC+*zni:4cY$pIDkZ_Xz/ w7wFPv$_ҿϊ:ƿt-jI Z<481+);nIeV_Ry}kV2/$e_/, I s_c@צ&sqwh i;|'\䊪0I6٥)E j:g>4|0lnK7s m ‘Z'pG}kwf Y|=WD 1DmH.knX̑ FwwWG??YDj? aksu G3>ge Imoq(khcvhC|89 35pd՗C.;TQuP >*CL@bPwLυM/ke-KR;յխZY<3\9Yvm0 *$# ko#a9㡬߼<)CYu4$H^O4"dB^z$c%r^(Z`2W^H:QKe;S3oHFIY8UaFr0GAJ),Q o ?%Ē[T4;cM=3'څfmwmaj9Kk+3O0Ȃv0 8USڛF E#n2a8-Hѣѣ# fN8RF`.v121' 9#<1ϯHAs Kgp.%HĠ,7VS:F2y4a= B`AI;{1*N4lvh?8+pA)=M)wpY \O˒0'LvϨ,O Wϐ><܎F=&[5\crnvENwez9⥒%{x#vP0Y sLӦwR5r&%@'aZ1  N} }$mrۃ̑n8e'<'GҩB2$嗒>sH`+aݝO@X|pH,Hh lEP 9=y7'uQŴ q{9`0$!c@=FrNF)Ii[T/ r?^Z8ծ C>%@YI?GҚţ'9D2@ |ȥ`Icl|[z9sjun=I."H5 WIPNq*[.#ʙap*|O'z fq 8 ƻH8= +$F*.FO%?Zl slU*dQ>VUnyEq5奼G d c4(YRYFby @du95]Ychn)#_~2:sZ{YdK\g>rn-{Sf)"PIS;NTx==ƽO7%l.>QN9ߌ@-Ź ,$8 g|pNOOZ[[h+ a~l$u(Fۼ@*Tsp;Ib?2+;Y_- !Ϟ[ zZCy2n6wo);-+hi/hH #_A5`3JLJG;wq\̑eݐR:V9.*gb7 p'+^kk-m+q z{Tw7J2W+0F<?ww(r26wNA8'*O&U26݋nFӖtKlmqΒ]'fQN0:ch榾$DXfbB+i* FIvAF1:KU6W(.|C,Win~nFObJExy]# qϱUخp6=\5bag{j.,-Z/*F@r~SL+{Eana"f-vn˹M{mRvxIz^GB`GW؁9\y7%Cwjdh9+I*trkjmt#aJ|@7psZIT hal6>D|7m8qHHRK!Ub\H%U|O/uBW39is2e "/Y#XqIA3KZrV1T9%qq\Ui?g=N=:֧=1Z44*;) +<OJk[kU|쌳eETcxz^>|z5 m4_ m< CX-[ľ|*gxxJRݬanc䜫qbusۿa/#'-şYᴵ|$oWZ%NHMMnIP+TmWZ ͵ėb]9쪱 v!| ~prE{]\?h#dƂ}v<3D碐]S峪\dWC9~j?|&4cP7> ͎eo ˅c$"XXƓ4xx4~,ce$u4X^s0s<FS^-E%FO@RfISWO/ a{x8o[0'i GjsZЎK=Eܩˎ?)e$;}2?uaV0kqo|CuY7f85Ee 8\eWi%I 3 Pp3}q}-ZԷS3 pFr sb+9;Oߴ }Ҽ-i>Xk7AL6n2ʆR?{}Wsu|){-Ԗh]Dʂ7kspYZ ?~W/¯ !#=SNLDVCC #9 k4Q[#'Ce?3ɤZoChg&uǨd,CuA1P՝5{X? ^4➑7< w Ϭg+ejҨė;Ldk;Z~V |ӣz5@>ĐC6F~|u¿|LռYÍ[mv+6+iWyQxQW͡YI%,jKk*fGёw6 cx൉5'*rV?YJ|r5;v]CvoQ>⪮FJ]*t'G7O"ּ)[jwdKVh!atqG:QCGkdn,2+Ą#0{o͗> [v?][&}N]BQ<+H-Ѐjr3cW4.n>r|[|HYM|o_ gc^h5Y44F$7!c0m&eg?g7~émgcl%!@BH p7>|t´ET@ N{c=f{8&g&% <ۉvP[8#y&gG;2nGr\qqO:9pŎG\O4kI m7e2Fö-:=*# NS;HNb`\   x5Hn@ 7OSa"Y@ oΨbE S#\P9SNp\{*K,0:3ŵJݕx:Sm l{ĥ YX0>՞L$r#M8 $m@;= _{N98H osbbX BĦm8gȍQOYoNiN%m%|dBX(&ER?&?/vrX|çڡeY&ܤS| jpa9SK;nIؼ/0?z%̢Co9XRP峎w%Qݼ1r9<DZT[m;ͺ3 M,`Q Nsmbd)!cE >IG Y=ˤf,G?sozxOPm^O$<8{h2ǹ$|YiEk2>e(w,|t9mR ٩.)ϗy |]?g~͟^}5/1''Rt K3|*ƼÍ6le) a'.~jC}ԑ)HK43l\K{%b,1zU|E?ucՓPvh#H5NXlrXNj Cc$K፮Dqݘ_.B w`1Ҽ6RG(@c@ld+Ѿ&|>h^3)A-i.tk1)EŹX `pq%qom",~H8mPT?0O8gz#1t}F4/QclNԌG.xѨrCW~ϋ?j4f^&%oWKq-QOu \>KS~Zl/|-⯋\Џ+)-u]OyHfw1F؊r%o+S?ٸhZN_*xz@Ӣ5i'쯡[_J#P]9H=4 Gt acBY-R ᰫc :?2OK_xYoB-.l`{{$^r^Md}>hH<1bJAc\6b ed'#9钾0VtSA,%%V$p݂_\V2=„J<p9j+afۇurv07T9M\yhv $s%U +9a}4aڴ2vFڈ2Wy##GobF8tGT=8=G|xm 2; 5Y7a,6qO;APK*f^>o`F:ҋYi^iN%)(fmMr39_],oHR$UD<Ow#RB g9'#z݆*l[pTW!C 896+:0ec2xrQSClQEYZuOVu fKlȳ)\`'LKw$L f*$*UC/'˜sy8Q%K BUqY;4sˏ.U, 7ih.Kʐ-fQcNxe-ۉmWaVU|t'w88-G4B C霌+i<'##* qzƫB2|Rd,@?9 tdkgnr *Hr3TX) 6jܸUO VhRi݃pHV\ pyV!3Bǘ n@ҸV"U8<H<pҭiE|<HJx(]T.09ɦw\I }lj>d bjŹ\5TDe)w :FEK=72A"é*ן*zIf"I!E`8z$RGX^*y p}ZE&("nJ򞼎Ag3LhvD )[#8jKXV;:  Rr.`V؂ }ʡT+dgM%̾tB:ےFs'sOWZlKnr1uhIAi,4ӸB FAӷ8" Dk*౓we폜>=ݡ28@bgpggQIgIQj̊4,{ԮKsM$(FKG%FWoaӮ 2y$0m'@Z 8٧XG r;v0iOz\E+n!R̬ 8<3g"9Rvn!@IV#)M{i٦BE;9+tHk 1S$o&fRpz=\ Y"YĴnkc e?Zm^[&ؔbCe03ɤԒkxeó buA=Lt7.g|r*w]pw1#jSwYñw%!K nqRo"q_oV*h j_j~&2~ߵ%oOvɰ|}H:VkiRSn7PpaA|[5͏M~mk7ýFEn,s&&Hf(I~l?P ud1D>Cos!9kDN\* XW^:=|&WfưM$- 9]V+|ĕ_?K$>&.o{ω-n&բ{ga弐4$,VE6iTo&%^ 7y,OM*OgCZ6hZJ2e#FpVV _ 3G6ndԢRDd%$P@ni7ӢjzJeҍ2iC%wR2KĪdobp3 oٷtgđhĹWot[1&hPd-n76Bnɪ\UqwI߄_w9?iö+[[jqq[)%xrN$m ׼3I`7u$8Y.7_߰7?#W3xCZQk$s xZoqdE1>~>?o:{^}ޏm HDt831UyfUDdП)yKo|N{ROZ]t v!?#_[ߝO횄~{mҨf#Ĝ#O' Y~Շ3_ofq qO6Wm)rmIGj#u;`A"ASElȷ$[\pݎ>aT].6= Ȓ vg2)q&Mo Ǩ>gΞ2HK i ' 0Tc/:kh #yd kd8wQ&;Կd2 $Uxt5oCobiL_,K, J} ;r2x\ػ0yHX(vN?݅d[hOqĄngE, ҲFx#㩎Cl"MW=w=( bP #e1JwQY{ic[)?1S$4Ew ;'8SW-/mMo<؏GWy<ҦIdwW4@dܠm`ϭAqtysX9*%cۃ.=zԺH>Ẁ+;A'5 dm#K*9F#nI>·;Vvu޳toXceV6n}i{In$K#4ĥ؝ŏ88'9jzC;`Iqy_) FLD!2\šh\EYqF>H )Ǧxm2Lmݺlftāzr[0` %h>fIg`RY|dQ`.1e#7%U 8SL;4X(DB>4d$Dh#%[ [4Mn"'m˖ݐ0ݾ\gޔoA $S~ ,y&@$]p$YT!%A*C9y;j+ƝiUa2순p0O` ,V2R#¤v#@;qih5$\7}N2Lg8feDOKuʐ1ޮ, $q#F!W aV8VvshLTa.p܄yTN~sAA5iJZ1RPdFWzjGj֑w,I9x#o4~;dp=E2 n_<Pw+\9$z%@0~Kisz2s! v fHFD?'s ;O4tBI"Xyvs4c w1XE1$or@ py!Ee-"a$ z6A\188ͫjkk:/hTCssҖJGHT!x8x'ր*g}r|a(`Bʀ08O(Wď~Z?앭k6RZ>/YufXbtˠ's$j l7NEx_l/Γßo|!C3-=_uw-.!O!?{A~Aj~An^1GSʝ79[Zc ̶W9"0\kݗ$҅  w4;zSOGRi?iVwZ'~0_xd O[ܛ ȌD8~Keƚ.o7. jK8 41&8J_+;Km?['[֮#K, xϗ`6?__:ŸtWcĺaZ n0.#Ybb^9%hM>hR|ٯC9|_ctᇆ.īۉmgEuUGH:_ )_4 WVNu2\\j us!,F' p~۟sg]|׾>1υ:7|Eiqa69ڐ QF|6lGlKa e<Ќd`JbXWs so'ï?7Bi5=/~.4ָI!x4n"l0A"7?L%쑣B?|cVq \ūgZOQ+ۡݖ6G{6)'r} M)q r7w9^rWy~Zk6׺li^\kTiEE 9LbRө=Pa;=wu-!:? MfyPA&QJ#;u-x~8qk{#..'%>`0$_lJh>Rtk ^S0@(nWҼ>SG,p++8rBpy"rG%II-]"vPTni9\nž~b8LwD Bb߻ ^P t ;ԲBRd`g":`aN@P3Tpe74!cCLI٧QUaV|}ހ)jEG0y#*a3/qڄi73M )$\L9۟' vJuբ`c$:m,;7``7hUD;s7_PGjq6Z+8_rG<{I]m$|BcR ~v<*I-\Hedm!rnQמ.)tLr`]9<sZ =ˌpض9`Hc)ѐI^h'[积42'unG qs߂ *;v֗+n \6@Y89-<ɃdvI#s4;w9im+Dv]X`b m;@zVdHd ʬJʜ`I%k{R\DI*J<Ҹ&ᵴ)9yB`Ks*P* `Qi4KtX&QP$nfU i>d9QzTg " C`C_9*@RՆ ^ZY`c Fz0q֨㸶!I y$*p*` 6H*|Ps4)S2dY 8RSHF1jbXPmK8B2[Hڥ! Z""IK̝ 8~`Nդ`~9|rxh%ڤ&cm@Kc8UZEe|+O^M*] Lm3uʨ9V/ohOD%ugK%b^<.p8<1&{,B{w+dr,HU29⥹+<-jeĞnݹ8 xeX+$HۂQARR_*aeR6@dʦ)bI 71y{0H dQۆdHڧm-ͅݑ O;N̖O\A [][.ĩlbh9=Wl'M$X ,qq 2NG21|W$qQ ח7r۪BIycy"]ʌxK6A|>.2y* WȻJ+yxdq=g8쌍l4`G_Ii~" T]j '3A, \;FK)|6G)D 9pAʀFw{c **#ly)# NA⮣OH(2r0wtzU!rh:g3/9یApzUnBb*FLznbh#YI4."CfYgbvqHa$P_.R+!9Sl%>"bݔ 7cXGZXB,sz#bBiw7vHı Jgx& ( HaI,1$8 .Q 09#jtC\;rA9AU]}$Fxhd`~YcFe*#?\l.!29݌8<23*JJeU,~T4e+,HщʂhRw']]F&8^QP;ݵ-ߥ2͚I meu}z]ۙ]ls$d \#n8v6̨H !ez~xteYF<$eW $ۅК(e+ 9j dR(vȌ$DoT+Ii)bQzVݜJDD Ͳ0vNq=*k{n"fI2ĠSx~(%|pf8ީ%c3K*Zj I2&֌ 7ZYԳNр u=jp"b~^wG`**q߻Vww6O8ssI8YLDɹK$ב46 FzmLnBu;N AI an$l2+ε1[ڱ8#,|*`{s ,7T PC`w^⬵vvہ+w~rߡ&dh!O,v6Z%q}&A^|,W١;L1I$\ ;s$m%Oul0iCTeΣm9%n~Vݕ 1j{Ր] 㓚qqp]F$A;# xh'gOk KiPG·;اTH4.h'F/nZ%ft] sԝw;O UWC=i$ Fp?{\x&("08 |u=>Rب2;- y2Ew1fTp'.:g3i}_*6KhܖI[*dsR"^ \0S;( y5&Xbo3%3[18$O#qihb6^CNx݉ R4{nϩ eY.eweP/ΙJ9A1i].H`pבQ*# /ux6(t0Xd ǹ9A-X>Л9!yN1p~J.Qy*6px?0x-e<"NyOZ$ !;67a2r6+ECw5]yr‘^’e&x!VڅT)(YFEH4Mqs" -BI`Üg@ w<dFiW͸R\,XTmn xSd>c[2O+ 0/TFx"mgs1tm|P3Yxб,;.Z9@8k#0(ۀW''i4i4Qn$9 ©nrHg8*]Ggɏi?'׋"#GU.:,̲oXAnkO4R8<9( =1V 9. ")!P`#mgs[Hnh"P?/̘OE>kHhrAV}S[0Ԕal,`d?:t1̐*ۜYƎq#'if~`aOS aD,2"'i'ASW  $3cr)*vq=9<~JHdi5-L|2`>bXP\BanNw9-c38'cb)_pzb`[U 3gt>|g$6GAX6sܼM"vpTchǞБrB'@̄+mc _U0+=G[ûxbZF:)}1V-Kkrp@>:Sn#82m~Sz5N^"Y•asKVwR0px㑑E I{(2 ~`rdab%m9YN@Lr_Zd)r)p`?x>*Q$Q0LD̼18#"W|Neڤ*(ʞg{ ".EmR~QV L>s$Szke0|HEX *x?Zi-@|ϕCpuBz!SE8[lpʫsfPA{T!Ǘ[Xr?+g۬pɌsAcpMKpLFb,2I%XmI9ʿ$S sJ6GNdf=R=YcZ(ݜ~R;ihVF*c}иښJZFH[ Ĕ`PGFtpL*`ܰ2yGTı[zZınn :zA`X$pN0y}&t{7Ⱥzd‘G6q '[XŴyDH*Kx8=H&BL1:9R]mˏ6M &' sӐCԂT"ed bHGzjK{{2Om/k"K 2NyQ<1J.#E!IwpĬ ?GhR =5`[tDIzmFJ:rzgiH,# Ӂux "ק֙pvc|.8qXFVm-.KbQ!.0T>#4'*qmv+k$_YF"30ۅ0sOzTᎽuO*W)s)IU*5dD]NH"ccs zOҗύFVe\S»>'QIvNEYG~^֥^jd1LC!0GN cӚqUhg-\(0P(s${wIdo.̍J4dwPྸЋ(wP$RJ>K/3 F^ *NJUP%O +J#s~5-v r<12s@kQ5 s1&#nUx#a)I1<ک`봴3[腭y"#j13͍' H29'Jq區RLȒ22ss\{XLL6vcоz:Ѳɾ+e >B1lc$gǾ#8rNA<`獹#"'5%[ <2ڰ`RfޤIs&<7Ā⾚Cq.p!(b 1;C]rݭ!>W_8$G̠mz#xo>t]>k2 KNw>-<$Z3mnC rNH8}/ %.mNY9$0=zRuݓȐB?/tNkXeؖE_OHL]`mԎ*[-2x(lUAdr(P ?"jW숹!aqq~:yҀu$E,DASsGm7IeKw2yM4h0K>[3'{Uy4HԪ,РǜULs0{$Ue'wҨ_' $ʠ?y(T59y$-睸ݒH,Zi>nGag+'Ƿ^~(`QƲ~bX[p#cgJ{669W*݊OYȧȱrv@8qު^س#o \$S{?h\"~e2 ,qGZWJ̋,PKFT(#*[O  bHݐ~Q/ ]+دoE<Ѳ,q>QߧORH{eB*cjO߂  v*sIQszg)vii,w6ш$-n\T^`aIR};㑏ڠ#y/<Ǵ;ݜ,@Q޽Kc$тGp ?1UnLRTHo8-6;WSpIvC#![lCHpʨ.TנO3 dhpݸ檭]6cّ @ I=pSdE&9M=˸yWNwd. )8O^D'*݌EtO{8^C$qKd TaP[cP".he]hRzg>9Hm.qJ2h%2c E09;.TIn2bPXK`һ2Ӌ{[X +2=`$N:grTf!=т,(Ӝ'֖ ȸEUU°]A. #*ѻoPIRsG%I/9 7%n.ZddGs y^:3 %|6vzuͰHV%1̯8  ^G+ԒhOtnm&Bp c?w;1z{TȆ?j,pIy06[׃ZFb[QcATgx>{6mtB` JxN eP_I,Q&9~&R m{xF'W Sp`3Ҷ&Gt~q٥ $AnG~Aah<+69 bytlaúnvX~:Ra_cDEᴚEo 57I;q7>ir,<Jᾌx9e2/$`?qlYc+Lt<hCYlg:|"%oQ2; ;'ќn$08T 9 {;ː .Ak{x Ťc<5~'A%?vP|wkH|7R*Hߚjn F0sǯC̫ZɕRWu @R"(B:Ս? "ay`Ł@Ïlq_h&^Xi'wqS_GݮbkIcn0~K1qK)_E{AC0]c8i,0If/8}mio$6SI#zo]Z#0^տ:GLWzr>Vcöh!W2=i eA7)F? &3mR+:`}64@^iJ+)=i=ɡZ$jP*;I"sC=pˉx"ʓMuhmCsӴƍYPɎ_?UC5'd?>-2`yR>P 犤O@ YHXu̺BJ+>T2ӌ~<9h-Jq1^F ;eo [`4jB6x8eiqNs*; o2ϑjI-aH";ZB[ykP  붳01(9%zVO qi! r$q 'sZuӲpgs/1"|+O~ǓۿL +:o%` %9R䎸ӵ~Miq"ќbDgjQTF1%E.K6Ӟid^gUKsXDYrr [c  ;0懛[ dngn-+B`?i$?ZǛ M[$SaO5yn9NOWntivr GicF{>&'}A*qe/MMm6~FKhe6*8 >#WJ^<(* u,0gi_r\Ϥ[I&AA^N;'5`i\5s*v֚'!+$\!s?H 0HEsKQn13̣l GC~ؿ L1btMR/H) L;=}{AYmQ=Կ1Eż6e =+-#Har>~N>|?$2/N1Q\>UKirG!>[:b-?!`NThdE`2xk Tm|MtPx@8 % dw#7ੈt@$c9ϿSY Qb KiLDau3տ`_0wêyFr8kx%K V{f!Tu^}q_nxruo}G1A\>f4Г-+`LR11Qiͯg{$x&77 J2~P1Ўb3)o/$iD ٴ* qIdZ$0q >JM)BuqG'M*D&X[Uqrxsை7TԇpRI<'_?WץbRNyƸg oGoI6u 9yƥ7!sj|4lnXi. FuMy*3^3g(YG?;95K+Cs#mlq`W&|%qƶhL{Lh?||N dZR)͖iZ Λ?:9@']rK ˁӸniwR3Mm (y 7A@@XO$[K$Q(֊2xx /$Fm<< sOlgY۴%qjp&)#E"b`a3ڳg’$p_yFrq_"S@Ķv>aL.-ϡSZkTkk "1pRJ~_ʹ)fnk5cG+-D,.H^;n$cfAVeI˔0[;I#8Y>&м[RmrxrLގ$K$CRr23ڸeb7? 7k}uK6I˹E*NT ԯʉˤI'$G_[rx~Cu' ċlp>Ƴ𮺶LX.yV?5+;>SJIՊ[(dw&@~y)IsHH؏5c'UWеito lR95gk ż6rIGUpEH-5IC`=k;HpFu6vInwmpi&d #?V BBb*J"g#ws[GP!xU&Pڻ3!ZKIX8X(Xn@sN;`\USrN~~YlVXfYcvV Ulv+ct)}Tp %\IqB6>Q`72<{$K,%!`Fd@*ŝAj^I>[GOSPRM"X]6 ~A=)bb@'xRt~%DFTVG*"BB䰐)fڥF}D-fyd㎝%lŕ882p=#`+~ԕՖ++v"}I J6fVH˷<õaLY plFL}jWl\EAymf R,{㑐qޚC*; y]6,x*+8cҗd/!W&8隗{nJz&NCUP q?搰0Cۑy~?S{ZSNFDZ[DwPOZQs[2Pxʚ 7ae$"Boda8kݒX$%II$ sTfr6O\2[^-_ uE-njwEF3x8u;z6lf,מN-x`f9escu93v7+ Z7J8j+;I(|]g>ۋ m;0$dt\+0svɣ\M9,B]';qóyI7K$q3f/А* N~a`$(AM qͤ_AjdnUԅuQϨuMV[Yئe L]\0|~C3⫾lҵ#;B܆$}1C[⺜١Ys6 @GnxمUb6$ScYPsUƗrU|ydƶfgidw;PzE3Ko8RIO2 ־`d }ήbF~nTG9V[9,|x -9ԕM\M6hlYG`G#ڠv6.kJm1MAc0 H*oq,Qm?#"C1'*9Tն*]7QY-m3nPcz~XNy u.7K)Q@696~}(; ɴ=:zz1*0>>S P݅=35ϙKIf`fxx<`txek-jkGʸW3:#adY]>dt#e PA+?c6i,qRp_H_X]< s*1Lv~IXy6zְ-ywae+jβwJ\{~5Zo7Ѥ(^O瑟ʽ6-(\{Xڬ!VT[dk?N} {;ol}k?iFoE6t-m[?tx태wW-895l/? 2Y n %Z3HѡƱ #s-לaj5FJ $>e;<{U6 " {tV!r \&ZN-"+]drOLQ ldB<@} ]j1vQi1Z*ynF H%U,DȆ6tpB $gB $b<l\*<\9fW+ouDEGƙ[ƣs*rG=SþG`}t],$ڬe "\ԁB䵙MO21f$1OlL-!#d2I|vp[q0AK2q)APW6i$=N>bXGb#ֱ.5(|̎*RE4R\˻1Ĭ64x khK7VY@G `yRM>C-RnW%p=փ]`10M! aLjxZHV̏F#eKo(1o׵Yم%7(r;gei$8!X:zI<;1]z~Ңݔ A2$sRb@enx :rHlTt*(&Gʣ=LTLu8cRe ܣ3}jē bX1}+2X1 x,1AGRz6HzLr9S}PLָO5'UF %@|y,4gO9cVdF)I2F̤9Y $*\B*$ qӭ?uπvCMy43GryPz{j͛ʖ+8ĉ`M7aJ:hi77 pzGLVFGi u A끚eWb`` ҋImH` #ڴV`is GVTg ~'pRy g8@ +.mFH7(ś8'Zb/36{Y9BB{s`aKR8?sK)V1 $~B!`2v} ; cOL^s#y2f$Oj9b>ThK$~Xf ]qI8#<~>@`}x֛a[ټDgs42֦ f# %]clZg1Y\NGM˔%k(8U+qG!1[SXM$nL (zE2F6Gcc37&#>I2O֓b(Id2.]R-(ڬr:7A+EARhI:G%Mw͐XHYlݹ>~AK!J [i(U.IT06 z "Z73"IR@3ӦA֤VIх('jTr63HtڔR(YYճ14|r/.dza7/ rI?H!HѮ.$I96ax3W Y$H8 q7[ 6w#\QB>>i :;nc&&wg! 5^yy"Y*CXJ*rGsګG^ۯjAq x?SQܒ?SsfkD=Ks"H#rn7XrZ2dl?%v;pWH]<ȻJ8 vqt-&y|'ɧNHX }x*ų+n;K ;sAl)}HQupg>nc7c㷟< dd?O5Ɨei,Ī9T'I98<~,S$ r@3z\b >QN1Yݠ;”da3M5<$Krwz X\'{VK2#Й ,<Ш)qV.n"h$;ppEX%Ivόt\##jx=ܚ#b7t'?ΩB1V&I(upkCrvmfp99M:'kedu9k %>imefuYwUݣ#q1KK2p6<Φb!R͍x{ҰG}Hبtp%bx婔uY%Hba2ă96 M|s3X?nn}z_2F#c'UW]M3.0G-OrWtު^7*:iӲ9$(i6Hl4%Wh9}%UntvXsY$t1#O_)I/^?V{*/tqЍ>s\YDr>ج kC=^f(-s}Ud:V1Y6V#%R}1>O!_b j=\نO's*Y>Чne|Lcau.M]@_cQo1b$I=5H$rsךm44%ea7kU7%r^d/ ̎@~t.B5XVFhQkqE $~lI3Tx WO I$:֭3 `0zu=Kx?+۱V 3?ۉQY;Ujr5i6:,wE, r? BUAg{ 8 :)kf +սJդ6=\w=zT-8vx "A i#.<tKA9EKrDy݉80,;YKŴ`cpr2FA沭@c_/~-Cv6SmYWsd'ߚH;(UaI]'#'ē&vReTfY$f.MJI3\F̨P6KRT%FXI6sLоjmKwaWlKbJX;p@ c6m FBX8xg3vw99isT$cP#J;fXV%O(Sg grኔK2lڤZB~nAXRA#}>i$Io`c W1.q^ {V\q$P\Cj}$.A!žjju&BĂ } rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/gallery/apollo_rover.jpg0000600000175000017500000027723610276336034030052 0ustar madduckmadduckJFIF JFXX AppleMark            }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzP7! ? Oȳ 7ӵtʷ-mf `#(89 tlOCߊ)#Ԗ=­avlZHV[8ۑk_څԟd vUo$[am}y˕|8f5O@ɤ։8Sv{g5*2ԎqNѕs-,ӚTI OcV>:+4nd,s ksc'ҚHܰdѠQ1]H5?.:ݰdrr1ڟ\V%WLA =;<澀GoO=J)Y#?49=>BrvꤕXVys-ooWQ[+ WVw 0U$(p9. ]S?;XΔʍe-K g_MkGX\I ' #V XWPY\jZT\J4q!\pO3ln߻s.[Iq!~s*1Fs5t'c\N`y(,p:+Bc;874z9chbYIcqdsN20OscGiFXc@҉F:8)ٻZ#4~?m=Ͷc#s[3jṀUs 犘CIL1)9TCoBus-y5Ή.pF9.ѓ\fV6;|W]SRzg|R;i"pvd F:?SgYX|3owis7&*9"@9sˡj<}}K9SgVkG S|Uم$ xsOA,uGbY8<~5JNOL{c/0ۋ;<9hcG}k=c=Ut+#(Niha^-|%GmJAVݚ|-00[?j'⎁cCQ[OA'ڤ\I{$WqV 7ԒH~V㾼`mmd2LEp vInJNzߋ_CºVk[gnL)꿶%?$S$ϾɮRL ǜ5Y ,Hݐz n9H?qiCSԣ$}9N^<E O*&;t>gw#'yN:- lTizg; 1uD$.I^xHt';VOCi AppleMark Photoshop 3.08BIMC     C  L" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R{ki8bF#:.p}0)n9!.<1Q#`V皣,F_{}?Z(87lG\rzw9'1iM$,hn(Wo7?v׌i,AB Hz?+64B{dzQl.fTd`89cvT]oo0󏘜|^ W-n!$Gtְv+te8:#>=ԐH4D<7' 䓞>V7FY>Y[+'Ty2oߖéjUܳc.tҎrВqjmws$ܴK8ji\OcRew@Lm}zIϭPNI&/ T ֺrЏ0ſcy+zbdLH9i OLVyi#0gcq9ʙۓTⲕY-ne,T.wqzc钥ٷy%v Ho[xAʅ-N\nqMX Q4'n`[|r;I4d2O;'$Vfh$I1c#hb'imI"MrS {io웖IBrʫpA&7-c;1 ؗg=Vh.).Qap2r23T丰+:Ie}7c |O6܋eר8W,7NnBаiKsjB 3(TA=#QgW v'1 vU{h9/"ǜc$##sV'6Z6(#JsNUmT],R!,€ NˋHFHۀs/ѭ$p ׏AK~"񅷀' 4.U/N:סX_!$`#F\ GV:|tAH/l.HrW8~ oHPe$ߦF=}ךKxkšlbԼscg 6fb6~ܷo{y,t?iܪR1`xTa.U)]Kp|[%&DR@}6 yCŠΆYd XԜtkGHZ!"%ޟ$P1y>%i4ri˸d5vt\$Ue9? JCzU2*?E'ӡIBZ"Wn'wi|Oi)`Hǜd ߷J}ᅤKps邃5Č-in=qGGLUnc;70]p{0ϿzёtHo0HÜg>5VSj|Q(*bFhc7Okҳ@h=?Z`53Y0rO\Ei MJ;H66wdq@?CinI#ݸ}G5V3MO Q*1$ sGQbܖ@~mÂ;ye-v=JϹUD7ݼ ?jUݤ䧘-Pc(){c5gy2rn;ؿ)9`VCk4"&ǀdq}qϯ5Bhӕ"32豨H9< Nx=y>&lLO-ؑ£)<BW[ixu2H*v;}:rFqz:}\e)fO1x>YE$n%Qc=0T7vrmBb8]b@beW<3La-wpW U=qW)&0;lcjʔZrpyA!`2qy9>ڟ|`73CM ;wxa▅YHYUs<1=gW[6K"";2pFF /ŏ |<uhLHLPeE*%,Vm)WƟ7][oKO\Jf0d鵘ݼ~; x&V\bO{ 7Ro/@.YYTos)تC9#2X[\}n,Г*&r7JV5c_noM9vv '',Tz$@~wS[_\E;3jK.*vD|haɫ |g_V_t^v"Ma$A- *AEyKB΅.r`97dE6"p\G:[u'u;O#Hzp{SQfN{^K|siP՚ÓkRF20VZk>(uixwU~{iolZ|c -q4hUv?`+YørX9s9MOuǰqxYmZ}sI j\NNH9k_5xhѝzm/UH]mC32'q^Qۭ=qmM5ԏTQ?zX-|%0+zV̹(5t,=|Y]l l#Rx4 ^݉;-)f[qe^6M""߆/^G 7m[i4K kyBpd0x_aZw+㩦Œ=Λpu!):qǡzυhz>s}=Ė +Im# r* JSqj >IҡKY5N$}1!_]ߋj/Ii>4i>'L&ũ"\ ·+{D㤿RG}$?lHtʈ݈ApUAS8nkg.io+e3IyM;"),n Wl1>q}YcS?oKۍBݢkK/ך\s#΋!b[*@|<$q\~*ܴt> 2k~מF_xzU/mln,/;WYKr[sy>Y{ojWoi[" *%Hz~zf~.7t_kz|ug# )A =*7I~^-[iF'` 2ps\_W:I -{{?0ڦVpFBkӼ%!54񎇭&whvicѷ_]X&gWK12֗Rbwdkߵ_V5}OBQg]4^'?%բ,#k EЌ} "W>PN_~&+oS=l$y3NPĵ9[ԙ@դg-sӧNj#Dt%KmNe; >J}29=87+冷 /=jpB~,tsXNLD$ ^j7Xs{ufcnumu\Ve=GSֲ^VE$)-nX(_XԟKOe|X>oI< K=erدmo ռA%ݍ܈Q9I6p@9;)M] &W`jUBB>ݣ+p3I`r8e<yA<mO~:>u*6AsߌoZ/0v6Fvzc$5fm4,X x\O8w 808d^[-;abwGWh-r9}4ZA32d|w#u#9r߼7F]GO㎜֦^9*ݻz׵KM3UԦˣ#X pIƥ%ߦXIJ-)w*;BU9cI#K/dw V7q OFbHQ$.ЇF8'?+0:ojJ客d6G=rp9:"ma,uV-D+F8!І`dɴ1!.-B;xӤE $̡ܻGzoH#fv.$2~cMnwS)rML|3O? ^k;J _@<%;pw&O_/ڮׇPHZG$pcW;0O_j&|N/&Rf{*D\&%@GQW:4_ Z&*O$_y%#yXD,00K!q]jZmw?BYg?\5A]Hd[;?~_A\^|x17e2ip F|LWx E֯绔ެ1[} ΊReþ`q^௉*m@,#rcfe B\f8g":rO?h"' .M0xv!%ߕcB6B>Ez/8|8 g~!cOkY< )YX GMsM -O7Xcԛ*H29'.z#U[5uR2fjڝ_GeR10y8cLf`0ǞO^qܴ5Y! nyip9;f%V N⤕8ǹYL^E!$ƥm/s4@2mfpLYԺ`0i*sVϴ "f qN};;NdoxqBu<}橰S>XbAH8~񥻰{q~%mmNcNsUiRkyjiXmar 'Pf23cXc8=qsTq,S`tOԒcpeURǽ1bיp'b2A^9*dM}&"A6ӓN9ҳc #;Ds8QMX3R"0rsӞ( ܹr!c'n韽ۮ*톢 *3Sk1 }5 &vѺ]=@ۻ֯ 3 OhwW#FhOʫqjRA)u<6sus^j\AЅfP2p;dJQfVvɟyvC4R:(] Heo%vBc*IE$YA1ay&+ $ѓہ}*kS{Ɍ aHd8>sWc1X3G*8$s#,/"HWP|gϯ\ܮP,Hȫrd݀Lzy |vqқWc ,|̲Ӑ6@]Tޱ ob#v:}ssZOK'˕FqzuxK4Tyn #֮5KɦO3Y7{5HAPrBppsz6QXBҥ[oi; cJ&b>[" x?Jܲ-jm@ ^69=)e*qno`s!Hvq 1*,O @-Nծb- W,9>N^c0#88 ~}}mai7vkcpEͅ<.iVL:`ZeI6-= ZSɏinX. $¹3ntj) FBX=8q\fm1 (}-᮫wgǩDhz@[ <qVgN͍׌wy$,Pu#~TK9kd|+[H$1GӨb&Iy`1}z3^1TfͿ1{«ۢfhF2xzLIɻ\X4ȞS2źN ~t4f$fĒn9;"H$iLx$Q\5T |!^ !Ant3[V[Șzr{8"$ Lp qI#:RLf$ʑ;>;dG_ќ;}88#4Ӱ˳]4xo :^ .5iy'XB+ d=>Όq/v;S>Oh6aש`>\cd[jvvu)mv<AA3zaYCnC3MG9#׊Is4,p$b pq'e00,!Cq1{5Ż>KN3#'OZ[wԒAKCp8#_hCEsYYT;<y=nNnWN~l ևR1*I \>`eVE't7rHY]H$Zop꿉X]Lm1̛HG$su#Hdr|xP=~S-Ξ0p:cU2@I db{֍NđT|#<=뉃Yy]Nsyʑ3vҮ!"0GN39)if 6?,I$uV݇V!G2 @ A?^+&7d(6N$m?OS+gG/L5ldy#BNT35IT'_'k:Νk8 =sgqH:^"v,F$y >fFm c d sǯZІ{_(sdQ- R+'x$ ll ۖ-hQq-8_]j˺м֜U%NkKZǁ5+}FY~sc-ӏeL>3d+ 5{V&nw>[&U5V@b##rp۷k楦Aa'{pJIto>Ԑ@$n8#~u0Znp$*vyr3^WD?VX+e> rpq#9;m{q:tuxB`KfN-n"Bі)<(7_e:,FYN?*YZ>ku ,iqO/ Aw)%H +ߑ~~14[*+(z<;%晤KZ~lLQ>E<\G3^v4[BW7P FNrCRIG > PU#[s<p9JxA=Jg@0\do'3~0xtT[O+Oz;I F}NǽX0>j $ϱVK+< UVeéIOD3n®A<)`O^)rvB+2v 穠HΑR[vU@rp;tjե&FKG .8@2"-JOGmt4o/v v~~۶[RppAr p3T%;gX"K+3Of&8C4a('OA)WdU }C*`H҂:<8u^Im 29FcK ź¢0>6c9zv6 ٔrڪQc܎jݥsƖ#v>S{r;Z˚lK$g#a 2pH ԑL%.Yr$ ÞZ mFh")uhĐIl?/9$OBBvl p}+,(m;,9W y..X|8@p" t 88#}kQuwbNZHT!^j RY8N{V^A4򉄪r{t<kA,k[xY(Yy'sN/ (xzX7%#n@GmK5̶.Y60Xd$U:]Iw*!1[qbrndt`XlG^Fk>Mk7V`g12'8⾤aw?Enm`zn)X.(W|wDŽڽO&#>!Ѵ?A|g5L#M^JIS3;GZ|@GRQӖyBZŕ*2IߊBHlSܛWF}n K ɟzeFVyNJ-P3ӹV4 y1M$."AQǏuv_JKKV[N>`>`>q9]̖ _eES;vyoGf-̲^%ß+h6$w]4LԦ5jR-ıYmLi/ad8ukÒXLFz~r}H%Ws0?c|Ex8ywepl݅1_5RsEk'$)O- =+.|Wgk1D !Pqg96X9]+՜fw5ռ\e,v!Vw}VF$ "w&jdyQp28G,|~}1J[Zh_?e+: c<ʊ dd IRܟxYQQP;ϠS# C T`]'#Ͽ94 Mk:X7omiڕ=H6ݑG4ȥ8o#FJ$v^\{? X]T")^NHepf2v/tlx>ķ-rc,A= ǝ9'Mk7 .眔^Iswv8]Isv/Nh>ײMH9py쑰PY~Vl`68ӳ3SRΫtqi`'6 gxN*;[qp#*8>V蔀H 6.6s s*ye QAyQf-#q?0X @N3pjۅ1@a$ugҲ- RTcunH98aygz@g}$^WtWi#i z7Z+\[cm ܫ]\)xO',I3QCM͸C-1H B;{-)rzTZUI77F19 u教i:;kdJ ·# du9pEqej5_%IR҃Bo~sQ^C$jM;x;I-;7LVlcu^A;{c~2xi v>>"EzӃ΁Hʉd1J66o?@(_g7^ELj4o><:a3\9G%E4ijnpS>Xӵ+kY"6VJڸyڒ-MYc#yh8Sӟ|o,<75pBrqPwݏ7X`4%FTcı>2c>ׁ"U3Js;LѶIq8T8֛>>0k=BFx9g|̌p8r}? xƚxÛ{gu{ aC1 _jSwU/x a;x,a*(A'U_Z5kW;4j/8<Ͻ5WdOկsߏ |#F!ݿGaR>ʤj'hUXۻt;ׇϭ󽝜W6H&5P* CqDq\J\6<2:?ʀr zVrU m xS7Zώo|^{VnH9,@r+ۖ[;k"w#ݳGϧ͏T'glȳj@Jevd8 5.[.oDJFbp?09rTCn<>7xKxL ߌ "'pD_ 0>^$7j[ɒ@̧ˋۮO'2 ed-xbcxl& ~X7ctKs>xOsztЧ,GQ(󤸴XĒO"ƚG?fo? |5HhS\0o7%I~'Yx}:^!Եvt-"$$gGd>m$5<92ZxP|yc/+&2r%2!*[!%pj\\%5V_ξ ]'M"^o"&&3Bv0Zn}~xP Q_]4y [}ٮ#r̉?8hS|X?i>'&z̷٩= +ucU'=B\RR,&aFw;kWF$zx <] 5-N{OS>dwg.=ZFZ7oZx&a#crdyڠGI+{~u2a!p Bq~ƟI/~6> L|;mocqqtį$NNn>Rx(#riO]4QFX#=x㱩ݴv[8_ޫCXd} 0Y˝p$U~eaڣƊJuv#ў'hqʒNHvSa':UAdyJM&3d ӊhşW/Zn MzUΌSHt2[|&m> xF_H5$(0.2 `ݖnwK1w,5p$7M\nƒZG7񧃼{/?l{˛wQPy#\ZgTJbwpF:41,9/0Ebz1=?ieOc}oKgi4s|29덠c_R'h٦ğ 2HPƩ7Wun*s ^947sH ݆8`v}`6G_ ER20s HZ+•\:p9 tZzy-{biP'3~)FgVbilR"}%@dȏs7ђT?' ߀?.' ehbei!ݢ w@p+_ / S:L蚏<7}iWmsbZ He,YH'C;7ksi${f8Y>dpw)`AN)q7-\]6s+#jO6hE5:z]wmeGK:W95SFα}sc#] {Us\E$kf5%cȼ'/E$N 7Z%XaX8ܸsv^YhtV>ؼɽà|*$#+<99|d%}S±e"c0>c JtW4 1}X,.id7BSt992Q׌]Y"HP](V9=h W^GbE6X#+FFO_<9nfGU6ىIG;A,w= Dʩ6y=0:`3Tdkya1 3ș'8q^8.F5Huqֱ5:c`R]oLs֢;"&0z1t6B3eurm8FrGj&[Wm8ڢT", y s tNMjiHbu# ,wnAADIqu$dUw(LA|fu+QnP0¨)$NЃjIPdi $l+l$c{cgr3u篩ɤ[0{!Td$N1>ְn_`E 0ay秵hq yr\+Dx`0zIO~kFC wT cJa)4|Ez<>H4P"b=NXu$E77.Sy8*7w힕{V [omZݎ?{v⫛Y<[jSۊݝq*Y#MT1WqD8HdAPp|tzOAh w 8$9X3-FdtSWyO~5dVhu xOź^xU86zU"&X 퍰Ms^|3 ZS:MA!QDlQB01fQGgK2PYKM .~upۋd}i??\jZ^ Mqs;e b]19O,|VH`|˞ =Pz8/FLDHq84 $yvk x=:Qkn$ '9.3DHHBwf u7KnZP]:Z+YlgRMmrͷ4 dx&- ۧO}[(xşm |E55hVz"|Pq _u>^#2NmQ8cȦrKԼWiڬnwZ8FXZeVT`ǓԞȾ/ڛ/i>3񅎕||fxo)Wv>\AgW<-i n# =8[D%[|}a>~7c;@S ?+ 1vac {svdFeqF~>%))ԟEj#zksZtR3ڴQJ\GUWJ>L<5czoxß|+MX6_Y`+`UܲXpB魠$6<@aq}:WҿD+ ; . |DKg;IXeF1: S k(MucjRY88c3N,Hd={?~? 'nnfF`mO1AZ4TܛGe6IGgJ<Hgմ]ZGsyDєO,;6"6w3ybM&x 3?m'??tW|?ˈ'͔~"ӵoȱ\$$q]W>ot?"msqc 1O+_KSjT;@'q4۔F0j׷> MJ#s\0q1'J|+5O`\NZۖc;sr8 W۷HU-!wFEInM[_$:uH\D[귗#r+n]v5lK8I֌֬dY 67!#9zR|+Z$if};46rKlk a}6Pk`bH#޴e-yj_5opiZus7&߼Bv1{9RZ4i G|B4L|M7t-j_Vg?-Z^ibsċ[#V,gc_RŞ2@zڰFS%id!O,pA4FMEja^S[vk_]yJlh&W^o xKfGUTv9-o[ୋì|F4aCq*hd!tjٯn.xS?˳~>xZMVnPI,{O5(.*ێͿTק?5tmJ5!ڧ6Os}*IJZBpM-M/yo[}KS{Gt]hrh:|@Ѵ*+:>,i :ILT^&jo qn{Z3KdYM4jp$H]UF-ҚV*~׍Qke\x3ٓci=̑n.b>W?g|K?0ݍkYQ&<ܦsiJ-Ke.Ŀũn40Mmxa(\#.@Tdo9|T&3_i׈a[ךSM]QX=tb;[F Z lavys_RYZiq/ex;hev಩ +W[ċ RԼ$,J1{l$ jkr}očnn|=ŗ,c[_ ,;X<7S7)J!eoGߌpy=@e<㗇,M׌d|).#uNE稭[Pղu̲YZElPxSkHA >QִZ6S>/#k_' &M#Jg%\v~_=M߲wՕE.V &yg\uσRI dr NNEdIb}A!(2I'h{;X v{2/)Fx ubU\Pė:jR_) \#yIЯmu7+GnLb9{=h@ĿdEDD<^m3'ދ3*|!EqF1pj;} 9,xÿ-d M7JՆyiRKL%TRNs^|.yaNw^K K>d% >+[-ΉGĶriAiuz5w {U=عw(mׅbe.B Ƣ)e[$ 8? aGjCk| l6*F$~b/-6 ͯ=n#Vd*llGaPBCt돀WsB-S) j&iQ@5J3rDPo[j6X[%ܾEʤۅQ8})h:Ǎ?dſf6,6PY-yjֶ-Ō^re@#9I?P%=/iEFE {оJטxS↿<7t=BG 5X_X -Ya5![X#ypZ)Ԅev*Pz2qvΧ3mcKZ//U$hxFr;H;it/kiZzVl:&\`׃^#~(~_i:__xdIa,ۡO1V5 "h$WfM![5M;-qk>Y6^%40Ui6j'Wg=> |2l7^Gox^ݤ[i7 JVK`~Ǐ#/%|57Efw\ r#s | 9'j2&Ӽ=aΚ676I b$Xr#>%5|?nc?Z\H uKL}rME2\,\dyql8hZwm?7|E~,I[Nd$/ZT:fy#WTT%!q?oQg/]<0G1.C dl<~; >;f".VN9PC,?h]wWk~*OŸK]xS߈n/8-[%lex/ծ58' >xSſ.>%xk$|Y֚| Ew5\n}76pY$*|;Mwߵ-.oIgn20}͌ezͷSş7|$o>#xWߏ41cY喰9>/ڠXFĈŎ7?ؓ[_>4]$vex 4ga;mpUlOCz^6|z 亶y$+< ܋y χ kH>Z 6pib IF3ё?̿N6~x+* !|V|-~pZ\gTaGq K[qA!yH4<ءNY> SK?3mu=JW4$y#X(qI..퐨 ׬3I ~_T@nnd.r>bxEzƞ>~c5O#w|M7-q~\jl6OG³A|_ WL׃EzU[gnUl &sG^Z!),1E4cKf4DSJyc [׭F{ djic)s3eO-Qk{e$n6yڼv>3Q݄Y*̄L۷בt5:WuW6dbK`b739|!OC[G5Qm"Zhvgf46BY0@W٪i-y7M6v .#ai eVR!tr (@*㞣/C-͜HG?mf`&0w]'n;ŷ-M |O DqvnS%R;A&KGShk漊I!1Vl(0N9+t <>Wukx?Z<=9𵤢-0d!uc,5Dܵ D&f[$l2Ij~AYhhͫg o o(];]U^on6 b99̱Ch+t+_-T_X ;KCx9"@_n+C=W鶯xw aٮyh[vV?ٟߴ:G쩢'O"ɡ^ k %Ffr*A :mҕ)-N_o6]rL[Կ߄_PtUXdYŶ;+٭ui-)<:&}ycs ^aQ{ddT1 wnQִt=vo{{xsQ=#N4 !@n?L_CH|u-#ƙtva=Ej7Dai mtDo Xr?T|gKxt]KmO]ε^\ͥ]8`d\ƌr/B rYS*jW]_:QgO߶M#_'_`:h#I4kh+<*1fw^+\}?dUm|M7`eiZơako ^|4>)詬?g5uc/R|q ~P*/o!> |"ĖZ>JW-Ѝ<Ϟ76eKqzN1wW٘ӭ&חC-KM e|3>.|JlNq{si1}";ڨ#aGÌaWsh6fM&J}F1[XmW˂)M—s5LO_}B3=oz~sp$Qcl|2y[KuVW4*7n=j]殞_li # ǪxOXN'𜶳6$ZY$(e6 3Ε}P[EEC"#M wɅyP k_ڧƾx:qmA!hY_id<7_ku/:$OXI%T(Tc:aU5wpuV3~*~N| xiz68pq+fO3H/-~|%gK_ xO֒7jU-aBLQ4SPy=3zߏ?ּ?*cĴ1Lʨ x9_ ~5?L{{]g6]>H a!U,EnzmX]‡6:/ d/Ee-f(Y/BSu3[L-gy5 V##N |с;+nv} ξ,4u)t?QT}{)8S>`Nk?:ޗ{뗚i7ZBEeiѤܩfF;nPy2hּDJ K֭; FG2am^g9cTRi =ėPg9͗8c_4Ex=ZkkunZ\y{e#(# ד\eM62DZ0?$I?ZZY\+Zfo]R{K$qziup .7 |8r/x5 MF+Hl$w+9  eU峹Pŏ\_xS߱uĖQIZ9.%Ѹbd[8|-~3~ 7KOk/ i䫦K-wOZ6iJ,;J*# I֔$ݗ{./~/ouO$~ >Ze͜xU0Gu "6Tω|7K $^kS4zK[_h5qVWdW"#Y+|Nҥ{)?O\;U,-4]+Zq+Hc !ʳ+dx;Ǟ [[O^xGWk}0x <kZ%Č2FFsGJg?.Ű>%|-on~ǥ]\뺊$2lO6{ 2 X6+'jNkEE?|Q-$a6X)Rb9"盻J )d >!]:Ce+>ML.gla2Gݑ9pWŧj>/.!l2\G-ľL cX%%TDYw|Z,|+\~Þ}>M.$M8<ͷC$JJO&_~)džZ/IkZcj:lYb6b*&$ Ʋ~\Rᗼoxka[OB,kVGpA )erxkt~=Wj~ xSwHHZ&ԬYo#.vF+qMSqkW>|x_Jh5]#z~h^Is !v08_#jS}Oi 9 ;.iMc"OnIbu\-Ԗ"CH\|sU//ںmc;YpE0g)wt4$ʏn}O[G2AjF}fIHrvDr8;w⦧cxV|M%Ke[&$.vs^.~9x<7LEΜdɉݕ$*t;$d3OҼ u +d[%uk[i#i'dy=h-GR1tҾ?i :%旬YZ$svI ߴ w]IaPM+ƺiۨ+$nyY D,8N:ukljozFCΨgDbR7,N+nٳY<=}׼mxM恬?b0j K U*ͻTd< ;&A|+xcY>&k6W-;JCS l'$?Siو%]n"\I{FMὯ<1ui5ouV_..dHpھ?h Z[ǧ_v ^C1g! \9#Z~&QqGֶ]r81pL$<(Ik kmQ'o͎7Ỹ};Nʃq3G#3I*/. }bKd OH Z 𵮕YFӡu{3ymt$rw7%NO>x¾Ѵ]G*Htn#'.U42rO]j%{zZ/~]7W.=J[ )  e,:RWZћZSTBF]CCX`cofҵHcd?x#(Ʒ Kj,-VuQ1˫E38*melp@5 7}צG7Owo&JyZDT#ErVn4N,XQ% Dp.Gm{KRC+,_ۦվIow] ŬIZxPDs!Hi [|R*sJ#Tzqu? K=?C TFgw1V *rh|-m[>(֮_e~Vjyb!ZKTGhY :\^ lef Eo ;?PiY˨CymR[NY@C~1X'¿~(vqKu!fido$tԾE-'Ǥ6x鴻K HդI""FDKQS3Wu~~9=Ђ8+ ;L׭D/%ᨼE{qaqI5Ԑ2[KT1(AYVӾZf]gS<fWLxñZ&mQJ /#G| E=+^up^i\ŠI5vIe[#NNyz$Cz\/.|WA<:&ɯ!Ko06G?} ǟPO]ZWVxCZ;e͆84iB)$~uXgGco3aj67MrJ#$-J[;)޹q} m-A_gʖ\SJu~E5ۘ~|OþtfYsg?$c{ 7M_md5ܯ8AOs㏇o>|o4F]gTd>TXHX,a˷ kZ[ g/yGN j|i=|ex_UGKT<{bC-U8 kO~о0o xm}JѴ>J<Lۼ儉29U*w^f/'mŸX mnnK$Ztm?|?x;LˊxRdK"ݟt?x^x^ tuXYaˑB31>q|öV|w_M*.i:q#^ plM;'CkWiisDZYsFEMO22JT$8Ej,W%ḣ1d8 񂧡5&!r*T={uھUΟmy0η\1wc 9NJmGULjbVҾŗr^^x+WtY ؼzMɆ) $9dٱ|ϢC{{Wn\#rGrk ]]6R )9:OJ5tZ!xyA.um-R\i9<(`dY+5&g9M"W &Ho#C1`3!.wHŋ(r^+]3EBȉYݸ ۹8ɬ-nSno]<̈́UV 瑟ƪ0=P/|A7aMԵh]1] 8%vh }NK%Lt:r/ʒ1F0CWῊ<AB"k1= fYTCJpn\3 C\0x晡^x{M[_^oxͬwkN6#)%;^'|F🄯%V~&nmai(X.1 [A k<=sa_U 嘙YD v}2[!?]hMoY4[KKC[=C%6˨]l fel |uvO~'6{rmsž9[~|p`>̊%XX /OW;9A>x|]dOwpM,^!|v0ARv?lxQΚd+b:m4o $NJ;N0UHg/sӡ&K ]V]f {mN%6\^.YAgK?ុVYaxǘp B޸4E5t'Gķ x0CxnD<̳ȢBB"?d>_խu/(ܜ sΦ4qUvʹ1]J𵾩 m;V9 )bXm 2,%H-MTI5[uLl~>`[ql~K"(UfogX} ߊMׅ ml%N_t3Q[X-;LN" rf\iKˇ;`I$|X~>%p~a.Ckj6ѤqK{!{Z7)1_~W6vo@w>#xBi%;S+HMfJ4n!|Xc᮵8ѴI|*cyn<-[eH.w_1fٚEcufz,|~'+5KӒ[gs ~s1f¨du)!zMSĿ? j?/7R\FxMмUeqj!x%yr$ip"(ߋ>+m+H|T ?xT ##tb슲R+xV;_چ_Ƃ-fW[ڽL*`!eO1Q$z{7$DS\g$~b?|8AG.4$bi@х#Msm;]WO/;J~.qZ ʷgxewڇ;\WE?$F ƱkM(4 _:a)de"_,&A̎SdT|mK kxCY$hWHKU(*$hK8/p^&~g_M{63k5Ut7.a>cJ%[ei`sb;]3֯So➡iq(> vtJ(6 oiG K>  _xxr, Ŭ <I~X.I-T C$ci;f4?g;M{/uW k:N+Zx'Af%y ˞MJ*Q +Vq? tovw>1/~&xO}DŽG [KjsChE9gBInIOð̲-emyhD#PpzV&iP|Bͧn(|20hZ}֝gVk*L-4ff!ecM;~_/*&nc#K?ZԭݒI绷-χt_CS w'O]b$FtS"Fl*[5)_gQwtѴ*_Ż Q|(vXuh^ Ԯ9a1h'h(;׎x;¯d߶)ddeXF-˓$dWó&~xt;M{&X:c2(i 5M_.G G52 _Z,f2P7m 0N|ycr5Ʊs5;wNw#UQ"ȌT( o?VMV+$;N K~ɟ3kSWadڷ-:ckPZ!LIpBA}FDHux Ǔ$ ֺ`߳D$64,}.5.v/S7#=9JbcB1$ҝ0ѯy,]c_[FVw8' SZƑHݚP Re9$⁦sҾx w_j?|_Z֥ܣ6%y OBM\g!MrM3IyΔL6va$1;l~./,!{O>"&#;FZhL3`WCᮩ_߶,~ӬO|u.O$i/Fv!W2ܒѴ)&Sm\05ß:5)C is+yl.ʬ5 ;Tm[o&,&ޔY=,~#MEi$o$"A jy< ~;P,[F*IQ5Qji-o;:イ;s}Ki; s:n' Z=bM>;lu"# oלi1 ѯumVҬMwq+UZ ]cuiI]Eh &'x\T? X jˡ1ܒg߰_|:m)KG:F77>7>x0Z_892Dn$;|cBk??'!:-~XOaH}Ub: {c ޼gRҁ]go(TxMFqkKӍµu;D*Md^<Λk_[ZS4Ulzc54pH(ӾHb 1~_>~??iog[𳙯D`A5ɆdP$*&3 Q'Z7/:ψwW,,f(bL#Am@ 8ɯPzOيU]F1x>{,Dx]P-vvȪetVT*>)|onAt +K< WEnX) Ǖ /ÏثWš>⋤[}'Ğ.?G$,ׂ[;䐪9L6Z?ʚ>W4;}jؙ'JXFTƟ/ߵ xsu-n--&Ԙ3åآj3 Gn䬩 y o|ӚMFwKCmi\h k]kBmBέ±j|١RQxF#FF_?lqj/>5v/\iV:%ƨV ,jU{N5x'V|[֫aa};)L7F-#<7 .o6/ Y[]"S]3-o6YƨV=@2*/32ӡ1|EI\{[c&QQ>4 #&f ӿt_~>??SR{CIk&3]7K=TZ 4$;|m+MS/.y-mX$hc8d]Qʰ,ysZ?hsƞ0>-Ӯ.]7 RJA<_gvɅ 2ӟ;ȌWKx3~˿_-i??>9AVTF P"n#Dއbѿa_نZ]35mndƥ_e܏ǒ ? |)P֖jR/ŭ2}=Ǒb4?f.̒G"VVg 7(>+x^ \({ ~k{[1d2M}e ,(ev*PWHNrQc[n shzT6.*{8@Fc*,|aw=ZG?>>>d糧҈aET! NCD>#ŞU͍izV 2[-.R=0Q0Zx +h챭xm-5OiS,a[h.3*̥Hm:>)~~>6ѾG}i:%t43ifԐj(v17+x|8o0OBKy֛4a/vYU@&~-k>B<>#q 6,myLC0Y gxS7 Uz6[IknQ(hL,b!FҼFu>Exs~ [rM?haZ)?2;^7UcoL mGǑk:U߆l⸕g+ %B?-v_(KYTQi4D>y$R0ی_-XT!]?]h4 -(%]. 3+b@6jeN73m)Xu-gĢjQjZG[MI`pZ >Ze'=@a??kQo^T)Kڎ%%3b=4fa[=R]Җ˂QsYҤJ g 5%$zZ,VqyZI-ƣʋj@U^;AB6D4¿|DQ-[#ΝQk{hgvPA爼_\_|I Bݮ5r[ɖ%R+BR qL&"&$k0 Ccy}VI ?)غrw9n|gzVjr6+yHh mp–+qzqV%[ -=EL[EAފr}4bGtuVpC>\ªO*U=9=JIli_'ϰKB &Uy|X>pqA3,V aa(rsUiY ͔\1O=shA9y3;1SN0 S&WX#| t&l|<4ΏkhlQkF/p-%1vw⧊%W e;-BW֟}]JsVE&O.PM~>[b)]#K[^f"?n4l5+(߿ WW1Ŭ^'kHdP]fH1Q5 C>I#?Mp~ZGNPh~y4eVխX{r2`fCſxk_SSZԾ7n xz8!մ2mKrчy#q㝯g4"%3%<˟l>vz5խ281(XSpN_eu~R#go 7#I״iL}n)!nnXLWi@KUw쫡|@WKߌe$1.Ho`' ӻ$?&|JӼH62'Ns3]_|hwg'go:W+dyiP"2g sfQw\R? cºw}[}ߊ67 "`2)P<|x㟊ZďUkz3l3h?D:/5]gE𮑬I*Yk{DRP1J6 oWCjc a_ƟuMA9BQʪG 6&A-S??%#_~/|Wm!M/3_X*Z7!kfd]v'_x/ᮍ,m=4).Pڞ|O'!5CTW9 jQ~ԑ &M4Xh6"74OeRҤ[ Ŵ杕Wh6zV\OmBx sQ%2J$-O6oBڅ$lf;DČaL\-zKM;,$ q EH}=eDqD;TJY@m3Adӫi$ f-$U.n!b n;xkx:~n#{K\I@44IQ>bJF߇0'tؕўeYn~Y. J դ̀qs_?f!>"Qо.O|~IXᑒO)$QC +G>~6'IP,mܑ#7;ۉ>I'j^څBy=Εwfӹ 3M LDr Zg̖#ᗃi?i |gw֚X{?UŌWZZ$%Ŵ6n))f#Kc>;{ W+toj_3d-%ԦI-g p.S<$H%~vx_Rk?Ҵ9 fcly6vVjoڳ>=Foee?Pİ=ʛT)s;p,ՌWJm㏆:jٚŷ:Ռ~ x|?qx5=4$\:08L n.? ~OljK"4mnd*n) &`#>>kZ 7g}~oŜ^"V2BB/1_Yd7~a۠xS[Dw-=cKq|<}F3ҩEݙKEsڟ _~-Ӽ;6L* vfOVC!WhH9F<+|QPQ~/Џ~1,u} 8/ot_‡h,@rJH_|ke=]ۃk4Z, 67Oh?jX*ɝZ({D?o擥 |Va} |_bk]ݩpKa%UeYwW kߴ?|G!xV dy A[k$|  -_[Υ,-*Gp:D+/|+R?>4oZ{!q Auo̶4RHy:SqVo\xKɬU/.l ‡QX*FINʭ}uPLl@ӭɼEIg-'ڒ"MHV 1xu}&?l咻my AdPBɭ=t(K#Oe{#m L%,FFs52Q57#qrM.m$z.]Z emi'`,'(%gT%G\+3ӷֺm71 0x=U*8TEB>^I a^cJ6 w9l!dV  @#3{Y$QY2w`u=:qY-ojSđILa9G2+h_PvDƅ` 09EtkXD2&ysr֦tOPnru avOF=;uP1X|P)oZZxj[km&ZidH%h  dڢN9QyO.@9EȻ╟ʒ'"8z 95- 4`N_O%"/\.nY1m gxlgHox5"~Ҷ [Y[>oOX.n$VQ OUmP Y]܋M- H)I-V0I$0$c׫5m}ٟeֳiOwYqx$eШ>77t˯[uގXf#c3\$J@FfrV_,xA'/5=2]W9[GO3TyR g!U 1B|Ҳ Y??Rޮ~Ǟ-koKX|iR#e܀vېF+N֪l%|'gͿ~)Zl`I(H^B~q~|`?g?ۻ>)|ukx>6KmՒhnOkncrTiA~K Š?> -L%Kn 7n\!p+G}.0I7gC~(zH[NxR,?h1\0P䁓_Wx K]cGmg[e`F  cߌO~_

    +FĨu~/cث[2]fLdn3.u Yh| iǾռ3|R MgTX+~.iQ[AnSۆR]HXb+#Oo?[߃6ڥɳռ=^u JɛU%n t5Ci<ٻƻţ]g&gs紁ewdb7^e`Y#A~!|/<]S<w+Ug+l>dV{YG&yu9I-4#:Fe!.Mx+myh9g>~Κ?jF/~ˠ]\ZYK7{9MU[*+՛ą׿d lڮ;I(˨Dҳkjw.|?h/_EK.>3MɦirZKpڄd0H De[SIOZs c W5woEIӚ_mWn#kb[1)T 矘i<մAc:$%+)oIfHyҨxRZ%oS@u Y_-->+Ɛ[ľ`ʿ|ȉ;_co|TW  喜tvg>k>fc*dKf(~_i;Cƾ)VoA\ʎIhFHA,bϋ?xTjuŽ7vk<|VX?2DP񱐶~j5ڍ軾T|"^&A4~]e9_e!e@zMKzG4 s+G4 (yV&G)M!V ^i\ι.>d}!o4k{iT5[#t܈66C+|X=xChڎi(ndEVC*Ā8_bj_ %|}gDdk8i%MeXlβ ͒TIF!/፧-2ӿ0]5[M $IY}9?'!|ZcɬF/kH|(A$Veq|akz5?7^mKKG]moZٜ"yoAW5Ms ___\_O|-xº5Րf،:ח%γ}p3`D'8'#q H1H\vO=9=0+H .C%ZGm'VV ræ;ϽSg(PAh =]HFZ $qgwR9Jx 2z4?6 I˝JJs m̥ r?z?߈V:o7K=:@ao-DN ό~i_QzV76z_KCw5ª[2 l88H=x4`4z}֑?YoHldaQ#演nGd*U | ƃʾ*]RYh_RFiBУZ\<*&YJvٳޥsih)wb.X>r;,'hI .~I= Z]A Gw|qqVXbbN#>#Ծ]Ԯ^W Y$ \GRt]IeTi,X$YzO<SZoͬOi /46IDr* !b;l>R߶Ö5ˠ]YdXUGwfӴH̗pTZEIORO |?.xz?)Y,sjf HMw@9Cy ۟u=>4_i¾FYK'1u;Rj_[cj'OZYn,%k8lAm~̍T˩)(4lE)>V%?6zXMkq[Iie ]Ug ɸ><{ jH[J/tMX3sm4a(?_Jo]0o&ޝ.8[[[Ww%۹[lfbv}K &sG <%/'p#Q_)_B ։|;uZϤOm a2OwD2/ ] qէ쮾j:Wχ%lf$ԭ")3s GC9X M:u/t ΍ewrw_ٵF3hn-ػ+yEPRt~E~ʿLsR bJu]v\m[iY\9QI}|ғF<]&֭CΤַy/ a$X0|&׆4Gm|SD&PͺL km,lccL/"n.QX?K/'teRBdAuva Eqok7_τnF+ЖD`1nQ_5yO1Q{[z-ѿ'~_<߈>!MԈ?іB;ir"eu9ߵ]3oK {&#,a+`4F%v+K7x~]8wh̳FEkoT1K{8ٙ]۔1RHɯ-)6VIQԱ^#ֵzN5C hŊ*G#<8bym!O5~~UAGܛi،DN9'>)l4ȚUPq#Xg^0kXnc$Ԗ0)y^20Ǒa'߁֣bɕ1#cucL$_+w1d99 Vh#cg97fVE\i N2,8ո|eXmixlI,+c1'h9LN|v1AU}WqM.uK 9hQ0MbXn⺇op ʘ^m`SǮE? NM!Iq>aX:J1Y?f/i #_~kݫn-nmX^XeêWp\K|b#7j->6WVEgy u PIer E.%IeC(ަ RVocԒjY##z٠xx|AO owk~B'!hI C~9~xƾ㨯-)[]>Y4h!wi,K;X̥ 1x_s/~ " Ɵ- 5b[XoPacK$PHoY}zB_C^4_~Ӿ%]Kaevӛ0$!u۴JfouF ׶WҼGagNѴ8x},+PKHZ\ |; e'x#~XD5Ե[YO4`H9PT+2Kb5{J״ 嗅|U^C}xF ZkhYd8ȉEHBWl~ݟb|A2Y,/.,U^<tm^eevUU?h#|ex z7:Nxk@ṁ1mo/ZDtGLaQȯY<~њf^ͤ;R[Q:OEƳ=bTZޟc3/̋}M=*1Z 3۸n*2oWOSm'Ai..0I,ufy ,Ĥ𽞏c"K/t_Pn+qm3[ۼTR >+#ܾ,-t)WK&I;o6ϒiXQU D\=Uƃ<'m -x5n/lo'hnaٻ4JԖ+Ěğ_j[[kg ~_6 FmY`%z1x`є`ttok}א[I?#CY_n ml <߲s~ux6-,^بhdc{_/v;dH8qOɼ b^Oݿ;%lm`f@9_vf鸽O6Uu'rʑBeBa^[ 8yƽ'skHNfrH `ѴRB8~Ngx8X$iDkƌN!/N_K> z_M'RKIGw[^G}i⼆G6lɗi2iNSM=wP .. Z$b+oj0kxEHDO0` ~ʟ~)xGY/&MZ[H%FHihAykm+M _h7:DEs'7}pcHd,2Xl9: 1_i3?~hVܓXcr݄FO*& n*@RnM?ߙ*S|$;~?~:~y[Ҵ =V15ō$i;$U|qj| *CD}Dмgic+@}0Y*Bne$eޠ.W rѢ\Poؿ⎭|!'7׋)5>|<ï{XʖFp +$==ȚOMx]G2_x$tji5НuBUTb_ 3wuJVc@TYʒtg.raQW*8G~'n+:3ioE٪äns:LРOJwBkE.W⦏Kstux}?9hH#Je!!$1ȾI{:-֟n|/Z9. vܙHBT(Ȧ|'?e_"# 3QJ[507Yx۞}7IDhSܥΝk+h$nT|+5\kB"~fMW5kMv&|aƝ0^匣kW#&x3ǰ_ {7ioSiBo :/)2n8)xfG~1i1eI.|+ C/緊EGy&.C OQSu9ng'*׎8A) u~çxkR聹-#0 b#p޾>zQdڝd2l`ma iIDG64yNFOQFx`R]k*%X;N1~k;/70|Q hd/5 VNmfFViP?1ZQI<&UBc%xRX e_( m)>8/i~;𝾯i%paywZisıD[$ Wdeψ^M-Zi!9wT %LG$=Gi?eoNbyuKKxKkţ!hߘ|o>KGÍ/Kо'|K&<]Mf9{ֹ=wRSJ,}?bߵFះϢhKbK[rdK{l+x- IBc)~yĬhR+7LMobV}4C a೶X)Qqj7wў]ݧ{ih;sKHͪg'F6*CnpJ n/G8ao<7V 6o[-Ml.5峋PEür[ 2BkK_8%0A J#VhNwOs5ovjMi&zGí5Ѯ=҆5Ho d%'uo.{JW ڙKS_VH5+ @42 yc,XQ/Gg^'I zInO+pB]\LDI˦vIA!|*Zu޷Ggz,67."*J\n>l|OC Ws6&׃bT##ZȓO v(9EfڱC~*>,xYMOB^&S,Asm),56A% Xh:fy+%oln/-Z}jܢUKB1$2y'3oz߈%$xuQSt3WlĐZEo˓c;:xH6+u[=jY![CA8B WRyfR^z {὾4[J=I!l&;Ŭl̈́*B8>X] !Oǯx6>ռ;V("-(+,oC<n Y].]xծ %Wb(U Ϗ*X[&k;l tin\.=CԔyĤrRˏ]5D^&,e mIg&&+ib#'gxg_|>d?Z\~4B >7Gr锓1(e̋7Ork2kw𞯯~wĚΖ`g}yeojMBX6# ߎڟ~&x[R_M,MA %[FAj.!4nm!V^W&Oot ;ZW6Wl23ҥ44( T| }Ҽ ti%i5F]MJ e k9m"&rʍjiG&e o_6Z״ ' 2/Zs1ɟRsh&?|DYo{ Q5u1|MrTԟ*H6}ſ//' 5ϨZcӵjV4R ,:U(쯜_A_kCB44vO2H$ c!] 肢3sJW>=|roOxnK[)|7ѴMS:F1]+x$NKܬȬ}'ෂoo ?jzu+⍕Mhx,nӥ+@5yk:sz_  mGQtokZ%ݍ'Q c6_3 lɵ(3O/|Vௌk0iw>v]I"\'YK`on4u}#[ =eXo<M+' pMSf3/mrߴ^6sKkDb)r : aW6i]$o8E9RhMXyVE,79;?iM E BxvmzQů<o ZĦY3#;I m,k7>+~\i:tvmm?HX`4W/a BJ2ZV?<]'Ïk{q\d\Y\[++]VݖVu.YƲKĚmǂ?xsÚpK>C'mݦ9+Q7"`UrM\#ԭ|ow,E՗ksE͕:lg\%ș{ʱƓL|Z>5뺵ܮe(SmdyEbDuuR+uߋ7_IƖ|^"π{3\GX&Y%I&$O+; 6Z׸i̤O O#o.wkx{5nb&[hACE&xoK a?..X$e[U%ɧeUe|mnSԭ.5)Kso⍣@ (g_xLS!+12RPR0el0Sb/_?Iq`M8k_=-H5k_Pӵo|I=𹳑dMkae+4B;;:|G;->"Wqè[~?~'Z[i5ԚY qjM+ZĿ#oYA8DS࿓o6خJ6񍷅lu8l8&,![a\̒#=3]YfS9]l'#ZG{|-NyF}׉o'58u+ n!/{3k7_G_'ϊPZůﴝ1H^Z1(ef$', }M//gY?V3j˭^ #Q6mw;V u'5z Gck9Wߋ,L /H!<Ŗ3g(rq?>ښ>:>\O^}gN[g1B"c &ƺBEooYKsM-7%|C|_sx¿uxMNvᯅ0e1+ 6& <~ |*xġaY$#;ˢmfޫVijfyCï %sc÷iߏn"]1Afܢ Lr'_G^2=ͨn'M>#3).e-vlֲ,L&T'w_gozZ­s6or:,#$6ܘC aM/WGO(ZuAih:Υ1'1š[x| o3uGB-Pᙉy-]q巖;\0U?iͣfsdO*3Ze^Ks GHo(tz/+~+5A.ֳ.z =A`Aʨ#S^{7v[k^:Ns^hd43w2ꮅ% Fo^/&VBmCPϽηoԱ]̑<76e`"OT_¯|/`<]KG%彽04b.*y x#52dIƚxwӼBW|cOñuMF7;44Xgo7|;KC,xK_=WRϒ/1*Lgr4Γ [ VWwSDUom uX*YR5PəJϿ_$x'w:m֘:Əىm h8hфfHl0Z|^$Xl|?RInt.["+I#' ) v<=]ZVM9$ BmBvR>?<[ω ~_.<ާ|:][{VA/HwĿP~?gڊTez02\[{S s||U[]Cyq5[5ؘ]dO2hYJn}!2j0eަ }JnZz!b[wDy|"ZioĻ?oKxMcX7:iGMυ4-jKhU`gibM[Zu./2tK~iĤb:?(gƏ4W#m[Z.hvz5Kmxmwʢ̀4R_|JFMc-RCLf W kx9EWb,89?QV^++ 3O$Kl߉;u:TK˝%5ޠ#  r=+ƴϊ~<<@|kxY}EǽvfqFF+Q-EVN  ki`y3o\#.p˻n'jRJƐsLIh/ol&\]i6W3xWמ"Ӝܭ15$Fehh 3b>Lei7M5 mHE$X<`2gӦ J/_rC婒vWMoϵCw:^4V4ILVvzS[|?⿀1Gajⴾm.tRX'stw#K;3Ş8ּKw&k֙o-5 EדFJ[! JT^៊eٗǟkCdK#{tA!7ncu&xBү^kg`IO#aKiX|9AxGyk'>xw\EW"Y2@(PI$޼pjw;Sm>23^~ӿ~. x7e[T|D_KIm$M-n_.T"𿅼zašDiz|[sw&`I'^|]9߃:> ^;ty4㈰O/ _k`Ň7K}:#UT2cTm5N:k MN_ZZ~+K{\w"RFxefHWO4?,T[=)5(#Ic "U !A⳼M6? Y/v<'edR f])%SBշs_?D=A|9egnIe%n#Y ,B 5座ukn7o 6O~=C !L3 {m^POu5XzK,3Rk()272e*hkW+}#JZ- GʻF\ 4i95RIhyo&ex+~K U.r2My+'(rA604ߊ~=xsM+XW7i7/Fy.hg1 nq]=k^ Ə;j]\[a>șJ10GJ|Fk;[ŗobeJҵ ed-g` aSj8<FֱOs߄oQ~ٲkqCӓsxgῈ5?4$6m5B)p)9շJ0G&ymww$`6Ka5 H֭V#׭r8aĨW-^I/zKɍ1:Vkp\n?!XNړ!YxHЭ 8,5[E":2& QtJrnqgG&rCO{s QYM&$bOjm+k6~chE(YfR,kϦXkZjwZ%Aݞ01Ĩ2z9M{uu]oLm3MsN(7dڣ"lBWGm-4~?mobK}fH/-mE2,{RM*4D?^/[/ á\fEq%&aJZ ?xs^;_W KZ_QԭZJ-UY UC;8>O=w|FΡo/Ɨ͎(KI6 vqcG]ߵn/* VAف 0?zxeFY<ݳ3=KnO9Qs? ~$zܙ"xݙ$x! mR9|Tw~575c!$V۱Yۗ|Q2JmDdW_(4!}Jx_OJ‘*nudid$.8z >b;>(F*nQ ڹcU8܄r?H8i^'M@{KWF$8ROltf jG߳i?U&bɾD)ͻ"<* -4eP$ϾskKүu--hX,mÖwORM~x[aN|~U֣ců]N[ۤ/":ngYd75 V$q@)c{>7ռoYMokRx[TYY%جdZB gn;Esn| J`{kxjDUXfS"E`Daph%Xu_>_xw:e w6JT8*pN0 ףtzӦ6*OαL'Eo>\3N=NQJyeF`7s=}urtW B $ 8:ׯ 1 p*fI2) =xcS{]K '9t<`=;եc79_R7k$7|Ǹ~c߭XFIDJ9 >^|e>y:>SZ5ȅL,vs轹J-Ϛ~l$@q돤f/kčv|7u*y-ZٲBt]ȧ!<1cP@B^2@<,$63ӑjDژiK\ic|1!A:OWvI%wu:[S^J0x=ѣ#[\IBm0PFyl v @p@^sP,&ilsfbdb ?fQj7tSNo泳EyPbiIP+;k4HԒ\s5ڮ^2j]3_|G7&}u^ Y0#Xr*TtE`.l%{m ]J`y+=Qͧk~%âܯ`K$&}m4o7H9H|s]K>B<. @ʻ>e+sl4>mSLSV)ɫAc}LQJ ߑ_$ >ѶVjEE$Mvyeo#H`8A_~%]-~+k%'X5MGy"v&&x#ʍtlHkM=%RRz3gy,[otaPץbuHYalMElN~/?iGƯ;Zym%5"9xQHr7$׎, 7wf/ˌu GP=inl?R_5byP,wc5.z7XǑYG d2dߞ=sBK`2B=bwPW_ZUtsf8231ڤT8澘%|.߇|=}sWo5>lle.ZI#UTR\FX *tT.ȰڴbRXdGs;u}jML[$ p:k/+0KCĞ)7&խlm]f"v|3++/g ~| w_G,48cRU30l<Wꚿ쮾8xH4) BM%STඛaum ]'ENn4{-#@mYI:*Hrj~ſ|+yKΑy Qs1JPJe9PMJzz]fWvQ>_߱[_I-ohu/ z<2D%m(m/WW/ԏk kYы0hzp^ٮ*#[0IF91ٟ Iwh5!8cy"Sf?-y̏+3\Ѿb79Hw&jҵ3JTG%p08gksus4أSgbqv= V؎r[Ȑ-b2Z(grNJ5F@mH]c3{N?f-Xm̌ @rpR )8j6oT> ]:[؋iI|aQO@kN>-e Z_@6K1vYČ61~Z~?|% }W? k|U[=rL'/6!#;'>7e/S?YIak>,!otH}G p*l%9GmOC3|^%Տi2iB~)%ʋ]1!ْOA<+a>/oh׾('ls&c6pBܯMI/z.qMp^u4ny +N( 䝹x+?WXy-eW q/n&[`5]bHSC eY?+Hg)bYE-յ\'R |ߗ?7b+opw'N"o~]ԀKne$W߻䷋յ ۫kޙGi{玕1[5"*a_q'wKs܎jIقM;hn_5;M?Kú^A\I4i+F̤+ g8_>uL)y1cNK=3ۿZss39F $$ZOXt$B_-e#9 8qDr$2whv6$rNy8*Hg8^ =pMze#W~9WDT{-Y_!ˆ9ڌp wOOzXʰ_aPe @*xjD{9۩5aQ4=츀l;9u ѭں;Cொ_YNqcs[âˬW\1LfMY6h5^ ޅp NY䳅eTNLOhPIsǟ>.Gxk<Ak)_LE{W<'gI{9咴n{if>C|'?j|e}+I7eъh!d9\8zXWcA~^9m>|G\<}X%!r^H縎MG挅U=-$~;]wCqk5-V$B m06R3mc׌>"Ikuajzm4Lm)Mv͜0L 5|P3)ּJOFC$Y^aw$"n殚TԵqJ~f߅6g'M"H]o#}^Skd򤶷V\+}<<~5t> GIQuh rVDdY[|~lW0|uuixV[[xvUosu;Ȼү gvQݔrpz'rW-K>#Wҫ bf6B03=iًKB_MV(u|u۟Z~c%Ş7;]oڥK%bt2zɨaHERљ\d1=?: [ߘ4Lr*x9=9,u0X:ӓ0Oc:;)aJH `>/Sӵx:XW7Vz7mVK̩4mNfT [5|?xqªIu}46fRn*rʼnUդ?&H/&@0 1wPRU{,6haU 2Hq~|)o#M7_um4oj4oElls5X癰Gow ~ gğol[ G:kIz.~m7;C*MˬqJ?/"e"RS公ZH-z"pH3Wgi8} ?ƸO߈ Ӵ3+1/m.hQ#Ǖw.? uOy]/Hj:\2c "þHxYDi%x7Se8|QѦӾ/5FM jJ24V`YR3)-ޤ:ZiG6>(%`}  iڶ:CiZ )ca F>I/>ċ?4 Ö^"/tgIXZCs "vX$ Le߮/V/ڿwYw }n&_)CH(?SFf&W񮡠 +F *]]J.#]=w-NOoZUo3Iix4.[H.|9A{-qI,EF;>qtOzqjn7:}ߟ4d2"6eȯj/*]as?źo+O|?ׯ4}[N5UK-ohTa\G6q__w _~ܭ-^Pmb Kx,yv$F1,JqA23mb_Cj?íxž}h>+-AGXkeġiBbO_|EVo Zⶹtk1i:0mTp%wѐ~^éޭ̶ 7.BHRپ%ЛS+X(ڿ??tyZA/iϬ%Q~aoë53c`!V*"4yyBV#^66xQi$ o$i6v !VxJeHpD$UPӖ!XA*9h\S'nn7(- BQ$D-F9\u-:5VG[X㔫?Hr8QI@9tt,ϰʞ{Tr"y,*Vr^ŝByZ$H9`vB}'CY^^Rsk~Fy9 ;3l"VvVm';q%dtnF xuڬe,QG.,~8XI !'-(^ ZfKD7(BF<=3rn2NC2NXt>1Gk+O$]6 $'= -fIdc/*H ӿj-Ḛ6)l{`}E,3 b%PyM7UhJtpZIR,>a>֟(Ga ٿ\A9< vKQ Sd& { 0#'m:jKEyT@d$dYQ8d"ΑȒ y@qnGSO`N8=8U-EdJQR nH11Fk0 YeP0N0pNէoqeb82rF:s Z@23oվ\Ͼ:S(a]cK`4R2 rr8ujV2D%Y\pxzxX\3hv`ꀦΞ1?k7hKޱ;4}O—IO|-I%ϝ$",a_anJqZCMEAL ې$2-"I1p?%hgKǿ %_ Sf;K^)к''UZOd>MһB1dۂ?~߲x'"|#RO2}CO"Or!I>xc |k8w7힫3uKQeZꖱIl+Ǝ"iW$aל#tbx~H|#ľMZ yX4%d޷*w)fEl ƿۯT<+~ :?)[AuAiЩWzr_~߲7jOxCcI9; i&hO= HA KO> ÚVgau]R-8lyGo#D\cc$Oj%)JV?IeokZ'mOᗋu1\&%sbMkycǾc;1P~(?h)0'<n\\|?m,f)-o '9fb1Eڟǯ:χ$^OL&'W o %o-$$l{-^?g2a:< hMFWNfϦ)_qm>WbӵQK▥ܺbmFt|H750j '(|SGxMx@V -eM?0z  B2Q>oWq +@>zXH!oPL=0ATށ5augR /ԝs4wPJBYY;׌{`yIR?W%œAkWUW30=/_b T(? 09ӧ$Vm㻎6Nq2?^*Me $zqի Eyq2MA4ESqww-[j <zqu|j7f0''<`L`I<$wϧ0.υ!EglgޫO v ̍  d1 s :@WV!#>g-BzH(UxLu-L9 2:i\^:T"? *$n#K@@mF鎧GUHso: +,hAt>~)dV<6ݐJA 8Z_C+=l/N1%\F!$Y@瓻?622shmd1f@<DK8t!Ou\Ԏ_[u!B9|2HuoΣmR\.ar ˃`֬\b38%+aNӒI 1PGln6 r5IJ'w$t2 pqs/MIH 2_NmT as⺆qWwRY"]y\+l T 錝~ d '8'8UK()h݂ZMm)' c*sګFItF01A#RK.bpwpy8O[u\U7\v⣻*nApp T{Uk" jLf )8!wiO@O=:qJWO2(/~cgZhSP< 2xӟZbI$ `yך+#|l|09gҥ uJ2-ʌgRxLg'% y'LBqwxu 2I*yO~$Tv n"San:)֐*dY 9n柧D\ʸaur1hbCzG"Eo?/F;c%Cq1ٷ6`wԎtdiI9ry s, 5XBH`۝FBcON Pg=n-.W I]0RI' [?92y H!X9?Jh^9n+)•q'5\Kk{bMI拏0a$C`8\2у>Ǟ)[Op|E 8Iʙ%68B0 " ʹU}99Z5x[f{dA(V2nm du VSK̪9|yP}?K2,(c(@luAT7141kv_g0Ĉ,mgpr}8Y%B0hLCu,[ ;''s#豠@y۞:pOZi+Np. gfC)n7N:`9˙v4qC5ջZVR^FFӦ85Ue!scx,8Hd-$YR۷=~IjF?4`*䷘NNzSNOV-ȒH#6@@ 6I9#9rsSrGNpG>ث3J[[dWdgVo"8rJ0vvJVbf}%9fx ag>;\O.4$$0zՇU{)$#TLB''x!ci19$un2fՄ"+~y'vde?JjTqFO۱45ۤUI6'rxrF}7!2Kmr䲌q=@ ddGbv :pGUc[m$yy !\3`Z&;Q<ͫJ5}%8?3O1텔$tf=3@ GCMluusӡR\]@M ,ew2L \ZL|\Ho~}(No?1?62#NI~Г0ܿ.HRZHq.ߓl"WkYgb=G?M=FSG@xO $/*,`cBBXxziGw}E8Y?7nҒyK`Gn%k #(a2C3<[$bLCo჏ZiBypy0J!g!]|{2)BXe@=\54lHtҷ*yWpO=@rYdowy{| 9F=N;λ;̐n@f uqy*fC `g #94C&w#UssQP Aeo$SW$zw4[%ܪ'rr OC}Gz׎qylQNx-סHs{oqܙ7@DGl8)EɎ@.?&Õlxټpbi1R϶}pG! A [ȥ7‘u={[dӒg]pOxŽхq%n7PPV,~Sd0f4;"E4ڱWHow)r[qr@jjOBpG۱yB=9\GP&G6cG6Bo1CR3q*9nEh4ϐBy$4n4:n38;S3U'1n(=Fh]٘eEsP8SOP),#e©G p7v= 2Jqo)A~GӟsUܲ+b;q׮:rF:U<0HT&pm^$ 3I sa)ً -.$S维R68}@U@"##C0r7(sq?Jb.Npp 遜mDR2<A`r0:{TX efy#S~|I=>h,dK8I0D6q'URV(%?29gp3ڟcra2j3KC*}8?Pj̐7arq۽2ZAoHo6]\u3E5ъ?'8j,Vdc,!*.v>=)._@s5ܳacϩǥ&[E5N!Iip8aF<8'jǗH숐ʥL~^^5 Iib# tCR8o^[3t#BH<93ogva0PT~oI%vːNGޞ,v[͑G$v~@O<%YH黌t㞃ZZjY[Dlnٺm9$*r 3 :~prFqH[, 4+)=P%np{O0A3qM9I)HcpC22r+gl6׫ }2>\SGalL s:U$֢r,`[cc ҙ tf"b[$׹3Ryhb%2 *@'# }{v,,4c~8 A swg}fP)%pyjQ#"Tt@gߵyp8w$ΗV3Æ$g'IśwqR ithnp `9'?=yF ldyPČIujwT} p;:ƥׅb˻!~P0s M> 9Ufg9{n.89<}MV}rpg3ri %g9!H,~ G Nha $PtXn^'i&nhK`+=(}Ntdif8'=U$N2wdoݨ˨c''H=?;VXef3éknRt#yynAyY %1#l[s׮AhJ$iCpH+Z%Z"s:Os`W/\}OCo7a:.*=n5f]}5lYOʸ yI*8Hh8cD#BwS1ԣ/q1*Mhp.@Lg@O$HDZC{56QMvwk%^= yM XFH:}Nt*  2hc$xApݨ(> `;Ά *IBDOdZ"xM\ev!Awj)F@iѹ>`H)h_4ܐ hI?Eݹ0 рX<^5jkf7Wp*݁i6i W1uy9؂c{à?w櫌ulxp-ks1\95ȬvlDcC\GyYasDͭ-d5t~WztKyE~Y OȤ,v戍Hu$e4w!VμPv=.oufNuQN>IPJv5e*Zcenk8Ii{Yw} wj:K|}<k_"Qu3 \$ǎFFt{=G;}5wv EkGI? t׻VyD#`xLj;R}ͱX;toe?@ %ƷF>sKWOa.>q6h-o 3teYumpunk]V5vO~sya˴u?1<=8x39un>,h>GKzn5ή .kK\?w[V3:c \[Yf4Ko_GУ~C>ѽ{\yg/m Q JgI,ty|N$Yq#Tc\BPю=W24 Ov}o%ĈkA_L}7;G?OKq-^?rY5b ];2S@Y$<j~.xB<Tth}}<?П79?3.nslvױ}7\}#k}6Jt7wmӖq٣7rĢO/XH^$~?WpiƁ pDcUt+oMkRge+h5cgWŮ_^SV7&\sb>mKq@9oSyVݾF:2Df/{DLbx)KO[ךk&-m-;{͟O{6,23kTƳzw}ٍ?ѹcKӷX_J7q@JQkL# p] u[S&X|5,+P㨲WWe=ܼ:ۗlzr#U,@9>yh[Z=>hQ=+e7v}cj̎X.2/S?9kϿ2(Kduzs,Yu? 8Yd[7O3wdٚwhx,EE/ni^8M}qrY&W3UFwgMK%O K:oDef޿NgNӀt8N>1e.:3]q&)Ŭ޳Cv;&Jy5YrYYUU:d9eX,$!}ig;5;Xwսx}V8 ̣e,bY՝ʪ }z> +}}q ѲkZk0%#]1ixT!_atVNjFB 24> U 0R'tM8 Ku5[MLV#8M_);*%(s vm˽{i&[; h6hp}:I)Rm/^\vhlDzñrU7D\[iˡa[ w-zʬjF"-]̫q]s+9͖Ct3TVCZc FoUyEdU!}s1Hvf'Tߚ˜QXW,Y7,vR[ܟA癱n¸ǛĕbTJfӱʒVyNQm1~FˈǷtC3zKWɔ/,m_JRNLVc(h%w][':Ab p=<9<[Sj:1i5OjKCh)-rs2jsjkd'+X/b(k$(QKU%gSKSz.vĦ1zv'w/ mQH=o϶Vl^CҼg\]{Īu5C^Փ.Ʊkv~|J"0e6ׇe59EtYeiyV5zKgЍBϫy/UUsO6𤟯BkI̤mZU{+gbFrUx MU7@գ}c-+Y6oJzK7;&mV?>#^7)J=m8RrTk&gݛO˪2|ؚW$#,֍&u'0eo<7cӀ[*2ɪӐ.tbwWWoͷX~|뜺Ћ̵=,K.vf쪤\Ubv*j.g :z Oűn+[tuhϥQ|Woޭ'>WKwĩi^#'c^wgGw+XY\oc^WmYnvԳwӋF 3stsr?. FegԽ~gz'O)22b=%KK^3.5V־bʻ0e.K'OUUjvdA/;*3+u+[ȸSd_YĖzt7jsJjI#:k+#;Q]v2I__--V.~~UzL:!CkW%EPYYk}{OZ/,ƚ|Y-j],[fM|͋3__sht'>feFPs[䡹{sf=V-kur6f-e3Hή z~~<3wK\&TQʒq-Z׃5wYoyz鸗iSUA"Rd2ݥpڲ5 qzK3 |+77?A*OyʧYyk/]d15پ2=Gk_eikri"?W.UBG8[f'O8*w3]/rPMף=T&Y>[S+ޭn="}CL5VӐ67W~]]<*uZoMgA|?sK%?pq}1\̚.{Rk:[w}kz<'\tj6x܍!m|a!-63jgs5[cOGm/.Z)<)~r[ly[yzo?|>];A溈tn&DtOgXG}&Ϩw%f"_9Cuj8\GWv]EbUf WrmV7= F0>.*3g}]Eyx?kq]?)!:'==oarl]c!ܹ#ڧkNV#@$?Mwc+[ؙsE3r ٶϮtesNkJvx}__7~۽o]>]d8z]/[%x+?g̽?#19|wkcǧ㧓-CU{))A>)6?Vmܛ{Gohyz<~q?`~w?A~ ~1wW9!w|JnmZy-xM:sj 2nG bl=@:' fgD2HCn%s( H9j?3<k8.!"1@ 2A#04B3)r^ )N SL!(kк.+^gUSFp +6`.X_T'UEU!OF R2iجʶz8+%׬zC_C[[V{C:eC`+:hӍ禺Kjy=䭁ZP!EZP"؎GշQj +ilmM`a,5MQ9<2EDjŖP;ը:.2v8y`)Bf5SNwWRX}/˿ beiwboR@ʭpN~L\n!`#U]3CڴMT 9lZE!fΝ;%4܌۲b9ݗ̿@_S3kT?Բ[%ҎO( @Cu T MF;ge{WZiд4p 7![*_^DkA~eVS}^*2 ُeo]W΢, @O>E $ üY'"1s2.ZGm$tUȕ2;ʲ ֮ZgfzXc(%F/"svp˥ZJ:e;? lL;r3_(yu&g`V)U ;3[ewF;[E;5atΣ-鍑[kOB=M2G"rIV8eTbr9%-uڋ5^'XU}aHg;ZqyBdSVKH\ycn_]g^N3L"°[W'-O և_zs =ԱϗW`-3o[4Z}momnmU.h_\~gs`R)ɿmuoLӱڶBh?ǖj5mI1%lYt+OӶT[侭|lD_'e,KӰҴ鶴 65/jBi0SUu=rJ߫{b- 'aeNO!!Y{l}s7hml($enKicb&+\mOo&~;ŝ3۵M/|ͅkt~ۊa`%~FP{7vB14TikΩ j6hm}Z0?>pvJ3ǨqGІ 5Ajzl}64kM 1m?o[;:-wC D)PO;.veV|chabvD~͸^v>ǣonuY]B%t:puibyogpW[+|-WM @LgiE@=Ԃ?]JO_o WP{7z0Fw3m@oQ=K[gRTƶvfw:u062 ?OS s+':De\_a65w48qrDh c;N!w{`n~`v;m A]O.h-M o}}}X߽ ~ƇQԂx`*u~R c1݆- 1XNiow?: ~RC?TNŰDZ8~W#OU+zi1W`]eOMXf@) 7;nqF_s hѣjhL!c~SCvT3A}ӮAPa_L,?}|aك%Od$'DaQ5.Xu3VBǸW理wF_Gy&Ne|2+(x~./esߪ_>3mk&I{Gc&¾fsG(XJ)^%x1.;ohrCF+vaU= " 2rCb(u6*մُ]Gb˳;ծvK}IOgec/&E[crF`O }ntҘ=ߎUVqY USXhNW;+LJ|UQTX~*&iiӠҵcfKO[ &a(ɍG> Ye`1գ̳l#Ɯԙf5 1teX7ŪVR_TUX;Qj'a@|aN ^b8gz)%`,P%ԴzbO*ѣ _H2S/C$Z `FL'd(1} OO">7Zqc!J=UM5{T涪1WUL7"R䬲pjcQ1*H y,av X>=m,ϨV&S 9]Ŗ.e vP5NJ+"t1ЈP쯦J#dКɳ;Jqp7!˰yUfcmb~f<9r㻐|ݙbPg}=n%ՀEQ{O;^ªtkEy 2^]ReJ[_h/WYeu[A ܗ'cCW\ju[_O]pxm9~9_k%hT'!{󇟑Wk1^ Gr°)_.PQ[ؖK [+ձ-|t.50ւYiߦEiS<:ϫ>&ϑVq221+!_s}4 "kHeTy ״[4#5^.w\u%όԾVJ_FN-*hX4Ǵ1 @[ԆQE~C1\6/hw&7; O7>u8n顧V);{=:XlƔa V1*"̺TهyNfSZ65`9;]/#4MVEza1X@PiXpm4Wcy3pʍ-!,+tc5D?%֕Gu1DzQfOp~YeZ<6J`qh%k|\C;<;[)cHbRWBcXKW`~'Χ#Je=*>ĝ2kTo r/(I#}ȯ&*R±|Yli7-q:kt ťkZUH*INd=JU/]vJ|bz8թ\!ZqFM3b= *xV?$`wGdZ q䣭F0k!ӍfW3~F^o!ֱ6~knl"?R'C\ snjkM ]w w3T R`֟KR`C=ΨՓY"_XpQߩ[@銉XLVp+Ƭu㶬uۋW6W~?nkc2~:}Xoeĵn52 ta.V_GU~Iek3,{cD9 ] WԗB^\ aSYW֠k?>dk"׶XY1WŖk~[[eؽ^%3FMm^r18sQ)`UVXòmt}qyVӊPдcjvmN 9qp xQÌ[qɮve%2jWQȸ{8?H^Uä_3kjOv(T4샵[/1_c_Ҵy ޶BX|u~:F6GKHuw'6pyG6;)S°fgwrNF_%ߚۚ`G؃X#.NNGk71k2lZfL}٫V KWNf+bUزrv`[ "WWS[W?!܌z+`q WZ5`/˾5|K72mܽ9lU}e`l&=wM38Ti~NыK`YU[HU^/;ɭr?Eߕ㖟<]x1/5]>Z0+SeՋm1lG![de [|d` k`sތS⾢6>[ֶ^EoR{m{9e!vZㆫ"s\G7_i j~/ɲ? #/9c GSĶ79JL\eX{aYuwՎqeUDlWLt0"ɱkr4UhyMtuUgIALqMU)rKB?ْ~MfSJq8OK0pwlί2ggqo@iFyd:km1C =#:<7F$d tV]P'tǯ#6}RxA|K pԭ13vv il̿\~e9_y-M\SZ~̮oȯ0.<:\{z6'{l  e֖!AOlv⭦n~UN=ĐJܢc[ Ož7E%!@q-O('~wC٩9?%Y}%Y2Vc海r{˱ɞh؛;?+ ?f&e7~ڊkV^iX0XJv/&+.=bײ\ǣ0i,J}S˔7]5љ\,寬[^C̋(saL1a-,f\371DZtC:]~JtKJOm)ЉkbW*60qyBFAax`7b-CN5 .mRmqU7htToHlo702 )` ή| ;+)ı+ivGgPñRE;ST)bӻ -kHENAVRӳ ÅÉݒq( b[m0.$JJ̨&V"ӼmѴ>lF^1.yh~ί̡ͼ)1E= "v7^%GjsѷkOtǽF{ꊌm~^QFD7q7;%Q4bxkp:RJ~<2f67^BDKylxѫq-2X_5AK oUwytAex@f)8hj& 2}vdAOO/fЯ5(za;+E}@VLV Q Z:ggХLn#?RD6ѵeFG՚6e?%4̅|W@vbas.EvcMfԽa>z}R܋oYĀ\ITMC4k5^gfCeO _RXIWl2-8X(fwyVaM>:!JwK۩NZzVl5:o njij۪[alzd|fɣL%Cϧ 6em5 u@~PRkQ[E[ *6řckdzez4˛)S)yĢck"CO}~mEEjjTz"z-i?I2vfjC?Yj*t "PZUb}J|J@ >α5` =%, BFѰ+RN LDhyUF%d^}G[JJ6P%7!1"AQ02a #q3B4@R?C~_kQܤA ,7v-((w +D _v'sS$M:PM&';zwe4]YKmWJv -9YLS<>NA L9 Y֕;+gsj@xGF-4LAJvGr1&j۴A\o,' m ..͏?DQm C -%ȵ}JD9C㵴m1tMȵ`jYE q웯2HvD9Aګ!ݸP>;N[I@P k;  gxFh[cQC%BSp;eÕ_) *T(OrapHQMU Ci@cVk*IM'Ȝ,i?q2Ңsk5hh;8r8j<tmT}i +(yVJg?<&¶&4Q* 0>tUa;#ʂh-'_E)g6@XVO լ!ܛgZu "HZi>eɼkw3خP@.PW-Π 1[.Вmc dkFZT45P `aIJe7Gl,fʪ8R>6=1Ք50Zo6.AwVT:7?.E #m]M9+džd{we$uӥ =q#l要qEꬩޑ.I>O/ ӻԼ*^>X!5ƚ,:^}4R?4 3n4Zǹ ;NעOa=k6+JqʗTJ 2)C p7k#g Cڍa(4ͦ58 Ӌ]uemucgw*op=_gmʟN$Z>ж'Yi*g==| yQȄ6@zh\e=I\ Jde9ZP.;ST‡t]'ڗW#H荧4&@ZHႤj5p'csJ6# M+41I}@"K4Zm4i)293i'Bwr5۞SK&B3{mj/e(}ZAk/C&&/ jh{$[BBxxd`d&J]Kڄw̙+(ge*)HG&߯]YR&EF!Lۨҧv2aVd!;'?Tq6ʋL QӢ)9xN1]E׳eB^vw): #/deGN C+KvVArSA#ZG39'XǷk<ű 6͍)f4tilQ yڋi=Q;$M3t 8?ܷh-cEku`v $vd\0rYS;+japcHVNfP%*: J4FZ&nS7${~҃Z7QS<~觍k矧( I1 7Oot_F;r8$\HWnMqM-+kV.smfҸS8Mih/qsgT~'6PXo M$AmQG֣N.u\զ~Ro;T& qiBi-sIdg&sujR9WjׇQVw_]2q7kE5D40F,vroʏ﹠3r)$0+%7YDkJ .ZM{ݶ'8(eyrtlbFo<\FG#ո/)&G!1"AQ2aq #@BR03rCb$Sc4stƒ?uC5KA8Tn٧#(Aa`WBwl,w.SѪX/TO3hp=DJE6V<֜iB)jU*z)ZjPWj?+q2G]4CSogWWiٞm\x\.kZad0ruP^MVF /D Ž}m ~iXʲ}aKEVuTDi G/Рcwu/>o+OD`Z:;K gMUXMpە#IMsԩh $fu›7U(-/Ua)XSKM]P较"y.̨:,)]>h_zLA%¾frIGx1}]QвMڨ<_Iͽsq L߇hIYr"&0yJkIH Zy(ƅ_J이>D8Sk+ ZE܀ 殝ߝm6[x|0Qn碹AR]ܫzY%ß$]M=WWU떊_!ZN7hz+䤩w#w+ k¦1 8Z(Zo-fI ۑ0%66[?EaArrW5}V\M ?_BrhT(YY@4J;Úz&cNi M?OE+*Vk%[<>a`{֞Jc~u'u@-(fV~YV<1tVܣQ!@7 W1(;!tSjUR/kQvŏسs[UD(怹_RD$>Qǚԍ@U. סgaU]4l0=r$ê kת gx/"Phr(%E\T=[llKvf;yBub$/tk p;.Α>WlͩWiWt\{t@6n:wͱdݪߪL'}Z}cU,V hS0Ju G2\82薉v픯KV 茫{]wDs^@?RaRj:Ziap•ktA$ͻ,gIi(47ݠK1%>VoԻ\mpGߵ39ZςWJƽ uZsZ+z")GՅ ݮuRJξ ({0Xo7ebiuV迶uG *vK\Ƅ."Rw{J=6Dͺ5Z.jr -]]гLytG8cFG?s}Ou2YwtܦsYJ%q ұP+F-곺 o%5,Yr}AEU'c} Mݦ셦ڨS*wZ~;w YQ߸?(%LCHHL\AKyO7B7jHRVJr漊>jm_Y~BtQ\N)  oF6 TM*v왫vrc|(ݮλ7qsiQ Hq"p~$) ʒ޺ߟݕVܫG5d/f2H~Qoq B2DZZz#V݅':Y(E*OuS+#:zN98{5/#Tm5) H5t`2 7Iv_;̥􋩊-"AS L窟r _V7<䬈Ǹ|V$,Gԝp7;j+Uҋjʨ|r1XeeCLkOE~+]GUm4u<=?.RPt/kea Ѿ!H Gi]TF/݂ 3j4sZek+[E1Q_ ң PykL #܀4*Y5,v%DZwIPS|•) =uPn‰FZsɴMw:&i>2݅,5>VwJ-KU''CGg,#\!im@1~*cE*pT,ϊjPWLxo*B~Etz)ewV7S)Z{AJ'dEx.@mT,-~Q9,}y T2wa|w5 'e`VS}XZY#+][ tYL-YL"IlnBac`-(Q*j7k W-%D5][*AQeJ_v:W}m+U5GUJ_gtdn^\[]ZjeFJeN<yUVe*t9(mIwQh-X1.<+?:\w #ϒfޠjuZr6+Qz(`eHj`L !I U͍;_\d+OnVRJ |Uz-\k* YosYjsVX hZNҦ2pUB<Wv }TR 6II+OrϦ6}Mq IXjߊæ0h ޟ+_DTn@-T5W=?%s0<~ s*͔8GxL+gROkӖD,r\lUu:eH.ҋ%$1*AXP}wm-/B[?cI﹔*:V]vn(T+LaI |WUBc}W|Td%’%Lz5Zb%>Cꡥ#^ASMKh)F= }9\-ךwPPvWwvV#!@NohCI81?k*6^Áԣ/S`epZ jrWbuU'WoaLIxMべ&h0]|T y/W )%l`ƲUZ\F]hmZPQ,Sop,,_G}f ethRkl9SfkF|ih׺S6>MNѴMiM @R!_]9!(|KZ\!nT- 44YoC@@eT5WXX~h~kޤ Jf(l4\MBbYfk%]aL1Oe;ը?1u}_vm&BZrY' %dtOE@C(:˳2 dؙw5rLm]ep̎jI%<)T3E5ci}c:c@WK4av^t~ T{Zy|i4i:Dm< ܍}ϻ8O WipgWh6Ӻ\gqQ]ڬk:p4A/pP5:X몶+),^O%s@WjiBs\SżUjvr>JqHVz]͸vk}Įѽ;;mloRWfQ9"ovH5!O_4*)'0jz{X/wUR:jo'm&ԸZ4N0T,Y)jg0CrUZLmw?7{395#-g)( !aez+c1h \(T! B- 7 [s0ݳdΪq}7k74˶f "xNV%}5vi?'Q N/Ɯ72jInWګZc_I;O"}m.vaʵ ZH]VJxk$+rz2PۋW']ah&Urw=7]Q8@njXd)2OvA TTmum8~{MT':4t ʧ TRjy&'h0HN;x=i8kIU71&T1ήjci?$} h(A9BnAB".?%ӔyPkTeJ:qnD(+9D PH?W?E0V\ty-wKW$vSoZ)pLDC\<֪/;!i6vu|]8J5&KMMbeG1Gn/BCke0i)ٵmI9]_9߄vwUܕɅT+Ǣ{ѝ;d`XG1l}qі4r RVKnW KTOfF.A);qng-WVxi¥{T2a[tR-(+V'Լ`;IN QU:~M6 pyf.V*4{OdV!en®(f2{h^.4&1]ވ?錶V!_VfǦQ'GY {w-bZ-u)죪iw Jkr \1x9;/IIZHȼF߿/|B hU_ f:y:Zcĩ %x!VRn_'w &ILɼKci p`Z7Frhm)p-`#Frb|跳 ?R؍8BpӹYnr&S ËwL8Vɞ99V[/Q2BeE3^L=J-;`:y݌`xT7JJi-/],y]mm"*|Xh6-Lo A kDUQUߩy2PϘK&i":Ea;))`;hxɿ#&(eͮX_!h2tI%P/]gܹW0‹ߩi7zp T)dN0w] y|lfHRV:/B),wd#2s]WdE5zF^\wW(En=2ɿ@Hw} - ;>LnVa+8rQY7\qSCŤjc~b/͸^n5C=KlV6Rtc2l2 NNNXw ߔ3gU_0T\J8ȧO>̄)糙u~`}Ww,Qk-q)xfv`[ 0Ns;YP5u5ܯb^3yg1{=h!\``}ys0yl襘 t7xKl;ܠbiPCzk V1kj=nqE5kaR׸(+?uǒǹu.83:M>c XDu^ mK\@5T'ۓ X 78%búW\_+W5E ppN'nzS\:׸_`e͚ebDn 5=&R+bHZ3'&;clj ->fWfU >h9Ȉby`!N _o mRZi@/B:811{0:a"Rse;~闖dFx"UhL?Ygu9xpiү1j_pd#X LHJ;9rƁN}l6 xڔL8sYGO~rQrt7qu0UW[~: |ʕdP(T>\DkHܰ͡XE}?R.tkĵ7%{QBW%)t.y]n70r86~bl1!YVs3Md\b45슸xnp7ĻEZ!A<Z ZSYb:c UyDbhWg$cWp' kfhl󈻷hZ"_vN$q 扆A:[ƈ~G~Lq˸3t?]78PA(Vݦ L:smSK 0q =g<,&3}WZ$ξ}8[s&MĕɶP~fh^2Ӣ+oW5!q,f A|)m/I8ȵ3#A %ʼ| n6oX Fe|d?Nfl  s0FJ~r H+%5Fއ3.Us RMCE䘋ut r Q`*)+Krȃ Yɖ3 al Z+_]5"mb?+xU_~ĻG[ыM1%,؀佒RP*𰴼\.dN{bb7?fUgC˜LnPl]J4))Q r246ZɶjCpfuYZț5C_9F,`7 4{ľf|TX. Mb^[am別;FӤT sH7h+eYU \qՆ}AaJ멕䊢?o^Lm/ bTr_i0Ȫ& b.q*XǑ`. <mx{ϧvn-R?jpT)HnwA[ielٖ T5e8J_<>YeM9]YTKt͹`L!fګ䉁/ d?n\SQj'%5c h0<&=!PN$8r lN1/6]R-Mq%oܾ ƼBy8J+iGDC%>K8@*qK":1(PE"Txz2,&eh0#2OP}NO)~IXӌGUI9C.U 2Ys; n3ӈ}ƿ0}ⲧT헏ُ19{A68ϙh@u6eU4U&UĪʔ˙x~ :~0Dh`۔.< ڇgN% &GL %daTΪ>odgU<-u3bk!o-RpK] *Y21h)D3*:Lo@ᏐkIPÒ3R\>m#u7X4(m )}Ba1ߗX(zѭ6ݮ*PT棬,4K7{ ~bAʝM%U_ L0 91M`@w\L\`(5< .*2`#c+lW4q;Zc3(kc(Rv Y ٱ+ܪwx@XGebJzr+6[!72L"29nSu)1}FffZCy,fAh(BCwkpj`9R7akISĪ,8*桅_rC+K1(Wv8*p"q}j(Խg@-fx Z̩**|f ]6bVZ ^HD6l-UӗE ./\e+.KOB#1\7Y&2>FivL nr\C1rh-׀)Hd?m<{%5VEYĵuջ%`âNvòwgy2--d|J҅+"˛u<ƨ)e$eoq}bָs[ 9HBcT4̀B!IzhGzGwTPPnaBJAjomd0fЁHpWVq5&Lf.0b3)9ncG%i0Fs<*g&ELQpZ0MJl 0)E9Uf6o1^ j1* iij"#oV;qSy6 ߘ6IZ9ws5 Ê#n/<;QOUPΌ#HY@ŴĦVد)m\.,2U]-M Wq-Jmgr4z(`VA)1:D+rcKIrYK(WRmXY0BUua`}57߈D1La@ >5U{`~Ҏ̲ԡ\8/\| @s\XmN1"4*fChpŏpQ-b30vlFz_JVޥjS3&se|Ne1y앴1̽Jdb`(+>{cԩ3[}X PϘ/^dFhCc8*\0cav%% $A#7L6<Vbj`(ALyI>qIJܥhׄ[(-$ GU\leagt`hM`? yL'`eRϩ53K(<)^B?Q|;%E1 s\ߨ`ȨÝ˚UD8.`J]yD: ʭܶ7EcF_9\.ЌE5l:Ġ4m D@ߚquN2 _ ¹/r6_ΥL.IzpYMC=24=-K&boaZ<gO,,QVqw"S5e U+VE4mfHJe( [ǂ#s]bX?d}Kf:&W69{b7M]әyU%e8.qLG5Ҫ\R[k<}' < u]GAn䤮xC}e*\U"3˩΄d+,0 |8:̹_ \SG {GQ\p87L Gc 6š.<(x9bRk3@tw91pbj8Sax},rPøe.4]3oTwBnl U'̰W7|yhi)+4ZxP?& vӅ2Xj +A֡lW _0xr53kzCPjzpZEt"Ǜ5W{H'pX4 ~BTS7NWrAMb01B]JkTMi]2[Uȶ@%v x*!b!jgab#uyh{%ˇ^T~ޘJQK340BX)G];RԠJlK;#DK@ȇ>8ݳvR R,)ʵ^# `1Q4b1 0֢re`*i)Fs. u2=(,(ΞoӺ7e|,;'X5j/8Hr=j(uGD3[BCpc᫜'\͕֞#b YiV!0yIX'eΦ&G4eRŸ6bRkGKľ лgtʊu QcQ$hճ 4!v#e^M"/G`&0cW<mg[&iGޖfL X^0G(x 1gcC 0 wrA^Y-2=H]Z 13< \Z?:9Upuuv)O/Rg3#A< +`vW!hjȯĚGj>/u 0ZNm|v'&&Yݮ&}g'Á3z aEr!7e%>!ܭ2]6YIkZ$Ne^#9+z'C&\k6$$Sl/ :-Q,$/*2uXȨx9ˇ=D:.q((ɌPG%(}1&PֽTԩjo϶2N2 OQ&Eա`*: B%C9^ ,([yko]8BLw8l'r9#m @Ze_i4I |qǡ?{Ƞw(!1AQaq0 ?.` [9xD]qw1RghSm0jꏛyGE_xXı*&ڌ-6JL~~!&8ی#Ev1T["5$6Qg^fH٨:I}eUKnY8ݜy9 -?<~(险@|Ade;1Q̗烂cO:^?%nLҫ;euq|u.P]~)LC,}p0p^/+؟kZj\(˕n^+0j׭.+qe}|$S[u_Ǒ0w>c6kqo~j`9e+@`Q%E0]L=VV qh%`uLr2XM<\W~? Uྰ #\TIu*n!e9(fY2.yк3*+S*.qYůj6H9(aAZf/b$Iƍde# J^aDmHZMg;[X) @ MY!cKpЉHBu/=AzCs05_<h\{ % מz@ hn+vJ.cQBYK11؉HG؏UBc] CgYODKspw(!nJA`]y7%̊)hZQWn[* k|+"W an?@:@Eq =bV sNqOyl.c73Ň7%bQ *9j* AWsʂ)$,~!ˬh60n7ȷ+%VnʻSFlq23n w*-o e Ss-SQ3S$[u18ǧy2ŲXS7 \Qı&l.:L%Gǯ{Js7T#wdQhCu+]"~+n%/UĹ_5ߤX+b.;BbP[SFA|\lJRUɼeHk[{D[ +7yaSE,-- ճ2V̨o#BҝďA8} R%0zoV>zAH-Qw Atk|eKC)ȥӓcbQyo+\t_EoN}P\( %@ pW6/>пA>wACXcHJɇ¿0/ 5kLY&UE}GeMo8J9 4!l9^D%3߼m1-hz&=}&,L.azݸJ^O=f<2[+Z Ptc_YS'h8rcϔ =}_Qe8Fai Yĵ{qynS+п`wZ3K߮>cW*8-/]pSoZ0p8#`OF1=rlt[;s*m}C0<~; Q4 ۴TY>x wOOsZpuЋi@y8&m] Z2%WvIUCb3G5b`z~ڱIژ+)q~3(]_Okx{1PΐJ~"j]1NJo&Ch-?`V< ]eN eP rPm꺽kA.Vt0xb-kc[Exh8 +6:] RCQ0^Ay4uO/2մ^w-)a#+ ''P@ |Ӥvb]!%?1R$4K[.T瑝\ {t,t[Y4*((1rdYŐbJ.__ӶAbف)#)R۞&zWVӎͭLx2BV| E)+RQXa[ߥz`]5ױ˙[`vLS<^Q0s7 WMUSw1 Ǟ|`絘CyQA.'WH)8<|( pnjQqo Df>՗\6n-œʚ: yz%Ep&]?-0~\؊ã !awq\RK:wOVRs8Τ 2neg_x/Huf$SVUV_^ L .Ev[x+votP=2 V@/(%㏿0X s|C#`5sd]Yb9Tfj fo /pZ=<;>!aS9St3ُHqg\_x6dDs_Uϴyf yZ% E?)!1AQaq𑡱0 @?9%qѰ\߸Rr'"R( 4K.@ a Aq 韸x=pWPC D p6%@/$)$/^k`ea(FEY)=`:ꌰlm (xq<(9˛Hy/yaQς`3Y=%br"ƌ^HqJ4_m)$׿BWb/:QD(۟3#j 4kzZL@Ƨj[A#g{w0 8LuOjSX<k{zm퐲:Ublݫo5f $lv,^ªJ U@ j38x$NieQ[^q!C\I0f9 &V*~g$~;M@ǞsJ'sO:AJzyHleh]`AMdyȊfPXp(1/0e;ޱJ@@Cm  `H @N ,>_(n6 8hEgۿ=3 ,tDY]YOzɗlh@z\ftIkXzur e$[oHw^.@D@e 8 <RzPֻ'gxlvzJ!pU \wC '۝#7_GP&1,n\ V. '|׿4?Z>qc! >T& I* _PN};{ )22(Z:x#Cq׳ݫ~ oZִǚЋ]?`pq(>B {ڼn!^Vo!*@ 4$&c!(̡hkޣ$'J_q&Y7YuL*&Ng(% sF"I?~0T0&3sL0Af %m\V7>I''Rvi[}5 E=`b6!$HƤ ypbT$$J;o (}B.,b_ y:Ȓ@x Q]Ps16! svr Jgӓ>h URpFa "뢼AB%^K{B$ja01B"< D&kNF0-+߈F\ &o0uH)AT&0Blnא=aG8u3"`v aJI_7 Ha $tk|}#)\7o稺ׯ9ީxq]c.%˧ rO#C<}c^E!ZVHk{b xyz@?hl^Zr/.-ód G8| yPED TQSϾg0|v|`BmdZXly!Lƕf0!0L2}!x> W꿼K? sjArޢpZz m*;̤<,@ Qx@ </^2Goa-?Gb "=%,+@F(On@M9ilKjt XEwemO+>p(h֐v㡷  lbX*i:BT5O5s~T4*C8a%5 uD ` !w0+$WU gΘ"iUN0 G)AQP@YUiծCrf3BaA'B[ ]Ǵb0̊)gq s2!!Ƽ1W\A:6('†n"Fa ۾kr|xeoO>P}h\i|sm=z,GjD8#OIlȞa[o&?Ehn>brp˝ :v3 ԝD⋪Yk 0^+c%Nѣ.`AWY!2AI۴PBZ0R0 ^`/~ F UtߙC, IiC<3T+au1E`<[ZZ@$ah:`!WJ +5rA~a/bc }@~a #BkH0'l [T [2e$DUA/w7ˆ/'j*9 ]Uaq$6Ib<"@f7B/Јߟ B!(Hv8d+/"ft@! (xB<.BzSF#I6;O k6!AaШ:8|7&qLCbp"✴ .ּQS,j6|`&&?sQ/>8g8T)H8BKQ֪<tpLW61j P91 `(WʇЙ?Jx ԧ PCݠ":ϲ:"ψ^P"ZJ"a? (c09λzo6TkpB,QO(Lh:,2Wò@F 0fTYH9 ]1W$Ԕ<L+_HF2hLT<Џ8a ?֟Mo18 op,k[|y`I 'Ocu_~Tkoo}̮`{NR+ѽU-Du |B5cXP'@w# C-IXX?;cR=`  ۢUsC;Đ^I_@%DGʛJ|mЏC:ʄ@GAP*< Bgg= wDcaM8:EC!FR?HA2H30'M(!1AQaq@ ?"тt/ X{ǚxgEQxa'??(zXi_X@bT?I8p'Ra~t-!QBU18IhX76.N0 _M\Ago?@Z5^ypW7C){uڷ 1K]86H/fJ4Nt0lxkΦ'thKzšYqn:;wFבLף+.(-]vO3CI:׼5>}x0t*Bw9&#-k{%V(16qXJwA9w9цZPowZFF?hTE 쎤X P2Mo@^(F6D Rk<1 '_|PH+qÂewNe%"!B\%;婛{ķEGuk1Uyq,iqJyR {-!PR|sĒ+i;@;;% t` u'|>k' qը]bB7 ~1v y%l9-r(}WϣXuLW0|cv?xK56ol ߜRJhGfڤ!IGEQO`ns0p8jB8M])b\7tb:^p£fY9p@|˚5{2=۾vZɮeIkNs:%+K 7VoR+&:Sl4޸ G&穁(n>mclz}yȸ!S>oETQ6AE޺?pA]mU2ļ׬,M%XqmEfS! weR)oF '0ƹ3C:m\LFJsn q".w1^rGHIdw`?e |xt#K% |,} MCa[wl@S[[@ k[A5@n']u0]-j Hm``7dQ E.,Sf tdVI 4~ <ݼ{A,fq<#HȣL2 9r>k8T84r"6/Q1ى>܃i=TPh9Un>- Yyߌ\ٰȆQ]GGf 2AD v .hm+{ f)t;I8o{yZo(o m.3c u0 hoch *0JkֹqWO+ES^Kl>|9Ex6McI؋;I_{02K+ 5"] xk.ˆ=~1 ~ݭd;c Z:{h6g- -]BMm}d%Ds, FYATpi ̪RDx^}#f&WGYw8磄j()0in:8BvL%to$QSN aWvD2؊*|<w;fn:ƭ'*a!k`Hm R5'mDžnyk@-8q;$R#6<"{t RxaFikGscZ£^6;Ck" қ"&1M' w9|6 < G-4Mb8vțFQs5ú̢(fs;-MĻpӺTekgqO /sPׯ`7Po4qBWC1A` (LA^>O)4E,,^a!K&▨MgTRÆk ?*@/ 9k0UAKDpϧ2p<R ŧx r#`,V8:_ٵ_?5KHk&DGY!-!`&'OhFPP_x i`^Y#`/b% S|CQqߧ:uP\&'1ځr [$K JX<pϜrםA9zøIX + >p=bℷ,ِ)8N>0|kRHQnMq$Dw}e3ьyEt G-(e)MkZinsLzChRnDɀ&s*BcIJE2{gtRإC^>񻼉ܺT7gTw\Dzutif]`=H H&IB~rS]T*s` ;ÀCSPlx!AEvd,l2w "3B"ů\(QvK\SxkVʁVPt_bڋo]E0ֱX" C$ DK ch/YŴi;ǥPubFG_))5@Iٿb <s/If$*!+Bk;sZvHߌF&׌ Wdְ=>0"a1~sg8?`'< hE|c>' Q}^.߆xK**+ cGpmWNlp4B$E󎔤_k{y T Bofv@58n-5M!0z@ ڭ|f^_Q}e{ 5@p)y98C]t׭ۋ \{gH]aVk,9.DV%Q4,B!<]ee1+ĂAܾ0@֑ )}/C\p[V}GGp}o.= Gq` *dm*88.͍_=&/RFߟ8pI]"4fH0F[z*(J %W+|۾DhD %ya jNj1 NMaVKۍF>a$'1DdAH Oǣ9w 5x#$g>9izy;,9<+p"7JkGHq^Rm:68# н&0ZpLS:$ OypI>MO* o_"=yTs\} '@ f>|Է2.ܘ)D5М/ P?*E<YHѤj5lx1( ToiF}fm{ٗD6]󼢼xP\7*B5Ux&;۾G댩t!_~=aY`\/H| pWZ.S.5 +ξ;7xbr|fK@th(A<82xTgnkۡ6. [,p3.׬ҵxu=f()w$mX8w.A;ֱBMXAaQZNp884 oCQpN$MH8wA1"_?* kx.~!J~0i[M̧k"ӛo #T+%=gqο?h8;^[4p]sms. *.N;Ηty;T`6(R]>yâca|qe8㛣5wΜ\э iJh+7#Pt;( @> &oephòqKF77?"zbrc"wA/\ 27nk(cP'L<Ñ؋py uxM7MG?d=" i#lB~1қDw8{j9R<꿟8 ko)ӸDq0eg`#ΣiB{`Λ=Qmo^4c.ƕSH<{à 9DQ Re!«|bQu5,^ej BG]6(YuAOYC>xLKN1TY~'"g5eN@3F\G"^N,Up>ܒ"-j(ku"'^I`X5 2 tϥlb`ˏHwNX;4_V Cj4=ݿ.t}62f3V#SM!7<:r,R4m>p٥H0׷(V~+{oˇG@=߬dq,k! xY${ZȸirO8T 1]>=ci#p*{4ho^5V ƴ0p>M?aZ[bhI>LT;k{Qt\*=8Jv|kR. xĈ|]>SidOϔXSNq o\[)hAIX*A(*w_xr>~}c1H}測O )3c@Kp L(_+t7U@5vCG)9`-Q RgXL[AQJ~0fIt"\]A|__ RnD/󖀳gbv?:U ׋ h#{͗ju2jEbKwL!1|VHAW$MÀq#cW#d/Sep1rӠj#êjx,E(с 7qM6 )_EN>r9To809($y5 ~q9.qb EP.XC_qF^߬"!9-9:tpi_U5lOeQ!xBWiZ-5q(:t>$!#CE,wܑj*L]CNN^հa5tY@`>1]4bv&-;K "tS*.  .D@fkjjf+ %񷌪MBCAurCV q(JDŽ p Ev{p%T^|uBhڹc b@}R*Ryڠֱt@ cv/x"Qi(>rD$Ö]Bd"D]]@y/ʫ>B4w=Q((!y"j%|͵ɤHGL`bZB]%GinˊZ#Tj0Dk.'iל|Ŕ6# 8 0lvα\i4B+>1;0V>yJ~6uxɄPGCdxgW(:Kzw[J8=d.Tc]LG5rR3󇓹^ɎD$?weDA8މm< y4S{Y1NItl8-=jIQ%yu\ꇮr!z%ֻc@1ZG'pwez* #mE$csT#쮬ӄuX`"Q16+baЄS~8X2 @# n} b[͈vW1sxζ&z4ba$m񐪀=\Αf %T*(qHs8: %PXu7nyo6 Ƽ <^|anþ2NhCNޕ@Ԩ3p9^f %5EpOjA`!Vb ),iWyP2jueU䯞L@z>K>bYX4?ZYRC !sHn4mMz0&k4~0Ȩ]s*lxB`  *-7 je EǼQgH^J,h}DAw&w9sf^1`kӇ!3 $I402ExqR -sY=vlx\-ِtx:o8+1ƿ f HE<xi!Y8V##U݆LHJ Z($(HЫMqj39P|fF"+-%0˰70'ۑc8ƴeRLU<[ ?-LQFAoWoy7* V/;\EnJ!o01 'jXto@/xơ2fe:W(\|( EA;-0 ڮu/s8"65 ra6wzBbaZ#SB_fj֗ŨW TiH`9Y:}XAvX\@_6rB+c(^ WQ ^Lmxä,58*-osF[,~ lCT nfNn"(m?8K+M=A9bkMe|>nX"jX)C^;ЍC9PrՊ4\C}h#@ic Pgiqc;Ey7&0>2zPPۿҦMoeZxtf|"%GPz,/9xEy1¤ q>k(fV݅^:nw 9[+~h^/m4Y rs+iTE%$l 9#^co2j| × Cx$ c3YƳ`sZ>^%|y,>ןFRHy9ş9m#5..6xFD͵x Gx<_w6Kx% ~g`7q |KBFݻÃ(LW ӚtM 4txśژuuύc]cm4,Ch =dO m]}BH0Bl0:r2>t.34Mrk[P,/'X7+hh48 /q5cĔ J42z體#G`9k< Dnȴ#<6ovB_0@]֌~1j˙Dt0-S2: r xpV47Ǡ[C[3cn~gwho]׼XA[ɬN&$Q6 |`Miƞ-Xs(4+.7a?37\G:Ţ*r.!G^q Pxx4X,J1{8MX(G^_bCI5ou:|?X䏐RcxS wl&EI?Ʊ-3a CZʪCOb*M04$&l@@7J=南Q&ߌ*j~e:ailOZM~#2֕Brh3XFb8Fx~0UPPmE_/=K>ۤ|xZ^|e;N$"m[x͏/t(]v 4E:_ B<(]55%zllek19Er>?X( {owJPsQa 2VLP"ޔ2\H4v-C.PMP:XdN{O'wchqi(4m86Wa@:~+;sܳXUHIWMևCDK3N^ D1xUܢo9ּoOq2\wx"{q_ys*fd}@@4k׬D.0*>b,DjyxoOބul%Α4j5-vpxwp;-`󻹊f<:@klh Z(+XWFEMv֧>IВjUjNWl,BIsC`P޵2Y:F3X<]ٚ8 H Ftn h^\ʧ1B]PbIb ;'9@vtƧZZK2 ܹפ[r ^fw#`0;뼙5p󄴡&ij/<\eKY)|d֘kƨİ_$G]>NA6yQ.Ú'Xk͐:Pr>Bg-"qD>< YPfLCJpة^oǼ"$R-{׿fBurwG<^@7y! :Le_0|9gXY# @]s~b|x7ٛr'CKq3g|M7 `. "n6'=XDYZ 4V9R ׌cM`Wvf Ph$Qxd\a{0q M9q*N'QҔ?$|aQ4Kb@MK.ɼ\ n w;>X#R>K:6'ڋp,N`|ty<L0 yjahc NA [@;3w6RK}KkQ%dCz[5ԚayY\\Փ[o`Ũ 1 F69s!\2AP'e 0>\BA:zgiaȽMtކwp@PyǻF^&*Xwb#ܱ MS~yCBx%{Ϙ@O-Myp6#ysr9ǷD-I5X|'Q5v,$R( s3!Z\Q=7Itf Lڼ4Y5@@FbOu>NMϠv+$CP`n4 Q* HpMl?)bHfq `6ߜɛTw Dʝ>)Ido5ǜ5t]-^׆82Qx ; 5MmPy?>L-at&T0qsՍFaT!tiќm2˃5zÆ@.Ӆ8ݏ7/(w>r tei $AEr7XXm.ֵ8r0 hJ@/2!#oL%ج$XkX#D[3 Yi D@dK?\~0b%߾?8`7K GVzc!M=#TQ <CPӆnƿV jyY7}ţ,ίY|⁳b.jJ+-Jw%n2 ؝?T* Xp %5cd6 y/M]N2j6{'F1RPc,ħѧ3@kk@opԪ m-׿JgC=n# ^Xz)'0w(@$h9ލ;Pam H.E2#.'6i>Ƒm2d`S%JN· @c5ֻ m\{b0 NR]sZd)9B,;Ʃ‰GTG8AFoX()TwZQ Zu Xwn R^i6{$FOBxpSBcbUV'S{7 hXx;67quK@ѷB" 1/#\Ȟ>0$RAdY8̯iSIޮhurf@x hi6>:y̑;G}aJkGk>CvHW/\\?m76HlLɢlNr}bKNoIN! (a:'xmxPZxF6G8jJ^'R'8|1NtJ r r"ٗձ8`b Ab1uCCx|@Bb4T į< ]u46Vb~(4SsAa@CSʯY!O HߗIJԬ R vOY@ RcVzZd\= 1E ]ܡny(ksNC{ ! {J=QƼTH=\`= p#8z) |@ڀ4RUy٠E!w P'l㌉Dstjw+M6[sq`( ok MZr8G KK@7Qآˌ~[xxz}bw>qLI(D^uXgHj>%o4b08dvv8ۛt`OMP`sY &])@vCkal bЛ.Dx;Ť3֓&!lu*=8J;s.WSC4R`dq. ^zw2&ǐ`V*MbBvK_L>3K"\ihqOJ /hq,EQkb@%h8.]iZ=b5BukF}Egb^<4ZpCm5]tBoX In!*RˁD p ]o#0|:Z`J갭wdmXX@Wیz0)xL/\A2@A4:]xG =5bI9(p@#d-N?ruO‰hG^ dWR 0<}ev? r$!;0c8EY^re.>ӠR,!zߌLsYّol>.VU 9{ /2 k:c&]Fw$2eEuQG{7>בWr$H/Q _> Cd` 0Zio^'_j0E/6+iwyÌ] nw vWXcCbJUc0WrZZN?8wnM+'`)&3 7V7Ǭ]ܡ+of%y01? D{H*9CK78`W%; α7wvڸ/W#`ָA \G2vo \v $PT::rX=C6g Ngni^8][ 2u3Q{tdִ !KB|~1cI &흸I~I7 GH=Yqt:z1^Z7dJ鮇vh{ | KGHi'\gv5|0Z_  ` 04FlN>1F8_>&x.tr S +|eDwÔ{˛p:^xĞHro0bn9eo1ČyG*O6BAQt^["vD02?9y0Q]xg ńiG.s$hp-@8JxM4'B!XMQߐ:y L ӥ߬c&=*SGp"Q',=??bQ*Nn0*3z)ZKW+rjWN94u+]vv7(>:v_ [B~?ñn=qs` |"By/nλp]eX&7)UPᅆh-AmJ|ˊZ[&<]QM^& Ï8  h#=q>@C]Kd۝R@ZO@! rWEgR.DB_D:%qłLu, xw[jfNGSqD8mKw+Das6!*o`bb\1n{;$ꨝo2@k'٪zo|a Ixy[F\ka~h4ekjq*`M}\uCe֝-*NvĆ]o)8SEO\M>qǓ^FMNNlΩ6 ?8H/>ߍ]ق Ewv[G@ɦL6^; qɄ1FSw;:b$t0'W1z#~>u#S :$+mtdj bu~D EJ!1}kT[Bc7~stwC; >=W^;  B# q&=vhk"X>*6ݍ ®I+@xGϢ+gp)VVa!x [1Pʯ5+)LJ Eq rt MX_).5&Ɍ. \upNckxMLR]mtmV{#eLp^oOLfU;j+o`[Fr~2Jt|N0100f͠bF   ! 2004:02:20 13:12:072004:02:20 13:12:07< H  zKi46001231143456ׅkHPrintIM0100^R980100((VHH  x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?t1a1DQ򯼶#;$Xҵ65̿Te5.G:cm3c$jHE|uvͯ>ν?®Gpݫrڀ t|1I OCB+h_)jirUcihKdN)rqPք6W7xsQ<䌛QP{vuE6e@A^$)FޡX^xz5 K~$WOVCႻA 5 dish Bڼ~Y 5f3|ekX,zm6iV?98V0݇q֫ ] Rad 4HnH=WDpiwh"%U0X>&aWXIn^EG'n zjo l'EX?Ǚ귺\{]a _OW^ki0:"rZK>Z72Zu"z yrT?~innMt ? nY?T[3_q1Ns׮+Z3CYHXeF{ִl3Y=#B6hFnPEX/_oIt!NC!31%]o]45?ٸ3~n\Wf?s~Hq_ŗʚ縬zUy2PpO (OPC$G#\ lV<_Ijg'yu=_k⎛Q^)KM/N܁{sޱ>J3odzg_h:>{ZxVKXMGPPs𽷉!-/K/ԑ'vc-|GV~HE+]|=mው;+x[{qY'h`0KQך̈́Ze[C&|tbUy'gq~U E8]W32!I򑞃?AͿJwsM-달c[\szʾS3z~qtzg?cw~89cӯ+f seG_2a>݈5<FCN:YTkYy2/X.k}pZ1s-6TہYq6=cNVҷaT~|G\ĚMrN̤|2$潇nZxJy9 Kc-='=LV:Ֆ[ZǞߊ-V;uQ"4*ǵHd_O_ ^-[9uHH!V' 29}c1k_39FR!~P?*1 xXI>UztcZ]q9@wWwqrǧV<O_oPj]jđS~#Qa+4:S.[Aɿ]wZJRzzo|ku1VLnSՂ`pOPy$`x,uiWpF`U2+w>(xJ6uh5`XnUd]ј)aP>hME_b*ӕiJ'w[m`?_[ZZĻHn|2O$'@ǟ?5~.Ω̈Y4ֵ[ί*r@xʶ^i53nϝ(m_֒o{XlWf7Hp;e|5JBm9 9퓌M+GCK?4KZ5 aRJH%Fޙu95*|;_h*Xֱ; #{$?7i)UOL?&/^2:N20zBE sܣC,)>'xEHi7CV1#*Wnsѽz5|?%IJc,lV-ψ=_IJ&zO7W `߹W<YRi,߆FHk%UWmVwV x]Xnt-gGo}sW ]\sVs+7N-;181HTw W<)|c׾@ 4m>ktf&s!|~Ꮓ㔮~P^!uwShk-?Tn%jV2XFFI ᎇZ4uW>^I^+i=~Dx?q48|^ ax4r^ O`/EnAD_-3T 8;7)e<-ዏivVF̵U nQc8#=jCJZX569eUcau42!$p:s_oG3-!KF=y=b, ڶqJ޶OapT} v]O5$T~Y-ΎdPG=ִ1Sm_ |"hvyN*! $ta) OMhu멵JNnfݧX`/W\qz {zJWKOSn ҎܮG9mM aK"hPNqMshSj?W `G 1 WjlO*fxU88u,Sq땾C+D~=440/,d-!Jֿn'0E::f Vӂ0s3_˔8E? skF@-&]Ś΄7ukM>QkLj~~I|'˿H]kVr:Mv%q!TE9Tk&sìtp$Lږ@5-6ϷR+|g>=Ѯ5]&)՜eգ~q_Q%Y?C8eR;N#͕ƙEѯmbq?c69s1_;|L/|}+O+}Ng X\rK KcG𱯳_3wޮ }~GK߹[Dl={7X[Eu ,b Nq½d?_ϥ})XH$(Hڄ}kxn[bOge<ۚ4+ykus3,|7K>6xKB-4hUw1$)䜒I5>/C>7Nƾ "m:qť(~l6Z<KAk,nte 2_V>_?L6+G_ x]_M[ =Ƨ$"2 I\ mݻ]GeN 24#l_<G(x 2m[hkoSC?xP)lSI%FJͫc{ؒjq<,_poMk".q?__r_q}fR|V\ cj.R42{_lW]5G.|#pe ydd";;WԑIJoS{k9f&LUV?H:/ rߴ/‹1YG(mtڤtiXWʑx_yOZIsuuYjyQ g5_#mYOW?V-''ë{0=Wˮzhd^@RwsN0pkڿ#3r]~G?z0 [7_p2ˍ+9f_C^[XnHgGWL?u:J8B)^˦x|6M(Iiac{~eA4(Fdv9[ۧ$7^&g,2L$Xae96YIRݚˣZ-4s5QO/MFkcs :¯kퟍ/TxhTQ"@?a?/%'d:?B#ȏ'g|n.9oz"B|GԮcS@)P}]`WX?e3]U}Gx|rk9Z't '^XጩːO~?~XdUU_Rj-9jc䝙d {f@=TC)p~_ڞZ̶%i27F*vAn{|{rNV/gĪ伷6?ɭ?--^!&ԍBǤ2wҽN^\v;#上3!U*-r;_aJ}篣xQ}6]~I~q;8úsXPw0Ǩ C4"BdDvYGa Ti]=@Ue}u6s{Ch졚% ÕUBkˠ<W"#RvttRݧ{YN!єw欷=_kl7) ܇j?h~>}}+cYR\" 79d!j77ROW(\V>gոG'HN~-ϊ>k&5~6Xdh< ׺k?g__k:C㿂;mFFI$cdҿJC%[ܔ]:XUU9_~|0mŬtGBL5 K+)adB}?LU_NO ڽA0X{٢fB,$5[RtzmsZ$U!>v۶_L7ZZMS>o3>a/%_Gh=H?ПCf?x|Ii-}tkfkx~Ϳ 3wɛtڍ*]7쓦&/k׷ԭtl9Vz` 95?.]x n(8 ;s_\x/g>󃰲rG|V]λ=ʰj>w FX.>v ھe<Kdc6 9o̗t ᚪ[rzr Z^7-VHUsݻgTsGDp?г!cs.u$IkMb{{9QdA eaiEj\eYK%yg/VkW=5O Jr:\Ҽ 嬙$ ya Ds/>~8kMt_Ι%ѯo)')9jiG^#ZhSxckzJI-Or [F%T2?W]x?Z F{>j3d62V6ocf_?C:Χn0г*w(Tݞy6ܗ);! DZ(Ƕ_j 4cl8^t/ZOQ5DVvѴ{%Cѳl-[hzmVv"_&(UcI)ڊ\3]D,|՗|+< rJƍ#FIn~I85+X/ I6xFp8HNd;M|YVi/V~|B>|NlOMͦ#H˳202 `!i޿ї_Wtg񍕅Cwk<2#|I H2I:˥ 6vի37jӓQjroD|ugT~2{uynt!Ln1`#oˏ!eJdӴm;J|ܡ~7+i䓐;༹Ҩ-/Yu}xGM}4NG{A@`X>X#(p v8?~toW ԵkIqҿK4 s&!Ep d@2x vTS9#wQfmߧs(ycѸjƅyJ@vӧH J y|?q--_f|5|g8\Y "3m$< }=8 T 08]9MpzYծ gP;mBF78qUKU]T#9M0Qa-#P2:rZ=vr7ሐza>ܚ|ǡO{~>m5j:zmI 1cA5ח->V}Z효umb(2,O=Ost/[8D{ʁ+ar3޾#N?2xn]f瀼+'"|S+y%1Z4E?1wB@}|Mx'd7~x+(_m_5ޡ}h|܉㹕FViw. ٚ %VVRIFVp˃_S7zO4KOCTzbVeթv*nE~~SxƳF,mO5ˍEfv !z 8<S%*C|UbjK4" }A f+P)^ x_K>M jT{5<#Ɩ>nO[hx<į?ۼQPwHFAM#S^!.yxW/u/L]qs$¦/SG-?w?*k\ BR[o(` OEqdue8oaBjNi%)oEs8Y86m;Ia){Yh)%߮g/OZ'yǫ qo!,7&aFeׅ B2\2`A+4,DS9^iKgw 0/Ě-mM6%9Ubs$7t~6CV^M'U-_|?Auo8Ξ;HݼINQy=8 kt&4i6̱! >XfqoCX֨eR*˷{fnCcgI+yA}+tz>Ѥеg5׏avq!vUq`;bT77OW_3+2u' 9H|mB]ؖg&E{FŶv$W:Vr3<)qq_dVʥ8ۦPANӜ)asOկs?hI%·ŦvK-mtVHk$_ûfR>cW$[Ede,qtC*DK>?NO^1 Y|c_ʣhSxSv#W ^>*yO,H5C?FT~zv0hL$v (uVI6NJU5{zvuA/6m,2bS>K-ð{XS> (~H;'cx? Z Hk4$0XqoEG.Р{DbEY$Ub \U?OџQCDO?6ynvuD3u":W&oK">+vSGYqKpQ OU `m+M%ThYJ>ѴD-+LLJHRhI} mģi86'u+L5+P>дԭ+MM7آhi"i tC-+P$$i$$HHE$ P$Ԑ(JiN)@(rQ'8n D/j]"<{ T R(Ror=HJnE?mpC7#bg>ϴ};8H,2@H M֐6g~i&jj Fz~%qM[x~jJm+Ih rX8iB4}^??i/Z *hw`?T< Ztre@CyDtW+T=} <'Wգ44sv;Ouin uޣfoꟍzyG곃ܔCGot==V}VUuhz%ۧCGH;WGuh\cT*t:ka/hAR!#'HW;Bu[9i;Ռ ) Tdm-J RI H%hZ M(( ! IARIlQ;PFy1Bri"J pQ !@MN J'v&yHi㝂o{Go@%;Hr6ԨXRͫpd0Q7vH U.vwYmNK>Ȑ.Hb'D95 C̲P2>6E5 -DO8D&A/#}S$yMT PIP)9Mdr @J]IH%\G (dPD ^%Hw@$"[Iݨ19.pL0kr;,rO dB=40i*DtHI$ )DD$ BG98O$_ 򅤽j(VP 4}?TR&6Hd}E+R(O V!bNiS!@ q4,ip=:#F '(D @Q@"IvH|SBr@{ i$Q@#DAI$PA(!(~@{애)l]$(<%t4 y/+! ?We3[ġi IHZWD$-$-P" P DA+@ (}P$M'EFSLq@$))n7^"R@Qr{QM("wMQ;^IϪ F>zUqD8G<Д{!@A9Mrrj !6ӀGtH #hFPh hGtܢDnTrBP@" v} %_${I$) I%@ cANAtrW910X9#I%awmAg&]@SJ%B@R(ZJ@ h6B@ %vM)"SQA$ VGp+K I{&y@SIJJE4h8{"(;]nRo5%VweP,>׆ oj?ݖ3a7]#H19y+Fם #Ur"@cAwS1ޭ絢e=:Wɇ{ -kID|D.p!]€;XZGe6ٟTA{tZ9 PD E@PIP봾}H{ E($GH$P \pHI*&kMyPrp;K{7}Jecu4Sy=LحW +@hZ(J&% ISI%BpJI%ɤ9@$;lQt]> C4#؎4lb&ԲP(=4LE9Y:wEV%G&Rꐹoysi]V$Zs`66~j;H|),qvTjo^WWp']vWl2~>  BE|g no̹JrҺ`l vhYGqbƸ]D8j\fOS5s `GF?t׉ux0Vp9QcDW}\NϷt <w <x6.;.۔g,Bw\)ZMN@@SQ@A|EK ZwK$tw(J(ZH BQddGnW֋:9Z}:OilS%݇# eA&TZ\5wu*:%ɞx8x;#-˧>-v˯%sM+,hJI hw@%@𕠀-P"TSJSO(i(iu$wcD[( a jFG5*'Bk};O!jrU+v nYό nr/fi+F  wPe<^ʹi,cH{i7lª3aqܮc#N!rIuu+|N^>lyu =TxešMGAn+5]v=nJd| e{@ dXKE%܉{k{Y[rsL1j94FN1ˌc%I!I%*b89nF1B!v0'G kL,\ZPn<׵)$.W*/QRkḐW$n۟.cC9ܭ|n,̕\Y~b9\f{OXLzJDcCcmǕxrF"cv>תx+,Ҙɤ8{/OZnGꚜ9^ 95/dB썦65+@xE H rW7@l$(Z )( ,^֡q"17`V2Tv^[[5p|q]d2MPۣY-s>_L󥁯;EZvOHy,ia-5cZo~dy͞5wSuΩؤH6}nu#'}ߙ>uǓǖ,z T0:>k/Vа%zkA(!v(!e"J!&@JjDi( iyPSJD T$$hu EdGDIa,eeɎu(Nw'2P$o{JaqQ8Nˤg rv@ uH{p|M\x€ ktB8Ī]Vg7bz8 ʎ7@%7%p U&h.M..3 U:;%nzG?2L'ց7M;eƺ2:3a<(zTn\~bǰ& q6,4Գ.g K$ZD i>sA$]oa[ya}5)]Ůc<==+#3#dh"w tJ׃CQ g˽זa=Qջwo֑#;'X{82-:|YW_O+ {l.C /v<"]=MmՁ/Nd9" ^{X)l&g3__mn7&3F<2znSl3flokXU>M15a#SI}iԳJ4n7Hv:`ȲyH+*221Q]} o&rtKBCHd$/@J(HP;y@o!*ATHH)vjo[8خx'XY10y?WreesN7k2L;D.&Wd$v˶ZLR25/M&aWO&|i1'NbƌF.IdWVZUvCagFo]3N4iX凊+344UXLwOKQN2nMΊh6?]'k]6Hn8_b }l4/7^EOscr _Dž#aؘsMeeH_;q⬛\tY!HFd.x"ͬ3&RMG`5]񻑽h$WE!r8q;VƇr 7$ zp 4# K.WU4V4Ott+_A$rHyW:#SC^t61곾?CN{~VoQ&VGlfl5)L#Q?X͘?y]Ǔ(T&퉒4n߹Z!ըaFf$e)xq]CA%+et޾ i;Z0o ;}V5%X9mlG[y{6 $sCkt׽i4'6.K7 Y:w-G  |2= lL-*hWsXgBB"Mm(%EI Hh-mA$A("ZNMӥu.i 餣HMDWM;"~iII#aOȜѰ켏x<cշZ<m0D+nG+ӷ|ǗlrɵF>yEimu dCa2u#F]x4wՏϒyzB1`cUh.^G=+OY>0H\LsؒXەۓY;}lMnTw*r4,{H'ti xy49fx9'zyS9y2su^TɎn3:zR oaiswͭ9kֶ;h)qtXr 2t7]>7Xv%)Qv絮?Ajab:wƒ>Jqߞq^;r4=^^L.\w=9ˎ|֖<ǖ\We:[פE &be{zrSHAqK 1+tϭ^l4=mNQIu[@wqfmI!{X :< r9EH˹(;#\4d{%tlTY&24*T-v<1ږ|b7zO 8\z4Ukc |CGW˚c%mh[r9Ē{Ե>VLZ܎|<, `zSZozA6Ѵ Z Z!_dKhZ6MPt ֒ Z)v+L"C?ӥSK9zitSH7ajsg!oǺ126۷u 249H, \l]YFS8/qخ>أ:\O11n#Kȅ~ZϖVq1fI76l@pɓ4MP9ed yd3ts}MiT,ȶ sF~ԺYM;Hm{tnOѨV^;֚6K`fֺ$䶤vGg .s$۷YË*.o ]'Wl< яt)=ݯ:O so^0ۙk6🂮s]&I 4 ߧ ؿ|&bx<`x˥_,,h٥w"I[)%IwJQ|SX_o- {kAv"&v;*q2ӡx+EƓg^wS|4:'e!O*, iںMDjtDc&| e{onY/?'NۨVJݞ-d \Np˄?ZYNT0% *5tp;P=/Vi#݅آ]Ci{% 6\6 C1coZY H|*i|Cbm{-8íFZ$cG#Lq֏@tUAM!+5rIt@m*wH!hP9/@Vw]n_N+@l#Cp=+2[^cj8I QAP&TzV\xoESZl UI 5Nf<,` l: hYsfJCc<4YS>eyrL&3hDؙClgĖHKu8rlΔ%iFpEcۧs,nQhIxà_ tgP\2_s٘ tht&t%Yc$oNUjҽY=ZE,?+̋sAcdǡK˜u S$veq[z,Ib |5&n xs#/rx.%aWAͬ_Ǽv?u\~?K|/-Mz RE$ ! otM>ȡ!a- P7n$KBI|v ^%J' Z=(qNwcDŽ[|i. [QHrB\6K}.7#\EOTaװnh6ۻϴ98n@i$˗7ѓBޡӝ=ZwYI&GxoZ.W4]EWN b+x.y9M_Q4Wx!Mt{ễҷu`\9a#XC|KYSt1=^ϗid>,߱"l辅ŕ?/v23FpW]+:^`ˀԑ88/Jv; h.ç@ܔM !2acc eIGk̷!I9geˏ+o$F;Ӥo]צx{çDq\ط91:{b.o6'nW{44`xy;O =RӤ@:hsЏ'%V7_'o7_ 3cug˞!YZ׸ 4oyo9JҏjF@[]<Nt'F]SzW92N7ǞwG'e+|qk 7eWdad|4M5Ǡ?*nb/&Þ>xQ;Y`pI5mFp۪`=1p~7izIk~!|Ϻ<-'ű.2崲:VO8?KlX,Zs/Lkeˋ ɄՎM7 i |P 9/6i갞̌fDN{ ѺXuZqeks-$ e3Ph?uU۵ߒπo+f*y15џ͌1/Q YX^M8_M&xT@tQ|osh]'nEt^;(Ff'%;pxDJfP2„HmRTʳ:f&K]B÷aErŭ[-uO`/ W.Ɖ#{k]ZJb-> 7mǹ^xKcF8+4S^]dbQ 9Q{*TvQ6ŭOs'Ҭ,sE !|?\v*bdsr[OAOZXiVgXwOYmai崾tjF;Xkw 8/v N؅O۰U-8{-Ou3GNџZ 8Fϫ1ffn~ ]."kufbH8z-p]ד/x>W-Zh,j#`Ы>w &5νXϨyÐ+ j$m];;_!]=qKz̮K#[_Gc6&9/ xǙ {l3茽+(:wKoggo:q;)ʿ 1bjnda%|'4X MLs`u.p]=CѺoMy?FBaM/ᎴͅyFNO~OZaR}LRZÝS{}$\+_1;oM="gHE,X^t[9./`LvNݽ3!%|[|qGQ?T~rKC\X*iGľԹVRCN)2VIr2aw+GA9&s#RF|1зwOgMσ͜}N1?͠T!' O/$Wv9i{&h'^cÌi; -;6-γWI_=8fp9/[MT_R1q.: v4ynVFq/f{$cyJp$^vI!H}Sig٤O4 <-njdl ǢFW𔖸{f;u8u1l$5x$7|t,JPam;ZFZt8/N[l}tFkp5Cbh5-VlMglw 7hTu->+q`>rUIXs`5>Oћ$@ofvƈFʘ#EckOi?N? 짷u;nGHt:"j8/xʸs[. Ǩv8 %W2j䝶RL ((& y'Yޥ֒ unFzlDQtHDpvn?+\.l/ a:Ų . ӝG@C[ޕ}D)!_*kcIQ}F;` < xRH@guC)|d=DL-5W4՗w8U_W =krNLQ|SucaƇ~rk= q[ۿ-?H$k=)iԎX#S?^YLh}[Zߍ'Kuz+:L!coN]֥2Iߒ'DyH<<5sod|zvVGN%hv1ؑ3X:Ÿ{{ԿD~ F+ .O`Ōjp.hQ:75olޝ39J+Yl؛/M?]E%c׾=aF" =Ӈ H$H}H V10H e5akQ~#ˍt!<Y`F{ÿt]?3vXgTs3H'/e,y;$Ar2\w7.K'M<]H{uNX\ 8jn♺Kenj2Kn]\왚әy8:q2q%"R=;[X3R\,@u]Kx%[=LW/ RCkmtkt\6ߵYU)@7xkewSK Ҩ<܀9nBY)}9}trdpݵ{w/oDž٤AVjKW ]0y8mqH_k8.G*G~;p?1m0snsՏ$v=n~Z>n%kە:pn md/t~Y Yd]uasADV$l2?0%.\ˮSLe$,CmNTg&E$09{Tǀ]ӞfIY游4w."3E ~4;q15ghD8ܹ%JX,5$S}ٶRv8 Im;ja69$ڇ ٕxsM1}8dDKkŊ ('^syc#Kcu+9oa10i1Paf@PtW{R*I>9ƦN'}j.ln?},Y9=9w.Wx# YܰgŐUl]D5nvsOUa7U]N_W% P\7o%{?1qܯ>jHG1[#q-b68cc#iLh.6۪I 559pOpϬ]L qmִ|}G%E%h:^7Vd=<{5K;#.#EsކG@c$qkXjpo*FAp;zN̕s;4c_vрƆ_ׇe˦ pE{a^=@|84o7Fм5N]!zoDz&k4OyS"ǖ.?^N#;)/WSvR.YYr @4ҺJ-\RKC7 WKeE=Xn.qe7Ql`5yqnLKl#yl;½|yW++zt'I$AZPdD7T۴R$FFz[ؒyE+w N\֌,Ƈ;V ̎ A Y&= y|mqzg_ȟBa|/`4 rOy\^ tG%%` ⾫9];aGfdCF2)~Buk |#aKvwTbɊ]nDyJnS7!%;f:[hY&FGhٷay|%m!is.RaN qçlUnH$`h湿 ^St;]5YUFM<5iHF pO,RXA`QK!qڏ;p 61MeĒ*a7d)q{ghݏ꺜)I6)rq#uKY<{M<Gld?z ""8 aYcvm^bƵ7}1f3C/-kN;eZDm.\n2OCiIp\ ~b,uAVv&+UNZAgPǨ X.gd'+}WW͝t96pM rPhҵ'0ܮf g2I;58\\m1Q :\f1۽vZMs+[o+ad|j4Bt.o+Q׶GȮg~1:#Ê8im~Ⱦ.Wgke±bUEco/|I{6W ܙD 6|re1L{ߦlb! . `O4G!y4D}z𼤏-]3/?M0a H fέ5GJ.?eORwU~_HnԜ8hzM4PQI$%HI}R(#ZX"oM~+}j)ᖼu<;z֫Lˏ3^y{H<_u]7ˋ QW%%ĂFލ:K66+u[ܷ=3#C[h9 ҖHvpZX' &=]١g,2's149 xs3M62R֭,ɟ,T@۲0c J:DxS'xbݤ y r1nA?FYv QL;/bmdDiT&ǟfK1z|9as\V{#>Y#WY`ڢ=作W|/l~}cgN;CXFK-9Ҽ#עy1YSIp!{Cyg1`++v@%xYߙl 5Az|I]T"<:qן`6 ̞F#' ߞh۵n o%2O%n8E0|EKHF4-bͪmDr5W;,ׇk 1pO+d;AkM/|<ps2&$y6k;OMۤkc!I9!C(:;OI0BI5"Ժs| ? KǬM}ckސ'ScX6$O<8t[Ubm aaxVAeƕ$@Mibk<}&_A2oc(sEr)ǨBr0QaB㱥oGaߢ`yUa6VۭE9.&YqH7s1ζҤ -&2X^{B'SAWkC\U5ƷDOT,4*6c;kSK9XLyc|7!*W4w A4{+'tm/. q;~ϟ-0bJ5nA"8Wm/?:\oSw|buuL }2Ѵ5s/Gk!4v|I?Mx{t#M? ӟ>sN+˚Qk<*X,\ Vτ;?_m0c4}uԞ&[ot2TѵH:8bƎ,hFƀ,+PD٢~NtX1ݟIs =c9#ph%q$:Cr̝l`(;UW|\l.S6 oF}>sώgw}cszFs̙Xx<Ԫ2xGIf02͏lWt#P S,Zᓨz#4G?;?ݔU71ɁWcӥe\ҳޗ߆(X|0qY2|mO@Np ;VRJQTd $;Dc~&g@Is$o[ҟy98).iB͚aΙ6VX-xg=m6WP$eԍr>29M,vW.o3!wWYF<`'i CHVQԱs'\ttW2fz!Wvax1ecoha⻯wcyhU,~o8V3fS,J|o说E?ģp3CIHs4ωku8Fs*zR쌉wkF,O zC5x sc+޳Ieu b`9 >t=Y\CԦZ'euY3p1n3C~c5an;Wo!<:}O&s+u텍 0eb$Hoq*lWA3\\q\axwO)`@:FV.VlcKtOȅ^ҺFiT.Wz|Ř8aQaZw]iox\l705ѿ*ItbRz3>537zIT9Ϗ a6bu_j}k5xd$4ߕܧlęRM4t ,ow6tlx22!C}Va9odLhV/|oآ o^~N]xfcⵀ1ukGD4'S/%Y7*tNؑg|ȴirUIUgXu8u$OG쯹t`5OeG**i,(.\p=񓽗;o/Uz5UVI[o&N<ʰul젍,xK}F=$V鮍$ؠF6 9)c;T‚[!:Q3 ~ԃ5N'V1${ҡ$NAF͊VpǴc{r|#̋/д[ tv-胱Cb#$|q0YVxQ2 (Y `l8ivI/讙ԞFKn=ʱa'yd8CriIiEXom'29qM*Y$9UQj+ Ocjcd`vޑNuF tlNCfH?QmjU->IhutK˟0C; ڭ'¾Os}]9>To~HB&9OM+̟=[%XÿU[?u#+Շ8_:>b?_%T߈}2`K?ݡ{ijZVxD+Ix5f9X%znOJXg=i vta-(+8]Ş.V Mjwth{6A)$(I$ I$Sn^?#v q^>8lxǚ>Lw=3qL{JőZ;nfMHJ­GRJrp~Nٯ|)vrq O,u,hXKO\.j"Gw.v#\Weczђy WٸwzfJY->}0EYVqХ򃂛ZpPޔt{8Y4[蔮 #t%peXm߅x6D}ܕ?l9S#,܃G&n|oQ4 3ɕVpn7:ae[Fof :}'%۸t$k\Iz6BHIv?5.,Lhsѽףק!Kyce䦾@|;q.{[=TX@&Ԭqa%&R]$nɣ1dž;SAFqw-!@ݔz9d'd-J{mD-K}}}__t~kysI,/GwӾۓ >O}o7'Y|]:nQzaclׅl)ʹDߺ'SFL4G8a wb,"julk*c_IﺏG 4 1d2H\6X¶T] YpgEWF|֌a۲ik<L,nw'&5*W5Md~Gʍ+杶44 Lz|fv;"][l8GLŁ7MǔJƂh*5`& e/gr <wE=1hc7p(|d_r蝩UPLHȚW]P?x)ޣh쥖&\,z'bMh:q?H=ZH遣*HMcM~HsZykvˍם27)*Ąs3b^k95U#|%$jդ >XkLύG7jƗyIچ{F˔Z˝\hrtbq 4sĨ #I$ְN@>T^#@S (PNdRHHWe64zIӵ5҉&hip}wyBDQs䏪팳ݝ/T>ӱ.c"M,kOE$ qsI*&I{n[xo/''zc,fJ@ft&8;WӖG QY+ƠraLbwR螡  .4;GK2zlyב㏚9VVt3a?vEr>spLǯF#+z_B(:C.V [+ѺO:d-xPB]&pcYp껆4i5,!⾉u|*uO/ 8DIq+FV-,Qtۅ6zO-OxsbCS)4&y/[4M4&F)q]כzT+NLOSVdxN4ac {y;|y%v^,yrg5] omq:>tdHh#H:Q\|4aj׺E~yjDLjns^7uAI-i;*Gy`I+CNuZh5vPu~-co^jkhW7&8s~} M=#H{L6/k i(M4(x:?rPC@86v{1j4H̤K6It&t[QAkC`Dtק |mkt5X'F Ai'm~;)۷J i(qCսZz)Vvv`!ީ X=4 hyb{"l/W+|صM|3Lohk%T»$1taE$`~ Y|36P lU%blO*Bh4}%.lfuXeEFNcVHG[noZǬڵv7I ]8sזoA1|#a$-.m8™c,U_]Aăuv>j7yѽᐽ֚[W&70QDA%qVq@Ok4X q$vTiYېa[ vP{McU4HvZ: e{d> VǒE;ʩٌ}FIR.|aF뺏!eMpX@)::HaEA4L~;I |8zH^-)V)#an*H7}Z#41]Sv-o46!8{G)]?tTQH"64nPph$䕁ּMӁlnlҎZ\'V.fts!#v0)K1\ׁ aJzs3(oXnsv$۲or˚FH-ēVO *hX;W&lvYqC4:6AuaOl1H&SJj,)|uF&DН/$Ta{q{x r/!Iu+5^ഞ}Ok7E2:GPQ7P`>.w9.jUdž"_; sK88Mq$67XN_- .a9n.SObq20ti3!G]IEn SB+IV"%۶{&i4PK]y^ZpQɶJF_N8|GT=*<ُ [@r'#$ hWxOysSWs!%CuFRY&\8VnB RyP#v5<5մI+ArG [tdiO"=–|IHFp#nɍ,W SuD޶uQ;|Ud4im?m Ejw-{^/Nsao/KmL;m$7`V^(c.N\,t7~wW{tc| _"b?B6oc@R|OѺTQ3@uj7/u 0X*>D Ljlu,%PxuU?%gOž(xΙӜ{#:ߤ+әs%gANi'FC5FaΥ6@)&Fw> }~ԨL@cMueɖ_/?ǮK_oO+k3wdX4:ˀ{>9ojBY#qpyI^~ײ/ dFtoYݽ:GT0>1uVf&dQXCw/(PߺXʾ~c.䖹$,v^9SH"_+k %"oki_% p{r:,1(.24]'w?Ӌ_Pl ?6:թ_^\^A}>2E}_5uO!x0v(F{G/]3$HHT:d8:YdlLl_?{,5™CH<Uxc2ɿ-l/6/HĘ1:3#rI>.yrhDO|6hhkxG-#q溨=pgwkZe1tC-Vh_~CIOunh/n9T8+=hp㵡,LsMv [u=!ްuV{|t}bmRU1gVȩoKN d 4A8[ ;1A㲥$%@;sjI&tN4ѽR{jgD$<>{(׌?Lide<7\^gĨEz{mO$Ic9.y;jZQ9DM=A$|2&Anp)$Ǧ HcQfKm.fFf@xo+qѴ_ޣ3;Oq'"^lgf~ɋPqr#΅4W<8ݸvQSQ:Qfsoĝ4u,rJ^Dˇ6Tr5ã7s `@mI05CƸ۸*Ec[3+ApNEkb1<l,.g_3 7Y#sp\9cɇ?(ԗȡ}e"VQuo\$B{"9ۥBHi$ٷ_^/房Hr faf8~sCbY闷jk7P( .ZlInyw j\nz|L=j2Ĥ}q ح)dysCFI&8U1GP#SCbO[d Dw)=Edf޹O6ۊ3@-5g.9skWêl6|#\׽ {M8{K-|QlhC!`%4IsY@ޚjǒUu;u+Bõwe]%cUITpkUV1-Xi7 B8@&GnARNt㼓;R*+Lr}5\dJ l,!.WF>o qPĀ֦i{;([Mfv@i{kO$|жFv->?hi;("55FSzu YVZr{(kGp9@55 JPr)XBn,j'Y  M7oK=ԵG*(H4Jæ=/~TfV54UR 'a#6%48JA^,$/{Zt;@'j)QpzL5d q_ ;4`.x-rKyݱ~,[\@%yc.VCopu\< ƹ( [74I;gG7n@ ȚF2@ͭ/ G ul@6Q w˖i;O-˩AWώ01|DLj6|:PVHo{c>yj3LJjxIrƐ\&W4p ̞4&OC(-$u|EW`Xq$25 ww^8>ja5;oQ;Zi)܎{&:m$~IgtOo=hFDJ}I$ ;ۺ?DSIKUt7DnB^uy֏.pI7{zo$]ӑ#ԕ"h?P*$m*u6H  Z6I5꒚]=dՍ ;2SAz5Y8◖xAz4H.z0~mvjIA ,}Nl?E#HasݸeTߎlޖ*iyK?Wӑ/QOT8+B7FW+is*u%3Fe.# C^9WYߟ`ۺD>[~J?:szY%14Dv1;uGB>3eEekG~|..: wyrlD_E]vTxi#V!̌0 x7VWujUrǘ9QJR9-`.5F e~6]897p濺!2MPӞxpy|nPEH.-gό Cy淑c%F"t6u &lKߑŧlu]!2DE~!UZKѡڵ- jFدr ϱ)DQj 8Qi3Jʃy䵄9Y^CP{4'rU6 Kud/}%j/9.6lt4杣S 8i @5VߒGL}͝YCجlh'@m7_Uk=u\GV'edt n=#_&XXX ?I9FmwVruGklPO+ GZKB24#7 ́xĹ{>/Y}Eg{Ml{oS6 >a#Q7FIpE}ըgv3q෺/[ ᩚ8]nsK Cqce1gtag2HZKkeɒ_>:KS8 GO_7UQbҸHY^]w2f [X$'VH&ܽܕ` fU-V'{q/woa]^쾣޻/pf/Xc@v[Ile q9#m_=Yٞ)ndb5KAȽMw>/'KrjGK>= -1 d;0^"Q=7JSOIbh͢Ot;4QHKd ="vD܁$Pvh7Dlh'd/WwKo/!@i;IO`08/P;/SƛSUK>GzOJCOXf3Q5Wb:[rOaA2h=p:E+n*'P؂vM^WǪ!th$>uC+7\Z۫0-(e0m!"~ VF+u [^<:Gt47VcVb$zŚ-^qcGJ\Yup^!wT3fD_ߒ.֝f>7iE*ϋvTsMu(œG􌎫1[sh6k?!\Ƒ71&W-g-7y9s* >[a\BlO#{x.jc8F?joL[s0"Gx{4Fi쏀n8c1e8ֲ`OZ0L[Ce =zuP5NmH|?E,νRpFڴ֔Z RJ?wւL$gO ;_`QX:TKw"M#q{#G0hLq&'c݈Am>Tb<}SVV-ԍ1p?B&QwcXy5*IO)ȁ%+\lLϕ:*QOۻ%sBǓ)|~Xad"\+&<\g8гquh;ϕ˦Yuh0qkx&C6t>i{?VzB^aGcPw]x1O7}ܮfXƉ4Ik& EN.G?z>v.חx!ČQl]bv*" f­[!Wo(xڶ&kb˓C2څy^r)[|ל 'uJWgB$j?cW{;NlCa^kWG>&0G^^IdY)ug }0𪤞 DwQ` ڔx d(O;rju(覽S0mQ$,k]idh6u!LΧ6X!Εߢ"7bNcn#'<@OJzEt1)9,ic wn2WC,Cb!ߢxk?zTOrÍ.N (YItC@lCǟPC{X1xA6; h@+q9oULlh:kXvM=~@IG 1akFF}DIٸng NmycIȉeV1%;S0ot%!Ŧ=E1c*gH~Lhkpg{#7sGP7ceaWꛀlQR@#p kHfo{;᳏'+ըuEFq5+YxʩVn5[+Ƿ$] /leh܃C FmoP1zN%7a3w =|D^% 5YYϨT029k.6UU&C&y'lM^j`f+<RLq"Ia-H z>7]PYX~SѾԳfwv{w<4ێiO- $N2= Drz^98x@-#򮯥C 0W<(6%F#vV#-yJP< y~\ޟy37i qRF=M6o{v^UiΜ>6Yh}~~֞}i~?^|s_|D-wcA;A8w aNc'$zt{^r'sɿeTDrbܨ=G =YK)k!rP ۲i;r*CqG^kDx!ۧw4]q =/BFyHU 엵$T|%tx (c!I&H֞n4.ó8 |t}5!]̄cvsM# &7Y< ocT@ql؉;r2 g}ׁ hs ZkOtX/۲{ ^GYoe1`om=x ߠv;-Sr>. scF]x4x*AYDNT2WR54oЭ/uZa촾 fbae곽vFZ?52<$WD s@\lҦsoږkӊL܃yzcf:g@v g_t5RBG&Z/l5dڣX؋V1C ,5HUgk s8Rot>|s\(W[yZN$ (YQ+7 CnlϊG2,h}M%mS%`k*[kkn3QaۚEK4Q.޽7[oi2A7g첺Cp@64SiyF^{~.kGu_tѹ>>1J>+իny?+tӱ(տWK4@=N߲gD(vx #+׏֫gTEN k>9SMMG,Wur&4yo5["Zֽש4g楖O.֍{iuE$WJKߩd4a!oj'h0iv͓g2hٶ8E3jT:1ƫF7!1-c*?}/]^ ]tDsx>^>~2{8kI2_X\9[؏y'F4խQ.$^@폧9N?H'c{TmM;"WuXبdjcGթ/kD {苚5zvo)J7m{[Bo`2lc!w Jbk@RHȝ{84dXD8DnB $rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/subfolder/gallery3/da_boys_3.jpg0000600000175000017500000012226110276336034031244 0ustar madduckmadduckJFIF*ExifMM* &(12i@ĥ4 WWL Polaroid PDC3050 HHVer 1.02004:02:20 13:12:59PrintIM0250("ނ"'F0220  &.  6>|N0100f͠bF   ! 2004:02:20 13:12:592004:02:20 13:12:59< H  zKi46001231143456k\*PrintIM0100^R980100(%HH  x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?$y֬W_tQ"ۀ@cUOsWʓ߅ܠtb=)zcRKC3Ԑ 돠+f@jTt:c-nSD$y55`?xפ~V;uZRy͟P1J9o|ΚKF4Xw7dTθ#1?uXE2jw2^ãgޥ8T,';r8g|񳶋eov=RRǸs2#i=\^ؐQ~dW' xǻh:Iem?Ck_xP29*c ^8:YGrIRAxQK*\QY/)|Zzwdj.pO^QdFjZ+\+Cf  ZNסSKj B;6s]7drų]4UވZlZt`plϹZN{(~&Wz{8DiN7{qa#՟Z&kx|Bd}SvUPMuel$hI ÚWy%~|!<^w  Ԟ9'MS').c>VP O~7KZ=W_]<>!x0ZjJ.%G.E%Hj/ Z > ~|.1}=Ǵd 8~!o#|6App{{W){P!N?RW槭N:I}0)ٵM"8=OF}`_ݲ#Iⰵ߾_o௪C 5s#5A(+61l)ϸw#ӊ]_SRFY"}}hg'֡Me,!Nd_wW~½Jebrؼ$GRB>^[vKiY6nq`θϥ},o6A/<5.^#WJ_?b kli}$ͪYC< +=c8Œg|?-a#ec&A'q|mw?n!mO>x=s[WʸPǶ(TkxKM[.G8w8j5ۗg]pRoG|%,l er8ξtwUPN=2>G4֎CKv++V}JȲ(W?SKx/տŸ[x+B-`dA [/pXelm"?qcI(e9's_w'/G .ֺ~JSOZ[Iit B˻1_Y'i>7lGxMFep{r/Mbe?ρz -?/`X(0wև|pr15V}6S@Y<:4CmD[#-Ob&h/8ܕ]/ņ`}7~5Q?7~O$dm}hјf&ϱG圍?%>egxRoᏅ."SЖFϴX\|碑1ߊ P[CKk-Uīҡq,:u&67>(׼G|s jM8ƽl'&I`e?Y]+&+-[nWezY TDU*ƻsE#JB?_>e|^#Ą&"QԿ'N+^ߚIG??V}GOz]EvrFs1j;v,.0=,ǰ%oFS`|k??#ZNsa#soh\ėZsYq\w5_ut|M"b.` ANp}+k$~!7~ηz)jkhyq# >}+K Y\_]V'3e=O{WZlYԡ*Gc4ږ&?DI j11^{C ce _eJ^Pób?}^&m'_zGW8]X79#r)o _>@6kvf8s]k/kfn&s|E'jR4Hfm/-"x)n8+A8T95#[sZJ2A%-.wЅWq%OR_+ߩsdY[}K0g;P/q~OJkG|~5M_ +4;:1yX^]I@A!zo/b䕭rGS%17 q|>k?mxNJrSswnvP WqGyV%i/i4o05g'+0kW_6E\G4mn>Us |w~?ik{,|5\[K.ʬךP;/ww|QzF,\2bXItoKp[37SeZWSVU+*\Bz`}/Rdf.zp8_cJo~f\l8NySxn Y,GWHJaq˞H=+l/=Jݶz|)?i|nʪ;jz /%#TzW558kK/?Z_4r2oxkk{`5On5-K{<ߟ k,a$-²I?zRcFH5B emt=z[=D%p$Ns_ 5moC?4FSϺ?>&FO F,@6ƿ[8HWGH?x{_ڏm~}5k1c K-p |tn, mKO!0 8!Ôe:q R!FKYEInQӏfCqi k!=)O(?oFHˤVvd1 FF?[|1\2;jۮHi.47dzq_F^YP_6j3:M[R% 88KzyR}BK->z'?_%;ǿJO94=MjVZr\\|e C0M _ng\be*zxi_%^Z)cbI:6+~ *[xS5 YFٴ6ɱBMxiCQU]yW-|Wq\CqFj/S4M)J6N>?*L|}MQǡFam"FE[ )w9_ HjͬvWUU\c%((sa)_Kl* V::Oe %ډEw,2C˒GfK# wplЫ _}ė8<5 g4V$^3Q_{Oma,AUpzFk~!2ˊ-`Hgo~H_hv{NU^sNO\/{yo>lma6[ ۟ΰ3EZy3Xgpi{O㐓RWV6[Ebc`u^#ڌב.K YOm2v20^rx&;/:rভ&b[' Ǐ,mfh㬒ۻ "O?葉,l좟P.ImQle It"q;;.GGAw{$U| Qӭٌj/pzAoaMkJƭ3ZԆȬ?4yge~Ԟ?pi>+YJ2mxY^1%<:^+ M9|$dIeщ 'p@[`+R}7^[& v +vIytFxdVფ211ҿ?!É_9~d=V6 HK$^Wzoa~?KKȱPa7O%FnzGqOE͕(m[eہDhew&Nz?5O[L%EԂ{_O'o"$; UVW?fOd[}Ѡw0s; u^h#A*Mm9/sQ#ti$&9`XY1֢?}*mV7W/iH2zWu=L*q} \8𞕨.6 QBPtH08%79SW=[ZuY[\cQK1p0儿/׵3k]Lx(}5KIW3O-*d31_Ϡ_DoƯ@tIѮR*ol՗)B0܎p2E U/4|TiXnD=^Igaʑ2>R漪MG_;|aFRëii放}0׿eO ivKZu-1Xw衈9'54x~9: Qcђo#f|RklC0qeO,ʔwZn?υ~"g`WնvoКf2DɵApk%V6My_Ѵ V(TĬ`1qҶ4tBׯ# ~#PEvR[=n#b~VQGǿm~Wֆ/7glw.c & !Ÿ1ozV?~e߄SȠyPޮ=pki{d0 }鿾?[-LZb3/xC/s\WhFvWLͽF9U`WX)R{4>US2^ǜE{x]AfksupE2%q x~x?xKm6M ^֘eN+s ͶG%گO x_Դ<;c6dG՚q Œx_i_k4"kk]lqvџj2 }ydzʪ*tpm{WÍ2;GUXQ^<3%LgdWcr.Ɏpea Zj9+ir}SOW+_xkm`Eo>Ikp:vechOU+{#&ɾTINjl_2t-~u-nVW0J|Ulqѓ~u,;UJ?o+UkC̏G;]mH;:Z{m:-~}kGRzu闳9KwUs_V~%߯$ 5݃/_~C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((f"B!1AQ"aq2#B3Rb$r%4CSc+!1AQ"a2qB# ?(v(9^uCD{G(T ]pr9!I+@T5Ȼ*M=JjK)ȻɪQDJ`[Bt]ҠɉCgvt 6b;-LHDH@]PF؄6zMA+MiWd$c{BJBm"6|!157j.о& pLT"8V]ʁБVG?(ÏuSu%2&c4l1i@#i mPA *F' R N0{#! Hc}oz Dw$)C@N*Q %r)6H#6irT*9]٨?'ʢ߀Q{@Z^H3`Sիʫtio `4lc¦H5 Ybma~JMC$&Fm($:<٬%j4KҺCxJЀ( tR%=4 tIϕt$Mh7a-(LJsNS {L\b&D_5+T:@2SJ=&RIP{IɎ] rbi9E ʌUG+@~8ʌۉA&i^rrRk%8??dp #Z `e[ZZ)pҌ1Jd!']&D=DF-GmyEH`g&T>+ !"B!(ڀdh0 iRU q #r>Q Vي*VF(&ov# R hl`'ړxFl.)N[PDJBRTrc=/2hP_)H2OhI bm15Ibl]BNP\ryCI7%yQ+& )?bTCm neUInbS_o(\hrtmw%ɬJ(CE~ho)T; M~r?,~R!^9NQr cF<ܚ6#fӃhR,>POdO䷎;RF 9]4 mݪD*@ME#ʨ+" p+U|Q m@6ꦆ9F@@aHУiR ** 0 jSf4$E6%E^7*Fe4lmNќkm`v\ mʑFrD.m$1x (-ց=0;M۩.6(˫I8-1(IMXE bpSmLF3EUt1)+ (J۫BP.67hڐߔLQaZb|%c T H D ~@t5H$0!_t6EMVSpPkF58o)A+%rV8hqoM/Һ^0Dqġ +H0M|򕡼e?&%!R{=_)C&cmU9(K.DII"QB~&1 '&Q9@]hI'yʺ]>xAWI  _*;M!D#kCiU>SI)ti?@xE>Q`g3mogZW (Et 8U9Dв9 $MUUȭ R&DRAࡤaF0xB 0|#By 9QVDѾDvZYWS\%0\G,^nbAU^iiWerkD\dY!Vam7Pd,K4Y%#68kpeŧ 8hܡ|\2vŽ3_l)uAR %![k[A5H`FjF*F6eJ&)ܩ/VmU)oH0k F`c'p@Zqmk,$Z7omvMbOZG֕ZX@֜SR{Ha)( иk@O )ɴ'IӓUWA)mSN6;45;[qH85W k@UVgUJL5dO )q@AD)0$+>0U)YU}'k Yܮtx^=.Wn{tljz$m<*3@R3MN7隆@#Rt- GQ;8pjVoMTo^F_jV*8¶øEؘIJ$M#' pb)ZbR(I1 @V"TR&vMhI(gI5RlQNxMI*4~0IΫ쬛S<G)e5Yw BjU!1O|!8Bb~'< Ô&&(KTJ%E@N>yM u-w]3D Oh}F0?:G5E|ĿtM@5Aڋa ȩ0`yU;;G0!oe > 3­pji <{؋x %k |i&p b _;(/#aFmQ]gϕ.ֱQC!i`UX{^TÃDfٮaQj##rKG8g pu@Ӵj.%fct?Z/ f%aU,^ 4O&˒'LHF/CNS%ON#֓[nsYk=rH—H=ER<4OzGۉ]LcJjtz`^<ӄ׸Ycgpqi`xY!G8soi9~B}*-x -QwXBG)D~wbey֢'ie?Z?\'xzNl䦂M;KKrB{yy0vZA4KF6i)zクn 6WD 8lYWLFp4(sz}R3G,{#>bv SFe!xBJJkvQG6JV#VAE.;yŞ+Gph 3tUy.9M_)U'vT;Jh ЈU!V4=AUKl+:}bY 7F;d#E?*G(C+(QHrXhJ`$6zzJqE*ր:GZ/K6^QvϨ7V:`-.ctv#.YMQYTN#Wc]WOiӷ4-xVI?Ec~AX~q't= Z%n.+NzD..=[ft6kZr{:>W;\ bvIC4ѱ1fgkٱZKFb%ۀ==:=ooh-'jsgkÖ'M,aejb t! >/ Iݣ]K|l.5/ڑ$_rj16-ՕnLɾ O^p+s]閟>Hޫ 8,K=c`If$D;y~赍nnI8pO٤ύye.!h㷥lmIcNw]t:7Y;n+cZ/]\rjb\qhG*<C٩/NC ORx~cj9lVY_) -[t#p"W*VTý.# k$VWFӴf֩q%-9cߪh?xv6Ř;֦M8i"8$|Q^LϾ$ am]Th.mB ŒZ䪬+q`[ӂOܪhGR!gR.hI$OKc_MyKFU!D5.#aPWj,w_0:h)6=fBNR;QR,X!6Ew|U_@\_˓o gk~~8Mi*VFPN@N$sSkV]I%)c+PJ-+Al4:8T=n?V8 moUE?jADNPFU#$Ā1Ŭ}G7ISJ?yHclHZ r3̟IQo|,Wu~CJ-7&I415&?gYfcwz |a18][Dc@{UyJƇ,r27Xb20k$u/7y?l>s'528X-JٮI$o^r tH \7tsK4d^֜;|)^f',8:[h|-ܒrV)>0,J6i^Qn)5,,򢡚yYǬ1ѸہYɱ-6-w8QM_TL۳i{(Cf8#|&&ّS1GU?xn7υ5mH@Sn}%Ӎ5J}6Z(np˻ӧL Z{0I_Q!)6꽶GR=֬+#(hgGE̲mD{WN=e-._ QT ( EJ;nN2ܠc]!M씃ͦFqM'+p 5.ꇁrH5mryb%F m.ۦJ5=hh֟oO}ɱa ŠC݅$g@q Usj9o̚6R69X^1${ˎI^~VFFYy{'e؃>Od0{T#?]O 9DYH΍&Z_"(H {-]팓VWηQ(OKei^oVZ +{ ̫Zzh̰#K^<`mjv6iM$LUw# OPXk.]Mm;۲_Mjgp{vհFؘ^Y+xG}Sѱ=eSgNy8 Vj4ªoL}83{I+#CցpztzhVgG(IKҢcӔ;ͯCZ7~B$z8ztY\ 5X5hM 6-=- a 0p,¸8U4׾bsT(:C'<S\.>z:쟇Jtm8zqmQMk$s{urB,FnڰH4MvHPIpk<:n\M([Ix39eyoZCdv#੠qVAA:h)822noII%D"x{xw+|y_MHҁ#{s9N$'&5)ϚB|&՟,ٳ['ߑS$i[&Z%s:ױ m *Ũ?u%46snE{Nv]>OsTqӗG[6iecI.{9OĹsp4g$ >Q\g;ɞ麡ݥ{k[a; ۀԎA>|sjc0~" Oa}>ؼzL 롦rS緮h ?҅ws=(e4i_Tփ2M>Ph>\RMZvik[*")gJk]kLаj'>?.8%I1[#`|a8iHRKܻB۬*mu3]j7B1V՘܅X^U68T-Mh#@ NjU{\G]i 6x5b^ Xm~'S+DIAHd\%,ZjĬ\kQu376ǸYotS  vYޡ0.n/S{eG5!\YH4{@f.EG4h: du=-->3N=Ѵ_VE:7PC>0|`hH4T:k*}XVJ8[`m$xYHI0n-sSQ;}C~SDZ/~Omic4'lhK@4p&h)Z@SuˏynaI hfE{o y'LhF8[k\1ӭ-K}V1ђ<O 䨠h i*hp|->pF8F IP] =Y;sH7B~XBRI7sB΍eV?ZueF6 ifiaDk@##ڙD@v v֓j«isʲ4SӐ~$L(}u:D8ޖK.A4F6E+h[]SɶBkH^ ,N l{V՚?]_ӽ;d;#W3gjÌ/dL.zOӵ X|^-O:ˋKm|KZP. ޤE(w%[~I&]H ZS TaA$hYM5k3\?]À$+]EI2kuo'LN?PM"~CHlY_G9|^6F3jãg 5at9ҴBi)k\l`ن}5G\cy^/4YS}?eqnؚ;>Y$L %i% xh)P YwR/齓. BgZ{F bi2d R1j*6deE TX]O6rI]4IsRNӽ kAʜnU̴ryIѝd)n#Q{nMgs(eSJYeܦZ3Dʘ`RT8ʳM%N0rA*5¹Y'»M~'%^υWh>Voh-i9*AOu0yYL]i*IIM'Ըͮk6P,#puB/;nkݷ?cKc,S%mDRhur6sZƦ_xquOԲO!Җ7udЈA/.fE#-\w4Z v<46% 9Q;PƘn>hϩ'yR៻F? N.p0p\O]7tzW6VfhVG۴PbV,ŞyLvu҆Hm768}ģ'=ZGGk j,M6>(LrSGgy]o6c+N)ʠ`p +bNI09*mz/&6Ǔ(9ƯaM御_!6NSSA<,ٺH"7RG Q8:ڒ0FFjwQɧpZ qۿ8P˧J1sv@OG,;v28CAfW$l4OMvBծ;Fv8wumiwk^kCwuR7Rѡs_bs}=<_M{[M'v8iCX!`ws֙eVRXǫׂхdI6*Z$ݖ~6eYP -; E5rÕ*u-Kc~_֙+]s&K.P ݱ_'r2u8sB]m=5Iݭ4ձXEc@dyǛE w?`5Rq8y8]ZPO:|k징K#B[\ } "I\֞ tiHdӹ|,x除>սq};"lh%n朂)y=Na&nIf=C3q[_h>KΟR'YK#hhyr9;+;GˬO}/8j0ʎ]\QȓRln43 sm_ < K3Nd>ʹ 4dyq$`$ ь+2TFڱ= P2h5xQ8hvim!0_UBJ1 Eӗg ȯU6FFt ?i6;oF֎?ԣ6\~;'Q?LӯW R#?ZmJ:(yPh9SQ$u6>23ܤ;R黜Fυ`{,IKZq5n0*uR=lEDӞq|"ڰz;//- pis V $J ͟찮IO1x*M&꾺m,SQ^~74+;58mm8%5j5%23 v u,h}wާ. m hSy*M8{}x$wIVhⅢ(~-[3fwnZZzd ʆV41et՛'d6\yV!uxJ(^q]xl7nC(AT 'm= Rے ]d'OLy<|"֓941<,{r穣1W1iy'"]wJj:Bin? ]Vً%juoktHͤd>7Ç?K='O 89$d{512@x~^ݿO MtPN.XrxqW龣q2B?LsyOlF P[FZṹQ;-Z_,L֋;/!\0Rm%.ʥG<{ltd`Xӥv{OӇ*),H_XM7WDv ~\GPi整TH#|.Kn7U0e7m`Ύ/uRІQWJ.V2MPNiZgl .qPI:HTiRM\:`'qzGJdM%sLoAyd%F/~3̷wa{?۹RLv_?(` ֞ԡ>PLF`L s 6&]g,>[n8AWҧXŢ5iE96;)6D O푯kh|nCM%;yM,AX xr⡰Bד.Jc(]2cFo*x%0x 8 mw)O#cokQ/pf Mё{XIO G?> -6CӗyQ}]Hh6Я4"$h.iH@{+g6Q6q!Iẩ(#ܢq@0ZlPxZŌM~  ۞l+MԭvӚ!W9%%R3beh$ۅQ'>b;-=SAwH&@ 7RՍ4npWQkim]_¹Y哑 Q;E;b_Jni@ -$Z]taeB0Jdd5ezd+'^14UL9aq<o)SNGSm]KQi֐ϴ!|Z胅 !gtMP[],s,JNhLҚj*hr PdI)PnN,24rR@n3_* Z\fn9-Fבy?Kx+8ٜ&k~P_F2*F )X*=JN5Xby eNdn@.Zv4bG$:h]`8Ut58[]A6S45?gɖW*H=KBͷNT6 LcHey?='Afv^W>4.1f|hkYyKytMq}RљRkY5^}[i5QFMiB_ku/U3.q$5$pN}q@imR,)'<.ΎA'_ Hq]Jïv:#kݸ >~.ƃ|"9/F+kB!=r?Kz}{L \oՀ~O? zuh!Ut.,_LtVU򹯨cg׸ ޙ6Xz[;Pd-^*]?@t^i'ph-mk9.pA# "^ҧdl7Kz3]#'|QL)<RҺ9l9!{oП]i#u2 v<6_k} PkLMPRmtˎrMxn _n-+O!l?x( ۸ϟ-dM4H|pe==/o3̏$CEk;Kr|Y\QSnG0Ms'_iu޹GA:GY* I&WI6dt S)qËv.vN澋lx..wqXEkmP30&o4cVJKETࠊZв@R #(I6*i0Q^K_6d[nz(9YSj]PW,.p_nZGpJvQ#h;w$<{C85jRFNCs<4)#cAsw2xwrGET6`_m;p|$ 7c}O>Ӽ(zĺAϕeKM-nv;%xlVMb™amgN]$Ȼ6A 2+ MdAUR;kZ E ,p4pTvrD54/p"4O(dscsߢH]aYU_nJ|I%C |EYC$knE#5$xUIcKtNښbZ$$%1|ͷָ4Ѭq ׭~W*s}i.4HuYX Ը+hU}}p{7A*T87]H>Uo,&qYP$hA$NGAA?e2h1ULaҖ<ClbZ]9 ㅗ >Fn Sw5F ^0!ͣ>a62(c,~hR駧Aܻ*f]ҧLup}BwJ`kI|c'$!K@Ǖ;/$^3wP`W4rnQyEi/6mL7E6D<8L^BOuOJWR!`䬷Ë\8>7g {y3ޅd 蟳kdԖX~"8%yJq+U{@Uis +}H~iZp'<+0<}U" —{p7搱I.֌ ZX,wSH@pG !c}&Q56jDH(O{2? nphP5ȤyCBck!3d EPLFQ 8mr'Oz̝E'ñь?Q3"nףtt^4n{9xUǩ)͎yrˮ9_s\Ȉc=] HÂ/uK;f3.d=0փa88/Y1RϢj8}uh &]NT:\M?igy/fRYv5\ûrci6;`gMӺFPxRM;^eo [@ riyue>97ઈ]U}dޛ*ɳίm9UϿZgSq`sZ 6>#I,w-m?UN>EuSq1i?)8n/"B5{[W`u ( +ey$֎=4ej6\7T?mh @~eCA~.Op$ p+Xݬ`v!.mQ$-LMyXm &0 ucNqkL^vPb-(T2@ʣYW#5\.F4J f{@K\<+t+!Q[ ZzB_ǗY S F.srǶxy. q*M/Q}9dAHZw{ϕ9w#n#eՙTC>6J$̍%ܑ>C{fkHi:`^JuN0\;ᅰt1!c?[1-,%|/X썅 ӣc .5r9Oce֚kk.p Q2>O]M^2xRt$G X nʛ5Ļ +x-MeNД\SF[.YԘa#/vc6o}fjpܟwLƖǐV~n7.-VӉ}nPܩvC5kIcnio--tM7hh wq$o/3ޭE {_NNtW-=\SX7HkdnJ =xWz=ΰI%R4Wy ,GGFւ M" !n,p d;Eh =,ёbс_OY4zGPMնv5 U8v Suԙ"a&sdU>N׈aiYWOOWM>kֆ7[#eN88vREhX vIbYwЈS{(Ko#`v=Q[,JI7{vu`i6Vj`.hIہz,zuqpsy^EnE,'?yk#&;-FɳɤWa.<{\AV{ 7W=lqbP>Ab sN7Jnis݊R5ÚC=i`JBqnۡˆi|4n4.O÷h.AYڨӉHʜQmnJ{yϣͦ.Hڰh ?u!`n$a4v >V]8sNI"JsJYDU nx(,2z[֛ x鱼8p\I)m\- 0 |Խo#j %SclS/M{[cPBn*5)%$7 )?K6хc\nP0w1݅{WN: os}YnC{O=X .HùoU޴ItfZq>U3L,[E#jSuBi G@风W?{E;W4.0W'lcd%;r%gTzl|ny.=gT J<g]OKhf0(68@ tИHjˍIݠ~Q;h%4mdO%+iqԕ>kCY0xmUAy´5+-tmossMIIm>ȶouUFflf¹cgxlZcD1\IΫV-9ZKViw7Nf˸^,XIXΚ:@moշۜE - o򟽕b6{ nxaX-σJ,=~T"涷yf6t䌀>S~5_}`e"+g?д%@#?XYCy}x:78TCBە\5x4a;Q'n6HMmN{0MG$ƒJ5tK#GA:Tzx!}e\쇢D#$~Ez;7M7 0P`|D( ϫ okR:c.WP$08.Rh5$(J&>M0%ÑP)C̰GbђFG_=Gi'|[׏c?ԷS FOc˞I{ꮝ4?R(24[Oz7$ֵ۴GzOgV҂ wp+?AD4YV: ޼2Ǻ 뷏J@MnKN,aN Pp.=I(J{$Nn,9Zo9Y,۪Vd{xUK䔋uP*2E@w-+a1ݍ3o[DL&B=NoH֑Ck4fV~9EӠq+ߺŭ%wG 8P Q%{`qJԉC,T*Ӛh: և9ܑ|~dP!u_+NnQ~jpl6)Mᖦk'#Gwus1qd~Z|PSWإ~" ]y"UD㑻@YDȜ=JW}2jn>7PW0ZyD:`g|V]~~jku@+z|-Sg-cɫWh>g9cx]}Xx>\BY炮Eumm*(G;Yi 8(@VK6 WuXY[JxX 2.=E* p.l sv./uq-~/ۦAFq=*{ iCQ/K /HƸ±skLMy+z?ӑtN4rxMɷ9~I)N5M\vr5:Ycx!%KcesmYH[}pwE=wC`ƯJ/k݄`m Q4n=N֓]$lJiCE^kWyh,!\GNBUEJڳd^}32ǭkD4FUdaB>B߸K;] xgqoj7#ï->Btf{im"- ,ƄR~~W/o7t?A㏔:-<< pj^<WK[^V`:~[ơ8vWSJk/M^KcZc90?NGm-j$,C"˜MvE${I{|)Ki8$'#XڿzT۪awy6K-;AHƗ3-Q;fl<$wATEu('oWk-IQYsMnStNߡ!*rifZ3{k`Oo¾U 0FFo*a2E8{0ѳP? o9:-9 '=+CtH{i6U$WJvkw-u,}W<+DyOpK^/g4ke*bn=dE!jf [GG gyh?JuZv7cF>̧4O|k=EF?!͇Uu];1ݤ-Ubx6Gez+|e75{)0'<sp5`+cZ[E~kӾi8RK%FӰL{(q nFIOAq FEߋ>vsFQG^PCxf265y7f)n+ E/f<@Mr ].XP,yIkLm=#8ZS6BH3pp&4q:6xwM2䕴mݓA'?chMĒ :S9xReӻvL!nh-ē*MTA@&_j-ju U?zЊ Z]3c-aԛH}V} ^HXM YOw핣;뮛jey1#"I] j>i:hw=䮵6 ;tbך}+%ш Ԗ&W~Q3i$JY'ccq 8>HY`BL5q-){z Gsf\9&ʭR;NKd IG ^o :zV02&~T:qDi@8 #渇X=A c ?>hf/,[|M(fë` +hy'4kя6-Hjc#u2 QH5%<{Q})Û\2tq8nO:"Hˍ~-sU4Mqo#+ǖsg64Yilf1jF4CMu2v;Dd~/iv$eLGyY(7}Xͪ;d]rԹi j%i9Ap-oN mQ=rM+dNᅤ18G(Gvۮꂚg+Pm!ϧmԫk쮆l{q68H )tEy=ѵlR;r0~ndmW*GrI𻟩ӵ-mvyd k/sG;7]M6 3X6iA8U"ZyWcnK Okp1Qg `œ- gUNAm4<bLI 0 k@v<(` hbꈡ;#Vl-!_Ԁ.O6(/*r;4Qo>شwj{Z;{c`kh.W驄|q 欝XӦ;.Tڎ 2;֭xK(L27Ri5m4jT׮jD?S kkkG,P6~`n$k+|ReO|oO"$W FBHOPӷc#s gp+뺭Hn抎unj&#ww&6xV5K缗A \re%tHtص}LMCp] }1hʱӺvCl s^.[xf8;OWf@%k65ڿi*+-Y:\gs=[z{?ݳ;C v_Tf\C݋u-arw^iς1.:FxxuaZA|Awpgp<- &pw49J}ї~/XA " Kiv-[gp}n(~KO鏧t:UN0lȿ ?_kGNwm$EÛ Kr}l,"p` UFc.Y1Zspq;R O?*_D U24ݵB F׸A@\J.1hvL>B.ce`\'+t*k},ieӖkw?)si,D<)ADβΉ״`!Նv܏ +Zy>pg[sL5_o؜~~ÿRւWttczZ/zO~]p\$b(֋9Yzؚh~K^0ݸ9)HdWuP8mVpc ("h '2~HQy?E`bqπh*%:hi Y> 4L&Zk$nq Ӯgln/± 4:$[ Ƒ ;Ny]ttk.i:KھN-dǏi3P .sk!Ri5)u=]U2&Z_ 9o n_W4_~Tt úhV[nwrWjzV̞2XM5Xw4VsԽ7RZtDP]Hk_#Ov/HZjL$["L\܊#6M/:覂VK77+7QME4d&:+6XԲ)ŁBwb*ЊpR zfbI7Pܨ Ϭ 7IQC*hh$q_"ygugWXgy(~CXp=֏NM$1D |uZvX7:/-H$G*Wi?pRӺE†G!q융>/6SFᲞFYK8:ݑ ^Mr9VE<*{*z5*ih9*͇P= q\VLpW CLo6]B0r7?Un8 x[ꑎdK$~joë2Q}`ޘ} ]CuUtZNӻtzvn<+ϻn\Okť6{i}7Ik߶ԢD?hTLʙe>ZxuԹbW 6J~B1rM.M\e"X@#WvK_9h|_u:פ9pЂIr@33 Bc)H kB6ksoRj4 ./c T5dB7PK×Սq~n#p`pNP`r隩uBMCZ'dfrX)yxgFm=#ӇK ][(t}{"k#cθMmSxݟώU -4ssh <+πSFvcK [a,U_RhG#n7h?n4/\hʺȄx*z9$|{fi\_]hM\SaeêeZة`,W"8˜m?RVX*?(n sZyd4"hʺʊFK~Gec(VOz/Dqr}>)$pNMu'@ʿJ5qKL$:h>@w֑wds&^FϲNq.~4 |ڇ_@$@!(i6dP $;|xERn{ \7?mx]0t':м4 FX#VP˃D{tp\T81P qT$IZeUԉ0ܪ#8c Ic-J&*V.w Jؚ\?U]x'`)Z{8甝>L]' hRkZ/ܤP tq{e^:(i'7hM- rRMҼ;]UM2H\8~æ}>8seKtљdc /p/6E?CEnO>/ݨ8 Ҭvßis`w+gi5I?>ӐwqXW#Ҵ,:6mVse1vG O\6Bl Q,PIpY7d6Mdv8>U=4#KB ;c Ζgymk矄bi5N@QR:OM~ʀ꾛;c4UJPglwTৢ ~SlӨ PA3;nAB h4㷕JFlDEMYqZ6K%.N'.[*D;#" єz$$)8=/WҸd*E"gm}n֜PmBMsI6Bx*|X1>=NҙؤRfǓ\쫙&8-bǩ(yZ'ش,lwUeӽIS6kR[-5].!kMSqq=D>H&zgˡ #+<G#dkakG[4H'E:w_NLI6MP=,}M 3vvV@i y XuS,ziiAɢ h(G>ynvlgDh |[%f5`*5vAXe 7ٟ0[RduԱ*w&K<Qn5Qvc{P}4^0~VLu1jtp0(AU"v(wV,|G4+?EJ=;A/ND盲6hb9N? X*mU[Slt:Kc:?Z `P#2E u:I?u{Luk.-k\Ok3svU]<=Y-٫P;D>,}#|:wm]ƒ cL6;+#QqA1kA.ɴa.-VXƒ?.lR@~ΗFָM%Qzu~ܩ[}f>;[O %m; Y{{dsiH}2v];4rމﲵ,'ON\7@dW*ٹI<|./lRL+q1G? GAы?JL<=nӢ"çf>,/s?,/TWV~k @ /Qǃ ^)Qy\tܢLsMv`)8ʳ.r"3¡o] nX@]Bl4L:W32WDŸR Z !v8 c wr㴒BQޙEgX~*2N&uE%M:GޕǹܕZ?E+_T⟸d+3umP}oHLmi''aVoOP{s$v𶘚jFn*vcM:Ce!--5Wt$iEP@= ެK;ӹ ʕdXl]ݏjݣ8^k>o>&=juzgU;&s>5Av( ~7mKĎK\74ҽc\Yϒ}7UfDYb^w:d)m;A riнsGekhm4I •gQcK(k9#ֲQ>+\ڷeVai e9:g>)k\W'{K,u]-ӵr=ZlP+9Kysu]d Em{/>JtI2i8spZqmyȳ\)zxU!9>,*RKI4Eڴ&­.Ѹ>h'U=36f<5͂C@h9Yc/x xc@Me> 9Cm?溿LAw&pblMkFOe<_Ut_@oBtR7{2WvKl<_)w u9XE\ntGEL-z2u] 'f~W7ݚ ]g˛=fvlW#̟XG KJ!6kQD 3SO6yy]iϗ4<{C#P sӚ\CtMpA}p!`4`5m;\.®=Nu`\p3𙹊 @.&S`HݸUCvT++HIqETo;5dqg["'ے\GʁQ\vʥ#ͮDrJ;Yrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/subfolder/gallery3/da_boys_1.jpg0000600000175000017500000012600610276336034031243 0ustar madduckmadduckJFIF-ExifMM* &(12i@ĥ4 WWL Polaroid PDC3050 HHVer 1.02004:02:20 13:11:52PrintIM0250("ނ"'F0220  &.  6>|N0100f͠bF   ! 2004:02:20 13:11:522004:02:20 13:11:52< H  zKi46001231143456_~#{PrintIM0100^R980100((HH  x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?]!m#gE T¨ 0?}wĹUvk뿀%]KAEu_twɷ sh = ܿrp2?*TʾYW@ t:(6Ш['S,Ҫ_@k6L~^:*Vz`RIՀS-%QgK:x*Z2lƞu:0+\Q-3u:+ǼWbG0+媮"*l˸H|ug϶k]Aؓki#j]V?':,'jt=3s|W.7O,~>7cu@3Ҿ$_8xʗbYK9p1֦FTd֠{x*I>NsY,C3oC6Vf'bQ*Q-{ 7'nk9|#_ tfmZ2Ic氒КC*BQn@E$~$rWazy)xGz7ZK=*nh=]KEӯd=l܌V!8=wt?7d57$W$( t0i3xaҷ\;`w=OPIl]mkvjxSZƚ]J <]ˤhjB ޝrH?$WXbzH>*sPf+ށ(>a>IuO_Ɲn+l3Q6WO]?7W!\=e|W'w?F-}VKAR]bS o&؟g>\eqs$8GK=/EO{s8Hy]5${` WW~%3Nծm-&HVSelc3jM{ Jя'?W ot,>6cw#E~|&ɡ|f𝧊[Hxp'޼̓5"Vq]nHr=~+zξ(&x=HW|?7et[ܒ6?Ձq/9'UrbǠ\Üs/;(%(7 6t3n2`9 <>`'C5 9a!vת^o1P:v_Ʌ{j-VO_ Ch0ZIxd~=oqGׂ}6P{jw$ğ" x.ĚyYuiG8ƽZGZ D𮅪h\7RHqY4VMSkVoEpc v#WCNROg<,)eVIJ8&0ϣ#8uClŔk~!esg%=q=} ‡>W|Iw5[&&|Kt3S#h9v{>< }iCݽ(™.n+g⾻#Ku]1ę%洖8ˆfF:dWR&2]+)be/wn^6 x-U &,ZR$o_|Ԣ?ZOR|/SziGDž:RigմD1x-;+oSI:(4ouOz^ t?IgH'Y6"Yʲ@󌑮9=r з:(ڧcOPVOH"$y/ ;F+%7amB7wI.W{.eǧ~c-u}B ?6=~%8v n)U2FGwᩥe6n~2zɽ̛hh`f<>k<ɂHgePyjSv>"M/q K+i{~[HՈ֌ޟ0?#_u:YV4IУOzj qGkO4 .|_%f8hΕj18F!]wg~Ń'Q!pPỏ"Ftޑl@'/#⏀q.? COtqα z^2g?g5lou?_=&"&/Y dD흫_wQ7K ƑԈuOM=wے zjJMT7~_ ܮ| oMwj K$HK67EoR g\SEOxkM4o3iev5Iq^.5i65_u&PR\\é+?E?d~_"ӠN1q^(/ۆ-" +WizeB:l6י+%|+C V^#t]nkd*[kTƻMx^|4Y\ks)'7& k7\Rz| wv<3l-=`PXr͇ n cI,Jlu ݫڢpy8aÄ HF~>SU[uQ?|o~Ì0ǂk2k>|i1cʨ (*ILWې|@~$|񶱣Q\EedNaSz7=}5X4)Tqxυ~u(=;_~7? xǾ#HK_oKݟQO#~SO0_1x˪x%U_gCV8姳!wrGbûGܮzG4-ڽB|7_n$oi'ԧ?*=u{S7,nАxJ\[Vhʯ/kԓz؎6~ýkeҿMȾzEϵ}B ;I߈O^r[lzr:W/g:k◇owZ< 쒙FٽŶ]:bAO~^šx@VgrҴ|rԳ(tOn%ljZ*U3wQM>#U>%dmL7K5]̌@bxoRҬ-4ȵKl%Ov@ Abp$~ ܽ%-y|MFтpkg'zc_|)xƟ "--Ihś+؄q d ^ 1FH|?$̚gGwqsYzfم_kF_sQVvӣR^n|iO^/e_Ñ ŮmnWA9ڠ<Վi#g,Xiڭ#\+l]U9~2WVsφZUitiNyO־e/uiOk9o n??W|<|E'a h:|Mr;3DZ? ^;we0Rn>l1˛Q[_X]5{? y=̉qۣb_co?bmcͯtTc=޻ kҼV;)QڴUo- ? Λ 迮Ժ˥*O^cVk~ý8x<CxsÚֶM/MxW; RƏqa$@Znc}0$91yxx^!TisPTSMZvpI'|ji7Z lZlvcssp%[%eBHW_B&r5؈U&Bgqχ\qiSX*j~:jT|{ _{0m1*E%܀:2^S|Pkqi(vz'pJaWR?г.%ľ#+2iF%+ш#Q WZ ;x8#-GGof*J5--lLw-KΚp1+h&Ce_xݱ$*=Y;Hn| PX33IqOh_V.[YUh׸~Gzn.>ǥns-c뚵ksz mUqO^~Hҫ?ߴXG6rēI#L-1~%\ZϞl"]B%`2z< rg-èŧ%0fo{1>?wR//f=Oynϓ$1G_ZQV+oWeS>*ؙ$Y5MRs1_F(~W4¿ wO?m_f:Rr/[,G!?8?4mGşN2l(Q\_͟_y,*?g|z;mK+?|+/>L䗻?FCԿG/ ԥƓοN6}3} #j`I.$漗j8m3Qtg&2G?Ƹ0<'js*WޠStϵxWPV2׆_.k.-P!mg?*v_{Q寻ʹq?]?YY [Ǟd8KpҾ~-KYOlPVԼG#hۂmFeOZnF9hTf5cԼgeOy>Mz"}|POO!о/݁!2)$* dP>~"խ[1sMФחe#ksl'Y]F[s#6{Wo? u3FAr&v'199p~<(vCq> ?4 ,~",4VW9R 9澯N4h TV86 5?#Y[܏cCKyӒ@`W j/? \O`'4"KyXL#fxlWxw9u#&Rt|~=:y-7IrR~w|Q*פk>e"S?oZYQ"|f#~lYh8p4mϧ k$0cy׸~E_xyzWOkjx%}Vņa@qx+dtX< O+*nFsaïf1;TV:)Ww ߎ GT3.1?|z?^}UDȃ?;3z}|t_ϖ><4ul^+oC7rP*Im./DŽBV>5ئd-ie\ kj}"+`v2i'c*g>R2t^"6J0N|vfvϖ| 9~.o& ^ sy^yW@"IFb%},\X#Jg~#8(RZs.cGZ1Щ uw%.xI:R(>ӵz7u.|cBρ|qDԏJaljucF&>e?!1j^q8Ν}o4K)<+e0~k*\ybZl0_s|߽y6g#c1F|*˩k~y\E<jW`?_Fm!>&_05SN1=>6E?,]OS C0Og xj9(92|:-{FլXȚZ_72|b-5|0[Ow?TԢ'ێyi$6[$K lkd EcI%h "ʂTIRJ[*F$ $VT!+J[ i%i l)#IZ$B*B-dBm' JҴRm"P@P@Jl zB%P - 46ihM-N -FݩI0 hQ=Nvn$Ы쭸($%KjGtI O#mӚѲOwDpiMN;O95%߄8)$y@4]wA}0q4my>.Fsp_mI "VH9$mBдJm$+B6֕Vuii]&HViSRJQ@Jm+J%RA$A+B!HJ Bд@/ @H@Bд-SIJIA it$iJ M)pP)M%+4D~Q;&IFԅ1 w}N%wNpF#j upG+#$"yR4'jCsAiAJ' 'd}F+8rkxUAԁ9gi$ۙ}j]%+H!(ZH3A#h%hiZHZ6I%iZPPdBѴI @:M @:Lmm+B@R@-P@8A($dҴ(h$ D P)CEV6d)e@HHiNB (@08)Lw)8wH sRN"H 7)v(wN?2iHiNPfq}w( D; jԓAtY2OxЮ?B 18Z9Y$A Vi= LmGh)})/t VDhQ@Ii_ѺPzu'Z(] ESQI#HS JH0?$wAT)Zi6 rU]A4A R!To!/tI iJ m0F%tPE{!h2MNߔj@SHN;& aRiHNrH-4i=i]ANLy94i4t j)C5ݩH@4SO)5ܠri>'*3 C]7O\W >lT7N.sZ}NV(!w)tn?y<蟍=T(_W3piQFեjߩKw~xM2*k\` ~t<'uE?ٮOퟂ1SK<foJDR~"s0Ҙe'?~xd%a\H5j C(萑nvOPj^:SQF(RM@A ds({I<$Vf/滂iNSq5R hJ:@ E:AE"@ @SOi4% I A($SJBH$ 6J M% M%M4kaHg}A zL4pSteF8M7Q>CwUl'uC=вJ(#HdxA (7IG"_("V@EI%!HP([$*?']^߳I\YJU]Ɛv!iD%6DSI(_(P@ d (!hDIM& H(8Z't%4}'I+<f(~Hk@ HSkiSʐn@< ɧ{lQO0P%"wA4SI@‹ BWMuUҎ @b@{!H)(i4S}@~]s֐X#Ӱc^ {,7@O?:ԗ'f聲zS'H㱭Kw 9W/☟'Ngq]>T V=j)mhEQc-6*֒QA66I.KA(<Ͳ^/֝)3%)eWcIi;Zxa;l~k +ҐQ%0JiHZ ; }P @$R(%7$@Ja )騔RwI1${ M#bդ}i w)N-#%^ rge#Q x=2Sd1ɝa;vc9=."7$v1܁ 1ҡl;seqKbpI]ӵ4{>![4",#bO*e;,hy J$.V ļ):\^Ʌn4y;7z|<9ui+B[ J@J+\q^˗{gF״A^()46]%4 ݤh{j9`clqeK_/>\;/;xuܓ81쪺7GlmYj&p$mĪgK[ŸӝCw8qi>G@H$ZGF$)88<+O~3X!7?[^ [".ҿtk\ߍʽdM/ˣwyG)qg%%v%^9yM"J=\wT@Oi>DŽ€)8(zI("?)MRI$M$ܗbBƂ2#$b.[p"^8@Sz\ D2m]#vM!C!>71(\9T&23rwgUsAl/{w* c\xnpV; D]杂 5(+qÀz[0$FD]gHRF",ٌû.3Q=0 [>(끼N74pzr,?EXq` c=d8od>9ypޙwPcʢEqM IUl+ELE1Co8Vct+Ez Y=scmR^:5k=W ݦa=R@;*64ttΑ塵g꺜#44nko>&I` kJUZ;X״;uokj: ~AkOI H@-@FoRZyV=k64-z߃ ~7q#w%aou7I uڴ(/Ga57G VT.vI$$s^&.w#UϸkE; )!Wt,+/=Z.;;SCp=M#pM%OBoL$!rlƘ@;.llޔ[)oWUUOaոtٞ]t:'rb gr 83@-#,3r05濹[orm?]|8%6`8Y\yCDQDm*Fcf1Xx^zTȌ(>s>4F6ǎ,v[ `tpg#u>ܘWx AU\ #y:/SA-n1ȔwD't @ҁҘi4@ 4"l <&Ҁ $HCp.,'s&kp.(X^ x)D7BAhuy{@_\*تU< Zοn>E8txDC°b7a7 ~\cFԘfkHkO?EQ`g4ܧv8fݠ,7L8g7|j6峝qA :ؖxLs kF^8c~(:8gw-s{ux(-0p3(te.]nF&2/[\=69eN {BmF&wLO>;`7U<̵|/i_s[(&Νbm>G=^w7j,؈y^8ĒDfm XѸJ,?m`J8%r,]<|7;݅$l.wZl(2Dxn7%{AOgnhn{'b7f72z .u~\& sEKC,u2ﲒ mo5ĺRj#\u?q'߷]r>pw^şrHZKA6tI,D䅼IX(D̐VMf'd$whyY: gDDWAwVX A{ϲb=ʿ`W4 t-.!9]Dmf\,}49];B÷:!`S 8:j{m© ȆeKnV9ˀ!EkNCX2nU×B^9}5a}7-8G˧G4-wDr%f$yrv ^Kq6-wOvY~SثeϏv"!3FO%ۆ;x߄)DJh%Gdn_i"̈́/wO &$I{XC,O 0wV d[!?%L[±d}G۸7_GQ ?<].s[\dOnlmf;cgPtD"_Iu$ *Y˷gYi-fw֗zC;mk[Y><*7JLV֍+u?!yq7`~Wgވ';b;Q2yG*0/s,Nym6$ˉ36;,lrg?uW%Uʕ'}6 &.Wәȶr2Ftow s [:MO# A5ʭ}e^3á#ةL/3:&[%w%?_V;m 7m M:~0v灳ɺCkdAAיFz o?E͉KX v]wq& S~<}=+ؽDv|r}NWU+Z{ sV-6)ӻjԚF48GuI[es/ɎI>o6ECXI ׍׎7ş.aӿ1f,= _kVNKfH^Վ:FI\%0v w2\xsPRj:Nd3`Yŭ8Y?'%_gF>ftDtwƀր6mza0 vrD#$GH  X'VO!zqwT7 RE+Ǔ6TwWaCe|v[(fAK\TRekAi!qrcöe^=`.@c"ku "ӱ[Ip._oS\tbMr~,wK^Z{В{*?9zK<>idK|M4 'A Ǒg3<#Ja0Յ=/V+XO25=q-6͢S )R~R!]ʐ6){ #"mP#dTJ{s|I/I"?n?gB1§1&}4@א熒M.2#KmFi>eG1ipU8,vMIoH֑GljQFERvǒ? NwY3xR).B@Q }mw1:&ζ-,P@ۂǠrxUý|t'Ks$h{Ew] 3ˊ n+ty09ǷU&Չ4sIm&s5a4]\8ާsk?B\Yq&٩74R:øu*g6r'kP+ױ_)G;7k؅;n Dt2Hv؅T9<+9?{Ak$'mU|G滖Yw!ɩ~'Ƃh±]ec<å[XR'crXm:6ˣF]Q88/n?.xUh8] F_qOl1bE׺6Eh:8=eނgźKtzAIuplpy?ȁFƕv鯢^[*c tώ4~G~w@{AutrTnۆФC@u#DTY_mΥ7UIl p5!~ M=C[mڏ,/e`nU(nR}U)xMBɇH@ Ux[9pQJwI޽xt>&ZEGYiىM wJ<:LNDZ^H 訟Foix>pF!cm˟퉦@X ;nBزÜ6ϲ!lo9ۉsob8t9[ h>>F?%1qH*9lJ6.ty!~i+-Ew]0z]t-LؕQ;Y,quN~@6 dqZK^)OM0LjWQ Y'֛ diRh}\_I꺼c\칍y0JiYY'7qtrEWq֎is BX`q4P')뢏NǑJT|1y:Hho+OM}Qd.'9{eyq4"H=MeC-țz+y4ǽѲ?:\;ZZ\Sj^XmtйW[f6#[{v$^1x2do7?8 &h.<}Nc'8}p?qJONT%Ģ?(G^߉w+J Q (%@:A+)7EU=[}? eU}X3zN$>JǧϢL(&6HDvVd#?f4hucXQcd6?KtiL q<T6]Ӫ5h虀r~_+}bWE+GU"9!8veRl!W Xom82u]] %#YXd}!k,<߸T!ץ\;3$c$x۔hɬ ڔn`nDDEWѣ (H~Y!h p7W`ØN dg&5c Yq=G䕣Dpm5p!7!-`({RU3^ӻH$BL<&?MȪQf?eo)u'vHmzGn c^ۨ@3ylEqJAcG^iYPf5m=U]DP``w&Z4-f  .|wEiŋ !L-V`Bs 8:RqoP n2 ވȇNM{5~\ U_ri _qtEqK𔑜#N۱Y2B`ɞ)zn7Ӷ&u3.׻FKmzWNWU߲n@͑O c_+pZ4GUnUȄfώ3`yߺYlcbYN:yW>A`Z81©Ly׋i)G*37XL2SkX^`kEXG+ًy-Q7U@m𦍮vo/vUIܝ A_sl}P|sB,<~Te< x? h{vcq9Lid9qlWQ;^4 z1' =ғwM2qd w|ǀ~]POG3ȦQ "IpB=qfG{S89TֳWݧsqPɕx[,W^._0{xs6RA(Oa cXXM̊Htx;->g$#q76'*1h@ZFNMz43ׂ)!x vk7+Qso0ul_bu1cd1]xpuN`q \$yAF@>7gJ&wX`$jxc:U2n(7-zr1v14̴&x˓N{Uj}BUsKoltupMYN֐$ѷL;3!~wT{7Ll7Icώ%sb *ׄ*6\:Z<[H,x-FKm-K'VDGvY٧O{UN lh{ <OnsCnպI̵v'өMhuFApPg 4Ӓ#}=%+؝8-EK?U^DG +LSI߲X =GmV1n;}Ȳ8fқq E= 4h[/=jBtut`zC1n->*;r}wSz^ގC8utP|_iMp Lz)0ɷ4v͢(:v.'-m+ J滿 ޗ+"F5êkuSoqS ϶75S ,bzH]eVm˩BiA#)Z! AcmF{zׅj}GYSHւ\ucWρuZ8fl.7,n?]c,.xQip9q"6Ew799 ,e5LuyT| >a4r=WpZ GnW}A⹷Oa>{M2V6pX,xsMhd>rXc;V/;6fݟ ~[W&[c\)"- R4UX$ݕ Ƿ+046Lh$l@K.'8h#m@^[sJ!ۛcUR $asF0Q=%2=7pǔ`xPJA=&}ni p6}yEHivAk'd{/P;߲ <t{ݸ W\Obs\:p'{S>6ѱGGw@ d+%mTfXlwf.6J]n;[H<<օv;K "T#tmv@Ct<`kCf pq 4[.18XvI 5l?;A=<Յә0@?۷|XFַr8zked~iKˈC7{leU"tS?nX!qf<8O+g6>.Wƈ~*Y3+܅cQ{ӽұP1o4hOmtOYucő :Ĵ{Gy$ڔ @b$Ā ~Q0=K@)w\lUVM{iEڗŏ,#b@'꾈״|RbA/p /-akZ8Lѹfv zE|HN4@קÏ~K͜ *7]U܍Aq6V,5^ߩjdsnjʼnM˯>"N%,t_ (ݬ`6Zy@:R@&*ϺtMLmIYy, pt%>OUK^}o y)cIvĮtXR[ ]3  `Bvj%Eq m7Fv0zOw1:x #ouD +}) hsWӽ.k{]?eCQvҷ-[AޓaMp1[*'moWqW6#~֠i[s*Cz:zA &ƹ,Vy%Y@Cs8Ëo<2P^$-t`tZwQŭW#}Ebj^(0΍iDA);jFWAzp?,YdhPƫ1 XCl?~?O[v@lBHYXy"ұٰG uL^\oӅc#;oOUZ\$5‹k[#CZBa6SZzf~ɫ}"el,WZW5oB-&H>VWN,(8@"VQF1] A}<WI+k Yt[k0o콫٣3=#xxΝ3:\@f%up-rr9?u󧊱b&G;ޮjIZ~%i A >~3ʶfRD״l&R4\8-HÞ/ ؑx=V-d>+wwNqp El/xԎlv oPeP; lJ$ou6NScn8kGug^S2;wof$kyeG,ӿӤ18uyOu֠?Ȝc[~%mg߻BIf"wWU'V?W >J鄎icEBtG8#ZpvR 4ŭcJ8urp^ r98vW+e;~JuK8ŭU˫ '#'lVKt?/K߿pf8K'Wm`xP;!ٹCO[tx d>KcObU{*挠ͫh,GSq}Uʫflq\g[ 4ibL~Ec16BI~=){-X%й" SPzo{ƹfݏkd{R9e .)\܈ .\>4g8mCOB1vVZy 5?M-vnK"z˕ߎ5Ek0p1snJ5^_ӵhF?NW/xZ'v@m k6W.Uh[=y3+Up4k7ޠw @4l$p"RllcADp{͔Ai׿ll'^M?R;8YioEӍ|r#&N׉ӹ, ~/W2…د(;bw^[:+Nl\VnO7T#d7I$ i wCI<3} _^ϭN1t̉\CzXw+K{o~29<6$K1'τ܎yi85e wu6̽<xPi"7@.EnP=;)=9%/$ǁ#UwHnA$?it46i1ɥ*F{L@RCrOzWY]S{i eSy-uYն7w*OotfX^v#$sǒm_֥+'/xIۨÝŲ0|rLQ>tD٪GniYN!&1c)^.ץͮvI"UWMVԪeZ 䊿`wzqԊV_mGjokR[ tMW 2y38 }u6߶ʖN4>,wZ} )ڦ,0eXp`>WdGSOI^dg2 _5 ūokϫ%dM@[/ 4ҭ#]eyZRG yu#໯JyO_NvhN/qI,w}ND"WF]RabYkAc oW%2ڻo|ѥs8SܐMmM;uRߡm]6w}H:'ԝ7BO \Z g_Kik:5Lhn6]ԭcj}G-d9?s>wWg^tݬg~ 1 ^{~OO*N"|祍$u8oW0> 8Y !kZ#z9~y0rI !6u=k-cl`.{+rُf%%n>\rckAn Cpحxl.,'RuM+RHevDlP'ddoEä/lJߵY:6WF=ȲVyv܇nWQɁ$1wy\ $I 7#˞<5,eu?7Dž1L\:pay6r4|fj@GkYWjtRm&1t@Bk=NODٲMk&܂8,ا!@YMkNI k/{%0WC3lN6w <)bpco=N.`dT64UQs >?@odqx$AY m_+(Uks!*̃N4:EdҙqI#ۂC}=Ϻ>"I Q*er7 l~B !*-"CߥQG;('Y ,XMeiqt9r斧>wV8鵙+y'u{ӣڵEOJ-%efk\G{@|,;ĸlG ~rRtPzV C G*ImQ.lM=7Sl-^ [;3,c|\+TU@!36F9ZJoI`Ũ Uh{NlZħ.#-֣n.cE" 6ypsIis wtW;_ew+}p!k pWԃC>+y-/+DŽgտ#{ˏJ˾7Zvu)k M3zN!i~;3?M+ (lMlx/V5H; _zd/i$*h!^_zN%:,V:ATe8]]C x,2 Ǜ7K 6/.!hE !$sk?RiƂn }厐xS\[;_R 'zQtI#[ڭ{P~./)9/}UluX +gCtP6)߸_ dGחʬ{VV!nӂݹ)(wd@k2>d.+U溏 D,[q9k,rs6T?G[楁];N>܎hqe+?O1-6eSigO)$)qGئE;(W bP9~l o_hOoP]-Trq YhPÏ?t*ĻsZLe>%Ej~P$P< P-R96gdz s#͍bv7p un{߈|:a jZJ׷}$.n 2A{|uHiH,'H;aﲎ9CYS8ߓb/`PdnVS]oRp"R O*>{|(1t9WFd 2k,oXxkd߃I2eVdDƭ)0C%=>=X> 7BqķJo #Pinu&{F];/j&oO&6["O<:gjY?Ud.=*a*@ns wAs~,#KqB5~Tg  WS~Q\ _5=Uޞ/NJbAs[7_Ddiʒ !կHC$y#"t&O7y]f?(\@{uYG8b!uTwWnoۅF0FþZitŮ-xoΌ #oe%f.egtwD4=Мaj@@7p%x֯..D^v>\nc9۝x-웛<h4#?;,,+zIq\X@}14 vaۚ1[~dNde{3o\|C>R; ӃѨԵ8jዄwj_Jn֟ic05l}1|+ڴEi#j_ uhV&ŰRDp[~yG6F^?HZ8#j^6Š򫱏s)쮱 ߒ,4ձX9[4vop>lhuvVS{]G!1eZV${푘-蟥H@*fv?s!̌\szp!> lO6˜ۆ5ŭ~Ij ߲4ыC@V\>m>-Yt |g[Rr:lr,[톞1S/7ׂǛ){z<}M5<OG.xFOeRɎXS_/[kG(~2p?y mw)l N9=<Ƨ^,xi$5~ʄϤRvD#Yϟgzn3HkNX^\y!,ꮭT i`,m'o#Cۑ@W|?>"~E=4h*_aS:P^٢ͅ'1}NJ wH~I(9) Hbxs"ś0},Ĝiu8-]i{4$-܏oe5u8 +BXwsl o(||&6{ }״?(=輀{"MWP7@AuEt7;Tqe6bɛ.U{V4oͥtw5ѹYej2最~&,~ay!?mU+zO`{.:b]V9; ?Ex*7}2&2c%y+-ÇNGmc«<1d^q >Ikۗ}|EK#>9@E6.'O-)kj9aYҵHqz+![=Q+<dחlwHe D2p,w +)y9p;*Zw3 ̄O,n9&IrdwQ4œ6|Nӵ١p uu!s&C Vcu F$mJ8\$6ddq'jШKGn׸U$ڈkqP9OMu=~K4(V_F.NTtF|hh%nh4G ,m.oMz59-U|(Vrp@ZgiW5QxcQQJ=\|tJOQI& +ƴFQGEU(c to%K\²ݕBdved4,,GuI 1#tRg; 4%r]$mF3x,&[SF!ϲ×-hn8H m%z@oУ(PYr7 USX_$ zUEثEwT$p*ߴ ⌗4mNt9<6^L Qa?'n,u#dyD9n?;=mPsM|/FŊ8Q{Z(4{(d,6 ZX`sl1ȍt6T&LVO.#졙9N ޖ CM;2I.쐞= eHl]DJKdH/.]ϲ mFo-sM uщX w3Wad?+2]edֵj^s2;"2_~*mczX V /3qkaY7.6Jaӱsزz- 8*4M;1$%q+,~?ˍlz,shw۬w \ygfu1SKУGjڄ9nh$ +Z(  /6O?]|1N!~xh9Xb6]Y  $6IWWn=9>ZyG~1 Ni}uQ❢w-=Ya<ءu5ė\tGR<枲5&C(z'V䬬݃l-F;68叩C2c[%hBsyWڮbʏ6&v l+{ĚpM/k5'hkrHz'_d!+ 'jF7By؎հ2Oz9jCh,7o*V-pTq缃] o-A+̋M~yəCoy$Բ餌x1';O)#hJ]FS} c+@Fׄy/(?+wK M4i1fݎ4¸ A !WN{oeA+|UJ-9n"=ݝXy&:HoO.wiir.dlmi.;Wc00*0feOXkXnIfqݶMPc@<(>Vew ᑧYB9؂&_K>wj#yW7Bd.Q⣍9]SZA`C|iqcn˻IFTeƝjRam8+ zOi9N'it9 8,Y"}wNd+Xhws5C+{PYp? ư0-'aemR2ÜP+$xv,u~J +>ofL\lB$ kP!F t? 3%3AmwԄYol#{=~l6y >F~IӰhi %sMz(2e w)G\Cm_r褀=±8.!>C#͞n㷷e$6&ui;t8x!%OHh=$Gu_'`]ESH\V X 6Բּ($ܒF4#qtBBCp!4=ksh\4;:\oI<^ʱ/WH=3D̜Xv cC¿su9n=&?NPa9ҸqfZI㑏7Qd8wiFe.8]:x~&|NlGwVҢ`CgyՏ񤉯+ʄ8[4#<~AE;yx&lhv<ѱ@X .5+~7tm?⼙ܣC'/&ÝðQzM4~eurwSYHquWiS#+C7Oȅ\>tC tL&N갘9??sad[n!ڼ:pi1ʚyŁn=Vvh7Xϣ'6Iw8 x^9]Q4ɹ>{(4"mNSsEyn$@nz뚲W*.$6:;Gu,hY;_HiEl;| mߟ'Jˉ D[[Vc0٦_ǰc3Tff2K4&eqsly*WjhDHY (rS@HD Ddodhd}!wHn)r9YΚZ;_eQv+@_4Ğ!M'[C!d4R1Z:@۶rXKVߢ#_);Eڅc\dGI!%>~}f~WU:qRP^$ݥoސiӱ|s>G.!ݶF$gtT ~1&wX?Ð|*N>Kl]uBC]}Scfݔ#w3(3c=,e!Zėfo ױMjL%" -lU"gdORUфƋ'9xی[CΪa2 d}W7|E T1AA]z>?Oym S\y?)Ŗ8Is1Hdp4o @׻ؚRZ80CC3!Bi?s?p)%ڒ*W#7%ck tEwynn91=%{~I"|{X j].pkXThkL.32lPA'2<E jpC" %DjH%\"9Lp *d E4 K]]fvO0(wD J|{=}6m܉܂,eY\(˲,M$ vIN齯HNLn<@z^a|}W⸻/m4CX72m}WtY9jcjbQ?*8d!{o$E `5oc LuF *8b֠ɜ\R ߨV#}cت&2\jOlNAQL<y' kPU^qI8kАQ8uup4e5"@}'C\{h-,Ckcs!|kQ3NzIZG5=[%Ot1p3Cp](nܕNdu;d࿅ cb626 tN M(>3Hcyxq=0qWJ`k6?D'++u% ls4YY ylC諠*Fwi DgEakvkF,Ҽ9f1煣@3>ʾLNۻ!52<Ål.,{ pZ:/ \yđHhu4 3?b7j:;?%yKWS>Uǒ<W|y,^G מ+@d4 z6f=VGqMlLXltZ{TictMejA#]V |`7Jg[8jش̨$zmu99?1AiS<q/i8I& Mi~ԫZ<0ӞMr|1}9m>-V?Șf+0;w~׺mwuýS[$ؒCvVO@:\؏Vn/K⿪ __b7q]L~ϼe3-#fsȔCv@`Z! 4(RD]w(n[H;@.ۦS p.$(3Qw mJ 𼯋Tњ+s$rOޡ-L]JU` |N0100f͠bF   ! 2004:02:20 13:13:162004:02:20 13:13:16< H  zKi460012311434560TDzÓPrintIM0100^R980100((HH  x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{Qa:9 1֯"(qzm1?l(ZAiG(8Z\g1 _XH<P ^r .&@?/8cdܡ-'5°{Tfٕ5Œs-¨9ҝwku?n*Oa"m1a H`r+ 94{ayGiMh >Vսqh 8mn\gHN.֥? Ʊ|~g֢y !MӏYRsg1rsӏʱ9Ƹ $W;y>2Cz%ysq?+̵F'~U/chl8#iUizdR:k9+bӅ?*יБ ͅh-UOV.>pޣ?YS-+JMV7ht R2:? Ҕ[nA.iAxCRGL~԰іD%'rOQ^g_ե}7mr'|M !O$;~]Z7+ޱխ[u5$ʤG9j,eD3= yڀ)"hqJ'ʼ{~bKzU/TΚP++ T\U͘o8xⶍ S{pikn sQ:1w#X I¿,/o3GzcL7PHrQdq^(=EU)SM%FXz + n|Q?|/5bzkI2դGFESCg?!_H3$9#Ҹ_M~qj7 2JK\Ax#<~5ޖCᯈO:M-MQ$fu_(BF@IqX;Prl;fyTeu̧>dߴj>k(%Q]Es?KƟ2;F4G*DV{ի?1ȯ05\,wV}efNni0sߟ\E2/PNgS/$WSH0ܬm$pweږ歫k&wo\̕%#k|n13\;暎m>? i :VĺԖLPՇ^K q9*߫`t#aS-{oX ?lt!ʆ<|_wYєK,7Ӯ#0;/Z6?@B cuت) x/ K'-$B1=G#|k_i֖/- #ha2Ga]o{sɟd'&tmm3{r@4ȼG:6u1!2VM` G~Im31 \A {_[QR>+:rHp6ZK$2 OOF{vrSgf ~b[|.\;a`c豦O ~(?5#燼;b~/%ɀ'M.G=2MxY^\篖GQׯ? 4{lŎV06=ԭ˧ڡJU>Q_Ӈ=yvSC Gt7xZwgsm0RˑZW+mKqotoW/ܬi `0E'g 3z_K>6'r]>N~=[-iˤKW |&^~S)&#LFvyy(Ox !w(mo?9L ~ zLޣw_~-_Mhλ; H\Wg;㟉χ~|@Օ=-ўـu7wO"o ^nK\vƾ??ܿv<ա_# yco|y9nn-8 UӞh"fr(=+OmvRx]GL-ͷڀ `H=Ah\b69p}9o[ɳ+7ş]#͞4{E\1p5OK~ӿ9sohНѼW1tnYYXVkK?S6*nHh􏏾$ZVZ`Ǚ>?wa\~AEZ+(p1\jk')_*}0WƵ} ma8c܏gUίǎ<9d_?-U-"\9zq?b\#מ"yv9n=bʤe>z?x!xtv]^]"݁QK1!Tqߊ׾:K])./c|"j 8RmfM% -+HMin@la >cnw3t_)W\`:$Fěv}6pk..9 w_, Ɠ*_/& ?kՂp _$~z|p|l?O]pJ/C E>W6X|ԬM3!ӢZLU1<k)%Ӽ }_ƶ|\eO j6]Y:c=y/DxmB/;j3o-~)Aao 45Q;H5~9Ix䝛azO>.LOguwC=,;$\;XSZEKM|wH9acg{8:)Z4|g\+G9E_MM:ZԮ5bn[_Zm֯Ľ?Ai/ٛB82?ڣ2x|zu:2}?3G~*"2xai(w[HW<N8_M _ AlM=6ź]␩'_gqSaij֌yѯ cPDZ,:cr s2"_f\k Q/kᑒѣ+OdnkQ,3^ϥﴤc5j$[Kao (9,Kk_~%' {iUGF8x^8i䕜(+xgoW/|<˫>ԗ "t=T(c¿.TpC>X˯ƺ-b@, 1+[CӧOoC r>4x:&R6hH+m|vӾ*IZ`uiwo\˶0I9|'KX/+ Ui;yP1x~TɂǸ?^syo]x^&Y[=BX ʠr21\yo+ʟ!R[1GPѧ4,&GA 첊:4Oͺy磳5.e_J?/C_ttj(No1Ǹ=~6(#.Y<9F˥N6tX║ vHL`7Z#7mj6 <-Agf)?@7Wse NWϭ~ s[[=x s C־ݵ;6sQ{y?n6>j>{j"ӒKٛQD C]_l-T\ʽ@1kļcm+jŸ/?Z[=OKVvl)og.\ះt8-+$@b8 mo_9)U&3Dƾ.Sn\k"%sim$prrj}km.mRÚ""$6)dAQ^~yAb9֏ 7oK@4;I=%EۏF-ݘv3?ʗ {|Kqg/ooom#.<2v$(8} '+-K_aS+ NX?jTs~?5h/1ϋcN~ֈxuqm4hq_>i1@ ;;IUzWedq;NV[-~?]'vtsz9%Mٻ/&g]ܱoS=OqȇJo+l -Mg&[.$@6n+9Z|7O7|ܾCJ^|c `>a0=' ׂ5 u&VH?clUT_8_v~ݑ4-?MUo|7h1W/n C'8cEz K}Y>X5P5U^?339޲qC:ʋZ rXn@,=2f8]'ڎ|;.? ;ٸIM'ý3Zx{= vWwʑTc\'5؎ "A+˅̽)p-.,sOEhvi!R]Tq+ d~3Xx[< ]6<x`qح|׈?զC_krTSTo]x<.,/%0H+Y-X;C)PGu(CGtG>1G\Tg" ޫso&}ۓȊ6-oJE'í_m;mN,Rgw'܋'+Kl~O/.J߅k'ŦHƕ C~eLCt"yGl|0V[LֿV{ג1ɪ/Z3֚']6ؽ>`Ozתx{ XcU1v#?. ֹkr7YE uxkk>^x$Y#[d-J錽e~CD" ѷ`g MvWG;g?T|qiYIfT^=Cixf g %"|?nO=[NM8N:8W~+Ҿ'5ݾڵ+%s߷2qʣEi* }I]I}?gwKG)RE=ՍÈ/ rrh?N7(x4 C=zOb{iR4 ؀8jJ՝> JRRVpwN_W_Bk_w˩Z6=ĶzIqۥp.3C&?ݷB4RaҬm[d#1MXOeMjm\aX ƴ薯giԿ  Ocߥ~?fo&|~F.6s;A5yHgHO!~]kciKco|o673~7(fЮ#" ؿy'Oڣ%_JעX%\u?pe.չRHiwb4yLNϊ:YXؒß'8WS!`Ks7\g3^,tZZ??gڔŷP?$7l+ԴOأ&Kc_doG/c֕1QKttᰒvq_ _σ 4:H$Nωu(qi?vr:0IWV-Uwf{ܖ]'$C<*870|mS;mP]\Ipt ƩZ5 int4Y"hdp1XĩMٯ|a(B_zGY|<]&>%zau -ف8Fkwo-K4 Ӓ0g!q[W=6SV=LktFU.Wb  ß.t- FXy'|q.|Z_ѷZ~'ׁu+mKp?+'6"ƙj@:a/S_•-GΜc.>b_hO?s -b|'ޢ!U'^r%`8} պFj? >Ps ۯNI⾢Xp+Xugiu_P܇*>pcW'wr'?Ff~w8H?JQ˗vzPχ~2~X/P{;߇72;KU'K7.K>S M*6Ki~.|4LIqpQ{˒[FmUK%gg T_VVdbaݻk{߲W{w-O YQ}Z+ڲ_FB"(?/ kmoKF 'cF3өںzpۍFc~i~ܞ/>"мQ ̫nb/궚ܛx#xJ`d IkK$-M 1WQL(UzwJ_xZ+kLq xDc +om%5k%?LXHuǷz5{_Э^MN, yZI;ㄉ@X͗5:qE$iX(ϘA9=F1?|ר~^֫=Nj4@ xXoz?ƬR kxu+J7d+6  Gɯ_ړ x[ Xܻx~ ePU *pw pr:JwJվf m_4_ ~֟5/^ѵPst]ҿ??K_hԼ?uK֨r.<;]YA"pg u%zW4Z.Z%sq uahzSk?C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((f"A!1A"Qaq2#BR$b3r4&CSs+!1AQ"a2#qBR ?B15soiJ@Ё*Vkb R4d!Cc"t\l `Mɭ:.ʓI!OA0)JIgd@@'Cg=!ʂTVT†ʂ@ ZRzHhl=&dʓR{Mj*MIҴMg5 j  cI&%eIJZ*L@Oi Ei5Zj Cfț5=vbЕ9> ЈlţKE"(M7LZ@yCf! hD -QL% PD QZ&Р{?Em9J&&$OwIDb>7SBhUR4m;`PA( 'DEhpN ND N=IZI@VV;JCiZ(+4ҴiZI@VJij=+CiP'tZkMiZġ6:bSbSZVZkJiɭJdbSi tPbPk@Wd֘LJğ% )S;No}9(n%1*Jd.*DM BG*y\IP p$O )j30P5LŠ#cEJ TJ pӄTSt;D A{@ +O{N O@*Ohm TA'CiZ{NZT) +I2VtE hLL'L2E$$L LP$&< I(N% ")CW$m<&vObSĠbE2!BhyQo;"*7TDR9FTD/I=NҴQJȻE hQ_ ;(A*A h$j$ pPZp@t  $R@A$*' R6(%2HҴ@M{'%i{OiHZIZɒ@I5)& bd)bP+LJHP+I1;%5LLJ)&5_)%)$SP 147D0iLvH?*h\S @%Fiq%-$. $EA‰P v@k4F *AN[@v{@v FqA(6F)ZV{@tQ_(yJA7CiQZtC$SRL$t%BJҤ$r2Id4St!ZE XCLHbP MZWNɭ rk(1u&dɋDJkB\rE!LP.Q/=\;SrnPm$Ȇ6%&'9;.;$iD-FN$A:Ot tܢ! Ai"/m*6eRZWi@iT4XA\=nI$%_)9 Rd S!D@dq?) !_TI8P:q:I7$568`|v潖Փmk {,HZ\?Uے}gCg7Bei{+š/oV~#I|MPo7T\'x7c e|!%a9I}p/GJmVaCI-]4ŗݿMgܩ-_,LcpV] 'ܧx OCF,GdOi,_;!.OW4YbwOjފy*W8Ji4dwo\y6iO$I%>N#D6wqDnr9>Oacda+N-Q((;ꊖD->D!8z*`~vOj dIiQN N $Hm{TNxi :D謦QVȆKꞽR ӡ34H *Hn_YCZePͣ\h JvhF=H돦J&kL ibb;_IJҤ {kNuZoԘk:mWnoMcA(I\{|\?h4; 4W|rfm JM^ Udbpi'Mg0iح慮kpPy|/{uwRa9Ė>OԍuvB~W }8HHiɸLR<$I/c!P)=n$QBAޑ tFNmyYK T`@0Q@l'e(>R`QS /P@!ۄHB{Ed8U$7*qB\ca⦞>\>M7<Ke^^+\-bպzfV6fWyk´Qe׏3 iiځY>d9#[ucD r=ea\mW%ֺ//'ȶkH$ăq}W.s?9أqt?VیvOK"KַnߦN'Ewwʉ(4! TD Q@ ='A)!dH$Z%$.kxF#?AR -rH@k3Lkӭ틍z+^etM@(oRp]%f$ dH V;Хx}S*;klCWPT^⺺6R) !l2`o95SL)$Hq Xćq܏3|RF8p$uo[+01Y,vc#M\x9PM|Q~ȦShpRy=Ys{f|G܂F0/>[_X.s"MϺ$6 14J261 畘7%$qys|6>F\pi;US͌vpekbt5?[޹2ƹ1Gc&%{,Bf0Cz4'!w̍oQ{qQY}y]G:n?Z[HܟVLmpti4 io-VgUX ȕͶ0}=#:9rs#vᕭo5N\{-l"vcKyqQMƯIQ~?5uMH-\~bɘyѴ^{Sž,e7 NJ\;-jM͟kvu ʗ>ֹ@썓_HE1]][(Q7Lx(aTooDW׎!(OjDPC&xLyD.&)]I  ̈́I&ѮF9F @8U +({D 0}5hF ࡴ S9vBNܮyn})n:><.c y/W#ђY˅7Pȓ/,umt=6#Fh{^l+3V[(}5,I4ڿ̅THkZ]3qD#ܮW+wÏ.I6V09,|fFm}h;5gn8 h3Yqj)>+e🧗f a8 BCa{i1;Ux<3. 濧O!E6F'17cNl ~s}Kx>-<+CyYl61v=G , v31q;YVW;:O+W]zWjq7ɵMbu{uA[F[BGI hb"$"2| +|M@4K<~y{.\;!Î .Q{躜1;=6oV>%>Hq=(lQ3d8aoں dm%e0ߴ}'} pkx?nsDIF DvkF:x˧>yܮ61Ҭ8<]17F %hbyk%n6]Lp ;-$…kmQX(pzGv-g'ܮk#ÙsXw8#Cs@N]9czT|fcMvaϜ,V{3Yniu>~\u1&ihDvyByLx!((m;Cw8#y'pTH쒌D@T o ʉA;N1whF0Q6<"Ch(D  ~;Pfy_ G(C e>,$80Z"`}\ndo +ymt>s洸YWtscf_?Do]w <97\-ߏj  BRPU"dacނk+eCW┞s=9JPVE3alcDz'+(Yٸ ت98^YWzg1phv/\{˒A{jI\d$k{/Ign;|59{t4')%zeaa{q>+Zxd6¯ƀCLҽn,ͣ0W,4vhXM΢[tYqHgDg-,t]g0K/$nϲm"9Mit _ e˺k$8</sDNyNa/ǦUؙy i;V5$ԀQ]Ƈ04ognE4X&xnTWEX0mWF,.#}ؠZZlR<U458{? 豶w~E54N']_KM9 RN#B8:[Ew]:%s}M7Qn R"Hӿ*Z C/Hz(ުԍtO,8n7NHpRwpiN6=cop>#U#DM^n9P.7 cCۅ 9;xsK=v;)vM y<p쾼|#TLj}uI1IM 6j?{B8F8;B0"hi(j x?Ɂ6]CDty@;ukd3͑m>V̳HwHwZA wPͷ]v$k;=-np:C7s+|v.P־Aggtک1ոZo4(!~9izbp $p^>-m;p#v]FT@3XZH<,3#kC 7+gSbhvG\_cEfp斏-M={ HkKu bh>[R;KOak;%./!kD%sAkiZ핌Sf}U0E:oQI}#mHS t$$$ i{Rl%sI$h-u ccETžf4IoQ袖H3hkwYc8&=Ռ& d= 6c'M.l- `ua.2 t<y9IxOFTŎyZvh" $PtǺ8 s5ǺvE;#5W q4.$S],B~h%qͳmec ,5!)Wpxo7Eڍ}"3+v_ Lw,os}9q#{v4)ӇP:8?yq%7朡+\">"o(Ri';$\p(ڌDF?1A~Ƀl EJ 1Q@"D UxMYZ^ l:q宜~Aa8bPLj3GkNj?Es܃[{\Zޑ(.U/B-947`{._X\zZ%~F;c`uͽ|3QOcdu;KbX^~[ctge0#nުVP֧S۷XBf4=Һ~? +氷v9[:jzq&"EVM +7JC_8p?+;K,vsC@C>\qũDъ :ږQuoŏ!l~̗сp9TO.G<WxI_]ĸ㸱K/#[O!dT_wX7I[Μ\d2kpp]~[@$|.[ 1>KWTY KLv'ch!E\,1#e{RcZ*LfqZKX?N?Fuaxo]1kYye64,jZXY^tϑƅrrI$ k66f yN-Ξ:Z+EMoJ;8 ǰ\ެcnC]E2[89fiˀ=s:-w'B>`h&]tЎkW#AO \)էۅI_m,f `-duɌl(ٹ@hH|R<3uys,5{,s#<_ Wrz69I;mF޻5ӉE3.kmA.n&$9qeSC{&a s/`P)5T9`%#XX,I:n@,Z..GE:cOoe{ʸ^rMMUD#.#zN.%[X:| !ސ}8scXFv _AC ~w7P~W|ؑ2"7%44ߔ@{ {"F7F7@A @ݑQH+A7vgz[?'4nJxZ:_E$y~ҠiY)ygpտ|Mu fG{fjo‘u uե fv@dRAċUq8Hik@3xZø{ȑj[PuY@^ySG -*V qy_w]kB]Eht0XA?),G?8yO|G#Fכͼ$oӷ5bIet7Ӧ3F4ݗ4=!Ijtppgp :;|x>R |CNӅLaE.o'p X:WN9sEp `~ϼ4XXDD79^ '}. qˆ\oL/hJ8C@]$ٕPl1HkE"1k}ckˤ F,Iҗ-Kl,| kκE,Zmmt^2[rVlhKtZ9eR:c|ue~ KF귅z{YrKBW[Q 1Y~Yk}"ikh]qwiq %a}wTlfUvXJkzy[J&!x*stvt4)S˽$vJ%wfpȺR\:0e`ݗ-v1#W!Eqcl靡c&Ɍۍ)j9鉓Abbf`k%vOfV?Tp,ƹyaq33)4]"MMQt({Lf9\rrˆeP432$wAmrrͼYapBb}ԭ;4iRS4:GjI'mW/uRI+&5[bT#ObQEĂÆ”{1$_"[ɧPaI)G{$ޫ#,h"-tΚ3?h乣ˇM벸`߈ьs ]uY"W;H̿=(;c+Yfkv}* bpX.[F#`'FkwtyD켓[~x&)cDlp'͏,H],2|èxąXkQiF[wysWnck;ʼnihlc#o汭FFޓWœKZ6I5 E19 h{纙[ҎWHa`!vUM:h2:V*%Ƈ!k<=]eV.s:/嗴~b.m1uM#8]411RNc&OnΞL`]?&U}:^آǘKL4O^Stxx#ZX$aWd=N\Y|;}r.) Gc*q[ cS\U?;N]n1An _qen}#u 2 'PW SÔI"*~Zh`UF{*bo'nr͊4r9HOۂ4%[P Iw&-M&w4'`$l='WeLr$xV%Ƃ&F=+ ĞlwU3\n9 9qEhK Yvǧ'؝Q)H%¿㈵cDA?]i`@:׉"l=]whJ)Qj(J"Y% ~D3{U[mJ*͈))պIuPpF*ѷL1m\;da”um1d./#u;cZqVY2PeK&]YI*kJYi/ i@@l2{W#{ Hc#L$dc8]1ej}E XYPK⻻25[2z@jCat4ir26W! \Zxt.SɥVWAə+<kgg~!e#[[? 'K#G&{P%CCM=.]G?aVo+4PN-6=;JO. qlo bFpmRF:,KO|<a'-e2{L]ܪQ'A3K(~= >ej(x*&B%n8p/?Nyc۫@/7x2̋&i'4ONـF^௘4lҲۑ3wc\5n._ $V"EYY\~`d]OkeL^$mb>c'Ooٍ,L41)I0rc17~V| J%_K4k`/nqLʹN:vDI*/%Tݼ,륆[)6>6qf0PWlډ[@ocb5vP=M텗ff[#P3+euVSoHgvgiԹd3{=d67Du2E!f;ܚKz] /V:1l#z~}.]eu6EGa$0 {\sn_B*Gzh4^.l Urx̱^-ɝ/P#tYy H >`oaF] {,|j]㑞w)HϔھrH+jvQXQx{x:}Vr")"7Nb6F *B29aGk*!#d[o\pI n-9^}@<>̌W-ײ %wW췭:<59yY8im6]Oكa?kɎOB/i0j8~7+_:2ӺM8\m߅Ө[\+رe&8!iQt KMՑ 8@裷hNmGbRk_8!JX͕3:Z 'O #(H2˷G<= 0I&ەxZ}B8/r|Sז])hi)g,C @TXe$2crze]8:;O'e}]=³ˊ\aʹʎBq=Tu}ceZ_uh̕⪕sɉ⩕x>O'TnB򀅷0DTw;(S8~J'| I_$ٵ[߹ZCl~NV\ĐǷB<@B  T:gzv'v;]$ahm\wGs9qw)sGrYvڲC{HkL E|<1P*7\=yn-owVl̩sK%FNwz_X0[q]!ٳ2Ib qa-ruodiʌ#5zKFlIïI;,Yp永X<ғdqs@i֯LAG30`֎{nzahg3 D$n>Um+a@qq-^ɰ01q:Ov;]G]Jww^NLd9_˰ f>dCCzcd[?O W-ӵ`f@_:W^%6!k9u[ݒ1QJ*hρjUꉧ方hBg[ںXn$Q wЍWK^AتR" `{}gYٳ!N. ̔7=[]{~͆v9~.%. LsMiq4T7 Y&k("˅^lРi|ܸ7ƘrL{%TwbTѻ́1]Y]ߺ^<prײ;%oJ͔MR6iO_l'䶖⛔t;1F i1DwI$#!*Q!-KA1 HPf6V)@7r{x"ٙÏn9(Zx%=RHG\>;`Y1ΡDXL0 KL=\d<’:Y?هxv@5J&{ZF :cJ#2]頇iap Q04Y$]Fs#1Aѫ&lc$|.wyY/q]Ŋ,yP? R1gc|sW."? <+rLy5*Gr=Fy\6(܅|j'pTU(/ vIM۔\i8?;{)K]<-sI-ZI!XKyp^ؙsgh+ e(0d y{94Xlm`ov9p4zҺc[2xUsj9eE;4%fx\d,ub.jyAWIdog{^iճr|_.C(p\U##g^PK&eQ5xSӦNwNvL8>moZY xݏOe?ˀlڥ]<~+ Jlv?T]=3m$#26]xn"{?5ٰ԰/,7|~O5%կ]NNu׷ʻ=]Q~Z|yŖ7 Mce6G)i+@9^,['w^߅{Ie=Dpbo`R76lK-_nVpIT5&}JcDP*dc_!HGtq0<u 7+Á. +cUIg˝l.#]k0u)'ȌI+Xa>ɖxMlnCQJKPYNAhi཭yϟ=O|MfV9wh'*v?d~3XdM<¸*ut~ 72FY%^Y&<ؾ=SxYGDkSZŝn$BpG OF~n$s?eحr"c-gRn:Qly4%'&O-HdAB~em͹eW#ճ1rpa>\Q˖u0l'Qm69Ý .Ð_䴴89*vas_%IUa%~lv,zƙ%2En:H#`$5.=רfѲG.ڼcHrImx#䫷sөv6kx9:Gh6L 5uuFDG|?~4Uuq7HVzHct_gCopܑ߲cl~eܿ^|p:VGP0FVIq:CEx[#wP aIAտcdA/7?'z/<ORH5Ke{}bnQ8'?z 5 VN\4zf9)Vo0 >~,.v'G7iG R܏IP ceant[s1D3qoa 9wp1bA̧X{^K5t^O,CCy+LYn#,Ȑ\>st{:s?e)g'ɏ'b]`.&wQV׳B58mSۻHW=P}'iY&:tEˁKs9i>$. u^'żYq /Pe @yBe;HuY"G9c!f-ސQEd%qYLi ydxn!\^WY# gis{..q,yq8Ju5wG$|S۶7^8}縵.Yn(N]bs'!GE0de͕wi+i%Uo5Y$8՞Ș9-x9^M4=? %cCdEGDOE$%!P@z\\Zק;jeA,YDTnM-9ߓnjؽop|Rz1;VV 4v=]yQm7jo\~[7eHcG8jI^y(dd䁱Z6G>p)e -xF.15UQKp$j'Ц(1(#3bvPtQq!-]eT"db(%"QY}b]0=wnl>Mc?7:,eioGy?@N,g7䘵9zMr䙁߆ %}IGĻڻ1)Gf(-Fioײ -Jt8m">GH:6 'pa2\w7hCț(4] .zwSxvdB_j(x7+ 5#}n=8om3Ou NE4txZi'ArVp=oi4K7=z-ъ\ CV\@fVsss *X>8 @@ t~^6ϷnV}խ{Zw7jp:H;oeODSI"\+aʌ̧mz}# vҸֱѷmTyDakhm({3- -%o[a/e$2 _63)q֖Na$fDoʹ |tyxsW.죅ڎlth+-n#9خ:Ӵ}k(FܩX܁iy`H|*vǕuI+w҄72a#h-"7V\̰Vmpyyx?yҥwD /T?9>7W,FI0;e.oJGB̘ w<;_k754rr$2h m/Q“Of͋*6~حN#o ?Og]龌8hWHF$ zbu B(? HK:}qÆ|1;~ G[Xl/Kik4cӏ- rۧ"FbL8/&P ]gۏ׆DŽ5EHE]AK<\NSQ$7Sc''H.8j4c7qK0? G@&;KtXhv6, eӹsP@T;]~i 8yO? F^:ka%cGKt+HFDFꒈhWO84P UrLr!BcӍ|_+/ެ/ bb,hhTo30 (aůȏՏ+̾9+OcK]38~tὯ=ΊM2|008t^ wI p>W[Wj 3|-8,qO4%!nnb~ZaMaKL6~!J8ڈKs@k(&,Τ ߺ5aVgkrsZ '*w"=Aۇ|񷶖D \AmT:zX! FAf]ʰ؛ zW9csoKneXm/-!46$ tB@/CJ XlS'`6_v.iodnݧڂ"R9urv+1]KXEg,e9}76}ըvYYna-.яԭҥ3~zOQQəX@?VR8XR[TMUi#d19ΦTO:(,egx^Sodf 1K@Q>ܳ?j6S6iO0[r ?*7o;wy#dg5oDӤ\wzPDžca1x,xij :OU%ASk\hJě`3Gp=z.qcwn ɇ]z}O5}66#Vڀۺ^k'/"']7Qbf\3uL;6n$ʻVzrKk/zh/0S,X78:tuBO \W|`b1gH"]sdcx+//s^t-9;Rvpp;I < Cr{זmdcn!$u+AjGCH.WWM.(KHMIe0z#8VԮkCpWWD2B繀;k-A,f&#_jߑG+*RXT1?C[n;oyٽu;=&Uz{°\k.;:wC#Wh, M߻57Xpg`[bGnp 6X |hVr/~kCuA13MQ}l}Yf9`װn SuGuzs2DPvj+6mr(v6wZ^Gm>&+,:}Kע"௟_gk0MUn.`}5MV%*Ui'QAʲƖ7YVmJͭz[N\4{/&񻙙OJs0~+г#auA KOxVRf DocAvWf? :2r|鉣2z m8H@$oKۺD:qj'Kf; m^Dxʏ_tN.b FL8c/2Q A]yã]ܑ2Su }FIk+&$JyU p{scNoDyy~ybWaK5Kk>&hqn7Wb+WNl6e&4ċa |BoKsl̍Ϋԑ j1,N |c|7WS46jAZzpsN⩗ ioGpK wQM :x akKZܕt7V#[z(\N]<)f( "kfU[]$aml_"V^\#NR\w<ݤ88?`C\TJv;J(0@_r5WYuU:jEQH \_ -ǃ[!6@hj# 4#vL` y: m:̎#(i-Uou xfbɏ~̗,Ѱᙘ^c,^&SZ&\Ol,S y9{eob͙!/.y{.Y,uˊO{$>'4JkuzUGaDD ?<_njN=5+PuͤޖO^QDj’wYI..pb` EߕVg o3PKdo"F$Kn $84f1劍|,6C#%Ʀ}kCݝm.e+i 1I Ʋ6%Վ[|#z>ABscϚ,.\h?_o΍3tWyJ j#xZ\DPF ZTF66̒+tu+u;]-b<=η8 Xp5]ڛ 0}\ E;zt–8@m*p}וLX)QD.M\d|Ƃ06F>#ڴhz[+X ׸Wj*)=v E@H{Q͏TdVܥր{W6pZ1;h+k\C`6zŮ2\ n5{oc]=.DB#cm8u8fH\v##fx۳Q܎! F^TcݩnZS5_\Er3SM$h6X}T:Iby BX h߅X Q:Xùt͐߅atye-WFMhtsݓM+#pm^ =16nkpE 4}F;Woy|]ˋ#Yw;k:-f{NNbŅh}_X|EgR+6>7yoz#fof9<7ձ:Fxd2FX)!%y,>9:yt9tk&JikC,7Y|{Vv|whȤrJ宫ͲDq{J2̏#x+-%0rx^.M>g)v>vy,wײD]<)"nKm>[\v+Z۶h:$Md]R5N66;hn3餝;S&g~A;sh\{áeAiѠ<4O6&m9" jf1cvnmA5U¨˼79Bֺ<|rv,uPnu!gitǺOF4յN.3 .7w&qF1V&5cY'~6EhkY(HP ?")Xv7KyRw4ק`GEҘivS*ot豃AkmV<{m%O$a5+}` 7~kز&s4.Tr<ѷkM8s4{ ߔY\kgŀ}k  a 򽣯xuz58JF'yg^SӲ~YWIقCܛN'{ "j$Vvrvs_%6$)]&yTr:dvL-{6- {°g߳"'-AEX3"t `.q^/ sĺf 1&Qfڱ`͛$y>h[ gz}Cc#-k*OI!s#ٌ$w"n tlTҡYZA =n[噱[O}ܸ2M5;2#+ UFN&ŀEkiH{%h6Wq5tͦ8's_e+DN(s`Me mv^!^*2Y~6%x4K~Ͱtt3e;1x{unh޸X˴7P#je05-rOU.CKѱ z}I,#kZ=[ؒzMHv߃I-[?9+Dn4]^Djh!jJ״z}$ݠD[ˍw`Hctm:AvWv,ߝl>ǀ :aPp|r)uDmlrAciCKFV-sک 7JLH벊v8ũA6\Tƛ٭nwQ%W?96| Ah8?E'!s›!c ;We\[ 4اc$NM %{ k6LFM| ~$x:e$c" 67s̎F+-os*8c  .5|(:6~ !F^0`|;<yYr\N<5Ǐ^{k^9Xv;H3|~Ma`c'oe" ;\ pR$)O5pVW" Aq~%H܆NwRP\"Hۦ3\kaycu7K-+ucVtK.9t&-%% 1:WPsZwU&Qcݠjl{6=^:tehh۶4u)xqx 66j~-٢A͸e_DH\5o@Xca}p}߅)Kdi$IiG}y:q YIB<  3P"]a{-}>xLyk U}-Ɔq;{y8 {v^3u>`;ެi) #|ҟn-}YQɂV&>HeK.4&>þȎݬwp9LMV&sIQl_ ߾~gQIxѻfGm3o ZY22]&^1f{ l[${.t=׾6ˑn@/exPx ~kχ&z/'H-"oS_)r-Y?ex38\lӫj"HOhnt?*h F\8=֖1(4 w<ʇ7-?r35r;fr0cst&4*٦@;ӝIOPEu⽥^āCjeYHNkRbI 0Fk"55ѿ5mZOjYQE>IVX.~/OVpL!c4McE)k= CS84a=j7.~Pni Lmp4jV5Z,Ϙ׹`;w ">uP$wi#w\; 4zwqݔ]Uv>Zw^PdDC,D/i NXϹڥǸO Dˆykm6{kU0(Iӱ Nf Ʌ[Nev)^| ePiBKD&V9!Fv!9ѝ_>H-i.^=F?‚b4cKVGIpxߑ[ls 5tvK{dE\^\ok3ѷb<7I1Xldy]i8 sce *nsDD1l"(,Mf%]sJ HQ Xd B4Jh%هz?,[7‚ kѵL!3LtN}T :T'PP2 R~/PbN @S05+SȢyT umC`p/-n+H2GD#̈nY,t\r۠Ɛ;KH1^y}֏Q1fߐxo^$Ьl$/wZ!4!4eu6RCe apY3V=ThUoLt~3c>J FK c"_7no#E𒓩캒]%V*7F\ uSHrE)s4e|]N0ft&GswgگFK&97<{g)[ڀ=Hސf\.叔8Pd~-jS|E];q_HH~8ō‘ ZW>ۺCɣ2gnC=}uͅ<'P︿t5i$ k钗C܁%2suoI[Xk7&d9S#wO%:s^tW~ ׺)[IkoqguC0ev` o@h a8d0:>DPqlמ?j54Fhupaķ?t̕N1^C,#ÐecG%qkd*ƏO/=E#}F p}xj\X4-&;C>|9IW~_D2(YZ5_/hmbIcӖ?ũluV*Nv@ZFQGW4y.AXt ;H|:@[~Vxeߺ&5}:VIsXxG\fMoʒ2@YAa/>mѰV2 Zun@J( PRV_S˻?6F>-ܒ* Hc$FuUtGةkzvY&65cgT_<b2u.Qc'DOkM ^ѠE xƧr`fXsKy=$G+$+ҖLh "%䁽>H`{VG5Jg4<7 *9qi'Ckl \<+E])F wBoJke{9PsÉo+-y`!BPΈ^\蠜nݷ)+>eq~e2kihUO1Ѵ7Bd.q$aē}Mz}e{, h-׺ HzoӻS6<H{\=iƟ*os>l9ڀsHɲ(sHh]i䷁uMWeSu l2Lܸ`o ܫCOS:mC0k_oϏ~(-Æ\6&Iw;:Ns3|/40=zd@{[׺RdͩJ$o ?Egu[_uWqܾ_6B6cN[p<=ܪ?/>VwXE>DEʌ_]-c1mK:K#kZl썳83b6UZIw%H8E@FoQdKyϫXSi޶WANwTr #m ;~mldoYHܷ65yʐp|$ljFȩv>GʮUItR/|mdl|vmZΞ69Cmt+qwUӢ 3c7xve8ظ꒖i%s쳡צ _>W/?S3g'4]:oc4y/#]@Ց%/};7zzSKdխf8[ޛ#ٽԒ3K~eǟeyM [t^gm/ءսW58_6L;Wn!= Dk捹k?*\Nr 6 DʗEkpiqnL/ ӫ,/}mnkYm"__?+ѾɲzWɗ.Cc̏yu:B\$6אF{CBOvVO`o`';vƻ z:u1o4MV")$`ߝ. EԷ!̍C$>N@y7ࠑCWe%`.TK M[I HUCKXQr 1}ԑ8 J4[f{Yc3}Ti qk\ v d8Ս'xї5XJHڊ5w"yqoXcT-x!xiu)ZjR!|V;Vp[ k͎A$4`qKT&p`d9q~ѕE:Y;[\h5ϐ8PU"Xn4RsEQI9EpAAp`MM`S eJY\ \V+`q|IA"ÅP6i <8,c%yAU_,Sx/-2zt%3N ɢ>rq\ dTm_iNIDžK+=xoWc }\lxub!kI3p⮵!9ンZ;_ό~=;9*#>3<ܗ:uΧ,D;'Ąr2rfsK+u=+y=yn<Y:X1)ⶌܯՙ0! ԉ۵?h9`ǃq"<~1%ddIUNN&o6IUep%[|#S΢ PpbA]/ؿV8c_\T$tٝd6S*L>$Gp+\;㔯$I n7B2}Q}.%͵ZS#MUq .,ˏ dM+CZy_$$u<9iq dxQ1h'$ٳww'_\]:czs^/ Yrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/subfolder/gallery2/0000700000175000017500000000000010644674645026705 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/subfolder/gallery2/fried_brains.jpg0000600000175000017500000020232010276336034032023 0ustar madduckmadduckJFIF9ExifMM* &(12i@ĥ4 WWL Polaroid PDC3050 HHVer 1.02005:06:03 01:00:28PrintIM0250("ނ"'F0220  &.  6>|N0100f͠bF   ! 2005:06:03 01:00:282005:06:03 01:00:28< H  zKi46001231143456Rj7QۆPrintIM0100^R980100(4HH           x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?;l/_|K]id֦fCo2C|C}X=j v"xwLǙ<™yKW]+Mr46hܑLV6Uk- [ZY3{8RLp-e:Wn<=?W02xڿN]Wmņ EOX*FG dd9Q>kԓ_7~8xGKԬM#z'Rтq9/#×߀>h^IJz\K]~BNqYZO42w:آܨ[ycNIXB$PR y+ ľt e.z{c?qc`[,>\R~6mZG?j?X^!MFT y[8=xOYx?ĚzZ%kiUrșVg[t5_ i$V͕s|H ןʿY/t.A`N TN ־k5Z$K .IzGwv6Zzim@;"C6ݸ^ԗ?K4VMvI` ?1^ ,JsW˝,<[כ _G{e .5+\>O= 3; 7i𮩬ibR8pTo ֽ˧{xx/-8^-Qχ~oO'5 Z+gc!D9YrIU ]cMf\16Ah%Xp?l{}CEa{QE FϼH wI3㏇_=kڮ;tQ4JK,D8f w#k%NI=%ִiž~w4-9uw<;+OL6Svd$:v}~sw|m6e{Eso7dBz JH䤯/-NUK Ckn쎷h2=̹9_U%&eR$g>(Q<®IW^uI[K>auKo|l;WUX"2 v4zkO_H7 [F8JBvvnO\c{8-#PY7c_Ko7r 2Kq' ѕSWKp,hŨsޗ&jK#ڣ6{yg9:[;*찴$ou"$q|H־uxI-䷿HgA y⾦:_|Qvo?kᯁl$K溆 ،,z;ꗿ0( k&=1_Z\IgPNV}hqxiw'"vZVϕ/=|t RT/^6.,mϪsa5p-/#ݠ>lHuPOH$Nx_/¾,#ltvcvGù}G|_GgU?g ;Mmu?_?e-P}ڋkve vx(ĜFp>x3Ɨi.I <мaG22ʮ0p 日#1"# Nվ7[ŵ g|c*]c_u5[ kAmRO"h$7>jsH۷/ xi%ӵ[)= @؛Rrx46՚?|#ՆD"nLp6|''9y]|mM'ῆ7߷Pʤh y oI>/]Wj,6PG[EZbA;K=+t+߯Sֽ~x3?ek#Bڼ+!źY j_#ڻ5ǃK%ү`I|P=S[??KSCmMccֶZPbS=C/Fx~~?N?եz&jqVi?}2ۘ}-U02sS8]nӭJ )_zI<7VfSlV7Zl IOHGT3 x<7 $Y\x ̺vbn~Ίx%6wQ#A[A,*R4-g\MbXd-ސ.2k1k|'_x爼BR-%x>]G:_R0D-ʹbW[  Gw6$럳6#H ٳrtmVzymvb,ͺȭfIFWy Jqr4zZ|d_gÞ_j.ؤ܌tH!`u@NWqͭXM7l+ 6ˀ;2=~? Ϥkߡ$OG@S%"x>??<;N|4&ҵ;I4Obx_>9Iͫxĩc[\G%ޑu3wnhUrV mE'o_'vPnmvt] †|t‚~?bM'B*Vh5_7`1v~dy]۝{+{֟ٯ<#ig~KOOY.%~yX.6__ ~xkVkxkJ&  9kw-|?*Vt0$o00aG'?ssTpN)-{{^gjU8GU|9fK+2i7N7~Lt4f91si?_bm8 9?_<~)'<+ՓYm߁ofʉ SR8aqw hGş >2x?[ʣ\`!I2@drF ~?4߇ O_I^,Eܯ/]٤F8heSdFc~g,%GE_wX|:=};{H.pC#랧zָ0sy*F wCCmx_z|-Zjdp-Hsؚc<+xvOߨ@1@=ݫދjݜ~!?kt |?ÂfЙF&M௹zx~Цv`" YS >Z= /&YO7Wj)uԣ+ۂ:J |?kD6ưg v=UG}:J+A_ Mq?'<?x;M/&ly~o$_Ca? ^^ 蚼(Lw–ck mgE =*h᳿~u|n_[hI"I';F;lk4}ku|?`nn$ Eǘ,gzW^LUoM}%%ngVߩğ6wFBfբUbfğ*|1}Oz?/_5? jO=YppŢ'l#CuLOZ8/{[)B睊o_/zj10ݭ,wCܰ<}>/|ѿi]\ѠM#x[@-`WFqB񑝄I z\Jon91W4?a[K ![$,qmF\*_8oNxZ{]`&%Sg}`hq{RT`T6>YVM􏜆6Zsۭ_?3;m gҭ| ơ;Hai/nf-HR7#7R$[mݼtV 9G.ғWx.OcZ<ڕPρVek׌[x&MQp/= WԽ>ZUޚ47 +˘Zx@g}THGq -aࢺ7\=Ŏ\ƨɥEC{rrag>Aʀ1jʅOe ⎟y]> tXm7'%![+6MGq$qV68=~1^\Njf[ںģ?c }W"K_~ g߇nKT<ZeHHAGkngzr3\U yE٠<C3Ƿ|R޷\ Kio/$Fw /'x :m$59=+c[K,sgx ZwfLwR+@ͺ0 \$8*Pj~ |&2NE?G H~7<'z<=Df9cTxso~]ھt}7v3.XŅm|(-]=*sM}QN.!N.V=t 9[}z--3;(<(3x;GYbp5g}C+ܓ6ǰa%k9po*d <;7^0÷9#xo]=e-i(|Kn4(jDM֋7?"}&ݞ)^?$ygį>KϩxQҰ.pF=F}|_xNuiNsҏraڬv=VvKԄVG7-ⰴ4 6q(qeP1urMJLH=ݳ<|}),˿32tJ]>{1@%rNABc`Pgh}DCɨZkZpcVG' 1wT5_2?^~n 7ź91Ё-X ):H՘9L1޿#l6iV>JwZzY$i0 E ׂ;`+iz^Jsْk%+FI&V{3cck[qi6-6<1y^Za +pζsY[,wP[JʹAe[?3Ҕے2>kG xyD`Rĩ˱#tK=-yb$O^^kପu$ڋ{ ׊4Ü xd<10@^7 ?c߈V~1🇼F^-zM2o3qdҎ|,8gd_VEk麮$Rm;SUs,~8#keF[gkJ3 Vw4/~G5jiT6W"uwf`Ҭ.\*ɯ t߉/u|9e88~'s5u5nt fM^(uX壟`TRpګI7:=2d9 q$Q|Ҿ_SOGYT|_yNToL0ǾfZ 8+Ҽ?Cupm;M[$H@liϚWwg?JUBeZV l66D,~YaX8' c֣4.˛K+dUQ(n`mF''+kgҶts'ҭ&4YulAetg$AW6~uX,ۍV@Ӵye{r~;B/&mw~ϪWZ_ |:.י4+2s&^?,dzMxfdYFZ-Dwy7 Elzsu Z&K ';e>k1_C0PG<2+eRet k-wEg*6OȨxCUtIZp 0SSMەs'c=-2]js}|$rs |fDRCEUQpBwğa^? C{n!- t_@x&J{HǗ6iCcw)Gsn|Kٮ 62oQ&GPE[i =7&jz5ݭǤ9]6?5ͅOa nJJC)Gԃ޿!Wz3z?\l5Նo&L"N-af4 S?t?MEJn1};A=[xqC[mN@H\G 0>Lqioy2gˋ6վYMot]A9dS\v8+m|<-Aj7$ae CJ>X(YԏhS/=fĚ^ u;X6Aid8A$coD\--͂d*|K8ͅsߗW%MZwGy|1somaK–V}lIk}H'^uq)yLv:w$M"<32@=3kmanTij~{ĸϳ.g5%t dU ؊Ҿ%VBг9+V%uQ/[ߊe I[m?>kY"Y3=bcZE >xKTon}:(I*kGrGNV7{%S<+5<[Էg0tQ8Z ZuFT&{<{wq }5ka4Y[ ySrDZxAzj7Yko.n,n~}MoSQ%Ob/Ixz4,:֭WxL¾'|@hԮׄ2Am,\ZnFPCs!{Z6ԬA~!q/oKX/ S\,Ȇ;!w`F$d{3wwQKlѕtqȒЕ{9LMjN8Y[Mw_3{]Pfx5+)xN׎5^_ZQYh-^[]Xyl*<39P3~*.Hit⏢O |.q֕iPHHe@?62Buס4ZKq_gTdNO1_=!RdNm^ևVOd E+1 39˟^u㟋>-c})i34X.7*/w5ك˕4bɉ9[}u>J- " %DF*g%BO?"Gm3D[BT4-E mnY Bvq#dpC:_:RZ뛉ἍF.aYw].kx/q#=O GJ:a<{J=[fukx.c/8Nm8wZ/E0_S2>Wʵ0s=5]xR#6|Ds!'ZW&WxZLůi@;^ŒdLqҿuZOxMf o\2JaRhH @]Y&5W.I7HU5(..%f'X# oj*y QS[]=5 Su {2RB/a=**I]<|d~uz%b4'F{,L9QatBc=)?u"Bal}+˯iӡP8^Lm'W4#yqM:)|4^?. /-q4H@pcTsۮ+ΖSʕs8ߦ#;:^62d-':sEzA,Kq 8ھ|=սxwJx3,lJɲں*oKRcFM~:P]Kfqj dtL( <ڸ=[)%oc$Ub8}ָyy}WC?ԶWg5}]P @3qWۦG }&Hu+C"D-@@Hт;fJ w+_y`rmJ(|&ioS:Ty`vOy_L WZ|+~uS)x7An5N{h|^]m'5kH5|[|A4X|;~;-n(= ޿g.5c.k8d:r9&fٸx¯kG( f63rx{^):m<˺l: ҋ~&eKH'CekU-Wu[-GM3ɪ 3E+njs۟P85;~ύ-W𧉬FAڒ23cєx~~?ѭiA* vOߜ^>_)>gQ*^|-,4[+ XAN=Wl*).u=&iMΧSME2I}xj~_g/t1 ˸/Ydd< |"$l<`vmw|Cjn إ ~e] r A~ǫ_&iuf'Btg C1=01^itDEwF/pd8=8U=>4lQG:KjWr֛p)h# };c7$/OL y},s}d Oph8߁׍A[>7 u[y4t~%<6I00|]zclb.iFlL'b{"{ p;#Wᬺ\xOo##qrVwoIe`&TdS!9XV5/ 2X~gfC 7=ʬʧKpkB˜嘏Lkˡ=EZ_kLJI->{[wrV]nz'']&%wq%UMY>WNh7ԖfQ4: Nx^kzi:5~d,2Ѓb~h/۷K?xgͧXII#[Tc8$" pdWw?4O6PXivVkXaQ2]u DkMpdZ\ʕtӖ~#<:V3y{ j l@SӶzRzpE8'ۮkAw8Cg2!<0ѽ)׋[#AN6F+gst^RihWP3B:j4򺺺]^y@]]]@]]Krht*$&it[@*=d˻e\H66!Oj =>8#DTc?\[?[SPGkXd7b XSbPQZBxsP4av+LxfPX>Fi4k:YHLt\W M5x֠xkS5 ~Z0L!AZCdR=ڜ2*Vqkܓ#j@ތcf%޵h2iݿO/ȣ`VOep8hÕG6ְAIIښum7hilE_ШX tcn|IS).oPHLȨ3eSK^I<62zՙ|MTt>$\~ʜ'WKsu5s?7CT6;5A2R7}qM4l:YH >P b~Fuuuu0:Rz✌0ʏR˂0;RCF7Udčآ\0#?ڋæ=趲C)g13FBKy<+]>:U Hlv A1rYqťٓlxp[ł2?$: ᐱv{t5]k5 @;V]w\>/v!8IiΦVC?jUoKqQ/0$FD*JQ 8ҼW&^HO6@O\i7z]Dl})<=1}T"\A40ݶ$#4fەcpqI5I33=6^2yI^-!~P1z=.ɕ'r{̆ofy/);HmQ;g*{nm3)dB 1޺ H$ 򺺺ή#uuzkG(ய@{RWw,Jڝt ITFIi#]Zȑ8{QD7eou7/ *)VaZ۫E mЊK.ەVE=Z8#oomC@෎6VL"-˶V9NxҢT\!19,ےiwm)FB;#WrR wp36K_ IB##WХ*֓irs=nԹ WtMB("BA9v;8"Wl* r q/~Qʐi5$YOI#=:jߗw<Zr-GUFKO< NZMj f ϟ.Tgi {;xHT}(}f%(]Q@#I:T&q>_0* R2EΌAe;kn,4]F8TUO}Dźx^2a=<0/R%Qޫ_mmVIr۶3ڪ/maɿ7I)^s^ q7obLvHp7ݾZDžd& կG3۶$d%@y2}y]4~.BIUҴHn'A- ʙ#87gŚuR-W z44i?›GZP\8!bU;X(eeqF?ot׶LˀZN]vn3Rԥg6'7MF[HycrH\\Cep R.xvr/+Q[];+2Cs+'n+XTJh)dp i|S7$\"NҪQ6ֶR/^)n6V{iaM!\HނUcʀqj̍!1*$ggWWWe]]KV•sIH;v /!u>F gl+)eKФrE`|¦.nA 6{UecUNӟJIdsonbjzi_ |6~S۰\[@9a!"{tp#Cݪ99vZUJ|튕k)yn#8<Ϡ-l#ui%!MSt(՚kluj^r#y4TLS"vk]!*6XǵG`ȡMHv"޴UDKlEÞ9nEEHH0r z %r-#1r+IlBjOQPñ\<@w>WJc䶏oiɷYݏsA *p yC̰OB/xWU=1+5ܦFITؤ+=Ejuy+QP.B{Rsғ8L pɫ?uVITי{ pF5S\ nA aN-K]Ncq4q=J.&xsϵXgR^ ]cPdW&J`WULBx1U'I]Z9`8{l4㹔e-qgͤ"~=}H}uqa4vmjxb_8x_|l;{TzKBOz,>|?֤tqAqJK"j!oP P#?[}ULVh`A7voqw!j|I7iM%Q7bjŃTml"2I;LqhJwlx$֧\LWBIvv&E[arHb ۳lheBbÙ[ PQnO4bٚ[U"gy6]`js7iSSmg8azյÛ TUv>)*;;+_OC7,Y^% ]jmV唅t@vޫ윶I);d.eAL8POҬ7Ze?UNm `gF,QQ)lY츤r7^c[hx~QM#G AYߣUg6}!JDOZ#n>_DCclAvF"K^>Y|88 Nj5rk`GҼLrҿPȥ_ Q/ޤ{9R-V#uv?S\t\?K/(wDMB~Wۏљhb(AҵW2/ 3WUԬyAeʿ޻g_CP|H) rEO."$cjj9%X̼}V3G:B+͔~WJ LbNV`&rXzQd]|hF~mLR(~q[ <|Ty&ԟ@j>sq`߮ωo<@ǩe;}*j^MRX5/Dkb~S7J9G_,da6]t wr#V@DvާI7TB96V<,4/bIYz !i-(rbIJE|֪G4fO7+B^I@a7+H>e98e*I9>Y,&̥Pyp;c"0<%~̌F3=e=99v: y$ʩsA9&Ul'@g&! -\[rBRRYʴ19*r Y.LqcF㼺t5Q@R6j#Һa'uG&L|V)͵N4^Z(28{#([mCMccQ ͩ}+ #jޅ)Yaޓ#ea Ys}k|2Im?TB37G_V/#R̙#VS#f6\X:]ʟHkR.ts6 /ƱpgÉk$MJ0q,cO)靝.J=kf^ӥbq<śsSЬl2kh)7lQĸ{XѯF[ΝV EgBZi6yZk.H Z%$Z}}kaxO0_ƯIhm k2i(ۓ!ӵP7KXi hs'sc×1֛pۗtdab f3+7r#_zmm% 0a4#_I(`]v|ԁ5um,C?c;.IL[$:z >o لmjK[ᵰXe1cD׵}GRDMJSڥ4ѵn4/CtiK:N6&`:rDOLG_|Wϼ-k-Z$# ܚefzg*$'%lbp/!D\t*0*zd@u݅,X4ټ &vh:yi )帓vjcQ (<Ա!۵RfɍӖ񙧎56:Ѻ1nӰW*ϗI%Kb<77/JD94οM4Q.Js^kۣeIrlJgӶ>OV}O gS$zE(t%V W%3gm^%2`ծ(a`{oNDd:w6S.`q1bH" Tf*>L`4J 1;uj$2՞DXboGbzի7 8';iLt8p@& @r~LO|iS='z)m3*_ŔΡc!ʆ`Č62 zd(޳Ri}>ve ! A叙ԞUFMp$eè hV:F|ޝ<ڇ,/qw 2~dcY삡v'=6HnH<Ğ(ele`Y5;U_1d*5MtYlvvZM$1wnF+(NLnm2m.yÎᔬWTym$hb69U},`+VG\rlie$Z1E9g >U`=}!*gm:,Ojr81^*4b##k'b'M3ҁ H4x؂[ /.f8'+T $Æcp>rrORf։JrŠH==)j <.G٣&Sezj֖V$suMQgo E <{kBBSq#h 3nbOsC/R 5n^h$zfԯNq[zfHU9}Cc~^z}O_}P$Mb"m^eSsS_lE.c݀%݂I{nVsuD4֙T^v v~IyjD6@8֚]+K01Y,U@>)6&-I: *BOt&jl06ڟukRj@PqU"u@\X,i?s(i1I[+ݩA`ҭs}wxR gEwA=}("5C*oJ gdV_P;SV!<=j&sG@V2ʑ^Fe$M5}-L\~S= 腽֣e5̊񲞅H,XGC޷Bՠe%YqFq>tNX)(5L26 FXcSIX5>qjɡYO]*e/kgrӻzɦNM'zw}'5!'aOJӈ3] m**R7XUSKn[,/#wFD!wbNrǮ*u>Y!q{'ƚZx&FΠ= ie@;oWNrImBkHś=yZPZmREl`M0Jn0b|;e}^&cն ^>o~2t $%/a:Wwvqn$-S lc n^g?AM|C7ۖT>M%Y󦞍u ~Uz+I#c1{֋|;4W3u0ߚ]Ү0[J$ @l/Y27S{ZXiqrYZ u"vVZ@Lr)iw7ڣ=“L֑ChZjy6L]pa#OOQwJfTU'Zjk(# Нҗ+SOy##&"B.H)rU!2ֺ(ÊJmѴpC[ :x،$wP-d~tRYdMDosl.R'Jhvz.qj< [o#50'[&RKrvFJI!=q_LI K$`|K9Ϥ0I&>S=SE&dqJ0dfVS9a:HI8*55AsA00ޠIn|{Դe[m鄊:tRB_l!SЄ`Vo~V3/y47ӮʥJy?StiSyR+/9Ҹ u~ Yhc1U[W^j$1]"Xh*qx[=j3D "bfC4*G7/޳H^Oz?ˌOd/Fybvo8>/\B2A^LFYw ƹkyXnCGcb~QKﳳ2+vUBnڽ})Ag4 (j=<;y퓒?)/5`N].u56֧qAZΝal,q/@ƣ}oQy#|F b=kS,Νo{PadFP,DJ3J;эW[t&4~XRoHLZ-I.D9E2%Dc\eUO4]Mr&*}3UӇ8]dybbTy~vLfo /C96K a^pw6jG5 ;D{!I]8Kp3\s#ò@O/0+kmcE.6pǩidZ},?̣'5QWch.Wfj5R7xFhZ,ܪn2|+񭥳R>35QQambv`L!t/`U;+ LX8:EBp>I1Gq269("hf-e?SZj~52̆ܧSz">9v%!w\f:'d?CZUXb5D,H8p~7'U?f}NcTi:-x`>m\]"[ q*۔3\ghzp> x |1ν7M\ \ÔRU i"цw ʞVLjLi k|LW64ؗa͞} ˌtc$Q>ԝN2uuŲ{=ITl%U>mR2H8GuAiemUgF9JM뱏?Ht[tGE9:+7~H.>-ˊ2Xy<1SG)X'# cTY,$COjS:`?SMƠ'Jiq'`2)R\7q!m^Ƃ iշA"QK,J;Һug>$J3o!~&UcQ'2A}*¹&AB9'Da_f1مHQR7G!^T(M=kC#:]j|3}Ef̪Cs!u+iHs$OV(O {ktxCҟ] k-b}~,્'kL0j+ҙKv@XcqHL`8W ֍5]_j%>NrJ}흪;y}-itm%hdH|ϥ Iv6`7~347icxMpቔW7C++qExqҀQ.OȘ ּ7sʀ;`niX*|™ AdZڵNy;.z&{mh h޵ta fszֽcU7XlSab%_b*Fz.dYץLTf:, 6.f$Y(55 WL-SڅX d}jry:t4` G7j, lqJˌ]~¥c#nz {F>X8\~ԋ%-ᇯO/"b;ae\x^ǥq%ԯ+ oғCHd^,EzpZO?Q&l*pP>NLcL3w4֘S$J&n48&M96ƽ&c@;whe˽[Xyd 15j>~ci!}=ZwסΗxt.H6Nly[L}&>1 wЬrGb>,t"Dy_zhvW\=ĄLϕqҭS<ז "\3rT1UH*sGp+Su*#9@_/S-yM{\NXpǢƹsi,ч⥌wd>Q1ʉr09b'nƥ!@V;e$j aqcZ)PFF|\yg+e Uu %F̪C3Ͷ>9i==R#TFFTzc5P~[f>f̿(dmv8.Αzܫ)c߷ִObnmbw/' W}>砪VG`c ۘݾէ}?v1QKvMW9Qֱ)M__B;\l??zry~kE߄,Gxa}ZcD<ٕ=,Dc dOUׇjډY/yl;~մ=-"crs4a",p4kR{y[Pr=P; TFØҳT `TyM=,ZFX,{T< FZqyd4HSҼ}:k3xHyWCn"ҁ[ gq ܌9%Ni S#7SEʉafMI&bM!ÞbI4i5IjbxOZm EUq(WտĸaEU;g5Ikwr.w`~BNl561+nRɵ)ֱ#,{VK8RKKMfG#_ rM#^'hA "|5PgGi)_ fo-9m$#֭Ex2E`?u#>$SUiW:KZwC~䔨ۍTl|WB%pJC QkM}*#DIPJQi۬ elozug33F :)F&XG2Q؋b.l%PVAz")"|[=Ҕ~S+vHnd]|)eIʝ*!O+ۨ$/N99Wjqz"#[iv m\BSkZc_xY4R2Q⾶xfPU+wN٩#єd?o MiSm#És_v#Vɨh紹OF*H~׏ 5p?"F?(C䑾1}skaM{q#+}W_")5'] ?<5:?@>$mQF'?¨:z.]"cP}7?zh G8|5phcvw~f#ÿXN+-|ޕHGRᲑ(f !#A 1RyhvEy<\ $V 2Ҥ/${D)O܏B0z T,dx''Q[-!/dT,a ;z}Dhdz:4k{}d9:8 ifIZIP}bz51M{I4ƐMzi4I=kH=h$ihbƽcMA$<&jDs(&6 M5ZgzLiDNߥ2DɥXм@$*qՅidTRTu'S4KxzjDҰ6_p#T$ENepZG )x{b#hϯҳibBNj'F5džLJ#uh l,DqjVOS$}OQB _ *g ÜMmDչ,pO҈_Z CS5< ttsPѝ1hm=F)?q ` }3D8ot=b۰4"WG#5֕CJ͵0I[[ ܔQBuӵyODv?Z?e]v܋2vOc#EuVWyDrjb]:HV?qֆsl Њ0Etx,%ީ?7V 녖Bw'0*-?qڭY~,2vC̝)JAh\_$O$c.oRv67%ӄ^mn)>ddᗍG}Sq0ݱj:uΝtq4R j.5[+jRm3u)+afHsL;8WPjnVpȓ'^ImWV>hZ䎌S*}3K,rw\ `{AkF$5f^aP/QM+$ɡ;j[PGCJ! q^raںy[4|-6G{VqitcBJѸrV.O4m˟Q@88O3=Uw5/[Fw5s.0aa[qLUr0` ki͍ڄ\xS<3 gođq(om>U#K+'h߷mb{~#,;MM1=&)9=Fkl`_٬2e@7?;b]GWx:h"u@ɨM_GmT\KdMb׎#6%Ktcڜŵf^Ed'*¾J}=B9مಞTN Yr)<@s,wC%i ƲoчB:v+iQMFH "zVhلb2?N(G'BQ&bӱ)vSZ)$M+yB^YW悏D\3"6?SS > ,e(Aޚ|m -sK 2A$U4a[So#:ZHxZcJLpr _ln;237e?.C6?As.}=>*? ^a7E><-2O?6n=φNYLbJVT99I|IVl\ $DwR7֢ Ҿqo#Oz:Y1Z6,R ! ,oZ}LQ FZfDGo(i1u5"{^i8Vc(̚e2S+5?S*\Fq*gG]k-\'!/хETU SDo'm-ݾT,FX'sJ{Q31;@Dm֙ᛦL޼'5٠kׄי 3^fי3^gV! 6Lv*aJO?l0H1lk`g2+n9z yz;TO.)8qJ`5˵$xR@E2*d2>r*<] bDl yI"q q(`0嚖F6뚓v!33J%1~S^,$I|XrSzuJ"kb§M7*dME3#1 :rfS펙e,M2yp@;6*xҕt4J%@ T\0xKĶSrN43/VY\鋑B7l8ոcnoʹl"zT+ kbiB%o'Q=OngRj/&k~ h:淓x4C{yg; 807_wɣMibbH'8ԣ.;Cj2967E$ZUkaBl}+YִZ~IАܧ̄zϵ*^~]jo,9OЎϖl5-&Y".ÝY [=} kO V;GpNB3wkiv\큊 ue4Yrc탵(f-k6}wVGu$ù?Gf'ҋ|"ҭȕ9Q;TSۆNq= \#↰H eSa\֭Ix#pyt-ǜ%kgk=^gdFW7Sє֥Fׄ.5w%Yg@EF\q{E$͊tap5pĻKZo"9g3SZX?ddyPtᨬG5nNv͘^惵5u#r+/#| 6vڨ_an"ze%ԣ{Rz]s/?q+ECm?nƝaFziWR] eq\¤j2Ĝ򎔞e y FNxu8oR!.#Ze戎p;?c3+FH:Ӿ5ŽN+OGMF)-6}MY\6f m>x5b\F4Ldgij{Hf?UZ5HA(5Lոp9 Rj]Clwq[&Q;zWrY Hپ#ڧo.rY~%"+TFv~vh4isLf4f&'5٤!Y3^I&'j4ׄYJF>HxzI`2uv&sMčawإ1JR)*#jp E}UTqk޴ l)Q"ID]bwf k3֯{#X8PY`6ı;ڪpwy5EE#s (9yw!BDʪ#$ҹ<4*yiӳ[T Ƈ&2F3C>PBOz,"698U',ݨ]쨯5|VOjvqrFa)=M1M*ɷZG `j춸0rqL( R+8ژYkHӿIjBhը͗l|=bX% 6 c5JwH6#'B>5AqH'+ enWU۸Ry#YCdtaLLECxa˒ýK!Nhry `𘐇g$hȻy$(G受I3FPzRbXǔ|#eKaI4,2 9 \K 10Hv_MMRCO Vs,GkH+3+g~WKsh~H|_4sJ P 5q(ʓBL8EL/2ݏ5(4/G 3I)&r-\/_&tiN</ l`>[`;SxXQ`cQI:Xm+$9M?,CK5Z[yq@xu-1Zʣ ʟnƖڝ3\U|`J|Bdj: ~cxGC+h# Ovė kjg˜  ˍ)ȩ7^4X$HbP}R/1 \ax"U?U`O QAO޵At~wLgIF}.pwf7:yjrq~iLuN1=>lA;f׶Wu?m QU0|L:}NK`y 夼Z8.j'28$g8g$ha 7/yqǒF`=&7V؊ⱻ9{Yw;L4_Zeeu$h%^lՐC!uHԸDsZSOo)Zjaʉr{Нf]Y3sMHLnb1J鱡\*BqV*Tlz^y#Z"29FMm6*tj.mL{!=7˱S߱!kD-uDJeqgNyrÿ֤ڤwWb^WґCp#NCo:-,g@@ZqEş1- $QbxX~\iji{4iu |٦F97W4 bAyF]8sUFwcU5a68[68FbwEԏCdW2NHvi4f &'54jI5٠b^fMxM I%*z b)mgVUOOZ8SD:O A ̮:ji xͶGp?Zp(]#&g!ؓix%O>{zPw PoR) Ft-vJ-2K*uXȡٮ8rer9/#s( d]ny?Ȉ(cފ((((9Y(QrFYj:D\-E #j'ۑشǠ]sq qF9{L{0F7pu+ HdTu}B7)$H̼ڽz䗥ށD5y|ߺkl8&:JX@cs[kVs>PX|V6gx:J9OZ8bTxeJbNIyݡH[<tj-?mjBHKeeV%~?5h9.?+ Joh: PQM$oI#=2£́SSF"6֋^Ϋ3"8W ; URo 4oA=^qpYY%P /\T-:XlI El#H74AKH61nk pr*RH3A rv?ΰlӭXTndepZE#mTkeԒس6V *4 ' ;cҪXcT+u٣jri"HpJV6C:E4끽EHC M{JϿ1f5B NF-+.9Si,2J9 '>P6E )0UIPy@Eg()+>.n41ZXgs1^$|yA56dE72w!TT^zE0cX^]D>Te,Q+c?`]kn2[Sb$w )bØ(9nWKBՕ|(r8(1a.krk#k>RCR*"O2;J~[tuW{L%+40:uΧ p;rv~ Jn)([״i"IQ^>6 ;b*Ú>W7j"ƣ+k YjM^XgQ+3+JKkosN85qj7P:a0 vZ0HZ"%Iv8ǣv ׸ *?N7 O(9iRG}—D$@B( Iqu6rj?éo5DW,НPt֕Ě֑`cO g| "բxn]%Zm-Xl@Gj&1#c? n 2$|ؚ {ol|RLqv= 4mFJnʚxZVIe;jp׺Ar`s i^&N2Igsirx~ C de]Ηse1ńݹcε әqB>'xbv'lt1rL?ZWٿ(3@=7)힕pּRw/[KN_2 1x·*M fK}FHh}x4.~4MDu}fIĿ;u5dE^4-5O};Mw4JYؓSt00yITi6 Ro5[,q)٢v|gҧ%6P4TR=jy(Z{mWX=n$9>ڲǜFe%leEyѶ'rѲA.ǯO?l۵3s~ޗz$u4IcJaބjrd򎂥a+mP=Ns0SQ9Sޫ|Uw<6yC' 5ϑvmsz̋ sU]F+X3,QQ+<1W#s^B1$68vm,/Zyl|D܁֧M?*y07+*YDϔcSXU^6b9sMYC*l=le7F9Bӥb0w>J5-3.њBP-UؤW\0I30 :)H1P[*3Z|9=ysl,]S@'p7/0rɊWh!oE!9wԱeQtt*1- #u,Q>6&AA{VyF\kKŔUEg'z鑛v\"=5'[< e\R| _Agޥ 9L%n OLRsIDk`-.[[ZVm["9#juqW+ۥK@#ڶi.H韍Ү#A㸫v9<&_Őz/M4q=gf)-6NYFPi%qڬ\c=sp@,Xj$ Omֻգ̔_*d+febQޢ!brjM,^38qãZfxF7saXA)K_կ/c#s*x94ݥѶo OrJۋ+;ݕoZg5|>_^*aYbvX|&,S< ^3Rn(C.-cLD+uM-v^vG 7HQTcrEJCzc+t#Ϳ-o3h]~7y[ړ(Źd⨲|V#p*0fO5$w={ʲC۱]çfÁ&L0AѻG"9UGxsEZ\'ɱoz,խͼ|~>c-Jq5 4mBTl-{X$zױ oӵzU $&K$>x[?j>_pN^1~ՙȂh$"+ۋ3 Fƚ.f xZ꺵j'ff,z=k&⟉.Tf!]E)$ji$^&tk0O7\~/HGak8Vy^IX$i4%]wp++#d4I=})aJ~ד  ֕KH7<tY rCmG*Q^U5#5L5V[ tf阐1%qc1&/h k"v>7Ězչt~±~1ā-z; -˺Nԓ)%)b}oZQ| ƈ(2|Pv.ʊdQɿ.p(|V/ j_!^zϕ'HÍ;IHڢ eVziQeTDf#PqJMuՒaFCʽgTO`\/j^de68/eRI\*L<YodܾCZL.pzQ ?ѵfd O MucȴEI\(m]0 8?3/CCOv-F)rцW]i('m}و vy[gDZ#[Z!sXSBǕ`zgMy]J,$HG() wK0~]T3,ݹXWzw#>Ebp;iN*m=EJUٰ~:&oպ2?vɏ2#Tk-KרBDoz[THrҶ(ΒZ>dIVܺ+\_0C8K[~coZ}WdK{lR'I&5L]\{COqUD֏m B{'^զXjĂXXqjK%2[OFư>ӯms$VuoIPҭ nF؏g;xNIp e߯Y$DrE6$e,14sqV:_JbpU%+qr"r>{Ak<WonWNq[H1~i/ʒ7UiEe^ |?aS-"([+KK/&y %XVMmoZ#2pYv|Ed{93R]ᛟ5]c޴&ѤM3Gm#Zűo9+/ںvΣSj}+g}L#:{~nM{nW9պ&L#\+!EBI)|ni Ā`u&ȃ2fSQZݖ((1ڤA0QodJ9ȴRKe*Tkc׼Cw8~Lfq@z4ƊkAnڐӭf;w fFM gB^mg$}O"(N@;7f᭮RAVDȼZUN4MŴ?Z}zT$Ml>u>Q+L$䴄`)YQ9=hnu&1WviG. Wqe)yœb˴iʜ+l*PלIl̼67]!-<y9%NOU@9ψT3*Xli5 %ev;yJFy*vB$L*7QM~~f7N_ZnV$5Ö_,4 ?B֦K]Y48t zն(!ybIKg*.W7)ԥGCE5xl9BLSRݪdA+xzZzda(F}XmY)qé%2L&i%a~'Q]ۘN0MVTjW ;zW'sVaK-og|oV.YtN'3)s+tcYRnVOnV FѰ$8'̃QnESxMyRT{KO7) z9'[L8@)Z֡iY͛Uu{bf99Q?s5ݢ'ҩ]3q4CI,Ѽ9'LV\S]edLGTVÎ[LyW dEޑ\R1#Rh" ]$6- %T=RBHD15]ɓƣI3;V‚(&̌7;Ы' bV[[3 3?@w51Il$t+הu5 d?$wMvAO44/ԞKذIf֚DsZ'}7DʐpiutBK̉{z[. Su[u{NTSzr BcL+5.kvE&X ~iVEpGQW%F`H ul"t9PiֈHTCCSjx` z4W`ocXdѤJlFRM%yBU^׽Z9*j.d5w`,z8 ;Ј={Q aC {3`<.;(,m.}$ӯŲp=EbgoD-KEoȫ(.Kn)dI7u| y@;hAF1ϔWI1 #C(ߑr(SZbyRt:%ԩe'j4lbR<ƟӴ,2vؓA e<\PX ل>+`ÐI/d5]QI%RK^—eI6=% >RVk s9HmDm)18fϥt0M}l$`/Zr@ ಐK4!Ef#^0vFӔ䜮zgPm,eNH+.R+oqșFrPSΎpn榛609<1՚nAm<*BzB $ «q#:z9onPc18{#*r"`0vUfPTt{4*oj=m!L15hefoɉGC֫D72ա[vCqUodDgnG̗ecj]7!RHdn&zԋ.yr~aBxWjQ͑H~R+GEVV}6غd=:Wۨ)/T;*e?2Z6"Lit fj,-2#teJԖc"[ͦL#s{}*erOCԈ.;\j 0«\orv=*kCG,#7ޙ#$'ٿދ V'rPFJK ,hV$)Z%m#8Ǹ5reZ{ IOL?U" SKiV\*yB̿J9^MF%﮴T&?zi1GFLq,`T3Z.2mqf{i}۰🜂ra&c'Ԗ'3B>0ܗ QdPlsMmH_0߲ uEV ~Ƅa4 u)9ڋ[ƬRla37(Z5 @շv||9}*\1&:ަ݁[GinrFw7O.;ajIaJcpѶ}Z"}( xz{99QEjM!e8$'l#4@~k솧^Yh=늩4Vm&׵5nG##Q̶KNXKqÕ>.7 ;IkC<*gb%;aV /s?҉]hb; %p@`Pvla.cSz5 _Jpb~{R>@i(źrrk\@Tu'K` v' MpUA9ת߭8_(ɦjE ExA+\Rzwh[OGAV.G+foմeg}Ee8-:+S_JL%LCV}IdCBOTdy~I3ҫnxBO&R5<8O@Ȥۑާl֐!go>Rq' Eu' 0' |6T`yY\bYss6ڜأ*z U˪T:fKؔ9{p%=2e2NF:tV(yr0{+NFΥ7cb"p9 ".6V#WMJo >WZ|^;G$S4D@Eޤ$!E|6T4 C2m^]bC@ M֎WTx/̧pTRvIcuO#! d^)sc֦6)u#`$\zYh^V:w-&C >jd[8  OPnS)kד9ʂZU+ݍB [рnԑ:o^%qM`S# :#ɜYIv_4p %fT3h®>b|{^U t7 X0癆*(jxf.C\]2qKG/3(GcNZZ$q?\zWL'>${kɒ0Y{gљC?/2; [èKs?0Iu (z[['$䑂mwxdd7B;UZJbB(_" .2He;n4ü%_֍y~M3zUYz28 ww&iB>S=*0ܓI[:G7:d&9 yVp֜kXcQ-m+ϽhvQm=^MlP:cUc&eP $֗$j`\)GHNU-nevL&"nuԚBi{םN\ܟ0L-li%H=xQ]}m )|;܈9oV4"]rDhl2 neHCzIs#nө`h4(i$Kƛdc8NaN{TBro=iŻ0;,.&0du5(0?RDLL?Ze vm"5EdXv.xyt?ҫzݥC 1ޗBeNP K[pvaܪyyu$iW#oZO!VA!4š3kɧิ7pU9'ȫ72T^k;?7-FϤ7;V+A;GFO Cw/ցK[,\M:Ʊځ+k X/Z8k^"ޓD.ʾo29GufLcG8i_')ʜW`A#މ_"[ W~$mTa{9wf@6;BaN\1MS-;G| t:K(s 3jOB+2k;䷕UW)V?rݓU\NGվI#loXƕV1iqJ1xd*svW-=gǑh4M4Gқ3UNm>B8$}TeveE+L(=4yZ% (@qF+(ͧE|]>$ҒKie5e$ s̍UHlZHyT7+^ 6E1y`a^ޏ1kz!HR#I5 ;=KL s4~%`-zUjʈ|j)+$Yzk,ƬEa7"!pƟn]"JqDq:`=)+t79=qrTV.ἵL 6l3'Bqޏje`d\Okaabju'glldA LKu QTk(f%qoq5ܗ9{T[kŞBl7V8]\j\ROF)1j# FqGx1*6?j7ZEz?8@D %<á= >!B+,)Þ7~hj8xSr(hXHI$R9C_Z*:S ;Sl!''ҍC=X*4o[9w۵TwؤWIKfPH.Ɍ@y{i:;F&f59/CץZfrd,aL%X2:RVN6 Ҋ'qa>բm42 !vף)zjz=YngՎ᜷/EW#ӪI/&(D֗PYn"B7sQVb%󦛫_hFm>r[+K=-;ss*x)(2[pц*#ޡI6 GJoźXQ^+ۭV/8"":l7ޝ.7zdV}Z{s ]J\0Yٕ J܀~'I ޯW.s (b$c?)~X@ki̘\t&7̮01Se"eBM7Ri˜";|V;.!7s\"ej7;N4gntnc ]]2ݟ`LFj0ZI*&^-UPs6z7vW 垤jZpuO0Zi3+ȧbBzS^+O_=TD3.wTӦ'گ-cl܌30Um m+efK)w(QCT0ǜkh]6C˱47؄" `~L,?Ɗ cG4ǵV"hl4cT}=jV(IIReNXQ^]ۊ-R%zNzM\\WdxAm <˅sC-дԠi;Ga\Ⱍv[EB3(سur\rSYN7/Yv2[9rGZN;`>XŸOJw[#nݙg;+"QGig`rTi w_!ud[MrvRN1SWЮ-ъHռN\;vjV =TJ@뗔gbqI̹ zV-P3bNgޞxE^ +WK,C NZI"oO+Ʀiky=|t"m׺oI@튉~-khWnX/_zbAoJgdzz Cό*T+eZ "2֝\yl31g{K۩هj"Z|˿4!(5gڌLjt^h`*PEZ>2sI tr-տhFKM]N2،8B'QZ5 kVΕz- cTM2H(U  7@=bhdrScImn *r(gQȤgdsz{S%7j'2Zr*wڀE,Q=㥡T+,՛P`AqfU0ڙv5Q|YdJqOm~ *w5%=bІe=MB1 $*8BNN>- 4)tȒ{xL'|}G:mqEsc{}-x7KmZ}9#蠶 "du=3j,2|X[{}+ottŇb+;oFEKYSb1XOfo\YKF~X>N$қ@@,J<֐1+6cga(Jܚ$|go𦅌a#P6bͽ-$vTGY"+YYW`G5ܙ>QDAc |׭&iRy8QSNW}Eu-6%'4pNR~?s Z)c?_ (8Xc,ybQf5W89L+NUNUdTJn aNS8E (d~ j^yE!Ow&d٫w۬qh.a,c;U_f"eŸ`ycn*.t"F+:f 1\'[×)i2Z4+ԻuYJl@jN#\ .vK/u"wyYzu5HHi#lkd`1V-N睜AރIP'VфaaއۯYr=j\ п(j\V0p9\2Um݋S:eUc#\k;mD^8lQ>EQcZKMfT7b84v:oFotJt'zV(/.p"9EjY۬6$q*<,1HVZ@ m#Fb #OJ7(Q NG Rĝ(VE[I,*c*w;U()9B ԓPMG1s~q* '3ޝ&M巉5Hs5gC$D"u~-5KB!T{W-H`XyDU o[U4oU%,~N$kp,aW*ц%#z}*Jpܗ <{0a /HYn-J?Zf~|Wa@aVJ [%OsS~TpN{ 8;h#s1:V*ow RizqIKIL,!\1aJ/c0J7 2*$rVB)% w-|<N◐,avf1g9=)r}iVQuBWlK|oZ10GJDnQ?j2|A (dTbňdqVQ ~rSGsHhM/7fDgޏ /}O slq֟ EpaڙH֗iwc֧)$FzѝZ+cu4"g F @?[R|iqAI3K1)fMB=Ī@r8nZ=ib@hqWF5=jꙨ|7JJEo+F&[G?f/8L*/4V8/0=~qH#~{m_VV7-rȨRkGʼ^'}5s=\iJ9銃'pdes>r E 85]|4G+ IN߮hC*nP>y6qPHdsڐr75ۜ?֫ xΖ?gvyynPEØy3UweLVALvB.-&I1c"&ܓa;],]FPth@[e Rq,@b1 O߯oOZ+ert[~)k|̗X=1ԓ Wv%0Ƴ =E\6PՍMH\d~SKی$S,jA\*H>nki6'FֶgEmYlnQb'+!#P8sF!8˲7n 9 DŽgSj;}ԅ[Lo?WM"e1mWTY:cM>于N3/X(BI)=s&HcMFɜeH)wO[nPnW֢\8]G$Lp@qdI(F4}&PԠNy$8tz=qÍu˺l]NC<.d3ˌUmt.5+mZq/njrҕo;ZmVW:Tm'b`W 0mYRq֥9 O)G-B!HT/zL$k@cPd ۖ[5h#KqMIP\rsdr9VzH[b5) !҇h%fyvɧz&O /1My<\rI9NT @״3;6H^6m +dEf{RЅK54yüKm$pfL49 SPaۥL,U-ȼ銑lɖ1Q"CjPs!iɺtg~t7dnٿ2TL%:ӑݛyDɦ"4{WYIPR2+-+QbznpΉrflgI./'o^QHG?1N2X ƼNw$jZ|{ WLP:Ilg5+ ˎ:yT<&I$:S#NuG`:RpTj"2vHh8{v$c@Nd y!U9'9yXZOm2Y alhǯB(L~&/_d^;2DefJyF0J1J\Aڢ0@rA*9GvpNaQEcz5v^J*lO\DaVVڴShƜKX2t DAL*Ӧ|n֭ג{;Y㣸 XcV #FT),uԞe"+y0jpK ظwq mMQ֧t+5B :\4dsE)W֗]}?iCdꑂpU(p]luT1 1_(F)^>[yyUE/m򯉯ѡw8kmWe H{ =z/15t4zߏt+)hM3A6ZZkoZ /,F,X-Q iFsIRwѻ,-dv8Gm&ܧu,C&h%ԫTiHQQU@ ՏhwmV<@hW1H7Qԫ)8EqO!Ac#}fYjreV=1*x҄x#@$iv]eW1USK_kv8S\1ZhWpAf4 "LjS?^_ uy,=wh¢d{mϝ%~63iheͅ'"TaE}; &H#bEW3B#+u/ڝkLGG||qlX OP$c\}OP'6 ia77)r3t _i ُToVB׋o0SØÔjuރmx9_Bq{UxWWtœ 9Ҧ{CN,\yUۚ _xpw8g)CbZӦy%`I5ȶyG$;TxUWWbP'hKrLUve9ȩ}#JrhL:bmIZ"EpX/~ֺeTrWr:ޘ -r(qh\LZ<ɜ_cDl|[7+ [L)VKrǢVUFKǨw_P1=j7o *OdqTAV&YTuMV^E"GnrH N>HԵ+]\hBe2a)(SQ'o ;q *j.2͂@(l Cy;G0pI<ҝ2r!Eƚ՗ /֠h27=5i[F|7f^mqgu [͏ZWL%]3 $jN]2HGjf(6=zk> Ug %; 1e(H=Fij:E2zM֒+@#01?Vk97\Hћr4WU\7V]d} S5n͟X. :͹,oQdǢDRhI02ޠ7ޒl 6wGOҐᢐ2E"ڄ><9QD72ڮVh7ocNɚ].m]G>SԻ%FEhe$9A\^F81p$-/KZ|U6h1psOyd$ a*ER<߽)mQQNѠK22;zWsSﺓڇXN a 2n|`W+UNՖ-i9ZLQ;!KTPt>LJv rzfj!56=B9 HtN>6-L n֝hTڢIl Z,%)k.%vB}.=z}1UWbd&e~Dݿhʓy>l)d GՀzbC/ RȾ9m:$ T[+ 35wLq43"oqT-}҄⎡:>o(Ð5eb@7vWvS&hA^Zgi:I@%kms\E/#O2 v蒕AQnKEլ:tHi^%*\}s,6#7rƇ1CbO2TMRi 7t;6w!q:eZOhJ ;G7HpGOS+uG" _ӬXreygJIb6T4Re R{6W॓5υwjFUdĀPBC[8d'W#%tϡץ^okf#Խ?Qԩ^d`Couuȵk8CR#gj>3w0k_H1`CjiZ\לeg;guu;O>G#mEx _xg0e! tWNW\ C}E5n" 4zg8חFyѡ`U-2艮> }i**ˮJT.XygY\:{ۉV(FRB.p*[473\B\oWk]oY<:NڟS1 s d N1=l>-2D{04ʼ䍆GJJamoRGG&S|Yg~peI.o!ҊZ[F42yF+9lIv>!cnդ1LވZs(H5JL#-Q9m`Fvq@Sy攰WAEfڃb&Z@ڝgc'*㢞΀QK8WEfQS鶳dm*x",ab֕r偫c!4K}H-)qfZ1!I?”3Jj0I@q ﰦdJm'Ir 3C[{nY Ѿ M6|)N7hFے٢S \KcқO sQsDn ^6#aІǁ~1 mn?Q}J?>S)Gegrjnjڕ* 8TPYJ+`=ZiKK4둎M']ۘO+Scȩ|{cqHK(3a{Bvc^=pL\w]cEUl]f [F Sq%tKp rs/n(_^#HFyXT^'Jh'͊jF):L=M*;Ŷ߉ 2TdU?M ;=ݴl hj]=崋0?ޡ>FE<ކ!xl \ø>@шڞ<)FV"y rEuly^d>jVWk6F'eQ<󬋳ZI} R rT1BfmS:uY3֢dPp:y ~)ccf?JvI`-+Ys O6f<^F|lm"P {UJ(H\9-͜v>5,aub#nY`zͥ^+~zgbѠ꺿"񊭷'Uz'Ӄr k Z*ٞYkqlT;oR_Z@jvF9#䘶VL j]FVF=vѷ LOJ5FR3\rbcVŸGL@X7$Q=-5!pJgcREm5[%? 7JJ2e[jm\[KMrrl SM]Cκ-~2,0όdc4@1A*d**r VH<ҭI.i˜~[cφ[<и^b\~| MhY9G)GZ* ʹ~wQJDq5j7F.~9CM떐xNבQ 5ˇu[/%1q%InߝdƖ]VeU5\-*$6f*S Y%kdE \ L㎴#E~+NLF9,}:Қ FMW[( GAڈfy^r,N WE9iQͦlx8nfy. NSU.R6b7:%h9511N՛W0kS7i1ho9׽1ksx#1%*j|P7YP +25Khܙl'[aʙ| 例9f,JٹUTpܞ|2ӵ(}JmgxnJ}[-ci^5q)6ٴ"껊upj>nnNSӺds:]ދ׷@c߭&E&5[$6lU+nWH29tއ|+ 6>'ZՓL$re³qwE^#k1s1)'kYfSr)qOaɞͱЈ.o%KȷM=N54>KJ(:oO[H(`J4Kw_*,ʪr9Oz puR`8m~^w$U(.Tϑ09Bt5cW7#zzv|}4PN”1اb]srOAN w(oG*w928szT -^6*q{;~ds+E6\oav0Q(yQ[aPHd=zTw<@eJ}h@M1Y jQR툤i0NsT(7*dl(+ڙ7! m֕x@&Z%2(`R-ϗt@aOlau[젒2\P =ꖱ6ls>iK!H]=u Az%hGK63 m1Ҁw"6${E4+kH́N2WayD$ Ŋm8O9JFlmֱmï z &$,Ezi8qj3efٰ*ןӘ?QUN2i: soqC%|6{W_[eTYDjBG)}iA[Hɘ~[Ty{i)N7X62iW s۹L cSNzh7^ %N;Ԙ8liW5St(=M 4!(2WUnPQt&ׂ}A|)@M rO}"V9eBEL{ IcFIߦ*घ='vk{/$S57~t 0ysf 1sMNPq._YP֮*0'8'j78vA\1DA84=q Zk/rS6wQS֒p6wU!ԺgRҤY&ccm̌>qWORmRm0ǦsHo$6- )w5w2>frzڪ(2[};jtsAxrY$n]Bk(JС?¦MhpNj` S\RCĶU9c8V\'w@kq靸j浧xr2csQUWee1VI,LKڠj/6`qV|08zu$nTu_%d_{aЎZN|\|%n&U[_.s"HXN69GQ@c!pAF.4@ÞwOQg{SD9%C(%x@..W*9ޣξSQAgE|YRkckEh<{cٗ+ר>OLg;N 7b(|sOmw`N6M$+) 0=+Jcvo#ȭu__V0kWdsҷ6Y9^Nq\kG5=JY 7Ƴp99'xzS&+m1mP3!|lPӑqM;|N0100f͠bF  ! 2005:06:03 00:20:492005:06:03 00:20:49< H  rSi46001231143456k C7PrintIM0100^R980100(7fHH          x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Nʧpj2n]3_9[c-ArG@I'%n滻H՞X|嘏@IJ\W_y4#v8܏5=?/ū? ! E[D|fy°$g-=/'7G6P|NpGq=60XFg޽㏂/ktk%T[q8lݸ9njWݶ/Ô)QU}׵I^ⅳ--y#cD֧Lj+"]w'q [xRH gwC;+_Pog<ɼjŶU/"$Py\E}y9:=^}CMLUVZKbH8sY{VvZzK weZ4r[lVZMv WRڇ(.gVAI}`z\ׂ-c"t2JҼ5YSz .ך:6OC<5%zWJt6񵴽^X 9!. ϾFjirh \s@k=?zwǼm^|+Gyc'm_s{G<J;w$cn}2 `}+ʫm-uH/?T-Ŷz~j&/Taˡ2dtB9 lZfCIյEZ$:۰ϐX~$099U/{dhzw(ll<,'\| ɕIyr #'ʊSu[H\Vnwtewk H#ÿğ4vt1w|rw=)=!8?ᯈ<E]QhG{ȴӳ;'g5uo߳uT"$+dWLu'@UΗ{JU:]ۖ)[]y\7|/|g㏆O}[[H/*XTՆڠOyrJl`ur_V}_jv䆞KbUsgok8lDpmCXua`AGfs?<_x?QY4@yZo1>l|ƻp*4՚C+?mVPq|:/,+c3A+9w G8Ίqө>65wO&W!DlG1܈2ki9_4{tݔm%_#u.nICɷ d$0< `q߅R3Uzal?##qV4&?S׵^z~gic ތokqsoe kdGv׽|Jdh=vPs0F⠞G bE{zY?լ>h/z叕w=⬘$^3虌޹yai8LfkL?fHp>[4.$npY7ڥbGg&iWK;(\IX|08zS|/ ljlQyc(@_n9cXME1o_/c[⯇nm [;-Ջ+yb^ wwzV4WvxLKnځmی_<)nZ%+Jk-s-u~dۿ=UJrsq#5MᯎcnQϙs}5͋Mq8M^><cr=Mr+?íL\=DRvڃA?VV7ړY FR֮Di \.q|pߕ`{PIi9=?a Gۧ/g@6N}iqosh+bS3|d f#iE>epO&mjJ_SX/ڏڔ:>e)1\u5<_z-iD<+e|Mx䶺z{3s̓ӑ+ Lm\J& 8pG+mJ4mZ_zgv>N⾽5*SsY6/"s_-kѮ~ў9Kly, (;w9 dUkj Z-+z9v UNfKnwjֶ Yý̍#3FUBT`$o|gC +F< lcLMʟ~+c9pRI%.Tn4oſAv,Ė (Op(rWg,kQvt<봷K{g:\cM|_ǭ @k_|[~^qyg\E>C3jxznҗ1X;f.,L\Hs5D\OGKtf5.4v236uQZq9?CAmQ5˛QX`) n?LIvB BqgYTZ.br:-f-bRxD]ŕ$,I R ׇ䲆苛۱Xl[r8^i(rФ>_i*uq#c}3yТazխˆxi^$3Ao  edž7G^g.$g?m3;O8;F99s]q֥SGd6^u~r>YOR+9d]Z( m_*ۗ9@Tuw} QXa?YSpK5'> }ktͫ.y !~/|3[h2ݭxl-g;smGBb uI2Iϖz7Ä{JmMimuGb!iȏ*=TkY97vaKݖrC(v‘!^q%~~Ӿ8A}Fpx๙d(/,u $ "0G~Ey>˗N/Du 7}~&od^gk7xE/Ͽ5ۛiff1]BoeIV|g}g1PG)qnE`dI7AYY+?)I~q5"ӞE <Δ-UFE9,Oӡ8_iuk7zE:kmovTUgfs˰\δ۲wb˞Rmbޭ3jѾ4:r[ Q ;[bE%IbT3g hxo ї\i`Bp^IKIa/ #n ;ܱwۥ،JJKG-u3~(9|M=BFn#UIKF U!3O >(cDl XK 5Z60d4Cvs[]OAΝi41a1$P2ɍy+uT U/W ?f jڱ2bV Pv7boi\]yh<O)䱈,lGb`)@'źn>I'Vݴo?G[QdMo–f[xOYAa`? >5_كƳj#PKV`*3T9`-h)_ׯϯ`VOi%= ٟKMN wFm n ed$?fF: h1x6'g4mҍw=~Unݿz7MT]_|2a~WWƿ5?xhv| ݞp],s=h]}Aa[e9Ym#g` FN4URJYmsIRV_Y.{ܬ^M̷2q 6eQB<O5۟^5Ж簸\|)^=~jM#p$#&g͚w)hzĒis=O4L jX߆'@J+C> >h-VZm+3oYʗ#N{А/.pGA?ozB'*C<20;` yJZlU<5jF,I:(=ð#4դ[mxW9o u_DJ [ǶmфHFY{^GOS<NU)%%|OM/FuN[xm/vk1z9o}GRkx+;]_۝NL bk{K;|?k QԿEMG_~oձ~)⏊nVsZ\4YqqlFv`qs_?ïcVmo L3?t:qQg7-8ڋI??|NgV%ᛉ-lo5{v(;yP o4;gxO1l4(]$ah2rzVӿ,iW?VN4uU-lNe]4#T5ݔJ#jo$vO u$x澝/WGm ZO:q!+[<_-̱̇VSX0Ñ_Yo$m?ťswGlkk8;V+{ub?35j.״x*vƷv6W:n1C_YۨxŚs:w.eUQ/}(#˯hJ׼+K<67:=RmĆrwk5F_Z^p<3x}j=ͻ7+} 7G5ѫj%Ό߃-7FV%mAl!'a[9L Gq]{X]z%mt]j6\SGS qtZhOcwvM++HtmaSI][by'+8+fZ5nbpĒۼ׏0e@ImR6?}C/_hM%o%ʎKfy$[np9fbĮ+/ !״=KFuglb`)"}'9IJ _݋Zqξ+nnI>/6Uy$,_1q֡9S_ ._*?ښ,@{ɮY,tO,j娮vmVxf+4V6}GeXIJtl ?ey$dٳyǀ>ŏYY_ /}M3gM?KT4}}W_šo{giφgRY?P;bb|w o tKS:772ychU$ jI#qpM 6Kk멦r}= Aֽ ?j^75:HV@xKt:҇ug~J2rt^:~>/.og+j[ ^gW{[kONp93\O}RFѮTx˳|j WZ֜%Xh՗pNR?&xx{%E[7oC$ BKo+HF5QοzĸfI#~E6sişm|{S} ˖A‚I<EuP\JQ&Bsݷy^oo\]FiRY'5*#2 &-"&k,BǤ-^Xg+zz UU/iXKbhycӏ¾u|`G5-Ѵ +t+9匴&XԑmN^q"0Kߟ~W|햪c.`g{b@9$s 4+WLԞ݌"s$du/8:VVk<?k?6? |OYw0K61ڤˎS˖=d?ߎ爯t^O]FHܬ0DfbبsXƢ'vkvzyMh+oNw%/"*P} ##WS/=/z].ܯ3JݝcSMsez3Åv?ɟ&ZOo^MyzBH92-~tsſSZJNЧ3=@KI !(b)j%F}"cI%{<7nxH959V"[+;@fy.|Ǒ].iI*y' kkus7*XFj9Q"7 Q*9>sT3Ż N heYؑMG'z~FwYյ_<#B6N 2`qJM?vs~SaCNCو--`c,xk3@|xOo_iIZ˩]izR",K%`vgh#*+ܣ}׀~O 4_6-;UɵQe$1V`x~6f֚nuk%dI[r Ȭ"%#1" !aWwjc|2 SY7tv%b{ۈF9Nck?g/MŶ,7ͮiGWUҭx/O1iW-5qM7eЈIqxGԣ xidբ$s + /:llƢE #> uwF㜉$v^ q8N"R÷~OmSVӞZP٬xn𿇬.ΟsI纻|E×('^9yod_ܬe"%[I0y|`[/ɰ6h bኩŽpkK A{bai,#$yھ!*lb߽?F-JWo>u-_GW᫒8{raL9!I+^TUKWt֭-vOY,]Ŝ]0KwɅ@%!} " G'3m6/urI>‡n|l=sofh^ƚ}鳽ؤ"c`YIFzR[] g'+||#i N2 yui<7zMѝohM.G ASjgW\<=.~Grn@؞ 0V-Ԍ],@cSw"*4~ Gq$Z|Q]|?m :<#.R{v=6Jcl1 ~7xΈz] lh0YV6]=qh'3Qk}ћ|6MKB;n4eBReKgqWӾ*cIsOOJO]>Guqq$&dA H֬oH㷒Y˘T/11&LǓt?ߵ^O|uvC[%Ot>| ,&X#aq0R&7W{8mNC!ݾ*>vטM 卤01g(^rI+KඞxVҋe!Lgu,ɷ_QQu#XTnI&֬NYNhMCc"HKDxo6bnf]H- yMD:Zo2g?$RWԭv%Gp]: N'u֝ґ[ӖBչ;nawU[+@^#sw'iѨIZTNsR:o NZynl\cy)֛*(GQD',Yוg\q ϘzԮ@[eSM d n Swqy rCcw' \Z\+ ,qƻq<̜XFXXɉ?%m wÔ1= r~' 3Em$'Pr;$c뺏CT_ҌgzX d3zKX՚[KF-7QO9'gj%v1X|&oԮZ{?Tf~"9dp9ҺgE.X69i'({1a%_u(crE j͂4:EI$C dv'$dk+#&dUqNOƈW<=)p1Q%u$;QhE C?P@ڹ|4xVWMshvqyo-ekmH8<]3.Jȝh3ʥD=WӷaHlU'Э9?QW>e;6hG0^33kvu:T(=(֏'Yp<xe}k-3iC%f(vP݊UqfG,p jOFƫp_1ʊM>hX3>^}GK9'xƿ{2ĚºJյ(;'juUo %|AM#I&Xn2cfJƗ V8l}Ϛox=1ŵpyFOS-XPN39ȫqc8-AXi%ܫ3If>֚Mh+w99Ɲ !#Ƃ~z>6w#F4V.rlyj|hWay!%p$AfkxZ&E?֍j4Q}օ 0sdw4z3t#ݧ;w {}3A:WCk 8yNq @>tsOQo%wCG`!"5-&Nةph ?4kr3f;/ >5B]/ǏZwsLA!GмnNIE FIǯˏνA:F6ȇhνSQ(})X1}5Bq]Pp +Bcm< rGq#Q, +*Pl'K,tXd ­x1:: [!d85˳p02Ojѡd,r ƣ`Iˆ+Ot‚SʤQ|{׸|+F"1$+Z9A`;\SiF˽R9`"]%\8)^M"";;GT54Qec bJlƄVhJ%v\O4=rV`˃&dLP{p6g_™ 'HԵjm''y0cB}1?M%apH9ϙfhʌ.NxyQA!wuD.p y<%`* hϏAE/>\:ܺ-sYFZ688i"G*n" <\x3Edxѣ>6J>|TNůKXau'#5fG6qp#9e$|-RtdAesߟdFN,/ ]^[%܀O?:G ;c>5%"r<⟡TLH ThQP4tōKXQ\#3*y02=4 3FYARw`=r# ! Τ[CݗoƤNFBL71qIG@N)zF}n0?ȪZY5Ǻ2s;yվiWq' \9*ka8=%FJ͟C5.Ţ|:|kI֬`!͹=ژH7#<*"DI'Vs,I2EkD4HRA|Zc|):*s||)>"䐥G?J*;d<'0*/=袴U$6K#֪ڌpXv,jVIVK(mvMg4Prq[ۦd]MȄ@AU#B*{ǻjE#UOצҵyKDd dizbx;O h.ح[tž)M:eRL*=?x* Y:Ig4,A ܀P$ey}c>Hԭ@Dy[IKЧc_I@&Bw21Czc$D$;ԓmv\D |'LO<D2GcP> `OsM!u_j)2>I(` Ϸ– w]#n# {`⇼%@Rsy30"eٔ'sPb"|H8";BN}8uuVp_>uuў[43g} 4Rh_wdM2X*58G:Aq=w ߏ \~ڐL#@=ӌzsI ;OB\vnH'< mF(gf~CγjڔP3Js5dz\[Ġı`߼ާH7hXt!Yh* *s:yJUw{m`,3Urn\o^8n*=,P%o(`ET֚%;o/2)۩#ms#,ۛo!xCeօ)Þ>7Q=qdFZ|06ת4PN;`^ihݟvOtD3c5oWJ6KJ&8vsl( (OjQ$.pBΦjqSHH~p~F5cz-Vu t'Ѷۨb5ded+'!s r׫Qvy8#ԭCaIY-m_¡/'UKL;۹bH8$Bqѝ*TG}e00p OVn/#b:?[-#6ХOjz̅|YdɺC7U!-E#,GTi\ͤC q4 *Ga`R %}y(l`da(4?ưFF^o069k5bГV[. *%շEoA!C,xV'5 mbT@;~lFsl,N7!*5i;ĉ8%1UoY# , bJyʑ,ch;w.InQm%,n0]y%'+&^_:1ӓ5g?ʈʇ{L+#,RuhrVwG>4?Ni8XR#=5Vn Zo p|tT,'׶)!-c 6O)Z ]X/3cHbI8?qY_ji!{ ._e]PiJ6?y| Gn˭`;I @81dg_REsko# #0 lISB2D\utLi n23Pj|w BsBڕ6vSLᲧ8{K-m!0Fqg=OMPXܱ9}ŭ)+(-1ާةMw@ʜczt @nU8(r8 sMu Սa>VEugZE?n쫑p*{wXI3ds #˚um/=/NDI۟ڝd%WV8A.+rcf[i'e2)<$o+m>ĒY b4ivqXyt$e.Ѧ;m@HY*cq,!$P[i\Xx,+/9Ӗ29"Vkd(scTVʾi=0Y ևY6%E5C.mSqetL<qS:jynZ0dT e\Q.}Z@{|HDZ]: rª}CU@<ՕKl(B'ZqN;U$y^"3 }SW7vN\p$VpI9&#YC[.c0X ԍ"FC`r*(Ymf7 1\'ʝ`F8P|)i7(uK(e\*(,Fp1j+ (lp8[n60Nu{`ϹcEFهw\ :\We?* ZI I@Sx6ʣ ߝ'ų?CC4:2H{Q=2'X}rhB9c3**8^WS,$ |io!jE [ F]s95=. 쁟DvS9TQ; 6cߦsW:3t$PZ)S[I:$fD=S4Vs zͫQ}5v]ij.ڡ!@d{T.鳧Ggr?<:-G"v>:v!pj;-˦ƶrH@%Pu{w>4B-%+D9's6 m"8? vc]"HZ+3GcO®.HpyUsF_h6bAV1G q%w$',U Gcz$2[#֪=Rn1{*_]ܣGeD1ԲҬVW>5J:)jΥY7҆NNKw4 O48雡yd;]y15R2ӈ*wI'4Hdr j|Xc_np]x9·-R&Z|0ś"QȢ%%x=W>WP2n0% ~gQu#gE"y҆u>tӴU;DYW&fx{v.sI5џ4s%j`@fiF۔pFA4Nmn; []D7,' 9v?y>`Ǘjm.+"9' q"\%Es? jw7w `dcϽ :YC9?tyJ%Ʌp+uyHuG63.`Vmm_+dx\1kYO^b&+ib%+IM7q8ųH[{Π\7q5RlyVbxHS)vCT]ҏ="c2$sT3V[u=D3m6X'8jfwXIi_iIf5g{9|l3Kh,Agi=o^͍ڞGT΃&n@?rO@,3(X擄#E|UHT/[3.gR۰-+Uv!'^~BF{=%M(ĘT: >Hdzzk vR~ T>FXi݃),X+cޚ۠A'VPe+,Q/n)h[2֋UrL=k w0\1u1*֌02軘 Vʭt mJOsGCb/,< jKixQݏi.aRIN>5Mo69S~5yXCÊ4ڰAK.&c-矟SmTBEaК"HXԐǚcoڡ4}&F"t]>DS[\[=9D`I"hM^5R{B yZ'fN@};1݅ kiWZ-S鯵7{yV{&z0rG:Awy֪Ua`$Ќh6pW!m#4j V8^'R sY%=[Yrz豬6^YtlCN6$4Uv,|m(z.ZǕQ/oK$=⣂N{T)< G `3q@W Ks e9]j1Ke<8;l3,HsP-[cgYdr~`b$Լ3w A.&cٟ!ڐ#!e?tz1/xE|0Aev ݕSGNy N{"$Ayi V/>+4,~_\=ܽ^Ym$Nިfe<I*lTSA$i6}|*gLx~@.'TPcAٞrs#δ#ʳ=އiFtXR2@J.nK#g!@f)OpIFߦ(hd諍NR;x39j}jko1G+u./  矇zv-PƶbF?Vt ěUTq>)R1t#z΢ŭSŞUj,'wRőB*Vi  GY[dw(r8\x㾻w,;Tn c`L}6  6F!R8{;k빊CE' Tq@ms YA{f8=Ɲ"@0aRҴ!6l@i l)Cir>t;O\7$*L-m2*(>UD"|ls*{Ǧ (#)ZP2)hx\)#Ȯ?Z s1*1U9 @(koSRXhq3-!2 ;}A-l-me[ddg8QA d j cvuK72P<ˁC]ݱ댂 n7^ӭ{D lB P8)tL>$~U'@;>{lT(\UkTݣ|sUPd|9U=5Ur閫EYVH -iimީ3ĸm~7乹o"T3&6oa֡Owenm k{ˎ? OE$[ ncvs*9Zjdee8)8m/&;Y* ֛r]tdl܌px'ZɪTױkґ,N>Y::į3ws֞6*3@I,AwcZ_Z;OX8zJ&ֵiO>]<{bŋJ!YynJ's=B0E>dS灊1 f tә&L7o7p䈍d)t8ɐF~r\ҮWhlvR.= -ΤO o{'[F2V=b-C^𯭏o?' :ZWY]&>(,@(˰j u.8Nhʩy''BhzN1!ڋ$fr>)OZ]MI\:\X/N)/h mr1(o$w!g*YI[DvKtрp95o!Q[~xYp0c< 3EuOL+z2pJ;#$p 7|U7v4iEIL2AH$X'PsޖOȮh3-aJ#uq\'X3(XUr[y.}٣$~jgXa_nX|Gwpv'{S(ѰU@<T{hUmH<(ʟ8AbB.Dgv8f[,ݨG((;[mGQ,6ʹ%qp>DU[UhuKx;AouIaq\' RQ&$j#q,QRT`>9J(M{AHVpn;s@uRaLs2w<,^##^|3"uq\xb{ f[Ar(k@ײ 2;Ƒ&oSr~DDwIOlp}{ӐOA:I]}(Tx+-̾_*T&$ѧt^Y4̠xQ%[mC\Ti@lI ZGEZyl.Տ {*whoƄ&?Fǁ^c +HH>uV%XQAJh7C#j"N>ۣn{L0?NKƿ\~2ɧ7e=Gv7OIfb;j,^Z`V{wP!M`{{zT+gdmwZx2 g'hNHXˏ ;>V;iLe  HNvT.}:z/ٯsJA\pr~✕^QsɨK31<d{}"߾'H@91 T#*1)aŮUgXp1GC*s#nq? VXt5e&+|s?R%/mxԁS0K$:[bwկ ʧ4r:FW+LEO fcy҉zG9m|nv@ %;N~U}tpOŠz,[$ܶY@Ty5F[֏-jA?ңti5),aK*T=R4c&_y@I8ae:or`ՖNG@9tDpy$cjiW#Gi@1Y.ZfC,-^p}G4(\ܤ }$yPm|![oi"[ I,#1Nr;L}*64s8Q 3{[AEo>luXmV']wp!YbYmD #(#Zz~ĪWr$(j6 0 fv >Zy$bǟ2sZrMR ]G"Pr?^/pmm#vYHKM{/Ma$jPd1@ CVu99x|, 'P'i=۳&\HY+ʷ|i C) =jLK>IJT`AD\#'y y ɚ)AR{c:l)jm dQXE0+!vNO8$y8UMc$k*fD8ǯʁ=3kJii$㚺tH MhFOl.x==A8lʯP` {UMB+Kk m'>⪃5f6}?ZvvrsN2QbjA$qFх҈ W6EsֳQI=<ێya8y4B,cҐb=i=eaΩ[,(XۚNv90 b 0W%Jj <2}вG>R夒d9T3丂-';AzxV) TJl$rso!ʛ|:62Ms|"OTFfeA${s*9* Nۻ.4qTY<Ec. '>_Bޡo r; TUH>UUtolycԪd83KV5"rgJ28Wq8Vw"#B 2H⨩X1!`dnVBwWl6sʹ;97dwxi#`7$=2Fv<`|u *𿀦VG#w*Ƞďl?gJ}ȡ#Rqǭ"{+߱KX1كٲ}qS~<"'6Lj1[eW8n]}h[b[5KJVh7X .2y''98ïzN q$;w]‘#u]NcI¬TH~-1I$WQ:ǻ|uK6ղؾ4RU"?w[3]$w0o! }Ic5QKl"i47w,l,6d {Ft^)nѿ2}ˑA'5䚞ܵc;g8<>]{j U9cg|*wQcÞGµ)6Aslnt6:lS"G?CHg?f]Gzx$Q--dz27R1WsY/E=138[HRR ;9"$ѐ Y!viXt&C]L1?KΥ<"?\j?xMg c| cϝ$Jʩz!JgH$T8W0nG7u2^N1>i|0@Jy# Y\y1W:S̾**0ۛJ4}h4슰¤p35Lٞ\Ko)ITz:6'TV]x&=v˵QfTвX[uw/ ؏Ÿ0@[*6 / =V<(96v0%Xht"E_Ew(H$ݶЛ#IJ[nǕJI qi[0hEcSN84̙P4H坰sF-ne $^\zʹ}PFQ@TD6|~fYdHè9Q\#kHeIO^E*? S]&fL09]Y}ϛ!jqC F‚L.{vINLlʧsLU8P#,}jBfRUp2@(!"ܤQ4}.&*L3+eb\wumIkwZnɈ_m:O%Gq(Tt(2@8)& #,)㷝_2ۏ'cǠesRbpTP4Ԝwפ'[=O|h+?0l*ѭ]sUq9|aM'ɼq?Z,AG$f#x' y55Ơ >m0t\1Ȟ* (4>VQ&|]FI*y+rBetW'5\ t#!@=njࣨ9 |ieX`»N0y/aʟ,SO۩eNH+G8լd(P(ƪ[[k& mfw8|*-Pn\pxaP/.m^'V{"_WM8eWh 3QEnF~Xe 6Wn&i8nk[?6bUHD-t褖RYUvie%U1JMOO5cLsT#$g2xcqف۸g:̃8?QѷvBBmm?#VGr4+ &IBޠwڣK,VpP $©W=d#4L|2J}އr; '>?S{Lxu=aE  3d|5u,\ͦFq5m'^_ASI''L PA"[,ϧ4*'7^T~ֻҢZōIN<43+ao.X4}р0(ߡI;hTkɩğ҇^- 5GBRZE6ֵhX`qޛ\&t Nˊhy4|Ьj51B !jd'7?Zq˷Ӥ+dVo'̟ (-ݍn5iഷaQm:YJ`.Z{(p".?du_?~ 7Rc?kOM^WhG*e%ѥ=;o%!scQ-!^HEuF+9l嵎Yl_#G_vM jOSlw _MBy݌S\RطψI3җ*0FTPMbog8sL]${<۞GTΗ\2 I#$|E4*%c`ʪVrC$0`P%$PT{x댩h.7Mi4~2{'R9ϙS ]y<Ťq`GjrL 1#K۝@r{kwmu8"7i3+0||E-zb0rc#zMq$v{PA6<34kVHͼ VSyUV6rb>|bm#)`7ĂUq-r@;2sRBv8j)YoŤ5T7#=+Zx 3U\M[b$JMś|3HkK&vb2Oe2`r&lΣm7cF>D4o~iBP"wթ'I)ͳ$S$٨ O`Mi7"H9 cuRtYHYՐZYl0#0ZMv XFe^'?DCg;’4;$"!t`Cd jXqz֑BBg'Li4J$1EmUub/$B=&[!?ёt%bX?LN=ҤنY~UpmWz kx0TOoO,rNwA5|#pxQ;@BU۰zWF|ΧhHxYg`#ԯtmMJ+ƁFV>p A/A<Jǥ  V~4M32=^mXEog5T>'^Z7x9M,yFӤ+D<+~⃻ TΓїt泈: d+|1ZJNWiSp yPdSyuyA&!3Uڡnx3PiݎW bw?OIm>(b; $y qާ9]ȣbI-M.vj|^&e!WN?ECB14ZtB\ $EChǑ T"ls4V};H\h0)#dϧI jҢqngz%h=33ܘU2F ]c/!Q;@}>GҬpF3j짝&2~j?ARWX¦'-v<  oifNl)XO/{ZhiL͓)2sMiq2kdO`_F{S`:OhǺgPT9e'"Y=bFcˁS.TiB j2IJ::uȮNTRt=]N)+W-h=("<<Ĺ61'404փH'N ljwl!愜ZWl 5G׼꓌4SVyI)Y/ҭؙ%GUNh;vSP-E4u@6';~5tY)?i"ve#T٢hddu!Zx4e@1pk*:}챩ž٦0 M*y1RF~W;I^v&9Q?ҤDY` ޟ fEcP;oM 7Fןz֔f yH+X5>Omf]7>=qUfhxH959/n`;GaCEzJ[c \`)\y ]c1Ð@Ѿk8"4r39?spd(EM'TKXf =%UMkȆH79#<0ݹ3/h%*~=_#Ҭb$vn/=?wo] U$ajihŮC&p\1A zgtRfQ#0q_S-z8i zT#5&8>3q(bC(#jD#KA_>T̄RHWl.G|QP9u1!Xı]*9<՟H,4#bX3\2(0X7J{">M+ ;kQTwKS. a Dm,y&`1MwR@z]]t| #P2yEN9 lҴˊ-<*r>F`1ɏϚg`7uOƂASI`nku2{SJS69cҒ!ٜ?4{MpOEd ׊7d[wvFBA\s6C=V s fV7dcg`e'6Ьʼn*RtR/+F2H,?Ҳ'$`^^ t;g-d|5BWyt{H8k9c6SI4Pǿ`262}0(ŷgh&4Ο5׎ >Ѥup$WZT5I L[Oëь?*JCI3mE{;[Q&ѻZ4>Kealfw &>:/ `k5#fHڧSfuv~_؆Ӻױ]_!%G\|c60u'f tᆙ?#KJ-#R'rQ8-/?$Ȓ *Tnߍd+ӎA"oJ4|gq4.+ O;bȥ ;%IR9&o{.Fu @J~tC,%2>URWbv6Z5 x8Z2F$FIa X?ZիTơɵ<7L` }VqG H R޿tmQKLEl,Nv VuΧs#Zi;eω&>;悄ie$u>PqrJ dg|MU"/-cXw!8{T6]"7 !,'o<5\6vzGoVc]![ն]4͝}d'dW;,Žv=TǝfRZYiw%<!'go7+Ȋ"I8 ~-UZuBA5d`*n{\X%cq8WDN;ltHh}Ƌ3Z{?!S'/DݵL^IjP;B¢*jֻ ET@驤T-w+ۂkFSrC̚M'F, !yf'g6Q+M~ 3|1Uۛh/^ 8ں +dk6o ^LǺԟJ/H~_ɮin|ӍZ,`B@>qL_t^$P}2v[EMsUtm>}TZΆHRH@fNY!|6e} ʬ*Q,lu^Ad 8rB@?eJ/!T'%UFIF:SI@=?JL yNMYE"34IՕe E'? {H{/CX7Ly?%ʨMǒ,ϠEf6"Q7)PƑJeN7t"OOUfT#'#zZ Rr#0sUtNץ"S7SjXcCeM v5?Em:86X䴋|hgSiqiqhZuAc\!}i$6Ҷ 6rAMVDS ʹ~Uڍ[Jvrh8k=%HS1RMʲn9'Vh'i=ݢG=ϐZYsr1gTmJHgqg$#>-NhtGҕx򛾂үu=?my/pQߎ/JMJ+Is 9pTo>J+y>̧C[Rl%o]g<,=;e},ޤ@pɪi]a;2}k:SԳ]i3:ޫ3j"="ߙ1)dɉreqeO^t]4vijn`$N!sUR*w\O3YkkJ#r{}?5uIK_ZRjS=\Znu{82V.m9O,V _pO5YC&V>;622Ŕα_hszM٭Ƒp|\:>#Iֲem֬}\5EL0|'OR@u+ ws* DGqZ/O{.꛻(3In~2>ʠ^Tn-c {4JkNW7.W.Jךqnղm_^ne?ǰZtQFyMQ\w_ߓPt:~9d[HH'FjVuk2#VueEJڢV-7$ۨg3뚔9`j't19$ |*{ ao, 5>u~'2d;nHʋPKp1ێҖO=e-վкF֮lU2  㑂oz!n+T|}lD][d)˯:Jn*O~_OuZmi;>~Ǔll@%p1_p\eTg3Ycw%m66fy9u,:.,Sy UW UI'G٥h-+ ֨~>msklW2"o,[n8upKi.1%uCUG.Ӵ\tKGMmKIgrH:X,KUIY@#~_u֥.F8c9 |$oz[]b_>l䊄#ьF(H[rn'gG-Бv[,88bKSȤhS:O 1+Ǽ}h(@*It9\s"_`O@ 75m{36LJ {zUFpF`0Oo|iRDH0#&/ُ[j#dҵ+(X!Fc2xZ:w2pGjµ'X";FrqcS;>Q0Τ+miXHs|n4%Fo||NI|G_iz%[/̆& #'GoYw${Y hdH ֡K;&ÔI#td¸y?P{JB:fAk_o.eg$%A>TFqR$S ۤW҄V;jsWl2IEuMQs*i)oeDgu_U5cG&빚?ӟ (սI6&0%L| ?,Eeu+qt: M=yg?i_kQZ#$ϖs\;lXhzFAαS{>ҘK$cZ$%!]kd9#5X+:rA>Iln % (icSw5g.e+Y0Su5ɮW{s.0ϗ®CSeS2 -nb ㍪qO廴/uFGӽ-yyDQH3ǵ{u_W(h5]lW}dpy})!'ݱ@=Kb[ٚMqZF.SJ/K 9?K#kkٞJ#DW?lYIr0r)$ilgm~}dq~ݙ@$M~=cFf$dLqD'%H3**1ʙ$]ÞbM_WO3ZRѰUwd?֌ͧB=p?6r1*a 2#]dhb-7DS4bGhgX]K^-τZ%V q@_> >&%Mi_6dib)me,._ q'o,{y?\U]$ɭjN*s}q Kye$l.u]J;\vyVrnNSw,V(Z4$Z4a(0||]CZt"F Y}3nH8F&w1:;ƒU`o|V !lO_޴{cTm{˂61?ʄ]Y(4 [Ɠ:nE"]7vW~l"DhlHδ]"K |'զ(6F}M]-zVH ү ',7S5?Dktطc%TKܻ9Ljٮ71Q=*=;mcD542czb<gK]Z LZ8}M2[ȥPM!-\.HZ޳&M'sגKDP$islR-Uil 8=9j{`8|I5X{ޛkVEs6]o Е–#2ʣXɛ/Ii+ tytFdi8DB}z1V`$+B9 sVh]W+uh#*. +靪5U\<Ҵ2Iꬉᙉ7|XתG+Nj~|9|unu֫fc?j |jW9ɨR,zaףQcX'GC0>?6f ƊiS]s/>Zvܫ~ϑ-I=ќQJ^Dxd[ 6+yxIr@s\P=߼ȤWtF r'ړ&5qOl< K&}kcncFei'83ztaagom Ʊ=OdA%Gea> _alS]M^ZGPpDx'?LqR3;F9d#N^BcCƽRA{0?᧕#Yrr=p`}K}p)?I+!@~l;KQj՝CedriZ H14օqof?w3iIᧇmN~`#mBQ:RU0B sMk\ڡ^Vvmn3sQޅlzY ![jq]g++z[՜K(C"zA5݋/GEt8W͸G3Dt:~X;d}*:k[iVp?RΣ/5TЬGˊi`l\c#Ϸ?5)-Lעd|l^;D^X3ɤtL~q5@ߎqޥoM- uGsc|v; FȤ5o]"ZJ?B*mqj7+̬ǕbTRt-(Fop8"|_{?*l1XVc}q qi*Ekze9Y䲐9CNQ۷*(-3.|S{5H+QcmNϿu1C/.tM3L"bc8XYKu{pBzi7;loI4R jTm5O'{?J? Ibo4),$- ^Y 3I9JՋr{zRUO7'r$276@b}jJI*|=x xǽ,NW(}VusOBvd.Pmжh d\7Ea#n;"UR[S!w|Ttn>5UJ(p9j[8rA걜hF,v{}EUP 2%y~lz{?Y-!$"(ƉӺN XYى8momX+qTq(*?+m>:CJnleuJ _#ٌ: 6z.&U#a؂ jmS]!q bs.{T'ѹ/]+NCCՇM2Ρmbo'cH:5HC#< 3# iv8dG>?!O:}Lwl?S'9$o:hV {^fw2UjG[oÚe\>T5,Thk l2[:8#uκ5vnw4a)t;ww?D}OF9\Oz$p#D,XV] ט)F |}{9gn(-8xU@Yw%ǐ;>QG֔wtRUQ@ q;Tr F~mWHFCSyhdzDlҲ '[WXLҩǁM=ys?}[:ϥ[ H1(by 5P>y"Oy$5[P) OuL %HC'JN.5%'i̯u':uc~ [n0?zh̊i_"jfgszN?Ź:~-?kUݼ6B<HZ'4Vqkَ^Itty A*EHuBe&8"jxd3%ƊA@/Q?AY'/\i7ZjQgN|nF Qm[Ku'mvQďơCzuv&2ixGط?A[i5Ŷx8 ?oݑBPw|- Q*%@l gAGK}ն4KntiTcZB;j1&73w#(ƥv28O-*S5%ѭd)$͸8Zao)fV]P~XSY&{ ֵ{O3j0@min-]s G v J{Zdfo.y$O$Vr!x U6ֆM@iIM$陻1'z6q1؆ U?h]#HiuspWQ+۷fpx +ATؒ܋^mu:~I1pQF@P5# 뺆ơ$w4R3$T>Oƥ>ˤSJk|iLu@F@#Hbw1=Úk^9K%]8"Q;\Sr3[jxRrG@]}+?JXFaC'' OxF+Un˥f&>Ĩ|k,J3-ǒ& ,* 2_+.w16|z@Z+#E{{|!tΛA\ǡb݀gq'\p(Nzf6vq4Q $?sn4a[ 9Ɉ|fʦ{E@4W>3] Lt#lax KO s\Pb_ ΕփI0r1j6HYd²o`lYTPKbJ Eh>VMUKnPﴗQFO)eVtLGibI}?5Ηi1`Ҭz]ki'ъQ:̺-䆑cy¯/RG&%mL6!5Z-^ jJ|{@N#Jt]:y.5&Ky1ccer0svb(Öǭ[>ܵ*xvx4ӮlUDKiTMۇ<{4b!=u[;+2`wcgN":y RFM|"ծPebeR@¼H 8dbbK5&a-,~#ԯGF67InQn(jKHk=WJܻ{__>dL\B#HthS^Z7vj |ƭ6zi{+1`Hbc!V.Qqj6 b?z݀dפUO7PK{h/qo![pg|Ajܔ7XaaO"[\JjO~;R(jzzӤ՛$W>t^U#"[jVk1=HR>ޡWpNwc RJv*qջQHϑj/th .⒒;A=w#i.:sXI7ٝؑk^Ryk&] c? Zbӑ LjXέhỷkyxܠGVmϦ*ǩ-$M/S-jr6Vyy֖GF"rs9:%wo*[́KR?ۜGA캧<2;%J>F\ʑ[Cr x֟}GhI$P͞8+E3qEXYzf}f^VmYp~uBfB0s۞*[Rw.#bO?:lֱW_cզ ^m@މho"]Z{S&b-r|1I+cr?!Y5,ѡ)qIfȎ/''O uy2m}1RzvJ֦"f#vn8^kx|R d@MMB&VAE #Gz ŖO)qZ;iP @j-qR"Pdv#|T~a&}&sw# D+|<)\5-jM+MOk-BkR<0㟝g&E>?ڊPavGƩ6^:zߧMm^lr oc? >Fi,nՒXئ}(meޫK97Y{xџlzj0U\{tL/-ѭ&v_ ?N'.{=&8o59/1q~ijlϹs&7̺u=IKi $|[U{۹ni'O0d7Jpʨ#>ǴΞ + Xj1bIVvȿ )[!Ӑ}(ei(ݼyEaF(,F9Pvu5Kِm#' ߰{̪˷V[.&%y"vAn. tz6F26{(ֿ>WJ*2G{+=CvgxΥ$: 7;O|g[n^7kq]QonK5xB@5[Om[v|@*vK-'q YΠ0y7:c[NsBGGQloU=95rkE\ie42)zdFYPh\b^Z2s .> 0,͒1U"+P&h7 VlI9`$ -E$$*3̟*q43!N;ƃT7+T AM1riozEzmC:ȲMt9S;{;EY4s'VmXJ^yadKq|?#V&->&i"Os*-ʙI>KOT-z/k+%fH';?:hӵg {p x,Œ3V!k[iŖ,dR@ $RAxiӺd.L%t;{ U辩:p^<\6[8c،hq/^k0G@/nt޷xc-? Ql![iDIxBx.K9Ţw$ c >[b] O(2w$g oP[uNܤ۲UPtBʒ8&PJ~U=bZnI !l{7K O}F>(VofZչ%xt F\#ٽ|CHVѵ3\HH"dlx_K7(qۺ?MNNNM[kwt!ErJ1"VA}^P%!~$άtHVo I4yOޝqF8]E8c,8~2_Fvm-spU!!>Ֆ\7S\J̀dbp]!t1]"}ø֥i^8*03ֺw;~^DgQ_^OY[Tnz !Ϟ|-̓BIv =<Vܕ#κ1ƞ?Tp%l}< pU[튋?vRƉt=Ґ#=#5aja"d" ^;iOԫ7sS?RkzV$yRVH xT+ȃG#ݞ,Ww(YA'_c5$@d ;}Oznm*-1l-4ءf8yTO#|XZFsZ.gwK9op'}MuUqJ zө:G=3ǎ pjDhNF>SXx1v4]5m^I&-c(.OcQ{!˵=0$d*܁V59i2^m~:tIcJȧwCr(2I]{#-QUvh?/}+R!m|G&-%/$QrX@X&fzb0灟1Sߢ|Q薑tkZQw?y}? 'zBxl$fڗgĆ +)m7O4a8p<ÚO7mBBwnby'Χ-@IcD*x#4t~̃e>.mῼrJԨ?rjGQC7sqz|tۦkU/)];Qt[ xqH$ܖ?Ҧ]Ŭ>9fGȢJqg Xn"\6, ^ƎQ[9y[gϥ61N(nr)BA <*>T$370`ίI 8#ι3$#q^DYߘ+ųIoqD u_9c3 zqU8éE>yZɇZ=Tv*[L/{{@9PYMT%̲g8F ?CvGO;օtkgiͨJ b?5oƄvMe&$+2lYG| Ulof#y/s"Y'=8?RjZ|`6!Gh5zV kV.#+q#eVcZά-n'im%ÎvƅGΎmo汹s|XnZYc:|O6~~u@랛Y $YU2E'#I;O':)^5'}EL+f ,ᴎ v}>'Mv;D]IXZFϡCkwavnjBz+ܠAO f%*{gG?H!'9IRI㷘Kil覙.aԟ;GWV\_20mVA[geIHݏ^Mf~zUNfh4#"}@kE5(2V(#2>}+]cSkdn|ɧM:"2NZFi\`` yw0JPJxc9j+&cky>mjO#\F.q÷?:lWvG3H3֓urèkc^j7 ;U 1 ؑz֓j-6Q$$+Pd{xb9uί7vHw£P;r~ ͍qiҤ k$ YʎrsrOzl*틞\iVF}2ME/ҥD@GcVg4; @Uv/:_B!tEr)]B,Ern 4ڗ:A#CI5x pRWE{҄VxXUΜVP ֘ o6~G?Vwsjvӫc(xudDry1ZP'Po` 591d`wPgwvĄϖҠ} iLa| rBct+!eʧ?GPHcߙE9TGxe 4FU #1\.cFg6NFh`-g.f.CnO]N~\+c[ 3\| =EXsE.d#'j^h)l+;K1/z[Idӭ-#h.eloWl HY]gGj Aם<8^Lkh'4 MsQ.] FsFOʰ8hc6 r0k&t{ZM3DĈKc9W<kE%o w$??c rd,qIS/{Q[۝9w1-g8,=9ERz uw_^CnQ&~Uqb ,- JF?mWTZ)G!tv!RWU9M.FVNe$:'-K%&O +)#W AZ ;k~ʵ߳ˤD2wQTt^.2Q;h홻A+h$n%\#D8H#qtQ1GY.j1L TVr9nNI85#<.W~'4*]Zo3Ai:[{؅q8{s\-h[#;WSP_stv5nݤVo#UbQ79h{83ef{;癤{y`?T-Ykay7 0~=4!imXV9"%*1m]5 mf-"iIܩeAv eI>ԺE:u%2\d"B@0>ރ$ό7!D<Agʷm6+ (mDUB ;Ȍ,L+S4]:\QӌWmBS"35+4NFyڿA}-Ъ3ɕY{)=3RY{_kd c8~TIQ`];vd֏ӽ] ֊E:f֟*ս964COBJ1/vVʆ?T-:2;Tɣjv~Io)#—ލ=ǵMeSL`ImEsRG[_i?$Lxv4oFTJlNF<¼?1YMKj_Iy&8A=*|\5}җd]<^7^ ?izJ)Bܲ\6?s|+< cV:B{ԶwI )M{KZȄKl/+l6B)~rIsQ%y>T``H|(Z&Vd߽*e4+#=9\xqT.)5;0~6ފ$xosq}A#Je/F~?ZV$DݹT!Gs%Pg5ԐȞ6YR%~u5 v{i>C?ZVbp1{ YYڮx6$`)4sδt@sy:ş-?k i{U}xZE0,f\>A[mY,AF3zGڙ$RuCGi9 jRѺ_Kwbb{_, Ҝw]އ/!bY1uܚ ;z>]jӠ>w LDR9 9'5͓}m!R>%sGyϥd)Wǝ]ix VX "M{{HpW-W¸ԙBvASN 0]2q4ݠ*LQ܀rOH=Hb{-Blނ9$:9U {1][&On"_׏jA@[#k)Mg9ṍ )|]U{1Hܔ=wZm+Z(p+'֥$H q[8)#<)%?HvGM}FI =1*ƺe5Ki*rq+bC{SZ>OTQX?LFA?.5Vd)ǔIg՚+x }p}z{[D7-B?Ziw0Oe!ω̋D㺞8wxe<⁛6AE-n#u`KG a[[7͚}HQʟ#U>'Ti%7RP鄺ZiwmۅrT7spw}%b&.0ߎ*xvy`;m$: gQڼ.ːŲ >k+ lE|ߥ_Mk$sG#G*s:c"=JDE8VTJX \ZI=3Go&O5H;=Eꓘ.0#Vz8ROVcu,P7UK]#S4&ӭ&aNִ/lol.qt)ϢiڕI!*o#֥&D[LͦIv<ՃΠTZjS ;* hm?.Z6M^G<f}2x!8.^E\5+ɷn2aq$0Pu {q4-]h2kK~jZ/p0G啅sc b縧+.w^к;OUNi.ZvrĜ~4^\=b)zZeݼ,pXF~ox>!b>Tqƭ?]m8BԍG֍?@ײnd91n]NxԺΛ={:Jۘ]q@y:BL'+) d|iܓMJXG$bٳj[ h?R%3$񬛾 IwqчƊj_rEgi9*OtcEvᣮgH5fHu(pyKwrd] tn'SPɷW p^>5^MݔJXrelމbVlamqLGcy?zWM:;*2Vbpo46j;ўY<9I.1(4A}k$`4!MXZz$%+1s3+/zLT4;H& i!KBڢi!HN&,dK'xG#v씣zg=]0AT+|? HNj * y4Rɇ'3|kBd{8JMyuT{9bFǵ$X^ky5f"Yc*;bqQ-r; aWҡXkAq%GnbxP4p+pe<);7 5J{!1]{J~r欿$oy5Y}1ɣqSa4{n}uHլI794ŴSzVUo,9] Ӯ^ʑZJt%n?ϕ _&Yr$5ꗧ藷J| IBA^IZ3 f5 y#:"99/HE ?it#{PԲsEtsGeccRTa }9=c{ݪXle`Y1Y3ݫw0L4\,ѕ5夔v5-Q1\;5?Ilgk5U&Xp[on7)ovqFWtA&\WC(19Wni#^s rN5A۵\ڑ Rk,n~"4f1$+CA)')Y3СpI Y&b qYdߠl+ycJ/sqݼR$TmݬI3=k&AnԺX^R 2q?G:J-VvɅSs?ՎHh7;})gM2izuROaIi,Wgͳ[B1õ.:jH?c,?!(Q{$OijZȲt]:{b&8>t辯-ap=dcIX &yZI.ݗpJW6P:{TpNXJrNZZĶKNdkWDt5 V1zFҭO |V"%P<8#ʃ?mޫ6V{֠P?qlک!F9q[l^z ]5r`Uuv\_[%w4hۆ3wbN)m0nz޴=2-6,W<FH` {sV-U^*h^ai\ΙMD^ۿnQv<{ן,2Mi:,:Z;[8pjkVOc\[9 cbƧW/",G9:7D4e UdI-^P8xaSő#Z@ΖrF$*9GsyƸ9BFHҨC]E]QX/dNu,\m఑ UbxM5|_vt =i=<@A=֤fkAblz$F^īh$V <dx\ȟX0&BK1Uk9AZtilb+}6 k^- VN1Q1Ilium;n|TMO}+YLo&\ۛ{3' -]sLX xp?&.Q/wZqXrݾ`RxNѼXGqcɁGldWeiޙnf,r.rJ<;dW)q>UUӀ*U;G"H? nضo{Q/#{R]rXv;%ƮPﭾm%E5D68zZ==ːs~\J##3Jwt}gu$x t2# ?V MEt/Í(dJ9\}Ecww}5 lUr3UdcteWzbm \qEki!s08o}ZwQiXFo{wk5wÎ;W[9hF^ȮFő*}[JUGF(XLKdO4я*|pF*٦,v~x6eH,F,۰EwYu1} 5Ũӆ8ҴR8r n 8jz|pO]G@VҞ5j$չ7v{h ceIkj,AC~1ߜUD:mAmG@ jpаqQqXVU&{Տse }@(~>cgm$سă%!ώEPi5`#W]k.Ԛ $S:xE"Y#J:umJBD yW3㛩zJX>ֺ"H݈'I򬵸 *y [ȸ|Lxٖe:Mfi']9pzDxm⸶R"c29VG Hğ>hin/#c Rfay:\9[Xlc;k՜tr1}B9P<`9yw^oD́doj l Ը1휨\ୢ0k[2X{RۥܽPD7۬SAzkLj{*ԍ>(lQwNF{\AEӯW#Γ LѸzxN)L˟t cvOje׋B[ep>yEe؎ȥ]]:αaD$. {)+~Z֖c]O}뮏u{罴%@X\5;kvsFR.1VLh*JcCOѧcǑ`N;~iOn$AH*Z?.W@CxK-:K[kp¬dvҎ58Oʞ1QwzIQwƘ}{W$ußJxC>y!9/+I'%zQ)f'ʤ?bUˏ| Vu=zR\\R62 85v!$z Vk02,!` pdѩXV'4{:zAsw co"JJJ*rDBZt떊 |e>*,d?Z/iQ5 vM0a>FDSu*I% *2XIȩm>R3m)<,}3SzXٹ034~=rc|N UZۓG`鑌z%ԼRm>qR߾E~ /suy·EVbut|̜zEd^K|3㟕z:o[KҴ:ʚ 9ځTz9ڸ嗒>YbK{ u>c8_JηԗE".E,S$~<`}}7%ԳIL}=״˻IP̃*}uwO 0 ?|au& !#S$2'Iy縎tqDIDp*M実i/;25 H[]0M٪5b:ntG![k#+X$3X?L]Zr8oX s].'Ď9dV,9~#ʖ9Wgg؆3"sw:̰K௺3SԵk*}6SoCݪ5YW Ò w`@\Ny&ye-̳+p;(Tr8T (A3(˦1lhpKqy~4V;K Ƴ*@|N~;RZs`XbF9{أB߻8f߁ۺs^jWuae*`D0Gzcʹʎ䏧 Us1(>UWK$3zXKy,vw9@P^rU Rv{+ G/y2"1kQ{z?CcFW޿F(dѢ}T\ #I(:[T{J:Rz~ YBKqJsX*mqqm9<*H-^ʶ%} ho x1]FG慔 rjg;K+02=F-L[3 x{C Llir]He珑^%=Ŭ KNT֕krX;w[GʲΧC3I1F&LJs|Sl+(.fe Px3~#.X˓V/gӄ:bfq}eMIWM63wygwmmmsm4[1.]֒i1\=a/>̲qxQJVB4}vs(-{m5E(–)[tcI(ao}ʣHyVa5F)JGh;opƣR4" υ'Ql:ZiAv\ Sѩ:~-H`+PIU k B4B>AFA0~"ۗeERY)pg߶uĪ~%}} VsAA2/ҭc9h/*⾕ԡL0&TԲsqI`@D+LV*7A_%6er|kQaB$|)!sܟ*ʖhC6e>Cʣ)1,=1's/aMo!AbF"[ -tв>$#[StPi_b܊(Z?JAV4"Zـj]s7:i4C2UsܯqViZQ-hfOrE|YmpJ2޳YRR[=*9tIl<>\ *юuN6XcouO 3/i-^jpk6_a؊p ?֩-~$KDdL?清we 8&YK@XXUoJ$^tGɊ]*f Ӱ{Y# `T[*ڼI,{v[Ҳ- XJ?NkZO X$B2kEDy!1.XlU+NE,'Q?]х\xLE Id>p q,SR:z0fQDު]Al$}j7UMg6pp1;S&.xDN8gھӇ)* nïY *$VJ|WRF|V8k@nF AdQG?ZjBK7#9S:2 ,CU#'4z.eTx&P؎De;||;WՖY7 L>Zd.NI57U$DxӲɴ8o noOҙi-˒:cyb+Bq'WKky'e%vA?n%5vDss Wtx#+#,IcǟQdYvxLWpFN' bn8\Hy!Gc@u[rY1?5c sҹ C)8\ٯDzh/ŧcu6Cԭg.@2#(Me,΍ i "-X]_2fߴ|<>=-,.',Rw#z|0n*ʲœ7;DeF6>.}+ΑqFTeX$>B6~RP r,, cGٹk.FG :-qz:ԩ2 G?2gP4֐X=hM >+x'5# w*o5CҜbS?NO4YOYy / I I{6ZvHRiw7 gAY~y5q+J殈#T[:L}*+sWI)?p^,%qPKg$*;bHy!;pk]E 9,囹Ҝ{*9^n8+dǬЮ[ag<(ROF "]Zêi8й#Icή]H7pjDgV%'zBvI>b0gtALX?Jrصj xC~`"o]Xvd?ßmċ ̈́'?ҽZuD̰}63Zy%GgԖnupG!%T| c>yYǶIź}I"f`$ AI?}~y* O,rYDZ9'\…IVa5REP΢NqAl[#uG$ ݄zl6*8QT hAIToCٿJͪ@5h٢8b'qYNimgf >+"ie|}OBOd5$ i x6Rј4f sW5Q+6'\Up+G,3CIp} Qe*HY 9$X.u)O&IvqO:QGai(C &xTvW︬l}3_CZEkǵs',i¾ic)&G.ķ$ҋM6p(5d\#G:vŵV1K" kD?erXU? ?2+=+v7HVx@z1RCcސG5ݽ#_843}O3#v|5el%KZ >9?*X$~mR[Kc>`'zbC]1r𢟣47 ⸐nlc4t/#by-,q1+Y}7`Զ+դt_FAn]~xs#GD1;>c=Nnb4{`Auբ&=?%PL"TV3!FOr t]oP6ʬx2U;{bEP${ӕɛ0,80&3Su؀@nFᬭrN}OnGAİʓ(v؎iFtuuK\>Km< 3vm-6uʬvۧ,ά"dmÜcԽ!UeSs1ۏ: Щ> U%_q@֯Vq3jA% ">8}sA)ƣ"l`z<,$s 'ϷõC(.fRF&y$q PldrsހAu]2Tg9PV# v]:lV0,hw#>_/v|l? Kp*OɣJ|<y psǕYY- w}hc%ͳ3X{i);C(Y.e2c9?SnT\F0~5]"LDLEȁぁvUQ1p 1MƷ~.2pNЧ+c;8 LvZD[Z) d{Ͻɶe@$$^]gL`*?#pI#xX $OUvImVa zy==)2<oj6TPPkyp|]۬o.!x[=UtVq/t-RvJp<'QèݽIvS>yD'曆IeQŎQɦ+ly-PNNp !m,-uw>n5h(EUvatXy_ r<~QUMލɪ_k#)f:>E!fF{#QV\9Qҫ7 2@GM?~G۳g;!YFZhrir_e<~աUߨ7zmb6;!Ɩ_x^.XM PQՏ67@'xq#<{]Uԛc|?OYA?zdxZEԺ]?zrD+斶]Q}6|_mZY4p~ )W`8}FKFAnf$g(奍l Oy+=?:7f9yn y|Qp=k[-sxƑ1TlFr*99ʩt~z^@]E+}jYаk_֨,xIʈ_?qQYg4 F;P}o_CSw[bcQNY 4X/3HnkfQ@pғQE* &؟G =.&H#NI+f'EZ̫> 9AmWNд;>ux젿v9ǭXөb!RT5_=U7X n=h8쪻9'Z[6aQn#\ڽYS1[B#51T`Q0M"U`ȵ8SfPo8eQNwtʱL: ~,nkOmW>IYW :9,+5_|$V2*7| q?jaW>,ґ;(;ԎeS e9y =8@,>\2ʽkD}'BG X{#X-a%uIlQ@ p3U{[9 v}=*ˤ!؁ c 䃳; C,6";c}Kf#p,RD6*,G'RY ?; 9K@TUFtp@<¹4SuyPDb3ÚwPhP# oͷ`Y69'ʊMd (IRy7ȠH;0>u?OҡCr,gX>ѣuե;=j@xCs>g='ES3hUN1|MBάDރ>U* c`C;jQ4R42R!c׽$4]@+'JzFB@l.D+rsfq,rgִXT);dܩSY,Tx-Z$e;V%O2)4L$W7,֬niVHapFW'ӿj6226n_ʌ"İ*X` 'ό)Rز1=lFAR`A3n R!jb?+62BaB$~?ZʶlHjrZJIUp{(೉1sSS"Wnev=s{jzMqDR0Cn yM$\Kֺ}e-RcpoB;}+tI_I˛YeCF0UWsڱ; {izOX0V11V 9CC2zo_5kw_/j蚅摪{&Z?C B)'fYJF#ޙ7.2ãmacp7tsv+,Z_k=E'ٴ gU?sUx$~4a:?^Dօr^+X=sqUSg^[Isکpb@'WdQo$GSs]# ;eO Ri}ZDpNFh'y]]gx;|N-7Yu$h;-ϽkE$h=5J.\C,$+VE6N5eK/$c 3[]J6$kT_7$;hnnDT01L'8{GΝ}@OS|Aϝ|#AjTzO;˅'YXg񡗒rvxlm8tZ.1?:s3%* y,BuV`ƾ{$.l]wS䴹²G&djڊ֎|+Ds]]šh=Qqu9[[3)'$fAKf-#Z:&x=bhGgIj"kPU VRyT.+0+/4>xfYV0OF< [ېA Z[wR;JBB&"ڀ(` _=W9ayz9⻌-*`b%4ں啧e@?ʾ jyڰd0oB 9#:-XNs+cEbǹ#5("r c s]򮃃5y҉g^]mnad*G{nן* )f$y-;DwI6q{8lZ6iBbIhOWZX2=F#UDz(rZ=aF6g#=[_yx3 <9#b l~.y05jb`ҥg2k>7#%Bx#8PpGrZ& y89V%L3eXy F7f6<7Rcγ]eMr{3Zl"[8v>fymXB#>U* {wnR%+zevMb.}~9qfe]LP9 @ :/,Cr|ǕWu :H䁑w˂ǰc99FqFN7&H\׃Aؐw ]C/!+e$4D0h## wYdOg;!A.7l,M LiOjt(=jo32GJ,J .8r9Uzml;;U $:eHt.#pd`0?UK];G!|\T=]'͏ J{լ_gN4'  y}1ҼP0|+ºT9G汅021\ }7XAx$ќ 8OklWS{; x+r1״;t\or8ݠy\ R+ \:9h9 GY!$UqZCtRm$h^[>]:崀Yp,C tHfdmFIN޸TfHr{p*rZ)RIʃDfy$d7=cҚX VUM9'.hیq$Uzfr WF9ϭÙahќcQPZx҉` *8ΟXD?t#H4nрCј^KCqI6s!ѢJ'Pnl*qpvVyGDv'ۊwS=ZH`S'8wBEz xlmǗ`X=ֵKkot#gh'+4n縱l(ݒ>#ђ7*8Wwbwz}N .c\vTs()TRBPo]ĥR{vN&$+/#-Tw5Ԭ![W   7w9Q Ʋ|y?Mdhna[n? F]㵆У3WFxr>ǜL? wa.v #8sM_J^D1KPq30߳[JX\Rx$-Čvq'w5RIcY%wIrp89: ["2yyQi-+ZNByo ;$bsO RHQ&l!udd"EAd)n6vT~(鑔QGs)vX9[Zkdѐ?قEvtĔ8ޕ&='jɬiWrǹ!Kv|],ЪA$i_[0w+NsQ>2BI2WD]ћY4d3&PH|H_\>8%dް鉎gLiS,/,I-dnP4:ZMu,+Ϻ C:jX͓̾M?*ܺDU}^IYnq9iKrm;2x>N״b[HRj["ŋ*J|k l8K“CTaޭ;Is4t1:픀/qJlάW $6Ü}j >Jq=Sg]tgWwz~Uxf ] hb+tsS7gZR})[)?fQɮzO)xqq_j:K؃˓^J%h$IlH`Je<+䎟".>{>̯Դr P;T*]SvKs)>⸱T8fc9Q{,Pv>T8\jKN8|N0100f͠bF   ! 2005:06:03 08:39:092005:06:03 08:39:09< H  zKi46001231143456گIhPrintIM0100^R980100(*HH          x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{Je#p;?x-Y 5ǎ@"8~گSvKzݰA$,8 -aT|K|4z3Ccnɗx5c[ďb"+,ȶ@l[à9Sj5K}kX_?ߴsD{iO'dp/=5f'qjVю0ׁMk17ժ;J߉$6m)<&(?/I!Ց?4`ЙykUN|yZnaal1x0GR䁿{3MMY+5^:ԕǶkwźw:DA(ÿ|I?ؚg-ȭ1hBvÐ7^aM/tTB-Љ!Dr!o9F9SӄHsTt%%ۣUr8 \bD7>ߩ؃s¾%y9v6r!aVϒǮ՗#itKgFp6u8/;zUKv ̀Cܺ;ִ4G[w}p;Z>tzLh*5[ǻdb?6,.alXp/wm<sԜq y!D6K+H:ֱ{zQi!.VT&h`AK|ym \lo1RqJ}X&[DZm6ϕ1 % k Uqke'|4jd wzm {m#O^߯=EІ {p-".BԺN^Ix%)-?ҬHL !'Qڜq,üIfGlnv)o ^Z]qZ&d$(NՕiz-~__W\Eupr~*R1}fyʹ$9gg=6]0ӣDa~@?jAi?ogu)+-6;  M|IW8ytYjZh %Rq=ujS}6Nj0roKH ivAz_g_ݜbIؓ `8>`ExT55} ;j^[᷌ 6yimS$g܌;26Ue$FՕB%M}]7xD|׼^As>{qֺ `.jty/d)HƦce\ 5klk;VBt3}~D2̑rܣ!p:J>ʣ 0IbVv '~tGRGݽC+ugCgU.Ŕ%ߠ`=zA/efk!K? k7ޑ;'ҼKMßVQadu-.AlA`=qȯ.;G;ӥ2ivqgQ .`'lY[7i >}!!gtyP68#!&?_ b[.Ǩ){"<|'?<]A~~͛'$~|ji:r#!ed*Ӟ{Sh=<Zy(E.I8E^ Q]eǂ&s~ҴǛO̥˂⏏+YB!;nR@P1R 28WA# m=(nt[eTA﹣xNER)G#)qpL^٬UQ%HITkhWFOG߆WhM7Pj4m'VPךL&dBG _3%ڤZ2 4UKי+>!5^2]KHBKu%ʁaS@fbC%ö#Æ;eQq\rC,?.lrľ|Mq,Ow3ZDGBnKqrG@ֿVnwvӤ fUPpñt]pxZu/?,$[Ac9d>>5|uCuK m"綎XI-R S}|AoZ Ϋgyc5DBKۯg$x|2=՚L.U1k@o<JqV>V]u2sMuYR!}Mb2s뎟S%I7+>?,e_T% }`t06{̉\mpc*O|=j\i !E=9+N# k;1֑؉-Kŭ m 0<xSGߊLj>\@cGң7!|x*ݏHpqG]f&MZ7m`άsҾf/O){'ҳizdž4{%PK٨nld+$/^о|Cuc,v 3q@=F漜ڗ]OO&2$~}?o|2 p},Jd$>z_^ko&[اPRԩ< +.ĥAGK>X'~O)?>ߏ~iwm4]j-x %V~Εvo]yhQ ZSXm'PfpCh\U|rBr }C3DK xxt v?~{T3 _wV=?ZC^}yo]Nql.ͥx1pcAnFZ1ۑl#i#|Iyԝ 8P1*{ Ԍ%ԃ?bk9 *J$C8?xG5]OyVQ TR޿@O'/R[:?ZnZshQz'G [|QmfI,6>G+婭jyS=l:|2^rOx5g1k>[۳|xaۚO_ G|KdJ/,\|ݒ>\s]\/aG ^i+?|u?/T}sBI,$kFu>H?} m*c,=?2խkpnj0͟J:*4aqv+*yrJ;񾥨xt;Bil಴xݙ|3 2})tYYwƼuNi!֡,oYk:[F3d)vE:|迲߄uU5y'i$ i7MFxw&_MMhGGEwt`hWť,J dӭ}SKI6<|)Gi҂.l< HSJ1usnFKu] ˴ #__6}W7oi}w0vj/u'ΑoH\lA>|cx_=ml:I*J?M{-}IK5^Xz/G揚,8Rɞ֒\}2"+e/TмQ}>>x4m#4-s(>G8>UP˫^ogz~ :ܮsTگJ|}^j:EmmΆc<&|Ҝ9R[7CJ6~(ݻa$<3+25uN_~X6)?tXn̓L?Rkm2#hJc hGKtRuX` 9<EcKmS0'{}qSș= j2c+]X%P ?gi?5K(Z5.E0 $7o~=f|/|;ӭG#>{hf]jvܒ?oć!SW=C}Kז<swoJ?`ʯDW/~&xV^1Mn+|= E}%p+zQ3]Z]2-ж*K4n]wJ,Oz+9#m>ouR6a{ WdƗ iXHG0 #fck+au.W` 2V%+N֗-:%\ ;>xq|Eg0h.b♲;p^\iy81pW?C,4oEY?z+ ;qF _ Wkc>!kt>ǵrXGt#WlϞ9&nGZŴ+~=CT:H俱tc} wW c[ϭj$6CYBV9 ^Ao.lQ\Yke`V41ܤ;r Otw÷h  ୭ƯOg._ę=oZxk5Ωm}E5? <%idD?Ί@̭eO3'4 a|u⫽Jt8xpHbW˛kACEԠ5utfԃm&^ivA fi)gUojztdܒz]#gL욼Qm"Xtpڡ?zoNBPhk+>j7E;X~Rzc%4i~$˛iU9YX^}WhxzZ776k=H?r?߆kŶLS۵fIɤ]EJ@ :?J7D=S|2ҭt{Mm X.乕dC>S3~ƺk3\Ⱦ;@r%|u$34%V_W}s> Zj/4zVsps{B|MM~~#7i{a@5iusoQnTri֬EJ޻x|-7|S>ԠOی}Y~z]ru}c?>'ĩe%Gvm+F,>&[;Th#-T.%G+#NƠ |a Y&>!;ɊxOΰ ,+OC%L9bؿa<-oĿ@ ̗66zϊ'}\TIZyX7>ῌIy[E}ق6*A"#.dYؓ>LK?yAZMoi'K߹oEhaՒ{@3}{r:{ VI5rNBשI_[Jz夨',H w'88=Nk[d2L ojcN m\ 6hti|v]wWk8};àQҼgt>HӾ\'zm>6pLSv~i:5 OT߼#j}~KYY߭S4ן3DKmI+xso+˖3{b||ס×Gx~|!Eh}\O&WPiBU{ VeEba_ o '"Oi}qXG8j~)E}&?,O_d>*}KĚl O 5~Qi%γ,5"ElHR9BsW| U <1yhu}[+[紟ҭcQ-l!vo~S*|Z^~뇫ρ(?R~-G 8^9Ԅ }W/˖ucOɒ|_?8ϱUQRD|y@6EX5c*dp:}k?7&^,vw oQ_ZCӜt><|{@mk'_ 9fXn%T'11y& _BaIbdYl)8Ih)|?u_PK>vs@_Em=,t ^% G$ $Q%RW_[KvsiCٓZWD($xVKauwq#3ԎǏ*?mς.='-cfIH=^ |ӊI~ {,5{JV>{gx(!8 hNxӗGqBlM<vb~lcgB53vƄeI7on ~>$Eiux-ԗ(\}>x3ZTVW[+ǖ*x+C޵Z 9j{ua۝ZZ-OT?ß e+[j7jXm\څaԕaQǯZnKOl9Z͛-9BjɇDcܜrkVO.+[o.NGnӵR}\k//'?W_<('by5y,{IՃo>#HCeӄ~I_Fj$h]q4^F'Aҳmi$?*Z/Y JĖ!c=ki3#Yƞ.y4}O7oݥt_޼c qF.BJR_x}Ϋ Y\ϙ:7:^񙶓yׇpXuV_}o|oo졇7w(e` ﻧҿ@Nj /\n8o=e(Bģh2}%[g߿֯gqP .n!~񆓥hnΨt+l70o BSU-eHRF?+`58ܶx\&ٽ/3*a jt_qC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((f"G!1AQ"aq2#BR$3br4CSsD&5c0!1AQ"a2q#3B$CR ?u;6kF0waڛAQ֤q+[*6D\ n7xI 7Ǧ:uj53uorRḻ3ǖ޲- 8 i>ޗ2š@ ٔ36'{V.}j `WW9 {zd,$ryvw'V9I`Ě9RJ};qbfqVsd2r3HhfLr5X9ʰn}*Ib '”]G$0LjuX(̰G cTRac&u#t5YP@ʩV3t)?%dP֊YAշ|m*mbST`;CzRNN)<sLD.NUPk cDj?i7 3IR~PfcCIM$.K*)m?XlJd)1 Cpºm.2NT6ʩ`8ROenRK9A6iFZMlrO\X9޳(P~hxJ> 53rvvH~t;u^Y DuKs[.!sM`q4& TT!aL .::)c @) 4@\M)3Lm1萌g3.5oPoA#p:Xk p;SR#dJ19ށ6c>f /ϾpM5A~>UR$%Y4S"!ԤeMF@W$n(*y*5\s޲9wځ6BS#۽!1T|zen%A;(;!9*7׵dv*Jb#h8=4A 00rhpy`;aV .ޢGҟ YI̖J5'H&5.qE  N߯:ʖ ۵`m2~Yt,#A;;i@Rl`yP%l`&: N[9M0X %Z+1$ؚyĒJ ǂyoʁm4e,pǗ:/̶Bp:PVsubͮ46ji&#P2p&4s-;> } #`w$ ]2zO[pN @f`HO H+n{QWeA#MNGJn%Y $ !Vɰ US-m{ՊU}o-i[e=wJIBbm%?_zYt9*vDYY ޫЌا9{Іm ƪYܨ*9"RXuVNT vY>tiҲHi{N1D-F_Yr{S@a9!9zEFpKHPkrDyTs8-:|v<ŀ9$dEAIMЊSB4"BNBPr:2y)/Hy(<2EH$ydXXlp@{SlrqҔZԃϟ,Eb¼]q">Di 1{֣+" s;рR9jKH } Wcኰ4Ld02*=AC1CV1 fB}CB&JyϽ#3&@v:1[1q=,Aખ%~Svq jnɩ(X#&1; )&c0ÖG28_H{ZO*=Gr3ސfS @sd5@PH{wy-QnEOJ Z')$ Sf XALTk 0cdgmnT>Aʢ4 _AߵM0c8埭xgJE$|,隅U|zP$ǿcQC?<ѣǨ57)VHT+mHbn\e+#z;b09`S"7! 'qډNTx@ M<12 # X`(Uڀ| B#`+8 :u彷RUm?Bt9$^jN-rBڽS3FXXn mT$ԐQ<+?bCQҨ%`}cԡu09n48Љ`2Ĝ\ 8—vƙH ,`$WaY9#(ʁb#ڌ\ȑ Nv OToF1ȏJtV"ih1S`cd$fʵc!IEM۷Ɨ}SiNc{)1$vpDsV?uQ:|4N9{,mP<|Ox$=vW:mLldLF@R>Y%[¼ q횑H"ys> u=NNz`з T1'EYhqޫl}W#\f6c1F o5`6;湕o3p ҇ЌJF1RjRN@{)HHQ~5[;)wAX2xGxOo!r FݍC}>=4xN4PN}Qʣ'4LYFJQRKX?8-BHZFe??ʦ$ep; Ԣ+A7VbPE2IIa2ڡn[¬I`I;rQo@]PEN5H^HbDĆT]OΘ"&2kr~. TfQ;ԭVVcbQjbȬΤ;|VN kޤzdPa_Znt&?:*K /sޘFP((hFDS`>_SFV9VYԬҚR H*ϹK F!`,`dnT# )PPj`)jC@#Q;VWoe? ehǵbucdVPd<@ s^* 8hpuP -9L(/ di* rNE2թS%Y $ EK|oX<Xc޲>5{dEk W.ݽ`cZ xrs&Ir}J*9T~Bw0Ȭ #qK"WFÐ}(%T4qK1`ڇ2dfj$'@"p[ݸXR{Q&lI@=i"c|c*6قnHޡFRs mӟZ ^u4G݉ ;dV7=)e3k# @ .TC +=@l7wqeI@iR?cJJsMa\owosأy$OZل7,o!rĎI+`oT;bOzLUԫ{Ni a5C H=1Oqj?0SqPDx4brjkvK)57jC%A|'s2v⽍O$Wז}K5xg>`$fX_<8%'5-}בڴKeYvi]ٶc9aZ|pv* S- @y[Dƨ.=;VmSO܃V$P QB\P;ԏ3ް'sڐ?J"jg| (W20܎TgH.9NcIi؀ig?:a?\6 ŁeFmuoQǽ$:vCQ(!BzoREJʊ`&7"ByV06,i7Nv@Au Ia\*B[2 9doBf|g1e}DP]r4A su@&+ڀ3Q՜co=C`j@O.W8SZ :4*xD>'x ^z5jd4T%b:&ƃpw\Ht:) `+D@yL,W4O4۶`ֽZ X*\6+ұA8%Q%lT"ASO*;6lMFޖyH[t CeFh95PB̌R)9$9\z@#?Z.FԊ-Ȧ!-$ *uP}kQBEfA競=ed r.gX,!j\#r+$ۭ<ۮjq{w⢇P܃ċ8ʬIQLHY vG>uV8KEfvƾ@½c^?E!J*,y!3^_HT { {RџJHENם?0>:dkm[O*퐡U85U){;&F@aʫzOyS9HFE/'=4["~T91B|+P#=(R2v[ofK ʊS9wQQdr3z1\ʄE Ơ7z1W,Ignly6$<9vv'M'"Sp1sA̡u˘!%Յ23 [/BaI,ї>c7^#k9v4mr(GRyƒ 3">bXeaXs֡KVRCց*LgKV#r 8#G|?Fz^MEA9>[nr|RM FlI \o^j\`7o@moM%V0}dd ^`rQ@Y"+B QhnK3V ^XM#J@0h$ 1r3ցaF̬M,xFwtrG  -;]ʰ cr{&;&J3%h,y*ڀ1Dl.1IރhՍñ@6s49Z?0.2sҢ`9qPdX|(1HV3*>}U](dqͥhNFUFrIFH:A +V^9u#/a+dnyP2Í|@Xqmsp߼AV`vr\+nxY6 }O蛉Ⱥ;@'9<>XGwqɖV!_90ZX(^hnQ8sʡxF01M o8s2+gX#:$"pq@*0P8&8'}5I<)Rjq3}O8h]cU2<'Iэ]CZ`ZsQۀ; i 9JBfp@Qlh;0ۧαJ@<M+‘M27ۯZrĆ,mXȜlAdF(2d ]X- &Jǐ]y0HĎ*m95q"`Tg'6JU5bgǽnꌒ Vf->+fmĮ[Ѭ 9 󊕋xmOc5/fC gk锷*H4GƼg"s'=r3]<܋dY5Aw4Ky-6qP՗#{Q%yqH`OJ^K{kQTG&>)8+)B"8߮9|&o1'yc "܌۶;Ud0 V@MYglVLI5Au]21Z8$H7z  Z xޥgy1ěwdi(7,rn3v&O}@_3O5#QJ``k (X',#gGUd VQhDZ60< #|Ŝ8E- ޔ5*>aV )K"@ܳ27(`<ۘ (IP[7YKqנwygVƔ`HMb*6Ȩʊ:`Q[rm:D`y2${ [Ay{PI '<4 IΈ7 >hdQ_sP+&'~55@3G5i=-hQՐAoYd<'fD6:=O2hdc$t+#4 d`ebj18 ^#z;o@U/)k!~5ujy8mdRnb?*`5{Ñ۩Ǚ1!U8狣Nj Ek`Q?+czG\Q` d>n'#zQq DK0+vb)edӟI btpsʙl49T^!~ c'a$`z`z{P$*jpQKg 7c1)n_ c9@HލG:C5[?9 jڻ{ #&} MfQ D6L}QB$z?Sz%  4!{ 3[d"rGb) 9[}sxqn/ cs喴wxcOx/EO*kmJ 1u ;ZɰչS\Ĝ(p1.Ur:ٮ_A "F32WձpxX5=! qip*Q8IX)aWH(T:GR)$>ay ;EuOGqC࿫8“>ФHi:<\#.סNel(F}e;jn8m.mSP6|VٛpqO3j>\} > Κ膑vW_ZgTӫ]/5qPUI#vUEk'DG9s&ɏ۾EywC,t1Qc*Z+.:,ph+jaO7D~@犆qΥ&G>Uv6/1!ދoVcv'R3j0:f*F}71h`ƊK.Żc 'EKj~"F1v9C,%Pҷ¼!PE-iGR':B &<ö٩)#@?3z1ր2+ehb S_ fPyR3ʓNB6p :s^Q'3)'3Mez#;T#H 3β ۹ڔ.o8'Ҕqk+-un9Rb.|_q9-b;6WI$3};V<&Y8<7,ˠNO?\6Aܚռey-v>o[ݸ5s$m]sOƞ%lYDs鉻};{Z8dDQG2EzP9&RdM H;gk*s&y-Eyʒ3i'#lʆ={R|0ni6 :S *(A6rf4l:-:d cDEID [#pvUqUmڒVݞOKJF0?TCV0(  o2NwQb_A|![Vdl-$cX`N)K]%ͳRN "P2XVنm⁁+>Q_"FN du3P ɦdCyo8c(k" (4iox`S#qy""cަcEË/ ,gb35t  s'δŒ@ۘB`0651ڰ=@!@/~9#fVfCy?ܾnwRcs{Qd>}=qP3ƣڕ.ay%}@S,T+ *ud,FI&!:N5ti,gCt`ߡT9An:zlğ1rs.jJeT$ΓrH&bU{! lkxR~l#6ʣT`zĖI:\?ј`<vi*1e+/C\ToU ^7S* ,jk,56#Xq[[&1g7xIJYC`|kpD[nM)/w   xKV~WuQ\T1;ױǁz<9y$U8zV_gwyxܛV*H05r5׬~:tJlzsr5|X;k%BybQӃ-6_a2X᭪WaҗNḽp9 P1p.#fWn3^])F G#P-z;nMnr0a³P&azvF"Ar>Ւ ӝD9 3Xe6ɧ"TIFI*V*Tj`̇A5퓁R4!X),`$#ּ3'1֌clP:Lys*lC]H';RuX=GvzT P/J}X#D!7%YNT\"P(XE vX);8N61Hw95Oun0PA;;MKQXly{/ 飖B2twg :=@kKárd{L ts CVX-fot88F9l(IONl%ZgELJ/E[¤ʴ@ BBB\9kq"ژX ޹hibR$)ĞtR[/<%i(HZUcUNjg:pv Z34r[ūPrR 3ʂ-tUe'$xO$6!NN+c/hVqʥnsuց?Byf STp9j0sX^mBu580{$3eb{јf2qm8}:OJ-j1N6w'@F* lTr>Aֲ|GA7lvU+nwoҥބ7IF<6?ZBH@JO'9g47?b_ځV!N9 UJOZ}p!rJ8*ơ _Lyo^}gVMq܁S$f#W;bNԚWnƀ%Өܾ]\rګ8W  8Sɠjټ^?!k)JL.v #) ?ZtGzM/PxMug&WoMLST:rq[(Lʻu;D@idbYS Nޡ8W-&մ5PVIQ3-biGBNAߐfXyq)W]sr63eT;`P Awv^I=(2˙#l`t"ߞE%ŭC!Ȧ!Jʹ W-muz^Bb~' Hn&:k.՛ӳj?Eχ8%%>G8MFدU}-_CZ,G} -W5q59s~Ē%F",rd;霃2l ||\x˙B6pS7ET*~󣜕+Ǎ&53ϥk|g2P)p6/Ҷ%RPeQ@8'\_1A;9^$n~#͋/æNHEf 1rjCsʵ[;+(+q,;fHqH8>R[5]Z6r?vŒ4umUڪvSaF:|+.F9\GPjq,7YۃV F/95#$ǵ ȢAqڷ)xڏoQм/^yi JeI+tkuE94+&S9̶Q1V׌@R,Mgǽmiy͝=dt̀/#|"\p~-5Qn*޸OR섻 `0U9E,7LFA;=yKkB^#O& 梷YḅdՕ9ֆArKtڤK>^_I߯Z05Lf8!۶P-ܙ&ce1UΜQYN|e 0@'qAU(ȣ 3&[m?3]ۂ@_P`09r_' 0G?֊G4J2( u5" pzt8T@Ƞ3nMIv YXcjIaӝLlu IG1H7qԺ3_W4BΤFԾwQ#9߽bO$cWܓP"zM`>1^S,l(]Gp4oB@:ȗL1ʱQlzܾ40 Z#еA'' tA銏ئ"mZbp1WdT~(v*QTH n1l,*&+k:clrUZՀ~,g汒M`'7d'ex%oVu,ۂzf>4as5Mvf ihf (h4?^mC¯0΀H5Yh39 >R֑G|5yMG N=y2L1Βն9{5c񋈀qOpkZRO̝̎Y&0=jElqG,$xÖ% )Įci^[tgf'rsO՛'hLdy6lblUti\"x?[+m?\xNFe#UЗ|r8ne=Ljb ZL߀}>g5 04jH x5aԠ\ðy2,}yդq_:!֡z4c/vappG1cIɭ9À\ƒ6pW50.jaF(L<bTxOu bsW#=jEV+8n)摅 ZϹXZrK$TWWe(tF$M׎n{ˍvWgLm"bD stW1"kӏ§ζyٳ)Jӄج).1\x3Iex2.zho'jӧTc5oJ:2ÝV}n-ݴl9S{W!{]D]2e2h&42T|^p~}#!t.*-$ U*aQGt4~Al/\l{~TݨFTPth=$&21suB\Y!TXN R6wbv\yk ٔt$̅e.09 DM9Gۙ>^#v.qCu}Dkvq?:}Pԓ&9U @'4.VїP@1%Y;V q FPֳ@'84̒,gC~^'xi#mJAS>hq$^=[%Oڀ1]Er{s衺%_-I舣;,Cj5]toQ9,ʼr;| Q0h8Er֤I)% /4N䟅5  n)hG躊~ /;Tzl`f2|+m[ڼM`tv#cz0{]!-5 "R!{e+굑߄gWE7|F2*q( > ca`nu$`ɪ0)%pJf VTfP7"%URo;c3g8\_R#9; v1`u@*)[niMF49\/:"K<߳M CϹ ~ζÆ)y>B_L[*j哹GvRyԄ#7NK89XķĹvGXВvXe˓˵ o ʣ#Sqf)vϲ%\ GQ[g+ li 8jM jlָ5 V5ou0]<{S-C!FsV$>A x2m>G$vp;y.+F4[#9 zO "ut<\pK xC2 .F9w4mdmBjؙ;fdgf25H.⁜׍>UO+0ڶ?^'2'keX1/sH'\WLaRkl[HΘZ]ԡ %oGpw2ʝ#VۚDm~uX9&X"gnbA@\W9%Ѿ#VAL4gbǶ pWG'˿GFT?pYs ~U!Awo6rjY}8.ފ$g,za,9SΨ-L$(M`so공)=Zjugk\._**Ip[9%H?*r哳Dl7޴~!6ZqN.v#VnW]ž F"Hpԣmwtj3X|OoֆnQ>l1JϙC9byFlOPyL" 䦮S?榣|Zp3ZAރq L ,@ sޘQqe2 `n@w4ЅFgp`8hqIj#ڱRt H= Ebzs++ȭν?3 kE`23jGRjG  #[BSdꑇ 0g6>"Q@u0 qZxFF)'`}P"`ʽx4.DVv* ?-f)ΰ˒6@FϰPN+Rd:_& D SȃawH`WKʽ@(~kzTӖokhn&"FEv88 Y'ўaFvp94Z Fc#*q~b7Dqʮ"1`grM P* ncC6)Ȟ)r#9S$x{.RVWb~"G!]\xHޫxDc3J?i!t1Cg !(!NyAmp+)e.F 9?\oz)| b$cr& ' {*L z˯PPc߰x8r8D}'YT1pX*LFώ&~ƒulң5dǣZ'6˚M9n{ַǤ Tg.["$uZLg= 5Nw*Ug/=+qƏrsƲCU7|=a&bnTR :)R=$co(Fj%qgJrۉLS7_z;O(NPA6N9ו}=xQRrɶj%f7KxY )'T\Jw\˓i+PSŏ &xG}gk's[~Vel)B]ۗ3_Gң%.N4b}=P'yUDUټ#dƫxQm@>l]a4+p-tqʴO韨Rkn:k\lZc)g 2II1h(IYU|BO/XGĄhY|s;qޞOX[@d>Tgrޮ|#v-8tq̅A6a$vb71̣SH7N5ͧĠYfX''z ٥"9!;R\9Y&`5i8WyI2msvk9hm騕#w7PNir2_G,Cֽ&?,ŇP-gD>'hQ2Jv &{{v_irD(nUv}j~m*lqf@q5JtA (V76=?J[ Jy,IfNME~!OR}h =~#!ZQ [$Zc(I|A{Ÿ/^S@'0@%HTR\htkx/'X[00HTdTϡܠpd$'ROFsˊ$"HXHOaϖ*¼m|Q,1)U|_Ws~ cUˡK-v+iISL΄ I`w3N6;ՅnTJR2 (ж%\ b:$Ns p<^2*rAigcMPW :o^H| 8 @'G},J}~{{Rrc; LF.$LLu(pv$|pĶ!0f|+}Xydmd{PH2Rʤ՜oMo==w3#JLd)Gf]-DDju4 s7W.F̰5Uo&K۹6μ}*#ovUF5eymϐBQ}~)]?1~!^cZUM0=K'qY/ CTH2F0Ca@D((V15i7@EvԔ"r\:J~~$ۿ1Q@U8c+>v-c@'8#: ˬxνUH(zHνm %~W Ʀq+gQ/!J2ɧ#-۞>PP;$Uj*KCaqB|E!A Nu{yUl`O3 g-t`4[E$`'G݅b(Q`9p{Rf[E}u.+j&>>+7<#p{aJ1Yګ,63^VϡcLiK&FL-琮)Ln}poJeMf/">dRIc_ϕ{(lyQz$c]+ZVnr+CX׵H#!fbݷ5B(ud w=U^ZT  0=Ek~%^w5 cuA } _7a]SPƒqҥEld)30xcŁ849#Ҥcl<0G~;FVk{Kn1ɢf yV@Zu^qd˸ڔTdqE9"s%\sFXH'qCNΓyVbbd؜/mc7uڳpL&峓YqЮq1 p޽SŎ1SG(rդҘB:P 2;9$wh:^ "{Ӳ8CP#_T2Uq/2>ΚTx WM=LJ2q xWS*4 s}!Ԉ*+.;?zV@+;_Trh ?,g'US0ŷʚlCc]_ԧWubʚXke,r3K8hԠ>p3 LSM @b߅klt~xe¯3]_åc|睛duoě60yi*<]A1 h "\mU}kC3X]WYXO\>D X'&Oɞ7ԟE~=LNT?z(3ڈzdxʔ6:jI]JƝbE,j[DHf+Xn8N! ml|ьOQPN)YDy\ZD-5::5ޖrp=~~8cx 6L 0sz5$$xn#[V+q ƻ6uC4p̱r,~kǸ8rVV(H[>GexFM8i>}'ĜK֦!-Ϝf[NEqcǡ5?xá%4C;#_Sw<7}ki#۩Uͺp r",db0pvµmT6|G3~|vX%YZel,1a=soZY[po+Tu9*i2fhђ308'c⯳Th|9`eeO;kZO RG^lk]Mڶ&U$r\:pˈe-ZA^D00Hcκڃ"dSsۘuѕlͷE'8ۙ:|jZ=98VYL^ß2sHc\ܝ q׽4&2c|m.pd叅 LޒմdjR2PGS.&xc%6tx*d1T7h< *2NwS"Ixl:G1VA #U^lБj-Ϋ >jOIW:C??#b_X7Vv="UW:cq 3j1 Z*АQ1QB9@h)E=}Cn8F*kc 0zR9[- ߱M˳ͻehy$ C z)-HȠDB XdVܾU) 5e3.Lb҄ uX+,:T9í.ّsڰRh'+Š 6` WhV:ji''$z=i {ٞ^|!JˑkYݹ ?5MEW/ \ E oJy]$&%H#z^Jp5;j#$cҁ#8_R[yVQ4![ lŅ0( 3PI&U_LV@A Td9{(U]$¸|)cE힗$IiGxx<T|Gڶk5訵ժ)UIF@VFS`d8W^柗aU5Bi4C.؜ʸsޅj_hshu{Wx_RBOpvm]FaH ʱ2=GE/=+ԼrrɣF;3V\q˘ B<I֑Q-x7qŴ09|Y8Q ڦڬ9-oj=۬N6{!T\cd#>h1eo+,r'd}=-/_H?[ۑ!$FOLpi lNP ;#i79kwwy#c~[ggz ZxLqlHc,$9r:p~0<_ l-飲M3$^ pX$Xʿ 9k~'om|Vu=y<,'h٬cP b3gv;ecdP=iDYn/Xq 3/Ih'Yn߁]^ۇ@mgH到fbFÞsצ*~%e%xmYc"-֧5$v6sy 4,c0y>~i,n`f dǯ*N)MZfo9*Eeqwp僉h ܈倨ʐF ykŠ}wud`q{jWXnYP/ȋ_O}LCVq HnA2VMܸmZfE%墴ld73Io:Npq특|fݢgt0͑,݆8 r_)jkt6V=Ucz! r<[g-݋g@75p,  ϝk.ŀ:9m28K)*s*CY^" o9hT /1*61$Ld,@;mMlُ.N4H(:m@c@Af崌 |X="5#aU9QTĥc; njIg1BBgԬ=_Iu@#(V8PQ&xr=#9Ϋ|-^[3廒?MCNf8]BqEER.,WU`ih$UE3ʎ ^zCt#bzHITE "G0N 4gIs[M{baZ\؍I׻Fgy-+e ޣ5=vޠ'~\7&ܖ޿n. !V7qc$me2>y( }Ǭ- b\xjMMxp\0[:܈[ #f8q. "nPX퐿Z#&&W sƶgp5a`~5ݬTHx +k(H[j?MN-fMuxW\C%n.l0~+VŸ%8GxXf #8d=/ۇF˨4YE~U帼 r\ڜàj)3YG,mxO.D@Nlr|GrUnA$x`ڻc[%]Qp K;uB$l6zR˻ k\2$o-gTցnr&CF(u&iLl19 m<8on'adYJ2}I&LV8Q޾/c/0E=mnoP9&qD@03b#[h$^̽_:{IϽzo偵 d'к -!0[;bv;c''cP3>lCl!K 38`aFX`.]A(tؔ[5V7Fc L#I]G7n6py`P0' ˈ \"G$tFvlg7e|[[CZ38ҧOMk|+ʫ˜nuGN}`sBY.%[Ɍ98T .LfфHo9ci纹T d`Av9ަ7 cCi׫Aآy2°b?ЩX*de Nzӡއm)5w%Tl3JDYH@Ո^1029e-ۆ5N@qívs(L%)4NU0(hyQVK'mTD)cS Dd; 7icorbrzIRrv`V@bh>V4jn LM1(#A۽m1[x47~ ?1S𕽜\"H|ۜOʾw 3֎/_;`$cc\oǾ^nwW GF8J-l3p*3hlèXpy'1PǤ>`2^\N$^pn`ĸ̎XNK>#:xψpx|GJvN0?lw~)5IPglj㶞)7 #y4tN OR8-vDVt.e6~.rc:fRw[9K}uyq7p<1 W((ak6+}&X@ ;Ql%5Ιx$DfAvv8яM.#gQm*Br@rG1[~"r 0M蛇V]h QcD&iDu؜Pn0#RQ>)v JUCW%~TODU pG×@] e02̪0IRHlD-ᇾNߝKK䈂8.F69 PP!Ѝ[%A;b%~C<;ggc%έ\Pb3ҟsX*lnӁ㉏/|u?\U71Q+HT|o]jџWWX5j UY[5ԑݸQ#Pł98oV6j9ڰ8tPK+$Y[Br>T3 ź[܄e.08ǵdVqk'wg4Y86t6*c>QAj!)ږCFCLFO48vg,[8G, *1&P\9L_wisUQba`~5ND>@ʽ>59όLr>9YV!d}?UWq.ѿ0yդ(;&INtX~#Op[m֐;-oq VQ٤ex+ɁSaxkFUlcI`Cv4 Ƅ>8G(^EoF4Oj|9@#Z/ciol;s|ϳ$;hKζ;$* d[ `t ~vZ&@SE`93)_&|g4iNW-B7Rg5xɳ璓VxnG.Xif9y."rM ̀Yb2NK|>?_Gף奱?2 LO>4tAeN*=X\(UT(8@vl|4&q~C4 s?6q-IcjAXd'AsJP;"X$j!' {TƱtՀw eO`)eۯ֤t* ۡ}i _u;~G知#!vN\(C{{R!?&&gw!nXǭj? >'φ."rF> V[F[&q>" Oeųr7>Pi_IʩSp3լix9 [ANxµܒu23W-lkxb >^t6mw]X)kyG([<5q%N#D WzW _FS\sq+5H/,]<i^5HN(C!7kNo}c~gPY_o \MzȒWamxwgxkifIdup՝;mەcIapUIA2q8ŭ 5As f!KN~M?5C {!fiwȩSQ/7!ҲTdI> :yw9` "lZ$ᇆ<+5Û9^U,.YNv?7|Xq+ĸ_0=Vko}eŸGuhui1ۦڿ-kpd ƞ$"9/'O,[s -Syjob{Cc! \FFR>|in Sk"?{dIEi0u\1T,0JFUN4s&Hƭc%ǥ5f9ZGI޴BT2f Gbt\V< r@Lw(X2kEA lEb|P\Kn֋#DRbME|cΧ$24j,a6!ay#|@` |3b)k!/}JwޏJUm A>blH9ߨ-($(ѧ:ȌLgl|y}*;jZ`haZ~'ڐ(ژr*Eߐ9;famtkaxGdO b0qW0GH~'W+(Ǩ(5vפ^ϵ3lИ̨$Dp{CpRzQɷz'|Ԭ!&1ȪK]EH;ø)etyUK8 KJ6&m5xЎ,vE̲ G5ÖN_ q^y+qU(GqFK8+{HX|/Τ'w#:2>~!#3TH͂@=J)P=9<@~MS [OƘ&gdZNHVv)rE!$l1ۯE` 4jT_`O#Y/AVP gW?C aym});>?JNStf\:`tc}+}}s`s75La9s^fuc=O4q_y'[Lb2>>?*kھLZF\>+\q Mm)~3."F*^ oZE @y"` XG @L'B۷-@@Z{(?K UFTwSS/FmeT`1wtN/ԠnxsG]R]6ݳԧ(ۍ6~u?3!\#7'e#O0Ŵ9 wpNsȱ&iUĄԍ!{kA7m3"Z^K]_KOH` ,iwm1^s3ē3Mmm!r s~n826b&^;kj.:|sM׋TҧK,O%?e:HjT1UZXON>g5q]׈B`]QQƹR9՟ [a-fz槡3n7ۘȨMdON P |dqR6#".Zlc<~\iDZ۬<] 3 ΜwhZll?Q_]߅>n~E|޳YoBf2zRWVzp5bE6VA?4|'hoQp+K3#^Wz^ngUvciJc| W+kƭ%cYk&M@ו§'ϖ6YǦ1bAPJ69V hHnWr+\iݿ2U\\4 NɅ;j5qMXg8,ԑ֗\eˋ`z֪c_+po|JKW۟Վ@_?aj?jbFy8mG|⣨ܺr\\IqӾ)l<^:s߄N9s žցk.,egH96ŸD2XiJ ~w̭rv#w9W/V#>'X*S[ '-ø=*ps.4'B5]i_Lŏw\Bùybdy'u>~{{HZ5I#wBB=RHd_[Oi$c"A[}U2{IfP G^+b7]g s#*ٱy 3b8a#Wʶ[!Fg,rU]k]8L$Ě;đ?OL~^jÌiL@>,MFRDg8Q#^iwAChU<!\Pñ,f9>=isldO}<*14ӬH2qP&-m]Ʈ) Oݭ&A]w3=- cQN WXIN;QrGِ^Jru vA+T(F'pks~uq8eFF_ّӽ$ҼB" Qd+qes>4O$cxJ>R]>._Jx؀1ʷO30G ;m!O~ $jas۟Ms?$]e*1϶3NjєS+/._{ʮq9"-WUksi.-ڨ.r?_++4l{b-FO+2'UP4VB}Fp0j.ڠQ;rIs9aS/*jmI&!V2 A-i/j D@:{N ^q"gkf˹@5Ie+SNǂ #SD`'Zq^9 !r94㧻.~x ZN|8?LpۍAS}\&ė*Sb5BiH> jF<{moyf?0A­Sl7{wq*TbAsټ N\v8rEd;&O鏭l բfڣm"W,pC8M\DJX[b[.IpAkHT<++k|\SY:.@קj'8qs* C Ė~k#9R=T~]kW,mԐw?$KU1C3w8$)SA}5&=,ӥ#P}X=[l~LjHʻ`d[l. ӜTJRq/Ȏ8/dzo8BlVȊ𺲝6L?v卜iRHUv NTw&-=1֒>Fm{ Jܣc3Ao6\UAK*G KZp]|j؟\ҷ?LP3'F‘.l~ BTU_pd&~Y|ڈ}]nq2ΟD9Ōoo TPɌϸ;Uh,[uT+z׏-`P,A]Tlmr<`(_8Gyt-ug;#>4;d-naa%Gn4#}[VEw16#fSTc*e_c{aޙG5*:wTIQb>g~TNԞ1ftj厕Rٜ,CH=.z⑕YtgDf@A4x8\`+jj 45dv"9YTV|<6QLⷫ$ n6P9WV쌭Hiw07 p^Kv r;չb!o}[s5Ѣd̉&Z|3y:8d+"+)w|5îWSbp\xihfKH(\$xWF۫?/Z>ogLKsu{ Ƭ}+j,|cDcP1']`r[b?ƒ7#~pTz,'3hYdMZ&a 01ѤC!lFN>DeBsbprwژ%m G$tOV'=*@8Ϳ ɒg7 kDðf6!rGfLǦA#0;͞K5m\Z>L5q2SsukEl)VNER_U}qݧILw0U tcJc+oq` G /lUŕJKN}Q)6odcu+#;>9 ~z8\:c)ӋF[D18`1Os\&Y8߶@6=zӨee` j,Bc#*XVv#m]VJwQ'o,8(wDBҏKvʑDm^Y zQ] %`C~ =Q񝃏}GDh]`D@<@}W /;DžU8.8ݰI#c[\ޡJٌ.s ֝3%>acL{`~4'Hߩ<|c^'(*oOĥڿ҅yó465B7*qɐ8i+EWg6>g!M|uoDw:̒? >,j#rA[>m3qkRGHYxڗ =#"B~bHUyV/ V9t71s-f r)BI`V¸..-SjJI:J#'zX\b˚ruO6QKזXj=CJBU|N0100f͠bF  P 2005:06:03 00:20:592005:06:03 00:20:59< H  uPi46001231143456HjPrintIM0100^R980100(4THH          x!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CQs\:Ajm8Aː3NgphG<nɿ+z?u naӥv5-T}\|27mۃ_ю\?Z⏲ȴSk/[bG?&}poGτݰtfiMSoy|zR)_J2%ݼe$FaG8O+M|)i$!IbFpxsY5 N9T_w/SiʪR#o;&xQuw8<<{kG$cUlwpkw+!E%VxkOYݓWEZmao}%KRA%ʯul}'LgkJVe}zQ 27Dak|Qx>|Gֵm6s?u e?9 YP6 'M#rWoB]NX7?ӊn.~pg@;ӧ6>l3 _ڷbPҼIYYH'N`wLHa޿k| K-&n.QQF~dץu{>+-^omp.8+B@##M$+yooOLYN|_Oy|MKcsXI#Of%rSay$ՏdgiIvI$yUYfm9zg5AE{JZ_=0Ә'K=0;QL67V 7{DHԴոu?%G)syҦ瑾g'7ğ nwL^A+R+@@$7~*|)σ{ #ZYDl@ڽ<5__[_ek*mX;xMBU0:sz[?5Ac+ias[lKiaF 9?9 iy;'鮇JMs._t?fKO9A'`csԟ\LmMrfG@ޭR~߲n>ij_ߋ4,[@*C*ʌc_-~?t/ß>XeҼUe ,t ORի'c[w{='xQM'a/pH])_ H'8z 񟏼_ch2E{Þ! )} r s]V#xJwtt|z.|-?bï|I n~jjUkgJ+_Xh }UQmˉ m吰=k7-PzLJkV ~Ѥ\LVn0$#ڠu{(-p{cR1ؖ>Gz5ӿg=I+{O\mdטḝH5?Wtvy&.WR=a2w#nr|?[e'|!&]K%<-# ydc= eO|:MzTn-jO2x&FK1ѓNX~Z"[m(^,K$!81rxoضhZ~y?\64XR2'pT5ZI)%Ew {;>dA挛V4s?Ϫx3TK"k.#ͳ#<*8W+K>vڬs^\EqXK>Lr9kTTSewfe?bf⯉>F_mac{uz+-Ӝ.n*)uf48KԬKLdaMΌ Gs@$vsߗ+y^1KFy+vxsϗ-FPyGQt%*#<.[$EiFpVK}GCeQ,rBb60.W#jM; :<;.f5-ͥxⷴtqw+j;痯?g"_,> .5Փ魒;PB̩ww/NBדaV)U~~zNro˦?| ­f?/$B XI3!_4| WxBO4?LgqxIiii,Č倧1l ciJkVCܜ%dԹuksįլbm< n7QI9fp7r½g7<}cUG:>s%KkKhabfK:Pl=*!G^~cn2շ+=ϓkʦ>vM%c55)IͺH?D o~ӴU2O*D0I,+zFk> ?Yi]yyj$j[%#pW(S-ɰ|vmg>$t.,nlR-nTMkTZ. je '夋#XF^F9}M_WRjT]~&}F9<[Ko k_<5hndEh!U;*Oʹ5OT,UſK>>/xKư^ΙxK{qm+QDrXx#k uw jLL2G\ןz5^Kc- 7Kx#Aӯ=kA- W #D}KS9*; 6vM})\1ܓqw : yɅ񷎕?%ύKoy^#-td{wAب(8nd#t~u,3tҼO=m\I$>9'K' Y0Xq׿xS g׃5}zòDqֶ`e]Af8 :W*{uF4rE~זǟ~K(;!iyn>g,FPc|{~h)][Iy5赒t@ r7qFkФʵcaR]ZjΏ.ݼ$XL}M}/؇h\Q (kՒưƨXl9bHGW8i)JR!k[%gMmKre8^ >>"O:饸K#?8NMGr@\{+k4S47C;m2쨽]$?|$q :7Cuׅl%7UGJŔvNF\ \.VyeXKcl˻c5Tt8Ϟ'Wxǒ=R]S;2[6uPIGP<=E$o$rYf*@q5q\6[YߑM?V0ʠV;5cx -ֵ( 29Q}/<|m~'ԑ-#LokaUsrO*kS|FOlc)пkRt}a;lDtY٥! ohV_x ӵF*F$Ewy#)GV5atpI:Ԛvn}C?dG~=|1x= X%oLA? gۧAi/<'u?Z<>H&v8 M)KS/8yJ3Eb*|5Iewrª_xzd;{b>v 5(2w[]RZt{Y,$ݴ"f3@R" ?);ZSU~[ _^rrR"rN'{ί;uB 'e$\9_+OoaKH$߈>CO*H.[Ndey>|Q ekyi j^5{Z(^b,QnĶx|ԏq:O%Rx W oozr 6V;~s&pIvYm9aDTQkG6հӏ³.m-k"5XSGi}18@'ฺc+Riy$Σx #w}kGTv??n|N5O`YhCv*P-}9gܨc' :ֹdk_VS_2܇ox>'GvèiM|eP1 1t5*~~_k*iL=|ixDM|rdAP]cj+Bm L7ldkK'B ,f(D7`!??pI٨;>g,8Vٸi~W= I,Xb7BB}=k* ߭<)4ͥ:ƾqE[S ~:j!`fa66D$[oU}~e g^.f[Fq6v!đ'O^%#Ȍ=;z_~ZW502?,E%QN NPZGTxb[|C? B7ܛUSHhe 7 rYhfadVsWW_#|4QȪS/8%ۺQguvd>>x ya;@41A$WzK5eywr׀!}b}FbK˺.',IQBRs2Mo ]nM'OhCm8heOd|2Er\$Ŀk_Cx޿eBs`|, qJIK Q[JO}'{ү9-Z뢒Z W|P׾ 5$[R0#lթ}.zp-ƠpI= ƻmKEy/ uic)#'x|VbǮXa}nwy4։WV|%0|>t6cxL HN sx/>'WzH CF` MTHy#q<72rAqe|+x<׻gj:++7DBntycwS)@{\ +u/l**k#(_z ׾"O80y8gV~!H7.5rZ4j$a lAQROeO$ѥ};OnP?th׹?4Pg,19+-u__ 4w zjVPʐB?zaJWb({*K&O{jZ/zk{@ī? oЮ|MasmxKhZ-Bo3:mq$.́aOs|Hϭj>2 h5KI) rgͽWRJRO1*WhKΡ| su{O_x~=: QwvOc\(Wadxi<4n<ʢ7Wb3sycV6;OypR}+n}?gX'YX^ܐRAa ;gGLf#_Nx#@]~ҼCkzZ l+F=GF}>A?~"j*$X({QYOb-0j˞aN2+oUWR}o|^Um_-}F  qiZIDf H7| ͿSmĖqZrVk9e*S\ө5c,㇛ѪG{^Ap)ui<4RC2_¬]aGk՟;jiu;Xb2Zs98'̎D9(@e/NF:72'N_ĺ]Սǝ8$28\r}1pRi'^6iIO}xVhUc%OoW]Z|gmg+(,\B*巌=F*S^JDZB|PI8ogۊdw\QGYz #x<Qњ~~~V|6% _ch儌#r`3 ~]`yp6(FTwJE̝WQ=Uy%pxV荿Etۍ2g+vh'v#sW|Cks1^vCpIuUFtMxjrq7>&~"ok% ]M꒏g/왤p0w8 'UXץ QnH#I$;Qg3ڃFժQeg?loP}S]>8-aȈ ,?2ONw>4RZ𶇷X/Pp-՚RF#%YU`]@rMI轮׻S\ok+Ou+k7~"Nwuq=Ń*oW]+.G }9$+U1+ EtWECg;Xq m 8SF-^)# AL"aȬ~:;|zu6?E%'fB =>.NAx'֡<#UG?⏎=|MisknF11O r 'k߲Ϋ}Y<~V@H=px'USMI|gAƼJrg|R)>n M-"SC#349pޠ$Ӻi==| hyWt_ɘD_mk 9T( 2lɐykƫ֗ls)?HK> mne5+Y]2VCIR+4χ3]Ci#k䴂m6c\^'Rry'~ΛӡۙS\{htr^9GD7C#:EivstJr$G|3|Cu O|7vWI SUCgsJZwmN|6SN8ⶣ/cפ=1Z4|V|ժ;_q%'"t:g;d96ֽEeKХ%L\?Gמz'NzVlaG8T|_ip"8a?x&]O}wKhk8}zג|E麶mM1ȫl:Ts3_)gQ)7PKWxG۷Puk6ŴٲSB[>H=qH6ZA G{xy7(&Td 7Tq|>aRU7^qnVg|' KMCS^ӎSpQKBsn޹~3|[>O3M_V2r 6u0Gmq!vײwߘԽ8+jߪ#|XX)ԭ1&Vd#Hc|A3FIGHvA}q_Wu ?{;Le6iѦ3Z;#3c I$t6v~'n$F)Cf(aKM}]ǫ+9iZ_]P"U^Bh47%HHÃݎ k_oǚ||.{j"U[t;mc\9Z *(N1}~S;v3Jeqm-z'r_i$ˀ\3Y|3󯦠F{[$dRxK4&y,XcD4H^;I'Ǿ~'v?|L>/$ݑI(9UI=5Aj?6]FƟP-mvb~cwvE`sq\eo=Ў)-78:X6F"\rzR(N쥭cΡc#Gm%S$+KL G9O5|U-Ҽul[!eN\*SMte؟e_]+z_nV3ZQn5[{9۔fR猞H>0 rniUU)&cb=@\^G٦±ۇ"{ڲ~0hI hϞRbB~Q/!;cn >Sk,*}1̚]hӷyuL4zN푦9Ul`T8澼jŞk([L+jM #p1hWNTw~ֵ2*uz>['z'S>: o[ƌ(YS1\s0q_0׵F:+72U%v@FT}Ic}q [}7z noy @T9F7FEMY+|'$p5 8'YoOm=MH;?.!RJcY,^_=T/{;ݿi7j-x c^}PKHT³14'o2y!g;@aS{\q2"ȋʤ.7Y-82l/X,m'<&˲&zT1}i Ac*hmkw ;d;O(?7Cw󬚶gkDGSO"YmnOBkW{?";k[."WAӾ[ʶ Kپ>0[hCHG+АskϺ9[9fnp5h@@)KC;3n8==Zxy+Dg4?TO[68 ; ZQ*/j-b-p>fo<;]9?tO(X8LޅqwQc?ң閮 9oodߛ__{Qo]?9ݱc',p=B[{Ңv!c4$D0=J:h0f둝ޙ!-uIfUq|ISԘ=Fߕ=%M!I )rxSņ_>ڤ (g@8215z&h72_Fy%B lT s89#5SɅc"BvU)7?Wm E4aRʥa֣wK I+rXڥ4Ecb0s֌vWuTF#| 4I.ıPN|&,+c`2qPhǶ[┋/ ||Z%܄:p^!Sf.R@yKcʚۍ2)YUPfS fu15iaVƤZ,[gsw'H5tҬ&Gs֨r31K{[?֊^mR(#9]H5RdURr #zg/)zcOR8'4Q0W鸨v ,l}Q떂Vx<\룙v+|`\0{ޙ!4rkt=~Y2KpuNiRq&P\m]#L>̑@6ߘhK*T/@R~2c;pHǽ+1q;`NyG)F3I>tdgYi(Sl5($@q;Y LDu'Ұ"IOSӵ->&Jo/\qkZGpd==YU NPzc /Ѷ|ǥ:#4'!1֟tcsIɱںBAG 0Qzo(1G@*rkt$֊W+څ+:uV*6lzS[H]F% ٔdVƫz^Ƹv&hߘcdTY[c:}? D$AOWM&]2d-؏JhE?+$Q'lnH'=NjpO$-냏UnSti&2Ao%_ήq}_#ΟJ1t9ʰ1y#3)?3[ugUQx5ʗҿ\L5\(v00G^9۸fb1=l>5e7HgӉyLfٵ_ d?gi"C×CGy\ba И@נKi]rXԭ +tkZfI$ I$tLMnQ=[;;td([az9iP0>RC']E+FNP68[m`cHffR0zW&gjjM%VK)')D.3c^|-荌5.$A޽h5#dp {4䍋R n[x4nr` sFQl􇹃Pd `άoq0'$q6,9p*wcQgzSSk  8;ZLIP($n+?z0ygIiR,4 w=q*20&u 9X+G}_$[2T)4`9'em!EʬLNEn+xQ(TvoW*̷@ (Y~kS{`F>d:i2n ⟦0N@6:JY c19a q^9Ў΀Z8#hN1A}MAb/9yǼ4HIڤDeC@ڋ1 LqUH lRAKY 2U8 U@614J av,Unqg9-`+B%UWZ|V #1Gc.m@q-&G%~d \m^E(+Rc=Mi zFdDC-OzV4ZWfs?o3e̺:AZs0D`76́HK +k3#zb@O\jW"d]ܬ9̚'3M\g%J ,-.v/"9=:zӸ*d3q}h]Xo櫯^eiN:M%=ԣb*c$Vg5˒N׎04y@@R DUL*B`wQsoČs rGX-57+33X`.pe5φ ]޺#]^L_D|lfH.#5m r3ڳ8MehgUAj)Ru9A[ѣ<=JiC޻SKZ oLw6ܦ<(AJj ]jM^:IL$zR2w-E1MB-Hq qԿ.1L 8PI2:XuDӵ _.FvxgP]CyѼA 9JW+leF?*nN:*ypC!@G@{TՉyU54uQv=+8ΓD}"3:} hC8բ'nсGjV<I֗4`+{FN ։߈69M͈%'a^R&;_ܜmL19s-ܯcgԯXR//O*wܚǢ3}]V%6L)(ʼn*]3\1)º˖횣we=VƬ;g5ֵq54% w9F9/? ȭ]K1 ʺcD̑ qm"?Z '?,@30f'mZh18guώVx Дz0wg,Xg;!&r9nǻ[ӥ$7R9$]%A†#|.O^Rc H*(@3ֈy՗5cH~W~u!^:S*֬8K%)]I]9{tޢ4I#[bt~(bt7Xu|/5rjZIbx؃ƙIEMt=ibN5w$ҕmWߨ(P9J4NOʫڋ9@G~ƏnHiFh HzPl.bMOA0=@H?Ea.A_JDj\LDy6֠LlI 8^keޖI[ d궮2#Fn lBKt7HS`a!jǮH*2oRX)0DHDsh3Ef i+=ū%">~B>\,pE3>w1rT#`a9~nR#LgR1 W92H;PA~֥ Q~fZyW Ql` g?e5]j9^q6rvڐ ky'L$*Hjq౔tc9?:x 4KU~K4 cSl crЭ!sPI(QHj~\>h|}!?wMʭ#v? LPYLšI; i1Fയ.O+QT]m>֪`JqiapDF-m1Y3X"n^eT$_N[:T5rD\`b=^OnD|c_JO\,c;4'gNIu$"㐃@Sw J`w VǒWQ.N-unN,\*#&iXY$֝[ Ա5,J WKͣx0s 2 =MtRi)K]E@!CNTܚz.!>Hk Sfԯ]7wiI9eE;dNmtYb*R5cΏ&E -v$_IpSג?JYGঝ7I[KN]?)S?փK`5S`L ɿg[u;#Qj(~AZcm0bKTNt BqZ -x 2MYAR˶iaR!S$L[11فEaOJD9zo@`#Ix꫖' R#˃1J>f w ;s?T3`hIۡlsg70qZ=>5"Yj+FĞmTxNmdO.O0j75\baA~qSkH9$u#V@i-WNk85ޢ^wv?@i& ;jgebQ6z,yS̸B6=zՌ`Bڪ֪ y >; (̪yvOV' wBiV[ZLpGmQEF{ղ o帪{kc9Zѷ-ZU P:UqHGNc%p*'Y;$~R3Jlצ]SxfXVF{ *ji$G+MuYOSqHf2J!bK19$Fk>ˑ½0,(=A껂>w*Op5eL^\d(@rp}w$33ֈdd j#J,T.?6pzg8+N+>ҭ\D{C Уiv8*ϥۥpġcUPW!qVӭe,-RZt]$sv&n"2M6?&>̃ږ: SD (KrS(Y֫lSyU= 1Uu N6Z܌ڟ jƩltq X9ULu#֬=K]QR 5s%iz$n6dc{f d"+Zeu8D˓k%2VeG_eSIBN:l(9/C:?`zS.hxԂ*qՀ%j0aqTG6}Fi"-$,FV,rv$ GO'5/n1m;' ҴW"4 tt;o]f1 Q@Lc"Tj_c>K)qeuأz~w]}Ob25,i 9D8eaS6} ~ ){E3MxppΛF0 %9ͬzY73gJKiG %t(Ve/ʗ~)'2(M>cfb>i06U Q*k6zܷ֫hOʸ852E|yS =MYTxUv1z[q,uTY#PF1gl$8QGF$k We$R9Nl:bt+cu/rr>n[Z[sYW,'gGGWV`? ot=R Cʧ>HZ+?ْl'#8^MLi+Iɓ|; ;J#!aoxƤ/4-Z֭cpJ$V+|ZDRX|tM;YFiPUHrsᠺHm̼î)d$#.*! Aj. H|B_+.0 歋ſbwvuOsȧlS8ռD!Wt5'}h7*47RT[@66rjrwn.02K(Ugί~NIYb&w <8&%[yV'ثϰh|G"lmӇ鏉S=H 5uwtG »NJA7w.$\@D|i\ eфdTM.9TStuiwʳ"#VWrHS͓㍚*${#qq%y>LQm}:.[ 3ήX1⣏+ivqH-0I >1|*vXVrg22Y" ,QXvZ\A Ȇ9aNl4rhrX: .lciv $7h:i~ӓIo"ݑ1{ӝ BB_OkYwֽ 7 鱭ƛqs]NHqi?hmeSהcW!'7fvζG9br6ޫkqs$ĸUp1+М/gV#P-l1!]^/mlX)UEA3J\3'CHp c4kFeqU'{ N;~~e՟ o'֨dT{)Wk4MU-u `Q3٥Q*aK2-ȁ?e&U!,I=I\'׸')'b+v=N@2Ⱦ1g [F Njlr-re$ g'\s'z60*QI%Hv蔷K#O+_G BHX!s cR[Kp$NN9玵7FlzT#4&|(]y8 g\F cEaG$}aUR|GWHrw:pޕje5K)-w$! ULJSv2njB1j|p~w ?EKFzPޣxxW¶Nd ,P_OGS6)zI{PG#-YGyUZk"iDzn~]kv} u?*ja:I+_rpPO[{faaߦIg8eQPЍ6 cojVt ;߁QT%=y'eMGZ!MDƽ5 uTbO3ٸ8Zo@agg#H.0?Syɢdf,VXpʶ7]6SԫfGːzg:eva/P%,[ `Әm^/y5mE~xw [ Aeye9 lc9;廇NuOnaVI(K{}>&e? 3wr[%h,+=PVG.[^ۤ#$CbFO\ԄI4&^Xne=᫵ E ֟u:`@<[g^.$DR2|qձK,:*?u2-xǜFT$w9/><֬La\ l\9a7OH<9'>R7\4~>lOxc /<1 #ʡ,.2v2Pp%v,Nr>'ojl@Es_F+gj^nHqWA/o.[Z, Jʮ^^0!mp;~Je{$#1"59 t8qI<FHe'< givijk1G[sv%>Coa>`Ԥ # ;i9cUlQ,|hPDIliZw2M)4>DԬzM Zj_-O+#LTFv$gͭͤ;Q"D+Tʤ<1i&$|<Ÿi%)MbږkI4pXe'P:O`Mƒr48Q_cHH?&UoǨFLXSEhJL7u$601cMOY"]ݛȣ@ y'&\u. kpT*КX :c'aInYdm=z}qkmX 0;߮қk7P m_VwWyjr^@[]z`;) E.VDuxb-RVk]T`Q; ėMi7Q0U+he"4 gnMkooyX4qK7H-ʤ~8kH8*&}2O3s0(?VK JkY$E,1U4[PB Z1&1H"tŚeLΩ7Ol.d)8xz䐡~  w}@,cy>d~rc% S$7fUDy U MN t,BDT٭r#Y,2~BxOo"1 }E 1ɔp\b8_Tgr1ߴ8J+_C%KH\HF[R@ꚬ\ b?*ЯծuK8 Hؑ'\٦Tﶮ$4K+;Keh`SY_BuM9Y1O=~87.ۣ 2r>oڅƖ4+yJѪދo@~9VrJ=V]z[ҵ)<[Ăy@hUs3ӭ%iV8Xݛ&sO֝m%LM:%9Jgj[)q ˎFzz6Z藡W`N0RG77:|}hO|Vy"bň~_ wȰ*_,YO &e>F;zYUob}^a,EY9uVP`#'q9>y亙1bI&l1_Esd hv`Nyrv\vNa.:#."HuY6458Bܺu$ r5gyw}_i$wb2z:mFe9zUٮq-˩=Ο'$RAђݹ$`pba_kYVIUܰbyn;Vw<9y 1#7Va0ܭ.>CYx%gGĞ>L:[;Tx|(\þjotWHQEa@Gn{{kIFL$MgPqJas.H9n]_ ұ͟ -z>gά cj"dh#ee*TEYn]Jܥэ1;,r/X?~y%Z0+=^]b @n,@>1ޛEw׵˦[ՆA}U2ģW\ߊyȯ{@[,滺ʠ*vީ'ƕ6T Fr sT99;b+ɏt̳{kcFE<Q&~WR8]Ff1HuXl)_1բ~'[̪[>tU/1ݨ v=o%խ2Ir:d/_975_e|Y{x[%2&Y<ߵT=E#i; LU.NkIs0%m9PrUcD!&'J]sMkks&U49b>I.߭G Ȟ{YϘ`zdXoAUc.)ǥh5iωIbepqܕjƭ0dqH-^]*8`ZX˒d}r+g=MI=ͻFWpr>4u}_G8xGBb3b XYB!sf>*N߶:rJOHϯ,aARDbbwxC<_ %I Q$wX^bS=r/+UH Wiڍ֙Q9CE3Zyr3~hKwhs5塆6g$B Q!Z'I'?r}I5 ~ k럴f9?,5f0FWA"nҍ>q奬oC>1󬇌,8cܚwN]Tu+ɪe.]{/0O )5+BF|euO'34N9@%OX~9fombY4˒|sZ{ -\G>iv 6^&q4vӕ8kX9?p *iNVX|~W&\::Uʍυ`}JA5APG1(¢(P=MOXoV@cpIb9l4mw.ΘᔖoB@#{]kܛH 7ƴ=/_]Bh 4?[4yĝ ңZg,z i3\kӴŴ wlUs^knw8V<.hXä^] ϓ7 ۇ򎟼6m`KMV$eF tB>m-chVܻd q߭}Zk.?*]ɓyexr-ZjzslX9%CK죕yZc1PqHowIOf_E_ķ u-Rb/I ~RT?D jK"w6tb1Byˌ㖂Ο̐,96?!{@mhLmX<|*yppmV[-"Bg{4pWNj {PhKln L_ ,ַrfabGEMQ)6>J2g|y-q+(ѭu"C+{b|Wi4BGST.*z]Wi*$UcɈ\q0'kRr}##E֋e}I%q3v­ LAҕmIwMڻࠕ#& z4ZJ ^pTJ9,9|^eP|Kg/+-գ%D|P9GMX1I21iݷ 7 ME $36| ̮?{jl4wW0g~Bv+i2d`I<#qw&S^Ph#ô۠Cd~ P Wh~ԽLΝ}ݚgXK099#=<>gk5jј]6:sҴtm&/>>Ån,5) OU%"WkCP#'`'[u G:UB/ q! Fsf[.<=LƷY$ ;h'7Eu$i`B\/AX 0_`wU%uzZDL-;Muٸc&K+y-/ƣѧ\s8u랣n}CzytUĎ=2\*YIB  :fN[h⍎%{0G6OR67!t۫L/w&vU>08'U7.~<-K1GOUxSm`b,[9{øb>[.r¥AE={TKՌ຋?`|مMYZ\2J@ $ }S Dy\zUbL¿Z=xsA)\?u3 \7db(4ȗJBٱfk>V9c1F\8EcPkԞ8He^]JmSMБ STmSau=uBlVm/2G('t첬m'9hϞ|*i29Gd];]EɔV3q2~XG%%YDdG-Hn}]o9+Tyr{M7Ho"³({b>(}gB</7@$mڬv3LJcЧV5XFsm`qA%hO(+w1h&i Y>x!-3b}))l䶛.F;cunmmW3)m:M%3$NvD!1?҉=KV L[yf&*X> hu7n9եa[)六yiYx~k]A%F=09 kܠuM;\-3Fs)s@o2sZ'dѐɄ ;*SoY*M;_z#=ʤOv5(ک nJhU-庹_V34dmsYJ[EO //)*իGv&|6R5sN |2AX>t5#v֫wM/; O*fM&öNHn. U˻aG^Zo"F>CU! +49~7 irJ)yiwPN!бXd=3J-"o4Kό*) > $(uene#,8ٺs0h6R-)3Vm?fywz$A(mNJ1lmL~#R%QW״"jqE铹 xNأ'PfkKh<|X}1`/&oDUASujy={D'ithG*HZE9Hϡ8wӸdo _Su#%ĬբsnE'ry?Z#ZӟxiX8RFk7ym0>X-[o@,7R63Ѽ7+CɧX^ZfE.>o{@kxYQnH?XKo+~C;94_ӛX9 $GAioiaQSąaTz R(2 1QJN1ڸ5F_DHc4BDkY)\sC]H}1ZD|Oxcl4kFݭ-O%?k_B%f^hR˩ۙ86q5aލ#?Vi,R1JC\9 WJqߣxJ希[3A*ۗoN5j-*Yn-e@u)ĚcYOqw4g_^ KOAķ #.~4I6Y,1`vڮD sq}*. nVud>Vq>0'J y%;?.MIڴASP|K^kP "tޘhzˋ[8؊#y"t%bzeڠRWYrS{k2>P0fm}}ڗи4&;fY&BzEP qiV O ZWvHC[FI/,dU`è2N҉AbpkOن 7,]-G[M/>٧j1KE+{`SVJ>G»XxvIjǥvqJPµkt@{Pfm(~"XhqCP ޻:ܻWf9`1@s6kX!~5v4']`{Pߵc O€րC{dF2T<IK=*+`@$yMf裀>]hjSڣϘ!ArǙ>TBl=#͞yѝŠ,}sڽ1gD99n=?qR[*yEg+kt!5-:VzA;W}nc :Wq s,7iqe`Ab$UsZx[mG;o[,5;"q{ >eZ:gUZymEeg Oʵ4E/g7Hüb1hڥIz㱭 1ⴻhHJfuH9HYjٷR1g")nV>k%-eCz3@cj)sR'5t}eVPR bUO9!I;0Nb9ٽޙ9?^'aDU/i%[KOsPTjxݿuGTRÊ51Οek$.ɮ!j"0~&.3/_ d? }'HmOiZvnj"$s1oZI^-nUfK:F?Zem{$ځff؜TJLG[HYǹ#j{sow52HfH(>;Q5Z>-1^q% >={UO׵Hme D vPrwƒeFSƶqvg^Y%/mTs cY)fuwH ;:ɞp9UWmEŴFouƸɞTP^y?*"$ƾߞjO1$E C؎vJϭK *f˛ /ʬ{Tr#vڞ-ix1Y M$'g!| FO|Cd8Rj356iXyb;M[}x`{𮩼դt%\ cVǕ,7NP`CFc} (ϑr:P( >tb>t sCcQ\A=+=("q'@OcG$P| K"Cy*,<6=)Kn-I8wX]ҀJ8k9,sF q pzCz h3Jbbr@pI5A$uB# m=j6k>%cr«D?dtWBC$žTx@IML픍=@6ZM)DX5+Ŋ<{2D߱[puI F֕ |,.żAVY;(kHaz~]-s䟍0T\AA$/)c%煰3չ>SMYf0任, FhK[u8EFs;R|;.gl&]f;v!R֡wbXxnMoZӓR6JZҹYo_U(R`uPKɮ]i~(>zyj෈\GZ+_lᏏ>/+hWEOTP0z  6;d籢z Jo:&s޻z`W)23]+Q3(2zWsE6+P4@U[=SO~[4ʟAճ4k-JщGJ̯q$#c^L~UY$o6bMz> Q5.SF'İ2UdK~7\5j*&|5gz4џ.YE=DI<%w]|xLr?*>_eZ^fЏǗъ21]C#3PmWF6a/~E{8u0?Z{mq ·ȃ< 88vFr=k{2PSܼU2c;˧"r1W-vOqsΑ/'ߥ^mgE#ҬǩXHHm^XT_%\—Z8aל 뎒J[}%bږCQMm̑ a>8PsetHc.}j^ׂ8~d |2iާ:e+o 0? ^懶Gv$lܱđ}KN-lIƁp<Ն>x!rNޞquHDs/_,rI{IeH 9*ۦ[̧Sd~KmR2-ozҬU*F5UF“,E|l<9tI X۸+*F"8V 1pjY@fS$ָœr cK#m\*8#0y/-V|VÈ&\sK"*HSӔLi.Qld?\I#Nr je5d5AAy\B pK\FiHќuk4a8uoxa$aK Tx.cWuLx6q66u )'*KBN(&df;>f&(s,Izd򄿴"Q5ot{՗I䷽|dfoٗQX j  ےDs>ɋx r5=#Vե΢bvGK|tr h`GMGym.w3K1<pwj:zxwH9E ~?RXLč~?%N6;QYyp Z#_}|K9)mŤB3A֏һo*aBvbv9°E ?)֣|cQ"QЊq޻FWr*1yFh k3j| 5? )zGzqǝ @@DN6e3K҉;Ps)/7YW}'KAkw2̙ʶ7ҼOx+gv'럥4;&ٕ\HK;us1/3yTWLwAjrF[Ue(!lb摿iu|>ɨIu9;3N g ]MuW9۽m= @Br?:YYrDAO)]q:4MdgL~ ^Ҿm_Oe06V~ .ovP09FNgIwZp٣qg*sB~!{~5/)6ג9%gzسZM&7V)0= T-"2EN!򟐮?VJcf}ia1MaW=1cIG^qcF>cE;Ռp'shC YjCrwF0sY ԇ4YzyCZMXj{k~/D;oYYDD  gRx%9{yy|E^.-^Hr{C/V׈4QaO=MIFT0'u^ԥKYB(G_lE'',l1U8FNbir%Io*gy%XM,jj(tm:$ eg]|HJ36b۪vT.׺h! M } *aZH]wWo{:R%rChebB=HONلl5;ciK-dH&1UJ=n5"痔snp h@b̰|~p21RV--*Dq0:NyP,//+6ƁUE J KK 9F#ͬi1uA$m3џV/ I:FxUQ!2PwX.մ/u'+P}vB!?t;:m+QUἴa%ЎJ5qv}uq !$lV +k}GNWyX۝2qWQ&ʓS=F;>S|t4d"zr7@U ߧD<ʚ[G(\_i}O`*KIfP5 gx-s ;zns4$F̬zJhc?8v8sNÊ>sfˀ$?\#A]fȄmEL~iUsa#D1&OXW/ܴ\G}j[HLϟ>L[ k:ohMcOl盔?#^nhxp9F>5euO%#^I~]~U䜒#\eITe~}Rpβc%Ml.L [="/5*fڰ 9\9G_?B%cS=t(B ~_R;fway|RouIhcjsڵEOHW7gpά1G,]$o6Z u{/+!wf# Hh|X J_)SL?ye'-İɱ9]qQg]Mt-Tf;v ]v:0oswQDzn uⓔ4xut]R(9e8N\27͹ڇ%j*q'QFJ'Ws9{9sc.:jiwC@sAư>B(V1QNGj:)'|V1YFqdJJbXņ7#ρk;F|ؖhln =k]j=g~6s;f{֡y{d y m*Zsi'hnvoik-A7MB+C1҂Sߵ(nF{Y Gh-ٱ;{Ub1Nw;Гr,o>dUJJ)Sc[ksɽ>h0FԲ*@oF@0(Lt499;tAu|0ې&4'f{O1XN |eSVA2L.'zkwG& TRS Ӊ=y} 8lQi$;Χ-5lncE͸$ o5i\92ݛ0kCHVxp=Ο [ 9la[.7MZ<x+0&3#u/gڕ[O/a?8?#UR{YLw *EkZ.G~jZKjQ(gR=߁)[~ v5MmѢs5?u-.RҠ'p] m޷k{hFR(`GB at]/c\ߥ X^|9gb/.u3~7UOu `Ld>Y j蓘.USqyĎ#0#]* :AVu?I4&Fo3Yzm+7Qak›ƾ9*.H[Dtz[[DR/w~y 6 0ܱG\vg =n`~'6p&BqmW,*ԏS>kԟ_[&'z^19RĸfòiHgVmԁL >`q69/L+cք$>|?kIn5.L1Q7d|-jWDRYI"rN_%CR+sܼߥ[6K"OgC6'mWsXzxFv; 2wv1Cwut>]ՠ K${φqL$WPT"Z%V SD+نX@_"p05 aHWzV2NWc|Z6GL/JJ59eP!"dNMҕ. cqx() Q'D!I]Ԝ )s Q1aѾ"`-70jV B sdqL 뎔eW+l4ɃE^ҦonލG_:jVi"-swWS};zu &g&P(==JI|%X(=O3V29Rį#~4YNX@F~VnL8$&7_kK>t6\d$L|{c""nixlBQ}ZX|bL UIp|Q4 PXsTS_Zf Kogo ӆ -ʉێ;5[#SgVdNv[ Rrj{,ZjV&bӑ htDlц ̣;cz7p5f+cC*zZ^Oʝii-0dh=8n!I-0Xt#)UyMtܾf=Λu U}v`#sw4XdKX8$#'X؝Yjotʨ\ !*R;V2LMݡ}Ӷ@y|9ht:',o3UYwo&5NQi Z G3s*2k՜~BoE;NK l \+ٚ ȌG\!ñhˈ+<{[6 ;`GЙŎ,+[ '*};ViiW`{`zע-#PԢVI-%TpdA/ҜvImװL2 3.k#] ztq˖5a?fjZi;qRx;C]:CVMq,BF :*Ý$gG|'gZ2AW=U{bv1$LwGk.7\b/gl5kKa&rf+q[7C>Uuo,VGǪn/ˢsM29dG٥ Ue'F1δ)$l"9V5p/7::ز\;{y675p>k=1y7c5ߊݫ/Z|i:澊xŁ S/`Xr $us=w*IGnP_ jGx"M:W8%M^!լ,- _ uG# p:`\I):Mk.09mhWv@T\P*O)F6دּFNB[1Xp>WYSrqR_8uWbwv h_X4u'V.$,Q4L"29;{!bR)|!6 g.ba$djeVJqr7˃ӊZS\dp}UH8+e=-D2$Q侾6Icn(R@_=F$pir @}+XP1)HT?*\h6b9's<˕zUWrva'&)\zg": 1jz?Qxrԝs1q%1Y;#^R2[`!fkG2($jp"Ē9L|ZZh,>$x][Ŵ0O*SP1v "?Rj,AAuj&!J+:R3߷ά֎e:j=M:4HB?x?]Z'Tcbۗ>BU+WnݼI(,ay*iJc~ZTanaf`1Bp1j-+U}sV}+Nd$J5KAni|4sjJaP_3DṠi/dyNi'ş ɨ* ׸\ X=+F1:G)+I*٦ bKwxe,ycprpz=,H5]oPfDHl75e2EbNhe>Đ~" u=HGcp3)}1Vl#+ij?zy`|1yvsTQEp]Xq ݹI 9?*w} ykVn)gDI'rsWv)/dnH&x/ l*[E*|?qjPB CF1S.lUw4bꏼy8mk}+6 $1D>+e&)o0~S x6R 8i؆yoz>̈́aQIƮ,[șQH;zddZ׺8$bUG@*#GGjuQgV] SHHqII0#M]Ҭ I\QcJE"Ƹޣߥ N9Dr~v.gME[Ȫ=H[LIh%}Z"|E+C>Xơ\o*N_r 3<|Cf6*})F'88? iiVk9{V^L4(oKl.G"6BF'Z q¿ҨQ/,$kXiZ(2?-Z&S04"_KK}ML[,~]yykp~3Jc#kydf Q:кdebU}>VśDc?{/{M.,bx7\cjѾn<~2O;_~LM<td)5N[('lt֪Fl=c[(Iy=k/⋎8J"4bF\T]MGƱm|.%/g͒0C`w)PI/"ƣ>s{ ݼ?bQ-~̸Z-t-Z? a˨5': W2\Yᾋ2Day\ץuk!KWḇNNk736|X1GzYF̳|vV lAG8*jݤkJ9m;.q<$1ə[>Va,~GsV9Zͼ1 2u‡SѴ]h,P9q8q=sKk{Q?CVu!UyFFӋWd5ehs&|="J)缳/̎:Ҵ~&'YGr`jkzLR,eY$?*n~цhS0NW)8UDžxf#8w9X84Y̐FlzbQ~)+4xGXi{qnQ Ėr[h`eRJr ʤ#ٮEK-3򟽃QJ5[mQKO(ݷO^]5VGLd yzө*cNl -BO ZI:vW"k)^[x=ŭƹ G8n7JtD9mc}9RIqLd9ѼetF'#+ߡJ ͟J  (NǸ mV!-F{Wz]ČoŲ{zf؇ŤǪ_*C2"irtZc鼟T]Czm?(n,! crU|~ΝL4# 8Tf>U8y翭Qxaw6A>T/7GfRsǥ!Rϧۮ*0ؙ5U8C^<u 0tYqFNi=shG@W|W4z- D =.zVGEXI۝}ENXd`}ݿ\{DѠ&LC?tUpDqmח*" RA?ך={9VK}[OvyTzgUouP9.@>`޹$E" `҄18h<csM3u (#e@wZ=OFQ1j#]*c7/.1l+v1~-ƕip? ?:ΠTse4.A8e&S^. B ǩ5T]=(2eF;&Jc]1$ΛHsB͍HX2@[Eҍ9Q#&>T)|2W-|*oefv4%QA#n}vt!!n9VݱH9Xv=zѦuA3g(nM]%.1I'mXAV!2zT')!z6w~&蛅H;zS.E0=) "A%v@9I2IޚǸr:V; B,NBix?R'؎PڎR9@qnAZcΙξ‰=Ԓ!l:ǿjd(nm1\WQ D3`Q#f\Pɥ&SRfHI{օ4z1(EP// MP]r~Ue8gVs%j!ilLNaq|R_}sn٤%E9OaD<ޓtc֗.(YmcMmб> 5H;ezThj0E=ƞ3٠-ʎCP+P,1]`S #RY#Rߕ9y&X!`9\r:װ1p!'~zp)ۄ=NhmG75T7T\[k˫f,'#'zg#gjIM+.²qiL! ~)H9XOՙvV]Uh?%Lƙ獞e@9 yԭiver''D`GBNՀh|L*,ccO crT2IF :7z v{Z@m*j:&$# y}G %EK3?H5scgb e/Ѷ܍N<4Kl46IZȆ9UY_b7R~?đZ\;Z3$[=үZf2-,sB1ќ :]:8O}QdBD\wJnEVˉ.YN%hQd*;lmjoo/l쯭yLj]ܶHTZoiv-'<#b=@h L+4g#^n*T{H%_#_4}[Lbᤙܸ"gҩ8Yiuif?J/~ KBT<ʬ?߅gYݻfVa6VSc%5& O+Gif6d9omu 9 QO8.G<=Ɩxݗǒ1|xOˇz#4X*.'=qk +;j>"m[/j7'Gn5 l#ȸOxQ2ķVxBO'01c'`vg4CNg#-)u"v&Iϥގ\mR\N١8'$QJ(f'@%=ɤmDɦlt&ϼN )&1؃TVMp;cFmRݣg tǮJ3+ͽ"C96X;~)JqL hȤT)lAIRw޴\32|0X qӕ3jTMM6}.8`w1TVR\Nw8Ⅵc\btD_C<8Ç Glg-m*GO!Ve ӛm>W,:_Ziu J  4O<;8k)@N^4:u:[yu ~|ρލV*zR Eq4l:k?1_JL&Q3\71`s0E<;u)2Φ8+L]@,:g3*pGYL6QHH9gjkcB[TeT30i? ;[:\q]-[cX(}j/CiZ;RI{L 6Al3ҽ]d9$n_eaUi&ҊםJعu_GZMDgFiqhVȷJL򥍫 vcf-D1=H75SϗzcĜ+pc?vdo ]jҔd9ITPp_CD:m #:+/x[Ԭ/DzFHhbW𬻆bid{Ѳ~>u-n[}q*M kn&uFW<>c-áÿ*]5^Il3Mԑ]+`"of[\+T%%g9G-TD4O>ό9;nq̖+E8 "†$gQ}K X}GUU=NզYymdır {q7'w⢤k{U"cT-/m~nY0`ڢ SIiٴMt>HRwںcg#H|oM>]i@󦤐{U˰OZMҕ H8|L¶)&#tSTBD4wE# &:n:hklrcud=SM@ QЁϜc^^ 6V9j'4eUz#gAkHgolnf#2O℡ 64ڵ` .7qFT]iK5Ķ捊Q(v{N ^݄QUs6VH/%OǨ5u|wm\ѮEƟs$2b:=zD񿇒R1>$)mA"dyo:t|Cf0v*=\\/>mEK73_B8Vg76{wz& ' +wn@x*b:j8oI "INQR N/EGLi ydJ|q5!/ǂ@oyO,zek:EPmyX>*/][NhfVX1 D6 YpVG$<5%es9Jβn.Вْp=tGͳh|IqƆ^2{ۈ.4aaЊq].)-e1TJ<$FKE+X<9e#!0AJkk9L?. Hu rz0}+#O0JQL,ŜLReGjԭ2_%V<Y+RҤmQBހ*SJ=(+6Б((mQAʰE֔V3G 9NǛ9YJٱ>ݡ}Ѿ_(ڍ'T]Z7\Qi-]"s>qYiNڝ# 3>o؆#2} dsq6F-(bg ljlԭh9"vAOʣ(>hM?f;Ģ}/LYm/vVY̗Saz ӭxJ[խͳ@ w+.N W T}͑؊J{epHzwbZs]Zi4D3Pk9,oʧ>G==5%ӮII;znݼQZےdc_!ڸ'G\2G}\s\ťE񍬏Hs:8y-ObdRٯ Eq=<Qypl0 XTtPzT}s\b"K)&A#ɰ0|AUttT_jŵf|?,EH ,0`ܥ>Ui:f/$hהZQ5 w*yf;~n w^\XߍOΛ.äEiM,\HQB=[TC*"g/JqQpіcv ڙVh|1ijy=OQQaXH#[Yrg?ZΤznSMs<#͓Jɐrkю>]7GJ8nB3֝& z"oD 4D7% uǘ($T+QOcRnw#I_#8Z.Ȗv:_bJ%cjS̖Xa巢jjxc8goPireʯV7`.9|[U--wd`$?B9[cw6s[$n6*v#b6f_G.;Di٤)s9n:y͠;G1 GW;U$eocM^B~ݺUj}.dnCO 0h.B=iH'=i>tT}i^VFzNg= '0*) Z"š#w lE4t鐤>fotI/"ruΚZ!{rNqr-B٬q.k\L.",0rT.ȴ30y@9NyD=xOnV qY.=(-aSozK~XwpĪp8wQ.ϯ~U2*XvG:\r+BU:YՁFʹHҢ$ԞRILxjFH>KG[.6kcW+1K}Yɝ)Oz9&k8g,*2Osң(D'LzNp3$.9qRrNո0!16s$ ScaM qz|+ǸI5`Ѱ1X:Fһg8@!Fw@A4ayD74u.. 1.d_OCOQF\uAMX1(3Nyyw~3ڱc` 4aփ9+|7Ƹ⋂ڊwh9=J!GhA+ƃ1K/E& a^;?ұbQ{ 9$߷.22w޺!nOc6lCp=q-:fPF4ܬJHV3aqA&qF*{HM#F !ڒL@ Ґ`:cox6ZH1=$1'*&@PÃJ99QNKPo];yD`?:PQ S"vSJ)㗙rlGfqQ\& ߻u8*GQKji-U縖I sZRHW$+`wSf"}U/.l lb-ȅ3qVic%-ͻ3_RE+ķ)A8 n;mU][ܻ4ǐj #cG$sXʆPqhU0ld`t8[V3<p%&,>'}4wV,18b7gҍ}2Z4Kqop,y9T$yH ,21!$8#4voI`9ʳ#"6Va9kyYY6*$sQ4dĨȼ|]e3Vd!_'.0trxQ}cφ;mQd, bbTA;lutO9a>ΤT:_Mu%)kud,׻Aq6cG-,;vnP@IX*w'6-0K\O\7G%d#?͍WiqvWXX/0=FǓ>?Z2[bN=ix!w8E$!I GF5g;NZڼ4b2F:ScqW[:㛚F9fQ JJYE)Tm?:{+&2'5u9ٻ'y`~W j$vEL6TZE-;wUsX.T#> ,M ~ qE;R\gI@`jZXb8,"e8 !ʪujfXv N~5.Τl\ *Zv`>~t6V)Kc0e&)*Qhpa]gqg]Qv S!=h;~@[,xidO(Q:]I ˹s_ǟjE:N<yF#?EYlI˛m N$0]T;D 1g)RmW'8:BV>T6ItgwouTdGv$U?/((U;Doxcl-"?r܇^+u=/"hZ"n+-!U$Hi\={l$C28]hlӮ{9d::W]wU[ǘd}+OU'L4UK]^{9Y#=)w5?EՋ$wJq,i4X-K|gzThw={ޢĻzo¡Uib(L-D'|ПE!JEG/\Rcp0h:J$t)g9Jnz o]ADrNTsB10ϥ$(IР%j+~AAjF?vs ( \c>{Mځ~1B`q u4 *NԌ҈9f!v |)jٕPlNj*IApAUK-fHRc',=?ҎyY.GGP,I#dn]J-"PUUk[IG)+$GlbYHPKҺxy}j򟮉KWH$C q%LǿZ[f(YyyFs߷/bc'&1H1lncCf+pJnHF!q{r-bFaISޓh9f sR:ˉP+8^݈Vr<1>'oJrzfAQQ4GUH~w7fPXQsڕf01CהI;&v=[nlvQZ2ELDyF:T΃Mެm56h\+~9%2 ϵC_ĺ?pJ[n7RhS9%9sy5Ӈ+e!9x"u6;yZ\۬krjL:-nY-]Te\gPcCmȺdrڽ\y+W4ǦP煡rZ"` u`e*j6HCWAY y$`ѳsJ aᄹS\; ? vlPd}z?tI*+#mn𨋸rYRV\SXC\{4ɰ<m!.ECaW`WQH閙 {v- )LkK@e7oWiyKKY ñl/cPЋZeNu:  oUIHK[h9-稨] V}>IlKpdCW9Vzn-)dp|4 cw5|]h3wLIX{7RrflmOUOjv .m39)ʓGl~' >z aʌI+qXZho*=A2K H*pA ?G!O3N܄<NB,k)6sG/ Fygy6ڥ,9̀Þđ[Dn"O 8oFmXΫ[)40<;;3ՆQ\$"GiO*| gm5%[;a_4YnuhQ㑹"37Q^&Bz% #d=X@$iM!.;oi滥B jI=?{>"pO5̊JmחL|1M䶍K\K(%ALW<,dck,[[ok]> ccjxbG.L]}wNfe'ng:_{(<9 @8۸xsQn#OFvaIt0GrKDU0ù*|zS ybH";~W\ L.SOmz $~]wP q[+ʈ2{w;rQ8j-uT­'BN٢z ]]]D!{Q@=k9 oFl2R{HaP6:ri@nStkq`Fƒa\} B4>Ռ{4e )s4$,bQ\P~3C~ŀ5ҁBc˃ZmQu<§3򵜡C{}9֑bG}[$&BY7%v'/4QjmyB]H^,Fl`_}zU{鍈-D_ $|Qu$v >-È.@5)-NXJ"9[v]uY'FC &S3sm*h`쪻0na4֖UHܓ͸^۞:LPdlH;zҵroT%~-DsNs}b {Em[h1R)u Q1H"Ќ;yz||.n5i}?tct r .l5V)^a$.X.=>uKPc^@JkۓmiąTldlg=)֊ f;IgoqpŝG,s/r)Z%*\t9[X/]6N^r)45_FiKTe-r&*zj^ߗ UnA'8RȬ)Pfةȓ9yw}.s`;TޤxNU@sbM+FߤUps=e>R_ֳ"I㻈)!Ktu=Z((Eֹ2NKd {`3Yi:i+0Hd$sґӵwL4In ns#bHTs+6:+)=z~zVo[CdG0 RJZ!Tlc|jF[üOG$e_`N(^ĥ(ёSfAXrc|q|ڋLzXKL=D+^Ң/T_# ʍkq5r<;n5М5e"V."}!OW<59 nBo<$T ]^]C!' uQjT4|Jk}-{F݂\cV#`hsjsAyܓLE9-gbv>/-rz"ns!ł"I9; CupN1H'9W21*. 'kxcPs^fIv,v K'>Tkki9X`PÖDHpSHD.Cg@#+ We2cle>AL.b>mf1#{.KHV* њT{ huEauR77bgceA v!cbyQ@<qIB.GavG\%VhpaI;;Rq-*s<2=~tۛ $2ӠYqDHY*%sΰLK L(HE ЉdJc]*2ifwa0{銬#zylYs!#m?yO]-u島2)_(998N.+Gp9%<wŠe,|Y[XZM [y#[Qb R >E6.249]Ӑq5ŬƇ*z니u4Q㗗\:w5+:")C yù^cLNH >RHmq$ b2$DYs|d]$u>}:dޑA4 $!X51 ʊ .H\`|O 4dLԍƂ/"E,a1l#Ο %,b7/D)nm-yE1Y[eH~uoF$P8jq \x$J򁎣sr,Kt(ucbI;ө,Ck&+oGn J'8 XMm<D3˸ۯKUY6yL9o:Җ9E{l4ˡsjy\~^PPZpt(! 1%0 ⚽YiMwA5IsMX(/[s$qڐH҆#Ң H?hBz^ XdS Ȁ+2Vhh?$[ps#5m±br[:({Q\4Nl[E vʨA|c|gt& Ve Wc5ϕ-*$n˛!HHښzQ\`sAOS bJAzD"e.}zle̙u9I cI9} :Rx+mu:+l@rFð򨨝Ԣj`ri0Q{+ DT;OU'y]NX2F1JVϗN8ag c pB3wYoS(V\ u=xd8x4kkP[Ipǰ#ڏ/j|-n+]ʠ;|k;ֵk\M#MQl_SmL׌0r|e>ӴBnɚQp ^ZmՀ@&1R6"t^8h Z%<^f}뚖G6>)(%Cg=@۩;07 q2jIࣧ 0>xs~Gaq$c sZ;SOh~Hebs5hnuTg,T>#['qPb/HRG(q^}8R$®p7z;Ȓ#uv]]]X'WWWQ1Ռuuu:l侼 Eϙ46+gesqm=PH ''E z'~@LG8;[%[ZkB8~RgqA{,s9<ѩ uv6uXrUZ15jr\p@ *CS,IPM /ҙi,"E9<8|cA~['1ŕ̻oЊ^;4*yr: fi~u03_|'o1c̢!=ji^/u,X#>l ֓6da; ?¥EsPJPC٨b,]/O{&A?u aXqOAԟC,JD {0$|>..e,Z!S>~uG6gaL }+7{&mG`sFeS6f;~XOeE ps4ծkO 0=:Rpc8)n7RQ0j,vZFwvJb<ڣC<  NIf)oE*H%d(iQ6MhO*4ysNuNma$P ph$v"fFz~UӑZKHȝ!zt[kA1RR F(CtK{5 nH :*mDV 'cX01L+:5?@,f߽ H"#'qE:Ϟ):IEf%pc 8p=0G֫s=H!iU%=Oa҂vv} ?ګE,4!=<,bȵbڠD$2cl> z;ʺJox *'a1ႪP:Z8Vawr `t5GAMd%L. }wkXMfC(U\D[ۯB͹1dg8WHm[$~YAG L;l>56'N)UX]8BkE^v\'6q~7i*οxTt*Gŧx욞+l gE1y<;D''; B9fmi1G='f@d\'ҩNı<"۹1Tuuuu:ciFŪP7C z{@ =*8QԬK𥉉$ڹδ68szey]&ڭ0|F`Y(oj"N[$gk}0>4{ 82ZL|N0100f͠bF  ! 2005:06:03 08:38:552005:06:03 08:38:55|< H  }Ki46001231143456B!aPrintIM0100^R980100("HHx!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{I ϠB\gֻ2vS_^?B0"{ۧȴi]̲߰Û3_OUo8o2dqgֽ/ل#߷+UOefOs02yVfF9$縯xA[~~kO'WK__FSW\ìhC|oǓ)|=MeQGwK˵J EhG=:(GawQ8uw>oefo U7iEK ahTJCs3|qkn3dm_}xՄNu`Z6o/HO J.޻OyƭxL.'J܃_mc_lmWy.yTs\}kơ\ndїބ|ԜW3ϒ;t䛳~v=^y!nHnóF H@kgti9!COrcz¿xOFKbSM'{)2t\lUU-{__h:&!bo. rT9#ufP˼ *y}d˕h |723`UVgeH>[$u=סoQf(Z;J/_CW&~=i_xcWl= ⮚_j6 @7=hg9*׍9ϊ-6K/\Ues'>ޭ*~Cϯ5> x'nF2e;Jr'' Nv*8،z^ FU=3˯4lwZ|x4;_JY#L?4~8~@OSA#W!UَmE7BvV=O8)`Q'J   GjJkY?w_,8 hFROlV Mҷ7̚.,ZEr Glwwm5f='5W KJI˃ fQ'_MJM,v :ԶI5&j/oU'  6qp Hnjǯ5$v}~Zʵno㱚|nt(%WeohrMPiQ~Kg=<@m'jԌd]ʿ %{%J!ڃ=>^s[=i=Q–#R(1\~򜏦>~k<-kuӤjDwӣH!S%Mt&}V9oXwp o-~aF6Y~2_5\ϰF-[#T~˕_!id?2/|3F!bZ "G`n?Z]G5( +FԼ4tfknDn2p? ੮.1$}NL<.mToDʵtw%UJYGz̠hҽPg*t$.-qAk%fK2EV23VO&ќA9^}"vU} 죿X7K=br1Bzūqra"W`m"; , ߶8e/G=(G_XcL ӷ1@G_x;MK k=SSդ}1T VO]Eapd\O~i.Կ kq{ y MV5Z..⼺U`hWM7iԿB_i;N@N@G(?S+lI&K*)la*i_) Yo7A=`ows"=+Nl̈́K;w(z{W ]Tw ~w5n?6Bo_7Ĉ?:ׅpxh,8`E4m }+n A>R6/A+f;zW4o& rbHL5wPSt|)1kiޫ߹8Z K1Fʢ" SP1ǧ/tl9-E߃]B KFJ{v~K %Uߓ'溣?q+.>A _鈚Vj!1Ս´#]'Q_}凤[;ZOKMF{Uج E=ֻ9nlPkQƽ(³_T*pl:r6$;lAԼK(3V Rqϻa O^t~Lƽ'QwibRϵm_1QMK?4i*y_nmeiy0+ Ul?ÀO.3no+~J6n+M~jע(#H1?35>2x-#Z\\2@K ~>՜:|N =NZ3O,aY".6#W/w1u N4KVDhLDA:n5{inmVyݟ_Aj4bmm1 9޵:k$o2X,q.OJQw>\hq{.=fHMIA3!phr}kA)>XkWVC̯={-H`ۏQklS];15GŌox...Ռe>8~+09L65>H;85>) <%xבER XX` ´ 4y_%j2Jivl4Kp y=:Y YtQgV_Z&=ewk4ڲ ԼsG=K}-–Qsy qΉYNIEe>-kZi4%T(L\@GV1eA=s57؊ 98Aҭ u'k4f[|i o,>P濔/*yό=KO-l ¸qv[@դZè؛A>mHãmW ~ൾðYic]%lQ$u$ i&GIUSV< [UJƟ$"_~DOӯ-LAP%rvtŽOj~76>ݤ{Di6 12ŸbBr/}%l^צ_??-MRɊiW'%^+`1i_þ(mjb S$//3Ҏk 5C봎8O󬹈.?wO._Ǹ( 8Hs\>g=\~_Һ,׺G=tS?\NdľGYjl~qKL ?i1_͏獏cXVg-N+x´~J,Bi2,gaȞNN{ O,kI|5†'iʎJ/n%_\FwV#̫;UI_3Y y~0;p }׬& N8[=~_wOG%C(==WdemTm[@qߜW=Fm~(xQʺ~!ԓR~u fTRz}EܗSw g3^ecAG~Ԙ1 ݞA~x[ [y& o>)9Z6R;޿B& 63n%ӚW_k/_MͲDQɌ0(%zw]Nf5Ӵ=ĥrKWmśE'ɊB0me 3׊YE55-WG޵w_s_{b#;7fl(+~|eցn-obtuT%t\);8k.W-k>kVP8s dVm L}YAy͍d bym{ Qqr77.H?s(η?sq1)\0?EvP+~kӷ$-?z[WW>1k/I>=s&% A<n:`#h6sKe5?ۆPDsj;xPi $mxc`N[vX?~>(Xo g$# DC}O$g9}Od:IucskBд?H@}Cm;pk_J*j}M(ҦB(6t(ף{E(}2 ŕ[T,S#>V`G9E`*g}lFRz?dK' GsX 089{GJ_9 @Ƕ `N!> mPG~q^C(Z$.|"ӵH)% O$k|ƿֿ8Ƨg &?U2#?C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((f"8!1AQ"aq2#BR3$4Sb&!1AQ"2aBq ?,rǁETLR> /3\Q!\QYA?4Q?5Ջ??9VOcV(e|dV|N'R=kjs3yzת]J_w=hKl K9)|_:{Sg&U'??ܾoSDw+6"\.x1v>4~^EY, B6"K]G~mrI/a Q[DZˁ$J?0FdYh"xZz_KMC/ Qk*7?7].{U|8-P~.s+Cp=_G .d*v+n k 퇽_C+^KP퇽drznG+8W=d-@hG}Mjsckxu0׿ kDljRxO*d$LzeS1Kcs]K᫜z^WV =V[-H ,%aquԩN$6y`-2G{4:k1Ik8%9;LX zK,Ik>>jmlEfF6$lXz[%j蜨gE[0]RY{U JW*t!Qw#F?56qE!cQ-%&#{HSv}ebtj6OƢ\>A6~WOzx߇=Sآ^ΡޤQ OW՟,^?NJ~cT;^\uG>?^ ~O<%#yWWϑ:u|(%j<*Q'Z|b~L?^7Y%o]Y{9|W˂j"DMu]`Xp/xgx}x夬b{U_\G?H9Z I|>F=OPG'}<Ñ`86=)%O C~cW~_NW#z`ߓ9'qcj,ƟIWδhy3l(S/ Kp?QqF}IR%1*pmrRʪӜdIetk_ ~sV, UcMIZ+NQQ_Ksc70ɖC(=ť F]oV>LdQh8 7[d?s[Hl)%i4ʰ=<9EHد RǔZSvbծ^#^"/ }֥Ftb>[6p(屍? Soa<7]r^"*WR' +\ϷBCG.F?6VW\xN5rw1_$^?c\Oˎi~di_琯µ92x֝CGPeTۚ6^=q]5$M;k̎Q{ZeI_.#ADZ+6¸vl=㶎_q:8N/@zN&p{7tC|jV*WSeûhl5c -BC(᫟]$EPkt xm7ȯ 7"B@1UMgbYx/" .['"p8% vDT"mdHݒ(vD0ϕga׮tZzo2+]C'^I\2^#;XlGrx}%|9QVؽ;^z5Q,JH{QHnH@Ϋ_ErȬIn+Xؿx=#셖x.^sm|BErԟC}~;Y2 z%;ypWP]*8+xugHi6S.En'?<ѾM;In;| qeT\<5/ nd^B׺ xeõY>W;t/g^nֺ4GFIz l6e_M:UZHqDqW{jp*Lğ m'J-싋Μ ~0jTױNF=jK &urҍH,ĝY'kqz .]w){xbqU=,5jxG/c^^xLr}E%]{)o#qϪDm ;\e x& ,FE:ٻ p"Q#u @Ao eB$hٻuth8Zӹ5?Ryqtٽ`,1n67ddwQ^4cy?tU^&UHep>o뺲n- aZ{Fn; ie ][PEr,=կR>_# ZϜG& \w8C,Sn,D6=kS]cpw䊵 w^»)\!)({X EK$]#GJQmiZ7 4/s3Q榧HݬRR-p} e41ݗpO%ںv՟4-[ڦ+S$aAr#)Eu$a%ͪHP,78Ŝbx)eSpjmFswJ*0JƧS37 c>8ynX)dd_ck;#]k!Hz`7oR'+G-y/a z}mV18d@R6y뗴10j/s2O [O[SeڄӊkcZKWu}Ο;Ѝ]~,2'1WP)Aa>dسBiaCKg!Yz/AarDvck.#kEG6\TЊ~_gsm;h8 -mI^抾/Ժɇ{U+;B'V?sz SnW|B}[kQً:gp͇«:nK(;-XdH w&D3B`)k -W^\RN3kSU=yH۸)Sqh?sqLO;ic}>mùĨyTծ/5]ilB$ 4Tlf j7l.&3Qxt-Nifiu*ZKѫZ_*71eTJ4m_\MBKrgĭ%˷A(FKc]FYPX(=EyXK 2"`dBb,N!7<74>ZE<:P'z1s3O]ѥz9Yx姽anIδ#\ieRTthRNW=63OF_! Ka]VA;#YxIɦ6.~{`1&1~.QV} fjȪĐW; v i+c5)`$2 E0NF._za'Kg QK(qJI4njyC+k4QJ2NZҶ毗>ڪفZͷr+&`)Ό\ڸ8#aRt4T/* jK#%fk'5ͬ[4supv>-=ZqWMFe-Q )Y6ZVlaE{\kȍ 1>U̠-6WJ|/}FQ$+[z ,PlrԪYkV2%QUb45:ɫ&u4^bU^Vpi~y?Klpw̽ϟ*P? 蕐/7]CN $PkmD,Da،E6/\;{"%a}gw تc]6lv5|I޴`,yxrtcjHrH62֋JvZ^&>7хc"76hޚv7?S[9{F Am#F)!v4)ЍX5s+NzQ|M65bx^daI*o˱9fxg>cYRvw2j|,dUZUgL55$Kt]5'R3l+M3Dm2XE^ e֤/5O.&滉f>("bbM&bqzkYy1/:*TұSnrv˺P±4 fexFԇmg<hɪ.^ƪRm 5(޳c浟LxPN6*̛) ]nQYql1$VWK sW5mjh)AVdCJ ,,#x*^司uI&je;F=KYi8|憾lFv/Vjj['/sYxgv,?*#ܐH_IHa>Hm":W>rh%D_"$H"xǾQF-RK-K8܉;9RF݌Y4"Re ';u+lF+AЈ(6Ĩc"y`}54ιlK#K"hbcV{] ;ƺZFXL9p3**iSP۱-`2+p X48q{ȒqZRI` ZI,I\Įqué|:K3ZT`gGaj82夝sźgzH_?;|"ļoVӳ/q~պԍ̓&KcIalLVËzKd BW>>Y**O}cBضZ_YSYIuE2-#Jc;&^ٚEŗh*,t:.$h+/_ v8o/,p"^Z|D.ɯQ30~2k{ϥzFȢzk GSAEO+A\iwn_+_bU_Bk {rz˂Θ ]GW)ssHJ݉bk\ڻ%iw{9.I$.?+-˰ +r\ Jáޣ/,Z=lk$Ir]n)rP$.\uE88t#EFuDi2Ql.ܜ"c9mнEC5vF<ܞWKm=HYXYoq,2mnqVhFG% N2uo $SJrϘNCӥsIN 7cYYK[ X;RK ՞vc- `VVK[c)Eφ6sK„cpI_}f5sVF. ?"MG+,VL"R $pK`1B(vG/blV,tW;IU5S2peOirS5:w)Q惊'~:~' 3C{#8suҍq:2ctI/mHܹ|cOdx Fv כoQ$L'!1lVBHKqK)lis{LvY':w(#li{裙O%ֻhh)U%s+YQm 񛵺Qv5Ru9gku̶+9tF<0|Kڒ\өsa9s+w5ZRvf2m^1kRvF :5%<0/R2F=z3Q*c\jܭ]ή NEs-bJV*u>rl+ͦ}ĔݙT,R]~W:)/+9;=\[k3uz @O#>Dbޓux#Uѐn` b\-l:msQMk57sٰܪ:{Y4]d@[!X,{+9C=>nELvx]ծ= n&aFbt X )lNK9#o K%7pKA-g$R%,B7d N\|;hhJJ^#tt_B{=G(̥~nG*ѝ4.Z39rJMq]vHTM=^)x[&ilUbIrf1l4ʑ|C|Cԇ)iXY:09x匷xW]r:e$=q(C9і;Kiy62o@1IlZ>$zEԏWt@Pkwؐ{ߠܕ#hV4 lı0B,c=qMjg,ErزWhGTܮܺ{[%o""k6t\JstNXC;95NO]gR،R1;KwTspCllǰEDyR#V$w#{QX ,}(DJ/EɺW4\tRu֍7'sktR,aStJ7%zKέks :koYn{8ikJ==Mc~lQFs];A¤լqpY9J["pa+ERn o,yg+JD#м+Ťsvk8(}Ոi:- z$dx @:RE.)\qݷKwbO|2/VQ;+j9۳ijsgoCßG$q(_rC4tR2RHϋ_[< lXxd]grl : G$}z$I6ib#V؉,1o`?Y)ܗFiR[+9-]?ux׌EUj b7l\JNR9NeWe偣dbMiYpbpEx?[P9Y^=lKƧED1i4Wv35cQӜTlCn+\G"<\Ng PJ75TVGjMnI9gN9,h1'izO:c,7R*ẉ*3FI8:Õl,y9SFreYus^ZP0i\Q{OSN#SRj laN)a!fj%l:g6*'Suy+I:zzyIKsyES͏)ڴVmIqG={'tikYKJh!VѱfWfUUX_m6]ҋL<+'I&yuRmFNüJBN׵ϥpNk44bՓ5^әIvo` C>Ќb @ZG/ߨҿQ^Y?lղ(ʸZI ݈Ʊ_^Awf۽b $[%&(LlU5נ|9:&sڇ~ksBvx~N_]+h B2/)頖?jcOo^DV1iLt9 qfWT"Hp%%ԉ`R;d]I}ɰ-"l"ڹF45vg9Z!FJT\gR^ԛݎŚNiY5U&35)(=% +̐al&SNiTj8n~kX+\©Vi;tE\Cs[&jILMj[|Y4ծCiAvѝ8H 4='W*e%yn˶M4%V.{gi!w_ ЭVz/M(3eqxe1hQsܯOsJgtNb ]\Zj[)>n?W֣<|-qdgTSg; LTYsߡ9|jmu-41s} (i<|3/˭55TӾKwEwl_wB>Q삳ܗZFH p$ ucX U$<'uq˹UcMn>5gZU^&lٷ֩ϋX^ NIߣ(\qSJT\ٲh9F\|vG.\Kvq: ӥ >O &@Hۏ!!"Shog)+-m&y:XB)=*{@PZnd b+H#Y }DЋb%,ӱxstGg9Y6p~*pu]7ne63 Ll^5Λэz0hi#QM'UsMV{=54=_7Xu`mnQ)kP+:RUŎM4Bܩ3gh55ګҵct*NOdc\jttBQιxRWݛԄաZn]I}<=4:pJ#jvHy2ttuw]w>rrZ^"vhM[&_w8suK㾲^wQڜpo_TY/O@O0.nQ!r_67i;IAN$v$g.b7:.M{rp_/1$ Nļ/hJ]^ RN\ɝ׊CujNQ mYFu)H3R呍OZ$#HjjK)3~kʪvJulf7S9Ǻj\-=6|-K'n9H)EaV';̊.ZSKl*-6\[XC|Vw[⊣ѧx_ VXO#[YoQVWdQQ6Y EXtn{*d7-H-`^bRM]2Ĵmc}\q{1A_ag 6+l<^=ޠl/|KNYܖ&MM'C_]R3>?jɖRre-O~2+jAFgyGSVC4 Ku3y=O|>]csIe\[rlu~oa|1nQ-;? h] 49CVy b՝{pʊj>;p+myWpz|w5|1/'l?ᗱn~|6ϣܗ&RNȘ-aN6)~ u /~Ĉ⺢S-d#l3MlVD}-fb[}$}J}H g ,l(EhasA]򈕕͊]m 塴{whtoGݙ%XV a^U ۔U^WbQ[vi뼵%繥w'Rrmskݴ8 K/_KLT*gqW= nCX-[aԬU04DYCGR [DEOtC> ٛ@5p7БK @b-"?еtѫA+QiS5RQ5(ʫsN̖M}] WzwmqӂBu6+~rթg']?x+E㧂rzcmx~4Wf*IOQ.$e'`ߠ-Ԑ^ %*?ɕ6F֢Lq[\?,ݓ|(C 6h GՉ~ HǩĖ-n_p|\w[hI+i9:}Y_fzot-3I5!B$%w&/cP1}nj. ,Dd:x,;lTɑXAomYLG|'e e؉ӹn@ JL rk)+/"r"[7)[#(A}"^PH1*IF-t^.X *~Sk&4[NR?Ñ}k:~oq+7ᳫN5#i# t*M-k>*탧e*35-[K i%# ή~lu _1c4f^n-WAe "x#/~鸲}Pxw ^k; ɀ;@1y'OK.lL.\O '[L]1Z܏,Iv%Ie{(_`?o-#o$hA'71G+[-$wb~6#E5?џ6jJ q-7wk3|Ovw[~\׉J8nZν5Ta8{fsBf׶ni!RTgu}qW 4ԬOؗ2[z YG V/3Ǩ7]1e"<R[n荴P+w" `27do#q9, wBdW>{gqd-G6 fh ,kN +>2Tr_qRk 'i[ԁ$sS%v6U$'Hg/in/:j.VwscڗnX2xěɾ-Mnt[,!A/rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/0000700000175000017500000000000010644674644024443 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/logos/0000700000175000017500000000000010644674645025567 5ustar madduckmadduckrest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/logos/PythonPowered.png0000600000175000017500000000166110414273736031101 0ustar madduckmadduckPNG  IHDRn,v启gAMA a0PLTEֽBBB999111)))cԯ8tEXtSoftwareXV Version 3.10a Rev: 12/29/94 (PNG patch 1.2).IIDATxV;09f dƴ Ktt3+|Θ++Ɛ"ג}9׵j ss;čÍQ W/͝,ˣ?[80.]j]\pBC~1&) _̓x!k*}\n}d-qp.j6)63_FeIq"sWY~y[nݱ~Rn[y1Wx6n,}ҝpA!Шj dS>\UOI .|iG6Yj*g{ 1 x^mu|{ |34 Nxt__/x]>kP pǡ NV[v:jg'R5M*'$s_B]b_tݠQWuufj ƋߤG/.j'"xY#;vǙbyqxPMʊqk]0N :ǽr8wo@2{t@[t?spƎgwstIME.IIENDB`rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/logos/img_background.gif0000600000175000017500000000034710414273736031226 0ustar madduckmadduckGIF89a,l=H*\ 0\@a4X  Ó(S\ɲ˗0cʜI͛8sɳϟ@ : @@D0B 2hЁhπ;rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/logos/pythonbanner.gif0000600000175000017500000004346710414273736030774 0ustar madduckmadduckGIF89a<H,  T<4,$ 5 , $6G& T Ba <!I 4-2K ,$6HOgXJ[d} (*;h"f iv y.q䤼3b݃>b~qOe՜N7hꔴԝ]&lK~Ww]dv_ݞ䈨 m/J̖w̬矽~(WXid̜;Tiqgtȉp䋸Եع|wԻx١ܜػ aͩ,tHRȫx+J!`'CGX]1y1ؤjR'0GU)o9>~vGյWi`(.ޙx#ǥ"$YC۩g[ƈf@y6]%ʭ@ d&!,<H*\ȰÇ#JHŋ3jȱGBI$0V%"`1_Sg'@JѣH*]ʴӧPJJիQʵׯ&Z(`1M2=ȠAs,[njdSWn.]) "\{ǐW12*3k3*шM44SYͺ5Lp2ɞ (۸sJ3gN9|*P$㑒+O)R6IdرceT"42+_Nj g0c؏1 㠃VHv ($h%,b@7TC 7D6Ψ3,2@/FL02n,KؠL8aXfieN`)旋ș{,ǚl&g"ti'$"؞|*BɟYşPRh%&%\r Ffnio)()+D(U!ޫg{G ^GJ&>(6Fk@c1^-r: 4T ;ހS8#O5|/6p#8p:LZxq,@aOXa 70j<S#_o\E o\&ܲ)v̹o[ >:ꨥFg&!uʝwky!a_z߰m *lҶpK+5mwx}pS,+P5tN; 8ޤO:S&eįryygF衊.hfd2 fnCmGK*'yBrկ%,m z1&:p&#PЂ Ụ(Fx/qx>.rp:QqCsg)~,88 "B∐&"h)ZRĢCƱR<` c/Q 2^/Spc&'ܜx)OE"}GHW@ҖQ*jQZGZ,zgSV,&@x{+]i-' qn0pZ>1mᨅ5tPc01ZӭnuNH휐rSDv3Mȃ8q'z&S>$~z՞@/a u@hCCDQVʑW+[kѤA@O  e*WRSJ0iLh:P8r P!;X}1㱎q.-@@D98O8bXE+u cȁZ՚u b#6E/~`/ aZ>2B ;HbIqZ@lb#,wB!p1D$&t5-. B0 YfaY.RQ ~S `qBLb-\g5KOvUMr&=)J@Q>-Mz&$}|㛄"NэtP=⡏ GPPeXoo؆Ap!V]fW3_C*WC\ ,Hb C&HA3 tAPEIC0qЂ#СB cCE(R BBb`J]/RG=]*bB+<}U^  [(/șR2Cb G-*i?w^*^OP F@6 t#@>ࡏcұ|;L UG8a ` Zv7 e7 zKn&? V&` #*шJAC` X&  , 6 GA0C`wAE3 zziCCqDPeH1)b1.@ TX@?8&2q\E[T I^YkCwM /kވ H&>ԡxé=,؆5Qngu8C0{Ͻޠpax+0_,B< ;A6_ *:ρ QT@x4 %1? S$8qV^ d@mxpnwpxSPy0 5jC8wN J @ Rj`AZA*' %6`/yvLI`ƑƑYg@ `#-I cU^UIVVdG}Y:)X4K"ðpH ZУ=Z\ٕ(8x@gg0w}@ *(i h @@ w@S Yș'Lj0T5P=X@ ]Dww*@s7k7I0 x CTy TbHp pIet`y P#k Fxz:Ğ^+@QZ`tp {0p3У޺# ɭ#y0 p_H}xo p f p < l`Gȕ*+,[S^;S[ T,kROZ^rN^AcP ;`p`PӦBЦ;h<KJldSVK.p [ղ/U[਑;ppQJ 'o H" X'pzt \0<#iwP ?`Z @+ pd aYc۴9e@[ M2;8++]R,w6O<2Y_p :'qXTlZ V`h l` L' 4)N:z{<ٳ?y #wa|Mo 0J 1E @ HG;Ɛ2@` 5z@ J D7 0 t [dP20^Tc'!}$}GYӍ3`§{@` <])0 <<(b2$p` t p jp  pMRpAv`PK1z9vXQAHNl1D,,_z3-?ZlQEJNR†TРƌ5ϨB? 95Z( 0`,X aCm?bF}:ㇿ}X`… +Y2ʐ)c83enaӗf|FD3}4zص×:i&?rCIr=wKH%OХ.%(^B;fI2H^ "W瘢LXyL$xVAhE9J* ecق Z(D7 ))hLBxR'Ua Hˡ.D*L1RV(` 6d33$H,*K̵ N- B pX ,VARK/T0 KFOK1ǐf3pIg ^X4A^x!-bg-XwY'_C3 =x6Z>}i5.o!8ҀjТ# 0\2 >"x@>y%<@")dHC\E\Ca~P< <6 6;;Nc-ib@5 ".Tj|fЂmkR`IWB$"'aHy'/zLb0ߒLb0 m/~p#``% 1.>2%pc,i1ťA1d3xh |WtJs6b;}pcvB"6K.kĹr0& IGʆ~@R t" Ӏ< Cf$*0b@0CPb@G0DR W$YDB1TD "dw ىJv) ebH4cHx8l {PpAy$l1 J%d: մĵ/e^[oIGI`!O0 p!%E)j)F*T1`P [A@R&p_#bP e`eQ i4Nr 73AlY4l^Sզt;q: +Ya`0IJVҒ4ׯ gÁD  P" a%x bOկi'})0qD '3p 2RV1JB/d'[!b.W4DІĐޠ`@\RR~2i.ZiM )A xO^Br1*>DON81Qhd BJAL9ƈ*Ǥar 0AR2 q05Q kPܨ ptcsӕi>9b5`Mi ̆6;1ahcq j}m&6tv)}AZRfQ>``"6" 9h%& 0W8`xbY%rJֲ$gYB4sj0H~!?r0A,"nXX$`ɞA#%X@ ,8 a!~dX(l"M82 { !pc * " ( LhF|!BG> s%-!dB_:E lZ ƖX %ahHZRC0}0"*8*pPdc$+9? .zЮ 8 c;e{ 8?(F,bPbB. irU. ĸ2`elPՌҠT. igNMkFwѬo/9+}T38v#.hg s!HH"PL0ǃ;$4,༘l$/( #\`=*-0D $,f^+Tя.y0M ]AH6 |FH4OLGޓ8, @!m  Xh`,8b($X8b8! \`&BH7XQZBc./paxBQYJ8- |2`XX/U(b#(00p7HK0 T"PxJFXH/:3C2:6ClA#BJZ38D3\13@:}гH=roZ0d$fi94D1 )#&C&8$ p-xőXb|ڤ XL\X6 7h>NK*Io#0(6xE7h0 Y yP(I"a14K/$yxzB؂@Л/P Y(g;{8 K@1E@70P0IHJHFWA9&) ؁(h"PZI2hƒDc12W_RhS>69Cj0#;j0rvH̀ : R `Dxctq(oى8`ۑ++ŃRyvV#0+ ƍH50#S5.S+ x5@0MXEp**H H0ȃ/2؁Ux/؄7U@FL*HK:(HQ#BE F  ɮ)<R(x?:xQ=PVPTx8 9$̮L$itDtox̷J Y[ỷ3pw(tv-PL3 4>piDC2_5.pϳvIW+?0:`._pM`%v%SW؄C sD@.u0` / /~4O7"E(  XCxUU)dB0؈&hb|Jiy/@PXY0 WЊ0 @&>T08IKȄNQ0.K8;X  t31hmq50<݂DPJ胏yN@. BHdCjj`jXr`tPwjpHLLFT B<؀ˤAU=xEWMqXpū㰂d+Z<.(?^4 G8OG X9w-p.ZN2HE ExtX`Uh,l3 ch;P[7(8D%Q)PG?X! TZ8&@Hճ? 4@&-2h@Ȃ8{"BY2 -Щ^[^\.-0P<@/p,``xD@.T@Q&h: H3}؂ExJϑQDb Di70@ ])X(EU\ņt ʝsv CLp8 }@5^R#} \>pt`w LLZ]'fUk^͖FH% M81Xgf3xf?`-(Qx7MR\ . 9RJ@P375%.0%7@~ HSpYD؂AxQ*g2``";77 Ȃ,x2a @p/~R#*.ț˛47(IX(PP+Q; !<"AphEz3@ȄLX ~uJ1Q.Hu?ЊA p:(n24ut <~YcrV[(oB MowPvdLdwpyLv`|q BTeU85Es@0th|pP"0ͼR־+5,RV&Wt7X6Χ#RxORU@s\Fh#%3 ;0LTBEy)z.)'P }U20ShV9kB7Fpؘ6P)rPyJ SHҧѓ5&.8l0@ ^AY0HE-x1+=NWHQ؃M5x 0c$x9V$p"MkRP<7KZc8R`.cjZbb㊟ /N\[mnvGG l={{ A|pp |zkey &pLUBU^u_bx>Й(fFg^Nx)$*,(, hc-PɃ.Asܬ: N}8!c2hƒKa4 H8;@Uyh90/-8 i(ր1N_yH}!&, a#0-ȃ0#ڍ8%81i0r<{[h(@:)aHWYAPKC8J0IHa&R-C 6V:YX` A@)pYJh.F(+6THl˞tp_LmQ3|X|#|}sX{Їx X7sp#PGn9PV^6B؄B?e:FxG@GXx].`T0b%  ho =`HTvI oa,Yb9X U.;(F1V 9^0Jp51b <2aDAxyEp~;l"nE!]|Q I6dp-1 E@ !3D1"/iKB*9:g:Թvdvա;p>7P>0vA?XatP‚ 9*3ġ3(kQlBnZĢ=hbh<A_A@ ?HS`B n# uAZ r9B"P7](D !hiFawVc0c \yEd Qa{0(  %r 9]UNT,Bl \yP(r7 &oHha+Av @3(0TP0&<E"c6) =2ᜆ&q^@, "D)Y^n*QbeRbɃBlR"aY鰣g: 7ꡏG$0|,$Y`5{#?)!Efmno>oXSpb{O*Źq%&X%A*vPP!D2 bJ3JXJ)@qB" *H!F8<7:Kd@74LH?!&bp0 è@-d7B/%4 eOxl 3p*Sꏯ$ZЇMPTpH( ! PC"+ &"aTb?X(%b(&Q C&^?ESCY Y iHxL#mIxG=h#0B$i=f5y+(DEHr7 uIKBt٢6W eVZ$rԗ%F( )$: epdHbG$+%1=3=l! h"AjR ؋DpED3#fn>APGxG !*$ɏMzF1"! R LE SP+4h(epf&/R(+A ~Jc `P"| b]&ϼ`0A&⅍0AKa ]nUmpiC!}`bY!gZ:h:8ԁ: Dy`#vm|1ưƦ)mW&\''&', zB0@ H,A()B|B)l#l~$d*؏@ @ d( T3U Z54-@A&B1d1lČSqXɘĀ8m@0($-|q|I@ P@(豈Rt @ xNL KDV !@,ā+ UWDd$A!ķhyA0?1@+(*tUW@1XB"Dp0`1?-*%0-hA !)<Bdޕ(B#tH-@7xC:x8ģ>dGگ8<>ԃ7 C=P<,@؃>Ж ,9\61|R(59B)Q)]HH+d܈"l0#,'A!B)A1` @=@3 m$@ T@dSFS @ BG``A" A"<̀@e5) (HpA-DTA)|B$8HQfxʀɏ0 UԡB @l @ ,THф l@ A @EX@OC ³(A*?0K11!`A(B'B$Yn"l""0pE#kS,Q+lA1(!f tbGL" ݰ@m2iH@:J ;2_6[ ȯUDžL c9ȃŀ($h V6Bld(HΛ$ȁ Z-4>$$A:q(A($ l%pr# U*?Xn \(Gq.@ )TB(@$.l$4Z! $Gp np p ^r+x&Mt,e,l/͕qm*PC/o&tċT-dl&g KC״=U$Š)dlbpU*-l(̢1.-B-L( ---Bx /)B$, |?B,,(!t$!†'ŒwTcxt8(-8H:<<7>$=x<?l=hhs(i#Z),NBz"{j%\B))D2\)\)X2&g)pr'g'2 ( B()5+2,r$..(B/(؁(؂00c*,((s3?3!Hs׍5JB6[oMX`MFL!hp B!) B,Ԝ4O&z!@<@+rC*lA9 J?&gqv, |F KL1(_?|&Q*#@ Brq>0@\?q;:RWR7  C9C>|@<n=94< $9@jƁY5Z&5&[c#5]׵]35^'5_`sB6a%b'v%PB%<6dGv%\dO#_&cfw2)g2+DzhG-i3+(*364S3ׅH7+NX8S)L*|(`ֳW`14s{?x ssA!\ !5U)@/VR=G-'tCCsSFz75_<ux c#5wC8@@V=C8C98aq $W_91ԂZAڨXAC&4t!{[i)"x""/2$?r&&K2&fsv(c()$v,+Z-r-.2 jہOȶlsK/7vM 91@, h*pn)F}w*pIT,T!A4F,k~~O:Wz QPuQ z77=:}V<9n8h4Hٔ#uZ[];^u_u_6;nbG5cKS6c6f92)gg+CSy.30vkkk'3϶vvoT,BsW9txsA:A0zF)xGto<q7LȿWqtQwC4z7<{z4>~QBfY+h?rȡa!ƐB@P/4&YGIE($%~~g|2LBQ- ǫ&wqn!q#lrKv22do 9&X:<38O$O^#a3O`607M9USOAD:Q+Uh;ռ]]ubYVZsy^¯Ir-nQص%ߍםt7;߁&_j\8v v`5!9)NFSOFs@ E]QGOK*K1.@ftP{:H%Z9=9<7;751?;?:>:>9=9<8;79573301.1-0-#a!@<:652412/1./,/,.}+,w)302/yx}|Ͽ~~~zzzuuurrroookkkiiifffaaa]]]TTTNNNIIIEEEBBB@@@;;;666222000...+++(((%%%"""!!! ,xd@c H*\ȰÇ#J$( 8 kǏ DG0Zɲ˗0) !sL D0 J41-D\A 1XJ rDJ&RY& hBA%JYq*8FD HR @ij ,CG gJFL`3R&jFoX$T%aܸ"m CQJԪBDСAt^Q"w`5tqXAmt,=qt!518<59@|p4C=3/CO <#<<-#P/9@C^=Ӑ. {Ï{䍂"K:,3@_I:K12SL-=4g衈&袱dPE'_4"(,i" HaD 6!AdF ^e٬.)/1B]PEq` %dE)ѺT D#oID!C! QPq хpQ6DB}Dt ,U" +F4Ҷ' 侔HcG o k,WCRGH$rg43;Ou.SLw7e" >0 /hDS(B@g-<=ٳ-9P4Ds7S=s/CNA3@gfoz f,߳O5cOC.DrH? aJD5|YG Bq#DrQz T(0jq  uD+jAJp Gz3 eх~ T8AF!n GÖ́&5Mp\sr!jCr1 qG B[PZ m : ;΁ `Az s𞖪j7F:f ͯ~ AL!px0Ԡa>_"0A,y'C ̐Y-RJzDH GPIZa9gl RЄH&G1r@XƱ+H1D:dpB :b!@@ ^JÕ_8 dˆ\` N1ImKJ |pF1zZ@vi%  [8"(Di L(D!A1@ Vs vCBL$a YA&nije?Ί@CD}~HMf,M2(Q\ )8pw Pe 6aɂ^ N$"2'M䇒3 AeAv߿4vA!fsCDŽDp惠6]P QpML%B8#+-/?/A2D5M>N?yn{pzondqgpftjsi|{~ypvŴ֛Ŀþ½ݧů㨥粯 "./0 ?0>/@2B4A3C5B4E7F8I;L>M?M?N@L?PC[NZM\O]P[Og[j^i]k`k_xnofqhsjtkwnv~zryq~vǕƗȆ̦ХϤ͘Ƴֲա¾ޭ¹塟̽˺ȹǿſ~~~ssspppfffaaa```SSSPPP@@@000 ,>SԉCA 0ȰÇ#JHC I!gǏ CIɓ(S\ң8-SRb@d Sf bBt0ɐ-4&JVO2- SrکG[lvYl%tܒvv7+I~I&+ܦc;Dv1e"VQx$JGHP1ߓ7HiXGtGh-o"ݝCnw4-.$c4B43`,X!9`x+4R.&Hьmָc {ϟH?C4K(<++F$2 W,jC/=KcwJ1Ș޳$:@ hG0oe0>̊zFBAp FFA[U_0('blY2@ T++T&A^k!1n b0$.dB%,%3Fbs[G|Do1TWG.~1Bq@zv!FTQ;]H >Gx @BL"HA !d!@70 %"L*WVː +$cd,5B 0[`L! t X$XA $Do+QZg#{(Ve3BZA\/BZϹ(]\Bߜ'2U 2y>y‘ 7*4@5E)&p*; Pf;(sӦ jp hhHf (@D hpӜt PԂHEC-Up Jգ,N d0uUJ 2գ =8Bh5Ek `[Bw v1R"H  PP[DXQ@rHxB8&F4YȄ'8!"-D'.Ddm4"F(Y+"G\;! aCg$2q P#1q]v1v#G]~LG!/ԋ#2w<Teyޏc bjxuSΐ8.+`"'x/K;&׼-/ WxD+a nYhrA]0KU0"(*@"Xxγ:/! ;rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/logos/1.gif0000600000175000017500000000005310414273736026405 0ustar madduckmadduckGIF89a!,@D;rest2web-0.5.2~alpha+svn-r248.orig/docs_html/gallery_test/images/logos/rest2web200x80.gif0000600000175000017500000000577410414273736030603 0ustar madduckmadduckGIF89aP)01 A2C4M>wlkayx~}zqȦϏĿþ簭 !"!/0 0 3$5&?0?1@2@2A3L>N@ZM[NWKk`cYvnâ̎~{ɹ¾ޞ¯Ų켻û´¿yyypppnnnbbb```WWWPPP@@@000 ,PyH*\ȰÇ#JH1@)3jȱǏ CIɓ(Sz>}xLGRdJfLg`('G?IhӜ.nd)U*>qs5#0^vդ#qэX8:RmJ:S*վ-v)qRFQpFy#L?Żg.Fy)RidX֍ՂT)J+v^ӔPkstJK-†Uʫs/y/oU"/RI3ytf9L4WxhD` 6h >(*CfFnu$F%b(8҉*bF,8.5(R (x VT1G@ Fx\NtBMn4%VbydB &IN@lŗyfkIRrJATa[Pѝyg:/PykpF*ʨDT@"}eiVѥcN$Z (fA$q*$+GBDedѳN[-FU4wG[FPIcQJ[5 &J@Ⱥ,o(9C!xlQB> {Rt1$R \$ "7rQq챁 k%b%x {4+bF#4+{"'2щ(@2ϡhR"%C FXQx |D1RWx4uF(2gv_)`v7Gc^Q9.U]$H"R#$tB;ݎ첇8풔ꆳ.E#8۲o%>p$J"GVxB~肏^x_Iۃs'a C( -|>15PTo#ShT>0#`?~R0^@hDx|aF67!@aG1IM °8!2Q8/ FbDF $i&D!Cۄ ;P-$ `7yd {#.8G#y S:I'~I. hG")@HF(Sz &0LBDsIN2Ye#ƑFe#7n574;1/(!tƒPG&26R'0ABq $ 0L&gO|~=8b7ύ"@y@ U0hK_S:@HzQ_u DJzJ̓%u$?RR4"QԀ i겦FLԫ@氉%slUUJJWG[D:@Mb: E0Rj)z(MjWֺ8EEq[ p ugD rF]Oh!ԺF]J]dMV?vdBjo\M^IJ_GW- V XrB# v{ѫJ1ؼ50.bv†LU8cE1`$(lcT;d% +_T= k n}/0{G,NJ-JA&.PBC7*sf3` @gYH~ e6`B C! ȣ#=J_:08CԨV5q` `pP6 Yuk_r5`.! hÕ+f;O\W*\a \@d{&7k])PSX]ny8}[?#[ 1Md1}x\wH\"##`"J89dȄ<x*KEs$#F~B̧C\,Iycthn&3D%tG#0%hb< W _ml8XO]eOuPL ÿ$?Eۣ:N Źo{{:su=J? z6o3v_ݤҺ IW&F3%{ZQ_@wDk4aÍR䩎[gow9G+ZFθZ1~+T:_ 40ayEr cGXMq9չՎQvzmݼʪƌ4{-Ӻ0M9uAR"|DYeD{YPЎʥÓqy6ɼŮ3--ҡMѕf{4Zz{u2,#76Ֆb˖ k/2lUBYT5e"q uRulF'WJ=3Q$ΚG52S&T'ۺ5Nj1fD9gf//)Q`ǩ\ErQugXYxD^{*AukҜBT7(se,=^˝f$wt=vɝ'3o)t} Lyx5)r (fd&!6eGK&A.wh/]=K;p#k19N!!Dxcd TࡣKP\^ 5vaH""؈n$|TJ8dM' k{-ݎRwmNbvchX%Rfd=kCHG3ssx6'Nc'QU@;( $: L)eA0\ِ[zb</0D&Ȕ8MsµO\j58Mg5yRjԺ?>C&ĈqYrՐxYm54v#ӏT^rqj(wg^4{CA YX.J- R.gVtBоMȌ̧= -޳V\7}N|fZ&DL*y-kw,#^㩞ժSEua-!sYRp*Z\t8}]񿎦{%uOTnj֟k'Cw(x #7#u3V0 LQaZje}3&]}x )Q_gٰ9on_O[ר@۷;o=!vlƞ)U}G{٦y|GԮpZ:uoDƧ{ʋs[Ob\{*n_)'S:{3Ã+pE?1OD_wˆtq6ϩ]U<8_jz]\[;{=q{{xo)ʷ'?dz=MX}J 'CVg%DUT}k#/۰eD΋ˬM>:VʏnqnY|k |k7M>+Z ܤSYlw;n͞k4M>:Vt47~)yR;-> g yRլm7mα7g3Ca 1/}[ׂ/7Oڗf-6셱6 &R`y[t={W?j~{ mγ?X~?_.Խnbg_5^nn3ϩ];\X[_ꟾm<> f?|[f+Grapg_5~ozZA.?V~oz^֚ˬ>,U^`fm~3ϩK(Ug`fm~X<[_ꟾmi,3R;YX<[_ꟾl>ozZk .?V~oz^֚ˬ>,U^`fm~3ϩK(Ug`fm~X<[_ꟾmi,3R;YX<[_ꟾl>ozZk .?僑|=,G}g,ݟ,zv|Y|=nݟ/6|=nݟ/(҄zv|ʐ\oCݟ/\zv| Yk1摭#ƳAZ9t}qlaZV EsD;P,imKs1"gOkȞT ٤֩T&m"s.{ CRyesQ攦ɱÎ9FRw7]VY1Zj7ji71bT3\,8#+{/b;VepBf;v\~3s(\n;2#e,4:F5c]LW^]3i6]N3Ji6zX`8B:Ǜof{(hZvً:VשUh i9 @RՖiYw6\w&Jz|Nj"YY_aqaL.1#6RM4wmi/ak3j}T*Z8a2$ 9.ĸ;jxwG5+ G-O.CE4KMHze?[?̝7趕^\v 萜DdE%邶BFXn]< lYrBwZ -L(lr{㲳fU|4;RL^uwF/ceM+3paGdȤ#hI֐'9.+Qj#qub<hHnRqc̔,"ׇG̳,>Dr2$YjJ|dJ.O &.}1eZEt 9)ͽ3V9cPG4?^ Z}F2["lIsVY.Rߓd"hPkʹŝO HJly\ls1g,{ *Y\iduqsť;᥈wFl5&s bֻuoʷ0vF~ thIMK%2]5C3`ɝ)Pl.6<.9YK6^p4Q81>@Ȭ2efAD,s6(sm дĝAp2 q'7F\7m&_woϾ*l%BnE.T^)rTѻy` Z,kAY#u$uL"ܹ쾧(Zs nhCHZF]/zKSI8rՋ1^DKvƔ6c@yE)6$dYIe"Ywrwzk]OiSTl_8QuNTRrUnj֑P;ً6rOYT/DRVH##{3Xo\?aJquQ9P\vl *][y"̈ay-{/]:]~kfVUBS)\LwMp,n5Q}Pt=e=gɥnT9Ē0j ɰjZu@Ύ)2܇ |*&̈́kyW:ȔUˎ6JvlGC$Hʽ:͟Ӳk:[N2UAU͚u])*4KV1EQSlΧzj ʛK 47%VGMap\M6*цһ(HZ~tsw1ȷJnNmrN jWL =qN՘th>Te6\pQj-2a9l[Dhls'oObØt.7"CE\ƪ]2d 8ӐdgWTE$\SK.gPHMJʬ/k ɾzf\=5ҦJԶ")*>ۤ(㇬L0E' SLe4]pܤ+.RX %Lir *r""f 3MGXjYW.M6uLȩ*. x*[ vյ_xv݄p|0k#TfQzm'"1y1k0fDF;HTI@\!^"\SeUCT(m ӥ#@JȊD<|˚ڳ=!GaVbUμ2CȜX_u=C3QS*LvUv/-A_kZGwDog60֓Bzwv5{`0Eo*"mM8ד =`dq ncV[{_/-^˗o^$+Aٷx&W$oyQ9-*[PT*Z !b1Ѳ ^/{J5SҀ[FL6*8|F%c~;fTVm|,΢&; hO}m5eSǑAq *";1]mC4t =Y\