summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2015-01-08 18:37:20 +0100
committerChristian Grothoff <christian@grothoff.org>2015-01-08 18:37:20 +0100
commit57d1f08dbca256f5fe16d57b29bfa523dec8f6c4 (patch)
tree3b3ee5f3b8c174887217e5c465048dea4e79bae2
downloadexchange-57d1f08dbca256f5fe16d57b29bfa523dec8f6c4.tar.gz
exchange-57d1f08dbca256f5fe16d57b29bfa523dec8f6c4.tar.bz2
exchange-57d1f08dbca256f5fe16d57b29bfa523dec8f6c4.zip
-initial import for mint
-rw-r--r--.gitignore26
-rw-r--r--.gitmodules3
-rw-r--r--AUTHORS4
-rw-r--r--COPYING661
-rw-r--r--ChangeLog0
-rw-r--r--INSTALL370
-rw-r--r--Makefile.am3
-rw-r--r--NEWS0
-rw-r--r--README0
-rwxr-xr-xbootstrap2
-rw-r--r--configure.ac156
-rw-r--r--contrib/mint-template/README1
-rw-r--r--contrib/mint-template/config/mint-common.conf6
-rw-r--r--contrib/mint-template/config/mint-keyup.conf79
-rw-r--r--doc/.gitignore12
-rw-r--r--doc/Makefile.am5
-rw-r--r--doc/logos/ai/logotalerv2.ai3474
-rw-r--r--doc/logos/eps/icon_taler.epsbin0 -> 242881387 bytes
-rw-r--r--doc/logos/eps/logo_taler.epsbin0 -> 10731812 bytes
-rwxr-xr-xdoc/logos/fonts/OldNewspaperTypes.ttfbin0 -> 85804 bytes
-rw-r--r--doc/logos/fonts/perpetue/Perpetua Bold Italic.ttfbin0 -> 75620 bytes
-rw-r--r--doc/logos/fonts/perpetue/Perpetua Bold.ttfbin0 -> 58512 bytes
-rw-r--r--doc/logos/fonts/perpetue/Perpetua Italic.ttfbin0 -> 76080 bytes
-rw-r--r--doc/logos/fonts/perpetue/Perpetua.ttfbin0 -> 60216 bytes
-rwxr-xr-xdoc/logos/fonts/smoth_bight/End User Licence Agreement.txt133
-rwxr-xr-xdoc/logos/fonts/smoth_bight/Licencia de la fuente - Version en Espa§ol.txt77
-rwxr-xr-xdoc/logos/fonts/smoth_bight/Smoth-Bight - Por Kustren.otfbin0 -> 50396 bytes
-rwxr-xr-xdoc/logos/fonts/smoth_bight/Smoth-Bight Italic - Por Kustren.otfbin0 -> 50820 bytes
-rw-r--r--doc/logos/ico/favicon1616.icobin0 -> 1150 bytes
-rw-r--r--doc/logos/ico/favicon4848.icobin0 -> 15086 bytes
-rw-r--r--doc/logos/png/icon_taler.pngbin0 -> 1835737 bytes
-rw-r--r--doc/logos/png/logo_taler.pngbin0 -> 377176 bytes
-rw-r--r--doc/paper/.latexmkrc15
-rw-r--r--doc/paper/taler.bib94
-rw-r--r--doc/paper/taler.tex995
-rw-r--r--src/.gitignore9
-rw-r--r--src/Makefile.am2
-rw-r--r--src/include/Makefile.am7
-rw-r--r--src/include/platform.h56
-rw-r--r--src/include/taler_db_lib.h132
-rw-r--r--src/include/taler_json_lib.h101
-rw-r--r--src/include/taler_microhttpd_lib.h119
-rw-r--r--src/include/taler_mint_service.h303
-rw-r--r--src/include/taler_rsa.h357
-rw-r--r--src/include/taler_signatures.h106
-rw-r--r--src/include/taler_types.h120
-rw-r--r--src/include/taler_util.h255
-rw-r--r--src/mint/.gitignore6
-rw-r--r--src/mint/Makefile.am131
-rw-r--r--src/mint/mint.h198
-rw-r--r--src/mint/mint_api.c1121
-rw-r--r--src/mint/mint_common.c283
-rw-r--r--src/mint/mint_db.c1838
-rw-r--r--src/mint/mint_db.h344
-rw-r--r--src/mint/taler-mint-dbinit.c285
-rw-r--r--src/mint/taler-mint-httpd.c376
-rw-r--r--src/mint/taler-mint-httpd.h106
-rw-r--r--src/mint/taler-mint-httpd_deposit.c270
-rw-r--r--src/mint/taler-mint-httpd_deposit.h48
-rw-r--r--src/mint/taler-mint-httpd_keys.c512
-rw-r--r--src/mint/taler-mint-httpd_keys.h155
-rw-r--r--src/mint/taler-mint-httpd_mhd.c300
-rw-r--r--src/mint/taler-mint-httpd_mhd.h132
-rw-r--r--src/mint/taler-mint-httpd_refresh.c1497
-rw-r--r--src/mint/taler-mint-httpd_refresh.h103
-rw-r--r--src/mint/taler-mint-httpd_withdraw.c400
-rw-r--r--src/mint/taler-mint-httpd_withdraw.h65
-rw-r--r--src/mint/taler-mint-keycheck.c169
-rw-r--r--src/mint/taler-mint-keyup.c657
-rw-r--r--src/mint/taler-mint-reservemod.c215
-rw-r--r--src/mint/test_mint_api.c211
-rw-r--r--src/mint/test_mint_common.c83
-rw-r--r--src/mint/test_mint_deposits.c149
-rw-r--r--src/mint/test_mint_nayapaisa.eccbin0 -> 32 bytes
-rw-r--r--src/mint/test_mint_nayapaisa/README1
-rw-r--r--src/mint/test_mint_nayapaisa/config/mint-common.conf6
-rw-r--r--src/mint/test_mint_nayapaisa/config/mint-keyup.conf79
-rw-r--r--src/mint/test_mint_nyadirahim.ecc1
-rw-r--r--src/mint/test_mint_nyadirahim/README1
-rw-r--r--src/mint/test_mint_nyadirahim/config/mint-common.conf6
-rw-r--r--src/mint/test_mint_nyadirahim/config/mint-keyup.conf79
-rw-r--r--src/util/Makefile.am39
-rw-r--r--src/util/db.c196
-rw-r--r--src/util/json.c194
-rw-r--r--src/util/microhttpd.c417
-rw-r--r--src/util/misc.supp28
-rw-r--r--src/util/rsa.c925
-rw-r--r--src/util/test_hash_context.c48
-rw-r--r--src/util/test_rsa.c112
-rw-r--r--src/util/util.c528
90 files changed, 19997 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..6fe514889
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+*~
+*Makefile.in
+*Makefile
+aclocal.m4
+autom4te.cache
+autoscan.log
+compile
+configure
+depcomp
+missing
+taler_config.h.in
+install-sh
+config.log
+config.status
+stamp-h1
+taler_config.h
+config.guess
+config.sub
+libtool
+ltmain.sh
+test-driver
+m4/
+GPATH
+GRTAGS
+GTAGS
+*.swp
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..7625aa983
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "doc/api"]
+ path = doc/api
+ url = git@git.taler.net:api
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000..75e08df1c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+Sree Harsha Totakura <sreeharsha@totakura.in>
+Florian Dold
+Christian Grothoff <christian@grothoff.org>
+Benedikt Mueller
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000..dba13ed2d
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 000000000..209984075
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,370 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
+Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell command `./configure && make && make install'
+should configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package. Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the `make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type `make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior `make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type `make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide `make
+ distcheck', which can by used by developers to test that all other
+ targets like `make install' and `make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. This
+is known as a "VPATH" build.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'. Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated. The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the `DESTDIR' variable. For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names. The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of `make' will be. For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ HP-UX `make' updates targets which have the same time stamps as
+their prerequisites, which makes it generally unusable when shipped
+generated files such as `configure' are involved. Use GNU `make'
+instead.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file. The option `-nodtk' can be used as
+a workaround. If GNU CC is not installed, it is therefore recommended
+to try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+ On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf limitation. Until the limitation is lifted, you can use
+this workaround:
+
+ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+ Print a summary of the options unique to this package's
+ `configure', and exit. The `short' variant lists options used
+ only in the top level, while the `recursive' variant lists options
+ also present in any nested packages.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names::
+ for more details, including other options available for fine-tuning
+ the installation locations.
+
+`--no-create'
+`-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 000000000..ea5a667ef
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,3 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+SUBDIRS = src doc
+ACLOCAL_AMFLAGS = -I m4
diff --git a/NEWS b/NEWS
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/README
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 000000000..4e7cc0537
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,2 @@
+#!/bin/sh
+autoreconf -if
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 000000000..789df8e46
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,156 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT([taler-mint], [0.0.0], [taler-bug@gnunet.org])
+AC_CONFIG_SRCDIR([src/util/json.c])
+AC_CONFIG_HEADERS([taler_config.h])
+# support for non-recursive builds
+AM_INIT_AUTOMAKE([subdir-objects])
+
+# pretty build rules
+AM_SILENT_RULES([yes])
+
+AC_CONFIG_MACRO_DIR([m4])
+
+LT_INIT
+
+# Checks for programs.
+AC_PROG_CC
+
+CFLAGS="-Wall $CFLAGS"
+
+# Checks for header files.
+AC_CHECK_HEADERS([stdint.h stdlib.h string.h unistd.h])
+
+# Check for GNUnet's libgnunetutil.
+libgnunetutil=0
+AC_MSG_CHECKING([for libgnunetutil])
+AC_ARG_WITH(gnunet,
+ [AS_HELP_STRING([--with-gnunet=PFX], [base of GNUnet installation])],
+ [AC_MSG_RESULT([given as $with_gnunet])],
+ [AC_MSG_RESULT(not given)
+ with_gnunet=yes])
+AS_CASE([$with_gnunet],
+ [yes], [],
+ [no], [AC_MSG_ERROR([--with-gnunet is required])],
+ [LDFLAGS="-L$with_gnunet/lib $LDFLAGS"
+ CPPFLAGS="-I$with_gnunet/include $CPPFLAGS"])
+AC_CHECK_HEADERS([gnunet/platform.h gnunet/gnunet_util_lib.h],
+ [AC_CHECK_LIB([gnunetutil], [GNUNET_SCHEDULER_run], libgnunetutil=1)],
+ [], [#ifdef HAVE_GNUNET_PLATFORM_H
+ #include <gnunet/platform.h>
+ #endif])
+AS_IF([test $libgnunetutil != 1],
+ [AC_MSG_ERROR([[
+***
+*** You need libgnunetutil to build this program.
+*** This library is part of GNUnet, available at
+*** https://gnunet.org
+*** ]])])
+
+
+# check for libmicrohttpd
+microhttpd=0
+AC_MSG_CHECKING([for microhttpd])
+AC_ARG_WITH([microhttpd],
+ [AS_HELP_STRING([--with-microhttpd=PFX], [base of microhttpd installation])],
+ [AC_MSG_RESULT([given as $with_microhttpd])],
+ [AC_MSG_RESULT([not given])
+ with_microhttpd=yes])
+AS_CASE([$with_microhttpd],
+ [yes], [],
+ [no], [AC_MSG_ERROR([--with-microhttpd is required])],
+ [LDFLAGS="-L$with_microhttpd/lib $LDFLAGS"
+ CPPFLAGS="-I$with_microhttpd/include $CPPFLAGS"])
+AC_CHECK_LIB(microhttpd,MHD_start_daemon,
+ [AC_CHECK_HEADER([microhttpd.h],[microhttpd=1])])
+AS_IF([test $microhttpd = 0],
+ [AC_MSG_ERROR([[
+***
+*** You need libmicrohttpd to build this program.
+*** ]])])
+
+
+# check for libpq (postgresql)
+AX_LIB_POSTGRESQL([9.3])
+AS_IF([test ! "$found_postgresql" = "yes"],
+ [AC_MSG_ERROR([[
+***
+*** You need postgresql / libpq to build this program.
+*** ]])])
+
+
+# check for libjansson (Jansson JSON library)
+jansson=0
+AC_MSG_CHECKING([for jansson])
+AC_ARG_WITH([jansson],
+ [AS_HELP_STRING([--with-jansson=PFX], [base of jansson installation])],
+ [AC_MSG_RESULT([given as $with_jansson])],
+ [AC_MSG_RESULT([not given])
+ with_jansson=yes])
+AS_CASE([$with_jansson],
+ [yes], [],
+ [no], [AC_MSG_ERROR([--with-jansson is required])],
+ [LDFLAGS="-L$with_jansson/lib $LDFLAGS"
+ CPPFLAGS="-I$with_jansson/include $CPPFLAGS"])
+AC_CHECK_LIB(jansson,json_pack,
+ [AC_CHECK_HEADER([jansson.h],[jansson=1])])
+AS_IF([test $jansson = 0],
+ [AC_MSG_ERROR([[
+***
+*** You need libjansson to build this program.
+*** ]])])
+
+# check for libgnurl
+LIBGNURL_CHECK_CONFIG([], [7.34.0], [gnurl=1], [gnurl=0])
+if test "$gnurl" = 1
+then
+ AM_CONDITIONAL(HAVE_LIBGNURL, true)
+ AC_DEFINE([HAVE_LIBGNURL],[1],[Have libgnurl])
+else
+ AM_CONDITIONAL(HAVE_LIBGNURL, false)
+fi
+AS_IF([test $gnurl = 0],
+ [AC_MSG_ERROR([[
+***
+*** You need libgnurl to build this program.
+*** ]])])
+
+# Require minimum libgcrypt version
+need_libgcrypt_version=1.6.1
+AC_DEFINE_UNQUOTED([NEED_LIBGCRYPT_VERSION], ["$need_libgcrypt_version"],
+ [minimum version of libgcrypt required])
+AM_PATH_LIBGCRYPT([$need_libgcrypt_version])
+
+# logging
+extra_logging=0
+AC_ARG_ENABLE([logging],
+ AS_HELP_STRING([--enable-logging@<:@=value@:>@],[Enable logging calls. Possible values: yes,no,verbose ('yes' is the default)]),
+ [AS_IF([test "x$enableval" = "xyes"], [],
+ [test "x$enableval" = "xno"], [AC_DEFINE([GNUNET_CULL_LOGGING],[],[Define to cull all logging calls])],
+ [test "x$enableval" = "xverbose"], [extra_logging=1]
+ [test "x$enableval" = "xveryverbose"], [extra_logging=2])
+ ], [])
+AC_DEFINE_UNQUOTED([GNUNET_EXTRA_LOGGING],[$extra_logging],[1 if extra logging is enabled, 2 for very verbose extra logging, 0 otherwise])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_INTMAX_T
+AC_TYPE_UINTMAX_T
+
+# Checks for library functions.
+AC_CHECK_FUNCS([strdup])
+
+AC_CONFIG_FILES([Makefile
+ doc/Makefile
+ src/Makefile
+ src/include/Makefile
+ src/util/Makefile
+ src/mint/Makefile
+ ])
+AC_OUTPUT
diff --git a/contrib/mint-template/README b/contrib/mint-template/README
new file mode 100644
index 000000000..fce5e0180
--- /dev/null
+++ b/contrib/mint-template/README
@@ -0,0 +1 @@
+This directory is a template for the mint directory.
diff --git a/contrib/mint-template/config/mint-common.conf b/contrib/mint-template/config/mint-common.conf
new file mode 100644
index 000000000..becf42435
--- /dev/null
+++ b/contrib/mint-template/config/mint-common.conf
@@ -0,0 +1,6 @@
+[mint]
+db = postgres:///taler
+port = 4241
+master_pub = ...
+refresh_security_parameter = 3
+
diff --git a/contrib/mint-template/config/mint-keyup.conf b/contrib/mint-template/config/mint-keyup.conf
new file mode 100644
index 000000000..1542d1a63
--- /dev/null
+++ b/contrib/mint-template/config/mint-keyup.conf
@@ -0,0 +1,79 @@
+[mint_keys]
+
+# how long is one signkey valid?
+signkey_duration = 4 weeks
+
+# how long do we generate denomination and signing keys
+# ahead of time?
+lookahead_sign = 32 weeks 1 day
+
+# how long do we provide to clients denomination and signing keys
+# ahead of time?
+lookahead_provide = 4 weeks 1 day
+
+# what coin types do we have available?
+coin_types = default_eur_ct_10 default_eur_5 default_eur_10 default_eur_1000
+
+
+
+[mint_denom_duration_overlap]
+default_eur_ct_10 = 5 minutes
+default_eur_5 = 5 minutes
+default_eur_10 = 5 minutes
+default_eur_1000 = 5 minutes
+
+
+
+[mint_denom_value]
+default_eur_ct_10 = EUR:0.10
+default_eur_5 = EUR:5
+default_eur_10 = EUR:10
+default_eur_1000 = EUR:1000
+
+
+
+[mint_denom_duration_withdraw]
+default_eur_ct_10 = 7 days
+default_eur_5 = 7 days
+default_eur_10 = 7 days
+default_eur_1000 = 1 day
+
+
+
+[mint_denom_duration_spend]
+default_eur_ct_10 = 30 days
+default_eur_5 = 30 days
+default_eur_10 = 30 days
+default_eur_1000 = 30 day
+
+
+
+[mint_denom_fee_withdraw]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+[mint_denom_fee_deposit]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_fee_refresh]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_kappa]
+default_eur_ct_10 = 3
+default_eur_5 = 3
+default_eur_10 = 3
+default_eur_1000 = 5
+
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 000000000..8fd4c0911
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,12 @@
+*.aux
+*.dvi
+*.log
+*.pdf
+*.out
+*.snm
+*.toc
+*.vrb
+*.nav
+*/auto
+api-sphinx/_build
+api-sphinx/*.html
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 000000000..4af5c665c
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,5 @@
+EXTRA_DIST = \
+ paper/taler.tex
+ paper/ref.bib \
+ paper/Makefile
+
diff --git a/doc/logos/ai/logotalerv2.ai b/doc/logos/ai/logotalerv2.ai
new file mode 100644
index 000000000..d474079d3
--- /dev/null
+++ b/doc/logos/ai/logotalerv2.ai
@@ -0,0 +1,3474 @@
+%PDF-1.5 %
+1 0 obj <</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R 248 0 R 489 0 R 730 0 R]/Order 731 0 R/RBGroups[]>>/OCGs[5 0 R 248 0 R 489 0 R 730 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <</Length 50398/Subtype/XML/Type/Metadata>>stream
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <dc:format>application/pdf</dc:format>
+ <dc:title>
+ <rdf:Alt>
+ <rdf:li xml:lang="x-default">logotalerv2</rdf:li>
+ </rdf:Alt>
+ </dc:title>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmp="http://ns.adobe.com/xap/1.0/"
+ xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/">
+ <xmp:CreatorTool>Adobe Illustrator CS6 (Macintosh)</xmp:CreatorTool>
+ <xmp:CreateDate>2014-09-16T12:36:31+02:00</xmp:CreateDate>
+ <xmp:ModifyDate>2014-09-18T11:52:58+02:00</xmp:ModifyDate>
+ <xmp:MetadataDate>2014-09-18T11:52:58+02:00</xmp:MetadataDate>
+ <xmp:Thumbnails>
+ <rdf:Alt>
+ <rdf:li rdf:parseType="Resource">
+ <xmpGImg:width>256</xmpGImg:width>
+ <xmpGImg:height>192</xmpGImg:height>
+ <xmpGImg:format>JPEG</xmpGImg:format>
+ <xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAwAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqkHn&#xA;Lz35W8naZ+kdfvktYmqIIftTTMP2Yox8THffsO5AxV4lqP55fmf5v5jyLpUWi6QWMUer6gY2kZqh&#xA;fh9T9yp5OoYBX41BJAxViV/5Y826oyt5w87zrLKob6pPctDGrcI2YKJGSIhGnUHgvVZBtxrgVJrn&#xA;yn5NW48x2ei3VvNBc2trHpV5qFeSTGaJ7hlkeNaHgsg5UGx232CrI28syWOl2d95U8z3OixW0U1x&#xA;eXsdxcMsscdsvFOCSUaQTW8hZBGPhkBPZcKo22/Pf81vImox6Z5yt7fW4CCSwaNLkKkjRN+9gqnJ&#xA;WQ1WROXiRir3TyD+ank7zxal9Gu6XkahrjTpwI7mP5pU8l/ylJGKsuxV2KuxV2KuxV2KuxV2KuxV&#xA;2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KsG/Nb80tO8iaRGwj+va9fkxaRpaVLSyHbmwX4vT&#xA;UkVpuTsPZV4GnlTUNVv280/mTPLqGrXgKWNjs9vzV2UWy/Vnd+akf3SJxUtVyxDpir1Hy9+WWp30&#xA;cE2pXcuk2jxktp9okdvM3xzKpd46rCXhmpJHDRflsALVnej+QfJukqPqOk2yuNzO6CWUnxMj8nr9&#xA;OBKerFEiBFRVQbBQABiqUar5L8p6sjJqGk2txyFC5iUP9DqA4+g4q8y8/wD/ADj/ABan6l/ot3LN&#xA;d8afVL2QyVVRskczfEPYPX5jDavnnUNH8w+Vdb+s2rT6Zq1hJUMKxyxN/Qg/Ij2xQ+nPyQ/O2Dzr&#xA;anSNY4W/ma1QFwKKlyg29WMdiP21+kbdCr1rFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUv1zzDoWg&#xA;2Rvda1CDTrUbercyLGCfBeR+I+w3xV5Tr3/OVX5c2EjRaZDe6y42EsEQhh2/ypyj/cmKsXk/5zCI&#xA;ciLygzJ2ZtQCn7hbN+vFUXp//OX2ku6/pPyzc20f7TW1zHcEfIOlvX78VeieVPz2/LLzLIlva6st&#xA;leyEBbO/H1ZyT0Cs37pifBXJxVn4IIqOmKuxV2KuxVB6zq9ho2k3mrahJ6VlYwvcXEngkaljQdzt&#xA;sO5xV8iwebtY8yeYtS82eYtHt7zTdYSQaUblDJ9UW0bjAsRhKXCJzkEbvGU5SGvKoIwK96/LnyC9&#xA;rOfNGuxV128QejAzNILSGlFjDPVjIV/vHO5NfE1BKUg/5yM8t2d1odnrJjv7q/tri2t4LW0SeWP0&#xA;DcpLdMyQq1CYUarNtQUG+IV2p3+teTvyj05vKz3c11LJDbQSSQPdyxIUdhyQglalAnJkNK0pWmKq&#xA;HmHzHrHmLyL5R1WeyuYdRGs2T3KRwTKyD05BI7KAWSOp/b26VxVX/MLV/wAx7Tzl5XOjTXcOhNDE&#xA;0v1eD14pbgzFbiO7+AqqegV4s0qcdyvJgFxVOTq3nqf8z/qkd3c2/l+Kzs5fqz2iC1mnlE4uFS5a&#xA;HmxSkLfDLsdqeCqZfmb+W9l5v0kvGqxa5bIfqdz0D9zDJ4q3Y/snfxBVfId4mr+Wddi1KyL2Wpaf&#xA;PUA/C0csbUZHHzBVh9GFD7S/LTzzaedfKdprUAEcrjhdQVqY5V2dT9PT2wqyrFXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXin5w/wDORVj5Xnm0Dywseo+YEql1cNVre0b+U0/vJR/KDRe+9VxV87vZ+cfO2r/X&#xA;tWubjU76c0V5KudzXjGg+FV8FUAe2BXp/lr/AJxs1y5jSW+WOzQ0NJ2q9P8AUQNT6aY2rMoP+ca9&#xA;LVBz1MBu4W3qPvMgxtUJqf8AzjTCyH6nfQyt2EsbRfipkxtXmPm/8kNf0ZWkltWWEdJk+OL/AIJa&#xA;0+mmKrPIv5vefPy8uI7OZm1PQVID6bcsTwUf8s8hqYj7br7d8VfV/knzz5d856KmraHcerCTxnha&#xA;izQyUqY5UqeLD7j1BIwqn+KuxV5D/wA5Da2fqWg+U4b02V3r94GSQQrcKfqjI0aSozIoR53jqzVF&#xA;AdiK4qlfljyvb3HnOy0WNIRp+jKdTvzayh4Jpmcrb0jiWKCMScfW4JGo6dTVmBV6j5xn1C38pa1P&#xA;pt3Dp+oRWNw9pf3JVYLeVYmKTSlw6hI2+JiykUG4wJfJDfmr+eXJkk/MLRpFBoSs+lUPyrBhQqR/&#xA;mr+cITg/n3S+P8q3Ojgfcbc4qxq5/wCcg/zohuJ7eLzOJIonaPnHa6e6MVNCVZYOLLtsw6jGlT2y&#xA;/OT83b60SVfPNjGDQqlzJpULU7Vje2qp9q40q9/zW/O4kFPP+j1HQtPpJp/yQxV7Z+QPmf8AMbWL&#xA;W4bzXqVtrMTuz2moWrQMhjACFAbdY0PF1bt9OApYt/zkh5PitdYt9bt0CwauhjuaDYXMIFG8P3kf&#xA;/EScIQlP/OLfmyXTfNN75amaltqA9eFSdhLH8LU/1gR92FX1ZirsVdirsVdirsVdirsVdirsVeKf&#xA;85FfnDN5XsF8saBP6fmHUY+VxcofitLZqiqntLJSi9wPi68Tirwz8svy01LzRqaRxoSCec0z14ot&#xA;d3c/51wK+tPKPkbQfK9msNhCGueNJrxwPUc96fyr/kjAlkOKuxV2KrXjSRGSRQ6MKMrCoIPYg4q8&#xA;j/M/8mLDULSbUNFgCyqC01ko2I7mL3/yfu8MNq8D8seZvMH5Z+bI9VsKyWzER31kxIjuIa7o3+UO&#xA;qN2PtUFQ+0vLvmDS/MOh2Wt6XL61hfRiWB+hodirDsysCrDsRhVMcVfOH51W0uqfnbo6TQxT6ZYW&#xA;AhZJ51hj+sSpcTDkxDsmwQ1VSRSo3xVn/wCTmnRxL5hvPTWKZtQ+pFEkkmASyhSJR6kxMjUNd23y&#xA;JS9BvLO1vbSezu4UuLS5jaG4t5VDxyRyAq6OrAhlZTQg4q82uf8AnHL8rJ53l/REMfMk8I4o1UV7&#xA;ABaAY2qn/wBC2flZ/wBWuP8A4CP/AJpxtXyZ5g8tW9p5m8w2donG1s9UvraBadI4bmSNRt4Bckh7&#xA;f+Rn5KeQ/Mnke01LVbFJrqRFLuVQmpHuDgJS9E/6Fs/Kz/q1x/8AAR/804LVlPk78t/LXlEt+hoj&#xA;CjKV9IEBACeRooAA3xVJPz809Lr8tr2cir6fNb3Mfz9UQsf+AmbEK+YfId6+mfmdo1yhp/pQU08H&#xA;BX+OSQ+60bkgbxFcVbxV2KuxV2KuxV2KuxV2KoHXdZstE0W+1i+bhaafBJcznvxiUsQK9zSgHjir&#xA;4WW61Xzt5yu9YvgZLzUrgysgq3EMaRxL34otFX2GBX2T5A8n2vlfy/BZIi/W5AHvZR1aSn2a+C9B&#xA;9/fAljmqf85E/k7pWqXmlX+vmK/0+eS1u4RZ3z8JoXMci8kgZTRlIqCRjSqSf85J/ku/2Nfkb5af&#xA;qJ/7F8aVbL/zkv8AkpC3GXzC8bHcB7DUVNPpt8aVdH/zkn+TEq84tekkUdWXT9RI+8W+NKtb/nJb&#xA;8lU+15gdfnYaiP8AsXxpU88mfnF+XHnTVZdK8s6v9fv4YGupYfq11DSFHSNm5TxRr9qRRStd8Vea&#xA;/n5+XsCE6vaRhYLsn1VA2Saldv8AW6/fhCEu/wCcVvOk1pq2oeSL2T9zOGvNMVj9mWPaeNf9dKPT&#xA;/JPjhV9L4q+WP+ciPNHmLQPzHnstMuWs49RtrO8e4iLLKREJYAlQ3EpUE/ZrXvir1H/nHy5a58j3&#xA;E8knq3EmoTvO9KEu6RuTtQb8siUvTcVdirsVfIFzoBvNb8z3AWtdc1YVp4X0wySHs3/ONIp+W9kP&#xA;BV/VkSl6zirsVYH+eU0cX5W64XNOa26KPFmuYgMQr5M0NTJ570lV3P1qI0Hsa4UPvS3/ALiP/VH6&#xA;sKqmKuxV2KuxV2KuxV2KuxV4/wD85Ta5Jp/5YGxiYq+sXsFo9OvppyuG3+cIB+eKvK/+cb/Lkd95&#xA;nhuZUqlmrXRB8Uoqfc7A4Cr6rwJeJ+YPy0/Me817Uruz8q/lvcWtxdTy29xqFhdPeSRvIzI9y6xl&#xA;WmZTWQjq1cVQI/K381R08o/lcP8At3Xn/VPFXhf54+WvMemedbSy1zT9B026OmxTpbeWYZbezKNP&#xA;Mgd0lCsZiUIY/wAoXwwhDLvyK8l+edY8q38+iaJ5N1O0g1GSBp/M1rcXF4riCFzHG0SsohpIGA/m&#xA;LHviVein8rPzUPXyh+Vp/wC3def9U8CWcflb5J17QrrULvzBoPlLS7uRI4rG48rWklvI0ZLNOlw8&#xA;qKxUssZUL4GvbFWT+dtJTVfK2o2rLyb0mli8ecY5inzpTFXyFpF+3ln80NE1VTwS3voTMen7p39O&#xA;YfTG7DCh9xYVfNX/ADl55fdLry95jjUmMiXT7l+wIPqwj6f3n3Yqjv8AnGDzJGy6hokjgNMi3Vup&#xA;/mj+CQD3IKn6MBV79gS7FXYq+bNI1Cyhl80RSgeodd1ihPvfTZJDO/8AnGw1/Lm0I6UXIlL1jFXY&#xA;q8k/P/UUurfQvKUcnGXVLsXN2RU+naWwPN3ChiFDMGrQ/ZOxxCvKtHn0rzd+dOnNokFv+jrMoxub&#xA;aORPUbioPISpEwChOKjj798kh9cgUAHhirsVdirsVdirsVdirsVdir54/wCcwZWGl+V4duD3Ny58&#xA;aokYH/EzgVd/zi/Egj1CTbkIIwPGhap/ViVe+YEuxV2Kvl3/AJyP003v5s2wArw0K0/G8vP6YQhn&#xA;n/OLVsbbybr8J2467L+NjZnAUvZsVdirTqrqVbdWBBHscVfDP5jLw1Esp4sCSCNiCMKH3hhViP5r&#xA;eSo/OXkbUtFoPrTJ61g5/ZuIvij37An4T7E4q+O/InmTU/KvmaGfi0N9p85WWB6qQyEpJE/hUVU4&#xA;Ffa/lzzBp3mDRrbVtPfnb3C1p+0jD7SNToynY4EplirsVfFesa41r5l80wBqcdb1Xb53spySHvX/&#xA;ADjQa/lrZHxRf1ZEpetYqhdT1Ow0vT7jUdQmW3srVGlnmfYKqip/sHfFXzZf+bbrULrXPzIvY2ij&#xA;9P6tolsJza3cVpG4XnCxVl+PmVdlaodiFr9lihkv/OM3lK9kN95t1RSbu9laQOVCVZ6liFUBQKno&#xA;NsKvoHFXYq7FXYq7FXYq7FXYq7FXhv8Azlvpkk/kbS9RQEix1FVlA6BJ4nXkf9mqj6cVY3/zjBqs&#xA;a6jcWTGhnt24DxaNgaf8DywFX0bgS7FXYq8L/MzTEv8A84nRv2fL9kRX/mMvcIQyb8h7VbXTfNMC&#xA;9E11x/3LrLAUvTsVdiqF1W8Wy0y7u2NBbwvJX/VUnFXxVrlodc856dpMY5NfXcNsAP8Ai6VU7fPC&#xA;h90YVdir5s/5yN/J6dbp/Ovl6DkW31e1jG5Kj+/UeNPtAfPFWB/lT+beo+Vb4UJuLCagu7Jmor9g&#xA;6nfi48foOBX1V5V85+XvNFkLrSLpZSADNbNRZoiezx1qPn0PY4Ep5ir89vPmoGHz95ujrSmtal+N&#xA;3JkkPq7/AJxl/wDJZ2H/ABjT9WRKXo3mLzPoPlywN9rN4lpBuEDGryN/JHGKu7eyjFXhfm384dSu&#xA;9WjvdS0s23lGGUxR2zshuw6ShTePEGYMEZeHFkKbleXMgg0hiei2Oqfmhr1tpljYwWXlzTJBxaKN&#xA;k9VY5JPTkb1GdwAszcU5bVpvQYVfV+haLZaLpcGn2aBIYVoABSp7nFUfirsVdirsVdirsVdirsVd&#xA;irGfzL8qDzZ5F1nQQB693bk2hbYC4iIkhJPYeoi19sVfIH5VeZ7ny55ntpnVkltpaSxNs23wSIQe&#xA;hpUYFfa1nd295aQ3ds4kt50WSJx0KsKg4Eq2KuxV8+/nDqx0783uYbiX0CyH3Xl7hCGW/wDOPV39&#xA;b0PzNcVrz119/lp9kMBS9VxV2KvOvzq81Q6V5cOno4Fze7uK7iJTX/hmFPvxCvGP+cevLcvmT80G&#xA;1yWMvp+go1wzn7JnkBjgX5/akH+rkkPrXFXYqtkjSRGjkUMjCjKdwQcVfO/5tf8AOOHrzz655RKw&#xA;zNWSfTqURm7lOI+EnFXhyal5k8samEu0uNPv7dvgkBaKRSO6OtPwOBXo+hf85IedLKNY5riDUEUB&#xA;R9ci+IAf5cRiJPu1caViXmz8mb/Wdb1rVLTUTNquqSpqcVosMaQ89Rmjkki5md3AiF4vxOi1ofnh&#xA;VnXkC9/MLy75cPlpNWsNHktrVp7doYxeXEsUUpheXYzIIlEcjM6qSApNOmClSjVLzQtN1S6vNdvp&#xA;vMfmO2aezkiuf38sV1G7xBPq0p+xGQsgZiY2WqcQ1DhVNdI8n+evzNv63iPpPlzkGS1ZnkJVT8Hq&#xA;ysAZWChV7A8QTVt8VfRXk7yZo/lbTFstPjANB6ktAGYgUqaYqn+KuxV2KuxV2KuxV2KuxV2KuxV2&#xA;KvlP/nI/8tJ/L3mI+ddJiP6J1SUHUFQbQXjdXP8Akzdf9aviMVZH+Rv5uWsdtHoerS8bZm/0adjt&#xA;E7dVb/IY717H57AhXv4IIqNwehwJbxV8o/8AOT9+bT82LMg056Fa/heXeEIehf8AOJ9wbjyLrkx/&#xA;a12b8LK0GApe2YqlPmXzLpnl7TXvr5wAKiKIH4pG/lX+J7Yq+S/zB836x5u8w/VrVWur++lWGC2i&#xA;BJJY8UjQYUPp/wDKP8vYPIvk630tuL6lOfrOqzruGuHABVT/ACxgBF+Ve5wqzTFXYq7FXYqx/wAy&#xA;+Q/K/mOFo9Vskm5dX6HFXlet/wDOKnla4dn027ltSTUJ1A/HFUJcfkF56L1j81XI+wDIrBHPpMjR&#xA;lnWjMUMScSTtQYquh/5xnvb2NINb8w3d3bJIZVt5JHdA7dWVGYqCfGmKs58rfkX5H0BklW3NzOm/&#xA;OQ7V8aDFXoMMEMEaxwoEjUUVRsAMVX4q7FXYq7FXYq7FXYq7FXYq7FXYq7FUNqmmafqunXGnajAl&#xA;zY3cbRXEEgqrowoQcVfJH5ofkt5i/L+9k1jRBLqHlgkt6yjlLagn7FwB+yO0nTxoeoVOPy3/AD9v&#xA;9Lhjsb7/AEuyWgWKRqOg/wCK3329jt8saV7lon5peS9WjUx362srdYrn93T/AGZ+D/hsFJfL/wDz&#xA;l5qVtJ+Z+mTW0yTRHRIB6kbB1qLu62qtcIQ9G/5xI8w6NYflnqz6hfQ2zNrc7BJHVWK/VLUVC/aP&#xA;TsMSlnPmr89PLumxOmlj63OAaTSVSIe9DR2/DBSvA/MfnjzT531tLKwWbUdRum9OCGIV28FUbKo6&#xA;k9B1OFD3j8lfyPg8nKNc1wrd+aJ0K7UeK0VuqxHvIw2d/oG1SxV63irsVdirsVUJr2CGT025s9Ax&#xA;WOOSQgEkAngrUrQ4qs/Sdt/JP/0jz/8ANGKu/Sdt/JP/ANI8/wDzRirv0nbfyT/9I8//ADRirv0n&#xA;bfyT/wDSPP8A80Yq79J238k//SPP/wA0Yq79J238k/8A0jz/APNGKuXUrUsqkSpyIUF4ZUWrGgHJ&#xA;lA3OKorFXYq7FXYq7FXYq7FXYq7FXYq7FXYq0yqylWAKkUIO4IOKvJPPX/ONXknzDLJe6QzeXtSc&#xA;lma2UPbOx7tbkqF/55svyOKvJtU/5x8/ODRXY6b9X1i3WpRradY34jxSf0t/ZScVSZ/Kf5z2zenJ&#xA;5b1Bm8Y4mkHh9pOQxVEWv5c/nfqhCxaDcQq1KtcPFbgA+Pquh/DArMPLv/OLPmS/kWbzbrUdrAd2&#xA;tbGs0pHgZHCxofkr4Ve5+TPy88o+TbM22g2CW7uAJ7pv3lxLT/fkrfERXfiPhHYYqyPFXYqw/wA2&#xA;+b/N2nawmmeWvKx8xOlsLu9kN9FYrEru6RIvqo/N3MT+A264qi/IXnzS/OOlTXVrDNY31jO9nqul&#xA;XahLm0uY/tRyKPvVhsR71AVZLiqFj/46tx/xgg/4nLiqKxVak0Lu6I6s8dBIoIJUncVHbFWoZ4J0&#xA;LwSLKgZ4yyMGAeNijrUd1dSpHYimKvKfLsn5uyfm9rUd9c6HHp0dlpb3sMUd7K4s2nvzAsBaSNRO&#xA;SriV2Xj9khdiMVes4q87/wCcgLu6s/yp1S7tJWguYLrS5IZoyVZWXVLYggjFWdan/vMn/Ge3/wCT&#xA;6YqisVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVSa1/5TLVP+2dp/8Ayfvc&#xA;VYNp/o6L/wA5D69DGyw2et+WrfV9QJPFBPZXRtfUYn4R+6bc4qyny/L5f1LzJe69Za8uqyXcEcNj&#xA;aRTAwQWsdC7RRqxDmWU8mlpuOKjYbqovzJc3Nrpuv3NqxS6g0tpIHUVIkRZ2Ugb7gjFWC+WPLOkL&#xA;+T2j+ZrSD6v5lXQItSbWYdr2a6lsfUlaafeSb1HY8lcnt3Aoqhvy68s69ceSfJt3Y2+kWVs1vpOo&#xA;T6ohl/SMjMsM95zfgFZ7kmRHq2/I4qxfyP5Y0ZP+cfdS85mASeaLSLW9Ts9YkJe6gmsru5kjEMrV&#xA;eNGeHk6qaMS3IGpxVGfn3LLFp35lNG7IzaR5VRipIJWTWLxHU07MrEEdxirNYvJHkx/zCuNFbRLC&#xA;30i30q2vbfSorWGO2uriW4uIppZokVUmNskUQTmDw9UkbnFXjXmcJDB+demW11Jc6fpt35VtbCN5&#xA;GkEEYv8AmbeMkmixSOyBe1KYq+o9T/3mT/jPb/8AJ9MVSyTzLbWms6zFqd/p9ppel2tncPI83CWH&#xA;6w06u90ZOMUcbekoj3rs1e2KrtM88eStVjuZdL1/Tb+KzjMt5JbXcEywxjq8rI7BF92xVQ8rfoi5&#xA;vtU1ey1j9LS6m8cnESco7e3RfThjiiqeCHiz8qfGxJ6UoqyLFXYq7FXYq7FXYq7FXYq7FXYq7FXY&#xA;q7FXYq7FXYq7FXYqxK+80+WtE87Xses6raaY1zplnJB9cnjgDrDPd+oVMjLXj6i1+eKvG/OX1nzh&#xA;5f8AzP8AzDsY5W0ltJj0Ly5NxZWuLO1l9e8uFBofSeUnie4BxV69baZPc67oV/c+Zra6skkmvNF0&#xA;63t4ofVja2kiokiyOzxxxXHLYUrxOKsmmE0d5LKLdriOaKOMhCmxRnJ5B2TrzxVIdM8m+XdLnhks&#xA;tFuo47Zme0s2umktIGcEMYLSS5a3hNHYD00FATTqcVWaZ5H8s6ZcQzWOiXcUdrIZrSy+ts9lBIa/&#xA;HBZvctbRMORoUjFKmnXFUVHonl+w8tXOgLorW/l+4W5S4s2eERFL53edatNsJHmbYHatBTFUFN5P&#xA;8o6pBrX1jRptQh8wmIas0t164lFu5eFFZrlvSWJ2LIkXEKegxVNNa0ix1r0DfaZd+tbFjbXNtcfV&#xA;LiPnQOEntriKVVfiOShqN3xVKB+XXkoWd7aR+WpIoNS+qfXhFMI2lawnNzbO7pcK5dJmLl68mP2i&#xA;cVZBHBKLWGyitrhI45I29W5mEzUSUSHlI0ssrdNq1+7FXnt75aHmT8wfPuleuluzWvle5jlkiFxH&#xA;6lldXV0iyQlk9RGaEK68hUE74qx7849S8y2Ply/8uX+oadqVpNZpqM62Fm9nJbJYavpqyeqpuboG&#xA;N4bmQtVRQIeorir0rSNJvF822+oal5ih1O7TT7hLOxht44P9HuZrd2mJWSRnUNAqqem5xVluKuxV&#xA;2KuxV2KuxV2KuxV2KuxV2KuxVRu722tER7h+IkdY4wAWZnboFVQWO1SdtgCTsDiqsCCAQag7gjFX&#xA;Yq7FXYq7FUq1ryn5W114X1zRrHVXt6/V2vbaG4MddzwMqtxrTtiqZRwQxwrBHGqQooRIlAChQKBQ&#xA;o2pTtiqX6T5W8saPPNcaTpFlp1xcbTzWlvFA8gry+No1UtvvvirH9W8p+crvzwNXtPMUllov1L6u&#xA;lpGsbNDNzBLLHLFLHJ6ndiVZaUFQdlUd/hrzV/1N97/0i6f/ANk+Ku/w15q/6m+9/wCkXT/+yfFV&#xA;ax0nWLC7S61DV7jWYEVgsUkFuhidqD1VFvHGzHjVe/XFW7/S9Y1G8Nzp+rXOjwcFQqkEDGVlLEsy&#xA;3McjLSoA6V+VMVUP8Neav+pvvf8ApF0//snxV3+GvNX/AFN97/0i6f8A9k+KpfaeTvN8Pniy1qfz&#xA;NPdaVb2kkNzaSLEn1h3aqK0MUUcarH9r1Klz9nYdVWU3ei6PeC6F1YwTi+jSG99SJG9aKMsUjlqP&#xA;jRS7UVttz44qgdH8k+TNFeZ9G0DTtMe5T0rhrO0ggMkZ34OY0XkvscVROj+W/LuiLKujaXaaYs5D&#xA;TCzgitw5HQt6aryO/fFUxxV2KuxV2KuxV2KuxV2KuxV2KuxV2KsV8+NcTLpemx29u63V0JTc3QnZ&#xA;I3tB9YjVUtyjyPI0dOHMBl5V5CqlVk1q0z20LzKiTMimRI2LorEbhXITkoPQ8RXwxVUxV2KuxV2K&#xA;vmLVf+c0Y9P1S8sD5aMhtJ5IDIJwAxjcpWlDStMVQv8A0O/F/wBSw3/SQP8AmnFXf9Dvxf8AUsN/&#xA;0kD/AJpxV3/Q78X/AFLDf9JA/wCacVd/0O/F/wBSw3/SQP8AmnFXf9Dvxf8AUsN/0kD/AJpxV3/Q&#xA;78X/AFLDf9JA/wCacVd/0O/F/wBSw3/SQP8AmnFXf9Dvxf8AUsN/0kD/AJpxV3/Q78X/AFLDf9JA&#xA;/wCacVd/0O/F/wBSw3/SQP8AmnFXf9Dvxf8AUsN/0kD/AJpxV3/Q78X/AFLDf9JA/wCacVd/0O/F&#xA;/wBSw3/SQP8AmnFXpf5I/n0v5n6hqloulHThpsMcpYyepz9VitOgpTjir1zFXYq7FXYq7FXYq7FX&#xA;Yq7FXYq7FWHefdU1OzlRYbu5s7RLG9ulFmsXr3d1bhGhtI3ljnCsylmVVTk9NtlYFVkmjG7OnRC7&#xA;m+sTKXX6xRVMqK5VJCEotXQBjxFPDbFUbirsVdirsVfmP5gsry986apaWUElzdz6hcJBbwo0kjsZ&#xA;moqIoLMT4DFULrXlvzFoUqQ63pd5pc0gJjjvYJbdmA6kCVVJxVLcVRmlKpvPiVXCxTOFYBhySF2W&#xA;oNQaEYqrfX5v99wf9I8H/NGKu+vzf77g/wCkeD/mjFXfX5v99wf9I8H/ADRirOfy1/LbzT55e+ls&#xA;LOA2Flb3TNccLGKt1HayS28I9bhUPKqK5GyqakjrirFNatdV0XVbnStRhtEvbR/TuEiSznVWoCR6&#xA;kIkjald6NsduuKoH6/N/vuD/AKR4P+aMVZR590AeWrjREgeKePV9F07V/jtrfkj3kAeSPaMVAkDc&#xA;fanzxViGqKq3nwqFDRwuQoCjk8Ss1ANhucVQmKuxV9Nf84P/APHf81f8wlt/ycfFX11irsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVdirsVdir84LK8u7H8ytZvbOZ7e7tTrU1vPGSrxyR21yyOrDcFWF&#xA;QcVZd+VXmLV/PdnrP5beZLubVotTsrm88vSXbmaW11SziaeNopJCXVZERw6g7/SaqvMdF0/SLm21&#xA;O51O++qLZ2xezgRQ8tzcswSKJQSOK7l3fso8SMVQ+k/71t/xguP+TD4qzGTyx5Z0fyh5d13Wlvb6&#xA;bzGbqSGCzmitlt4LSc2xLNJDcmV3dWIHwgDxriqbw/lrpx8q2er6bbXnmKXWLu/isDC6Waw2dg8U&#xA;fqyq6ykyymcUXlRaH7WKo26/LjyXoXkrV9e15r24ntdVsLK1srWWKOX/AEnTjeSW8kxSaNXQy8Xf&#xA;0m3joFHLZVB+XNF07TvMP1vS3mbTNZ8p67f2aXJVpowNOv7aSOR0CK5Sa2cBgoqKGg6YqiPN35ce&#xA;T9J/MC48iWQvxd2kUVxda7c3ULW6QrYrf3U31NLVXpHDzovr9uuKobyl5F8jebrDzNc6ZNqOny+W&#xA;tIvdU+r3UkE5u1t4mMTq0cUQhpLx5xnnsdn2xVT/ADt/3p8lf+Afon/Jg4qwZtI1LVNRkhsIGnkt&#xA;7EXc4Wg4QW1oJppCSQKKiE+/Qb4qk+Kproun6Rc22p3Op331RbO2L2cCKHlublmCRRKCRxXcu79l&#xA;HiRir6F/5wf/AOO/5q/5hLb/AJOPir66xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV+b&#xA;Q/5T/wAwf6muf9Ql1irIf+cck+rfmUnmCXaw8tafqGq38m1FijtZIxWu28kqjFUyu/IXlWzPlSwu&#xA;ZNMtNN1DTdN1HWtRvrtodQ5ahGJZngTmF4QJJSNfTIYr8XI4q8q0n/etv+MFx/yYfFWZHzN5Y1jy&#xA;foGg6617YzeXDdJbXFjBFdLPBdz/AFgq6yzW3pukjNRhyBB6bbqr280eUtW8oWHlrVhf6bHol5eX&#xA;Gk3drHDfM0F8Yy8NwjyWI5oYVIkVt9/gGKqN95x0Q/l7e+UNPtbmKN9dh1azmndJD6EdnLbMspUJ&#xA;8ZZlYcVp1HbdVFaL528v21haG7S7F7pnlzUtCtIoo43imn1KXUCZJJGkRo0jTUF6IxYg9OuKqvm3&#xA;8z7XVvzVuPOtpZP9Uu4Ybe606cgF4W05LC8i5py2kT1FVvAgkdsVRflHz15F8oad5mtdNh1HUZ/M&#xA;uk3mlm6uY4bc2i3ELBEVI5phNWUpzkJT4V2SppiqR/mD5q0fzImgTWbXKXGlaNp2kSwTQxolbK3C&#xA;SSLKs0hblJXjVF+H3xVvyTo+m6pruspfwidLTyzqN5ArEgLPb6UzxPsRujbj3xVkI8t6HfafpNh5&#xA;W8v+XtUvbyxsl+uXmtumoS6hPbxmeNbL6/aqrrcu8aJ6R5UHWuKq135C8q2Z8qWFzJplppuoabpu&#xA;o61qN9dtDqHLUIxLM8CcwvCBJKRr6ZDFfi5HFWcf84P/APHf81f8wlt/ycfFX11irsVdirsVdirs&#xA;VdirsVdirsVdirsVdirsVdirsVdir81zrlvof5lX2p3NiupWkN/eJdae7tEs8MzSRSxmRasvJHIq&#xA;OmKpp5g/M/SW8uXflvyZ5bj8q6VqjI+st9alv7u6ER5RxNcSqhSJTvwVdz361VQd7580bVrLSjru&#xA;htfavotjFptncx3ZhtpYLYFbf61biJ3kManjWOaPkAK4ql35eeXbXzH5z0rRbrVo9EgvZhG2pSkg&#xA;JsTRSKDm/wBlKkCp64qyfU/JENrqV3aj8xtIhEE0kQhuJtXEycHK8ZKWAHMUo1O+KoX/AAjF/wCX&#xA;L0P/AJHax/2Q4q7/AAjF/wCXL0P/AJHax/2Q4qkiXVxJrf6HN2YYjcG1OpNNc0RQ/D13ozfAv22o&#xA;nTFU7/wjF/5cvQ/+R2sf9kOKu/wjF/5cvQ/+R2sf9kOKpl5c/L+31PXtP0+T8xdKnju50heK0m1U&#xA;zkO1D6YksQnIdfiIHiR1xVIDqUfkbzj5js7W7h8wwyWmpaMNRhdljlW9t3t/XViGqU51I3BIoGpR&#xA;sVb8mebPJHlu+07V5PL9/qOuaZNFd287anHDa/WIHEkbfV1s2k4q6iqmbf2xV17580bVrLSjruht&#xA;favotjFptncx3ZhtpYLYFbf61biJ3kManjWOaPkAK4q9m/5wf/47/mr/AJhLb/k4+KvrrFXYq7FX&#xA;Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX55+Zfya/NafzHqs8PlPVJIZby4eKRbWQqytKxUg06&#xA;EYqln/KlPzc/6lDVf+kWX+mKu/5Up+bn/Uoar/0iy/0xV3/KlPzc/wCpQ1X/AKRZf6Yq7/lSn5uf&#xA;9Shqv/SLL/TFXf8AKlPzc/6lDVf+kWX+mKu/5Up+bn/Uoar/ANIsv9MVRB/KL85jD6R8qatxoFr9&#xA;UfkVHRC/HkV/ya0xVD/8qU/Nz/qUNV/6RZf6Yq7/AJUp+bn/AFKGq/8ASLL/AExV3/KlPzc/6lDV&#xA;f+kWX+mKu/5Up+bn/Uoar/0iy/0xV3/KlPzc/wCpQ1X/AKRZf6Yq7/lSn5uf9Shqv/SLL/TFX0F/&#xA;ziF5E85eWda8xy+YNFvNKiuba3SB7uFog7LI5YLyArSuKvp3FXYq7FXYq7FXYq7FXYq7FXYq7FXY&#xA;q7FXYq7FXYq7FX//2Q==</xmpGImg:image>
+ </rdf:li>
+ </rdf:Alt>
+ </xmp:Thumbnails>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
+ xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
+ xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#">
+ <xmpMM:OriginalDocumentID>uuid:AF0436EA903B11DB90DFD5CCDBF73423</xmpMM:OriginalDocumentID>
+ <xmpMM:DocumentID>xmp.did:0180117407206811822AA5DFA83DBEE8</xmpMM:DocumentID>
+ <xmpMM:InstanceID>uuid:a78d6ca4-ee25-5f4f-9857-efe87b47a9ff</xmpMM:InstanceID>
+ <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
+ <xmpMM:DerivedFrom rdf:parseType="Resource">
+ <stRef:instanceID>uuid:d0c80ddf-8d24-9c4a-b5a2-f5403c78eac7</stRef:instanceID>
+ <stRef:documentID>xmp.did:F87F11740720681183D1ECDDA24ACDE7</stRef:documentID>
+ <stRef:originalDocumentID>uuid:AF0436EA903B11DB90DFD5CCDBF73423</stRef:originalDocumentID>
+ <stRef:renditionClass>proof:pdf</stRef:renditionClass>
+ </xmpMM:DerivedFrom>
+ <xmpMM:History>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:0380117407206811822AABA902D116C4</stEvt:instanceID>
+ <stEvt:when>2014-09-11T14:30:24+02:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS6 (Macintosh)</stEvt:softwareAgent>
+ <stEvt:changed>/</stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:0180117407206811822AA5DFA83DBEE8</stEvt:instanceID>
+ <stEvt:when>2014-09-16T12:36:28+02:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS6 (Macintosh)</stEvt:softwareAgent>
+ <stEvt:changed>/</stEvt:changed>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpMM:History>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
+ <illustrator:StartupProfile>Video</illustrator:StartupProfile>
+ <illustrator:Type>Document</illustrator:Type>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+ xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+ xmlns:stFnt="http://ns.adobe.com/xap/1.0/sType/Font#"
+ xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/">
+ <xmpTPg:NPages>1</xmpTPg:NPages>
+ <xmpTPg:HasVisibleTransparency>True</xmpTPg:HasVisibleTransparency>
+ <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
+ <xmpTPg:MaxPageSize rdf:parseType="Resource">
+ <stDim:w>1920.000000</stDim:w>
+ <stDim:h>1080.000000</stDim:h>
+ <stDim:unit>Pixels</stDim:unit>
+ </xmpTPg:MaxPageSize>
+ <xmpTPg:Fonts>
+ <rdf:Bag>
+ <rdf:li rdf:parseType="Resource">
+ <stFnt:fontName>OldNewspaperTypes</stFnt:fontName>
+ <stFnt:fontFamily>OldNewspaperTypes</stFnt:fontFamily>
+ <stFnt:fontFace>Regular</stFnt:fontFace>
+ <stFnt:fontType>TrueType</stFnt:fontType>
+ <stFnt:versionString>1.0 2007-02-14</stFnt:versionString>
+ <stFnt:composite>False</stFnt:composite>
+ <stFnt:fontFileName>OldNewspaperTypes.ttf</stFnt:fontFileName>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stFnt:fontName>Perpetua</stFnt:fontName>
+ <stFnt:fontFamily>Perpetua</stFnt:fontFamily>
+ <stFnt:fontFace>Regular</stFnt:fontFace>
+ <stFnt:fontType>Open Type</stFnt:fontType>
+ <stFnt:versionString>Version 1.76</stFnt:versionString>
+ <stFnt:composite>False</stFnt:composite>
+ <stFnt:fontFileName>Perpetua.ttf</stFnt:fontFileName>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stFnt:fontName>SmothBight</stFnt:fontName>
+ <stFnt:fontFamily>Smoth Bight</stFnt:fontFamily>
+ <stFnt:fontFace>Regular</stFnt:fontFace>
+ <stFnt:fontType>Open Type</stFnt:fontType>
+ <stFnt:versionString>Version 1.00 (Kustren)</stFnt:versionString>
+ <stFnt:composite>False</stFnt:composite>
+ <stFnt:fontFileName>Smoth-Bight - Por Kustren.otf</stFnt:fontFileName>
+ </rdf:li>
+ </rdf:Bag>
+ </xmpTPg:Fonts>
+ <xmpTPg:PlateNames>
+ <rdf:Seq>
+ <rdf:li>Cyan</rdf:li>
+ <rdf:li>Magenta</rdf:li>
+ <rdf:li>Yellow</rdf:li>
+ <rdf:li>Black</rdf:li>
+ </rdf:Seq>
+ </xmpTPg:PlateNames>
+ <xmpTPg:SwatchGroups>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Groupe de nuances par défaut</xmpG:groupName>
+ <xmpG:groupType>0</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Blanc</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>255</xmpG:red>
+ <xmpG:green>255</xmpG:green>
+ <xmpG:blue>255</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Noir</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>0</xmpG:green>
+ <xmpG:blue>0</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Rouge RVB</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>255</xmpG:red>
+ <xmpG:green>0</xmpG:green>
+ <xmpG:blue>0</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Jaune RVB</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>255</xmpG:red>
+ <xmpG:green>255</xmpG:green>
+ <xmpG:blue>0</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Vert RVB</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>255</xmpG:green>
+ <xmpG:blue>0</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Cyan RVB</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>255</xmpG:green>
+ <xmpG:blue>255</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Bleu RVB</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>0</xmpG:green>
+ <xmpG:blue>255</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Magenta RVB</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>255</xmpG:red>
+ <xmpG:green>0</xmpG:green>
+ <xmpG:blue>255</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=193 V=39 B=45</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>193</xmpG:red>
+ <xmpG:green>39</xmpG:green>
+ <xmpG:blue>45</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=237 V=28 B=36</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>237</xmpG:red>
+ <xmpG:green>28</xmpG:green>
+ <xmpG:blue>36</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=241 V=90 B=36</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>241</xmpG:red>
+ <xmpG:green>90</xmpG:green>
+ <xmpG:blue>36</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=247 V=147 B=30</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>247</xmpG:red>
+ <xmpG:green>147</xmpG:green>
+ <xmpG:blue>30</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=251 V=176 B=59</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>251</xmpG:red>
+ <xmpG:green>176</xmpG:green>
+ <xmpG:blue>59</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=252 V=238 B=33</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>252</xmpG:red>
+ <xmpG:green>238</xmpG:green>
+ <xmpG:blue>33</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=217 V=224 B=33</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>217</xmpG:red>
+ <xmpG:green>224</xmpG:green>
+ <xmpG:blue>33</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=140 V=198 B=63</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>140</xmpG:red>
+ <xmpG:green>198</xmpG:green>
+ <xmpG:blue>63</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=57 V=181 B=74</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>57</xmpG:red>
+ <xmpG:green>181</xmpG:green>
+ <xmpG:blue>74</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=0 V=146 B=69</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>146</xmpG:green>
+ <xmpG:blue>69</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=0 V=104 B=55</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>104</xmpG:green>
+ <xmpG:blue>55</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=34 V=181 B=115</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>34</xmpG:red>
+ <xmpG:green>181</xmpG:green>
+ <xmpG:blue>115</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=0 V=69 B=157</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>169</xmpG:green>
+ <xmpG:blue>157</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=41 V=71 B=226</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>41</xmpG:red>
+ <xmpG:green>171</xmpG:green>
+ <xmpG:blue>226</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=0 V=113 B=188</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>113</xmpG:green>
+ <xmpG:blue>188</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=46 V=49 B=146</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>46</xmpG:red>
+ <xmpG:green>49</xmpG:green>
+ <xmpG:blue>146</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=27 V=20 B=100</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>27</xmpG:red>
+ <xmpG:green>20</xmpG:green>
+ <xmpG:blue>100</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=102 V=45 B=145</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>102</xmpG:red>
+ <xmpG:green>45</xmpG:green>
+ <xmpG:blue>145</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=147 V=39 B=143</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>147</xmpG:red>
+ <xmpG:green>39</xmpG:green>
+ <xmpG:blue>143</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=158 V=0 B=93</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>158</xmpG:red>
+ <xmpG:green>0</xmpG:green>
+ <xmpG:blue>93</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=212 V=20 B=90</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>212</xmpG:red>
+ <xmpG:green>20</xmpG:green>
+ <xmpG:blue>90</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=237 V=30 B=121</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>237</xmpG:red>
+ <xmpG:green>30</xmpG:green>
+ <xmpG:blue>121</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=199 V=178 B=153</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>199</xmpG:red>
+ <xmpG:green>178</xmpG:green>
+ <xmpG:blue>153</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=153 V=134 B=117</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>153</xmpG:red>
+ <xmpG:green>134</xmpG:green>
+ <xmpG:blue>117</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=115 V=99 B=87</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>115</xmpG:red>
+ <xmpG:green>99</xmpG:green>
+ <xmpG:blue>87</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=83 V=71 B=65</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>83</xmpG:red>
+ <xmpG:green>71</xmpG:green>
+ <xmpG:blue>65</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=198 V=156 B=109</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>198</xmpG:red>
+ <xmpG:green>156</xmpG:green>
+ <xmpG:blue>109</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=166 V=124 B=82</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>166</xmpG:red>
+ <xmpG:green>124</xmpG:green>
+ <xmpG:blue>82</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=140 V=98 B=57</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>140</xmpG:red>
+ <xmpG:green>98</xmpG:green>
+ <xmpG:blue>57</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=117 V=76 B=36</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>117</xmpG:red>
+ <xmpG:green>76</xmpG:green>
+ <xmpG:blue>36</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=96 V=56 B=19</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>96</xmpG:red>
+ <xmpG:green>56</xmpG:green>
+ <xmpG:blue>19</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=66 V=33 B=11</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>66</xmpG:red>
+ <xmpG:green>33</xmpG:green>
+ <xmpG:blue>11</xmpG:blue>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Gris</xmpG:groupName>
+ <xmpG:groupType>1</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=0 V=0 B=0</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>0</xmpG:red>
+ <xmpG:green>0</xmpG:green>
+ <xmpG:blue>0</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=26 V=26 B=26</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>26</xmpG:red>
+ <xmpG:green>26</xmpG:green>
+ <xmpG:blue>26</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=51 V=51 B=51</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>51</xmpG:red>
+ <xmpG:green>51</xmpG:green>
+ <xmpG:blue>51</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=77 V=77 B=77</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>77</xmpG:red>
+ <xmpG:green>77</xmpG:green>
+ <xmpG:blue>77</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=102 V=102 B=102</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>102</xmpG:red>
+ <xmpG:green>102</xmpG:green>
+ <xmpG:blue>102</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=128 V=128 B=128</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>128</xmpG:red>
+ <xmpG:green>128</xmpG:green>
+ <xmpG:blue>128</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=153 V=153 B=153</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>153</xmpG:red>
+ <xmpG:green>153</xmpG:green>
+ <xmpG:blue>153</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=179 V=179 B=179</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>179</xmpG:red>
+ <xmpG:green>179</xmpG:green>
+ <xmpG:blue>179</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=204 V=204 B=204</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>204</xmpG:red>
+ <xmpG:green>204</xmpG:green>
+ <xmpG:blue>204</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=230 V=230 B=230</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>230</xmpG:red>
+ <xmpG:green>230</xmpG:green>
+ <xmpG:blue>230</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=242 V=242 B=242</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>242</xmpG:red>
+ <xmpG:green>242</xmpG:green>
+ <xmpG:blue>242</xmpG:blue>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Groupe de couleurs pour films et vidéos</xmpG:groupName>
+ <xmpG:groupType>1</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=123 V=112 B=49</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>123</xmpG:red>
+ <xmpG:green>112</xmpG:green>
+ <xmpG:blue>49</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=199 V=163 B=21</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>199</xmpG:red>
+ <xmpG:green>163</xmpG:green>
+ <xmpG:blue>21</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=182 V=165 B=85</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>182</xmpG:red>
+ <xmpG:green>165</xmpG:green>
+ <xmpG:blue>85</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=235 V=208 B=101</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>235</xmpG:red>
+ <xmpG:green>208</xmpG:green>
+ <xmpG:blue>101</xmpG:blue>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>R=122 V=111 B=185</xmpG:swatchName>
+ <xmpG:mode>RGB</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:red>122</xmpG:red>
+ <xmpG:green>111</xmpG:green>
+ <xmpG:blue>185</xmpG:blue>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpTPg:SwatchGroups>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+ <pdf:Producer>Adobe PDF library 10.01</pdf:Producer>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?> endstream endobj 3 0 obj <</Count 2/Kids[7 0 R 8 0 R]/Type/Pages>> endobj 7 0 obj <</ArtBox[224.515 66.2373 1617.31 1080.0]/BleedBox[0.0 0.0 1920.0 1080.0]/Contents 732 0 R/Group 733 0 R/LastModified(D:20140918115258+02'00')/MediaBox[0.0 0.0 1920.0 1080.0]/Parent 3 0 R/PieceInfo<</Illustrator 734 0 R>>/Resources<</ColorSpace<</CS0 735 0 R/CS1 736 0 R/CS2 737 0 R/CS3 738 0 R/CS4 739 0 R/CS5 740 0 R/CS6 741 0 R/CS7 742 0 R/CS8 743 0 R>>/ExtGState<</GS0 744 0 R/GS1 745 0 R/GS2 746 0 R/GS3 747 0 R/GS4 748 0 R/GS5 749 0 R/GS6 750 0 R/GS7 751 0 R>>/Font<</T1_0 752 0 R/TT0 753 0 R/TT1 754 0 R>>/ProcSet[/PDF/Text/ImageC/ImageI]/Properties<</MC0 730 0 R>>/Shading<</Sh0 755 0 R>>/XObject<</Fm0 756 0 R/Fm1 757 0 R/Fm2 758 0 R/Fm3 759 0 R/Fm4 760 0 R/Fm5 761 0 R/Fm6 762 0 R/Im0 763 0 R/Im1 764 0 R/Im2 765 0 R/Im3 766 0 R/Im4 763 0 R/Im5 767 0 R/Im6 768 0 R/Im7 769 0 R/Im8 770 0 R/Im9 771 0 R>>>>/TrimBox[0.0 0.0 1920.0 1080.0]/Type/Page>> endobj 8 0 obj <</ArtBox[6464.01 6726.74 7856.81 7766.5]/BleedBox[0.0 0.0 14400.0 14400.0]/Contents 772 0 R/Group 773 0 R/LastModified(D:20140918115258+02'00')/MediaBox[0.0 0.0 14400.0 14400.0]/Parent 3 0 R/PieceInfo<</Illustrator 734 0 R>>/Resources<</ColorSpace<</CS0 735 0 R/CS1 736 0 R/CS2 737 0 R/CS3 738 0 R/CS4 739 0 R/CS5 740 0 R/CS6 741 0 R/CS7 742 0 R/CS8 743 0 R>>/ExtGState<</GS0 744 0 R/GS1 745 0 R/GS2 774 0 R/GS3 747 0 R/GS4 775 0 R/GS5 776 0 R/GS6 777 0 R/GS7 778 0 R>>/Font<</T1_0 752 0 R/TT0 753 0 R/TT1 754 0 R>>/ProcSet[/PDF/Text/ImageC/ImageI]/Properties<</MC0 730 0 R>>/Shading<</Sh0 755 0 R>>/XObject<</Fm0 779 0 R/Fm1 780 0 R/Fm2 781 0 R/Fm3 782 0 R/Fm4 783 0 R/Fm5 784 0 R/Fm6 785 0 R/Im0 763 0 R/Im1 764 0 R/Im2 765 0 R/Im3 766 0 R/Im4 763 0 R/Im5 767 0 R/Im6 768 0 R/Im7 769 0 R/Im8 770 0 R/Im9 771 0 R>>>>/TrimBox[0.0 0.0 14400.0 14400.0]/Type/Page>> endobj 772 0 obj <</Filter/FlateDecode/Length 3434>>stream
+Hԗ[o%WWiqA`K\Ϸt&y
+sNuWZU7޽MW7]߸`)ΡG->ٻ1૫-N5eS[7ɻ?-<`O? {W?ӯTKD]%%5w璛5yZDⓜcε\v7
+_:Z{:.`nQ,6fE-Msx,/51*1y2B
+w:
+cqb;O/_=w^
+?zaaLfҜ{8e8!W,!?PO9y L뗸K)<oJaj:H.?mX
+貟+'PU#om%9(4)uH# +S"/|]yx2zRy3Ѱ\C[rƴ)7չFVC;u6-;: Ҫm
+
+χ!LzD[HK߬H!
++1Cɐ*-<5&]a5/,
+4>/c,k y3Ez{ՄMIiE6޺-p] N+N!:HF,RC,)!U]aW :V g;+WtHu<j8%ےbTţ KZ0EgA ] ),2IۄÝ :9QuIaB^a`X;*-nmw
+__)δ TY^ &vypJOSҟNI)߷G񿻹|vܷn`&k &75Ћ,,ۃ6/+D`X3욹5i:0BH6'53R'Y⋻Otw6^hӋOqܖ }g]]xw~pQ=-yyTY CֳĠp yと.n Fͩ q>w`u߾ު/3 wƒnIrūF:O WGWNgh=活{z7o o5Qp;|B̭{ 4tV]Z.ytP߀z
+wH.lO^_^7[)@#IKuxZv䒪w˥ǵ IbeߍuhO?5%mo
+9 XϜǽ
+ym$1ڽ"dJ~#w2J +nn)|ѐʟ PDoO$ lOpsfnBHexWumDѧ.nWMe#ɭcx=OAA<be՜OBI<WBj1VTqP)l6j2_[zUpv!
+2$?ܦEʰ(=DE84fd|WuKs8deZ};bf ),G#+2OJ}u)vonR QX; N"IŒRJ!=:hj @jua2j;.5A7JO^Uؾ\[jp_)K
+ ",p2U? G cAlp^0lw9U4^Kel.Fh)O|#}[tg{(NƹN6.U1Y*j+~e"#z-+VB taψ;/hqHA#+q=uaO"pְd#6P|9XhVY&`ٺ\#ž/$rː*fJO~j~>`U4T OIZĶ8|%]2(.%.c
+q
+/GS0 gs
+486 0 0 480 6513.1162109 7207.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 780 0 obj <</BBox[6557.12 7644.14 6955.12 7246.14]/Group 791 0 R/Length 61/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 792 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 793 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+398 0 0 398 6557.1162109 7246.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 781 0 obj <</BBox[6625.12 7519.14 6893.12 7475.14]/Group 794 0 R/Length 60/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 795 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 796 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+268 0 0 44 6625.1162109 7475.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 782 0 obj <</BBox[6622.12 7549.14 6896.12 7246.14]/Group 797 0 R/Length 61/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 798 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 799 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+274 0 0 303 6622.1162109 7246.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 783 0 obj <</BBox[6647.5 7607.5 6943.5 7516.5]/Group 800 0 R/Length 48/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 801 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 802 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+296 0 0 91 6647.5 7516.5 cm
+/Im0 Do
+Q
+ endstream endobj 784 0 obj <</BBox[6485.5 7766.5 6957.5 7240.5]/Group 803 0 R/Length 49/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 804 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 805 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+472 0 0 526 6485.5 7240.5 cm
+/Im0 Do
+Q
+ endstream endobj 785 0 obj <</BBox[6522.5 7682.5 7035.5 7180.5]/Group 806 0 R/Length 49/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 807 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 808 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+513 0 0 502 6522.5 7180.5 cm
+/Im0 Do
+Q
+ endstream endobj 763 0 obj <</BitsPerComponent 8/ColorSpace 736 0 R/Decode[0.0 255.0]/Filter/FlateDecode/Height 87/Intent/RelativeColorimetric/Length 321/Name/X/SMask 809 0 R/Subtype/Image/Type/XObject/Width 310>>stream
+HgN@@uޝad"xVzj
+slBXiniYBru\(N[m.LCN~ݬ_֛n840-!+b-7NӴIEWLn5ql_l˯6t^QETPc`PO՞诪
+Hg{Jc6'9 Lw
+jH!T2#Wu\dt3           }? ^ؽ>{}`s3fz~p?xxv/^zkZ욣f(uP6ʲ$vf~{iv] N 0Ʒ)v8XL c<麆RPQd$ <q,0>Mӽ^ ˋfXTW3
+c>vڭf^Vʥb!fJ&s$>yƓTb;'gL]7JY*GA(x"t&rZ7v"BUSjz9w,$Pcˋ|x;.n *opԙ/NsXүRD,^_~ݿ?/..>]=>4l{S7`yHʕj+ϝ'GGD4SժL*y|-U`58;cM+phSu4uQÞ>YܝjLJC CC2ߴ!_ FSkTMgOofb#M{?-3-lmd8 M;)*S쾬4Fl}O1n>w~KƟeaX{Zw~=ې)'Vv?!t١ F= !5ʕ[ 'Ṿ-ڈ5Ն͊N$Lef{
+=kr\y=]<;15FtDn׳&
+NC1޴rcӳмl v'y%vr^XF@/؝Smh_>_f ܳ7 7Բw嬽f#zϗ?myKVDD[z v"wv,&n@6}r_ϴ;h!;,Uv_Y1*C7X wj"+Do^:k 휩c9/YGAv''Ʀ=`;1;Ұ$JDO!"v]r =UX3 vY6r@ zC͘ 얳)XnWoq{N*9ާMcv4rDOLgvHXeX')>lVDE,'ʚv/tۆ& XO9:Y* ݣ@R
+;ͦc^Gxq8B<;ۍrNq``皺<`\!z@LV<ڭ6cxY8`h7$Zsj]m"KcfIhVvCaFAz.+~kuvvӑ,Wֈ^e}
+vv˩.s6VD$m
+F`G;[/\ʧ/;:ۀYG;S+\*"zb͂]LeB%I-T[vL`D<A2Jvvvٰ,X.tܠ;эRŢ1^T] QGe]VHs҅jSSI, G^Wڲ vڕr^X/A- [;rMʼnܓO/ʖ:5Kd ==>X2Sl]-5D=^4.Ekvg*B!5 vky*rD/rUa
+va\*J}zT^hgj6rwwD/T`e',w{sK"4ؽn:G,w=T7`wb|1{_OX5{xJ܂i.x>]}"zI{JH]]^P$Qd' ; %X>`BvixnN%k8\noww Iq/>0٭Ozk\0˥!" ESYihx&#AzD/Ifh7e
+rzYXi턍- ꈞŎv VJ1 rZmhq xYM QkɊvNQrj-HavS)UD`F;X结Be҃R$zz+_ܢ1;rN]g1v 'ɉ oЎ߸dRӠxCB.ЫAN*=q;I$DOvvO7٠]9+S,Sx#v&`Anfzɵ6uvxvNniv=^~Frl'X}ts
+ܯ1=AYRѓ-ػ쿠/'ON>lhwߠ)J2ӛT;{Cv#߾_b{eRαtCo_X9ɛ\Ўߤ+WVKr#ܗ/oVa$g%fej響/h VN51g8
+X[bJ(z r{Pvȹ=^_=^n舟x@<^}D=RT vbmӱ"zDctmv]<@qz G8ډ5idz ‰t}]6 r@0̠HCf. r`(6vn"ӱp0H8`(sh'kzUP@.|s
+턁]1àrH4.HCv)E"rh,)N1T<=l Dz;grt"=ti_?A;~`Wx@.Hykdw{P)S\"J7h' L%StXA;v/wGRN@R4-Un"34M15x3(Nt&nNg,4\:-Kh7Ҫs \&+Vw/h'[mWKl@.˗lhzY1\qr|W;h7gPg<\Pdkh7dcQ.
+rbXXܣ0nsYaEb47n]tkUeJ%T[K'h'^i #rL^BɆvoze
+×{s{a;LwHj| u4({U[^^׷b:%kr
+=ȽO;ͥTct=ȽNz6~G`Wy' 7O曣T{b:Crt={աzLg˝bj"Gۯ) 7W{Վh';^fЃlX4'{]YЃ|u'{iZ,r=n\.Me^.jS /Tc߬VЃj٫N
+vvخЃz=hV@;. lvw ;.r~䶻pB=.yavb8QN;Ʈ=S=ЃpTM7*h';N w84ˋi'ؕI`=OnqI;. C=N=Ui@r Zek
+=ȩn!<4AN ˋJ:}uAN7 ۏvR[]āc^'gN紓kE8j g+7=ڕiڦ =ș "ϱ,Aβ/JK5v1cЃ8NYNOrYE;9q Vr}iܽ=$]zs=?L4].σ<?Ң\k1Cr~@oH0A.=Qhvbh#A.# ډM0(SA:"Nh;]i=E1#̒8ʺ9ivbhSA.NRaw\gWi@rI@8R,A.
+ ˧ fv7uvug==e
+ Է+T,/h7Xy=nchˢSA,]?؉?A zʪꦂvzv:7]uӃ\UwSA~]7;˹5~diׯgwE{.W vٹڣ]٥vpk7~qTn|ۏ~qTyyyc1c1c1c1c1cO
+HI
+Hҹ0Plc@ P
+kku.`A| i
+H{_t:if3q ."&"?#i~oaaaaaaaaaaa/Cv/^ڽ<{yyo~?۷ݒ?h;r߱Kfٜ|;^l.zɕ9I}K҂dsE2M˴z2c"^جWK{nNHO۶%Η9c|wnvϳ!*\LӦxe4e$!DZ`0>Mӽ^̀$U3=swZ΍",|9@aA/HQ'iA=AvO;Z)eDDrӽnj6j\*l&MQd"F"ch,r@z2~֎tAᇇAQdҙl._(ʕjhڝnϹ=,7*Xn6a]/&v(rۛϟ>^~_]8Z-ܯPx,t?|/.>\~.t)̩bu{U=3^a9Hʕj+^>O/O%br|"_Jydpk}|MjctX(Vf-,'6TH% }|@SɴǑUW\{\9w]iLSt2ɞš
+4U[s^suQve[nwDm|7-aWl)/ى
+f)€f zi|݈gz ר7@ 婉vvT WA礉vvDժ5Ь4Fvkc,6U+UkNWArrZ]F@N`:M+ʠyezBv3 W*@Ѧ# VhH W,Aq2\YB
+E#QKtKjJ\$$E |vJJΥV9{r^<)55K#=?c2]l]@;KlӉ݇A/
+uܠy]@ţ  ^4NkhNށ^1 vT̓blmLMF ws}zh"SeH<vAa%߃+л?R%V_y`G=C g chю+S;=DSŁ~B.A>Dvk-%#p z7pVhw%w ]߅hC8`vד䜳D"#298"mU#շ30qp,o_X`KEC녢,ډշ{id Q^
+Dv]&@8F;vt<9zPn^tNo @r/E;jL4˹.^/IU6λh'is:Gh'Z߮{Ye>
+vQPv! rvtD^Tj=Hkgh7o gXAMNrul=gW_No8Al2Ch56A g2@c_wh'le9zvvqv/glr@Π7On!;:=Yh'^J)[@NՁNnn!;-mh'tXM Qk@dumkb9J zF ډ=ߞ,"nTJ̮Pn}v†LzS*7x=b>2@N!W f珯Nа29i h'^ 9 Ie;#kN rY)nB|6Ы@nvfT:?:<hvJ=%Mh`w4 u
+LBk1kNJ$?%'G qvVoA4Vi_;^vٽ>vϪ܏?@OЩeF#r߿}'zh7~-j}IfUf*YٽHB/_zhqv˽z6f9ܗ_z=ɛ\<q;NϜg=n-I3ܧO^oZa+h7ݧ>[_.v Q1M"x+oG;AFow]{ r OSwEͳ;x~<>ُ=^7ڢJ8^_̪T{E;AB?z}Mo9h2>F6 ~Jj Hv.'F;ߦ̀i4ؿz|EFfgywYgt}4 u
+T*|LvFd2)#r2&4;z\zDN.WtS)G
+M{i/BNZ="T@f FӨTGT* Mow^̇]FZ zDN1ڍ5lh@i4:#ډ׳|{TMzV7B3 3t:#r:vqv/wŨbAF;_lߢ!;`
+Pn vvArs<t
+b: AȅS@;vsL2DbtyB;a`fSh\4OeJh'ֻ<cX,d^ rIG$C;v7 B:HK$R|yn턁]aI#r$)TNbeR)#r-Vn<4Εj h'һRgdl}"vr!NKv" +J1ɀdrrsչj)͂fJkD۽޵kl!r[[=FB-G"[_C;v'rP
+˂cJ}ne"vk j zD\6WwN]lo}YT@U*ڍE7Zj\Zo-ݿrJsRΉ`n]w*SO!%^^:Vm4(NqN-ﯯGrV]Hcin AdgV;h밓q=|}|gnXk|2ɍFhewlHn<.6'3`7luM&GrlU,a:tj>MNa'\^fGrbלv8n9|t7^k{iZ,XhN[/Grzw2ݠ. ,uYXV^1}IjRakzz$^o$v~m6Gr"ݰ. ]nYIwDz \S9vGrA10ݰ%<S=GrQ5vZ"-t8'v.uxd=;vZ2NGrjd]*\SS#9ELG$vZ* ]SWU#9UMWVYYiaaZnXgۦi{QV]a78pL`=3 a'awX鱜iAװڝDiə트oDvezeY㉤8nXgmٶi ;Iݥ8Gri9b==s=ь 솱] uY\q֮ư{N ^y^9duvi$|HEzsЊ `= v%%C#9͘$km$둜QъȮIGra%Y;Y]QrQ)X>
+IRfI#8Ɋvخiqή$a=K4vO]Y#4+*Ic˗;I]]YO䲼1 vEeYQְvͰ(H.ϋ
+vsЖEO䊲͎Haw$k>xU#a'ܧ`T\UUv:빮zz$ea'o`ܧv\7~[£vO쮗s}5]vOݟ:S vvWqۣSu.iVwy7Ͽ}ڿ?oo;B!B!B!B!B!
+H 
+H 0Qְ+Qs@+`䓋
+vG_
+H΁@
+H z--JRMX49jV&""""""ڟŇSrM'crfG
+'3ݞ<SSvA[*^HL+{,[Uw;лƔ0WL9E@}S T&"""""
+Hͱ
+tttlllfffaaaZZZTTTOOOJJJDDD???;;;555222---)))!!! 
+HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽
+ 
+V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'K
+x-
+ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9
+N')].uJr
+ wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4
+n3ܣkGݯz=[==<=G</z^^j^ ޡZQB0FX'+t<u-{__ߘ-G,}/Hh 8mW2p[AiAN#8$X?AKHI{!7<qWy(!46-aaaW @@`lYĎH,$((Yh7ъb<b*b<~L&Y&9%uMssNpJP%MI JlN<DHJIڐtCj'KwKgC%Nd |ꙪO=%mLuvx:HoL!ȨC&13#s$/Y=OsbsrnsO1v=ˏϟ\h٢#¼oZ<]TUt}`IÒsKV-Y,+>TB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O
+zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km
+H
+}}}tttlll^^^PPPCCC222((( 
+H1
+bbb[[[III444///+++###
+
+
+
+HA
+tttlllPPPJJJ222(((  endstream endobj 737 0 obj [/Indexed 735 0 R 121 823 0 R] endobj 813 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 316>>/Filter/FlateDecode/Height 345/Intent/RelativeColorimetric/Length 604/Name/X/Subtype/Image/Type/XObject/Width 316>>stream
+HA
+}}}{{{yyywwwvvvtttqqqpppnnnlllkkkhhhgggeeebbb___^^^\\\ZZZYYYWWWVVVTTTRRRQQQOOONNNLLLJJJHHHFFFEEECCCBBB@@@???>>><<<:::888777555444333222111///...---,,,+++)))((('''&&&%%%$$$###"""!!! 
+
+
+ 
+H̡
+rrrkkkWWWPPP???444///+++&&&""" 
+H1
+|||tttMMM@@@***!!! endstream endobj 810 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 316>>/Filter/FlateDecode/Height 345/Intent/RelativeColorimetric/Length 603/Name/X/Subtype/Image/Type/XObject/Width 316>>stream
+H
+H1
+kZaVX+
+kZa
+~~~wwwpppiiibbb\\\VVVQQQKKKFFF@@@<<<666222---)))%%%!!!
+
+
+
+HnFE/ b-Q Ϋ
+Hvv0< @ Ivot".V[
+c$
+Q _?c'I>jޟ?6S<?/EDƟLәL:J&T |,{>mT&ˋ\_sGe|TB^EHT\_zZ)T $
+К
+`<<Q`r|
+Tkcs:M:F\)
+'*hf9ﻎ5[RN  WZsa; +@
+p$FotA[-S
+W\7@:_mgkw<9)_'~=w%P&@c'<rRz)v
+@[0_
+Q噤8Eᨩ*0{d@B$6ǻy=#:*'dĹyLe
+4{4tCoȟOi
+
+^ʘ
+GL N$_Y}5
+rR)Z;(s^Ԕɰ Nƣa4
+O?o,o}] 0܃q׶uJ
+&|՟̌߀/oo/O\gihP
+,
+R5`9||~<ǘInTY6@HK@IR.`/ vmiLO N(/@ =<==7@TCa?ř_Y8h׊PT?G0L|b;]ms8Q0'>$
+<I'tP˪zE
+ Y
+ k! (rV4M(Xb@끅 4( _Bx%hbfTk*^D_Pub6@4%IE~_[UXb8 O!{
+l
+?-2Y>}`򉼦owzvqz '|o~  
+o S\:$I($eR
+k5;V#z:ڭ&å|}{A{fXt2mNpa6X$;ՙ{>
+{O39O2}+TbB}]k^p<<b9n%:Na#vZgR>/t0u;;wf9slۦm[Te_$d
+@}g<E+p
+`LWg0N I>
+$h
+'oiz26t*^X4B_/.a
+_`DgӢςHX6x|}]
+
+q
+/GS0 gs
+513 0 0 502 6522.5 7180.5 cm
+/Im0 Do
+Q
+ endstream endobj 832 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 833 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 513>>/Filter/FlateDecode/Height 502/Intent/RelativeColorimetric/Length 8911/Name/X/Subtype/Image/Type/XObject/Width 513>>stream
+Hvv0< @ Ivot".V[
+c$
+Q _?c'I>jޟ?6S<?/EDƟLәL:J&T |,{>mT&ˋ\_sGe|TB^EHT\_zZ)T $
+К
+`<<Q`r|
+Tkcs:M:F\)
+'*hf9ﻎ5[RN  WZsa; +@
+p$FotA[-S
+W\7@:_mgkw<9)_'~=w%P&@c'<rRz)v
+@[0_
+Q噤8Eᨩ*0{d@B$6ǻy=#:*'dĹyLe
+4{4tCoȟOi
+
+^ʘ
+GL N$_Y}5
+rR)Z;(s^Ԕɰ Nƣa4
+O?o,o}] 0܃q׶uJ
+&|՟̌߀/oo/O\gihP
+,
+R5`9||~<ǘInTY6@HK@IR.`/ vmiLO N(/@ =<==7@TCa?ř_Y8h׊PT?G0L|b;]ms8Q0'>$
+<I'tP˪zE
+ Y
+ k! (rV4M(Xb@끅 4( _Bx%hbfTk*^D_Pub6@4%IE~_[UXb8 O!{
+l
+?-2Y>}`򉼦owzvqz '|o~  
+o S\:$I($eR
+k5;V#z:ڭ&å|}{A{fXt2mNpa6X$;ՙ{>
+{O39O2}+TbB}]k^p<<b9n%:Na#vZgR>/t0u;;wf9slۦm[Te_$d
+@}g<E+p
+`LWg0N I>
+$h
+'oiz26t*^X4B_/.a
+_`DgӢςHX6x|}]
+HAnVDA䖚OfTĖ oo
+
+MH'Bz .5.oŮoy/tO~[~F=}o|u3& j]p[]ï҈7` x 7̶a#w_}T=rqg>!pU=iQEA-Q}L&t&t˚>Sd$kYugIV]YUaeuUUuEJ:ªα4SZimV]ڬ!k'Y+Pթ^1Utϯ UŦzUYRd$k'U+INvҵd$k'];ZJNvd$k)Y;INvUuǪʺ Y+ZINVeu ǚʺMuwZINVt$k%k'];IRvҵd$kSYu\UYӵNfu8Y+];INVvd-%k'];IRvҵtk)]KZJNvtk'Y+];IJvҵdk']+INvҵtk'];INVz6{tlV]tV]w|V]7r 8Umʪ.eu꺇Yu݃gu vҵtt8[еtk'];INvҵtk'];INvd$k)Y;IRvҳd-%k+Y[JU
+HviEV$9TE3Bm?̜U!px<=dx l{B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!(|8|4*?3yts"V\!nt~xPN? Jťpqg6O˫kq}}uya%$w^;.r9aʥz$K3hu5W׽RR%egёBK`0=nK_k<;%pk)jz bp'b/d^Y >81k.*Wǒt:@Z)ztA! <(|خ.?OgB.u)b/NG|(A,
+"}o~%'Èk(Η*F\$~NW*[7t&H!yI=S/vzdlju٢JkVbT*҉H+wQUE/U̙ao0)UNY-gaCZ#1Z,fZ5t\U}G+EJ_1}dhtz^iR bVh2jVըV/S˨ (h]Dϥ:44]{Ѱ׮0B SB٨5Z^mլ8;.a2Nsʧ%aPM^v6YYX'ɰ[/g`5KC1j֛p< nf=j<%q\Z>S|vT5g|65+8E`4-Tw}8dzg!@=?U9&A G衉D*\«J`X'|#S8 FrZikk= {Cva|')\.+ET5,ֺ#[L±T\mvzllnWixn5&[{ ղ*RT8 Z>dR|fsRBP.楈SERxZ/gk؏‰lĕ<̗vDM;Jꋋ7|Jzy ?
+#*P[(:hh6jID^O揤KdF mSMJ ^v\MDnܪdehB5e-h<ΈbDhJ]F8=hOW:lD8I\K6.֛aw[&'g*y!.sWn$ 5UDbbVZހѶd2qze+l٬d$Ry3b_T`F1J"aՖ\ݙxDH6֥BJ*VZt:]`KWY,l<C`,k^k4 F$ty+a}~y}}}~a: \fRIHX:dox"B*j;5
+ehu{pda],Wrmnת ~lIM-j! #x!Z^^?V^mJe3T2L=nam'ۉ?
+~]5D/Wi9_V?U^l#~7l*kKsXVETJ~jifVc{uM(}<Y/C۬=LNdmVBdQrV֒˯lUTeb\Kb[*@Q#BǶ(詓*3k aUb:굪 &ǣ&ɨi"{RltQ^
+2_3~Cj90YU~ױoVe8HcDG"lj#͢z
+U07~1Fr'(
+ىa ; aի\*8mv
+6/waVˌ<`jOX,Z>l1r$"AvN
+JR-V*^lҷTw'jkI9{YG泩X~cû}X*WV,P9 1daZy
+8,ޡmݡ[GTT!}vFF4Ɍ'32WIBmԪ$ ֫+(5ĥ@ ۠<%Ѕ +2tfK'/NSXCNZT!E,#OQzB0g- TmT5N%Ѱ^#K堒%4ŢX<A
+S 0ue31`ŜkըӱF {
+XL2Kk7" Q%֥ȥVP^Vc< AYE.Z #H8
+P$Z,RZ3znZ0yCé猅%aݨ2ŀ=qTŒTw;&VQ+ul4z (+e^TDju_[/-{Wpy>^ʖ\
+W&G:z}M&\dn h@[᪸Z)[SY-R9ѧ*qF G+6يZe`PsFڥfR^8.NYC!)ҀUMA%3ڴr=L]
+z8sRk$@jz
+ƹ"QH+حHץpUT.mifi׮z}}@ʪ$PĎ%l' W)an@dJ.-񀘹|: \v)^s2.
+h; UAݬf+5{IZ^^eOT2Մjva{|Ņ\/"LTk0r(0avho&UzL G64)RpȞS7#%OsS ^{Zy0x*tp`(bWԱo,Ei$XӹD C1ݭ6Z\5MzWe/ϳDm\_qfAQWa/,s~0EC`#=nJKU^)i@ ҩ]|ABFU<xQB*2ZT.$V]ݻdUIl*4Ɠ Z&Q`t6cPb|&*j~'z|GU%+]a0l"NBNa
+jcKZ504
+T^]LS5 x r,Ɓc}rUɪZ,!6x[.3q]A%R2U}=3=9$sьѠOXMnZ5]\WXcнmpvlT oQդ>'j/N{LڃeJVAcK߁\>aWɮDs9T_/TA`;C"-R-M\*9S %U*k?M^mV˶ .Knn'sL:KWkj'0H%Xz#jsxx:_Utrb:pd"έJXQMM9+ D~ڠh€:wZJ!э[
+_%~|W<LĚ̕mP%z[ Hud`vZj1T_ogG+uN倵q;V~eD4RU H<3}VKfg8]6v%l<wo[7:k6 .eo*^Q^
+8tZy%Zk4Hk/V<vkۘOvV5MT$^_ 3hKۃ1ǯoƆ
+VKY5O[Ot߮ǭcFVEEV"2d|BRpSout6ە kFfU(JĖJ*M,xA#k%tNFҴ׻z6*Q!E L&u8`D!Bm½x:[,͕Nts64ʪ,9<DV RI8ɰT}|4Lv7~`;Z8&Kg,C$ׯ'u</ 4kYI]׋f3id! Kz 7D
+V&ղ,jM4pƤ)P4J&T:C3l.c?kPg0
++ܭyܭܰlǶWsXx
+ZU]a&p2"x~#'d:B_k*
+0A( @^n#RfA-h \Эu=ϗy%+c_/X NR /^{{z^ֵY%fMo;>2r9f|^uЮH+_}՗1TxUHq\Gzf0qH>+_wJZg4[X#rkVԂwu)dٟp|{1j/ ltbdΉj=ZጭB{MN]W,Z 'ԍ4E|Aw'=^_^ .ƽF(rwd5RfiDDؚ` w+
+պnk1*3Th 2)frp>_P+co׶VKZ v2LN%YVdPpfzEZmvMrDkЭ墦+iV5ZO Nʘz-Њ5%Z kBjzn[F^o:ttCk" ޙ`'/PB\ټU[h<~FӅl~ZL*Np[S"$f 4b>L&Srvj%pPz;z٬]{e(l?pV!,UPMVomzo
+ap<Bztخms>aB`KgwzlqqdЮE5\ ~C`EoWXکO[`X3Na2栵QV%%Z
+,Y^*UÙi:]]2Q]/ӉXh %I,kplqVNh !X4^ &a,ɨm7j"!8*"~`asb\Gt2;zU/)<k}"ZDT%\oV+
+V\6>!iXBĶ:n'0lDkVA+xEDT5&`[Mae"ZÃUd5R*WkZEG~XЀId*ar岯AzDkH+HM$T*Me,"t+ރcODkkEAMS $+ŢRa
+p7d
+tfe
+Cqךq\eˀX!/ycq%Z
+G+E3HV4E0iMi
+UQdnX+kx`N EYGҷ_xZS{\ժeM%Z3J|_YNTJj5UeQTjh TURzvbE8q_sIq, Q]UxF rH jY\ w7D|' ﮠS= R13-2P1Fh,N3n^ [-!XcFD2LRk~+%P`8(6ЩT*L| JMv/{a3^^.dSWVԩ|X*˕Z~&] ,g۬sןd~!"ЩJh4v?b˭t]S,7Z)ND^]1P‘X^%ix2_{E;v5z9Ah" 1MdX ˶-CSn26k<&1CPt6gYnI t:9QE5B'lS9\7~<1ک_$bgjv{Q a=_ΞcDcZ1 _zdqT-=_|_7+_oӟΜdXm6iܳWV3b8Fb=|٣&ZѺzz]/K[ Y_S9T|E`z߮yدĮx /:фоAX9GYXL]J~{ qV~|||m3V80waCag TwΗnqK<;!F]qӬx2xSBPPw] ﯠZFcd:[(_i&hW+os]P1GcD*Jfwpa_`0tԩv%vAG8勥Jg^9AܰtYJ슃5/+zZrS x]@]"]*VjVŘ[oEU7y.`Ռ6]qp\m{ј̹Fhئ4*4+z5+՚L Y7dةD5)T]U6*np̣rJ|]]^dXk[Z;7* 9=hUD슅\!!3_;@* Gz)G^vA˕]m]2, `~h o.zzf+Vױy/!vF_S٣N z}!a=a铉~:vaW
+n{~\osrH 㤗Kfp؆\?>׳K<;z%1돟ȰB \蜯'͜PMKA1Npl&
+9׫gGwQOOjk? ׻2R*Wr3ffyI`]ZφJP$qӃk*_nj_xJYR8*9TTŃy.1-GL^D^~s|%Ⱥ鸮cjfNW!ox*[6{tUݴmSWd*zR/?~&92Jaȳt/\? WX2+!|:U^.ވ;IQUy~b++fz5vԄ]m"ؕ7 vE|DYQ= | \]J3OXHaQVUEڭY+ W0<2:۽>0
+b=ʕ=C/aH (oDPmdzVTuòmda9*a bڵTRV:ܓZ9$a p< Nj.ںstkÄ+Vzp֡fj8]/K9;WpB 7z4K^7Zj:lzS//h$sfYleW@_oS
+qܳgҤ WR^\%t\읬C1\͓zR{գi;mUa\+B\#z'iAV3?rH@Ѣs9$HsmY51 Pvf5՚{6ҒUf9yUOr^Nr:Kyz3k<[|>DԊ*fNx:n=7Z!MW%9HBt U:Ui*FROt\a{JrnTFR_n"3Tmp|z~~z;Gk'/>r ˏorotU*qpo</^^^_U&誖Ϭh"ct+tUNֈRO÷y;rnY2k"/7|ݺn]`/aYӹbֺ>xMΫDWu80,R|}8?ۯCWE|<px2+Vw06ۣ~ihb:y>"t}|~~~ZٰSͧAGDW9X2+DF d:_k59G2kVo4[no02zw8Za2z1cMtUx*[l۝֗av۬f~Ka&u؟WHkT7۝Nj>jZ (5lRƭk,pG9}dLgb1zj!۟W*>X*'⻪ 3OD|65tYɧb!>]O*Xc ;Btr.BDXkixbf3Ӝ)r\Ȯz)DWE]DT SrZVZr^k4+7ylok{
+ZiFr;ÇnO]UsWZ}cdYvotUCk=0t: +y>*ՒUUkx-&y'"jZRy1m6z0֪2quٵ?6kyo֫b3s2{z9>]5_mFӹ|.dbx4vR$!MWU|v45}<5gё>z=MӺVV.dr6)*ޯzČ:fhj\̉V*ޯl*FR.B!fRX8Ǖ/Id
+zIWJQ3-Rd"U5.םSb۷V)iY3
+H8 %쾣:DWW|`|\VŬ
+KV8]x2+/j.GáQ=nɪp
+d:uHH[D#bnIy&
+v.FvH4ć5hgub~&[@0o;*Nta}~~/Wҗp ~II*v{Dۓ_ +fqG֯M
+
+q
+/GS0 gs
+472 0 0 526 6485.5 7240.5 cm
+/Im0 Do
+Q
+ endstream endobj 838 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 839 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 472>>/Filter/FlateDecode/Height 526/Intent/RelativeColorimetric/Length 11417/Name/X/Subtype/Image/Type/XObject/Width 472>>stream
+HviEV$9TE3Bm?̜U!px<=dx l{B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!(|8|4*?3yts"V\!nt~xPN? Jťpqg6O˫kq}}uya%$w^;.r9aʥz$K3hu5W׽RR%egёBK`0=nK_k<;%pk)jz bp'b/d^Y >81k.*Wǒt:@Z)ztA! <(|خ.?OgB.u)b/NG|(A,
+"}o~%'Èk(Η*F\$~NW*[7t&H!yI=S/vzdlju٢JkVbT*҉H+wQUE/U̙ao0)UNY-gaCZ#1Z,fZ5t\U}G+EJ_1}dhtz^iR bVh2jVըV/S˨ (h]Dϥ:44]{Ѱ׮0B SB٨5Z^mլ8;.a2Nsʧ%aPM^v6YYX'ɰ[/g`5KC1j֛p< nf=j<%q\Z>S|vT5g|65+8E`4-Tw}8dzg!@=?U9&A G衉D*\«J`X'|#S8 FrZikk= {Cva|')\.+ET5,ֺ#[L±T\mvzllnWixn5&[{ ղ*RT8 Z>dR|fsRBP.楈SERxZ/gk؏‰lĕ<̗vDM;Jꋋ7|Jzy ?
+#*P[(:hh6jID^O揤KdF mSMJ ^v\MDnܪdehB5e-h<ΈbDhJ]F8=hOW:lD8I\K6.֛aw[&'g*y!.sWn$ 5UDbbVZހѶd2qze+l٬d$Ry3b_T`F1J"aՖ\ݙxDH6֥BJ*VZt:]`KWY,l<C`,k^k4 F$ty+a}~y}}}~a: \fRIHX:dox"B*j;5
+ehu{pda],Wrmnת ~lIM-j! #x!Z^^?V^mJe3T2L=nam'ۉ?
+~]5D/Wi9_V?U^l#~7l*kKsXVETJ~jifVc{uM(}<Y/C۬=LNdmVBdQrV֒˯lUTeb\Kb[*@Q#BǶ(詓*3k aUb:굪 &ǣ&ɨi"{RltQ^
+2_3~Cj90YU~ױoVe8HcDG"lj#͢z
+U07~1Fr'(
+ىa ; aի\*8mv
+6/waVˌ<`jOX,Z>l1r$"AvN
+JR-V*^lҷTw'jkI9{YG泩X~cû}X*WV,P9 1daZy
+8,ޡmݡ[GTT!}vFF4Ɍ'32WIBmԪ$ ֫+(5ĥ@ ۠<%Ѕ +2tfK'/NSXCNZT!E,#OQzB0g- TmT5N%Ѱ^#K堒%4ŢX<A
+S 0ue31`ŜkըӱF {
+XL2Kk7" Q%֥ȥVP^Vc< AYE.Z #H8
+P$Z,RZ3znZ0yCé猅%aݨ2ŀ=qTŒTw;&VQ+ul4z (+e^TDju_[/-{Wpy>^ʖ\
+W&G:z}M&\dn h@[᪸Z)[SY-R9ѧ*qF G+6يZe`PsFڥfR^8.NYC!)ҀUMA%3ڴr=L]
+z8sRk$@jz
+ƹ"QH+حHץpUT.mifi׮z}}@ʪ$PĎ%l' W)an@dJ.-񀘹|: \v)^s2.
+h; UAݬf+5{IZ^^eOT2Մjva{|Ņ\/"LTk0r(0avho&UzL G64)RpȞS7#%OsS ^{Zy0x*tp`(bWԱo,Ei$XӹD C1ݭ6Z\5MzWe/ϳDm\_qfAQWa/,s~0EC`#=nJKU^)i@ ҩ]|ABFU<xQB*2ZT.$V]ݻdUIl*4Ɠ Z&Q`t6cPb|&*j~'z|GU%+]a0l"NBNa
+jcKZ504
+T^]LS5 x r,Ɓc}rUɪZ,!6x[.3q]A%R2U}=3=9$sьѠOXMnZ5]\WXcнmpvlT oQդ>'j/N{LڃeJVAcK߁\>aWɮDs9T_/TA`;C"-R-M\*9S %U*k?M^mV˶ .Knn'sL:KWkj'0H%Xz#jsxx:_Utrb:pd"έJXQMM9+ D~ڠh€:wZJ!э[
+_%~|W<LĚ̕mP%z[ Hud`vZj1T_ogG+uN倵q;V~eD4RU H<3}VKfg8]6v%l<wo[7:k6 .eo*^Q^
+8tZy%Zk4Hk/V<vkۘOvV5MT$^_ 3hKۃ1ǯoƆ
+VKY5O[Ot߮ǭcFVEEV"2d|BRpSout6ە kFfU(JĖJ*M,xA#k%tNFҴ׻z6*Q!E L&u8`D!Bm½x:[,͕Nts64ʪ,9<DV RI8ɰT}|4Lv7~`;Z8&Kg,C$ׯ'u</ 4kYI]׋f3id! Kz 7D
+V&ղ,jM4pƤ)P4J&T:C3l.c?kPg0
++ܭyܭܰlǶWsXx
+ZU]a&p2"x~#'d:B_k*
+0A( @^n#RfA-h \Эu=ϗy%+c_/X NR /^{{z^ֵY%fMo;>2r9f|^uЮH+_}՗1TxUHq\Gzf0qH>+_wJZg4[X#rkVԂwu)dٟp|{1j/ ltbdΉj=ZጭB{MN]W,Z 'ԍ4E|Aw'=^_^ .ƽF(rwd5RfiDDؚ` w+
+պnk1*3Th 2)frp>_P+co׶VKZ v2LN%YVdPpfzEZmvMrDkЭ墦+iV5ZO Nʘz-Њ5%Z kBjzn[F^o:ttCk" ޙ`'/PB\ټU[h<~FӅl~ZL*Np[S"$f 4b>L&Srvj%pPz;z٬]{e(l?pV!,UPMVomzo
+ap<Bztخms>aB`KgwzlqqdЮE5\ ~C`EoWXکO[`X3Na2栵QV%%Z
+,Y^*UÙi:]]2Q]/ӉXh %I,kplqVNh !X4^ &a,ɨm7j"!8*"~`asb\Gt2;zU/)<k}"ZDT%\oV+
+V\6>!iXBĶ:n'0lDkVA+xEDT5&`[Mae"ZÃUd5R*WkZEG~XЀId*ar岯AzDkH+HM$T*Me,"t+ރcODkkEAMS $+ŢRa
+p7d
+tfe
+Cqךq\eˀX!/ycq%Z
+G+E3HV4E0iMi
+UQdnX+kx`N EYGҷ_xZS{\ժeM%Z3J|_YNTJj5UeQTjh TURzvbE8q_sIq, Q]UxF rH jY\ w7D|' ﮠS= R13-2P1Fh,N3n^ [-!XcFD2LRk~+%P`8(6ЩT*L| JMv/{a3^^.dSWVԩ|X*˕Z~&] ,g۬sןd~!"ЩJh4v?b˭t]S,7Z)ND^]1P‘X^%ix2_{E;v5z9Ah" 1MdX ˶-CSn26k<&1CPt6gYnI t:9QE5B'lS9\7~<1ک_$bgjv{Q a=_ΞcDcZ1 _zdqT-=_|_7+_oӟΜdXm6iܳWV3b8Fb=|٣&ZѺzz]/K[ Y_S9T|E`z߮yدĮx /:фоAX9GYXL]J~{ qV~|||m3V80waCag TwΗnqK<;!F]qӬx2xSBPPw] ﯠZFcd:[(_i&hW+os]P1GcD*Jfwpa_`0tԩv%vAG8勥Jg^9AܰtYJ슃5/+zZrS x]@]"]*VjVŘ[oEU7y.`Ռ6]qp\m{ј̹Fhئ4*4+z5+՚L Y7dةD5)T]U6*np̣rJ|]]^dXk[Z;7* 9=hUD슅\!!3_;@* Gz)G^vA˕]m]2, `~h o.zzf+Vױy/!vF_S٣N z}!a=a铉~:vaW
+n{~\osrH 㤗Kfp؆\?>׳K<;z%1돟ȰB \蜯'͜PMKA1Npl&
+9׫gGwQOOjk? ׻2R*Wr3ffyI`]ZφJP$qӃk*_nj_xJYR8*9TTŃy.1-GL^D^~s|%Ⱥ鸮cjfNW!ox*[6{tUݴmSWd*zR/?~&92Jaȳt/\? WX2+!|:U^.ވ;IQUy~b++fz5vԄ]m"ؕ7 vE|DYQ= | \]J3OXHaQVUEڭY+ W0<2:۽>0
+b=ʕ=C/aH (oDPmdzVTuòmda9*a bڵTRV:ܓZ9$a p< Nj.ںstkÄ+Vzp֡fj8]/K9;WpB 7z4K^7Zj:lzS//h$sfYleW@_oS
+qܳgҤ WR^\%t\읬C1\͓zR{գi;mUa\+B\#z'iAV3?rH@Ѣs9$HsmY51 Pvf5՚{6ҒUf9yUOr^Nr:Kyz3k<[|>DԊ*fNx:n=7Z!MW%9HBt U:Ui*FROt\a{JrnTFR_n"3Tmp|z~~z;Gk'/>r ˏorotU*qpo</^^^_U&誖Ϭh"ct+tUNֈRO÷y;rnY2k"/7|ݺn]`/aYӹbֺ>xMΫDWu80,R|}8?ۯCWE|<px2+Vw06ۣ~ihb:y>"t}|~~~ZٰSͧAGDW9X2+DF d:_k59G2kVo4[no02zw8Za2z1cMtUx*[l۝֗av۬f~Ka&u؟WHkT7۝Nj>jZ (5lRƭk,pG9}dLgb1zj!۟W*>X*'⻪ 3OD|65tYɧb!>]O*Xc ;Btr.BDXkixbf3Ӝ)r\Ȯz)DWE]DT SrZVZr^k4+7ylok{
+ZiFr;ÇnO]UsWZ}cdYvotUCk=0t: +y>*ՒUUkx-&y'"jZRy1m6z0֪2quٵ?6kyo֫b3s2{z9>]5_mFӹ|.dbx4vR$!MWU|v45}<5gё>z=MӺVV.dr6)*ޯzČ:fhj\̉V*ޯl*FR.B!fRX8Ǖ/Id
+zIWJQ3-Rd"U5.םSb۷V)iY3
+H8 %쾣:DWW|`|\VŬ
+KV8]x2+/j.GáQ=nɪp
+d:uHH[D#bnIy&
+v.FvH4ć5hgub~&[@0o;*Nta}~~/Wҗp ~II*v{Dۓ_ +fqG֯M
+
+HN@ i@RrM;'yY{4e1c<O>flc]DZT+A6L65)'|5&`HԂi—#j
+ _=!dMJ<i橫vJ4s\4v>g
+ esӸT>U4g8 mf_,'p3Lk̚uԆͬN)>nu< AOK XKvO=ѣKvyCO+(4QU=EyzBO^+= e)īVS쉖\3<kTO,=S'M=MĻ&{JdOW穧({bÞ6G)STZX7OT\O0(*c (jZZ45QIph']ǀ.gݥ-zPh8G3(`vgCkHڻd|ex8!<c1c1c̛!
+HWcCz
+()RT `&ƶ4ƨIxo'IZ>3{^׽u{ש tU'IM)4_QKUX
+JUE1@t:J!#T-7OR0eP &fsq\f1TJm@4߮J&BH$HRL&J%"âSɤ'pp6 q
+aP7
+0(,q~Pک 6O$Sj.bZm6ۭ]fV)r4r[狤*L 7U͗(6@ yLE ިp;
+h2pqiUDGbx"L'Hm3(B!3ܫ'VL鰚 *M'_&Wljv2!ƇSD.ShnRK,j{k˕ 5\
+#Rn8-8UPݯ4TRtPrTvrzX ZZ\(&RfP8trべvRTc{h8su< k&կAET+Qp239;hcsk{gwwwg{ɣɱS#1\|~"hlBoF#؀iHx8Qd hy_h2'L6vONN?\_ˍ{m
+J8RiXN9GG'fWo?uӗo߾|zwhR1F c19|1$ V%Xt
+n?HmvшעraCpnb6"Q\5t@Ǝk;N4Ha3Sh/_>szXL2( =6-Brf 8AˤW|v=fJ(M <rNLBڀ A/
+դS(qe*a4T2I'9A&,n8(6_6WӉթysЦQr.^oRSJtFe l8箁19huB!K`8uvb4A6tX=>/b) %eC'Qm=tnz~u۷7Wʅhlb.+mhu'Da8:* U3H027:ʉpMf\."Sogu1 .YO۝rL5ٺ}}Tyu듃T& F 6l*@79K$!5Df0OxwKN8:ٽLT*M3Ɉ: +VvX{Dj|"7ͤhMTjZ=%J s_$.<|{ųt<qTLs`Dcdn<]:]o3AWO"39==9Dx~S~LmZ]]]^(<D4WYX-2Ã!äҮZƟ {D1-@b|f{Of2^SjY!aoN$" `RIx``k8vԅlSt6*wbK)lЙ入奅 9.TxIw{AUxxnV?2/>^+G>'4BZVA&XA=
+%sb>Vdw #oﺠƑXc&s77dЮkOa`"j6QX:_TJ$ 3o^s2:ʦW
+Ɔ=O*vـy*f b9Fw7uʠu< 1$ㄠ)(@v>S Kmu$qXL% 4](WjR65̼p9<^p!:X`b#)7ڏ%2(DN\6C;al'
+Hٵ}ѥ-̓yR/%u/ή?9Ufxk%GI\Wc<3G 52;Z _u7Kg҉ۢe 'F]*) E3Im &
+״]Id<t]Tcxx[k߷jLҟl-N}b#D'Hfōx&6O xvR!JD @ierR): 1 ;5(Ntȩ&S]~h7+Ϭbdg&WP2% #9ɍD88U˅u<؍ ý]t< BgL8gBcpTJv?3!Blp2WfX TyDztmHd!btm1Yh `Ͼ`,!9!lKhV+ĽF9UĮ.'3JMʵc6v$(xGgҡǦ.YNd7w,׸orQqZ2>q<{Z n0}lxzy\wJЉz]N̗SPF.M`ٛJׯj>6A-MsG.r
+>(B'K_̸Iu0Ƨ:. }0Fs+
+1^݄Gil̜]+R_/K0(lP.h؏eDd"FIJ%0NnŨ a#N}1L(;H:h~~ylSW6*`(x|=ÎWءS*EN<CsyhQGzR6!sHe0(ۀ<`p"ĄU/oZ3ݬ⍦!4[UL&z 4TNF(@IEp
+ٶ|s 4>u,Rh fIv&G&ycL՛^w)n+i8L
+^4},RdCR&fNy}>Mv+)0/d {<RU+vدč r0Љ}_xN%d ϟ*UWFb:Zw4_?>}xz1mk$ѹ[lmB] 9vE˟?ÚW dl_^^_v5g[r٬fL~@I.ڃv^.ȐqFroq
+uw|Z_аC-J"ݖ.;8c9ѽW;0fs^-aQ%8c7Rj?ӋAVr:1('_ivry9 2)Cq:qZRHdo3;pr\}]ZLGVtxX^- NZ& o Y@q{=Rb FѰj\ct! \nTP8dN"nK5t;$vhױt:l<j\op0NլW\Ƙr%rJ^UDNIjvr@j
+cHcBfH4c2{d4s TFp,F(#v">
+
+S‘bGR
+ yV|8zr](lG9;
+î.Tq 't:J=E\o:/4 0
+q
+/GS0 gs
+296 0 0 91 6647.5 7516.5 cm
+/Im0 Do
+Q
+ endstream endobj 844 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 845 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 296>>/Filter/FlateDecode/Height 91/Intent/RelativeColorimetric/Length 6141/Name/X/Subtype/Image/Type/XObject/Width 296>>stream
+HWcCz
+()RT `&ƶ4ƨIxo'IZ>3{^׽u{ש tU'IM)4_QKUX
+JUE1@t:J!#T-7OR0eP &fsq\f1TJm@4߮J&BH$HRL&J%"âSɤ'pp6 q
+aP7
+0(,q~Pک 6O$Sj.bZm6ۭ]fV)r4r[狤*L 7U͗(6@ yLE ިp;
+h2pqiUDGbx"L'Hm3(B!3ܫ'VL鰚 *M'_&Wljv2!ƇSD.ShnRK,j{k˕ 5\
+#Rn8-8UPݯ4TRtPrTvrzX ZZ\(&RfP8trべvRTc{h8su< k&կAET+Qp239;hcsk{gwwwg{ɣɱS#1\|~"hlBoF#؀iHx8Qd hy_h2'L6vONN?\_ˍ{m
+J8RiXN9GG'fWo?uӗo߾|zwhR1F c19|1$ V%Xt
+n?HmvшעraCpnb6"Q\5t@Ǝk;N4Ha3Sh/_>szXL2( =6-Brf 8AˤW|v=fJ(M <rNLBڀ A/
+դS(qe*a4T2I'9A&,n8(6_6WӉթysЦQr.^oRSJtFe l8箁19huB!K`8uvb4A6tX=>/b) %eC'Qm=tnz~u۷7Wʅhlb.+mhu'Da8:* U3H027:ʉpMf\."Sogu1 .YO۝rL5ٺ}}Tyu듃T& F 6l*@79K$!5Df0OxwKN8:ٽLT*M3Ɉ: +VvX{Dj|"7ͤhMTjZ=%J s_$.<|{ųt<qTLs`Dcdn<]:]o3AWO"39==9Dx~S~LmZ]]]^(<D4WYX-2Ã!äҮZƟ {D1-@b|f{Of2^SjY!aoN$" `RIx``k8vԅlSt6*wbK)lЙ入奅 9.TxIw{AUxxnV?2/>^+G>'4BZVA&XA=
+%sb>Vdw #oﺠƑXc&s77dЮkOa`"j6QX:_TJ$ 3o^s2:ʦW
+Ɔ=O*vـy*f b9Fw7uʠu< 1$ㄠ)(@v>S Kmu$qXL% 4](WjR65̼p9<^p!:X`b#)7ڏ%2(DN\6C;al'
+Hٵ}ѥ-̓yR/%u/ή?9Ufxk%GI\Wc<3G 52;Z _u7Kg҉ۢe 'F]*) E3Im &
+״]Id<t]Tcxx[k߷jLҟl-N}b#D'Hfōx&6O xvR!JD @ierR): 1 ;5(Ntȩ&S]~h7+Ϭbdg&WP2% #9ɍD88U˅u<؍ ý]t< BgL8gBcpTJv?3!Blp2WfX TyDztmHd!btm1Yh `Ͼ`,!9!lKhV+ĽF9UĮ.'3JMʵc6v$(xGgҡǦ.YNd7w,׸orQqZ2>q<{Z n0}lxzy\wJЉz]N̗SPF.M`ٛJׯj>6A-MsG.r
+>(B'K_̸Iu0Ƨ:. }0Fs+
+1^݄Gil̜]+R_/K0(lP.h؏eDd"FIJ%0NnŨ a#N}1L(;H:h~~ylSW6*`(x|=ÎWءS*EN<CsyhQGzR6!sHe0(ۀ<`p"ĄU/oZ3ݬ⍦!4[UL&z 4TNF(@IEp
+ٶ|s 4>u,Rh fIv&G&ycL՛^w)n+i8L
+^4},RdCR&fNy}>Mv+)0/d {<RU+vدč r0Љ}_xN%d ϟ*UWFb:Zw4_?>}xz1mk$ѹ[lmB] 9vE˟?ÚW dl_^^_v5g[r٬fL~@I.ڃv^.ȐqFroq
+uw|Z_аC-J"ݖ.;8c9ѽW;0fs^-aQ%8c7Rj?ӋAVr:1('_ivry9 2)Cq:qZRHdo3;pr\}]ZLGVtxX^- NZ& o Y@q{=Rb FѰj\ct! \nTP8dN"nK5t;$vhױt:l<j\op0NլW\Ƙr%rJ^UDNIjvr@j
+cHcBfH4c2{d4s TFp,F(#v">
+
+S‘bGR
+ yV|8zr](lG9;
+î.Tq 't:J=E\o:/4 0
+H @MM}ZÄ
+f;q!!#ebH8R#eH9R#H9R#H9R#H9R#5Ƒp4Xr0Xruϯ\ yOM,#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#H9R#!H9R,#!!bb7so
+H{Lg=PxB)VZ"
+"iTj a+a5`R7 x"8  Cˈ8E$H!)'%/徯y>{lDZAheA&?c Z+0337701i{B1P{}X[<I ܘC'`kڊawc@V-ށH80?G=N#|Id ͕`0L ݕB!6 |Ґ,! v$
+t`s<=LOOÝIRHv%@B$S,׏/|f|?_Š@%FkhL7 Cvm" <o6DD,KBapPiX,2*:ƄEICŁ|.E#Y6b E G(MّÊ?älChFHab,N<z,%-=#ӄHOK9vTI\6B!Y0ֈ,oH%WҳO9V3YgNd(͢<la5b!ؒl^D&OJ+T|g4VVzRA^Nj\& d[8azq\uQion^^Z;U7VYZMUƅ^L06FbYۻ<VW\Up~i-߿oֵbu`
+%чRN_o
+ A0 gǞ_!Mq3m隦k)2t4"fzЋ1E"8$*Ԃ"^Eu7^ftQ"&y~SiV 9O<೗o=o WΟ0 7w5G/8<lln3ᔌsJs3Rtp={|3q@hHzNrD.;Ksҏ#C64~Q_QC"I"%;)DEw^/H-%:KNoxmO'- E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QFdD~/"H"2[ԵHO[ko%.to&x21q >^8;d%k)kqu,,i]2O//̌wZꊅ^~"~`]T|oڇ<ˑ;͵Eif>X'GSK;{O-[}ޯ[z>yRTZ,ETBzfg3oWV=ff㎛MՅ'BD%CqIU7>}jfn~af^|tFcUxRىx4X1ۙݽCc/&gf_{jvfzjPow3cXF-3&<&ђWlua/'&=51ѡn5;,1D|AaQ 9kC#OƤ;q`׵jGNFjBTXWV"^޾Hj/w^qo_׉K}us],Ϸ[HmD!8%3kvtvuǤk]wo]x07bӇ(d&"Z5:>j+t64_qS.ם7o\mnpV٬)*q*ec0MW;.5zRSc0Ϟ~dRV_[:XmLreU5uRNL\(s-Ih]_V{#CO:jʹ)(txQXp&/Ǟi=#"mĽ!h#b5#f?y*ve:ieeX-)&cLV1"rF
+KF 6ϝb6nKYF^bNA 0`6y~Yƾ
+q
+/GS0 gs
+274 0 0 303 6622.1162109 7246.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 850 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 851 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 274>>/Filter/FlateDecode/Height 303/Intent/RelativeColorimetric/Length 4305/Name/X/Subtype/Image/Type/XObject/Width 274>>stream
+H{Lg=PxB)VZ"
+"iTj a+a5`R7 x"8  Cˈ8E$H!)'%/徯y>{lDZAheA&?c Z+0337701i{B1P{}X[<I ܘC'`kڊawc@V-ށH80?G=N#|Id ͕`0L ݕB!6 |Ґ,! v$
+t`s<=LOOÝIRHv%@B$S,׏/|f|?_Š@%FkhL7 Cvm" <o6DD,KBapPiX,2*:ƄEICŁ|.E#Y6b E G(MّÊ?älChFHab,N<z,%-=#ӄHOK9vTI\6B!Y0ֈ,oH%WҳO9V3YgNd(͢<la5b!ؒl^D&OJ+T|g4VVzRA^Nj\& d[8azq\uQion^^Z;U7VYZMUƅ^L06FbYۻ<VW\Up~i-߿oֵbu`
+%чRN_o
+ A0 gǞ_!Mq3m隦k)2t4"fzЋ1E"8$*Ԃ"^Eu7^ftQ"&y~SiV 9O<೗o=o WΟ0 7w5G/8<lln3ᔌsJs3Rtp={|3q@hHzNrD.;Ksҏ#C64~Q_QC"I"%;)DEw^/H-%:KNoxmO'- E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QF"E0`(QFdD~/"H"2[ԵHO[ko%.to&x21q >^8;d%k)kqu,,i]2O//̌wZꊅ^~"~`]T|oڇ<ˑ;͵Eif>X'GSK;{O-[}ޯ[z>yRTZ,ETBzfg3oWV=ff㎛MՅ'BD%CqIU7>}jfn~af^|tFcUxRىx4X1ۙݽCc/&gf_{jvfzjPow3cXF-3&<&ђWlua/'&=51ѡn5;,1D|AaQ 9kC#OƤ;q`׵jGNFjBTXWV"^޾Hj/w^qo_׉K}us],Ϸ[HmD!8%3kvtvuǤk]wo]x07bӇ(d&"Z5:>j+t64_qS.ם7o\mnpV٬)*q*ec0MW;.5zRSc0Ϟ~dRV_[:XmLreU5uRNL\(s-Ih]_V{#CO:jʹ)(txQXp&/Ǟi=#"mĽ!h#b5#f?y*ve:ieeX-)&cLV1"rF
+KF 6ϝb6nKYF^bNA 0`6y~Yƾ
+Hױ 0CAfDa$ M@ :ߔ())-JAҢ()-JҢ(-JҢ(-JҢO--JK"ƒ-=Ϲ88MCkܗ8ǯ
+HkY
+A.W(l1cR@
+`
+hco5j
+EOqiڇjV?)?ch_u6PL'x")y\Y~OcuL!?&х=5prbޓO_} )@߾n|Z_}W~rtq@t.g?ogNm~~k5o
+=Bϻ,CX#R/di+kwz:yGwzvJ
+lw-vdZ=E3vfw8m\)ث C26`A6( LJu
+X ІZՑ`@$uZC . pL U*ZM RMҨb2\P(k`ĔC{KS A%hS
+E쫢ncKv >Ф6 0
+q
+/GS0 gs
+268 0 0 44 6625.1162109 7475.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 856 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 857 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 268>>/Filter/FlateDecode/Height 44/Intent/RelativeColorimetric/Length 1219/Name/X/Subtype/Image/Type/XObject/Width 268>>stream
+HkY
+A.W(l1cR@
+`
+hco5j
+EOqiڇjV?)?ch_u6PL'x")y\Y~OcuL!?&х=5prbޓO_} )@߾n|Z_}W~rtq@t.g?ogNm~~k5o
+=Bϻ,CX#R/di+kwz:yGwzvJ
+lw-vdZ=E3vfw8m\)ث C26`A6( LJu
+X ІZՑ`@$uZC . pL U*ZM RMҨb2\P(k`ĔC{KS A%hS
+E쫢ncKv >Ф6 0
+H F
+H?'uV7$sL QKBT䆵%*rlk(3`|En}~~}5B)I7F}%Y^F}-iзF
+胯Z~:koO[.%SMG:~o.qWNA8 },3D9>p2!y ЗF}-i ^F}%YF })IWB}5[0G`_g ~oKRȔB6M%t*S:(!+
+Hy<{KeXNǾeɞٲg:gR2D#˄K!B"Iɒ%Fi;s}<u?x>d@}S Z\~vVFFfFV
+;KWVeʯ;Ò
+dK$AA m pq 
+ Pk!!A~>^^n.$.ˆ&$["?O)h 0`@daQ1 I,/++#-& @!`$ppmTT@b_PXDL\v+)kZ45  u5Ue
+r
+4
+Lu'STRQ'h[XYw8t?]]99pgkmeanjblEPWQRKD
+!<s..>1BJjZzFfVvvv
+~fef\HJ;}:2"D()$qC]Mu ᄉ,fRp I)hhY"/`RɈظԴ+WKJ+*Q(/+-Z| ?7'+#-59)!.6:*d()8쒖d_ ޏZI+ ,WR ;'7O߀`3s/].ZRV~f}C㭦۔n56߬V^Vr쌋ɉc#Ï}<\a$5%yY
+SǞ9v,$Dlhk(+YRF dSXθPT!Y;D<I
+sN!Q1\+,.khjik ~:ltl Jp=599||l KoOwg{[KS}] d$Łȱo7'{k3#]X,NTX0:@a-[JP'Q:H<q*:.bVuM-w:`hxdt ϼ?PWfg_΀  r܌į#N}+H ȦZńBBZNI] "E'7ģAp~1_XxwH ~[\|͛`f^LON`[o7U"9c"HANVZjx)1!4KFXefWT20w/OF&d_)Fc}b98DÇ?B ݀c4fTf_|$tuk%W2SbH>nv{0~nV+-8yĤ4t-|I@uv[Gw'C179, 0?C+m[Csd2?$Ϟ=jomRVjBld1P[m7 ۃbX K˫hZ;x"UuwL l=ʖRh M~x5?;3=9>
+"{mn*/s*4>=zJrp`rmXU\|B2
+چ{C#Ƨ u-]" 8 VVV,lxL(?Bܙ/]ؘhK Xg_kEP1?@<y&BF~QY}<8261=3 o OrƅB&A"d{rnZRlÎv{Ud$NQ恆B,,xIs_ܗ?hnsgJ)eI
+aR
+PMԡ\#ʈk%)#eKٳɚc> s络Tw9O<~@?8e C 
+l ٴ丘ۡ.gľ56c<|}5GtK
+Xw,t-)nqIJ 4L`B},8dzdldpWWLM޽y1'tKjpMK00urIJF^qEu=Jd's"$I $0'=9~D#X_D<x="$,xx I gw(jBZVA٫֎~H;
+!2# Ã}oj*J^d<pˇ*a>^T)oyxWIJG,<|&r^V74wP ?13@|Vz]UYAvZ5*,
+O_+n,Vqn:~m׭Y)H cp @yoTV۽99ƢݏbQAB^[YGps<avHwVE\W\Ā%$%e!nX_$?+yUGoXX`=(@X2:FyDx_65ܣa-3|" )U ¢/ 1UAx<wB\Noi+`/GN)?,)Ymz&V\CPYY E1b,@P@Ɔ0Źć<Zއt
+9.sJ`lP 28{~H-DY0b6,<E,IuPGa)11P0Ե Z;^S ;=G0{ޮ+=M5%<h\qRX0`3sjgY5>dncX]{'GOg;Tx,ۃ ʜԷ<˅İ`0=q%8_^vg ѡ<Z>Qeo+R -_f<4)WiU -]}C<~h,]]:g P[Z" K VR28jx/$QJVQe]SG =_&Gk^g$Qo]vw8~Do6-mšjVڨ
+FLBƶ#p_p4 sػ] U%iO!:ʲb8<X-ek+(ů[&&CY@̧ _3۹Yn+26R2J۴ (~QCoI(7~Ejk|]GxVUv.Kɩ5:}7(2S^of}6/LF\jgqhMbB`T~aR6Na4 hwzx}6o.M}|78JUNjՊWʀOZYZ̢ER3>6o(xs3ISͭ%ʃ.V4<v98*.5!)v]u$;%6'W;:۔rZy0X&sEu0Q0KRΦ'wC[j@y˃Spcj!\24 LO!,o1F`p# xLO¸n,Hq~<-^8Иk6(i۝ (574JfcluISj5sc[֭ƺp0ИZ).w{񸥰ƦD<*/-.*)sCm5 kifę`Q5>#&)Y
+)RпxeR_Q~u%iq4؍ʎ}&6^ԧ%1K1`/l$TǍt+p
+ooTCcvD;Yiʉ 3;h~@4~L! yk{L.?3hawG CmY1n`{.4cLfvAq9FyHmj?#?+)5Z2sؘh=;:- &mh}`F eI[ .@StC G`tҡ_L> 4f-lQK ̷G&eݸG3&񪮢,/#1wK%A3OIc>3w7ӫvK }\HA\ݩ̠J}{|3*;z~K3T[^zbQ]6V!H>JpAqyu];c oWԽBf>Q8#[iŊ6ЀuJuNи WK6xOf=_V?u%dŚe 狀bHt`eh+8\N`F?])чݶlX$5 |{ߊV|C2n|^AVRġMC$h!L\x O:|jy^y'/qg[Hr1Zc rj:&.3>mh~;8Lf񮯳qvXi)K
+\G nKAiv5V mg&+o
+Ǵiа~&^Ջ:ʲK G1tb9e,>ie-
+x A v|
+#++L[Pk`>a_W n93 6.go=k&Էߪ yo4Pf6Y!ȿWSdU=m eɊΦ"3sH`Sykw{O}[ \(;9gR%)!n6&[nAIEMK`j w7v0:O{lY.;p[Y5]S{p|jf|[ȋDX-[ %CU8P4ظa5]!GȟݿA EcSyw1w<a i}^QfւyԄ~ YF\).oNA0uyPꅅpFCnGϐ:hP47-oZ 씆 a`J)@8@0t~'Tcb4B2 Լ$N0yXy15 ~iOvK>~$aaTh,
+4>WύZF ە P(0$rP9`4,i"9R9(Jˌl\EѠZx8px9AI8M 3VPhfPO7)!n6Fr#+RCkgH5@£_Qr`q,3Ì*p&v^iUS#7ѠZuh$
+Y
+
+vHRR 4. ![dD88Z~<󕗛ػJϿW7HD:hkxRle(!Jg0'&x%g?o!Aqƥ`wS9d\Rno@̅w^vh[:aik(r6gq~NrJ.b8zM\h 1^~zlfpqpV. EMCkCiW˞h}G0zp ֗N:mJ
+0*;|^)y I G7Nzn]*#Bbd/ "J/w=ߺ"sf2EdȔS&D ET
+4g*J
+X eCDGwf6znkӽ z>|k|QcRѷt O)G-|?c#hɽsx\Q}:#cc.+ px4p8?l(M <.<>թ" ~:<|KjHC*-UEmMEǧ<U!jﯗ" ZrI?s
+:`=UHe}!#}qve&6.g?H/F<T=Uhz~ gkÕB\ xow {^R~NUokMqo7])'x$ [JzNI卝Q
+Ӣю&#BW$m+]? NAN觾vqcow3ՒYDOdmH*[>ۏ񎛝pFEq> iCXFhSWӊ`7&?%=* y-S;7cmgЎ;ZU}c\:N.;X(y7I6.h9șZA1xp I$qL8E_6p &8(C8Pm0)A`(@![SM9z
+@gcyNrbj&ER! b`|JTTCRThJe
+ >6Ry̜BjF[V*4ʂ'.{W)Uh
+ɶn>}U+1AhRZUu Jjڻ=] "hVSs Vq9*.MM.kTm&Twƌ2)BA!>nfZr47$Vb302%MsGXp $ر^{(+- 07,x] XFcWSEnRţuy& 27$F+D`pDz: @SPZzߩk_V¹APh28& ?d ,msPGaZԕV*J6Gl1V2SD,@[xZCo=|G_{mq&И½dwHa87 yվz{㬋59-2w .m㏨`go;DCV8S iFGq]mj,[ h̚@Gtz;M]QAP(T1LcFWLaǣ“*>Ai|hٛjQLl:v H i@gCYv|-l|+t͝<#S^mAHX4 vJz.{RP38
+iF@#};Bc15 ƅt?]WH| )+Wӊ[{Rq aR]cԵ/k 2- FfNAF[Q h|iicQ3q9[vHiХ1%$nlrFS@AX(:H@?S2 h4Ӏ \?SɫiEխat?]BߐNK,v{]gpt &CObg`dʋ-݃#A!h
+q7Ք[2?0<˵;xI-o;q0F#Oīuh&͚'RPi>IRH(!J!q"RB(,%24i"͉QyNu߰wQ~{uz]z磩0yH*j<ZP۠W~;h"}t![c mH,2s?wyJ~e#l^h(MzjNC^0<v>qM|oFϯhm'h}e9Q78o3Re;ЧIy m!Xe OjXgD ^1yum.gnD%w6_c\q픓+6xDn=x:82!A6@uşޅy9ZQf%ͦ#GDNԵr[P8ڢ7طy"!.m&r ˨t
+
+1۠Gn t|{ ҂ژ%Hy}'<|QT3
+` EsB|\miȋ31lcKLnFGN.͎W60XgF'Ɋp#Gry_*%CKF>4򇶁
+p|A` tս
+pU8F&n:pT! u}⨁;f>O)lmQXCA|&>M-oAno$+>76pyG \ntpdB6U
+oE>
+oFEVT#``$ȪX9FU+8lG'UK{,V;bRa'QLU5-pTtRw6UܦL*NmPFDž;/>W5uQExp7罏fg5vR *Emn=Kίh':ɥgˋTbWXgs#*1UTt4P503ʭ\ktwumW
+;T<Sn?OΫ
+ 8'?T`@C\^
+8A/x8XVec6hcF9e䶞[En7FeAkAC5PY`%`*  rʩ"x'&%g=JB}×K q
+F[ |r|Z, 6ȉOةgbQFnbvi4if2>v[)A)19H,Z6O=z~k%ܹwlNPG&aϒrf\5]D2i!.֩1]mb}5/MuPF9wS_0JvT/ee93Hd+%C
+cI)$5B"(k%$%dm""ٲk_R~<"Ms sݟbbUes9>1}+hb
+\ln3:#*,4`4Ʒ(1,@~11E>#m R4TW.`w4p̙Ǿ U'p0
+zq Fcl8Z=0)`k1h|` 9Jk[x8&p
+jo(vb cw;C$vT?6FZJh2h`uB
+F$ =q@m$#>\w?'+UIJOx}G݃SU9Tpg\w>dUQRw<A \6nA-΄%d"ߍek&#h`dbXR
+L@׀k) GYghK,:H8@8p nifw*B>:}{??5|Xg-wL ptֺr',f+劬EE~=QIM3}؟no,zo^-%K8X,"8q:4>q96:^
+ 6:׀_ew%9E}+G*-C>$(X%Q{PԭlcmAV?k#m⟚ԠAUw[؟
+MEяFХmCeQ c:>T *ث :_}?"ܴo{ Sԧ8^% *aaW
+u8H
+q_72ufPՁ@38\4] #*쾩S Wp,Yj 1iU7L@ >(;%*uxϙ?ဿj@'}4#ç8}]UrRc.T\eJv(?3bkP_DRrX;bҼq>'Qi,\
+Zʁ#9_g#* N- JuH)m5rI)l
++BF8aRcuԅ[O_%10"mwk*J /uf1 |>;A!1UԷh3#=,(Is:ECiq2wF<s)6-5vƽA-*u'@L
+{c}Co8ϊx|qK-嵠۲48V@X;
+=X vm <;rH#8}/zPP^9q`0z m եF0~@MC
+!6Tg&EN/ bpH)jgt?nl#A޼,H 8>0H8RؤcrU μx`~R_Qx4jy?:x_M{V3PDe\(NBDʴ\}{G`xARhMl-6.r {D)̉Tذs*O; 2yl|Bɪn3s G pyόV#ɇqVJ%_#f(v$N0:snr+!2C.!TT5$CҀB,)ǐ"t3ʐ1CVfLd.i{=ޜug=-[o.#׷t LZ(Rggiϟ$߸bk,IcK~
+?mA9  Pj\8ni]AJd=/αG@TZqN.]WZ m9:bj0wK
+_M0ǒiZ;{!,^*
+V<F0L`-Uy7:ف*r֬ZW6XDVKVꮃv.烣ϯoǃj8`zIk}p^N@t7oob}3j|jnI 9Ϋ+@iT=I ww4١(S
+&Eu]#n~72јwx
+XǃAmȿ-jm
+?
+J@cOൄg(p\}$<M'mLd%.[ ?<`׮Sol{/1W"- ƀS#3z!]u%Y lLyZQܡchaz>Gc[=gb``%En`܊ v5߳MAJTp7uRjLOUPQ7jrb_|0K
++g NV͆ÿQbjJk
++މaSU[mzaIX<\|#Sr7ukm=-LӆzʼlPW m؂a~r̝⪆fjߋQ6gyT('ֵ`߳Gv| }KV 0<l񰴆Bƒ@p
+>jsCU1*P?k+mRlAoӃoWGEL/" ;QE?RS0#)6"ȡ}Zf"?xqons/$VJvA.F>>B(iHVJMY~V ߳ǭLޮ(+ZX!IJ^UkiOTVx|[+Gx={,d܊
+s2* /S08cs<(. RXԜB}52 az7Y ѡ,PI)Hm)-E~xCPDRNyǞ&G]GZ}
+2O_ <of&hC}혰K^.z$֭``ƁW
+\RV.`X{ /\W2[Դt ̑OaicӬ /?R`k1`/Y]e̔hdq@WSUA.)#x,_ו,x5D )+[:{G&` X@" 1*IEQ^ƽЋ@WKm /YR k+!)Hxz_ w?53m2$`P@C v>*-IO rq\ Y A+8br'Dx~8xDM/4w a PY"tĄso1
+G2Sn߸w YId [L>>!~9 eUud {H& Ijٞ@joV'݌
+ u?eoiOXxs-%/+^W@BaC&f0n‚*o~'
+: Dޒ$tRKV߱>by9Cmk
+r3Sc"}N9XTSƶ{[]a1)9EU= Mw @R3s J*([ک}@2jlLf
+" (
+j`Q1
+.뵭lu~g$_o`0y}ɛDx2?\_]Y;GMqs-jȏMLgHeu}s[Aȕkӷ?Xx_ϯ
+?7wG53=y} &qa6qf O&I%D9,-,#jX4721#YSml$&A.ّWP":TwhۉށsãʱS7gTgBݹ۪S7ǔÊ{;[ApgT*jfX׃tS$16 Z1(c3@'5M ߼+aϲExU^>195}sJu[JukёaŅ}=][VW-)
+q
+/GS0 gs
+398 0 0 398 6557.1162109 7246.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 862 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 863 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 398>>/Filter/FlateDecode/Height 398/Intent/RelativeColorimetric/Length 20921/Name/X/Subtype/Image/Type/XObject/Width 398>>stream
+Hy<{KeXNǾeɞٲg:gR2D#˄K!B"Iɒ%Fi;s}<u?x>d@}S Z\~vVFFfFV
+;KWVeʯ;Ò
+dK$AA m pq 
+ Pk!!A~>^^n.$.ˆ&$["?O)h 0`@daQ1 I,/++#-& @!`$ppmTT@b_PXDL\v+)kZ45  u5Ue
+r
+4
+Lu'STRQ'h[XYw8t?]]99pgkmeanjblEPWQRKD
+!<s..>1BJjZzFfVvvv
+~fef\HJ;}:2"D()$qC]Mu ᄉ,fRp I)hhY"/`RɈظԴ+WKJ+*Q(/+-Z| ?7'+#-59)!.6:*d()8쒖d_ ޏZI+ ,WR ;'7O߀`3s/].ZRV~f}C㭦۔n56߬V^Vr쌋ɉc#Ï}<\a$5%yY
+SǞ9v,$Dlhk(+YRF dSXθPT!Y;D<I
+sN!Q1\+,.khjik ~:ltl Jp=599||l KoOwg{[KS}] d$Łȱo7'{k3#]X,NTX0:@a-[JP'Q:H<q*:.bVuM-w:`hxdt ϼ?PWfg_΀  r܌į#N}+H ȦZńBBZNI] "E'7ģAp~1_XxwH ~[\|͛`f^LON`[o7U"9c"HANVZjx)1!4KFXefWT20w/OF&d_)Fc}b98DÇ?B ݀c4fTf_|$tuk%W2SbH>nv{0~nV+-8yĤ4t-|I@uv[Gw'C179, 0?C+m[Csd2?$Ϟ=jomRVjBld1P[m7 ۃbX K˫hZ;x"UuwL l=ʖRh M~x5?;3=9>
+"{mn*/s*4>=zJrp`rmXU\|B2
+چ{C#Ƨ u-]" 8 VVV,lxL(?Bܙ/]ؘhK Xg_kEP1?@<y&BF~QY}<8261=3 o OrƅB&A"d{rnZRlÎv{Ud$NQ恆B,,xIs_ܗ?hnsgJ)eI
+aR
+PMԡ\#ʈk%)#eKٳɚc> s络Tw9O<~@?8e C 
+l ٴ丘ۡ.gľ56c<|}5GtK
+Xw,t-)nqIJ 4L`B},8dzdldpWWLM޽y1'tKjpMK00urIJF^qEu=Jd's"$I $0'=9~D#X_D<x="$,xx I gw(jBZVA٫֎~H;
+!2# Ã}oj*J^d<pˇ*a>^T)oyxWIJG,<|&r^V74wP ?13@|Vz]UYAvZ5*,
+O_+n,Vqn:~m׭Y)H cp @yoTV۽99ƢݏbQAB^[YGps<avHwVE\W\Ā%$%e!nX_$?+yUGoXX`=(@X2:FyDx_65ܣa-3|" )U ¢/ 1UAx<wB\Noi+`/GN)?,)Ymz&V\CPYY E1b,@P@Ɔ0Źć<Zއt
+9.sJ`lP 28{~H-DY0b6,<E,IuPGa)11P0Ե Z;^S ;=G0{ޮ+=M5%<h\qRX0`3sjgY5>dncX]{'GOg;Tx,ۃ ʜԷ<˅İ`0=q%8_^vg ѡ<Z>Qeo+R -_f<4)WiU -]}C<~h,]]:g P[Z" K VR28jx/$QJVQe]SG =_&Gk^g$Qo]vw8~Do6-mšjVڨ
+FLBƶ#p_p4 sػ] U%iO!:ʲb8<X-ek+(ů[&&CY@̧ _3۹Yn+26R2J۴ (~QCoI(7~Ejk|]GxVUv.Kɩ5:}7(2S^of}6/LF\jgqhMbB`T~aR6Na4 hwzx}6o.M}|78JUNjՊWʀOZYZ̢ER3>6o(xs3ISͭ%ʃ.V4<v98*.5!)v]u$;%6'W;:۔rZy0X&sEu0Q0KRΦ'wC[j@y˃Spcj!\24 LO!,o1F`p# xLO¸n,Hq~<-^8Иk6(i۝ (574JfcluISj5sc[֭ƺp0ИZ).w{񸥰ƦD<*/-.*)sCm5 kifę`Q5>#&)Y
+)RпxeR_Q~u%iq4؍ʎ}&6^ԧ%1K1`/l$TǍt+p
+ooTCcvD;Yiʉ 3;h~@4~L! yk{L.?3hawG CmY1n`{.4cLfvAq9FyHmj?#?+)5Z2sؘh=;:- &mh}`F eI[ .@StC G`tҡ_L> 4f-lQK ̷G&eݸG3&񪮢,/#1wK%A3OIc>3w7ӫvK }\HA\ݩ̠J}{|3*;z~K3T[^zbQ]6V!H>JpAqyu];c oWԽBf>Q8#[iŊ6ЀuJuNи WK6xOf=_V?u%dŚe 狀bHt`eh+8\N`F?])чݶlX$5 |{ߊV|C2n|^AVRġMC$h!L\x O:|jy^y'/qg[Hr1Zc rj:&.3>mh~;8Lf񮯳qvXi)K
+\G nKAiv5V mg&+o
+Ǵiа~&^Ջ:ʲK G1tb9e,>ie-
+x A v|
+#++L[Pk`>a_W n93 6.go=k&Էߪ yo4Pf6Y!ȿWSdU=m eɊΦ"3sH`Sykw{O}[ \(;9gR%)!n6&[nAIEMK`j w7v0:O{lY.;p[Y5]S{p|jf|[ȋDX-[ %CU8P4ظa5]!GȟݿA EcSyw1w<a i}^QfւyԄ~ YF\).oNA0uyPꅅpFCnGϐ:hP47-oZ 씆 a`J)@8@0t~'Tcb4B2 Լ$N0yXy15 ~iOvK>~$aaTh,
+4>WύZF ە P(0$rP9`4,i"9R9(Jˌl\EѠZx8px9AI8M 3VPhfPO7)!n6Fr#+RCkgH5@£_Qr`q,3Ì*p&v^iUS#7ѠZuh$
+Y
+
+vHRR 4. ![dD88Z~<󕗛ػJϿW7HD:hkxRle(!Jg0'&x%g?o!Aqƥ`wS9d\Rno@̅w^vh[:aik(r6gq~NrJ.b8zM\h 1^~zlfpqpV. EMCkCiW˞h}G0zp ֗N:mJ
+0*;|^)y I G7Nzn]*#Bbd/ "J/w=ߺ"sf2EdȔS&D ET
+4g*J
+X eCDGwf6znkӽ z>|k|QcRѷt O)G-|?c#hɽsx\Q}:#cc.+ px4p8?l(M <.<>թ" ~:<|KjHC*-UEmMEǧ<U!jﯗ" ZrI?s
+:`=UHe}!#}qve&6.g?H/F<T=Uhz~ gkÕB\ xow {^R~NUokMqo7])'x$ [JzNI卝Q
+Ӣю&#BW$m+]? NAN觾vqcow3ՒYDOdmH*[>ۏ񎛝pFEq> iCXFhSWӊ`7&?%=* y-S;7cmgЎ;ZU}c\:N.;X(y7I6.h9șZA1xp I$qL8E_6p &8(C8Pm0)A`(@![SM9z
+@gcyNrbj&ER! b`|JTTCRThJe
+ >6Ry̜BjF[V*4ʂ'.{W)Uh
+ɶn>}U+1AhRZUu Jjڻ=] "hVSs Vq9*.MM.kTm&Twƌ2)BA!>nfZr47$Vb302%MsGXp $ر^{(+- 07,x] XFcWSEnRţuy& 27$F+D`pDz: @SPZzߩk_V¹APh28& ?d ,msPGaZԕV*J6Gl1V2SD,@[xZCo=|G_{mq&И½dwHa87 yվz{㬋59-2w .m㏨`go;DCV8S iFGq]mj,[ h̚@Gtz;M]QAP(T1LcFWLaǣ“*>Ai|hٛjQLl:v H i@gCYv|-l|+t͝<#S^mAHX4 vJz.{RP38
+iF@#};Bc15 ƅt?]WH| )+Wӊ[{Rq aR]cԵ/k 2- FfNAF[Q h|iicQ3q9[vHiХ1%$nlrFS@AX(:H@?S2 h4Ӏ \?SɫiEխat?]BߐNK,v{]gpt &CObg`dʋ-݃#A!h
+q7Ք[2?0<˵;xI-o;q0F#Oīuh&͚'RPi>IRH(!J!q"RB(,%24i"͉QyNu߰wQ~{uz]z磩0yH*j<ZP۠W~;h"}t![c mH,2s?wyJ~e#l^h(MzjNC^0<v>qM|oFϯhm'h}e9Q78o3Re;ЧIy m!Xe OjXgD ^1yum.gnD%w6_c\q픓+6xDn=x:82!A6@uşޅy9ZQf%ͦ#GDNԵr[P8ڢ7طy"!.m&r ˨t
+
+1۠Gn t|{ ҂ژ%Hy}'<|QT3
+` EsB|\miȋ31lcKLnFGN.͎W60XgF'Ɋp#Gry_*%CKF>4򇶁
+p|A` tս
+pU8F&n:pT! u}⨁;f>O)lmQXCA|&>M-oAno$+>76pyG \ntpdB6U
+oE>
+oFEVT#``$ȪX9FU+8lG'UK{,V;bRa'QLU5-pTtRw6UܦL*NmPFDž;/>W5uQExp7罏fg5vR *Emn=Kίh':ɥgˋTbWXgs#*1UTt4P503ʭ\ktwumW
+;T<Sn?OΫ
+ 8'?T`@C\^
+8A/x8XVec6hcF9e䶞[En7FeAkAC5PY`%`*  rʩ"x'&%g=JB}×K q
+F[ |r|Z, 6ȉOةgbQFnbvi4if2>v[)A)19H,Z6O=z~k%ܹwlNPG&aϒrf\5]D2i!.֩1]mb}5/MuPF9wS_0JvT/ee93Hd+%C
+cI)$5B"(k%$%dm""ٲk_R~<"Ms sݟbbUes9>1}+hb
+\ln3:#*,4`4Ʒ(1,@~11E>#m R4TW.`w4p̙Ǿ U'p0
+zq Fcl8Z=0)`k1h|` 9Jk[x8&p
+jo(vb cw;C$vT?6FZJh2h`uB
+F$ =q@m$#>\w?'+UIJOx}G݃SU9Tpg\w>dUQRw<A \6nA-΄%d"ߍek&#h`dbXR
+L@׀k) GYghK,:H8@8p nifw*B>:}{??5|Xg-wL ptֺr',f+劬EE~=QIM3}؟no,zo^-%K8X,"8q:4>q96:^
+ 6:׀_ew%9E}+G*-C>$(X%Q{PԭlcmAV?k#m⟚ԠAUw[؟
+MEяFХmCeQ c:>T *ث :_}?"ܴo{ Sԧ8^% *aaW
+u8H
+q_72ufPՁ@38\4] #*쾩S Wp,Yj 1iU7L@ >(;%*uxϙ?ဿj@'}4#ç8}]UrRc.T\eJv(?3bkP_DRrX;bҼq>'Qi,\
+Zʁ#9_g#* N- JuH)m5rI)l
++BF8aRcuԅ[O_%10"mwk*J /uf1 |>;A!1UԷh3#=,(Is:ECiq2wF<s)6-5vƽA-*u'@L
+{c}Co8ϊx|qK-嵠۲48V@X;
+=X vm <;rH#8}/zPP^9q`0z m եF0~@MC
+!6Tg&EN/ bpH)jgt?nl#A޼,H 8>0H8RؤcrU μx`~R_Qx4jy?:x_M{V3PDe\(NBDʴ\}{G`xARhMl-6.r {D)̉Tذs*O; 2yl|Bɪn3s G pyόV#ɇqVJ%_#f(v$N0:snr+!2C.!TT5$CҀB,)ǐ"t3ʐ1CVfLd.i{=ޜug=-[o.#׷t LZ(Rggiϟ$߸bk,IcK~
+?mA9  Pj\8ni]AJd=/αG@TZqN.]WZ m9:bj0wK
+_M0ǒiZ;{!,^*
+V<F0L`-Uy7:ف*r֬ZW6XDVKVꮃv.烣ϯoǃj8`zIk}p^N@t7oob}3j|jnI 9Ϋ+@iT=I ww4١(S
+&Eu]#n~72јwx
+XǃAmȿ-jm
+?
+J@cOൄg(p\}$<M'mLd%.[ ?<`׮Sol{/1W"- ƀS#3z!]u%Y lLyZQܡchaz>Gc[=gb``%En`܊ v5߳MAJTp7uRjLOUPQ7jrb_|0K
++g NV͆ÿQbjJk
++މaSU[mzaIX<\|#Sr7ukm=-LӆzʼlPW m؂a~r̝⪆fjߋQ6gyT('ֵ`߳Gv| }KV 0<l񰴆Bƒ@p
+>jsCU1*P?k+mRlAoӃoWGEL/" ;QE?RS0#)6"ȡ}Zf"?xqons/$VJvA.F>>B(iHVJMY~V ߳ǭLޮ(+ZX!IJ^UkiOTVx|[+Gx={,d܊
+s2* /S08cs<(. RXԜB}52 az7Y ѡ,PI)Hm)-E~xCPDRNyǞ&G]GZ}
+2O_ <of&hC}혰K^.z$֭``ƁW
+\RV.`X{ /\W2[Դt ̑OaicӬ /?R`k1`/Y]e̔hdq@WSUA.)#x,_ו,x5D )+[:{G&` X@" 1*IEQ^ƽЋ@WKm /YR k+!)Hxz_ w?53m2$`P@C v>*-IO rq\ Y A+8br'Dx~8xDM/4w a PY"tĄso1
+G2Sn߸w YId [L>>!~9 eUud {H& Ijٞ@joV'݌
+ u?eoiOXxs-%/+^W@BaC&f0n‚*o~'
+: Dޒ$tRKV߱>by9Cmk
+r3Sc"}N9XTSƶ{[]a1)9EU= Mw @R3s J*([ک}@2jlLf
+" (
+j`Q1
+.뵭lu~g$_o`0y}ɛDx2?\_]Y;GMqs-jȏMLgHeu}s[Aȕkӷ?Xx_ϯ
+?7wG53=y} &qa6qf O&I%D9,-,#jX4721#YSml$&A.ّWP":TwhۉށsãʱS7gTgBݹ۪S7ǔÊ{;[ApgT*jfX׃tS$16 Z1(c3@'5M ߼+aϲExU^>195}sJu[JukёaŅ}=][VW-)
+H #7
+d}4 !#чCۧOD_ϟGxt/p+u%nD_:M[C$ч2}0#_.N3 }<c/.4
+E6(BPd"+٠
+UV(BlPd*+Y
+E6BPd*+YUV(BlPd"TY
+E6BlPd"TY
+U6(B Pd*+YEVAlPd*YEVAlPd*+٠
+U6(B Pe"TYEVA Pe"TYE6B lPe*٠
+U6(AlPe"TYU6(AlPe"TYU6(AlPe"TYU6A Pe*T٠U6B lPe"T٠
+U6A lPe"T٠UVA lPe*T٠U6A lPe*T٠U6A lPe*T٠U6A lPeGlPe*T٠U6A lPe*T٠U6AUU6A lPe* <,(@ lPe* <,(@ lPe* <e>_ lPe* <e>_ lPeG~\U٠D\T٠Dq>\T٠F.alPe* |GA>\T٠_D.1lPe/#U67|*TYe>@ \=ʻS˼*<Y;{6rwVe#ycU۪eӵUeUQ.G.,B2o/E.f,b2oe둫7*yD.ު\=[x3rPe#y*yu#"WyuC*ymc"Wyi"yi*y]"WyY#yYC+yMc#WyM+yE#WyA#yA7T.j\k)rr[2U^ƍ˼[+y Fn\ P̴ ̬IˌV̜yˌʐ̘̈<e\ɘU
+\晰e\Ye\)eNсEp8:G~`tڟ_+CYGq$:GD'q_9 3/Gc~~ tʯ5CCs:@'|~ϢyS]A-:5[]E#+϶C{ f\
+usK~otwm>хƠ_\gǕmF\f,EYF\ˡ܃~u-t.Nq+EF:8:,yASgCg&ПϧзOE >E?2.p?5E}gB׸z"я24G.3}D u[c~mCt6Egjkt'7.-S@_sSDG}oE-*lQ`{أ&k-
+Hy<swk)a,c[ckK]R%e)BTԡVɉD*ʖȖ%[,Jۭ{۹7تxt۹ן13;"""~wLMEp;M&}Mk.Gz#ut8|2",&_A |;Ff`/6^ZdYY988vnbggceE∛Ƭ y!L/Z/]66v]ĹDEvDZ̹h"mcv1]n/
+5!K5E㙕΋tET\Bre2T'Reee-^")!.&JAHq)Ԅ/,80'
+.•WT)khjjiiϙ2MIQA
+Rb"B|<܋9'Y$=ůY1`د0EL\Rj U^q9073562]B[(Olq&3IwbOb$ )()͚vN]\]]f .8oprt_gfJKsScC}]mMu<u5:pi8IDd!1 eJ4U &f \7{nz5oV/M]{N- 8YzrJB1Ii9E
+
+aѲA~vr\3fZPD\ZVQy70$<Sgf^UR^USSF.ȾÇBd{$0X?{P[]y0*l:Ña^
+ʚz&Vk\=}ęW߫mlFa/../ Y#W/FA6pNQA^vFZm.-/灃ZSfK1u:B£q’
+qk{W/(
+_BšFXaAvzr|LxϒLhB׮EΙcAq)m* w
+OF)FZ;CfwO܌i ֦U
+#C\[LPٶIFJ\DP
+wil/ݼUPu1#oIcҟnoixV]Qs#츾pFq?j50»XȸGP]}Q3!fH1Ն]{;⺯ @[m'\Ӧr YO5_?͆i['Ok[;{̈'0#i0Ɔ= u%yB=lM DAx3gi"k`ÇE0s UXVY 1Sç5]4huqC<lOg5"KA<S8cH<|.GZ 3vƌӲ@ 0SO*QA& nyQ JF (+%|ɂ3ygpi;s%ťdaWcƗqbZNqyu}S[5xQO&'aƺ4
+gsc}mUM2Bmeb!QIMʪq,f\SO$c47zd8YOkvuW.[4m63uY#Y[lu
+jۢ>MS\QSߌSb<iG7744ww{S} ✈9ېTQ -LhL 25%H6xs7"UOjč97T<{8Z2صYVJLig:[0΃d-,&%y)kGP\?c|@#qqt5M둶g+13y?MjZMg<@0z ~čV4ΰR[߃ k03<TQ.XH{Xػ\JH.*G}EbL?8WeEz9ےtam ̛Ǟqf<XHֺF$[g";]Me<MT_ #
+U߉sw{c]ei^zbLV9)1lgF KmQ3w IL+k0FęP$'5>2Xa+hq31̓Yd:eCf6^A9%kں#ar_gˋLmwZ{׈A;&&l=k:L֢',ܑ3 k^tن1:緯G^v4W'Fؑ# cG63fχiوd_VUrh[aq=@w[#vkV'8C; qfukSm kB?n٭yfuc[mÜ?9 k;#)693%p'aq ٹ#y7NC8gvܴΞv*')l1X3<|hv^=3WoƧ>kYBg+YӏA O+2oQ⬮qy,nmg΅%%W)1IO٭g;aO huB_sf<Xڸ
+f>wK-I52vXaD] nbikJ'd ټ{ɳN^ףQ#C{Pٲeyi<ءV[%1*5Z^I!/vJ42dUmXa(I1֦GTڰg`9+_@H\Za>]3"?y'r + 975.AU%y rL x`kSkg'Ϛ: SVD#G65 O $F]v4>OyТM]W,uC97<vsH#3EAu%n\65ܥNBX6]SPPTjvuQ
++j{_9<4psfjقʙ6Z$$wuv ]dhq!v4H
+<oq쐪䊥 <9S*.auPG~uWVf79!4ɱW.X@9oX#
+56m\%e!jl^ Gy=aکB9SAZl`0'7fvcPAIOꚻ Ga>dnvM`GE@ z6u]c WBn!5ؚ_ĝ|OSݤ#GBCYBEc\ELddI]
+-x ),Ckɾc_ᖚ~s;LWg{~ޯ7ZW^9>줷
+Tm^Td2k-wG'n_k~?5?I/# ޼xcJU% dƂRrxW\Rƃe-}C Θkӛ^,{+!/e&Uj
+҂j>s?^Ϧ׵u ךjOh.}{fc`QE%!kZ;y x#!T~p_Ɔ;[k˞=H =vp:lQ "fdrw5@%k֬`=62VW^LJ}YTys 3;d-3[>A@n`V2~YA6,
+jϩڠ$')&"p?lnL=IiMKg?dd~? YhUܸ b-d_p̵@[Y`  Qwrw1T& O!oؤ|(w{ UWE~J lkf,0
+ dC
+nAUi_9YPL ǥdP+h=\<8@XTOg\ #`ePAsrv.(\95,膋*?3)6
+CY0iO~phMZr
+
+EA1Ow4UQ)jamU3ryA^kȩ/[
+L)CemvպmιmX{W?x<tV_CQ0z;}ZPdjA舢2 幉Of{R[MVa FQ/ai&w%w~ zB춚1\-T~elz ̝}gU^͠M4WF]xZoJ |1 xOD_Oce xX#La`FՋi ^nc3ն} P^Ő:{(ogs Eq~Q/Z6^Q9TR *[z?gzj`' o"SFLZQ̜BR5I98> k w@{mI&"`Fvnزk4:y?ի 5 poseAJdUS?o[5^QZ'l܋HzתqHJܭ~9c+'2,8s.7Cb^ gNަG~^y1^6e  e.aUvW4yCܗ69 )NfeLFYBiøA2Wσ}5fQQ1Z@X|OӇ<xMٜRb߰tI(s(.kEtM3)J/fMʲj:fW
+ZfFTE~ʳvڪ2\,KyY^
+|l ZʞCYFMc 3t!="%DGʞ~#K72  欸\hȋ2lXHQgQ(c+$Z+ Rfcrx׎[_k֬zZeqF9e @Y;K3cB|+u"TBҪڦv^
+p0n\̰{e`Sw (c)fOvԕ~#",q/:-[vkZXPNʞ8M;V(Io^ #*OجR2W@{mq`&#LOWFf#+T
+.l:&NhF9\ڎA
+h_kUAӀ+7f݋WL^M=f>i"a]fL2(9ڥibͨmc{a+0S#y1_q3,^>!Ȍ30/_<=9>؁){SmȿP_pae`똕݈ʖQ00*ωu9 _6^[TM!zUR@[!g.F݋GTn99(c,15U_
+ Sj2BU6Z؛<d|᷀gm{1fȿZ`la!/JIe)Gz^̬Gzښhࠤf081w&,;6ua.W69$wizFՠ
+{Ak2?鉿NA&63N>exȽ>ؿA^'g [Lgd$)o(llЛPeRP
+ [Z0ȿ*;&,T>q=Pa1g:eG
+VvSyŕ"g@ac4KlRas6(l\,{6 <İAac:l'e9YHa3oW4 J̫'w.[EyXUk@SHZUwHlVYc7(lLi`GmqzTv~v<hY38(16CƲoSB^0#gO\q+iE5ReT%>s;oOn7 =ͺ3iG حep҆n<(ll\5EiWTccX0#gKXV][a {G&᠕n(|຃- (#Kw䂪IPsbCnBحKge-L:;C;*:s@ Prʞh*Hw4BԂ>9T.vT(lz"a8#RƇvJ/x./߀&{-|㢙 ƯfYVYy}RX>023޼ u1?yY6
+~URׅ'y|0]+#‡>˴Ig;$6>`Ga?Ȗ"J^Ey٘|0~J
+(KM%˂tDMPX JzAP09{Mt=g3s]W%! \]H,ffzrż\<js׊XM.^[3jyXƇԲk@tJ~eXB(ў/:b'[ZƱʪygעZ%\j"MA-S2mݱ"n5tZ#\[eg&+Ê[_cd{{Gk< }!pgCYNRĹ'ޱ)(c0olf2ʋߔ?w#{/L-,z#(6m`2$yaf2/9
+,Rm[h*ogVѳr I+i@\R3zKN
+|쌴N)23Q tHA)R0
+RPJM [bߴVy3>q#萂)xwCu%Bl)MLu!~z7= H;JɊ0eU[9l{^nESۙH
+|MyQYOxRNR1_Dy# GDVlxbvYC0R._WބcU3K.Ŕ!e * ;,!)Õ`xe[[YÓH|2Nգ^!2K;2t!*`kLs PDʐf2=5eϐ: _EqAz*҂dxg eW#(YH*Sc=r;Q1=Pn)L tUƔ*3q
+JӳvK/m@ʐei3Ph.HpQ
+i*Р RH$dPdqoEua۶}z׿ps}m7Аo?*hZ;܈{KkhW[*?޿h.+H&6^y ;^M/n[Dl}yb7aj<K4 e^Us7yd ZFj}s]UiA.?ѵ#$;~&"ZSЖ:˲߽tbeR9Yh-(ga򾢱D ZO;J p۳yRI-s!k~΁oPˠfׯ>^[wd?3?Y8T_| -e<}h8;3-sK,]cxے+@ԏ/_u6R]4ef>qUƻ\QT7ZSF{ZcqڱAS^H-g`f_$eh{3/#?d'prwSUWO9ZX"H2$Ll"v8^P38^)R1
+"7%&źB[f^/󪚺QG@g}yNRT~s]UiA.yZfd]gpLJnEc -wԕf=s}fmIN.AiU]QIQT{mqfBQ;5, t-wf~
+|1O^ OSbB,kztu֯F" .pG]gN7UAKoQI9@pxeć3]Tr&/{"ߖԶ CFY.Hli E/qƻ\zK+f\:Ɗr2Аec&_4_qә򪚺`q,e?q`7Y+um^1)?5taCh\lZYf/Ζ}/F>*7 Agգ6,<aP^cȅO@ ruYh6H0)hmtf"fgb,S 惞90dc;,\Ge.LL7tD$-m3{BnOlYƶnƽ Agy{!ގV2mEWlSs+Hx?kiֳK'f#$\oߕudc;5͓ڙY*)2,<R*kMw Ka;)$bSeˇ84R}afW\ih}*$
+@63Ge'EyܦDxYF+RAI(A˘
+VJwcLZ0,S)VnrO,irhO(UJ*#GU0
+ʼs)i8a^U6/19 =ODʬ9u#- dD823/U~A/8X'{ T"|f
+ _;O_SSoY'ポ^ce.+|!c7Rs׶M1:gT_kջCKEjEl
+g 4uԁV& p/qR7Z?02!UY]'S,)~vR_UzEиpLX@aΨ̤3wLKQBpA\^dɹc
+Akzrt=Q[.]{0VշDՔխ}#D3ku=Ou1Ql^lL9y~;#*P+MKfQw/BƔ-:eIM[(q!\Y,9.; ]e0R^aLtғOMC_!,
+_'^8Ql @yvL.]vG@čԜ5fro碬WnZE3ˡO * ,
+_ukA63'7ALNhþo>)0@fe͖( r/2\|Rʺ+mw`gvX.(/pF`fc!k߾#)33QV Q9ETt-vhvcI33<e?2Vٜ<@KjF& L ^߿}i,ؘ0K0_N|[afj&"(JOD,(c
+|f}eW`gfF(7U>K;,( >J[Cgd}v6sq47- /jfm{]KysKW/ݍދ sݴJ_,|R:ֻ~eui(`&FڪKrRoFz:n0efd˹+
++{ f&]mg*-B (l̒JfJL-m!BcBpm*~x="`zcMy*QF- &f`/4OȚWT7ݾtv?5(/WHBqg/K5;Pn,zoGn]k!'NeL
+ضXSE 3']S|'͇Q"l׭%9z6AV7E ,$k
++fTH}=Q1/3)&Q/uZ(3k
+]ާrJ[ag3.}]Y<%><`vD$ig*`n~!1Iyag3,}>7-bAg[ =zeJ2rv62ƆoY\C^2.`2z>AH};AA<=9u̴i׋0C:{-wag3,S>.j2bN"RJf u4<3}>婉6}-Ļv}l9 _35ql0 5fR"l<2y(Pt)|D T<Z_M} leggV4v ag13?5^[&#)&ߍ4U/ҙ):`9%U-=CgGΆO_ }^0.٨g$e)mnQ͟e޼pެ_ʳ <q_@ص:F' _3=B~Ȉzîk~M^ 2jz6ާ.|)߿Fԓ{qz8l0RO_gfG=[^c{ÿFJs
+{̴'Ч F^;uUeDؙT2q\2j L`O>`DٽP]N\^}]lQU]sk'Y#S4n^|9}:cME a>.z5~f ,7E%fU6v @Ѧ]fuyW~;QҢh_xgs K(j9 ^sNIUs1|4 v)`^ Yϟ8fw1ݞ2~fDqllRҚ^h`̏ʢԄ W5]ż`Nꭴv<~5Iއ:kUЧ<WUIN`?7)"#}l0 V9{ml^=53bne"MEqs"sJQhC/&(hMG&WN zؕ9%"e~;b`i9şpцg:$o/JyҀ1ŐykVY/>_C$*4!a59hTBqNr!#FNIu{!zwxΓy_ИYUh
+# t՗e$Dy;W_CIfFL3ň9܋K.nBDc!P=m ;Wmoߨ"')71aL10mcӞא)Amzr4
+̜Q(# L\FQ]o'_/mB1
+\+h<$R?c_(C]-%y,kMe 6/
+fxŀhoeaM$2~
+99&ݑ`(#!~(I6+
+0kBqw}E DW]IVR@ y`c'!C/)kS] eYI~䇚 ì28
+Y@e\Շc~ɐ{;kK EyC@3|~h)L/#-dv<6f9oÌsio. [B.cb&rKk;{Q9 z(`_7A _htRfnIMSGQPYq՞`̘G`V
+q32d
+f.~aemuߑ_+@omi6[W.L~Q]Xl\ <},$tM{\}>My_ZZ{j6?d\?a[GCE^z237+T5t|~,ƶ)ôm]_QlJInٜ c&yb@́] NL)mǙ|H[זgq~íd
+U f/ dwukrOGK}%Xku?SN25q/&-~sl6wC cIۦ 2R֍e"]p1߭qj$OcI48sk/sxaQos+[ ~fyF1k+
+ߓR?loo6uKā[Sښi!c Z6JZuY3 ֕s>.Eb[3w[6<yWKBPжYi) Fʚ(8 
+]s[3y[6*a9X:y\u?&XXq%mlY3-ζr#9<dE9]8Zo)x;ͬ흽V7 <LǸ8AN
+t)O5E9I1~6:ʲbdidbq ;{E%=y_U C>Nh<kˋr3co^dki|`ʶpZO 3!Ϋ *Z;܁\PF"jMO0n(~(%."sPZjқ7ii`+
+lݦqūask(US%Ќjq|d*kM׬y5cXq^x9\ajw\Co")hfUxwv`7#yW-[4L f;3OKW&&[ #R#>ګ<{Y
+RsqΛ}=cEV
+KG;(2HAX{fV5Zcoq\錅 \֨?̓L`<N.\ۢT1qp ̜— -=zq^b۱#h}]m턌YˣZdŅ&A&g߸EZAuG7ȸҪ&8Ġf4"u<Hlm$\.e9 aXe=L 3\a еf&1E5M}IzqoM1FUMh~Yݰn0^9Cʚ9sJ+ <&1-YQYu}3[߸ĸxb ;#9.\ "1^3YCmz^+("!ͭ=oNHɄ⮬%@ = [H3A1li)ARlD7lƫZgF뙓k 7ᬩgdfuop$*rRF47)b:1jA*1TunVZbLx\ ctX/@ y&5k03>ð>]CNnѿ>A =I4xxPS9TuxF5kpm0|2;5t<w<uMmw|}-OB tk+K!Fz\>oejjB6z<|bwkYx+/I
+YtCSk!=4 &s鼄/FQ@Bz:Ôب@kΎv͎bMJLXe~1kC8\Wشy=z-9\  .-F]=}PT#kYX3~"^(vwkK
+'D@ hVQ^h!|Ɵ3ZШqŤU59aim}_IstUM=Acӵ7cuO1z /;48@ :ReYIa~㇩{_Z{v*In$ȿf%T5ճ_97
+4|ZJHoYMc?$e~1 IiHT@nnmk5 ڈ{ 93 U=w
+OLNz_X\ZVQE얶vOPTm
+4jnWTٵwacK+[K.^a0:t)I
+;%M_TRQ 7nv#cS3s ^Y[{? u~
+/劁#B ldLu5ɪJ %ZV^QIM5064ٍ&E.Ҋm7;:{zo3844<<<44xޞΎm7Z[j*J
+sEDpqdl43c4b5
+XVKM+ _:Hưa;s|B"qI_PxjuMm]Ʀ렎jmxjUeEyYI‚,MMJD==\ٶL+lFFN%&hbim9
+9N\/ 84<2:6>19Ux.(3+BNnKEWKJJK\s!;+St>0591>6:2<48;[Zji a|؝5A/ c4t LiVLՃ  '$&%gD"QFzlJrRbB|\\l *2<,48כpuvd۱lЂ:tB{o >$'-!B¨̂ΰl).ϋ
+9-QaC|}|/2t 3`pц&IHKaԪd!`e[3OG'gW7w'˛yr=\]8l{;I%x 'zXFR'wi1"]#llC#cS3s n`XY0Yp8lPe1m K:faanfjldhGP_Uetw0A|ƩѪedp) :T>B7u`AQ: 4_^`BoWzF*$L!tL"V5
+L&TUU.΋|ߨmi]#lX".V'U1VE'ee?&]85k 1n
+q
+/GS0 gs
+486 0 0 480 6513.1162109 7207.1401367 cm
+/Im0 Do
+Q
+ endstream endobj 868 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 869 0 obj <</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 486>>/Filter/FlateDecode/Height 480/Intent/RelativeColorimetric/Length 25812/Name/X/Subtype/Image/Type/XObject/Width 486>>stream
+Hy<swk)a,c[ckK]R%e)BTԡVɉD*ʖȖ%[,Jۭ{۹7تxt۹ן13;"""~wLMEp;M&}Mk.Gz#ut8|2",&_A |;Ff`/6^ZdYY988vnbggceE∛Ƭ y!L/Z/]66v]ĹDEvDZ̹h"mcv1]n/
+5!K5E㙕΋tET\Bre2T'Reee-^")!.&JAHq)Ԅ/,80'
+.•WT)khjjiiϙ2MIQA
+Rb"B|<܋9'Y$=ůY1`د0EL\Rj U^q9073562]B[(Olq&3IwbOb$ )()͚vN]\]]f .8oprt_gfJKsScC}]mMu<u5:pi8IDd!1 eJ4U &f \7{nz5oV/M]{N- 8YzrJB1Ii9E
+
+aѲA~vr\3fZPD\ZVQy70$<Sgf^UR^USSF.ȾÇBd{$0X?{P[]y0*l:Ña^
+ʚz&Vk\=}ęW߫mlFa/../ Y#W/FA6pNQA^vFZm.-/灃ZSfK1u:B£q’
+qk{W/(
+_BšFXaAvzr|LxϒLhB׮EΙcAq)m* w
+OF)FZ;CfwO܌i ֦U
+#C\[LPٶIFJ\DP
+wil/ݼUPu1#oIcҟnoixV]Qs#츾pFq?j50»XȸGP]}Q3!fH1Ն]{;⺯ @[m'\Ӧr YO5_?͆i['Ok[;{̈'0#i0Ɔ= u%yB=lM DAx3gi"k`ÇE0s UXVY 1Sç5]4huqC<lOg5"KA<S8cH<|.GZ 3vƌӲ@ 0SO*QA& nyQ JF (+%|ɂ3ygpi;s%ťdaWcƗqbZNqyu}S[5xQO&'aƺ4
+gsc}mUM2Bmeb!QIMʪq,f\SO$c47zd8YOkvuW.[4m63uY#Y[lu
+jۢ>MS\QSߌSb<iG7744ww{S} ✈9ېTQ -LhL 25%H6xs7"UOjč97T<{8Z2صYVJLig:[0΃d-,&%y)kGP\?c|@#qqt5M둶g+13y?MjZMg<@0z ~čV4ΰR[߃ k03<TQ.XH{Xػ\JH.*G}EbL?8WeEz9ےtam ̛Ǟqf<XHֺF$[g";]Me<MT_ #
+U߉sw{c]ei^zbLV9)1lgF KmQ3w IL+k0FęP$'5>2Xa+hq31̓Yd:eCf6^A9%kں#ar_gˋLmwZ{׈A;&&l=k:L֢',ܑ3 k^tن1:緯G^v4W'Fؑ# cG63fχiوd_VUrh[aq=@w[#vkV'8C; qfukSm kB?n٭yfuc[mÜ?9 k;#)693%p'aq ٹ#y7NC8gvܴΞv*')l1X3<|hv^=3WoƧ>kYBg+YӏA O+2oQ⬮qy,nmg΅%%W)1IO٭g;aO huB_sf<Xڸ
+f>wK-I52vXaD] nbikJ'd ټ{ɳN^ףQ#C{Pٲeyi<ءV[%1*5Z^I!/vJ42dUmXa(I1֦GTڰg`9+_@H\Za>]3"?y'r + 975.AU%y rL x`kSkg'Ϛ: SVD#G65 O $F]v4>OyТM]W,uC97<vsH#3EAu%n\65ܥNBX6]SPPTjvuQ
++j{_9<4psfjقʙ6Z$$wuv ]dhq!v4H
+<oq쐪䊥 <9S*.auPG~uWVf79!4ɱW.X@9oX#
+56m\%e!jl^ Gy=aکB9SAZl`0'7fvcPAIOꚻ Ga>dnvM`GE@ z6u]c WBn!5ؚ_ĝ|OSݤ#GBCYBEc\ELddI]
+-x ),Ckɾc_ᖚ~s;LWg{~ޯ7ZW^9>줷
+Tm^Td2k-wG'n_k~?5?I/# ޼xcJU% dƂRrxW\Rƃe-}C Θkӛ^,{+!/e&Uj
+҂j>s?^Ϧ׵u ךjOh.}{fc`QE%!kZ;y x#!T~p_Ɔ;[k˞=H =vp:lQ "fdrw5@%k֬`=62VW^LJ}YTys 3;d-3[>A@n`V2~YA6,
+jϩڠ$')&"p?lnL=IiMKg?dd~? YhUܸ b-d_p̵@[Y`  Qwrw1T& O!oؤ|(w{ UWE~J lkf,0
+ dC
+nAUi_9YPL ǥdP+h=\<8@XTOg\ #`ePAsrv.(\95,膋*?3)6
+CY0iO~phMZr
+
+EA1Ow4UQ)jamU3ryA^kȩ/[
+L)CemvպmιmX{W?x<tV_CQ0z;}ZPdjA舢2 幉Of{R[MVa FQ/ai&w%w~ zB춚1\-T~elz ̝}gU^͠M4WF]xZoJ |1 xOD_Oce xX#La`FՋi ^nc3ն} P^Ő:{(ogs Eq~Q/Z6^Q9TR *[z?gzj`' o"SFLZQ̜BR5I98> k w@{mI&"`Fvnزk4:y?ի 5 poseAJdUS?o[5^QZ'l܋HzתqHJܭ~9c+'2,8s.7Cb^ gNަG~^y1^6e  e.aUvW4yCܗ69 )NfeLFYBiøA2Wσ}5fQQ1Z@X|OӇ<xMٜRb߰tI(s(.kEtM3)J/fMʲj:fW
+ZfFTE~ʳvڪ2\,KyY^
+|l ZʞCYFMc 3t!="%DGʞ~#K72  欸\hȋ2lXHQgQ(c+$Z+ Rfcrx׎[_k֬zZeqF9e @Y;K3cB|+u"TBҪڦv^
+p0n\̰{e`Sw (c)fOvԕ~#",q/:-[vkZXPNʞ8M;V(Io^ #*OجR2W@{mq`&#LOWFf#+T
+.l:&NhF9\ڎA
+h_kUAӀ+7f݋WL^M=f>i"a]fL2(9ڥibͨmc{a+0S#y1_q3,^>!Ȍ30/_<=9>؁){SmȿP_pae`똕݈ʖQ00*ωu9 _6^[TM!zUR@[!g.F݋GTn99(c,15U_
+ Sj2BU6Z؛<d|᷀gm{1fȿZ`la!/JIe)Gz^̬Gzښhࠤf081w&,;6ua.W69$wizFՠ
+{Ak2?鉿NA&63N>exȽ>ؿA^'g [Lgd$)o(llЛPeRP
+ [Z0ȿ*;&,T>q=Pa1g:eG
+VvSyŕ"g@ac4KlRas6(l\,{6 <İAac:l'e9YHa3oW4 J̫'w.[EyXUk@SHZUwHlVYc7(lLi`GmqzTv~v<hY38(16CƲoSB^0#gO\q+iE5ReT%>s;oOn7 =ͺ3iG حep҆n<(ll\5EiWTccX0#gKXV][a {G&᠕n(|຃- (#Kw䂪IPsbCnBحKge-L:;C;*:s@ Prʞh*Hw4BԂ>9T.vT(lz"a8#RƇvJ/x./߀&{-|㢙 ƯfYVYy}RX>023޼ u1?yY6
+~URׅ'y|0]+#‡>˴Ig;$6>`Ga?Ȗ"J^Ey٘|0~J
+(KM%˂tDMPX JzAP09{Mt=g3s]W%! \]H,ffzrż\<js׊XM.^[3jyXƇԲk@tJ~eXB(ў/:b'[ZƱʪygעZ%\j"MA-S2mݱ"n5tZ#\[eg&+Ê[_cd{{Gk< }!pgCYNRĹ'ޱ)(c0olf2ʋߔ?w#{/L-,z#(6m`2$yaf2/9
+,Rm[h*ogVѳr I+i@\R3zKN
+|쌴N)23Q tHA)R0
+RPJM [bߴVy3>q#萂)xwCu%Bl)MLu!~z7= H;JɊ0eU[9l{^nESۙH
+|MyQYOxRNR1_Dy# GDVlxbvYC0R._WބcU3K.Ŕ!e * ;,!)Õ`xe[[YÓH|2Nգ^!2K;2t!*`kLs PDʐf2=5eϐ: _EqAz*҂dxg eW#(YH*Sc=r;Q1=Pn)L tUƔ*3q
+JӳvK/m@ʐei3Ph.HpQ
+i*Р RH$dPdqoEua۶}z׿ps}m7Аo?*hZ;܈{KkhW[*?޿h.+H&6^y ;^M/n[Dl}yb7aj<K4 e^Us7yd ZFj}s]UiA.?ѵ#$;~&"ZSЖ:˲߽tbeR9Yh-(ga򾢱D ZO;J p۳yRI-s!k~΁oPˠfׯ>^[wd?3?Y8T_| -e<}h8;3-sK,]cxے+@ԏ/_u6R]4ef>qUƻ\QT7ZSF{ZcqڱAS^H-g`f_$eh{3/#?d'prwSUWO9ZX"H2$Ll"v8^P38^)R1
+"7%&źB[f^/󪚺QG@g}yNRT~s]UiA.yZfd]gpLJnEc -wԕf=s}fmIN.AiU]QIQT{mqfBQ;5, t-wf~
+|1O^ OSbB,kztu֯F" .pG]gN7UAKoQI9@pxeć3]Tr&/{"ߖԶ CFY.Hli E/qƻ\zK+f\:Ɗr2Аec&_4_qә򪚺`q,e?q`7Y+um^1)?5taCh\lZYf/Ζ}/F>*7 Agգ6,<aP^cȅO@ ruYh6H0)hmtf"fgb,S 惞90dc;,\Ge.LL7tD$-m3{BnOlYƶnƽ Agy{!ގV2mEWlSs+Hx?kiֳK'f#$\oߕudc;5͓ڙY*)2,<R*kMw Ka;)$bSeˇ84R}afW\ih}*$
+@63Ge'EyܦDxYF+RAI(A˘
+VJwcLZ0,S)VnrO,irhO(UJ*#GU0
+ʼs)i8a^U6/19 =ODʬ9u#- dD823/U~A/8X'{ T"|f
+ _;O_SSoY'ポ^ce.+|!c7Rs׶M1:gT_kջCKEjEl
+g 4uԁV& p/qR7Z?02!UY]'S,)~vR_UzEиpLX@aΨ̤3wLKQBpA\^dɹc
+Akzrt=Q[.]{0VշDՔխ}#D3ku=Ou1Ql^lL9y~;#*P+MKfQw/BƔ-:eIM[(q!\Y,9.; ]e0R^aLtғOMC_!,
+_'^8Ql @yvL.]vG@čԜ5fro碬WnZE3ˡO * ,
+_ukA63'7ALNhþo>)0@fe͖( r/2\|Rʺ+mw`gvX.(/pF`fc!k߾#)33QV Q9ETt-vhvcI33<e?2Vٜ<@KjF& L ^߿}i,ؘ0K0_N|[afj&"(JOD,(c
+|f}eW`gfF(7U>K;,( >J[Cgd}v6sq47- /jfm{]KysKW/ݍދ sݴJ_,|R:ֻ~eui(`&FڪKrRoFz:n0efd˹+
++{ f&]mg*-B (l̒JfJL-m!BcBpm*~x="`zcMy*QF- &f`/4OȚWT7ݾtv?5(/WHBqg/K5;Pn,zoGn]k!'NeL
+ضXSE 3']S|'͇Q"l׭%9z6AV7E ,$k
++fTH}=Q1/3)&Q/uZ(3k
+]ާrJ[ag3.}]Y<%><`vD$ig*`n~!1Iyag3,}>7-bAg[ =zeJ2rv62ƆoY\C^2.`2z>AH};AA<=9u̴i׋0C:{-wag3,S>.j2bN"RJf u4<3}>婉6}-Ļv}l9 _35ql0 5fR"l<2y(Pt)|D T<Z_M} leggV4v ag13?5^[&#)&ߍ4U/ҙ):`9%U-=CgGΆO_ }^0.٨g$e)mnQ͟e޼pެ_ʳ <q_@ص:F' _3=B~Ȉzîk~M^ 2jz6ާ.|)߿Fԓ{qz8l0RO_gfG=[^c{ÿFJs
+{̴'Ч F^;uUeDؙT2q\2j L`O>`DٽP]N\^}]lQU]sk'Y#S4n^|9}:cME a>.z5~f ,7E%fU6v @Ѧ]fuyW~;QҢh_xgs K(j9 ^sNIUs1|4 v)`^ Yϟ8fw1ݞ2~fDqllRҚ^h`̏ʢԄ W5]ż`Nꭴv<~5Iއ:kUЧ<WUIN`?7)"#}l0 V9{ml^=53bne"MEqs"sJQhC/&(hMG&WN zؕ9%"e~;b`i9şpцg:$o/JyҀ1ŐykVY/>_C$*4!a59hTBqNr!#FNIu{!zwxΓy_ИYUh
+# t՗e$Dy;W_CIfFL3ň9܋K.nBDc!P=m ;Wmoߨ"')71aL10mcӞא)Amzr4
+̜Q(# L\FQ]o'_/mB1
+\+h<$R?c_(C]-%y,kMe 6/
+fxŀhoeaM$2~
+99&ݑ`(#!~(I6+
+0kBqw}E DW]IVR@ y`c'!C/)kS] eYI~䇚 ì28
+Y@e\Շc~ɐ{;kK EyC@3|~h)L/#-dv<6f9oÌsio. [B.cb&rKk;{Q9 z(`_7A _htRfnIMSGQPYq՞`̘G`V
+q32d
+f.~aemuߑ_+@omi6[W.L~Q]Xl\ <},$tM{\}>My_ZZ{j6?d\?a[GCE^z237+T5t|~,ƶ)ôm]_QlJInٜ c&yb@́] NL)mǙ|H[זgq~íd
+U f/ dwukrOGK}%Xku?SN25q/&-~sl6wC cIۦ 2R֍e"]p1߭qj$OcI48sk/sxaQos+[ ~fyF1k+
+ߓR?loo6uKā[Sښi!c Z6JZuY3 ֕s>.Eb[3w[6<yWKBPжYi) Fʚ(8 
+]s[3y[6*a9X:y\u?&XXq%mlY3-ζr#9<dE9]8Zo)x;ͬ흽V7 <LǸ8AN
+t)O5E9I1~6:ʲbdidbq ;{E%=y_U C>Nh<kˋr3co^dki|`ʶpZO 3!Ϋ *Z;܁\PF"jMO0n(~(%."sPZjқ7ii`+
+lݦqūask(US%Ќjq|d*kM׬y5cXq^x9\ajw\Co")hfUxwv`7#yW-[4L f;3OKW&&[ #R#>ګ<{Y
+RsqΛ}=cEV
+KG;(2HAX{fV5Zcoq\錅 \֨?̓L`<N.\ۢT1qp ̜— -=zq^b۱#h}]m턌YˣZdŅ&A&g߸EZAuG7ȸҪ&8Ġf4"u<Hlm$\.e9 aXe=L 3\a еf&1E5M}IzqoM1FUMh~Yݰn0^9Cʚ9sJ+ <&1-YQYu}3[߸ĸxb ;#9.\ "1^3YCmz^+("!ͭ=oNHɄ⮬%@ = [H3A1li)ARlD7lƫZgF뙓k 7ᬩgdfuop$*rRF47)b:1jA*1TunVZbLx\ ctX/@ y&5k03>ð>]CNnѿ>A =I4xxPS9TuxF5kpm0|2;5t<w<uMmw|}-OB tk+K!Fz\>oejjB6z<|bwkYx+/I
+YtCSk!=4 &s鼄/FQ@Bz:Ôب@kΎv͎bMJLXe~1kC8\Wشy=z-9\  .-F]=}PT#kYX3~"^(vwkK
+'D@ hVQ^h!|Ɵ3ZШqŤU59aim}_IstUM=Acӵ7cuO1z /;48@ :ReYIa~㇩{_Z{v*In$ȿf%T5ճ_97
+4|ZJHoYMc?$e~1 IiHT@nnmk5 ڈ{ 93 U=w
+OLNz_X\ZVQE얶vOPTm
+4jnWTٵwacK+[K.^a0:t)I
+;%M_TRQ 7nv#cS3s ^Y[{? u~
+/劁#B ldLu5ɪJ %ZV^QIM5064ٍ&E.Ҋm7;:{zo3844<<<44xޞΎm7Z[j*J
+sEDpqdl43c4b5
+XVKM+ _:Hưa;s|B"qI_PxjuMm]Ʀ렎jmxjUeEyYI‚,MMJD==\ٶL+lFFN%&hbim9
+9N\/ 84<2:6>19Ux.(3+BNnKEWKJJK\s!;+St>0591>6:2<48;[Zji a|؝5A/ c4t LiVLՃ  '$&%gD"QFzlJrRbB|\\l *2<,48כpuvd۱lЂ:tB{o >$'-!B¨̂ΰl).ϋ
+9-QaC|}|/2t 3`pц&IHKaԪd!`e[3OG'gW7w'˛yr=\]8l{;I%x 'zXFR'wi1"]#llC#cS3s n`XY0Yp8lPe1m K:faanfjldhGP_Uetw0A|ƩѪedp) :T>B7u`AQ: 4_^`BoWzF*$L!tL"V5
+L&TUU.΋|ߨmi]#lX".V'U1VE'ee?&]85k 1n
+HV{|SnUHpKK%-J
+i£
+0Фm 
+*/E|Tz, S`2>g:n0@vn}>cs}so.E{Z>w
+w.BS0 H)+g+w]:J`ܟ(\&-$NTT1S(H&C1j(CPn1^ @Q1 dEK!P3.s""FRǶOhA I;SL|`]һf<͖ޣg9}?h2t_9h3W&>ׇ2Ɂ)SULQ9s̝7O,|rQ⧖<tYڕV?~uyao_|W_ݰqS-moλw~v]?l޳}?C>rϞ|˅ᨷNS"O"zl,yIw;ȝW4 !E0 V!]!Yt+gUY;Xu$klYZVڡ+PH- 6yB|!_z(._Z櫌|P(g =Zr![ƹDuqŵ 淬mR l!jho#{z|=
+qDܷnklF<7K,j%M
+A KPXuXpel5\ſo -kPO'8c8#8 qoc2ó88
+!IF1Ym3Y,OVNi-tw)1SmH %:
+dǍpU^b rAFβr9EJvydQ juM%1>!:qYh'40ҭld,p.gF2%MҜ2K^BDmISy"Y+`LR9$u80EI3+4(2rl"(r#8BP x.npXMvICHcȂAq ` Տv)wcN3g`:ў9S
+eW68!7wH+DYQĄ%%R1n+0EF9p
+9a@J c"7.xYݶiQ^ļKws|Q*Ms2]`%
+7k
+UF 6+^Z&ZZ4
+[ˊo44gACo\a7.C2s~Ήb7=ENd_Q.*A@~EͶtjVPgجu0w6sfXg+4 ZunHM FuE0^Mw~w~ŏyG_.&89 I!44
+Re(U`B mQ
+zyv~9Oclw\'//w`O5kC
+
+7Vʊ+{/Fw۫Eu/ :1+yT &t`lZلaXy-Ó9|!ҁ,t 04@œ~ WuZu:H஝Þw)wl8 71<_hh6&m
+zQdr8",߂QcdP<d(i89TJL\Մ#5ᕎz
+lZJe$xۘAcFFKlaY",F!2& DAuE4p#wjWG aF>FA0|\o,O뭌I-u;o~nEyi;i-7۶}C
+vڲyk ?u@A;1ɂ19O.Nې$qS 1:nQGhf$ sې}I3BY6X;>z݁+OM/Wx2Yp>+01n`3KӚQQ>2}j\Uj:n:kL7 yɇd@i33٬/ =ڵkNW~.2Ķ}?yHoⷉ5_.
+uxtésկx[- "Ql3}pEYD0*yRakL"a4BD0_jLJR Kh\Jܻ[{N*>r6a` Ʈ:RN q
+VoTz8 Fj jwq5KWNxyuƍպNKGbop>d
+9B!_ț4b
+U]j7'\q#Qx&/QA5׃ t `k @ =#=@=
+\w1Y 3'$B 2_;Nza_w |x乒olzKc_:CҡC]CDy0<~:G1w|$g<a1V!igE4DS 3}]eFliP[MM=zG<ۻ7''o7nIv"yI f̀49JeNQ((7zc27n
+Kxjd*<ܪ`M&H+/w7^v$)bFoe@c5
+,Yҡ' ZuK6W&'d;T{؆QO6`5m77y5ZZ-49{_^^Z|? }Bدب+|޽wއ{׷Ư+c11]6i]55j֡+Bip*H.U%MK.QSEP( m$h!Xn #}wΜ93sΙsΩԲBu(432t0f7lj2Y_ʮj?ϏĜq9YX+f,fve>|^MbUG{pzvhq=ҩ4qyG2 '>3۬IiЕLy(>zcGa;hƘUFN~hHV%?4m?ԛX}]qR!2=l_\'pVDf_VNU5;TEt8N(G`,Cqrmf?a4oB߉gK:~cEԐ-ܺ'%! wŸg2lȕWdkTYzP'YBdSR:>*o:Z=f%e7-FlZj6xZiYǮ*Gf]18!)k9<ֹ,& Λ;1?#K+*yy>ɗːH!Dr#RtYdOY{Ÿ_WgK_h})Q|܋ , ~&rO֚W`uLkhMZW|ĽȨh|g_1 (B]w: â;g}b{ 6;1zxi^yo~~1T8a50rqW482D\Ԑrh YY*l (=L\kŵDl ?q&Zvً-% u<uʘ8'?&ʃCasYj*c.N4ujcV(BaQ^H^}xb5w9Gs:0HdU5sߒ}sR'][hBMiJ.uY@}J'y1N 
+-$֑(m?қ4"\*t04%ͦ iGyj>ÎQ-GG=J;* 7Ej^+e]KTh>j{Nx}žƱ,
+VkBPlWz,[H'O7ݫo CkKx B;P튆n.?PٶeږbD--@ 0)v
+>BNQ<^#p#5 -SzDCJ*T?2J=ͭmiUsi,2^pBqޠs14ƆI|U΍#e%4X !|,#о@#!NU|7=W/d&kQЃ=T2CДn?̪FArVi |CCȶJ7 >pX``O#t+o';q$-q#
+H|kxW;ϝ}ξ76<XH4!dBTZb(j
+jVjVm->CZ̒ٹs
+i%"00qg_#~۸ubn
+<[݇gqN]bXɜ
+[ eܸ"Arp&Y@Vr{4p$Zad*QU]S9+Wohl60cή K.[bWW֬]7~phxƑMha󖭣? ۭ@w3;>
+w]|ⓟO|_~xC_z_yWڡǾ~7o>q[ǿS:~8?O:Og쳿xz~ ,I600XO! G)8籢.U0rG7 ͈icbKi$C8p
+U@_nK|ϼlm^44onf^0ϛ5_6cC];zK
+g
+l'8ESpwIvMY"5QSctTw_hL&ncd`Ħ Ó:7ἢ?Vj/n,Zzز"j<asY*L\MQbҽK:Cb(3JgzKg:Cb'Ex&<3_UWaz1fY ig>RQWӽJt^`}QF%()_
+$ED<r tSA._Sm6fWL2m^&!,0#$kKb+ /qZ1E,v C$=p(xRzV<G)R^-#ILWMUL\]}b4ȋveqйt~NSeYWX֨s{WK'vX
+u^V<$W,Ӑolʧ9
+찫ѰOd+N"H28yV܂ZE*B[i^J82$1R2Es.˟cUAE6ˡ9]v!OD4 CxS 9y(.q9⏗Rkm
+Bnqyz_^d5'##"7:I$ O@C4!E_=ؖκULeu\Pg4c !:z :Uѡ(Av!nաHT`7)Q.͛o
+#$BU9sE | -[yTbʇ*K[W2,t r0nB1ż(va"ي&ͱ+͢fZ9gL:h줟a܉V,cHFoT1*8+s/ v(f_3srX|6Mnuҍ2kՎэ.zanbx@<GqU5Mt!­ j/Rq{E?e1@Hʧ%|Y87&mulK x\z6jU̳4sx*P;;(BэՐ+UNÿȥ6A; KR\B㳸ZYz#|G[ZGZE8z+ߘyܼcwM9mbM{ɓS9, S*ٿ~ yr#7?VLwW]}&X%kr@
+q
+0 g
+/GS0 gs
+15.8993082 0 0 -16.1025677 7632.3847656 6956.671875 cm
+BX /Sh0 sh EX Q
+ endstream endobj 891 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 892 0 obj <</AntiAlias false/ColorSpace/DeviceGray/Coords[0.0 0.0 0.0 0.0 0.0 1.0]/Domain[0.0 1.0]/Extend[true true]/Function 893 0 R/ShadingType 3>> endobj 893 0 obj <</Bounds[0.210899 0.350209 0.494759 0.604521 0.917524]/Domain[0.0 1.0]/Encode[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]/FunctionType 3/Functions[894 0 R 894 0 R 894 0 R 894 0 R 895 0 R 896 0 R]>> endobj 894 0 obj <</C0[0.5]/C1[0.5]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 895 0 obj <</C0[0.5]/C1[0.987469]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 896 0 obj <</C0[0.987469]/C1[1.0]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 888 0 obj <</G 897 0 R/S/Luminosity/Type/Mask>> endobj 897 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 898 0 R/Length 86/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+11.9217148 0 0 -12.0797682 7632.2294922 6995.9882813 cm
+BX /Sh0 sh EX Q
+ endstream endobj 898 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 887 0 obj <</G 899 0 R/S/Luminosity/Type/Mask>> endobj 899 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 900 0 R/Length 88/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+225.9361572 0 0 -225.9361572 7629.4453125 7389.2958984 cm
+BX /Sh0 sh EX Q
+ endstream endobj 900 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 886 0 obj <</G 901 0 R/S/Luminosity/Type/Mask>> endobj 901 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 902 0 R/Length 84/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+7.9551892 0 0 -8.0455885 7632.2294922 7026.9384766 cm
+BX /Sh0 sh EX Q
+ endstream endobj 902 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 885 0 obj <</G 903 0 R/S/Luminosity/Type/Mask>> endobj 903 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 904 0 R/Length 86/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+225.9359131 0 0 -225.9359131 6749.46875 7454.5385742 cm
+BX /Sh0 sh EX Q
+ endstream endobj 904 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 734 0 obj <</LastModified(D:20140918115258+02'00')/Private 905 0 R>> endobj 905 0 obj <</AIMetaData 906 0 R/AIPrivateData1 907 0 R/AIPrivateData2 908 0 R/AIPrivateData3 909 0 R/AIPrivateData4 910 0 R/AIPrivateData5 911 0 R/ContainerVersion 11/CreatorVersion 16/NumBlock 5/RoundtripStreamType 1/RoundtripVersion 16>> endobj 906 0 obj <</Length 960>>stream
+%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 16.0 %%AI8_CreatorVersion: 16.0.0 %%For: (Coraline Lafon) () %%Title: (logotalerv2.ai) %%CreationDate: 18/09/2014 11:52 %%Canvassize: 16383 %%BoundingBox: 224 -1014 1618 26 %%HiResBoundingBox: 224.5146 -1013.7627 1617.3057 26 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 12.0 %AI12_BuildNumber: 682 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Repérage]) %AI3_Cropmarks: 0 -1080 1920 0 %AI3_TemplateBox: 960.5 -540.5 960.5 -540.5 %AI3_TileBox: 582 -828 1316 -252 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -307 125 0.5 1389 670 18 0 0 163 161 0 0 0 1 1 1 1 1 1 0 %AI5_OpenViewLayers: 7 %%PageOrigin:524 -780 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 907 0 obj <</Length 10404>>stream
+%%BoundingBox: 224 -1014 1618 26 %%HiResBoundingBox: 224.5146 -1013.7627 1617.3057 26 %AI7_Thumbnail: 128 96 8 %%BeginData: 10254 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FDFCFFFDFCFFFDFCFFFDA4FFA8FFA8FFA8FD77FFA87D5227272752 %2752527D7DFD71FFA85227F826F82752277D27527DF8F8F8527DFD6CFFA8 %52F82727527DA87DA8277D52A87D7D7D52F827277DFD69FF5227F8527DA8 %7D7D527D5252527D52FD057DF827F8277DFD66FF27F8277D7D7D52527DA8 %A8FFCAFFFD04A8527D27522727F8277DFD64FF26F852A87D5252A7A8FD05 %FFCFFD05FFA87D27522727F82652FD41FFA8FFA8FD1EFFF8277DA87D7DA8 %FD11FF52522727262752FD3BFFA87D5252FD05275252A8A8FD18FFF8277D %A85252A8FFA8FFA8FFCFFFA8FFA8FFA8FFFFFFA8FFFFFF7D522727F8F852 %FD37FF7D52F827F8F8F827F827F827F8F8F82727A8FD15FF525252A8527D %FFFFA8512752A8FD04FF7D76FFFFA8A8FFCFFFFFFFA852275227277DFD34 %FFA85227F8FD042752275227522752FD0427F8527DFD12FF7DF87DA85252 %CAFFFFCF522752FFA8527DFF7D52CF7DF852FF5227A8CFFFA8522727F827 %A8FD32FF7CF827F82727522752527D527D527D525227522727F82751FD10 %FFA827F8525252A8FD04FFA827A8FFA8F852A87D52FF7D2752FF527DA8FF %FFFF7D522727F852FD30FFA852F8272752275252A8A8FFCFFFFFFFA8FFA8 %7D5252FD0527A8FD0EFF7DF87D5252A8FFA8FFFFFF7D527DFF7D5252CA7D %7DA8FF527DA87D7DFFFFFFA8FF52272727F8A8FD2EFFA827F8FD04277DA8 %FD05FFA8FFCFFFCFFFFFA87D7DFD0427F87DFD0DFF527C7D7D7DFD05FFA8 %FFA8A8A8FFA8FFA8FFA8CFA8FFA8FFCFFFA8FFA8FFFFFF2752272752FD2D %FFA827F827275252FD11FFA87D52522727F8A8FD0BFF7D277DA852FD04FF %52FD07F827F8F8F827F827F827FD05F827A8FFFF7D2727F8277DFD2BFFA8 %27F82727527DFD05FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFA852522727F8 %A8FD0AFF527DA8527DFD04FF7D522727275227522727F8F8F82727522752 %2751272727A8FFFFA8522727F87DFD2BFF52F82727527DFD16FFA8525227 %27F8FD0AFFF852A8527DFFA8FFFF7D2752272727512752FD05F8FD042752 %2727277DA8FFA8FF52FD0427FD2AFF7DF82727527DFFA8FFCFFFA8FFCFFF %A8FFCFFFA8FFCFFFA8FFCFFFA8FFFFA8525227F827FD08FF7D27527D52FD %05FF7D272727F8FD0527F827F827F827F8272727F82752A8FFFFFFA82752 %2727A8FD29FFF82727527DFD1AFFA8525227277DFD07FF7DF87D527DFFFF %A8FFCFFFFD08A827F82727F87DFD06A8A7A8A8FFA8FFA8272727F87DFD28 %FF5226272752FFFFFFCFFD18FF7D5227F827FD07FF52277D527DFD0EFF7D %F8272727A7FD0DFF275227277DFD27FFA827F82752A8FD04FFA8FFCFFFA8 %FFA8FFA8FFCFFFA8FFA8FFA8FFCFFFA8FFFFFFA87D5227F8A8FD06FF7D52 %A8527DFFFFCFFFA8FFCFFFA8FFCFFFA8FF5227272726A8A8FFCFFFA8FFCF %FFA8FFCFFFA8522727F87DFD27FF7DF827277DFFFFA8FF52FD15F852FFCF %FF7D52272627FD06FF52A87D527DFD0EFF7D275227277DFD0DFF52522727 %52FD27FF27272752A8FD04FFA852FD047DA17DA827F8F8F852A8FD067D52 %A8FD04FF52522727A8FD05FF52272727A8FD04FFA8FFFFFFA8FFFFFFA8FF %5227525227A8CFFFFFFFA8FFFFFFA8FFFFFFA8522727F852FD26FFA827F8 %2752FFFFFFA8FF7D52527D5252527D5227F827F827527D5252527D527DCF %FFFFFFCF7D2727F87DFD05FF52F82752A8FD0EFF7D277D5227A7FD0DFF27 %52272752FD26FF7DF827277DFD05FFA8FD07527D27F8F82727FD0852FD05 %FFA752272752FD05FF7DF827277DFFFFA8FFCFFFA8FFCFFFA8FFCFFF5252 %7D5227A8FFFFA8FFCFFFA8FFCFFFA8FFA8272727F87DA8FD25FF52F8F852 %7DFFA8FFCFFD0AFF7DF827F87DFD08FFCFFFA8FFFFA85252F827FD05FF7D %2727527DFD0EFF7D52A87D52A7FD0CFFA8277D52277DFD26FF52F82752A8 %FD0EFF5227272752FD0DFFA87D272727FD05FFA8F8272752CFFFA8FFFFFF %A8FFFFFFA8FFFFFF52527D7D27A8FFFFA8FFFFFFA8FFFFFFA8FF7D27A87D %F87DFD26FF27272752A8FFA8FFFFFFA8FFFFFFA8FD04FF7DF827F87DFFFF %A8FFFFFFA8FFFFFFA8FFFFFF52522727FD06FF27F82727A8FD0DFF7D52A8 %7D277DFD0CFF5252A82727A8FD26FF52F82752FD0FFF5227522752FD0EFF %7D272727FD06FF52F8272752FFCFFFA8FFCFFFA8FFCFFFA8FFFD045227A8 %A8FFCFFFA8FFCFFFA8FFFFA8277D272727FD27FF27272752A8FFCFFFA8FF %CFFFA8FFCFFFA8FFFF7C2752277DFFFFCFFFA8FFCFFFA8FFCFFFA8FF5252 %F827FD06FFA8F8272752A8FD0CFF7D277D52277DFD0BFF5252A852F87DFD %27FF52F82752FD0FFFFD0552FD0EFF7D272727FD07FF52F827277DFFFFA8 %FFFFFFA8FFFFFFA8FF5227525227A8CFFFFFFFA8FFFFFFA8FFA8277D7DF8 %27A8FD27FF52272752A7FFFFFFA8FFFFFFA8FFFFFFA8FFFF7D277D527DFD %04FFA8FFFFFFA8FFFFFFCFFF5252F827FD07FF7DFD0427A8FD0BFF7D2752 %2727A7FD0AFF5252A8272752FD28FF7DF82727A8FD0EFF52527D7D7CFD0D %FFA87D272752FD08FF27F8272727FFFFFFA8FFCFFFA8FFCFFF52272727F8 %A8FFFFA8FFCFFFA8FFFF7DF87D7D2727A8FD28FF7DF8F82752FFA8FFCFFF %A8FFCFFFA8FFCFFFFF7D52A8527DFFFFA8FFCFFFA8FFCFFFA8FFFFA82727 %F87DFD08FFA82727275252FD0AFF7DF827F827A8FD08FFA8527DA852F87D %FD2AFF27272752A8FD0DFF52527D7D7DFD0DFF7D522727A8FD09FF7DF827 %272752FD05FFA8FFFFFF52FD04F8A8FFFFA8FD04FF7D52A87D7DF852A8FD %2AFF52F82727A8FD04FFA8FFFFFFA8FD04FF7D277D277DFFFFA8FFFFFFA8 %FFFFFFA8FFA852272727FD0BFF7DF827275252A8FD07FF7DF827F8277DFD %06FF5227525252F852A8FD2BFF7D27275252FD0DFFFD0552FD0CFFA85227 %F87DFD0BFFA852F8FD04277DA8FFFFFFA8FF52FD04F8A8CAFFFFFF7D5227 %7D5227F8277DFD2DFF272627527DFFA8FFCFFFA8FFCFFFA8FFFF76275227 %7DFFFFCFFFA8FFCFFFA8FFFFA85252F827A8FD0DFF7DF827275227527DCF %FFFFFF7DF827F827A8FFA8A8272752A87D5227527DFD2EFFA8F827277CA8 %FD0BFF5227522752FD0BFF7D52272752FD0FFF7DF827F8FD04275252A852 %FD04F852525227527DA87D27F8527DFD30FF52F827277DCFFFFFFFA8FFFF %FFA8FFFF7CF852F87CFD04FFA8FFFFFFCFFF7D52272727FD11FFA82727F8 %2727522727277D527D272727FD057D2727277DA8FD31FFA827F827527DFD %0AFF52F8272752FD09FF7D7D2727F8A8FD12FFA85227F827F82727277D7D %7DA87D7D527D52FD04277DA8FD33FF7DF8F827277DA8FFCFFFA8FFCFFFFF %7DF827F87DFFFFA8FFCFFFCFFF7D522727F87DFD15FFA87DFD0427F87D52 %7C525252FD0427527DFD37FF7D272752277DA8FD07FF52F8F8F852FD07FF %7D522751F852FD18FFA87D7D52522752FD0527527DA8A8FD39FF5227F827 %27527DFD06FF7DF8F8F87DFFFFCFFFFFA852522727F852FD1DFFFD07A8FD %3FFF7D27F827275252A8A8FFFFFF52F8F8F852FFFFFFA87D52522727F87D %FD65FF7D27F8272752275252A8A852F8F8F852A87D5252275227F8F87DFD %68FF7DFD0527FD04527DFD06522727F82752FD6CFF5227F8F8F8FD042752 %FD0427F8F82652A8FD6FFFA852522727F827F8FD0427527DFD1CFFA8A8A8 %FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8FFA8FFA8FD3DFF7D %A87D7D7DA8A8FD1EFFA8FD0FFFA8FD09FF52FFFFFFA8FD56FFA8FD19FF7D %2727A8FD06FFA827A8FD58FFA8272752FD08FFA8FD0EFFA8277DFD08FF27 %FFFFFFA8FD55FF7DA8FF2752FD07FFA8A8FFFFA8A8FFA8A8A8FFA8FFA8FF %FF7DA8FFA8FFFFA8FFFFA827A8FFA8FD0BFFA8FD4AFFA8FFFFFFF8FFFFFF %7DA8FFFFA8FFFF7D7D7DA852A8A8A852527D7DFF52FF27FD057D52FF27FF %FFFFA8FD09FF7DFD4BFFA8A8FFFF527DFFFF7DA8FD04FF7D52FFFF5252FF %7D7D27A8FF7DA87D7D27FFA87DFF7DF8A827FFFFA8FFFFA8A8FD05FFA87D %FD4BFFA8FFFFFF5252FD06FFA8FF7D7DFFFF7D52A8A8A852FFFF7DFF7DA8 %27FF7DFFFFFFF8A827FFFFFFA8FF7DFD53FFA8A8FFFF7D52FF52A8A8FD04 %FF7D52FFFF7D27FFFFFFF8FFFF7DA87D7D27FF7D7DFF7DF8A827FFFFA8FF %FF7DA87D527DA8A87DA8A8FD4AFFA8FFFFFF527D52A8FF7DA8FFA8FFFF7D %7D7DA87D7D7DA87DA8FF7DA852FF52FFFF7D7D7D52A852A8FFFFA8FF52A8 %52FFFFFF27FFA87DFD4AFFA8A8FFFF277D527D7D52FD0BFFA8FD05FFA8FD %06FFA8FD04FFA8FD04FF52A8FD047D527D7D7DFD4AFF7DFFFFA827FF27FD %05FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFFFFF52FFFFFFA85252FD4DFF7D277DF8A8FF527DFF7DFD07FFA8FFFF %FFA8FFFFFFA8A8A8FFA8FFFFFFA8FFFFFFA8FD06FF52A8FFA87D52527DA8 %A8FD33FFA8A8FD15FFA87D52FD04FF7D7DFFFFFFA8A8FFFFA8FFFFFFA8FF %FFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFF7DA8FFFFA8FFA87D52FFFF7D %7DFD35FF7DFD21FF5227FD0B7DA8FFFFA852FD0A7D5227FD3FFFA87DA8FD %20FFA8A8FD0CFFA8FFA8FD0BFFA8A8FD3FFF7D7DFD30FFA8FD4DFF7DFD80 %FFA8FD14FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FD31FF7D7D7DFD13FF52F8272727F8272727F8272727F8272727F827 %2727F8272727F8272727F8272727F8272727F8272727F8272727F8272727 %F8272727F8272727F8A8FD2EFFFD04A8FD13FF27FD39F87DFD2FFF7DA8A8 %FD13FF52FD0FF827F8272727F8272727F8272727F827F827F8272727F827 %2727F8272727FD0DF8A8FD45FF27FD0EF827F8F8F827F8F8F827F8F8F827 %F8F8F827F8F8F827FD04F82727F827FD0EF87DFD45FF52FD1DF8525227FD %07F87D52F8F827FD0DF8A8FD45FF27F8F8527D52FD09F827FD0DF827A8FF %52FD07F8527DFD10F8A8FD45FF52F8277D7DA8A8FD07F8272727F8F8F827 %FD09F85252FD08F87D52F8F827FD0DF8A8FD45FF27F8F827F8F8A852F8F8 %F827F8F8F827F8275252F8275227F827525227F82752277D272727522727 %527DFD0DF827F8F8A8FD45FF52F82727F8F852A8F8F87D27F8F827F8277D %2727527D52F8527DFF275227277DA82727525227FF527D52F8F827FD09F8 %7D27F8F8A8FD45FF27F8F827F8F8F8A8FD07F827F8A8F8F8F8A82752527D %52F8F852F87D7DF8F87DF8F87D7D527DFD04F82752FD0AF87DFD45FF52F8 %2727F8F827A827F827F8F8F827F827A827F8F8A827F8F8527DF82752277D %A8F82752F8F8A87D7D52F8F827F82727F8F82727F8F827F8F8F8A8FD45FF %27F8F852F8F8F8A8277DF852F8F8F827F8A82727277D5227277D7DF82752 %277DA827F87D2727A87D527DFD04F82752277D5227277DF852F8F87DFD45 %FF52F82727F8F8277D7D52277D27FD04F827525227F8525227F827F82727 %522752F8F8F852522727275227F827F8FD045227F87D7D277D27F8A8FD45 %FF27F8F827F8F87D52527D2727F8F8F827FD1EF82752F852527D7D7D2727 %F8F8A8FD45FF52F82752F827FFF87D52F8F827F8F8F8272727F8272727F8 %272727F8272727F8272727F8272727F8272727F827F8527DF8F8F852A852 %F8F827F8A8FD45FF27F8F87DA8A827F8F87D5252F8F8F827FD0DF827FD0D %F827F8F82752F8527D7D277D5252F8F8A8FD45FF52F8F8F827FD05F827F8 %F8F827A8A8527D527D527D527D527D7D27F8F8277D527D527D527D527D52 %7DA8A8F8F8F8272727F8F8F827F8F8F8A8FD45FF27FD0EF87D52FD0A2752 %F8F8F8FD0C277D52FD0DF87DFD45FF52FD1BF827F827FD1BF8A8FD45FF27 %FD39F87DFD45FF27FD39F8A8FD45FFFD3A7DA8FD44FFFF %%EndData endstream endobj 908 0 obj <</Length 65536>>stream
+%AI12_CompressedDataxr$7 m#ͶH^۶fdl$+Uul혌dJ%ɬjww!MYW8s_q3m.o~WW>arc?Hxy{;~<ûޝ_]\lޜrŗ/.ǫ۟nϯ.b/8'q:c~,v8Ǐ?G~ywync|ehF_.pq ;H&trwwo/>~^}f?/vquun9^?/.>B}݇O/w^6ᇳ˫ Xpt%f֟>|`j__C0
+zW.߿?\ yݧ /a{0||3)DI:<
+h9&twD@Tf`x1s:l
+G[:r\
+l0 qO~`
+};oܩ;q[w&]pYggԞح=dM6dY G֨b(|\<Xg$1{JdE2ELK䖨8ycDSZ3sEAANv[QEB9KrgB9$ f!N аPqB2)=DZ255E@Q
+:r "LDÉ8{Dr%Tuq2`,NF$["2c |04X"3XZ'Ds
+(I>BD3
+;*,14R̠'LO;`́qʥE@%(;ju <PBB[*߱F-eB]?_m8QSle1Y-7si6Ɗ`f=ٸz,ᩦUkYv@a&'>, 2˜ KԊhwUt"[3* :G&bis8s0=&?*6"ġIz5-}qnA5ױG7 = ڝݲiy<hgesVюݟyּUpw4N6k.Ӳݮrd[*iq+vw;QirDڔY<7*F2 Ƿ-6 f^o ކ'Q5T쩛]W$R(Ϋs:4!J6DYl]TՖH۩"Zz*= -b?kKD^/<-v o6I!7{%.TpJlCvo[ncc0s|{℞LKl4qMzC-AGVMqx%%v1n뒶qDףBv%e{t2SdJ6Ua-mokwf(o[5;#FCPx|hBCvWROِaX;<KbSJyҦ })նmL"7ښٟP6d-fyņa㨡 =GOrsul:zζ#w<"zTuTix1U_Hgo$u}
+]g>a;ׇjqua3>P_vSOZަ}}ہ_K:?uIlog늙nrSS#s凋%ebåƑ)|l׶|Z^[wS-09U
+qgq޹jJ J]~JISt5' <1Dɂ5cdb!ؑyĺ &O1)F4
++~r1YѲjy\Ee\xH#%պe].Nv8-δ
+ ,1Jvf]b"CIsaBEVlhs,;Bౄ6F]6ҭ; G JG#]
+C
+֐=뎯%Tb>c[NM~frĝyyxr'(7cgڱe
+ۮ~3=[~1vyV}S1K0\K]_TqQ|f9t4=)69=Òg_`~USY51#iD!T9WL 7О#\c]33
+/ 2 4ܿ-YM;Z`|P 2کpi) 蛆{W>FŇj^tpsrOz;=l|ZZ$C/:t9Z\ iXE^{p٧Uz
+">c(p,/D8 o9A Zp;ht}ςu:l*! Бf>-VqSQw33w?ѓ%!4XZo,M,m3)s0Q ϹV'F)%fR/^}dPR
+ :.h %7"bAFLԴVjׇ 2$,Cd,K8BJIy]mO*ʴ]i=#RZֵ4I,/_.YVdP4WK)Q$,ԉJ J\^*he>=v@V b.,s{r K{9⠘^οY1QvL69.V,/b Tpn1a{0H
+bPѲ EaŽ֬}a[o mLc
+MhF#ΘaۦN28㪘:5h7G:S9 0V cwzLg'F-P{`rK,^i-UaJe")/{*ǖrlKvSo"CWdۺ1;Λ(s鋢Lg֖Ly}+wZV0/
+׫Zȉ
+f*w垏j?밽922kMv-T#{`'L=ªE״ZjPpM\Kc9 ZEX0ڲ$Dځl%Yj@9Iw$/(zaԢȯ'vSb( u ^lwm~ f’YNUۑ.;;H'v#t}Q개Ta2TPF/߽Z c$nt]sO@9Աgc_M17V?ʂ
+D~6r+H5|%khy'v{04h *u΁
+ Pkxb5{POUGK]OCRegہ}+H1j|a &lCPIaVzhӲay, v
+d󛪏+J^TYj^;(>^
+>(jutFGTA`s/o0>ƨst1)kOv âg.&ОsugSuο:rEڋwϒ-8u&A>LL|vqY{ e|SA`U~:WjJSёDvQGT\vrH=PRrLG%!?: jN03ʁ<VɱC꩞1OeIG;4,0L'c9e#0c=Q61qYG:wX!ʑݓP7F էAWl)ؤw Zdp,̃ ^M1$
+P"7&z}dL:$Bxch W>[q݉t,WЫ/;%y߾CqZz}RJ3 ֒
+HرS
+5F8ZU8i|uLi.ˎ FꊥEД-Us{y[h)*iXg;Ю:n< Zuuh"/
+:w}\{/:'WIcQV(z0
+RNFQ<PRq=Oi2#R۷GG ׋JXMձ)mq{F2ONqL^ }N^Ob5 u|q4ll)7"P*Q2h
+-^L܁)>øa}iWF)n时d| fy <F~藎P
+~滻˛˛g? |_6_]___|r(6mɚ1t`M;02W>/jo~7?}Ճ6oЛjncs /ᰛ۫G_ :~8}wy{˫p L[/H<=`c|
+p'L#D2<4Mfi"Pq'-ƺB#q&d LL}DMj`ByDǒ<"79
+
+fTIE,n ȶɡAY6ʓ
+LK
+H bQ
+^EICD0ٍ
+F% Lv lD6CcР"A1,pWB̈́4 `EHlQfAs<4*3&@UrQ#܄aУ-T9򏃎ދőXMG` 5 ^{KO1Bܠ>".A_Rbk
+ΆGE
+/|8q@ l45QwBT& 48S I E5_p6dF@p!Ĉ
+,>m$ OF  pVP ڡ-PmhF!c0Xd6lxbVX
+iRY!
+|nUiVMVm(X ELm(mu2rHLBVq+헀HCUhRGP4DXXӄ>TJxt%a5Rx T¹S#':nT[Bd(A:BJ ԥ-20@SԩNyt!}cSCERuh
+j LF60#%O!KE40
+J%1JP
+>]sL
+D1J&$ _"R菂#RkQ
+!EyH*u
+aPGw޶Q(hH BA2RPbP2EEن
+B_'3s~3!JrL|-&*dJ ;H #6c 2Ɔ2 ml\˻Lx*B}K#ALO
+p4VJJ7&8|#hIiѡtFs
+otNΒotp f4߈T)F* P7:bp_o6KHoП}oDC1ohd4ΉM8z4@,pqFHaL"iN8:PT@Quq9k/YqY`\&<kڄ#>2PO$]@uqtfMȩw80gf6G&4QɬԔ\rt7!{HSqI9n2ȳi8 ("GjBi2r4S
+snեa68:qƈf~#Vqnx ՜qKmэ0_#%s(uUkm\,@79^q`(ò%u,bՌXFi'M9r/6=>`g[(u;bev
+M&3#IkbFcᭉG1$%hA@ I<
+ML%>2Q(J 3\%Qm$'Kd+9t%l[6Z K\lkk}f
+◌%d81%gi1%H%q$XmҢ%!92Ҵ%t$VD-P5-
+6iK&mi& KQWJk%aWѴ% -=V`JgW'GoSڲ }iڲ4#}F }iڲD4m4mY5mY^l^,A/M[-눗f-KK%ޥY&ܥY*N4kYǺ4iYB],.MZց.Y8,KJsMKrMs%%9&9ҜeoiβoIʲ4g4gYb[,-Y6-Y,K\KsMXK%IՒeҤeiiҲ$i4iYY,,MZ,IZ,IZ.Y,,ZPf-Hf-K K%YEssKҖmKҖs
+^_e
+6y]l+mBIr~j!y[ȵ'1'Ҥ0*T3 pbDɊhd3 |Hp<Hba$lI؂ m LzB!HYE?54Я)x[0nE}4)Lzor 3p׿]#:yaCJ" &!X3XL\'rj?oG$ ,AasLơާe6ElӤ
+a܊P1@nb7ܳAG\C'<9%X̄D<LjA,oh΢B>T-+Θ
+[4B pE >0V^>(_]k- Ԃt6Y$W""+CIaD3f3= RBlPJmP(|lPiaܧ+nB-'jkO
+0ckɰ *0\zETQi%B60DZIfO-<g3Oopecl)PA+m
+6qs|(DN[ӲFcF@qN\#
+8A@Z@=,V:8pA > 
+w|etյMlZWMoEx%ߊ| "f,DM#c̔ムo̔rf1fV(ݤ*vvT_{o"V
+kjJiH`j1JciY
+{6E4~͍,E@V֪Elߪ kaɵYvpS|Gߊgi~%I5ib ~m
+%ME1<57Q'R*+Ԏ,bWQ`Wqq/rS(*qf|.s8t|9>i,2;wsčuG{\z'9մm1覴dٮ,ey9̲]/&҈{@LOd,]/FF}T_z\6bF|L4:ϫ2Uf$4w^ xn-
++p=Dzp):[ispRgB\|&CvvpphyC/0l턋yR,o˯ LΧ=n*s
+Q{ >kPSYͶ#n[
+
+,m$5,ϓ׬uf0DFjQONE\=;q\tnF_rc5 HZq9D -ȉ—e6/aZt/(Dbܬ"!Ȭ=T@{ubOv$nԅ]m5zk rKXF=a'Mzgu \Nzz{+|$n^Sl
+Ipcqi{xkZ~J ֳ ή\߫ͬwzċ‘ liԣfCWT$֌xGs@ 4nd̊m:n()U qdlx
+ϯYS;5 `x X>nȲ}Usxhģ@<I7MTIHmPO~'tJ3x9? 7o:65tV/Vòoe5xx^۴nj\FCx%R)v:D
+"-{-HC!RX):DJ"bH^KCC!Ri9k44u;Dk"M}"'ƞɻ[X%#WZvBG=Б#C{m#WN`!ksv#F{m#ZEءS)t:tZ/:whuNCԡS)vo_5tkH^[H}ii)w;tZ'`cGBO.Q"1w,HNSؼg;Xk^'`cG0Ŏ;lzήC&סr} 5,CСr} #L{m#`tJ:bNCx#`kXrNCx#`SGbG.Ta F_O-GGqE7-[i7I{R=)tQ4u4{Oʝ'-G&$>“׭R? ל4XO˅L8g4iUGiB f;M3j7t"[uEk{^dǕoay {c5܇=:i r6ˈ_jk4=F:L:\w݋l5efǜ^|zYVu=V#SXgjkV4=쩹?:;ӋUb 5+_scA/ ܿg ?f*bWܿ),}ָ=7&
+S;AWc]xQ̓*\ɴΡTG3!~H<$vI^sǓnpWahzC&AuȲ+:"\2"CߡCu3zR\>GixCF)v+;Jbb@"yC!F-v3{Jbb@Gª6E]:>o%۪s\A+W=){O'-G\Kyc{]SksN1W_J/
+ſ<Q:#pHU<D-M35{<`4*R\mqąC/S^irHfQi20d0e{[o6jƢ0{@6
+_\jm=!8OolV|m[_PǞٵ<y dRyt͘zr~q 쳄~Ю`%*^-
+ܤ̔+⢏+<-__t ݃kghT>ڇz]uզ!Cܹ)!NXAj1j/;'>@ezܫ#^l o:LNUkNP ޯFe%˹,ͺY i/m;]`jxh/fMe6n_AA̹%f
+O<&vSnsm;X͙ڦg M'BSۡ<64{}V ._ n6N.O2cr"O5oSy:f䚆MG3~ł~>bZ5KztڟͶn퉍Gc|(t/!`a1dڦ -ܲOxmF-;JZvY^gP4qTp
+Yt=S89Y`e)Ə`]%aߎ衏ٵuȳ?lZrɰVPD[6Pl$ffZMAW
+vŵYbEmfe'Dq3}."/,:U/o>޼>W96
+sRڳǖ}
+UҘXasDG&k,9G66O۳}
+2}*n9}ZHYU(
+ZTC{fG 1Q\wt٧_+$96bWyZ֭
+NȺa! }Z]\YZQ:ɥX<qAfc3CbϮy~f
+FK
+^ϭsVt,Z?q;a3:WAKayIcz_ٟE%=s葵4 [{vK/:I kdS {I oK/)W~ /D޲y
+}eآW%1t{I.ZIOIgL|wBxm_C6槯f8[0|ݧ?8_|sײ}/K{c? ;XUnܤ0U2@<1؀-o`zSZ]78y hA4J"rP%qS#<nGzN:#QPͿR`|/8~9㣷o?]~{? Yb
+GN!'ȉ= = } =rb9C
+i/r&.%iZ#("-rH`}$XҌ3o얭$C)"GXH`-pU")E_w5jH
+B[UF]@FN\ŒW@ 5r\Fז W֍3r2y4Ox ș*p"YTO$ٱzD5@Aj`ԫ-UHvFFGUGenSq\\IZa)HVlv[j3?abhvtۃR_Kc
+IOK87gdM  ?|wW
+)Ƚ߻,<I_&|33AbH!
+ЧLSa,H
+Dd
+>nY4
+D+
+NҘ@,8*xwʬp3r "_ڲ5F54f|gxif%aakhN9Lxoa *Ef8#ve ~U+[g_7'.l?+d}Xvކl:TvcGeNacxH
+󬄻ҙBvX; =}-͘!f#6V)<쥑,4y`0E fqA`#ƞzesD
+
+YH$E%5xp
+45@I
+2
+6% Z*Q(<
+ rOU@Ţ|)^+k
+74UT gѤ(/"jp#,GaZx~LBwhmYD
+'tԓ9eXA*󢚠])</&~*"jX1psjΓRq$aǃ
+ABH0;)9iz7j,}>BPlݙџiM~OyBZ!֒6KFlL,3&4H AΩ$$,~Ty-]`ɩ$?ZnA)()V|ȖpRqL23rx$Rب"e2^xJ<
+\T/fkhd1JsfCv |p,)p$Q!Ҕ=. jX5줂j1ɫƮQ+T$;_M\ȑ &v'^
+k uh(stvDy"tg Ohi J:. D
+RR^<h7txF,$Rd;S-ńwV>Mqo*0<HT!kRW A c Ƌ
+
+s/2FSb"ҴY`? (06)wDz}:H' fUy
+ɩKJWpɟA Msiàq2%P?Y`<7F1w { (iN2eZP%,sF8vRUOr|Ip)] :%5Ʊ ;MQw&-EPޕűQ"qVs?&H]5F1s}ɒ֩U֩,2*nд0;`)X%PVT3UFTX(yմS&_faJ00
+K
+,I#pq
+,@@$Af2d&{W;>#VR8
+؉-W*MfLuYP){1CoMm V߰+XcH<"C,qHR@F
+z ղ+Xs}8|{q8 KhlrtP)J]5iHAPj-T DVl.S-'F1!8+H 4ic@0v;nhZpY۳A7tѱ
+Zc
+QLB(
+o9=;$y\?/(󒴨лb|6wnt'sž|bu0㯤w
+YX% b5*\`0/tTd\ԣ$o7"
+$*B;(q-Bn޻\ QJ,",&c!`ֈ,01-B3UrK|,d1 Y bƀIe`hS/)\RY7j#x
+K&]0Bs'Pߺz"S ^{nXӃW=~%e#J%AT5^lZڴF{ >ElZ=FAEAC!dޢۆ*4Ch(%1/xW&F@Ћ3co1越Yu1d OtdS1hc"i~28yAkY (ڍL).S~o~(I7{%̫m%`Q7_yߓ-Wޘd~;slwlˣ٬D:3&?M Ql<WoĒ'\/)DWtXqͽ'-[)lA(h}AsPQ5,i ڌUVRz<rqϒz_q pzU%.z*|c`8z_ᬙϦǎzW\v}=~ R ^#&O2U Ac
+m=,(7Xd)`"ESY^'7NklLBnL5bf%4e3n B쭛iўg gw|
+3!t_8x\Fp&Eqf g™(.Z/ Dot gQ|p&*^^8Bv_XQ, )Cf Ԕe^d0 z XcA%+GÖ1
+"^Znh9FT?FZPfG0Sc6.Fl\кlqJNb oQcߘ[/1Ko̾mzK*48#td^:f Y'
+NtY'b(.diR/>$ݐ ѢCbrX5 |H~U2z 2:EUF]((h|v'>6O=[ҷcŢq
+k=;m
+ZJ'xKHuo_Ġͧ翾Rdԍ! ju6%|FB0/*}F5&ԛ_ :+D$tWZR !~czfYCȼ'=E{bSĜT+ǮD㰙)k%[bYP['~TaTC؃+rΊQ],pVDf/{EtVoVSQ~in,rW1XʆBsQ>_6F[E|U\ÆцFCqxFIU6D}mvᣬ#nBbI"WSZ壣{piߜ ۑy;־=?=>V>WO$f#?cNFn ⣺gpFiTonlG73EvhPzIYszG[(ch78O
+Rc5>k}ԣFk]*=Fi3E>/;т+Ʉ
+O;8f=1]֣8?gTܪs1uIu3ږ UH<<wŋZ*`IH;*a,s@d5^M-Ng2Zah>c%s.9h/V?!;}
+\d=b WpѕOBa[
+ n SR(:
+ϘyKGFׁyʽ=ʼ`s $(VƐt2/*9ZoQ^ASQ|;D(x\uwb8Ab5yoZeY^(*!vdp-r,ش%tLSY)6o \`+q*㢼&'v"Kd }Q]P[RB"HQ6Q/7gݓ
+
+b
+"] 1x5QJ[q
+]#
+
+٠#;FkhWy`ϞT0il+(;R1Q`У
+YȢ@(EQR;A`}, da kC $V8"%K#ȡT-;׿<CJ_WkTx]sys;s%2U?deAa$ƺ$W?lUW'*.^psU\dw[%xbU~OG*J(DpWSEQ0cQ&Re*.hAO-IrYHKg'Wc'%.)mz1tSFܜ&(5]Ajߤ8QpF
+]V5Mtū,u\n]2@ꂀyM>+pkJʏ9 $)+) r ͩ* oD@S)D8ʚ;d@*2l5$QT"[3&H몽?Bx d JЄVv!+ʦ " Ds+5(۪"n^t*pHjQᏺF(H@URpokxE䨊 ĭ
+&C
+0$^e ~PuNU^Ē/+:AqVQQG,ep*jT
+ϋ"TJWxM (h<ϐPZr<<@&2:
+9x*XOJ
+OhQ6]HC(X`9n- l@NF fUmfRdCt`d4z+]|(г_A Hb"ǀ̓9' R&0dFyND
+rIK8a`xrB M"$E[ԈyuAm҆c:"A-\vSHnUۥø#(J,?q12@A96NFG1h1e`B9:+:ȍߩQ4P=C̃Gg>~ d @Oh2)M-5-%*`<zxe9L1Z51MZ1)E4b j O?ɚ9*kJY"s kEύJv/4L^ǥƵXVi ],.pz]E%mr!u oۉ
+*P)N&
+VomdhoTYpOC/˓9"p
+nTCgpBRIH/B;$
+$Tq9e Nl@
+hsE5͉xE3:3 b> }A$XKExݕ!XO@ ^
+ &uT\0Lyt7. l ƅ4Za)" :iYx&R)b55Ѡ =!4҄(*:e"<HGiC"4@1( bdF8eg$vuhoʣ1Ɉ!/mK.2Ms tU7)T?
+LHxh9
+Ku1 #-<T& #M8Dm#CMъ[^56|H0H4%VGUȈk&p")IY4Y<2,߀
+,3fEG˯Qڊm?ɵ5S`Ȱ1+^TJF :q" Hl "R
+D28$&_7$V[*m+$0[0
+c :uvDxU!3$5s`{AXqdHwvpGhTP"'n4MY -LmFaz!/0H9)Ӵ]2l6ZGL(0* tq>Qx'cPt`hHGQ S~G>NP&"a$ .P<T 1aJB{!IԺP>AS7
+;B>N8\<$s՘a洡
+ICE=.0"Ow~7 *4XW*q*+6Q dBSiOP^%] X2eäA~F*D
+4T)GN F PNѫ%~]`LGǁGixq
+O[h52*8΄= #qS}\ahP0_hLЬ@M59Nl>f21y}0-d.&* -ލ~y@mO{)i|@f4HD$ݒsVP
+Ō0D'@tlseAq7`X'smF5@qu "7槈 Ԑ25> ="=шQ&` BwI'3D@gCY"Dn&]sdH
+)G)I6bSovEH. ;FhX'!G}7(:g0ʑ ^
+]Y+LHd2N1 *EDxF2g$c0%A]•D\C7OLB&e$`B9\2O!+$N
+bp"o0o8AJ<OpD2I
+_T**ۡCV]2YXy%$wc9}1T*\=y\$*pFzC&'McdTMb.n!Ih8TAD!ILfܱtľec'tIPZ+$$ F, /0
+BL$ּHBP$:H\dTp9&0{ 3pFA$v3\ͅփ'YǠI;dqbh
+^$YH+a+bK\Y[QuEJC{52
+g"IRױ}ÅNgpa2FI؅S5߮KK" Sq't``.p/^ `(ux**qD2YNp9D(+.b
+ ]}"S^'C?O(s]ARIնwoY.$~_i? +ͫ3SIM.Ϲ?dAxƃ\("#x v]A0҇xFE 44d\9WȼWstI Նg@2{_7A6kޢhѼy]{Uq7Y\㮼kif͚5'hyͥuͥM
+_عy;/w0.zҗ6g*0v8y_}uqy6rn. vwΐ'
+羘1?9|~y/?;u^k IVۺЯg7>nt@U0{_CΠ.3wϷ;{8éE?/vpo :%p3J)FXÞ[Lnwv7nqíj_];(o߾m۶m7m X:;NGL}/'Ϝcs:Kkl;wpBum oЗͱ oڱm57nGэd-]>`{kKμhH&
+ނ|rgnΰAz&vk* V-[:k({}SYS`݇7.mxe <8ixHͣzr3.BW\
+0FC1s Usc2$>,:6
+0c*tYYx&
+=YC}M5,'_ZLOx
+nh:٢سwn;7ES8hTa1+owOJnoa0|yk۠a#|̾ w\հy\vS%)ηi/*%&2S5 ĥ- ]v:f1|/3Ǔ5-`̣ wz. f×wM<lIK3MZk :X u>4'gDfpxW61Aoiz ]eegMO>QڷdxȰ쬴⇊Up}R FfV"<٭0,* ggv1_"<-)* /Iʆ-`2uƑ.gXFnMO`s\vNїCowʰ ?;<'+WB|ƗWUܡ}:wfi cԜdf!G@* c^"#'/?/'fe 2 Yj 4m]a!fnA3tPwpXC{G K!‚P×YB߈촞ʭP#b򀬼‘*c8< dxм#Gzs{1B w847jdac8<01<fa&[ Gxa f{G}>zpX"_^&c8\\bp]Fe`_\42//c8Lؙ
+pQI(p śy ppݠI[1|p cФmnIe G ײM\S3Y)oj åԖ1Iڛ3#Ksú\f0\\Zg `NAʰ3 ZpcNpIS;c8@ d (kf1>Ñc8` GH1i0# p4Ñc8` GH1i0# p4Ñc8` GH1i0# p4ÑpGp4Ñc8` GHHa GH1i0# pAÑc8` GHbXd G&Úh1\ '0#<XTÑ0/ȰPp]|%pdb;vaܱs'u й3~՝1 4iцS;u钀 \e %\;u5Aqڌ1\ڲرKbN*c8"U7)'$&%vV;0#^:^-6ppI[;h]ws5g =Y;&v=Q0#]HHѳ{bGpD;N={HJ4oB:DJ39Ac䆶rId GMnhtѧOopФ%0ܳO;w0#&-T{Mӽt2+%opdФe{-wJ=m@V0;<|\ڪܧ~v7
+׻{3#Koؽ$w2#Kotw2pЀ>ڝ- L馷ou)2ءSiu
+%#[z
+
+<Cuo _n0\L
+۶V {F1 BC\{c>d؎-(y#F9=a+*r,OoDAvZ1nӁU xGCg akFCz1[!f 6QY~ނYvƍV
+QU dNf>j
+9dޝ;l0]xi=edྑ9k w6xpZmZ2p{{ԔzpP3-RJfajd 2wPZɌ0 İw`Z>.;Z ߉ 蓤c 3g
+&åO \maOؿ0|=c8 :ß1<1\` G`ec8l\a1Ñc8` GH1i0# p4.pWp` GH1i0# p4Ñc8` GE`1)0# p4Ñc8Ҹl#\0# p4ÑaÑp0p$ Q;1q;s8o3|'cw{axt ^ ] jfk; Õ0`xDv5u
+}s?0q⽕nÝz <dH^^a:'=QY!BACf܁}.,%CSO}a=7q]eegMOqU>8gN4Wk|rٙgBM]J6k'?P6g2,$ MʾU{wSΟcW1x;;<oW[y\ݴY-P'hѼuWGʹoruF [l&׭3s_\4)DRlH(okH]s(k٪u;^V :uFt+:uJ訫2ucK-8,:kbbReT>_^b٢3& 5%|\qsp_R a%4fV7+izMHԹKR2d K1<<O6,kHFڀ>t/4eOg@̬Pt,'/=n̹^Y[oZG Qozg[^ 12ڌj'j%]Ik; 5=##3sH %W8xtŘq &k|Ą s壋|޼@Сڬ
+z
+|Ecaѡ?ć'O{/.֭{W=9(M ݦ A,}߄1ŞAnYKAA[ueTOЊmI0;p'//;bTQj;v'>2ef=SO͞=t]
+lKTCT >裍7~ gm/y`Jힽ?p;u|ԁ_d{͛fفS:|O?9C U KWZj/
+i{`b5vb Ο wiIn d ,w8 H_` G
+o:$UtWJ=e]AĻ:$]m{xW?O?߮ |^d=!W<[`I[Qwq.9~_ƺx54s8? N1^nIMn%s J~B,*]*u^]E U$Lseŭ ޭȂU-k<d7/+n˂[/'3xxGn'Mtz/\(Te 47%wگ݂"+.,C!t7벫z-H$?
+unY4nN%ey@/
+,o%7'p2qF/a V(FoE[ST(2,IH/x%ޣr!x /ܒFkD*uM%Ttݺ[eȎ*I.Ip@^P]<ѼUssbgCa$.- K{]x|&$eyh ZKƁjDq+p&B+nl:yh1YD:u/R^&r>Pqh:zIyۦ{2O[\RM:Rv+IeDP.QDlX#?i<ޯc˪M2-$ H8 A܊EÅD}!)A/P@5T5+*PD{UWQjGՀ4蹲tsD(ɜ[I_5(
+UU7)Ԥw듔V)ݰ5^y`,]%.j}!W\hӂ<o.y޲1ނܾr&O,.)vIHc%G 693C\I"U%ڙ@)`
+Uhn@= ,y^Q1/Uhh1nj9fB+,v8U*t-3+g+E/lÐUϤ-3k-
+
+bEMӿM 4N9W6t<Epz$6ՌYB5/Ppj9VKtZ7[U_a̢$ěE]9efM"t|ޠOin3=K%ti Z/)*ONֽVX:efM$tl*Mft{ 7~%I4E
+MU?ֻY%tgºӪtK`Gռm,}12K fx`{LW~-d-UG
+hٟ͚8Q[Go6$
+OUKȨTŞY>!0B끶bY"fc[/Y
+gfY4l lƪRՇtfQ%360ӵ3g=XY"0KdzYs6aVl¬"-MX5^2[e0`z^e{fQfzpt2KdSf̚Y5nئ*MU|K5O%6iչ!I!ӛq2fLLwɤ~i^ifcpSmś-eVg[XwpgAUN-j<%Քh[1
+/..(j
+;dU}]_UΨ]2O<ye-r>cz˸&EρIk{PAvWPYQI,CZ}m},G4gSS`gBChiv{ӓEI0:J[͠AwMz:6ߺA5>["͠m`jCF8QoNͪp{;p!|Ј!'7mrDwwA[f͘K:xIfBf+_3憛esgtG}ɧvꝇc=h9SO?N>ྲྀF:<ISgv͘6C~@9O#'Õn"sþɞMen7~LDiܨ{rT]Gs;1Kg͹믹򒳧useÁ> .sos̳N8rNq3g^9o4KΚrW&sx#N82g5ͷ6!A&1uɬkLDnkQS622L~sxѧ;<c>892Qamxt7 >м[?«o=̜ќyE6ab>4̍}<be$s#gȑ'_<9 GٮM4skod^tґ2s]ǝ׿k//Y8%ۥ5v ]y,}7_ynx؝7Nut^^y㭷x9#s洱;u< 6Nc^|ü^$̹=esQH[rKn\2ͳ_9/>ǜ?ů'|W?쉃*C&_G^x냏?לQb g\},[ɪU\Sٜ2{T/dn59~uهpGkw{}ε ~Lgç\~ok^W6>_I9y˯׮z-Oҷ;>^t//_fuk׬Z[gN.gI+4wo*~.|2 ![/os.}ѫd2Isz/ۯ?}wɼ+PR>G^]a˦ +a6yK]1yzUSO"$8bڕ,L0[#e6a>ks>˼섡mGq7?]+;W|O:`
+lsOj2WU'؝́'_+||\f7fs=?Jod'9S%YK̲/4O^STRgRc|k^{JsۃO7$ٓoXfd>08ו<jZ߭E7~%ꭅ7Vl}MO go?Q.0o3I&2ϺAfr62!2TL}]p +ҟݧn>st[I:.Ō<yܮгoyzm=$39fO2Ϲ72-ѽK?
+r?kLi ;vcϽW21{22{#|go9{L{IGo,,z2,aWN_8W?{9W`gJ vK>ٝٓz-QfcYR]SM
+ Aެ- 34&}EÖll
+<ThJ,[ߤ)}`3ݘ__=ˤjyףpٓzcGԘ2^_evLzיf\A-ѯձ Uӹ@7,t&R>+219^Dz3g)R,˞<*/ps\[N=Tu2{;kse>eb,_ohrؠ4hi.id>z]驻7pڨ*hȤ֙v[|ל|`I|W`~9rG>2gF/ޣ{-2sMDo`W[uYG[~<qdtŴ2g\˖/7݋2Sd:'T{AQ\K?G<uk>~{W:,]o<u_x]rߑE 'ç^&z]ן/9`+o׭ͧܽ2KY"JvӯX嚯VѻK5US/?5k\cְhU.]kj dIe2iGI抷{`yڻOwf?b>\هMOeLŽieޠ27xw?Xr{o螫s
+;,_b?]\| Ͼ{oUfl:+W,%~ũGZ8{Aw:Ȳ5zγwҷTcdnDbW<r ^.~񥗖>#w_7sZMm(1,|K_XăwιhalٝΟsO=Y&y8 'hd>ۮ:Cv,"qa /ɇ悮;2{+s]NQqǏ.cna 2?ff;|߮xw|ŧ=rMK*0'ngw7κiM;֍w SvN~w//#s_'^8;ϻcO0w2#qEWd7R <,/?w}9ؓϝy٬}_/<sQ{By'3oWΞ=K?}JA27+If灿f#N;g͞}e3|qG a2+ÜuF)iOc&LzaߝFu,<y7~Jv>casgL05|ร'L;v1vno 9iӧOr1G:lWo2I9xŸzI'a߮3l!9eډӧPCRD ?u'Nrܟ&ҜK a#Ua;[I6;=(0G;hDlږaSt}oI31%#G>hp~y,3_Ws?aޝ2qK-[8mʾ__~d6r~f}7MdoC~&cdnla&קuos`3ۃVz=ɫT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJURT*UJ?*j͠mv6v_5elUf|/sf,s#2x^7jD;={/"5vcȈ=zmnP*b͜?r!csRpsw=y}h=0j-
+7ܾwW~aںvM8yOއdpޟʘ[ <ϼG>d>{g99~>5Ly w' Ze<5oٝz5wϘOϿew'uTNfS^sVۀg__~nݺ>_wK*p']u>]v7'u'gŷ.|egk~󍕹 ~Kn_}y{Lbޖcwf2\vݺWΜ<o s.2׬Z.}/2oxҾ9SW}XRr_h0?{WN>;q)W[l߂́#]>[]N2ĜܲϾΖsi#zb~黋|ʰ2i; rU.wE݉|gMf6欛|{:.w_}sfMIz-O2|Cל|@ sO桗WVe~L栃fyd=Q/=xIe21W6N:q$Daԇ] o8mԠ
+lwǒ3]jsۃO7>]Lj T߉<Tf#Ǜl`~k^wJƻz'sUA"sϿ~;Ot!ۖTv_p+^4Kmϼw>eR*APdTfn[ V*čwr1I "w=/ZeO|趒
+t]tϋ<yә0ͷ2ۨ{~9<ޗFf#sSi]O|錿ޥj18y/eo\a칷=_&1=\dRJ&{RT-nzΘ
+t9fkۙnur+ ~dK5wL_Ϊ5/CU`[zx~ݘOfi62P*pe6}_LfIև̞^ʊ?Ef.e60DVmdM_?DO[C6{C#dO٧QVF}Z?|:FEfo/Kٺ1t6ݒn[3_fs~ռ mЯȇ2L\ÜA;._5ܮ̘$tMsssfE/.ѣLhY|uLO֘"s'715ZcĜn7?Bfr62q̘_Le_o_}#M c1].7TnܶAd2_25Ok[Ĥ?Zϖ=7KpBV XfIl9fO2 ?Mfy98#4236fy埯5VwtQ.]F7_uZTڿؔ}Ozңç^~b\Gw_ۃLüO_ sĴ+dgůKdk)|5?c)Y2rO_^ŧ+\Be }9›]Ge23s͚/{ieFřU?@?t2WycKLKgc.{3|cw:H^l}9y?w^]mWZG6뮅/ފFeҪUw/|eX'yUw/z2`%\=2(| e2޳rҳ?|e3[m K_\ăw\}wܼm܌4g]xm\?yГ-}en .Q&3z)?^';c^t?<~-W3 #tڥrς^q޴#^R'#xڥsn{}Ϳ/;w}2f=jSλr
+kRa#vR-ݝw#G oݶo߬?.?|ĈCks wsȰ#O:{e2s 9t[pv}o\KmٴOЯo{melF&9`=D9j&>H-O?T1KXT=ӯnu줱-~;;i1LuBm҄'=u{rw>uۚg1nI[g#I /?f'L<j4rZV3>\i.4ǵN>qZ7mesxK$7.9w5o6Ϳ\?mw?m5뤭ӞF^//i}
+v+`G0jO({3G~dDn{n^9Lr3HrncR:qҚ/b,7{Yn̙n{eMN48uO<zbkJ֝JYӸ"lo~2:^Y9ݠ=sq{QAaT +>g石w+kY({B;ي*)QmN°s5d0nWa䴻iBb݉n-4H-Z na-+L波*>{H+
+{uCŻ~*>sz{{^&^RP
+]gl^F|:ACg։ 8o4mxi1 }q'!J|PZaYI:_)Հ!*{5i3Tk"2m"5uzPׁ釽Qx;dH34_9Tj"VV|]gutf5w!^=򕝃.\P<'__cӠ=LSNMo'C򵝡ΡRk;+uEVz<: ^
+`[ᦵk4)?.@(ZC&LTȰ
+kMUB^\qUmVղs5Ap-UIVa0/Ը}>ցE
+ݭ{k;C>To*S6_٪ Zf'*u&jVP
+ڧm *ؖPln)o:؃(jۛI_(IP_Չ5͙ װE UlQ[V%[b#{ }\!O3i2Bgԃ*RQWU\+*Vhւ>UcoW[|GQ>
+@Nԅ*@yz9vT1Uj*U5W Ճ UUjA}j+Z>_*}Xn|v^΅:͡ZJTUTsڬ@ZT +ՂV֌}Rm1j6q*=Cڽz!5C^UmԊU2"Zj>Ui3 Wom-RjؖPln)qzk^ې5ĐocwܺIGN:f8чC&!;5k ͙݆4;v`CC:;]ƍ0QZ+x&3?[֝\d?Nn1 75^j0ʝ?4_I"G8q:倝nHC0i=5(=F Fadp[趧iZr ARdZEۍnLGK0 ]*; Ȥi irLh`N6Ej&c0)6HQJa520
+MCq.8C-^/V4E8[{,r@Bm.ORʐ*4b3޳ 5O
+d7@/> #s'XS1B "uf9IXd郅IFЄ*j;lgm6˧<+ :Wqb kaKӕ/u;%u9(r-e "I]re VR C9ѮRLEGK\ec _MԙET +QW2̹8kz>KVsQ((h
+V\VE e<x<ae
+Ji|
+d@s3jOz=b@k 0GEsdZR֕aveFrRRB:bVkLjf^Dp3UR@U0 [/0b\1!E8E8<P79m$γH 4_hȗ̈ErQ*`
+njY @r#t /*kʗaDԪ4ED{˜ga! `t\L1XJ0%M,V/1/Y31Y萩QxԒ ;~z$לiQwFeK0f
+ R2O(H!(` [ Su`z$2.Fu Y\7 |g<*D
+ +U)N`e-2T
+yXѓ)dH7u(`5-IÆ5ee H
+OQsHa󢩺Yd2? d͋c[(!#U3S ˡFǑ,,ފ
+w-ߍiE[!!g,iH%1`{0-\Cu|SܞHȆ@%]k+N-rFK`#\Ix0 /)3Y7?plim.U_`  Z~2UO"Jhm:ƹv
+q͓O_ųrLG/ Z
+qc9bz3_neSjWZ`fqeI\߅2qWtH{m` #Xe\ @2cZjp+ElKpJa9(h܌CP7`+S֒aMGqJNDlcKXکC4,帼H֭$esԘeAx,? / 3AcO#v!2A c#VD4c:7@bo3}d,i2r M^x-Y3u LF
+ك.gdKBZx$ǣ*C9Eż` Kck`NS8<\v"Ds50*S 7.T;ǟ3ʂ>Dv.!#$h}sEӏY\8\+G:8fWqa3F~S# #aѥ^א'q4kc:Xae>
+tPILw ǃ%!
+ |2r\lfX|}#߽,pIvdH-̋tQ+,J4T=cZDbq?"8VD]YO4Mgic`I棐 @d:<VwpΦf
+g$̄x@$_p,I3|4/,ҕr|~YNB/h.3%8(À^{j2rQh$F
+/SzfHJ[@'8 -4VKI3OKQ4(ܷ_?Zy%i9 3PDe$j6"5d43 ")D7AkL8OwO5zOמ3zM_}NDa" hl:uiY3h\aBmnľ
+]`/eG͍5E C5m12ʉcD1 H$I9phmRsT|q(E4F~F0HHP!
+Թs8d.y]~Q|%FWR %* k!$,c.4]>ԳNr#RdZW
+$bC,,pir 9% "Ѽ4,rAЉvûZ,Fr
+zfR rF,^cb>! j:wiY2a({z^
+hNrnf",w
+<sjʕΡ7qy[h "\p .6\Zah3X )| ߦǮ\A!,>0IaJ̐Xa&cfa&ǥA/G ?+R#EIZc#ORp%E8?l欔4mN@uBJ䒂NʁoZ|/T,rsE轅4IH>D
+(burZk$A: "~[a"
+]Z+4o"%- $%Uԟbڸ }
+l3*;5]5% ƈ6?ְV1Po$ s6Q(Õa(ve0a+bs<B÷_cn!cY˃}N=0I|BJ=CQN^^^)ȣ&e͜K|xD<rao,tx
+H<+`&5X6{*xHψ
+@{H;d J
+2E~LQ
+,gH0Z"S`Eaؽ{qةH'y! 4:^]#pϴJֳͬv|(Po[z|3'`8 '
+pH%YY.v0bq`/pvlk^E1xl,NľΓ (`
+Łn8ތ){
+t
+ӍYYƧ?U~X䤲jh.ZQ9b*@ADxE~l!iKѬuj4y1'PdNoET%\JU(cP5FeO
+[8f{s{ri$pF#:/`L 8!dIa3X<0!'_L c2,"T,Z<K  *tߌ""'Fw'p jfbKB$"{ n(iTv
+a\l@g/$wW3'ŷ d$ԥ3z0uf0-E ysbB
+@ce*L$$Y~,л#[G'E\e)\CmeH$_رyٷ״Q7?\J
+18l5vNlhw,d<@(WCk9\XGۗ8tMѿ6XiĊBC0hs"Hd:v sݣij/D--\ d~C۟-4qYfj\l-y;X s I ~<y9Bmz$]k.Ԑ6g$Z㥦M:XhpKh!@:pe,x0.3
+F==YY} Br,TKu˱X=VZжHBZ!C"Y@#AߙJ0nh9L/kȮgeA8&BHN+izh FR) NĖAEp&w%'ʗ
+Q%ho!1$B!ˡʄՂ%?$(F@dRz0:G[,S"V@@8P/NYlmu wc8QC9d!c@;6"C~x\![àbŒ-qLoZrДhsvN}BAQp
+/6si3!" x+B4] ƫYC|?'gI%\A0 2(DQ3x^t_֖m`eafЙAm|‡_+"Թ)5#>: u ]O;?9r@
+x}9x65,'q# d+ccdL|s@5<1JF 8 2xߤTKf!Q"I!.W0rB(]#["$dEr#y9ry#k!ˡYLWT~~:\4Aᓄc5K1.qThQvQ9-b&S0É!ZO%M6 mf8|Үb9t@L`9u6hCHawZ'u
+^d$
+Y(҈*dlra<|96E,"e
+ZR,`׃
+=
+8 0GQ[R "8&ź.<J:-àqN9#BThڦzHhˢ$bXY<zM/VO jGez64}LcT)hU3[rf$m1y+7_nu~Ȏ%:!}=̾OdWB
+ X+5T:Q^pLCET}䩫+$Be5GTb+u6 7{) 6i Pg
+Cc)
+e<M"c)n<d:Xa/3c焳P46,GTW@+͉g.6E]v@DfZ-"u)B(#+d9V ,Q㢲+:612]
+d9miNZK];5 sz
+K4yjG{1B_,5i&j-vg]CTk| ͠P9I*)quLϢ L7fry^g
+Xӏb=.FS/#YƍC^5O;dG> pF+m9O\RD3.5Ej48GH +PAr=5
+d d(jgIs1bVZm0|!=xP1)0,╅m`3]rPc/|@C d$mڔ5$ \"Kd{Q-Pgyz,R׌ UY!iC<(dLo(d1hJ:O#a)donpζ77`sS:-B4;Z(XӸKJAHP ҂*xAW##)
+Q
+Q2aQ
+5KenE&ώrr퍗K8բQq 
+kA[L"YafJ(eSb ,g(>D7&@Ci^a5 'K]
+IX8`v:Qd]cѡ5b]_ⱺЩ[b^׏$r8'-DK=Z }w\BZUZ&Iu1E<(/p*Vhk g$d/z8׵2]\Z8Ý"lmVD3cKe$x&,72x֏͡(G (IO*Y,l)6 :-Oz@.8cPfgQ[ErO6XӋy 2gqI#Ȓ :z^&l䏑d -Q z]'ˌFV?l(bthH +49|.yS;t,GY&=tÉƼa\e`ND[p7t䚊Xc
+UcsZ@Z 'A" ^[7*oD0S"rku+"Ƕ)!!2ld5C98\h,<޵y:H#5 TDpqP)_Zi#Bayk[! D f Y,<1by"y7ZV쉷&Y,
+3$F('
+'`i#vE:da4  8r+OA1IGMOÍ}~@A%s&9&F Oq=cN!oQ6&?ta-vqt G9M(@q0B[ChkǹMb NXFyp0C3W
+Hz |PfƐ04 3 mlzȟc$s;,Q6`iI1bH k8wFh
+ ' "c?\( i
+Y,<| QQ:#*(}fwGdC2,`2yH“^lFA|Cdp" `
+u鱥!3ψDS^( e Qv%L_&z9@`%klXy-P :%nhjeX`=o!]>zK13g80Cy, Q)11Cñ֓8yz]gsG`wj "4{
+两I*T/DX
+~-8
+ X
+
++Bԅ❅@MRVX}^ng/GҖT$:qx,5p,UQL5"jiVL93J8^mJ
+{N5}&1*\C[TӦx͓pK@bz8M6%E0k> /
+Kj.WJW$Q:y8R,,{02c4іsuDc> [Zd@wkEIYգpkX΃=֗:knG\b1Iʇ0 aKʇ#Dl)Lr* k0{Â[dbJd9Py56BEpoˮwDoEldecDtNjA~.iX
+o8E.XvcʔXnGauGK3/48͡5vI܌"u7$Y)nF:ƘwsXB*b9#t0>ԮBX
+jQw Jk(;H)Ov;qGT|<uӖ ||5t>-28Apl@NSGrEA.oQK% _$|;,P|ztb;0Ӽ\2 E"
+Vc*
+F,@ 17e/@<# +mrBˇżЦk^(cY{W-7l3!-XN
+?z))=IJ.#ʀ'|;}+B1p l}ҘԝKuE)plT?q8AsJ\[@3vCaM2=xQw˨8XC̈pG=NX
+m)^h-2( @p*Q,@">(b<egl Y#hKOXB J  0hGp$rLBN?7ݨbx  2ޠgV0R+
+m4S+%!V?qfZ @MEC+0:aέKB~e2t)J1P@ chIe8~F (~OPBq"J+0H)t*nDq!.%(۫o"x7>8 o%H4=_W fګ0$ Pl<R`g, 3 hўuT=Fˮk-~3,h-
+}jT1 p_Lfuư}m
+gdJ-c,@?evGCxFKOzJ`` 14-:`(bNd,s%-Pf d.O`qd81 `60hqҸ^ ЯK3F@7`h4ړ́@f=K :q4)P6)XqP cܞ. YYhހva]S*,,йxu|FV[Px6fц/VGF
+.f /Бi?N4[5tm?[b \e y jIN/1 "Y}kEJܞ3JB2sѼIN
+| /5=ٔ`r !_Por fa'^x=8_ş]
+טQElI::"6s^?Ix uO_7tL}F h`IM!G0"bv2c;[gVĆGL
+4"h
+"2dn0
+lj#bǚ\b Ʉ-߹tGZlkb{.R<.GVgݛn j,u]kkwS ? O!s(K> ߟC?'`[LnLѧG!Sz Yb*d/9H" 앭9;u-j.=P$wt0ɬ*~U}Mjwћhhџd
+-_'pi6ހ7M-ކ4m`JZGFreYI6sЧF}Q$u -B@pBb`9x4S* ExdO_4Οh?}FY~O[ikik?m&M?>ZJ,'SO>ei?>gT="0l[׶[5Qsw_]Z34l{6?Ra> oS>W>||*$ ¯Hөx.4?翳PJ(TE jm!xW+;#09[0)r]<OR(AQ<-P Š-HNa ?SPJ8K~ft6k*<eADT_UO]vo6E$py'6`6[De-P~W[UZnh*J5uɋ28y1,'4
+?Z+&
+{*bXd8Cr 5P5i!q._dIcmV3+4,[\~ ֖XimIf~|f`fO g[SSC^YBy-Jp<gVUgKNrV,Dht'p?y:ZqRm8-qCK$M(ɀہK(8%vhlޱ͢~ <-!nFC͆XC ^4A$
+H!;fDA*шLF[)F1xP/!K1 !$`
+ M=⌆x@l`b,
+-" B^w1h Pp  E
+< V_uq#yZ$^,H))>6tgXU"OI8fEMPnw,zT,f1uxCun` 3 T8ضq74L@ִph"mZ-^h@) DX"`e%!(bZ4K Ϙd(4;V
+fw 7],r,;JӋjJ#m'K#8hCޮuޕހ],Ue@} 6.@׌p ڊ
+ 9lྨ-&,6Vނ,`0^~_M)TODu8&89?t- Ņ"hZw૲Ⱥ/"ЌcAWVo2}4%P*(^E*%mMhA'7U$+2Hw1,TXkN@1e/C
+ ;ѣ'Pe2b(`)$A|7)^!,L]eYO]pX[0o5P&ӅM^ھ("0< c$l+M t"a_I3IR$ cR6X1d_E#<ӆcAYJuc(pÑ
+1WeNmoزsfgg;AU봫N`3ω4
+ͥDylKʙRM4Y _]$iםm@{ـ#U\Pl6j{a#@?mr={+T[jIGtL,6,ꩆ^"'<aCCƏSW
+8zKc>
+ik*i v\w1E)Q$ì VѴVzbG+Qq+~G&]5 co?S<D"mDht.i&/iL+[U^l il kFj9_-?cTչ4樫Û ~ڕ& y,ֶ'kfiKӼ G[-"7%kw:!@KM鮖x!rS?&~BH6yd_:q-b_քD=p3=ufL>0)xǔ1 `踺ʬ:J16 `Bc?AJ5~Et‡rTDV:s5ٯB )SXq/ԕ,] 4XL]SfڰQU/RI|C"EGëC?'
+9m|m8NMyR@CUԞ4iąed,VHL @&0&,vhj21~x+ftaN5a2N\:y=NؼԓSd[o(W>X`lBb5
+mE vVY,2i8Kb.iBUnNZD<,{!Q$sU{siua r-pr?mڣ ^ϔ!as(CܰV2WF )3gXbE+'+I=7nYƼꌔިe"~ 27;,Ol[Ug.%p6ekPq^$8m<1&_DrR{Z<~rU%ͮe8_mֽ
+
+je*5@sY洓~x:'쀸l|!ʄZᢏɳq1]lе{ +>;xIrrG̈#7[./2O'reK=^OS͐sp#W'.z73bcg)//ds+}z=i )ܠ2˜I""rU.T~%c#ڏԬX>,tJ8J-6 MJ&"ո7>#ҙ t9|F}
+Q|D&f.0wBsjH}\HvBj6"pjĠ߁DLuɕ1x&{QT H$rA*{>tga^-X
+ͷi9U{JW@n?OoBz;붹e%~.>@/Z0v
+'d1\]N\e˟u:DÐ{Lnzp^eW0 :]$ <%&r1e2}-
+5VKdLì5QlרE bG1ˉE-j_1 c1r 1Z/{ν^'L̠CD /糾~yN0@wO 0:vQXfǁr
+Q뙵5by%=3@̔tn]<]L34AH9aÀ/O)1VLqO|/6Km͘f->+:f&
+YWY迂)Qk:z<ǽ+Jq{_+De!ތ.fa']7fC 4h!|2G3^(8m |r'A~;F"0Rvrd
+\_礗a k /, w5- "YOV4'z\ʝ2rsK^]CArsZ;2-c~C 5w^KG̀ 
+y}ä'tDc @BS}\b[oHb;Z0Fƴ*Uā }qt;|6`w[_Z-V(}bqI5Z 7__ưɏ)ih2]0#:(AǚlDeYУY.룁&[ًU06(vh'IC<;&ƤnpJ*j3[MrLtn8GܧM9O)H/VpJF/_y9򳌞h*f,j }N+ o!x?4+#Ͷ@EIGÏ, e9 }3$G0[2
+GN$,# A6V
+eՖc| QN'_+`F X˹%9~%A9 lϭQ ʘ9Q`vSj][HjKW\VI5ɽ|ɉ{]&tݿbЪ-cjtSY*L†4Tӻ\Vp_y
+\tfgǺ1 mioO*`RM2dXIRSWUìǿHs0EVeJl`.Qbdd*F
+\@q{6~|z̽J1Ӵ;5 ܺEc=+z~xP_8||No?r4
+% ~/aH|DeZ!gd >^P%?$GdԨG4c@>ƿ1'*+~: S7i?h}uOp;+[^OdSD-U*{z%^d7 _Z.!
+Q9{(M'.2z3?Å xF6(
+R=EB@*K>]}/@b:fQ}cѰ| 0a F9Hy`=y՛*P!oU|>k i>q!㜞p\]
+Aٶc f/Tkw`5Ⱦ'Jɽݗkv9i{Զrp7GJNLWvrvڹ<ӫkκ.\dg.$]3E{dzCړ32`OGY{lϹS{]{6wˡU^\^ws%U7؛*c&nի~s`yOWK ڻF޿D>R}&iͮ[f_^V<]uN;v2#p8sduzr p:bك#I͑5SqKGXsUј\vqKvRivt
+NMTZ8^bgz^^Ԝ&wV:㸳p8[8Ź)%)S+U~=q9gȹp OO)+v(,9-ҩi5aOEN"uޟe:/ a=}sYqV]J3L\g+M j䪖>W']i2~s_Rwޗgܿrӹ1wN޻J{rv]-w0*t0޻gNؽ
+:ȍi?=؃']VO{sOG=OQ3niY@yq7<oEWac7*Њ֬><H=ob^N]'-|L+i޲ _=<n.2ߋj/D `Mʾa=fEf/ҧʭJ[U~w'҃<}>(A}Ċn B $Щhɱp^B8t4WL|2xRE^ z gN{/t.8;!Ǭ5\@R['T P}C/jhnBaݝ9.prvb68.HFdVŧH-#wO l%A_;9+剋eIѽYb𒮒D$y鋐oD$J^<EvɌJΊ6zSRP-u䩷C1:þ\u> O4IVԈYfLw3s?]2YЧ 60/Y;B&b#5 -9?} K˧#Mh>s݉w½np=+<ov}M>-ZG8+B9fDRPWYKR"%bݞWљ".~*;@ghUZ+vZ1&SDzY">Vb΅'ug̫rVJg;{ܽC緙x/Q/>-,Z">&.R
++sy('.ڼR?6dV$S<I6.{?SqT;LuA#@9~MWi<'SLϵ^
+FջiW' }>_gȸ<h O\hYyT[8S~7xRxs:\sfx1KfF_=}Γt7U~Ս5n߮w5.W {j$#} <:7x4Ey8^}5i{4k<bu^]V{C`:c=?^ΥYJܮHy긙&ɳtD/f<ì!j/Ջ"QGٜ"jy2WpMxIotX֒Ƕ k nDuhQ7>Tfb7)6xF[mϥq [=\1sQ vsZ*3x2}h- 8ow#D$P GK(m21Lgba pHq0Rx}ʄGw  "N> Z+9$9쒀>xZz T+ldoopduw0ɸnSEnP1i+fً)nY֞pߩ
+,gB~֢)`9pL{X YXDUY@wbx,Lt\;qiZlq=5"4VF U;>ŽH95bGHX6B])Fz9. ޅx
+Sݍ4zD{SGvK j4#{<[\eZ|am"}R(m#;>Z{Aӳݧ- ɻZ...<[rRvôREYl=V|V]H|*)t{ˎ2D^mv#-ytiJho!EX0=<Hs'\@wHS?'
+9S^.N9Gb߿7w=xuV$c53,aV U %v?o+Ş=Sʞ}yZv4kΖwkS㻸Do)O[|~kj>brb?c.^-3!y@Z]\b3&؀:) NN#ՓUi9JFLuf], ʈ|&e2H9߀fpM`ohςI{P(sX*<D$~s:#Jsȑ[W'^!Պ5(HG]+\+Rf?RGً9uhAw%!+E` y^+RyF
+A2Ꮩh1)n.H'#O]c H߇TFHMlcJ_G
+}>R8+uWu.#bk84A2Zd|u U6BگfN
+ڞ:|8\ ʮg| ^AϽ5X׳lM%0M4,)DݭAo&uΎLd{h+ uvǬ̌ dqQ 0ĒUS[>0 7td$g]sdvuћ\IJ"ʐ :;W߉f0\+1>c1]L&![u~rKZ ")E-!wR8V39 b7a 7ߣzÍF҄ s#MrkVN]+ v}7+qchdCV .<[X7^MIB}kT؊eލG[?y1|6%Φwy7ؔlŴGV]r,#ufSYrݼRo5ͺ<L!+AS+yW^vn}8~&mjƂi%k{'A֮vya{Y¼Մ =VQ`{|5 Zu,o@JzOs,?"V=KTNg+ҍE.Y>c<X'>r鎞!K[M}nUn/<_5iP~l/'QlWDw?b[yem+p7O׷'|6ER<^읾fe'P}+Whcz}GR;> ms4¾R
+eOW6;rtԲu w-pTG-?~;2>̖Z&[ȰK7toy-- G'A=.f>.o>r;tN-G~x-m)[<jl~]+ݞ<7${ؾ^I2=I2)I$'9$99
+4*" EiRD Eݫzw.۔$3g[yӝɋdxTMbTXU6qi-*2
+KC |5pfnեXkOsӵay%R_9 V+$Kфvͣ)ھeXURSJ(R0֯}麏A/6 uZ,q> (krfA3kf(q[,&ә`p5&7mz?vF#nP1R#<Qw~uQM屽M.ug@MnRW
+j܍O' {*z1VVXOSSilii]Vzl&rl:*ë4Nr,],T*Ž^igD5✛f{ a *w7׍^3213Djw%Ez1ཝL]^ԨhF(ڬf:3{hb6r\nE
+sӍxc%+ߍb®cGCF+5콲˝IcWFNdo2Vnu(Wٻy|Z.7;ހM3 YfEV\+zs{Ԙ年XkP"*u^E3Ru'Mcg6$W}vUn7g}J֚GJzS4sVطbU `֚ҋZM<u[S'k#T`A*zC/*OHY85t1yJv1|`x_ƻodPZ|)bnv1ömkmRbk20K׺+W64dQ#K1E惚IZza4o6I\;cqCFyҋw-7b5Tbonf7db VAc_?1sUnM-.47,ـ8NNߝ][ɴlesrn:95kr7&oAW3<F 6"Kt:т5ЙGI9UꎡR947<S:/-GG VCgA'CgArw4 </ +I~fklfo tT-iVzMY9n؊R-1=k2ѯ‭|ɍ2jIexŔ$̖?6_Ŭl?a[ >TMREʕ-ɑ\VAsf9v9l@5a86vѝek\6{46RxMn`7'dM4~cfdc*k:淳Ԓ3r"b9 ϒƭ/vJG4å5y=zfz+'=V,\(m5d!i{1X:3\8#݀z$O &j0QՎfޤg3+E~^i,|{#/ `*Vf>S:!g9"OYKe,lY=j.foS]^Pb=dS/)"UNz<fk,[i떖r@h#Y,[vdO^O6؞]*s%'(%;ƌUi9aes-IT(%LGFa6raが5̱6*vd4Ӭ&e H-(!FUbU-^.)Ɣփr2XT˕+;q$_ֹԳ[ҩMfI9U"cNo-WdO+swАF+25{Z{<vWd^ipcVd|n"hGSZ[ձ"#G ս"Ct݃+"R-jYѬ<{9V9lgZmyI+ˇ#߸HU^F
+sD#60nSբΈl#y4o=Nrb2b9ʸ:Me\]mNp9WWB)l B댓TU^64s#ٮu# px8uUx8ժB9"pxVxx6و?N\kt<sϭ"8K@#t#tc
+6"Mk Ӯs#0UffW*R לP^Hhc^Z1̼6pm lDlleR6Y76׷;zuT!}=S8.2^u+.H
+dn7).`'i3
+#ṽ.;?SriI1
+7ܘry~}ӻ%U3>oV}>sB<gDqKYj??~{XuSU3Z=utY9tFF Qtz#ⵞGi5 NRG)Nt;#Ӌ>Noзh:X:
+F n|J Ӌ6_w4*27.NopYm@4T'L5$N/NLz.nt4?N/FUYb5FX58뱂zlوh:X:Eܠh:ݒHat&F44Τi`4xc
+tNbF:?9L
+Cz-YntChk겜:
+crS5HfZ:/tS^X=#Έ*ݬ5mtg#f_q.Wy]9J@dtWk̘)Uq̝Ɋ|]m vkNfk;7[G_h1f;yFgq8 %h 勡:=]v}'yL31ξa_Ic茝8ZGs"e&<aS9Qx:7AWG
+쒼ȍvXbcFyC)^|!QZc\JTsgN-PA$:R:]X3 [-$5bBv¾v|Z_˰(o3,J^M}g+tߌ< fF>#E4ÙF`8c#F۟=Y;xWg5ש?KxzOS; gIb3/{U6C7 +ƜpGQϭ;Ǻ愻NʳOdyR^CNf9:~{*> {-qh98ZD̲e3Re`l+V%Y
+u.F>n'߲h,n+vW9"n6$\:yt] +k<.Onhs 1l1U11^~S! ӝ3?%0;6LFqלܽyqAL+D!%:6\U>u]* ͳ6ًq<32(MɉtE7[?iqRtzENyjj0ق+s'yMglJnNh2.,{D=l0zr|=mB4ojraؗf}hl6I?BӎO}B:= څ6xՐq/=q)L6:os+~i$w{iaW6t/#Q-wd%ZO폪_QSe <mwQ./Vg }u(/>M7n'3IᗺRKigڭDO![*olX|egrvn;RI"8tVd<rY˝ R6 N18i7g\+dBbr*k-gt2lʝXeηE1;Ekc%i!cõtXJ*
+n _9ogDǞex·)VP>z(5P>o:SP>Ӳr(uʧ%e~SXjgS <ׯf_3 707ɩ~mT߹~sCg*z~P5gR:峝sEzC5\?-gdw3d]s|~epɹ~8OeSj65I6HasuhĹ~F7~ J,l3~S]tK s̯b|_u]oo~Wijй~*rDj}3_CR~[Xz3?O*:,p~:32\?]'~ugkwy|ugk~xY=xxH^<կ"|Ŷiw?|8_y~s,z+~IC<Tz?6Py_UGUD6\?-T?:kābUV0G[j=Oʳڳ6ys̯B}q y|Dղ\?qU1O1S,Wmgޥ<ѩ:Z~LۍىyRW˺gk^Y~ISj=|`H~#3$V}i</>KRT?xs '᥵gYb 9ozntm wQ՟g9l#Xuuj2F$Rgzn1iEUm:dZn%/=e!0rZTKI[J'vܩi}F=ٜaoTڱ˽osх Wr+Կ;M%}S靽H6dw=L$w3_O#mRbwDnldlndv,r38
+x#Kۑid56Y\?>>ΆOzτ{g+ݞ6: :Mfa0]&[{'o칛qO w#6;B]gÎUf1m07=펴nmM0\KONh@+84ߡ8R:GgȠz:HaEVAY;sX{*no G'-˝4r%}_(_s+.x=ZaE[S2ͧMU<zGYm.ݤ+x" ^woyRL q+nw+iYj;j!ϼv]݁{tOUZXϷBjr*un
+$iM#t5xBa+G|~䧿ٯO!(>!]ivc( Njѓ7"\nŻ0""VK9gP,
+{aRHv#!`=^bod)-gonrӷ/ 垾K`:m4,];u_g>w D[䟮{bP>'՝{T$t.6secD[E!dbWZ~"$}Kl;ݻ콝әҭ'sgwӮ$zkr~ř|ΙL[jNཛMD|MnVYލc\eUi@`kmߦB4+<d?"c?Q[̓@_$6v~}4M$uȵ=Ok~n֢Y'hr^([frtLJ^̄z.O] 1h6.=6.ş}c鼵_>!T\\i)NFEшX[T"`RJ-˚ZAٌBC
+XVî]Enc349#'|b+Vi2xzPj<.]cfG<tdSRhG3?@
+P$c/tą Wrsau&"zPONvx(d{ R)!tx6uD~-#
+}=LG2} eYU ь~@W3 crvb7!{O8k^ܒ.?%Wnr:#*
+cBi:ڧV.wGn{&zb}qrnWhfshUR~lT~+mZ&6x|<'RcIgUŊ[:UWd4h/zSz7CD7wh|ngx'SƾUN%v\g|?Fddx>勵bl03l{@D?@K FHIQRSFU}\,Mz!vl!Mݟ?ؓPV}lA 7I3:
+x T9[ oܞmMG}K4 3l%Unq2TNt5E1QH\ Rb:֖Y[O&3͌v@ -yi{IG)!68+Dq즊%a#yr+m]B^Jq gndK_|"O _rg}7Ūtc$#Mt71/it,NoNiOx}xхLt5[TP~vR>F >HԄ[ fH&##%AU?ا*[#m5DT'[H'ltsdʵ!UW>xs6iox"gQ)£B9A*tH(}Z$մ )Ijz<T!a}y[D6@zacjhyvvEF<ԃ~:ͺ"YR^Pi"N$eoty|4S@1ah?dC/;^ַI7w_zp4uf wkTޫ4~', Rwu^'8[y}
+܊oY ǺJ,̄1/6J38ECLcCF=&$,gJm'ILjg6&űgIvVNX"hLqSl=\
+$hq=yT:Q^:{rJ{}t]yUo[\26G^ uq-j;KubW^ Zv(nutP9rמ 3yu#NIܝ |5C:05O]Aژs4-ޓ -"#='v{a?޽(
+aWwIZoHG8&L52ET`yIΓQaPeU"@q6C1ftA9>C[e*HmL2!  C᱃Tx'.Xo66^:m&K^ rsydtup[#j-SCBHr"I(D=Lf0NDb|Ի uz8kY]%Û{ȣh\8xQ( +?'"*p}"I>F:LE>FXMXXf`3 eTJ*MWf<h#_MH]sQN-mvXէz7MqFz˶F QhzzC!u.ʿJ/p>yvlWۭ*?_8Q4(KrA>[ _xt ;R&l.fVI8E[v.x5邦̵X`3r_o?'f}+g`b;~<RrCq0\k1̹Yqxh~YoZ$u#OFT'tP*3vu,vs~TzR1E\"E@fO& !sr\B8[(œErR=&TN<c.I`@b jޝQbӧwMIE=E1bxK5A]w9~U"0
+SV.)i?}OPqoH})f@k%-1ֵO鐢e ᖓ>LqM@δGf].wpg:)󍗼D߻DT*| ʞ-W|doly,:NhVeq]B;ϼ>C ز ]o63l\b&{D&j(GWFHkJg7{##=)& ztCSc̉L~^Jh䠷$8ۃ%#iR
+k
+RnȳDwXRƄ<))h
+6gDcdx'N-yѺ6@: z3"ެ?y ivCX*f{>&D1~Jl
+6_,O7QogUN#^3 Dn/DǮhֻGnnVMOJqqSR1'uH=|Qop(~y6i݇nsӾPURHtfy/@:V5Uqir/ǥT bc|nzzI/_ oLg<-C1'2Tè 1\tѶSpg ri,?d8y+4)Ks錿ۛefKw%=y #?-4~%U/&)my6)H0`c^Zm.+J1cK13y t?#ʻO0Z9㈖UɭTtW|0TFa!;]:9P}C0jf?[^_t5J  !^ːBtSϑQƮNU [G讈't.d*4BnMT0%䷷鹓q1
+/'
+t>Nst>N~uY˻z;DW&K+S;ic
+3j$3@*>9e"DAZN 4d-:ӮO>K5-i?dvCQObU3%bxՓ
++4Zyz:rP[twKYbE`K^SNr&多 eAY4y$
+Dwɤ1;LQdv'HqOy@TTO28L;޳u6N wX-\tVZc#i앿%CZ0N} y!Kz礒" Fe/!Jn{!K~RLωu,Gg]d>JuFN=T\6D `ѡX펮>&[C3j:v)T0<%Mg<r{uL[ ߨuhvKGS?2i.DCc>6dxl07U PZ- J1j,TzȤ˟?ʊ(,xiA }#Z3]k`3qpae.JJx@ǑQ']lOeNR&w"'qAi[)e6~K×->8,Q6z諑Jv̝KVB
+jYN"yU͛Ԧb!qZjϔʙq[oޭOhC R|E!j(ژWd8$؜P?9TuU駠bRjIYoO0cd4R 0Vt2Z{'o*t2d9@ꝟ |a:Gg+
+| I ?C2Ԥ;l7a-hcT9Vs[>2%̚NtieXFҘ0P^w:}Q2~}/^"ӑ'Ϣhw/99":˝Lͤ :N6
+ ӽ>B91RWLB0LQ$Fn !2aa-Iv`R\`dP X$\u e^OD].OEȊhI~o[ҍ@P-ʕ<}K3 iSkYndD" 00Dbal5
+eKBTlp"bB
+ I7Z+d:e>70# |x q\֚{De>H=e^jKE=v"Wor @CNW)&2,pDg0$ⵐ褭s3C~.#(4]@CÅힵti=!eMS
++/jw? L 2jͰXfOld4]Sȣa!뢫B Ԣ.ti亴],Hh(@@ϬDs7\~k NF5Rz"`+w_q+C{J26:3uf"&<+gn =#ܖ`c2d,Z%VK.[r/[j^U &PI *uĂUʣbH*  ַ~f<~m鳱ݭmx+wK<)}9Xcu
+xNgM&>Y:ʥhhH<GB}"a7_R,]`@o(Oý4qL* XC7x:wHify$$$ZM)!y1F94'U\VWbW+!uxDr6r!9[wC֭ҭ]iynKAe㖫mn(g,4%>w44E{EM -2+Rg7ݢ"6WTj6wFsvQA0R+2Zv|Hᰮݢ^wH>gchG':5Eth:{DkY
+J3Zl̃-7Y /vs xJ
+^*WqiӶO"굠{DԢ<mHSYY#^*%NZSR6ҽR\H_5wwn+;گ`ėDR&ax[T/)帘cvr\ 4 dSW4&\ Sgw'ww] 460so w5ֽk=ƒ7bw<qu- ntiv)g&rEv"Wh_܎'+BQAnHpɽudhQW0q#FQݓdtsV_p5wu3,1wݣBxl-?KVS}}:wp.?g+1 Bp`:1TZ%u<tޔZYbu:O~T*|. !i4S(ToCK5v8%e^8Wp0)7V[3ir3СKCY|]^$G2T$]RvbXۃ&UX]}\\[b.Yt{ƲȲ_k[rlDejmMyvr",zvYN.c7hƖi?]jaԟϏòU-RbvVd.3Q:dFNoh능eesU}k:SwUzMGYoA:}{wE^k<܊>!gqLXs:k]_&(Z.?ovKHݛ
+E~+{"_K^،)^XvVJWǎҳ4,4R .G$5Ӧ<,ېzFRْLD"y|:5+iMrgt+0kws.-g=bLw56 y0VX;:7OD<!BVΑ<Z%j9k]L `ltޙ; V;?EZc\c}$ J*M&!TH'X{lhE][d5{S3ilt 猧Rpowc.7t|՚* ȌQOb˓F鈕yO==rb ݧ)-=bRPW њLJw SDR[F辨;='GѼ&Y^T
+X,u
+I(E*_6G:M*|v]#ԡ$ܾiѳvf4uByȹ x"%yWg-Hu؀dRRuRKsò ,?+[R3٪\X݉yau:q1Ңڴ;BK4i$\i{?#7<d#ug+6k/Hx)賤 Uҁ)7iA ѕI!uH7$%ۙc isA {T[忳?Xr7ߑ~hZf1AU?إ*WA-i?֖c=꧶9^
+ͣ{h5ͦ%-^H:/'#ej=!Z_;LSQdam#ߖ]ܸ):4YzԚ6Hi+c,,m }9FQ1FPc6M#^ev?MW#QTpPXrnfɛ<Fr lJWUq+Az=IUF/uYHff$C_Cܨ|uDzF_[zgOF)4p\1;NWʦ{3cb.P`n/K$uoD)=4TF{c|v^; vFM8塯r[D&)b+.-l+x9Dѓ2F6s/1YԗuKS|ڌ}A@1'1S\lqZ3崓K rsyA&E{L/tdcߨ Aᝉ{Xk_*vjG<g1hԯOUh ecf)*ݹ%E ZV[%-e!QS>OUJm*gTe g`*bŰT◲ v3B~IeSʊ
+%؞zBW.&ԼXK.f]ؿOKCztg^I{1,]"^fQM͠u޵qB=&][Is0TDgo8vx_~+/+Q*:^W[F;Bc˩H}u:Fs ԖZ?Pc&UDyXSfunvc %6WVb%az m^4kЉ\WaOuɭ]1
+e%f1
+BK LE-q2,/Q8 ).Hl$X,Am ~(WL5XZz!֙o &R_ ]䋍(5gJw]ۙM/ 'JNTP僬\W+nrRgdz7s䊃SBw({>A^Z,~9GHq+|2$AU&fFG2`=u*IåqA bFP>VH t4[^LGeɢ̛ǞYJZzmr𔥋(Q sFJyNQYLGF<†ۗak=d><=f4NEȀEA@KB7||y4P
+ 9F@θ#*4!
+t/NB+w U]ኯgyiMmtelFGYMCѩehESOQ9&u 6aEt!qq/VqBƮgYYex~4I`m!2e/&A[Qz.!˭fWGΓ^y5+~֘TS42F.4Ixq%bKGy-SkfZ ),־Y.%!.5;Sy!TdpXxMTYP[yXfC<'(M!U1JخėR
+ZFnSZQ:ʋ'gNmq ʯ ӳGlٿ+
+4|%֏nbaTTv6FQ(EfRA;RC,:L4J6Kf :¬!i! J4%ih,Y&La R(aR&a^ֆ%j Ct@m+ιV9;M=OAw~{杨etv?RX;lY'͕àY_yNIsvj1 0Sӊ{lgXw̑{h ;jUvtu~2d~Uڨ6JJQkߩ>wG9?jeiBiZ;U,;j#OnMS}= \di]DIu^Jvz9uKәhs}V Ғ9}w'^6 ڦ tѮ0fSC4"2%xp'Fz`25<R_9r{@gtz|##=N}"B~f}Zzt+"~ =$#]R^z-y\Ikoa-c6\?fD)&*S yFj]?A)T1J RC;=M0zCwz:iCAֱ875ҡН^nC;*zCwz08:0zCw&tuRS OɶN%._1P\i:B@9$m @R 3ޢ/գq=K!3fa44y ձf[=֙C8R߈>N$g}x.tPCn)  ⎀|U>i8H?l%(+{&!}C|ehk{Sk+qMv_8z'vl6zɽ<>l[;~ʵљbZ5+ͽ<~"+5/J\!6H7$ m򿩦ȵt/96AS/kѐ%kBصȵMzx""B4%$7EqG?vuu]qz}C
+
+oqZx
+
+oѭ%
+ 
+
+
+
+o»
+xV
+
+
+ kw$~>Q wGߗ +[WgKf#&m~=z^WySOWoGm͏#pFcG=ơ@vdjnSx>Lo6#jᲁKշQEc+Lo_ϓ&~k^<O9\99& 剏%M;hYFmG<qD#ޡѓX4?O}/o~da3kò?{o㺾Ze4h M!eJQQ(T(CT*)
+P2h̋u]}?CB?kN{-98뺟ڢ85#5OiuJo^|!-)>:2<tF/WǥVfǫ)bԸ7x(9eIS.]! dOdt|Rb,q' M/_PXH{T@nAyJlFAQ|jbܑ7;٣n0QCINzjР3M Q-kdbflЮG''?} ߉Kcn7}[њo߼jnϻv)cQ{wnvu\b9x̨HޠskM55_dcC@h)Yq#O{ߚoR{~)΍s ё׮Fƭ"?z
+h4eakӶ%{5-#1p yJ㒦F]UvޭܧPAnt_ln:UOc9BTi,l\}9qKp/3n'B=|X\w-\r]VXB:bIsU7mʬjF l׬~XbڅwP3oj?BwBJS%ZܷQA9{pOŬi$o=Imʬ.q8.\ֵwU=~RXbvTZ@h^|GElYviطebxֽl6kEi%trn>@e MFͮ|?Ʃ97)軷#;7t:73m9Eg16{8T\FYD
+5j&-ZC2qyMݳ篰U2Mt)_@pDzZcmK0&u Xe-s윽C"%1qO縨<.em{`=&)+,P}b1`ǦQq|)yVͶmgxrXz񃻷mXh3&mq
+cvl0.YϜovö'_X@c)c><й8?ҹ;ֹ[cFi{~b]
+ypY[ۻ q 6ܥ\4;јmݾqt.+s=3ѽ!ȶMQV ) P!(M24Efɴw$ZPWUZxꅔ=A:,636V+9 `GFola토=Q)*,BS#]IO:#rTFʹT322
+꓌f[ڹ<~%/A߼lz~~N`?3'Ws_̶F? }PUB<f_06묜6|e4tû/Ԕݾv!%@_VsrFaºknsO~QVÅlP\ʅkj4xß]Gc m4?vbPUKp9<| U΢ڌ[5`1gBH̺?x>*cx/mg&޳mR(l_Ϣڌ[IJNuᬅv.dozd_ݥ4|nȶQH=kJè&23nK .E ;IȾYXZ7t)#ԞmWfyD59Dܵyng
+~{ل]䘽AKϠ3'*JzFz)<6:q
+[wB05n?d;z
+8js{bD?d#v9qNIe[R xE򢛗]@KQfĠȘLMP(\O3E+":O^u{fdϝڹjqGDEL&(95󗺬߾XJfGͯy0ݫ'U%2NrcDMTYPdyu=c 5Cĥ^+B-pk謍7$ϟ<,ȹd3wDf|ј?s_4*jQKƛpk>LMs{cQ^mkF&*-EFc
+we.zk| jhr~\^u.Kgɑ ,,ǖ](\_Pkz k(V߼'*c=uy.Rd+π=GΣpW! -2W&~L} y\YXɳG'_ạ.hɭd0*j߹~2j
+K}MZfD0!+cNg(,{B̂GdڵԀ+I5#bOg>#rg-HPFIGx:X12se"kODN+/\rr~nn^pdnwDދE.k|נߢFFf́e's@BK7+И ۗjˋXf%';MyPJP'M`jec]EQ^VJLV/$C2Y-f@O$ќ,$25&Cn9|L=gvfTfHV$x T`&*48Ueb`e&3 9u}K/Aa g,3{[̘2f rI-A_5VMopAȴ3_kg>m<X\VUx
+h Cδ7.&fdd#>FQ4}˞hXW^{TTF۹ڊ0csLGV0məyEu Ȍvmٽ'_m3g԰bdllϦ5Kodt#Io"_QDVx2|ZzBdӢY٭5̵uu|naym#0 aճq|VZ詳36S PhP֜<{!Od\WVH֚ Ao_4<s%@/3LNm͂3u$VPw x;ۗĆx,cNShE׶}qiQ"Ze~iYwo^k7%|ܙ $/*^+jG@ı+4x_ A7ϟTeRc<#mE*hwZk$^O:n)nT7K? #릺 xlv3YS^J`KApW<CeW=}=hl.q`LQ&Ӛ $4 PuBnQœ "Xfj;s-Q&?uJk2LV 3Ty-^y#3ӎbDex M5bHZW ߆uxcs} Rbl^h<N`њ8ym Mrᣆ@vlGLSGvnr LbHgf~Sל2oY8^
+Dn/xc3n\8u(t=J`zjdѭY).rZxFq7xB2/OW~GT ORY)/t~ !#>lS(h_M;|*՚sSQSR?cLxvCwY2wdQܣY_,!5S.~$Lv̤a<r`G. )I7"w
+4]mgS+ffT1FUtʯfDHk<PߕƓd{3Ш,8aC(_S~ "w8<vfZ{#KK)~|.+NTO=f\$zQiÎ)WK!u욒[}L' F/A'/ݯnx ~ݙPw#9q{.;Y}x{05ҮݭxCw(GͥSdEXl[/ׅD%e~5\h~P}?I447Ԓ5]~oSTU\}
+<4
+`w.>~I u6Sʣ䴌̗{o?y^ם?85ƎHra2*]Ï^X^ rCugwnt~l5+ey>!Qɗ֋= =*yddMSL)7f͖\4E|ы%x{4DGYV#t)Jy]ӗꞿ[04HKV#T)F7v͖Űfd*~x̰_<moWp)Á#LSB|,jH1 l[
+'ȿ|p+OJ9<2XfBJ*eMiJ ,rk;CMSrqV w ^& >?
+s
+s#M9b|9mpfٽ3(f,6QGSjBluP\0|ؑľ
+>}Ed
+Rt17T;zqRCգIΕ[`>L1זJ<i7oz0E`RV5q-p1],gLP6#q_42_BFA)s?izVK諎5'^Rd?<.FA)s
+OEc,['.| 2+s_5T;gjo5r>GRV<}A]d07##ya&[;1lD;l
+>⽜Tja%cjeR J[$W[,G^˽C Jc:|2dtFfʰ$0~j=sPB)s L`n!;r@[,1liKN)d/nG%y#]'- ˲aѩ
+aM<rҡ es-6xXv?W{/]OٹHsl[,6a2*syJ_^܄Q#s.fNTle󶛚FwƤ]/nx{/mD0왋\6M*
+eXvE6[vk]e=ͬD ux-fټ9Pʦ#_eSF)L"C:H+Ǐ/|KO9bﲝ'(Dl[&7tǨOb]Qn
+/b4 _1Ry/;p\"TnˣϰBsT…𺱦8l,YY곍e|^Ivm`d"O0l#xbf)G}1۲<{#g%PYxf:Jl1ڲ ˽as,ȗoy_hmyބ59PMڰH󍙙I[v0G%Yms[nvBc-zSTf)r.tpݘ[e-Q"kYb[xZ,Ѝn_KlS[7޴ mY3o&W2 D-ރ,*3lL?&kcQڲhkҫlk+g8|;rZ,e Xb
+ČW1;}s_?TtxM~r,EM~NӴ}TgAN_[Yڲ@V^3\$>_;>GS]8[Hce}+ɇܗUj%~Qډ࣊%@[Hc~Yuѵԣ^E/|j5+_^$07dW ;E$~-j"(|B+t6
+_+-O_9%rs,x5aX@
+—@/`es;՚iSxy&eA`nVM]6;y<UKd_MKnGCOC6u# 9v,xJ,"OX=u[1|1^%;u]mq&G›=B=C^-bl|l2IQb/6lź1gUl"/d>c,Onaƒ3<e~Z
+Do"~X)2De<HiA/<آ ٘VMVb<p޼
+hAo?8ZL=<0U0HQ&69-9VH[kLg"v扽k˵8e:)VD
+z·uOR/޸ 'Ru[>D<0 =
+;-_s6eCh`WtZ=FO~' ND  [LW6{
+љc{>5%;QyJmk?Yd $GRaۿs.hU3kB8Ƶ2>^^| .ۿs0UWS{COU"7gW|T9ZSYP2uQ_lƁowx50-|,xU;4:R._Wz~`OXt>*"jU>sxg+*>_SJ
+T=ڠ2,M/Ե/<e>8spO$׃ʢ
+
+|u.U
+* <ɣ3On\/[GT,;{UpWDᄛHpJ*}lѥ V:mxzaQYya# eQ/g>_,\ѧM+$|3
+ Ja-BP\ݹPӈMT&߆ݼ_D*|J=m?@UfNM6dӠ@xCҗԧKsȥ _?|eh&
+_> _Am>—4-|᫵ D٘Sk_[pj`/
+K0~[/ezD|K0~QĠܭGрP+~tي.x| kw#4fQ`'B4d-O/~5 >Ɲ~-B'ǎ:|#,lh̢
+µ C'hJ:}l]e2ܷLesa@h6&W>ۖ?K)L0vׂn>xL_ m&bv:92o!id# m<*PDz9*2C)˖Qџ .esۛnvY4s=G}Ve'e^lٵUK~ް_ز9 mî(|h'2lU2sY'X6(xRwk[ uˆw.Բqʆ]6gJm5l˦S6^]6?r/aK~հ,,F]6$ia-[V G :_NbV"m0lbDZEomwcd({QTq[eP?jߞk_A["1:PCMXvS̖{Iɾ[#3a;YKfZ`|7Fl?qC*es *{~Vs?7MfWf,[l+7={/AeO+f^n6EkBw|~eA.)=셇ؘ(I iaӖݳ`I<2;eܸ_58@ȵ66eԖSp
+P~^aYu4Ș^"׿tgٺE,`)׳bV[O@26{̣PZrΘ4j}!R{+!;ۢ2scwh5<L̺]01nq٫2߲y #`ƨ{W u ًlޑG%_/{Qo]<y p-{ k{32Y9")A1zZ쮍,W[U_rZF넇)}kKo1*nfV0=<L;qG?@1sj#CcqC>F XvOjZrԫ*Uoו_J
+(|EY6_xR՟=Ld
+J87=.e>sfX:m'Sf}J!>+̍41s~{1F}<; yX R~VsFF<و*K%P̽ GmF)b|ux#2
+_Fæ/,FmF콶JʺS
+>(57e/^1D6#f.r&kN\نJb<*ob7#c5 /'Ō;3,j)oۈ3ތ V1Y:3ٕ/6U¥`2e巁rT1fFf
+9WxVnR)$;6=͆bfIF-Qg6 <uf#Af6rU^{mYcC7SR̨3Oiz㮘ԫw9 $z}xT[zRrTÏlb8ryw+X}E}es7㵗we~1y8r%9FX#s[YWi`)31[l1js-n='KSTӊ{bw9/2Qk(eR<DRqB'_{Nz52w.tzœAfr?X MV՛e9:0MQgwnt9IIzmdfΙ嵧~(1jdR~}[ݗ͝1v@_~(ҫw+4aWw.>r ,e`rfvD0E/2EEl^c;[_m̈bqZ1gdlj.
+S͗{n;pbBدo^ddvL0әi>Hٝ~}ur%L|9Kl?i,N3uTFoW l(͗{1 ix]WVk }z^B2fk B _f|zxE)jT񭗐xhg>F xvAYgCk@>Cˆ[YItV>Wʳ(F9;#?gw /^7֖g_cuF/FfϞij}᤬[%5 /q
+dpS&Cԓœt
+-ݷ>W\d^>!兹OEmt;EKAjX:)*S;0*h/X2|*
+o\3ԚfFIȋ_cf.tudNhʫ(/3.7&(13DA{L,FĦ\:xaTe޳P*cFuL'_zG󔆾W |"rLxNJ:hB'D LWz fD zjPSOä4 fQ2mY"_J`i.'1PlEeZf46#'c]S9T_ lOdM0"C##JkSMyI4(xB2k&3D~ 
+o*PEe 5 ʬkat#;m=ew/y2y߸ ek" ;O~PB"7(Uf<PeWf }qYL4'ou#E/1W@Oav!47<*{19((23ʌ.
+k|K )ok\?ttODnMf13o=nx= έ_?Z ZƩ#x8XrS2|1n7x?͹% IUɝ'v4-e60r s45O2X xɕo_9p0oS-<:|*Z~I!]LP7/En\cga./9"YdrwQ'e߾_YH24gtKFE¼3wYj>CWM" ,?sۦio?~Fe0pmG~j'ؽAmͦMR+1XO,(0Iyu=ceOɺq e׼ugwי^܄u I>lN";̍Wk,3}gߠ1r
+V?5^<~.Orcćey2?Tbꤩl|9qN in[WΞ8sլ)Fܗ'sWdCF*O0sˮg*.܀&?ݽ6߭_?)w# Mn M eF +EܻJړgYXsiMu(OܵoehP>ϯ?s^dFһ2(jۡ /ŜUO]2qk4%߾x4<p%I!{q^d̿&*u^-7 "ە3]$[?ZSZw9-!jQF<6`Wz朐v)6o?trWuĭS[j6
+do@23l+ԜC;}z>^~,Pȯln]Fd D38-ԜE=}Qs4:wr:2T?wv
+CX`9wyHGkqy#|]NF-Yb(DKd<`ɦ \|>v]~9w}j+Ks2ܱkrk=ME!Hp- n4g490YvXyٹ L4P\VDbn+MS2nɢA 7Rrf[ٯY-*.jyDkT9vmq^ja2YؒaJ涫6S6_}umј,^4>F9;#)f_yu5eF/&-9<RFA]gk6rFap
+iאY?;rB@_ 'Rn-BMν=Kp9&gd*,~҈R؇uL5dYW=w%=1f_JZƌBٚ"+2ݜ}g!5tͱ^ 9+ vY!#F )qv[FK^z$^w[3Юy*gMP9{ILwmR{i2JĬN y]l-n@6†l7p (VM.o_>$ft,rG.du~5΃FHrin/xѓiY9w0= iyC]Ui᭫RmX Tn@6]Σp9Omi@l7
+J*a<ES?.+s=udQ!5ܚAQFb!>}>=Ҟ_uтD㚲KcXjo=w*A])rhvdh`_pđ30+QXf޶a'h% BK*iMke%l4jFtF9LԌXH7d
+߾,# 1(Z rtg4;W0xb[wF|)73adf{5qog;+lcF FњrWsk~9;pJaض-tXj7Y$
+g5>dǒYˈЧ 2ν~?0%-i.9<*tX"`yV!߼DD5M 'cW ]# ҃
+3`xs흽C"#sV=~،qsQhUvѸe.X
+i85)clϞ<xPx;R:x x4̨a&fͣ϶q{:RZNYKw,𞃱'Ϝ{A@A3BlS\ĪK޺u.90\ƪƤ!wI{*.#u^фȸ/#M:t P#Ϩة_2~\YZwbZ4>?1ɺ?mKߠa(M/w06EQA7QM TZiS772FV}:huOܐYk$tL̬V`̉KQ~НGAIQ₼kȪgO4Vf͇$!1lQ2rʚ:SZ.q\{#qIg/f]R^3J$u?~RĨ<**SǢnt_lns5qo#؞FI)iL4>fdžC'Rҳ=@BF֍UҸKnwS
+Sǽ㪲;73%'DGvh, ݞ)oKɎS|e+|v?r<17 hQ{5͓=($0_aR/7"&#NO9xG&oW%sh*ɐ!#%(ha^hsC@Ȟ HGuϞ% X<|Km"njxZ[SYzRFj#wmdbtJrhv\c _g2BBYQq0&!9kyw/ %(k_R
+Yx5EY)q2-}&.-kh<wA;OJ=u5ݢeU5OiiO]J_ȥ_D
+7<AE\VRTp+';3 8<,m*'i(9t htgoʸG+65Z9hG3\%׌ԔHlڌޭh;KKK%)\ gO<~@x6u.x!OqP:= nȸ%=VQU[gʌK]<|G9vy7*[X'ϚԤXlD5'5}X],/"pcӺՕeF>wh x&ODex_ θAcD
+ZI}E]r%eՏ MH״شXo89Q;UۈH^Jߗϛak*ݽs3 gSNEܻ38`Z"Stƍ1Y5ՠW7nqAJAEcT+V޸ec`sn޹[taYEU:5kJl6=K4Nu"$.Ry#IҒ{n\ulJb|{‚6Zj%PwhɑUvϝ@вQ!D$V\=|@材r|֕k7`!6<kDj#_z5[":V=Qˊt}~իH\TD޺Hߊ)/]HO=}*>pd୛ֹ8-^hf:}x eԣ2f4n|qC;X$=,Zk`Ȯ1'Sϝϼ|5ƭ;?@bWV74<{9q+*MMWukoN'&;zh@^nN+Z[56Yg(1ϪAӣEAcUPV3y96vk7 hlɤ3i2/g_b#K+*k浵Du${C=QZS]]UUYQ^^-.WpV^KϥbD
+rsv?H$PکϔЃuWRӚKz2oGg7/_P$ǐ֧Sf\ȼ} }
+ ˰j~qqQaύwn˽彐q{2>(8lV >klL7ASuh)JbԸC=Zzhړfγspru^e[pخ}QGc'L<}&\ƅY._ɾzy7o1X[7n\vj˗.^8v{<hTA+/]l6ԟ|z8ŔĴSC'Dg?pnTT5 [.Zbs-CwyHt'O9v\ Xu R5sgRϜI9xD#"E=p޴VfϜ2NnĈa#k AGKo#%ed啐ғL9kE˖;:yx"w߁ȃ9{<.ĩĤ䤤S'D=r8u_o7'{,lFU":h
+Cw<=>U5W1r
+㔔UTT5'M44NQQA^Nvh)Q#C-'0i|AGHiJj\ս~L|R{{4},RɎP@ŲJKKIJJHB@ۯoJ_aA·4#55_l <xȐXt:UŲ2dAե%f FiJj5k,6잽zڻwo11~V}*U=;E矰R`P=(I]Dn$8'V]V26,.^,//-zhMrʼnHt:OD_oD] 0!$6QMD%o 0!_xA@TBڂ"DgTuj
+
+|iY/U-5E5%%m/h(j(i _[QYCYQ[SESOC_QSR2_Y֋{cQA2XK~- +4fv+ܖ9;k(ߠLl襢I+
+E^ѿ^cdX#Ҡ mӪ_f=m~@iDUqFwsyxKnK?]\Ez;7y< c\O׶t MkeoCa]UUHT+HhEMuZmT*Sk`љ.0$ Mt\f?w^V7"zh/W"W
+_{R7&+l4U{-=
+l
+
+ۯ
+
+
+
+
+
+
+
+
+
+
+
+
+T
+
+
+p*
+
+
+
+
+
+
+
+EBI H!["$ !"4HysYk} }ܻ%u~%S
+
+
+
+-}
+
+
+[B߅n3
+
+5t
+
+0t
+ihjM700d``?UOWGKSCMUe
+#?INVZJB\LDXHo,RҞ?C6T`ȃܤɊ*ȁ4#f3gYXζa`mm5bL3&F teɓdBJF!m`W01)B^AYE]S[Ox,ֶs~g8i"W77_WE ;mlYӍ 5U䑒 JٌVV1|(IB4m;:-\x+Wyy]ooU+=/[uB'G{6-gLJ&I"BX|b>~AJ*fff?R}`p7GDFEEoai!|}n. f3{R6EQ^NZ0r}B@HD\PОjhbD98-Z? ($4,|sԖ;w퉍 `¡C1 쏋ݳk-QBC}Wy/^왦zH 1!T"L!8m.,R!,".%;IQE]{6aVxist̎]{'$>t<3ig3MK=sd񤣇ݵ#&z kVXxf&I"B 6djBt 2asv)Τ8y1rvNn^ՂBWrs/g]<q6̩߷wa!kVy,uEEbmjDWSUI^Nj0)dUC=h,ߐe+-w8t$)tj Y^^|VYy*+n,~ ? SO''9t v׶舍}<ݑVfL< A- aD ^#G *Ե,m-pu_{xԶ]H's
+]QrNͽj7 1߫S]UYQ~ky9/O?s{wn s˂yFjR"ae1vx*s.^7 dS9~LzfVv^AQqɭj
+r]1zUϱ43F$/#!;*bq ԢPY%!#5P/h]8u܂ںOft_~w޿zݻo߼~
+iFb<n_S}F2RO&ݹushbg9B4U'JEȠrZ,&*hZ*â=p82q)'Z1qC/_
+ӧϟ|7W_|Ǐ !Mϟ"'߭]Vr0/9d$QaޞZ0UW,+ wհ6.pY(ΰ_T۰)zGɩ.^ɿvVEU  + ǟo@B Yyes3ޝbdBƙ#cwDo
+psi
+D|8a d] JNVќjlnmTK8v2ҊPE`o|%e|MB IN޾AJ?}~MUyKR% [q4] e Bȅ 9.V*ff_YVYL@EPq?k ^I!|W/>FFTݼw9ccGm Xby63Mj%*?`}q!,&9QQMgkB#bJʫ6<&Pk = >fR'߾*yOyT_{HQ~vfZ=1!뼖Οk9@[UANR!hݣHF6D?UTT{WϤ B/?HRH%H' UW !ϧ8WtX%NvVft'@g1PFȅrQ;%H=E&0Dm0"F>{F$ܬSIn_lomnK W]M
+DvS.f8zmTzVXSԟޢ@5Aw"~k%T#(G޿myلܭ*+.r!-Q뽗8P><ǀhWd{71R.l]=mٛpeUwqU|M  D=*WB5K.e:{KXLC`oWTg ؅ …oP䴋9J*<hxO (8%F0ii~~uٍ {c6tu$CANBD?F'("!w8%=+{=kYAUѮwo^5=i]r-bZrbpi:
+ =+fa=V`YUi(ťCkGM2&
+uԱPܫu=/+=p`mf"/-ǫܮ \#x %.-mhf&8b;rq; O[~٠{*Z Ez=jX>[h*Ob~XdaW Vԟa5oǚ ܣN.jj ˢU3Q Ok߸JYlg9}"]`hGMJQ]yO ި}DeAv` iU ^xX[CH Z4wdܮxF`/jR:F38-
+MDy]~=nQeѫ* |hPUm 'I
+ʃE* 4I)iZ;ݲPrkE1˂ 뗄'
+Y2o6nWhiNG?VQ
+dU]ԤyD>xlV~q9Q/ߐ.eAV>7,;ySqw;MWR<.v 0 m,q )b^1Xu? fbh.+<}$.&ljW֦dy? Qda̶w]qkܑS9Eehjn YÃ쌔C{C|=Y1 ̩.o~ DaqZ;!9#~ V>޽n~ZͫYi]z-oC8޾߭]jĨR*(1]W ۶XjV6~w]`}||z.jW[C<QyiO$)"0fTVl]JH\FACvφ='\/y}o?}Qm䃻6y/o=cdi1!dꣳ[/1QYe.KUz3/ ͻ_?xiý7/9JrݪoG.7zRH\w럾xWᙶ/>>OZ?)+r.9aWDMtUnWg+"2~װK*juCFu#Ԥ>}}ծ%WPylw;u)Ɩn"v%¸׀_h {|i J+;6[b7ka `FK {45 J&_\`JU7/>'*{ V:S&J{ ).=Dƕw럽|'Ҥ`7"jʮ]>?&tr'[өjhѧ6 "2,湮
+ܼ;ԅUQA5)/A+<]hFV,mL_ R2 1r\aKѴKj(FjR,ZǓեYg r4B!71LD[$ S[k7n;p"#711
+?0㏯hzp(lҾ!k[!<nȾHMV7h]N^/}{(1abV_7=zPy#\r0?z$,:(xV05muP#d"kaPw[]x*qWO!G+uPk!&id'd˾+qB 7F <0Vڥԣ{V͛e G+u0eVVIxos7*]t_(ԭZluJ:%4 f*kX/X#䅫wꞠ.~߭a_lfmehAdMљn.|W21fQ$n˧uwJ^8#o|+cm%uP2F0q ۅ+os$r Yjt)D³eYgw{,6YK*zsWD=v5o
+C6qSo^[8Wr7K1O/>~,62N cQcn^H>_ZS  /w`Ƿ^=DJSwH̫́5 [35XYY#^<b2ˈOp݆? |,G{;%yHGwH3F-}zf(3ewszwK\B2du@ʸev`SF+4:Q
+ޒ1rΌ9Vo=@h~q`V,Tv5PQZu ^1%鶋Vma1p'۶P:d鈍\dm(-OܜzB /*=yeP̫OF:6X`e 5~9e"ҊZ& WF;)vt_ނX<X:r3Vcl#ɒF AS#F/ilw|>4dՁ- % :a4{ؤshohz=ep?wY'2 g۱Bꆖ#+2> Ft<sȮ0%̧ᵃIN$8m
+?/|ܒ; >q%-qgo2hb}j4N37lב97럷 |P`êgmr3"hV3\DZItO+72gp`t.tƚ%`ŝSZ&VlK8}zçS*~ɃkSGx.=M}4X JtA&ӊ-'n" ֙w//+<y2Yr#dh@ n`|}Ծ |ǐ1hmP:Gwog۳i;3=܈Fh tmޤsW=zAGøMSCMI;C]qKFtBCQ{\Wml~ۿ _?}&aۆUFN` ckUBԝ7 {]Al+NovrGGаK+l;X t+4XfnoNFy~1
+ e=s{wq'G-`X;޽xt%akV#8UCS|R
+(4ք>c8E:fNZ2:&rSc'kٞSRC$8`ϹdU_:Ec jSѷp?u_82PICڊ 'b#ڙ*I1Cpcf/"FSf}d#Dtbk9YB*NU>[Mc[]l *踒b
+<%7h 3:^<[mJٸWqb«ɪ>s wɳB ۧ7MŗNZUz|^#O K)-%7ih򬶂Uh"wԍyo܅9?:?|\W@E8Iq0V 婳=&} l?965*0>[A3WZ.^ } V_?ދzOr
+q$*p#
+U9 [=g),#OgSO \<W09.mnݲ6.>a{g^]TPC 3Yd"+2nTw BF{_<^EcSPV`n+#xIF8Z5~a.Ll6rq<q"x w"|
+TP;WhI=%`<3sU[xԪ2::z≽a>ɉw8>jnBw'eN}XA~,졘@s]n4$tl9SJF8Awh#J _bc6Q;AZԦY/ {b] 򏯟Ֆ'n "C, E]sCgʨ-J;ŗR׸( 195 M*F-Bm88[Ѻ4%>{DO@9>#ն;Ѫ4\ׄǥ\*_⨸qx{7miNKyϱ^O*.J*5^ '!.FrAw2~;q_4kMP݆*7TqyBN<3WPBA~y` 5P
+*|Z汝!+#(;r 9}=opx}o[+>? "|<:kUNdłU_~{(A!Tw L&OB{8~g6A=>BlhZhߕ'p^ <w=P=C{0zqbx )QDmal EPԲ!6Q䷥뷠 x-<S!+(׸XȈt3 Eݙ~23C.1ˈ}z\ ǍJF7b?Cs4R9^ub-ǭlTbxXs8vd8'`2G+GGjTzвD֍G>4zuXPuAẒQ|
+~1Ш8r<H*?7|;8j78t uW:~ 9r
+OUМY1ߎR֟ԕRg`<{RU f*H(8ODVhb脳W+ꚠQqU36q2"-<Aͷ4My8YT&*X
+bӐ3.
+QTpq X1a;88F؀/(28~q6" q27v `" 6_{ƉMҜa؅w`<,q6tf.XJy>:3Þ8BJSg;D? T܁qTurލ!t;8eTٸ{cm8ymY>Ζ61qrv5|o+!˻W&DٴadSG[? ~8dG0 8O[TKgs Ĺ[' ]9\{2[ Ulڨi܁79oBbo3TQOʨYo.8bm}] UwH,pLĹqb,H?ʹ*cu<'-sa UOT⓵
+ݛF781N U^>o68S* =ޛι#hzrq-ꤊ5^fԼrO x~ivJU IUNК:o܁z8N
+'pa<mFEۜwP7w<݁/Nx% c<3Qc3Cps[/c*3O溡e"dK0Rq̣xS\A˳֍gnp rhaY8 <_wʭ%.Z˟"^MqY*q.\8JSoN>uwcM,܅u~T\j)Lo|XpmzXmR&5Unp 7<@>6~`!OXkOc,Y66dT#`0WZ?cc/A=/16F0lP{x>=puF6EYLimݲ.17aT5TfOAΘ Ⰺslq[=<4c*n<-HO>Rg<+e=cpL5bzbd#ψCCƹ-ڷ8¥\S䁺.~$ F6ur8P& ۵AP_.@?u.5JS- 6Щ؀U:n__+6d 85-:?5ޣ+6nSoхMp'N,<Ћt<ӫt<֫t6x“Ttх+zοb
+0+{.|2|~oѥއlKt;[w}5:/zN#|^K- ^/ѕew[t;w'-݉;w]]{.|W{FC?Ҍ[t0R'q. ObUpn S؆*m*8] s̡Jeb- *ᤊt/C~yR?Кt'R'UV?nr.^ָÊq.՟hM݌%F?(bt񧽳Ÿb!\b~i0/'b.ę1Np?`2bxzO3-ɶ6!ΌqML=AA~;mnn8qߎcs#iҴ1br3|%Qįp%ۋ ơ:"+q!88_lG(6V8iԡ:ǸDpXŠŽc<hL!*NA=JEmTl?Npٶ888~ sAllg}osPTI;7x` q;}!w!ZvcqN0= Č |֖9y_7 ]}G8XK{BW-@<.Ү "8
+J(Z͸n}S?=h<FA=82q9x Zg hM;f3MUvmqЌ;/*>5jU0Uq
+l|x8ĞNz4*FTԝ9ﻎ_~%=Z6nd$,DE
+:8Ƿ9Wx-w,nG1QG'|Wh smmWJmc'l\ ':ެhO,"x7Jgm p:0#&i].`*1Bf"M>*rWַ\qω׫_Mn?-hwdh Z3!7l݄ݼ|2.| oD;ًc"ﰽYw_di|Fqܑx۵Fmf<0&1=Pݛ٧ou5"TqC4kM)n<zIQJ hSӥժ
+:A_e?GXx1* .TBC.ڸ &́*g^C(]q@]Y&؎o3SGp%Ux! k85@E.8#~
+Zo^fϕF@vwπdqd;G` ^<ms@D_޽hq_9) Vqj"'.U7a"O66^i-SsJ1o!K5Ԯ,sZ9{N,]Cw *?<}PwPLn7KU
+ڦvKξYܮBFMUEObmkx@ ӪG2
+7U]Sh}^iNj–ef:[A^ .^uMdC2w03iw!q_[(Sg9zJ˽E9`nof:T[Aۥ8V@=3ANnǰExy~Ƒ!+,UdE]DqXa)Eyq7 6rX::ߠ[hO-vu;xʝ
+rj#^Tg8·zOYz<ϰئZd'g]G9ǖgH}
+UUm:zSOmX4mb{`lJh zUQU
+;?b
+}TBd3TSŽ*(&6A;84>~^܉>%-MqPK&Uޡfߺ%D0ƚWRb<TWV:cR
+10C 3bS=Z5t0{G@tKDt I+"7 |XX[QxDlR;3b}+_lsh%vW"4/1B_CK?JXJA{\5a٢t0 =dq":p;8[ oDh[6w0qO6^+ >=8yze3f2Փ'b7VOY8Xt^5(:6l;zƝ7h $vJ/Z=,:y *>GJysQtlu$=.x:HDW>mBc9p>cg>oI箖#}S2}|PSsPo9ӵp?9٧:a|}Ծd"a"d"hzt%=.3#Cn9k=:ԍVm?Utr bz| Dufq#4(P2]:d[™h'Gs r}ƩkSGx.=M}neq"J:s|Bw$]Q]sb,_=]t+5'K
+s%4t + F$߰]GܼSO h<}XU|̡!^.8Eq'4:Pt#\_cy5 h- F,-n\IK.Npa6 䆖{\-vmPd[_]͜Gv.g>UENLS F KN0ZD&v:d<s37p?wYh 88Jh@I+c+\(,xPNƱ- Չk Φ(GH+jOu^5k|7tQqlugiL$vp.KǿN.*3c 1O]*A322b7X`e 5SLp"Gs\W߷'&u`юNZhG2L`\9=sh <t2ҁw | %#.2`BkhS :
+M@kyKlܙЁ :gS H6&J2#<Nց\c,aHǥ'>BAK2H^4Ham[qk^2t0d|Fx;2xzQ񒔎;Ne%tأus >C{6u( 1n<Z Ppn\_;}߽zZWeĶ mkx r~S2?kyx˧N `EP75owK={]<k׏o_<6`L;7-˽Y)^ˉIw~WE2<mt*(*.)=Feb fnztrڑ=<MhѪYILRj:gʀȽ^~Z<X 4>o[vҙ] hOxD+os$rQZ<P1pʃڿݫg5
+.:3o|+cet 7EgǺ]QӀV1V>"Vg8XKvLD\VYzr '/\-ɋ7Q(KG*odؿ5g(2(ęlGwmroTxw0[1
+Oe<oWQ}X\tۼY
+}B"<cd5,lٗ}^sn՟}P˧u5
+޳9`oꓥDyG/RVE=z֝/lÜ* bzf~Ʉy:1*/97Om;1l$/Z.Z>|סy7QBuyARK8-tuU&J7::-_qہWlGVy? K]J;{lcme B|m>!o̻h-8Eqφ#g
+Jk7FY]ERDa4=zԅS7r5MMcG%{ tYs]+!|n1
+sFyvŖ0]8=ydbާdhҕVP70/4fR<pC. R!k[OS$)dKZ<h%"cR=2so@շ3U3Qq=\J®Un,pd&>%x,7^BNYeź\ Wh?~|F0Ҏ pkn(# hCLFA]}͆(<*֣g]} T㺚r\vGy/q*/%*VxJɫXڻ\i
+J4>tw}ALRDjwլTTWY. ~>,/<ANI|2<NϹ^^1jWx>ZjRjo]HNL_CR}22X0|h|uaKʿy^>iL80$E4G˜gi׻; ܝ]XZu 4Y.>{hRiwDz/Eaf>ޥP
+*5\+7n;r*3>V.޿~AUiavFʡ=!k=Y]٭0!vRR/ߩ} TrYcmuYQnR1aV-v65PCZ fu8Z=QznoH'f`o>B0Q j]LM:#"}YF(1Ũ]٭偆+SkG[JNtFECV}i/fB37'
+Y2oꊲPa]AW19iW@XLlbJkh/@Ew(/W/'ڻ%5)3C-IRlOd<p5AVQ]+e>d\>Pz%]!l*PpHHI ZꄛdqaDa.ŀUh?j"5A T(? *3-ȲmKFq}9Y&%'!"0?!,ڕǚ>; O[~ ]YI|EEVI Q?%;CXXԮTu8zG~~)~d;B( TYWuz^Ӆ6p5c'+5]#sGȏ뷪@onyn
+"-޼jzP[sZŴX|&w~Y$+QI9Ec-#619bε;?'BZ v,ߴ4@-FAvf1 Gs#]5& (t}LTT| 7W˪>l];*
+
+ԡ?ixPs(RƩc񻷄x\(I
+MF6MBb>a[v={fyO !e0T'?ķ/
+܍V\|xΨ]P]ϛ!c8!L@y{}hԎ}'R_/*_`!_2B(a9&4ѝ _Ql{r2N%ܻ-"5BLaIj
+rT]P.Db9Ums] G
+AR^u/w>RJH'H
+BjaaA Hij ߩ,y-rٓnfŒlfLT,򢵋%o}<9æ-ynU޹i~قJ,YPf IH !&T r/OK9#:,o2ǹt5e$.0}yWBF^Y]p%(!)i/\/)s1RU *- a? ߾r#M .'HŅy2$9#zSL}-Uʼn Lť&*hZqp^郄D=x$Lƅ%ek5<z 9y5| 1 _O/cuݩ&\<z2)QaޞZ0UWF-JLXʹ탼|¸@ԴfBâ9qrk%eUwb'&l5żRnǍ uީuzA^si'ݹushbg9Fz*,&wKF%
+DV^YM)/(t֝ǒO=rN~e5>DV=~yAb^x 9x#da{5Օe7PI\j]1aZ~T-5%,5h\`X>p %7YYbm57Enݹw‘i2s o*Ss䥮H߬Cܿ[sV⢂̌ScM!k<,gB_[MY^VJ\DpQD4.0a;iKy \z] )9x8r~¥으kE7JJoWܮbQYyViɍky9ٗ.OO;rhb=;nۀLpwurk5k4TʓpBi1r8[,.0C  
+QӚ:mLN.K=Vlu9v"ԙs.g_-((dQPp5?/7'rsigN8vPo{:_/OwׅY[j*cC β`*BT5u MgY͙7suA!#ؽ7C;|ԴsMK=}dcG:x n1["7^|aPOK}$YIq!R
+qII
+SԴ3-m~d*o_M[ص'6n J$Ao%??.vϮ۶FGFl
+
+^lBysfa*rbA}bp ^
+brS4uδc0=Wy MQQ[XDGEEFo~>^</]pt#]-҄ X^
+,X|/D$Q(#%FMgZXER]ܖ/\{ڵ~u]kJK\6V3MM 5TY&Ơnb h+d(\"ȈIIkL3nj>f\;{N -&Ao,r^4n+YӍhNQ<QVJB\;
+0P> C%2w 8!B4v;Ux 3YmXX[[Ͷen6cT]m- 5eɓdBdM$L,BxQ RҲ'MVPTM700da`?U9TWS0yDYi) bqcnjfaTtK*d,vDTL|DyEIIYy
+;JJȁD9Yi) b"B#1*:`R Q~le("!))%%%JRR;-c )Lt 3j4 /BamAA
+B Àz(ԓlB?` 
+yfZ5 K@CX<D0 !
+   g%t2!CZY!PfZ
+
+
+
+
+
+ 6l#06l(*A(Е:lHQG" P6z(Ç WK_
+|%F7_@P!<
+SԵt (z:ZSĄC_CGQP71b
+2|G e{a _<ce4[9:-rqus[<
+޺٬7JJKo 8JKKn\/:{|U UeExZ]aƉMT7uٸ3żUw U.IܹX}ظQZ_ ~I&s slvaIEU{0@н5U%g _x$1~?P#;0k}?k Θa7cfp:ˁ3>/ { {Ep½eyi(̗<jk]AAG
+]]ܵ'^3U5Uʚڊz׵qMeyiUmO3ݛ\k涹Ğ T?й.<1 t؅2#Xwy]MUuUMuylȘ`?bppT{`kŶ ;042u^x /M#׍78`<l3Z8u_6\?@gw3V]Ymf ?YUZ[m>5ƓcN< f ob}67ؽŵ#|s[()pחVV׻rUV*WUk'U#coVp\skpUTկ߾)DG䉈t/M! H +C}!R)`kf!R)`kf!R[C4[l l2D
+bkf!-hbkf!-hbkf!-hbkf!-hB7h)toB[C4[S[Ix [C$1]kX"$aq2l /aq| Ct <h+Eh}ab`GS.ÅithIhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖KԶhɱtOm鎖L춅PtOp[NPtOp[O3qEA43qBLAE|EGHEM5݁t`tGEt݅xf"t@DDD*t hYr!i*a,3,(:@4/_bE)>bg`PL#!(88$$$Afuy(G30"bexXHpuyGD'$BjdbDrY"yD'&2 ;JOMND&+yN@"2s JJK0?'3-9>:" Y@bӲ*kW^hW76Te%
+3RSE3}~E&dT|;<_zD~<G;/_]QM3F6- S/.%~c'ǯ=i"'z7֗d F?69MlQǨ=W_W3?Hȿ~g}uaFB1I3~V[?ϊ:FĦU~起gH~췿v^[Me8&%k>'~~eâsV5m7/"/|||֦U9abQ4l8q
+ JF"{g^$hw~,1D:2Dn9ߢ4HD֔M&{GX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDb"VLĊX1+&bDՔD~S"?xyH;'.D|N/rJψĚ ~9ϼ~?Hwt"F"{|o=o32/>|lH L*[ݱ{x/w7W^|eYNLtg?Ǿ×o{~W?{եL$Ḩ򍧞ѫ|w7}~ꏞ{_F#&YҰyȍw_ͷ7WOt#;77d&8.e!񮢺p>G/_篾O=ཷp"W|DH2%^Xgp==SO?^|G޼ ?|{΍)LvZ"+WƦW6o=}<ط~'<=;NlmO]Q,Y"(<&9qsy='2~?>x;?:~oscYNrLxЊeKH`XTbfQͺ{>C=Vك|+_;>zbk]MQfbTXִƶ~?=myi8KV$b6YEM{p㙳nŻϝ=s {67Ue4z֠kFFq+cGGGjk)I rԁ<$:1=q=w޳׻=wnٺy]cuI^zb1Et1$$IJ)*il^߶}KGt:on[XS^!1jIWTVYSиz^PWSYVfLGfb"̜¢Ҳ镖d%G#@Mw$1ɩ̬2]ɉ1ᡎ " B&Q1q G q1Q#(
+2G ݲZ NQK,mr7ȳ8Y'[DDD)***cg7%=$M)TS)ݔar2MY'?}pg] <l6vR^ߏ6>|OD A!>&s=wA!@D8"(3R\t'H<Y6y<r@t}ggTTR̺w>Q:wDDDDT/(((,,,***...)))---+++//khhh4Yeݺu6lhmmm3m=Ww1camfvӳ||]a"=L_#
+ۆ-tdw{W.!+$1I"O<
+^#g!3E7q4qp_Ttt\%|1w"1[} zbZwtttvvvuuuwwl۶oǎvk<p:4<<<?>?:Fx <.mÖc
+s^juw.Ӟ LϊY-8{Cq\NsXY^?Ŏx6nƫa\ `gUxDFkVDbǽ<uW¼,撘EGo79 f2&vGxcX9AQ"754qF0ymԕ"2,[v;LTKA2k%j}i/ﶈ(^dV,YRO/^eݹ.>xAě)Xmbq(.X$ \q%٢8UB?dagEw;E O+Q3O81N*
+
+*MXX{"<'Nκ_"kgDgɬlq1AGKX~vq'$EEEΖ?͊-*&&jHt8//c'YY>V|Y,kC<z~7ysEg㳲rrrp̵1lyvVK--sVV Y|=AhpeҥǫX;+i!oxo/ ZDYܟ;«j+^aX>ii'Isݯ⣉ZhD>~[V\Isݯb@/
+Dd
+ϏZ:뵭_=X>Q?C틇|JwDκ?/:ӵU%\h,>oyha-S;ybbq*l7|
+ԏhYRY/3Swg"thwEvKs{N}\a-uGtv߭o/dgNY%4n߄3ok YY"5^;+~Ev[5Y WkP,Ig(7LJ~˜yvנL-/5mwb(qJYϋba<opb -,l;5-7[ ߫,.COD
+문hinp4nߎ>o yᔖ%R浳q
+EZ`Aܝ 0oΊ7z297J\
+ agxqttX=A3~oP2x%R lLL=03~׌xA0 c ZvV9+. T|3~WwʳDʼvV#.@%N~3ʛAܝ `gfkΊ7zⰦE$m_-nHA0 .>V͖Ί7zPxiW6OV↤Ke([,lI:r%$$`MI
+↤CP_͖8Ś7M˛A08٘%R0@wqB~~!i.PxvHto$%%aMI
+↤CP,o\XӊPXX(nHA0 %ޢ]b;K4[΢\;8I[Hܐ4`(q***%R㵳 QTi8I[Xܐ4`(q*::)YF
+↤CP ]D
+v6""B233E$m
+qC\ ex<;K`jgQ%Fʕ5-'i+TVVb %.C%$$!Y"uBE$m*qC\ eDvH(%.`MI
+↤CP͖Ί7gQ\i8I[Vܐ4`(q*)) )٘
+Ú Is1,:Z\XӢqBCC!i.P2TJJJll,;K`jgQ%
+B
+
+
+E$mFqC\ eT<DPP;K4[uB
+ E$m$i.P2TZZ;Kf΢V(WQQִhk֬CPqqqtALY
+*..ƚ Is1222Y"5S;*PURR5-'i+477b %.C\`vh,jrbMI
+---↤CP,KgQ"tBP2iݯ^
+֭7$ J\JHH agfk΢V(Wyy9ִ†  ys1 Y,YQ(
+媨 cI[Uܐ4`(q*'''11|͖0:Z\XӢqB[[!i.P2Tnn.;KfjgQ%
+BċyfqC\ e<<Dhh(;K4[uBQ44NVOb %.C糳Dj,EDgQ+kZ4NA0 UPP)UWW5-'i+tttb %.CDjvUBP+kZ4NV7$ J\***JNN |͖0:Z\8Ś Is1Y"5^;BV1ִWXmnqC\ epvh$E֬Y5#nțe(vHKP(
+jjj cI[a۶m↤CP,IgQfi8I[Wܐ4`(q%R6
+Z\---EVV7$ J\*++KKK[r,uAH:+.@hhcqC\ e(vH΢Pʵai8I[a׮]↤CP颳MwD(Wkk+ִh/nHA0 )tV|,
+Z\mmmXӢq!i.P2TEEEFFԯ" ,ʅ3Pi8I[a޽↤CP,2¢>(j%.cMII\ eJ5,uAH:rcM:888cs1Y"e΢DXPс5-'i+8p@ܐ4`(q*33%R ,ى5-'i+<xPܐ4`(q%R6
+Z\]]]XӢq¡C Is1~ջ0yguzm+ b %.CDʼvBPimqC\ 1lmmmvvvtt4:kuA;m60FGG Is1€,\X:aɊBV(Woo/ִhpqC\ 0`{{{]]]NNNLL,uA;'^d%m1qC\ 0 ;K4E}Y,YQ(
+ڱcI
+↤CaXvhݵkִhpqqC\ 0 ;K4΢DXPߏ5-'i+8qBܐ4`( ؘ{Zra- `MI
+'O7$ 
+Ds1
+Z\8ŚN:%nHA0qqq"0cgE$mӧOb ͑ggQ,VY
+BE$mnIܐ4`( ݽf͚xvhf,ִhpqC\ Y9t%’BP`MI
+gϞ7$ 
+455&$$._tAYi8I[ܹs↤CDs4XPuAi8I[[n7$ 
+n۶(11%;5-'i+z↤CDsY%’BPCaMI
+v!i.P_nbMI
+' Is1bg]XGtUBP+kxxkZ4NV뮻 Is1€}}}֭+))IJJ
+a-t>Obvh,bEP+kddhO}Jܐ4`( cǎ 6&''DKg8I[3!i.agγJ%+
+Z\7t'i+|7$ 
+ڵ,%%eʕ; E΢q Is1;K4wS;%+
+Z\G9s 'i+{↤CaTvh|쬤↤,_xvAdEP+kllٳxՓCa͛7WTTfg|cgmfl.;K΢8XP5>>~9l%m/↤Ca{b@?{"{gk+河D~d,XPu[ngxqksEm1€*##Y
+a-Զƒ>(nXggQKV
+BN8q뭷~3,}Mܰ4׳CajNm~Xp7%(,(jr<yn??#<"n;CatvvfffFGG; E`.m7DmY"?P΢>XbɊBV(שS>O?~Ӆ$ 
+<x.+++&&&88%_:㏳DY,VdEP+wu׽+/췾kA0<tPwwwCCCvvvlllHH;K;-.<%Z\xݘhqD ?ED ]?KX:SAY>(YEE--|>vC@L,h!YówϗΞ;wVbvh, Eg[ZZz{{O:u[LJ*
+A0ġ
+A0;K4GS;WPPf͚aM{$iCa@
+p@apvhf#G=i4WVQX Y9t6888666??kpppttk&Is%m 
+P X,/fִMJ*
+A0;K4G]tiPP:vvvݻwxxkqb q(8,`֌=tִc&Is%m %mhh8x ִGLJ*
+A0ġ
+p@a!Y"_;cbM;l4WVQX 0 ;K4]d :][[y澾> kC&Is%m 
+P XD%loo
+A0dgـȬjtv۶m$͕U`( aq@ap"YYwg׮]۠I\I[EaCa@vh.v633{ǎXӊI+o+` YpX"uvvuu屛J*
+A0dgbjg#""\.Weeekkkgggoo/ִ&Is%m 
+P 88YYtvÆ ۶mÚ$iCa@
+Y"eήXbʕ֭koovI\I[Ea1€8;K@ٖ{vwwcM$otm ,K.$:^^^܌uuuaMg4WVQX ".@P YYtk^bq
+vH˗/OKK+++[fMkkkGGִLJ*
+A(
+p@a/`g|!lcc ۱1I+i(,p(`gyljjjii):n:bMm4WVQX ".@aXpX|ta-644KXv$͕U P,\LlXXXJJJIII]]]sss[[ִ&Is%m 8 ఀ%IgkkkZ[[K-H+i(,p(`gY:lٲ5klذuk7I+i(, P8;K@٪*q
+kZQ:Ism ".@P)٤JTE,ݼ6WVQX ".@P YY XӶ$͕U P8ِĂ򺺺&i[MJ*
+A(
+p@a_Hb痕֮Yk &Is%m 8 Dj,]tl^^^iiiuu5@]g7wbq
+,:[RR".cMb4WVQX ".@P;Kfjgrrr+++m6I+i(,p(|%;K:]TTTQQQWW'^jA\I[Ea1
+
+*,,,//E֘$͕U(
+p@a,/lfffAAAYYYuu5ִt
+D\¡
+A(
+p@`gLl@@@LLLrrrfff~~~II ִ&Is%m 8 /dg|1]g\.W^^^qq1ִ&Is%m 8 !Y"^;[TT5mI\I[Ea1
+A(
+٩_dbǧfeecM[b4WVQX ".@P;KfjgWX5mI\I[Ea1
+vV|x6---++ k|bq
+ |%;K JHHHMMĚ6$iD\Bgq@CD
+v644Tݓr5I+i(,p()t6...999##kbq
+ ِq8==klbq
+ta-vuV#.CaMe4WVQX ".@aXY"5S;lٲ`tVݓ5mI\I[Ea1
+v6((Hݓ5$iD\¡
+$ONNƚ6$iD\¡
+xG\š6$iDbX/bg|ᵳxEÚ6$iY `gLҥKQ(vOLLLBBִ)&Is%m ,2ygK-H+i(,0,CXD%ήX"88X\:CݒLJ*
+ ,%R0K,A=2ִt
+DbXY"5^;|rtV݃屛J*
+AܝoZ~ ;K:+6$iD\¡%R&טּ %^jA\I[Ea1
+A,,;K:yu2I+i(,;K^;~G\šVN\I[ag|!ұxu\I[Ea1;K4wu}8,, u0I+i(,/@DKYq2;ݛ,/$u_:vwV\I[EaY"K&7WV dz@;K IgݗKJ*
+AܝF;K yge(-$i
+YY"_;+.CaM+J'oAY"etVԺym%;IgݗP`º,\LK<.+ixu_bgͪJ*
+QwDSZQ+i(8egbV4WVQXwg[Dby,H+o
+MD@1 a!b2;d{/G_t? t=
+]DtODSAݓ}alBL_pt?!tQ="Ʉ1_zgp[Bv5 hRVZy hËf7e/Fڟ-@~k/Rڟ#-@k.R iҵ /fҸz'"Ҹz'b/@sw/F һ 4wzb@/Asq/R һ4G'"@/Asq/R һ4G'"@/1?f@21>ab<_6c~8xOq:ytm<X8ݼPZ oиv?]3a2k7chSo"'EͰl}#kyx9N9=Ymck#?>/sy 77]#ٟef=ncn`ϔAycsgv?4ر?'' """"""""""""""""""""""""""""""""" """""5K/&ݦfjbRep{.1JYi$6RCх{懱'FF+"hR``
+
+ dDl;8c 18 'Ǝc Q+BU5B7W \=  OHH4$%%%''$''%DEz#< 8
+>3!!)k6nwpѱ'NpԩN~cGG|m]injQ˒\c+VXNJdJǓս$q& 8 1
++״u[j=k'NVUTVVRb"&gR3r
+ ?˃ڗShdBz^imƎ+w :|MPwyǭgo:yоunTnq5g)怱عb Rc#=ZjJT4~/9M5N.[f͚ƆD#(\Мu#dMĹl빵x#̬lX۶W=8r솏Wv빏9g畝mk+.J shc*jp>XW[U]KO2fcuaCǶW@.I,âR2K*״lҦ6֖_h,3cqfWqnl5O=Sst'X?IrV_ּasgO#Μsۻ[*s҉[cK*pNزn= ի.Hpo326%n]{O߮]}WXsgbɾz][˱"5 QfK/jhZ׊8Z77Vl<K*(_ށ?|cȩnݴnM]eI+8]a|Ml4ҭBڷtlm}vH}f]\vs;zzW:穬wUܺqfg&<:1=q]ۖ+z޺e389l|&gϫ[:tӇ?2s˪WMn1K'Źse-mWnƵ E٩񑓧oe ꦶumKN\i68p`po-xsx҉3ŬK+/[ybW]s{Ԯ*Sj=ewNf2s WU74u:kغWī8-,ohٴ8s
+H40D\7A83k6v^KNtiSqڴC><2<4{SSMq1qębniff0 sZrןxT6\~Б'NLi%5jc ɗE񂗐QXgvSvrDNKr:i,sG;6vջϕx3(ÃC 0q*%i-'.-)%=+g];GƎ܉eeQ˸\2qqzr9]CCۍӒK\9m^[[
+2&%eK8ĉ׏U榪K͵{y~sU׌><<`"'r8|%>)53e};vо[ZJ\I;SFNc߱*Ñ##mY7q]!`z/o+IA.5VħWn*u#wniOUT#0k%y>K$]Tt=׌^gMUEYFܧ18VAe
+$C>go-庾&}x׿Gi,G}Ufs+ҹMSN?<m3
+Q ϾO?of`F_<z̭ۖ\zկ̦)'/Mg>Φt4M.O|
+34%*&ԧADw?MI dr!dw?g~K9]z ӊϮ'*z ?+=4_?| |9ͬO螘=*Q9I:>3}=^ʀ]"vPN3:2"^agf1y"{} vSǒskwy?w=iܟʩ V {x%/ӈ=6]?`>vB)5^\N~H{O}믟??3llXWNHp۲`eO,Ԇ&Vw2ws9v)+'[AS޽wk]֠PNν?_ 3ٴIeqλ}zW|h4v+.yJspE>oLN.o߹kjokuAt5%o׭2_tV L~sO˽;>4S&m2_,mn/brҥKb}>tauc{woow{cu!7{k^]O}fW6w_t=4?<|wom.yYՍZ̧Dt޺}޽{wo\[MF7QWc|09ZSgWVQ2mZ췼~KGptzfaiټPb~J} ؕ-vzݳ+*WWz&G`e}skksc}ueyia.NL ُM+n]t&ˤW_֯NSٙٹlʾL`Wx*IOOPixAyQa. wh|j:;;7?77˚ex*Tb^vd"ZUkqڻn%J2E{k:W~/F'e}),jȻMط;)oxo,rv Gx|*:91>6:<4lk7n6tt#vy֎E#cv@_W,]/^fWeS1ɭo2oo`(2<22~|1Tڏ#?pļ|_L"H.^=j ۍ0auoߓ{L[U흲Э%w8U-:7L=u*ힵV{C_$+/[ZeGy)YXS_vеDS޼4ծM UWٟ.bmiY4ygDџn3/ [#,.]_\?œ֚xvQt]k{[d㥢;j;h_]@_goܹhzay>v+/p{/哙_ڐmmc^P]Sᶝ_;#-w9ʕ_p^;vҗf3~ LE;~eX?~:Y%oi+q֫[._zů.v)P~}~ԯN
+
+pʼD~u|Q(Եp6%fBwnj_
+\L5.:@vUFix@#?@iW_\d ..30UIs vMEʝvEEeJjh؁])Qq?@yѮDڟz\hET.>P+"*g( M(ӮE}Z~@v(7R<PVEF}΁]. [aO]1<B0΀v%]I;ho@NI؀ѮmK;2E\ODѮjH;&˾@$ѮjE;eX
+
+[..Ӻިml  u7ׇnJ`]YS[oTN^KJ?1iTזށ`_WkcJIu6y
+hE9$ ߬mlN v4K+zÆD[P$jk
+K\UGr!WY%7teLk싌Gxl|82^MX:$CãcjR?Wx^?${ZFT:D|SVΞ>ձhl*>i
+K".B5uOݰ4QTy%7"CԪPc{x<eS^9fN%STrjUkaK hZ5QWY%sGWym&spi`CաƖ d,12ӱAZyS
+1"1ųV&0zQ 5t*=30?;M\754wGLL͘;[k6tKYzoFN㻀644655qhWW5xfnqia6=5P,g:-,-//-̤\{5<=G^\S7MXήR2=4̙-eL\ZjM_g`x<HegWVVsMܽ? ڑu{MPML]<M
+21d *J7n 5D&gWm\[M5oj
+^rvvaie}csscm1%τy'wW2+jLut) NLFc1;Xg:OU7CmhjviTL|LZ_/D흝᮶f>ZU jQs(RЩDR+ɩ!NMWo8[^5qZF&&+k[;{{;[[!&~-NꞼԘ6Q馈F'&&'2 Mgٙl:3 &*5uMᑩڊkw[=ۻ{o
+
+tun n7fL'괊HS2UT!'EtF*KKK34VS5q]1q]qmoinn-Jepppkw˴Dtlx0mm2fWDJOiUMlԮ~7ALٜ",;`1ѴzƳ .VS륺f%{ dlbl$2809OtތE" 6{^d2Wra*t}}ccskkksDD@(C}]-5;-;wn\[^ɦS56:23ٛCdBUn]]b4VVȈq.iƜ++qֶlo-LGm"mh*5g6^Kk[LT޽+qX]^3 tj:&tDt\h/_&'%+WZ}~*QY?îo(nʹake4y)Ѹ78fU/.ۛ+K 3l6N%㱉QYM v޹Y[QT婨]:(Ӯn%RUGSY6 {ζ03lXwl\ӱсB\Ҧ"JQ)N,/a'qX$,ѝ".}zKd kxQ^SjƧ $S6s`'&[6& K8zkgxtӱ@|MLgַ|G⻶8?k"7X?v\+d޴|[% #cccBcIR\_GiBzK~c\Lff`dt r%wWowT{kɩhdOƵ&rTV-Wj]0:n'X7.!M3f:;oI4luIVU3Y^˥#2TW[[386҉{Ch ^/jd}#ӑZ7D~1QMڨkR䘴I 2d!73#Cՙbo{xږj;=?Oڐy֮D*ͦ&<x455_f޳/ ]0W75.s&AIhLd;Z3K^*UMU5%n'd 7'u&O 8?7#1ly b7wQͤxtbԌJMz2>usÒx%2p2l[ܑ&w_:+K g%Ilm]TcUVL&dttdZP~e`#;ok6۶K@$ۑXYQ]]#oh̀t|T!T$e0WV7ib\Tw9hީ?g+ -ISUMa{SΪbkL/,zpJ4\̥Sv.ah@K.Pka atvfn,+Y[.ˀxomQ oS2`FN
+Y&9K1iR S4@s[]=dkȀxt\S 󉙱%΍l>?fГ^kYfDu/423I5yNQi*/#b͚_ڮt2<lF#v]R#kfYI32'ylr=4VwSm;fZtO 35xtl(dU)4tۻC_#ϸD!cpݲ};G`DIO"@(:91>& 2{\wZedG{{Lutʄ&Uv<eӒW쓡e&S/OʥѱH7=^q4ҴڰFUL2i* h|*F'''L(ݴȰ{bc&e{g: ( e)T۰[E#cv݅fiSX$L\_Q<MMJTIofV\eu"-bR?v{r~-]>B*UԺ婲ظU_+e]_ZI2?m\Ѫd̴8uŨaOalSe4i[o`ߋS'=/4AVYt?F"=-׮j4 vMDe465.t]uY- MDԸu-7MgeRUV[[mjzn\j+1֨{[Cx>#-!5S+isen1nL3eS8UVym캖hB}&Ӳ2+snVp֍Kpo7d\ًcSk_Y7y"\Ye.ͮd{h7b-K{jۘʺB3n5L՟wI|/޼Y®foICa 3l%ށ0v~z7֡pO#4Lʬផst*]7W
+G&K?_u~%4nNӱ͔1*d[Z{ݠhgzDYM)LyffP'Y:BX+^e;o,YUUۖM~a7Cdŷ'~eWJGV͙U;Ú͸ldg5JySCbGG[}z&I0oo-,踺!]r&Ǵq'P$఩Gw滻ݻ,
+F?!Oކi/]];d{{UIBiI<Uu@ZUB-so,s)T揚揶Mv k>QYUYa]][t.LFx- m('}9efcaGcVrXrzg%I].>Z2s|y{ߗ
+um
+_ozk6M]u]UIN}N.vhKYֶ8UVD˗f٦]Yb---y+Dox;ʏ*8DY!|IZv̜-0a-v)
+'/Vpum1m ,edݳ8q\Gas<Ɵbae (meUvolxSvRb:cFHYwɵߒKGr^XIm?6G)PXVzUuXǥ鏗|mŦs JMvG]/ "2XpEUŌe=iauti:-M [e{KC&HaleI uppG[_[kbU@_3tD U#dnqm{쿳-a= 9P-/iITѰ3[{޿mIšJTe:{ITN~?<O.o{ٗtB؄lf\f][w?xt]Lz M\{$ϱspwy{I5'd7q-c
+*щi9j;Xy!vG۸r57ka;w T|ҞtNꓵr]
+[=#JMgDU^,s{<_Z-r>݋.T}Poh rwIN~xckW82KQٌGU;ǞAX㒫lwd<*&$QNTX
+nMyuru!F '94:>11>j3pNKۏ]O}rue"Qݝiq+ުvq2Emv=x^c{]1,){{ީmVka :yFy[#.I!Ɇw:1GaS+MƦƆz uo8)5M&6OzXMG ջ;6]]~d5bN,twU֨F,¡/)a7᮶Kx 
+TW1{\mt^O{}M /|w{wĘ_Y^g0YC;mhvǣSgր8:k('zN[N 2 5 P($gD*kcrCibC饚:3B)6yy"ͺ]m 
+P\n-1LME81JM]5in̳Pn-.F9iq/P|kwo`ppphxt"sh66tl,!  X{ƒ<h<|쑖C{wȟoƦ≤2-o[kKsk#D^u:N>%
+Aua]MƼsT@?4cؔuǝm O )@u꺳Krm:=N\ Yf͞ oq)5 Iw /4-Bt[ .{nnI\-`f80$mܒy;`zã=mMi]8v9=U7ؕ_q"Zl`R3 J\nnzZ‚9}x>۶^on_
+WHJNx7\ݽd #9؅m$~`$q /rYm}s/k![˳^\_e GPA&wӠ г H\6amli봻bDo} 3gr [#mPn0E/epTqN`VmuauwHNL`FuzS] DܩU5 Mj67*Wyǘ堭r9h_`(226KE[$Y<(nǫ'.`pp(2<*ffgsdT.`z\7u E"ɩt:ͤYݦ5|G1{t*JNM 䟾@5aiWǍ1蔉t2.v[ D
+\h,b6)X$]HSy`x<:H&D<H$NS!ACgE&LdF&J\&7+<<1F63Uu%
+GhTp8jZSu*i88#;㜱1TSE:;֖붺׀ofnǯ}ݝ&MFcccC}]m a 9]ckGw_Tζ&fmmȨYu$oȯi5==օL4oVY7n\vkUM]Csk͍u7_1ArY0a EH M--TsSeԷkx:TW $o0Q]"AϹY5 k fě{fV|-Bp!&~* k׮_wCf .K$ZoHCX/ [B˘aLsQU
+ert211΄d,H$d.Kf҉p&;K[X25 ? eӱ\<12Xm:g;Tl&JҙX2J6ϥ͟ųt<'ۘO/K$bd&ƒ霼ݙy ;sX&1-7zfZQ T81NSk_?g?W|g5ct. zP#)gO^>?<cL&1&x!0w_c1GsB6OCE% <xG^{SM#roSG:6{hG=☐By#clc/+;^~vO <G+;3n>;oݧ ?7 Ͽ~NOg;I2TX6mkxBx<k~{?~sfٗ?{ei4߿Z?ٓ{[013Jc'cű_ o.<m&_23_D;<8fM;7
+
+
+v
+
+K!QWeR3x+E%XUui"^{,(Bjyx]!ދ
+!ڇ*B=K !NڙB+vi%B 9^BH}o#nx]:!uNTrAy+ՑCx]!o^<R@yץ6#tx[N!
+}ۅ׹M!)!^g)!e]ufB׬IB}g=uBHx>ksBNNV:!+1.ByxVk
+יC!g׎ !ׇ3NxBy#>NiBy79^'0!xBjKuRBH}ww!]>}>!Y?B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!BHMh׎sX:$(_6T?…
+xYtEB ֶ6YbE3ͭN^V~Aro=]*zG9
+?8<
+Tz%)8 0F{;}pU%KŦщ陙)c|txP;V
+R._Nv~
+1f D:eӺ8!F:8KP
+5
+ñ
+(/5
+@nie }"qz:"wpLm +BD4u5Ϛ Զ@|z4D- ƓL&../`,L#P
+16&Ru8Z6[vV4X{ۀhdR*OM:B hñd6$cjA8& WW{ë6Bm
+SBU @M 䊰X}'j2ڊfvrl@`ޫ7IdV7677֖sX{;:
+HRp@}RT=2+뛗/_ސ~(P}R=4>Ng,fS¶ ͬ
+.}s;
+CH<_Y/ZBijZœ .,,Db̢ސl?Pi&RͭM9x8 Yp4A8Jer7vd4T\
+Y
+Z7
+@VP_Q{fv
+@5C?rd5>vU.e сb]
+K wv]z`2/3Ila
+/? J]=}}ݝm-h Bc3
+ֱhOѭ2ydl|bbBmԯ]ؠGf
+^X۾׻ oQ#'&d׿
+$/35[+`z!z:}Ni&E*i#eggGU[A0o`d\;X"-.k[Mg"{(|u啵uWvͿ/ 8_49
+T
+?9)_[ȼ8'I#G?-=tގ.cIfW}ݫepגo)VEDickT8潰ɃZ٧& ퟁ*
+VϤL5k_Zxk88X8BR wCqW.OTk}ãzpN?\ڔ
+O1’ B ]^O-)r0Pޡ4>*q!Ɣ:ԁ[/[z/:3 EW%V)3r+Y;RI֫e:g|W\}WE
+ "5kY7Яi*j-@:diG~kZ (#o9Q!=)~ 2g hOZj@:pcǫ֋5ظ
+jrO$t6~թ =z/c?mYM-ziZ5335iEgݙq}"mG&' ?#DR-Qk>d
+YͤQ@q@s7Q|Uwx?CX$F6+"~en-WN^#j=g^ᗌQOM[ZZq*
+
+@<:朞P4tu^{G5g
+
+=(~/gxP
++`r"
+_X@_%}$uClSLHOp
+lpEj7WLdl-{AZwWm[PǾ
+>=P
+
+T U
+& @S[WTpAMͪ~=ύ 5-<eyhg
+pH32 <rݭזu9#;z&f1 7s k_9_ ]O#`}U㫕jqi[[iM7Ⲁ,. P?oC !}\pVUlLUznOg>[.SX38jL32[kT9A5C/L@}/3u[{r1Xܶ_gZw:[MTK<'IC@kodHE݅*7@œ;{j~8xH-C|ɏZA.
+U~1޾Zptڼ]m3w-QuE]7)Pϳ zC; [YC{:-ʪ|~P"MRMVLo[m |qm
+ZU>3(nVzqͥZ7;^`,U~R϶1҆Q`;ėU~cj֝%*(\۹r{UQ̩7 _Tx aB!B!B!B!B!B!B!B!B!f\}7ߺՋ뾯}]ӼFg<˜dOwſGן>ZsϦ;_ƌGbLXHLƍ2x$ϘB"ɔ_sTLT֌rnH6}t6ʦsƂ:(ID2ɚ>lHĒ0t$x:I&30Oa.1D$=L3/ͤd"xL6ƿT2 '$r2tFO*c,rd,5*GaߢǏ<|ދGHU~񽧏}E/gCGw}ڮ{*y/?RLT^<W[_>M"y.U&KyߋmK帼~߫˸]}>i^y߫:{*9*я9ީbrb.{ _5ֿ~!}K_|ۻWϿw{򡯤4d1a#35nqqcU\0c{Ə&f<nᏇ>3G2r/C§
+f# ӴŽ\ɛ>tO}ߡib,Z,}wz:̡O&c#Jm[z˒ү9~_2#D*.i.F6@ ̤rFC+=xxтwOJoqȋʋl|r}|YÃ8wi3TY( ?7ٍ PM,bfi)HӴ&n#L*)Y;|/I't(U  ƐG83{T\2wx8 ge7hgj]%KK?课_1i{ҠS%92s ppT*wz'(:X>߷, ^ۏrcB$+o߿ueG&
+
+U/hm-!B!B!B!B!uB!r'E 4HoAB!B ySČHΌ'3I#d3x
+i3Mdp$ә{A`TF$siv@X1 }>n'"鄙uE̜,ޏGbd!3l$JR;>uxwQyfH2t<Wsg3I]Lc-_,ͥ*^T.eE.䲸2dG*.`Ʋ[Ì?
+d:R
+[!3CJ>4Kg&JOC?a[d%T(hYDT+l,ͤfKj{Ɋ7tJ!BBes*9ɭ/^S=gJyX;P^lIuUf\*L%sKX?v3*$7RIC\^ٜOL%bD,ݺKH*I?!+Q4VSNu)44lT:'Qپx ^\X֎DC1T(R}B*H+)q3`+CaH&tfV=PzM㟿WIt/bƋxgE(lwQ#ݳJa%oڟ}ybzѪUaJ{HMerZYk??T2kd$i­n.(ݹ $ef.~f}2>lx)uo{NJ7KxR*1*Kjy,%l:Q }2%d w6%j'AJ_m\)lJ7K_\JUOt􊯷j+%+ T9{uT~\.Yx D +DgxTZ7eH*nt=a)D>ʔ_jY+*~zԺR&ZR="V۷oY+Z̴P% |<KIbfJD<2bݤ f3YU۪*xό&2s+"lnͺe}-nU4g5\šyk{x~O~Gb_|߷UD6b<?xa͐ ύx2; lo*߅< doAG6Wwh6d.)3Iգ 2 $#TPR"TfH<L$%y-JTƩ#Ocif
+nDEI$ 3fd3G}Yos O~'B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!zBtVxVYu444x
+J:+N:AsNs^sQӨijj )˃^~=൒
+ӵ8V}EӦitiz5r!7ayPPŧk%^Qd(U֌i&5~MHr!7ayPZ~El^+:]E"FP'KKKKkkkjnjh>|y\M|X؁59E"^-J:+NWxU1 gϞ}w7OO_׿5>'y<%f)*ZIg1PNW.\jRxt}plu-P\T$k%^+-G൒
+ӵ9k%^kR\*ZIgJȫk%B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!Bycz녩V Lj`USjTZ50ժV Lj`USjRB!B!B!B!B!߿u|뻩=ֽ^?]}Eן_{ǭ/>7c~Ȉ~デFp}_qkxv ӈ}O/mCGwf<$DXHH6e)ӌ$Siv_^w_|QKws{7>j7nO>{nfT223Ft|`A5ʖ3޳_?||ӯ5:_wpO= -[?ݿزULB'V*st@6rc/W(/|/zyo]0>O$#D.m,H<Kc$kdJec?@Y#>я&?|7S?.o$X:hB!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!R Rxt%Z[gJYᵶ
+ӕkm^+!g:+NWB
+uVx^k:]듆WCYuzܥq5օ [L9Ay… \ɿ{x,*v.h<%M65-6Zmi|ǁx]xK) wYOO߂koo.,\^ž8'DgO<:+"Z&25Y
+ ҋNMWWWGkOӯ  kF41͸fBch&5SE_O >gax=6~HxOm-%bH%$ ~;^ ?zJX?A~ "# %& hORIdIpI|{vHIf$va9 QCC8NVH*<Ԡ>%7QK%5@  CP8_XXD"h4x<H$d*d2l6/iVVVVWW666677w4W?3$0.
+)HC$_ůF:#v?yAsx,-KfCH)0H@K}oz7nܼy֭[o߾sݻw?O>O?>?|ѣGO<y ?3<5{x%)$tS.I[<Ri$gxg/&~)Z~>i>0C )iM$mZ[gK=[VM k(]R+O|߈od8>Iڏv"[CVy+?G~Qj8U^kpl kkϊ_sVI_p(Y^Xƿwloر=?ׄd/+뤆+BRCxs斬%=g_'+U$-p2 ƁxQ$pZUr>[dq6pv4Ť9#guVduPP!RnU{;"OYj!XžZ[gZ{Ɓd0&C)k +W!R_G(ؗkm^kMYI;{U3
+SfepV:2d=:2CR5+=e(rb\X& PTfe*CXEr\8)`MZ:!Zj*Y聾2rjZzpRX&mww75Kip,!EEVI t˦,!UPQДtCeZ4\X醒j8Y:DCEVI
+\8)`MZ:nmmf  *CX+'9B!pR.,ً/RTf/9ZA8 'I;00FR5 MvPZT.jr\X4i@jꨨY((]
+I
+\8)`Mڱjq,}AekQQ$IpR.,uERe&hJ:2(]
+R\8)`Mډ t5KHTԬ B_PZT.jLF. &aCCC===,!QQ}AekQQdYpR.,2CRN/ a-*J|>/NʅAvjjjdd%jjj, *CXE`iiI. &h___SS5KH8iʂ2(]jI
+eA_P.jr\ Ѥ͖uJR8iʂ2(]
+vvvI
+eA_PZT.j7nܐ '‚ljD"uJR8iʂ2(]
+n޼)Nʅ؁d29??OR55ʒNcV [Q֭[r\X4iSf B_PZw۷o˅raML&DYBLr(bWԆT&Z;wIIf5KHuh֢tQ+{\8)`M|>OrjjBʤuQ+IRբIieYz.(h Z'|"NʅiRE֢tQ+OI
+>spR.,mllP` ,bWDiZ8)`Md2999yZSETZݕ &5Ki(,t+"X *CXEÇr\X4iS5KHuh֢tQ+x\8)`MڝjpTPbWDTZ'OI
+={&NʕMZjSRYQ+bWD,TZ_~)NʅA|guJRk*JEŋr\jѤf 9%vBA,VĮ`Be*JoF. Ҥqloo/5KHk*CXEo 'IKrJ4 !jEq,TZw}'NʅA͛l fے{֢tQ+~&Nʅ YBNQBM],ıPZT.j?I
+~_ʅravФ}j(f c5Z??ȅravYBNOf#D]"֢tQ+I
+׿ErYB^9vBAVĮ`Y Z-S+w(S%?o~=|ƍL0&j:
+?C.,Rf,VĮ`BewTZʅ2OoѣG7ofЬ 68fEBdKrvX*Y(*VĮ`Be$>l?w/~ɓ[nfjhYB^eE+"XıPŹ e/˧O޾}{iiizz%`=KHm,! -8>KHmyPoLHmu=\?KHmQYSA5y4} NYoL]<G7jpT<7h`,y'$~qQZAXf y%iظ{ӧOҸ(I ,@
+-Mr+U
+aE.jK1STGE^phMcuR
+ PN=,!'] ŵi\VyP.=,!'IpZjUZ,5K)qѬuliENj=يYjYBXۢqWZ.,!'$֢r*t@QZPP\ENj%0f 9=N}u,UuRTV5Kix)ͺ(IG5u^!f&X:U ;,!4\'5Ҙf 9!jVcE.j`%kVR\'Z15KBVf]:YBNf߱mToEZH%ErB,&poN-D.,5K q׬]e\pE,!$ٖqTvq`YBNȱ+ KN+N BjjXEވ)AHmpz*N BjZ 6Z%N BjZ 6Z%N B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!g;B!B!B!B!B!r
+LaUӮzvô]'r Z I!Y0}ag{l!Fb,C+zaaVHa]?7nNvzӣhG;=Nvzڽ
+ǧY+^˞̟}&o4~ښseЋLVkVrZwd:yc2.;r žg
+s$]ǫʘsel'IAݖU ][xa4c{QP2p3{6z2˓=<{ھ`Aֿ7|koͻN%Us&ġs,Y^& GiYj1Ⱥyx7x\tb<({gqG^o7-/oyU= = ܣ2\ޑ./f9ƎM`AW"DɨCXhlLMxQ.Gj=Ǝp]$n}kcǮd':U}N?]N=z_p=Ma%V]p`&7<&%ow?r kq%l6[SN,kΑioMU]6-0^knn8w'Mط6o
+K7.3Y`Y%vj5ޕ+{ѷƺ\Xr<;IExIr h8n˪ޣ((}GYG{<yј;p\;e$gX=vmx=<{ھ`Aֿ7|koͻNUs&qЎ 4y{Y\ϳ޽LƏrӺbuw~p{o yPd k,oZQ_L~?{,9z GB.w/Y,c˳j]vimTlOO9^ٹ>Vw9eq<( $N';ϋC3XQ}{AZcmks乜}[ںiVðImv/h]/?9<FOyo=}z+ЫNw/]"ISM9}6-o6.t7<>
+0' 0'8ҜM Ѝ_3z+v5<lxӣ\Os兮i
+HԗKI+ri/nvK6# n K1#w}=6AVEeDFF8ykskg=637Ə)X߂orSSɄm||==>LxӃ3ȇߛ7e<~->L6S{.6sIJu֥fbK69or6l붗KTbi(fs}_w=OM}.TANasdliu[^FČx+l{GoNGaͫzr~z\ݼsy̱9Ale1g7G<0].'NCFXC%X%Xm% 'eZfz(-ia ;tFIwCUsŗ]79[|,UѸ
+ID.~i/WҺ߳=}f$'bɴ.]~zrk^;|4Oo6:<Yi8|&E)Ch*.+9,ĕoCL(f8lK+i6=EfH
+f
+z0M`FiK[ͦʙ*aÇZ7184Ӡo3``꾣/gOmm<%Ms(A=6H{'$;o]QQ*9;+,C-.Bx؎}VxG;XZdkM6 עΛ%ErlBFQxw|]-fG0Eyҍئԣ(ĺ] :.kK꜡j.FlId`kj`Ƅ
+> p<ZjgY24=0
+7C\B@!tǛt?T~3^MD5Us(ҷPgUV)!p8
+"XL}zfPAj'^; tR#V E >,9s NTF vԅiǑ6r1(d<<%*tjQ1"GIPu(2LTAЛ=X-ĜHz<@6L.ڛxLY|sl]! M>9fŨs\p
+(3|]&dmdˬPa26<U8
+JniΆLḦY]Y8Bٴ֡**TsfBo ,/~]L , R25R*u9n8t, %~
+=Jɔ>Ta.`phv|jPU~A 
+PpSYF4@4dA҆ uQlHGǮFcVos~tݗs;kLQ6Fq2p ȊOǖ< jJdZgIUaQ#'#63-*4>0 C<OH:ehG(ĔUJ-${<WI EJFkoW,N&Q& ڹD}#aƖ,?Hփn/!;PE(4H3q"
+gbqQQJ9y=jg,&d. @L4cWגxdH7EԢRB E+vo;Y֪yV,/~[W4{FAj.@A'3%w:
+($
+ҵTi$bƆmwo
+r Gt406܈FA,[kADRo$DT;$OYgOV (&Enwvd $},:3Inf@ a 7!3sC`7w 1H.+
+oGh~w7|*.eU!Fd:r'K~ICq$PU#)P
+q
+/GS0 gs
+486 0 0 480 273.6162109 546.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 757 0 obj <</BBox[317.616 983.64 715.616 585.64]/Group 915 0 R/Length 59/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 916 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 793 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+398 0 0 398 317.6162109 585.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 758 0 obj <</BBox[385.616 858.64 653.616 814.64]/Group 917 0 R/Length 58/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 918 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 796 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+268 0 0 44 385.6162109 814.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 759 0 obj <</BBox[382.616 888.64 656.616 585.64]/Group 919 0 R/Length 59/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 920 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 799 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+274 0 0 303 382.6162109 585.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 760 0 obj <</BBox[408.0 947.0 704.0 856.0]/Group 921 0 R/Length 42/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 922 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 802 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+296 0 0 91 408 856 cm
+/Im0 Do
+Q
+ endstream endobj 761 0 obj <</BBox[246.0 1106.0 718.0 580.0]/Group 923 0 R/Length 43/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 924 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 805 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+472 0 0 526 246 580 cm
+/Im0 Do
+Q
+ endstream endobj 762 0 obj <</BBox[283.0 1022.0 796.0 520.0]/Group 925 0 R/Length 43/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 788 0 R>>/ExtGState<</GS0 926 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 808 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+513 0 0 502 283 520 cm
+/Im0 Do
+Q
+ endstream endobj 925 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 926 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 927 0 R/Type/ExtGState/ca 1.0/op false>> endobj 927 0 obj <</BC 928 0 R/G 929 0 R/S/Luminosity/Type/Mask>> endobj 928 0 obj [0.0 0.0 0.0] endobj 929 0 obj <</BBox[283.0 1022.0 796.0 520.0]/Group 930 0 R/Length 43/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 833 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+513 0 0 502 283 520 cm
+/Im0 Do
+Q
+ endstream endobj 930 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 923 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 924 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 931 0 R/Type/ExtGState/ca 1.0/op false>> endobj 931 0 obj <</BC 932 0 R/G 933 0 R/S/Luminosity/Type/Mask>> endobj 932 0 obj [0.0 0.0 0.0] endobj 933 0 obj <</BBox[246.0 1106.0 718.0 580.0]/Group 934 0 R/Length 43/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 839 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+472 0 0 526 246 580 cm
+/Im0 Do
+Q
+ endstream endobj 934 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 921 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 922 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 935 0 R/Type/ExtGState/ca 1.0/op false>> endobj 935 0 obj <</BC 936 0 R/G 937 0 R/S/Luminosity/Type/Mask>> endobj 936 0 obj [0.0 0.0 0.0] endobj 937 0 obj <</BBox[408.0 947.0 704.0 856.0]/Group 938 0 R/Length 42/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 845 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+296 0 0 91 408 856 cm
+/Im0 Do
+Q
+ endstream endobj 938 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 919 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 920 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 939 0 R/Type/ExtGState/ca 1.0/op false>> endobj 939 0 obj <</BC 940 0 R/G 941 0 R/S/Luminosity/Type/Mask>> endobj 940 0 obj [0.0 0.0 0.0] endobj 941 0 obj <</BBox[382.616 888.64 656.616 585.64]/Group 942 0 R/Length 59/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 851 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+274 0 0 303 382.6162109 585.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 942 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 917 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 918 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 943 0 R/Type/ExtGState/ca 1.0/op false>> endobj 943 0 obj <</BC 944 0 R/G 945 0 R/S/Luminosity/Type/Mask>> endobj 944 0 obj [0.0 0.0 0.0] endobj 945 0 obj <</BBox[385.616 858.64 653.616 814.64]/Group 946 0 R/Length 58/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 857 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+268 0 0 44 385.6162109 814.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 946 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 915 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 916 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 947 0 R/Type/ExtGState/ca 1.0/op false>> endobj 947 0 obj <</BC 948 0 R/G 949 0 R/S/Luminosity/Type/Mask>> endobj 948 0 obj [0.0 0.0 0.0] endobj 949 0 obj <</BBox[317.616 983.64 715.616 585.64]/Group 950 0 R/Length 59/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 863 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+398 0 0 398 317.6162109 585.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 950 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 913 0 obj <</I false/K false/S/Transparency/Type/Group>> endobj 914 0 obj <</AIS true/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 951 0 R/Type/ExtGState/ca 1.0/op false>> endobj 951 0 obj <</BC 952 0 R/G 953 0 R/S/Luminosity/Type/Mask>> endobj 952 0 obj [0.0 0.0 0.0] endobj 953 0 obj <</BBox[273.616 1026.64 759.616 546.64]/Group 954 0 R/Length 59/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 747 0 R>>/ProcSet[/PDF/ImageB]/XObject<</Im0 869 0 R>>>>/Subtype/Form>>stream
+q
+/GS0 gs
+486 0 0 480 273.6162109 546.6401367 cm
+/Im0 Do
+Q
+ endstream endobj 954 0 obj <</CS 735 0 R/I false/K false/S/Transparency/Type/Group>> endobj 746 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 955 0 R/Type/ExtGState/ca 1.0/op false>> endobj 748 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 956 0 R/Type/ExtGState/ca 1.0/op false>> endobj 749 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 957 0 R/Type/ExtGState/ca 1.0/op false>> endobj 750 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 958 0 R/Type/ExtGState/ca 1.0/op false>> endobj 751 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask 959 0 R/Type/ExtGState/ca 1.0/op false>> endobj 959 0 obj <</G 960 0 R/S/Luminosity/Type/Mask>> endobj 960 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 961 0 R/Length 84/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+15.8993082 0 0 -16.1025677 1392.8847656 296.171875 cm
+BX /Sh0 sh EX Q
+ endstream endobj 961 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 958 0 obj <</G 962 0 R/S/Luminosity/Type/Mask>> endobj 962 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 963 0 R/Length 85/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+11.9217148 0 0 -12.0797682 1392.7294922 335.4882812 cm
+BX /Sh0 sh EX Q
+ endstream endobj 963 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 957 0 obj <</G 964 0 R/S/Luminosity/Type/Mask>> endobj 964 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 965 0 R/Length 87/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+225.9361572 0 0 -225.9361572 1389.9453125 728.7958984 cm
+BX /Sh0 sh EX Q
+ endstream endobj 965 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 956 0 obj <</G 966 0 R/S/Luminosity/Type/Mask>> endobj 966 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 967 0 R/Length 83/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+7.9551892 0 0 -8.0455885 1392.7294922 366.4384766 cm
+BX /Sh0 sh EX Q
+ endstream endobj 967 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 955 0 obj <</G 968 0 R/S/Luminosity/Type/Mask>> endobj 968 0 obj <</BBox[-32768.0 32767.0 32767.0 -32768.0]/Group 969 0 R/Length 84/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ExtGState<</GS0 744 0 R>>/Shading<</Sh0 892 0 R>>>>/Subtype/Form>>stream
+q
+0 g
+/GS0 gs
+225.9359131 0 0 -225.9359131 509.96875 794.0385742 cm
+BX /Sh0 sh EX Q
+ endstream endobj 969 0 obj <</CS/DeviceGray/I false/K false/S/Transparency/Type/Group>> endobj 912 0 obj [/ICCBased 819 0 R] endobj 5 0 obj <</Intent 154 0 R/Name(Calque 1)/Type/OCG/Usage 155 0 R>> endobj 248 0 obj <</Intent 395 0 R/Name(Calque 1)/Type/OCG/Usage 396 0 R>> endobj 489 0 obj <</Intent 636 0 R/Name(Calque 1)/Type/OCG/Usage 637 0 R>> endobj 636 0 obj [/View/Design] endobj 637 0 obj <</CreatorInfo<</Creator(Adobe Illustrator 16.0)/Subtype/Artwork>>>> endobj 395 0 obj [/View/Design] endobj 396 0 obj <</CreatorInfo<</Creator(Adobe Illustrator 16.0)/Subtype/Artwork>>>> endobj 154 0 obj [/View/Design] endobj 155 0 obj <</CreatorInfo<</Creator(Adobe Illustrator 16.0)/Subtype/Artwork>>>> endobj 731 0 obj [730 0 R] endobj 970 0 obj <</CreationDate(D:20140916123631+02'00')/Creator(Adobe Illustrator CS6 \(Macintosh\))/ModDate(D:20140918115258+02'00')/Producer(Adobe PDF library 10.01)/Title(logotalerv2)>> endobj xref 0 971 0000000004 65535 f
+0000000016 00000 n
+0000000194 00000 n
+0000050669 00000 n
+0000000006 00000 f
+0000539738 00000 n
+0000000009 00000 f
+0000050726 00000 n
+0000051599 00000 n
+0000000010 00000 f
+0000000011 00000 f
+0000000012 00000 f
+0000000013 00000 f
+0000000014 00000 f
+0000000015 00000 f
+0000000016 00000 f
+0000000017 00000 f
+0000000018 00000 f
+0000000019 00000 f
+0000000020 00000 f
+0000000021 00000 f
+0000000022 00000 f
+0000000023 00000 f
+0000000024 00000 f
+0000000025 00000 f
+0000000026 00000 f
+0000000027 00000 f
+0000000028 00000 f
+0000000029 00000 f
+0000000030 00000 f
+0000000031 00000 f
+0000000032 00000 f
+0000000033 00000 f
+0000000034 00000 f
+0000000035 00000 f
+0000000036 00000 f
+0000000037 00000 f
+0000000038 00000 f
+0000000039 00000 f
+0000000040 00000 f
+0000000041 00000 f
+0000000042 00000 f
+0000000043 00000 f
+0000000044 00000 f
+0000000045 00000 f
+0000000046 00000 f
+0000000047 00000 f
+0000000048 00000 f
+0000000049 00000 f
+0000000050 00000 f
+0000000051 00000 f
+0000000052 00000 f
+0000000053 00000 f
+0000000054 00000 f
+0000000055 00000 f
+0000000056 00000 f
+0000000057 00000 f
+0000000058 00000 f
+0000000059 00000 f
+0000000060 00000 f
+0000000061 00000 f
+0000000062 00000 f
+0000000063 00000 f
+0000000064 00000 f
+0000000065 00000 f
+0000000066 00000 f
+0000000067 00000 f
+0000000068 00000 f
+0000000069 00000 f
+0000000070 00000 f
+0000000071 00000 f
+0000000072 00000 f
+0000000073 00000 f
+0000000074 00000 f
+0000000075 00000 f
+0000000076 00000 f
+0000000077 00000 f
+0000000078 00000 f
+0000000079 00000 f
+0000000080 00000 f
+0000000081 00000 f
+0000000082 00000 f
+0000000083 00000 f
+0000000084 00000 f
+0000000085 00000 f
+0000000086 00000 f
+0000000087 00000 f
+0000000088 00000 f
+0000000089 00000 f
+0000000090 00000 f
+0000000091 00000 f
+0000000092 00000 f
+0000000093 00000 f
+0000000094 00000 f
+0000000095 00000 f
+0000000096 00000 f
+0000000097 00000 f
+0000000098 00000 f
+0000000099 00000 f
+0000000100 00000 f
+0000000101 00000 f
+0000000102 00000 f
+0000000103 00000 f
+0000000104 00000 f
+0000000105 00000 f
+0000000106 00000 f
+0000000107 00000 f
+0000000108 00000 f
+0000000109 00000 f
+0000000110 00000 f
+0000000111 00000 f
+0000000112 00000 f
+0000000113 00000 f
+0000000114 00000 f
+0000000115 00000 f
+0000000116 00000 f
+0000000117 00000 f
+0000000118 00000 f
+0000000119 00000 f
+0000000120 00000 f
+0000000121 00000 f
+0000000122 00000 f
+0000000123 00000 f
+0000000124 00000 f
+0000000125 00000 f
+0000000126 00000 f
+0000000127 00000 f
+0000000128 00000 f
+0000000129 00000 f
+0000000130 00000 f
+0000000131 00000 f
+0000000132 00000 f
+0000000133 00000 f
+0000000134 00000 f
+0000000135 00000 f
+0000000136 00000 f
+0000000137 00000 f
+0000000138 00000 f
+0000000139 00000 f
+0000000140 00000 f
+0000000141 00000 f
+0000000142 00000 f
+0000000143 00000 f
+0000000144 00000 f
+0000000145 00000 f
+0000000146 00000 f
+0000000147 00000 f
+0000000148 00000 f
+0000000149 00000 f
+0000000150 00000 f
+0000000151 00000 f
+0000000152 00000 f
+0000000153 00000 f
+0000000156 00000 f
+0000540197 00000 n
+0000540229 00000 n
+0000000157 00000 f
+0000000158 00000 f
+0000000159 00000 f
+0000000160 00000 f
+0000000161 00000 f
+0000000162 00000 f
+0000000163 00000 f
+0000000164 00000 f
+0000000165 00000 f
+0000000166 00000 f
+0000000167 00000 f
+0000000168 00000 f
+0000000169 00000 f
+0000000170 00000 f
+0000000171 00000 f
+0000000172 00000 f
+0000000173 00000 f
+0000000174 00000 f
+0000000175 00000 f
+0000000176 00000 f
+0000000177 00000 f
+0000000178 00000 f
+0000000179 00000 f
+0000000180 00000 f
+0000000181 00000 f
+0000000182 00000 f
+0000000183 00000 f
+0000000184 00000 f
+0000000185 00000 f
+0000000186 00000 f
+0000000187 00000 f
+0000000188 00000 f
+0000000189 00000 f
+0000000190 00000 f
+0000000191 00000 f
+0000000192 00000 f
+0000000193 00000 f
+0000000194 00000 f
+0000000195 00000 f
+0000000196 00000 f
+0000000197 00000 f
+0000000198 00000 f
+0000000199 00000 f
+0000000200 00000 f
+0000000201 00000 f
+0000000202 00000 f
+0000000203 00000 f
+0000000204 00000 f
+0000000205 00000 f
+0000000206 00000 f
+0000000207 00000 f
+0000000208 00000 f
+0000000209 00000 f
+0000000210 00000 f
+0000000211 00000 f
+0000000212 00000 f
+0000000213 00000 f
+0000000214 00000 f
+0000000215 00000 f
+0000000216 00000 f
+0000000217 00000 f
+0000000218 00000 f
+0000000219 00000 f
+0000000220 00000 f
+0000000221 00000 f
+0000000222 00000 f
+0000000223 00000 f
+0000000224 00000 f
+0000000225 00000 f
+0000000226 00000 f
+0000000227 00000 f
+0000000228 00000 f
+0000000229 00000 f
+0000000230 00000 f
+0000000231 00000 f
+0000000232 00000 f
+0000000233 00000 f
+0000000234 00000 f
+0000000235 00000 f
+0000000236 00000 f
+0000000237 00000 f
+0000000238 00000 f
+0000000239 00000 f
+0000000240 00000 f
+0000000241 00000 f
+0000000242 00000 f
+0000000243 00000 f
+0000000244 00000 f
+0000000245 00000 f
+0000000246 00000 f
+0000000247 00000 f
+0000000249 00000 f
+0000539811 00000 n
+0000000250 00000 f
+0000000251 00000 f
+0000000252 00000 f
+0000000253 00000 f
+0000000254 00000 f
+0000000255 00000 f
+0000000256 00000 f
+0000000257 00000 f
+0000000258 00000 f
+0000000259 00000 f
+0000000260 00000 f
+0000000261 00000 f
+0000000262 00000 f
+0000000263 00000 f
+0000000264 00000 f
+0000000265 00000 f
+0000000266 00000 f
+0000000267 00000 f
+0000000268 00000 f
+0000000269 00000 f
+0000000270 00000 f
+0000000271 00000 f
+0000000272 00000 f
+0000000273 00000 f
+0000000274 00000 f
+0000000275 00000 f
+0000000276 00000 f
+0000000277 00000 f
+0000000278 00000 f
+0000000279 00000 f
+0000000280 00000 f
+0000000281 00000 f
+0000000282 00000 f
+0000000283 00000 f
+0000000284 00000 f
+0000000285 00000 f
+0000000286 00000 f
+0000000287 00000 f
+0000000288 00000 f
+0000000289 00000 f
+0000000290 00000 f
+0000000291 00000 f
+0000000292 00000 f
+0000000293 00000 f
+0000000294 00000 f
+0000000295 00000 f
+0000000296 00000 f
+0000000297 00000 f
+0000000298 00000 f
+0000000299 00000 f
+0000000300 00000 f
+0000000301 00000 f
+0000000302 00000 f
+0000000303 00000 f
+0000000304 00000 f
+0000000305 00000 f
+0000000306 00000 f
+0000000307 00000 f
+0000000308 00000 f
+0000000309 00000 f
+0000000310 00000 f
+0000000311 00000 f
+0000000312 00000 f
+0000000313 00000 f
+0000000314 00000 f
+0000000315 00000 f
+0000000316 00000 f
+0000000317 00000 f
+0000000318 00000 f
+0000000319 00000 f
+0000000320 00000 f
+0000000321 00000 f
+0000000322 00000 f
+0000000323 00000 f
+0000000324 00000 f
+0000000325 00000 f
+0000000326 00000 f
+0000000327 00000 f
+0000000328 00000 f
+0000000329 00000 f
+0000000330 00000 f
+0000000331 00000 f
+0000000332 00000 f
+0000000333 00000 f
+0000000334 00000 f
+0000000335 00000 f
+0000000336 00000 f
+0000000337 00000 f
+0000000338 00000 f
+0000000339 00000 f
+0000000340 00000 f
+0000000341 00000 f
+0000000342 00000 f
+0000000343 00000 f
+0000000344 00000 f
+0000000345 00000 f
+0000000346 00000 f
+0000000347 00000 f
+0000000348 00000 f
+0000000349 00000 f
+0000000350 00000 f
+0000000351 00000 f
+0000000352 00000 f
+0000000353 00000 f
+0000000354 00000 f
+0000000355 00000 f
+0000000356 00000 f
+0000000357 00000 f
+0000000358 00000 f
+0000000359 00000 f
+0000000360 00000 f
+0000000361 00000 f
+0000000362 00000 f
+0000000363 00000 f
+0000000364 00000 f
+0000000365 00000 f
+0000000366 00000 f
+0000000367 00000 f
+0000000368 00000 f
+0000000369 00000 f
+0000000370 00000 f
+0000000371 00000 f
+0000000372 00000 f
+0000000373 00000 f
+0000000374 00000 f
+0000000375 00000 f
+0000000376 00000 f
+0000000377 00000 f
+0000000378 00000 f
+0000000379 00000 f
+0000000380 00000 f
+0000000381 00000 f
+0000000382 00000 f
+0000000383 00000 f
+0000000384 00000 f
+0000000385 00000 f
+0000000386 00000 f
+0000000387 00000 f
+0000000388 00000 f
+0000000389 00000 f
+0000000390 00000 f
+0000000391 00000 f
+0000000392 00000 f
+0000000393 00000 f
+0000000394 00000 f
+0000000397 00000 f
+0000540079 00000 n
+0000540111 00000 n
+0000000398 00000 f
+0000000399 00000 f
+0000000400 00000 f
+0000000401 00000 f
+0000000402 00000 f
+0000000403 00000 f
+0000000404 00000 f
+0000000405 00000 f
+0000000406 00000 f
+0000000407 00000 f
+0000000408 00000 f
+0000000409 00000 f
+0000000410 00000 f
+0000000411 00000 f
+0000000412 00000 f
+0000000413 00000 f
+0000000414 00000 f
+0000000415 00000 f
+0000000416 00000 f
+0000000417 00000 f
+0000000418 00000 f
+0000000419 00000 f
+0000000420 00000 f
+0000000421 00000 f
+0000000422 00000 f
+0000000423 00000 f
+0000000424 00000 f
+0000000425 00000 f
+0000000426 00000 f
+0000000427 00000 f
+0000000428 00000 f
+0000000429 00000 f
+0000000430 00000 f
+0000000431 00000 f
+0000000432 00000 f
+0000000433 00000 f
+0000000434 00000 f
+0000000435 00000 f
+0000000436 00000 f
+0000000437 00000 f
+0000000438 00000 f
+0000000439 00000 f
+0000000440 00000 f
+0000000441 00000 f
+0000000442 00000 f
+0000000443 00000 f
+0000000444 00000 f
+0000000445 00000 f
+0000000446 00000 f
+0000000447 00000 f
+0000000448 00000 f
+0000000449 00000 f
+0000000450 00000 f
+0000000451 00000 f
+0000000452 00000 f
+0000000453 00000 f
+0000000454 00000 f
+0000000455 00000 f
+0000000456 00000 f
+0000000457 00000 f
+0000000458 00000 f
+0000000459 00000 f
+0000000460 00000 f
+0000000461 00000 f
+0000000462 00000 f
+0000000463 00000 f
+0000000464 00000 f
+0000000465 00000 f
+0000000466 00000 f
+0000000467 00000 f
+0000000468 00000 f
+0000000469 00000 f
+0000000470 00000 f
+0000000471 00000 f
+0000000472 00000 f
+0000000473 00000 f
+0000000474 00000 f
+0000000475 00000 f
+0000000476 00000 f
+0000000477 00000 f
+0000000478 00000 f
+0000000479 00000 f
+0000000480 00000 f
+0000000481 00000 f
+0000000482 00000 f
+0000000483 00000 f
+0000000484 00000 f
+0000000485 00000 f
+0000000486 00000 f
+0000000487 00000 f
+0000000488 00000 f
+0000000000 00000 f
+0000539886 00000 n
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000539961 00000 n
+0000539993 00000 n
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000000000 00000 f
+0000255776 00000 n
+0000540315 00000 n
+0000526925 00000 n
+0000530281 00000 n
+0000272389 00000 n
+0000072965 00000 n
+0000080180 00000 n
+0000077135 00000 n
+0000078917 00000 n
+0000078454 00000 n
+0000076711 00000 n
+0000076244 00000 n
+0000075807 00000 n
+0000072621 00000 n
+0000268801 00000 n
+0000268915 00000 n
+0000536963 00000 n
+0000100986 00000 n
+0000537080 00000 n
+0000537197 00000 n
+0000537314 00000 n
+0000537431 00000 n
+0000255969 00000 n
+0000257133 00000 n
+0000257476 00000 n
+0000254768 00000 n
+0000530345 00000 n
+0000530669 00000 n
+0000530992 00000 n
+0000531314 00000 n
+0000531637 00000 n
+0000531937 00000 n
+0000532239 00000 n
+0000058286 00000 n
+0000058829 00000 n
+0000064795 00000 n
+0000065059 00000 n
+0000065376 00000 n
+0000071367 00000 n
+0000071636 00000 n
+0000071964 00000 n
+0000072239 00000 n
+0000052478 00000 n
+0000055983 00000 n
+0000269032 00000 n
+0000269149 00000 n
+0000269266 00000 n
+0000269383 00000 n
+0000269500 00000 n
+0000056047 00000 n
+0000056374 00000 n
+0000056701 00000 n
+0000057027 00000 n
+0000057354 00000 n
+0000057664 00000 n
+0000057975 00000 n
+0000526888 00000 n
+0000199803 00000 n
+0000081996 00000 n
+0000228123 00000 n
+0000199867 00000 n
+0000155074 00000 n
+0000178049 00000 n
+0000155138 00000 n
+0000151139 00000 n
+0000153025 00000 n
+0000151203 00000 n
+0000140569 00000 n
+0000146002 00000 n
+0000140633 00000 n
+0000126432 00000 n
+0000133614 00000 n
+0000126496 00000 n
+0000101099 00000 n
+0000114198 00000 n
+0000101163 00000 n
+0000080732 00000 n
+0000091259 00000 n
+0000080796 00000 n
+0000080227 00000 n
+0000079327 00000 n
+0000078963 00000 n
+0000078501 00000 n
+0000077183 00000 n
+0000076758 00000 n
+0000076291 00000 n
+0000075854 00000 n
+0000072668 00000 n
+0000073002 00000 n
+0000073158 00000 n
+0000076140 00000 n
+0000076586 00000 n
+0000077043 00000 n
+0000078037 00000 n
+0000078795 00000 n
+0000079247 00000 n
+0000080558 00000 n
+0000082042 00000 n
+0000091204 00000 n
+0000091375 00000 n
+0000091441 00000 n
+0000091472 00000 n
+0000091749 00000 n
+0000091824 00000 n
+0000102529 00000 n
+0000114314 00000 n
+0000114380 00000 n
+0000114411 00000 n
+0000114688 00000 n
+0000114763 00000 n
+0000127223 00000 n
+0000133730 00000 n
+0000133796 00000 n
+0000133827 00000 n
+0000134103 00000 n
+0000134178 00000 n
+0000141446 00000 n
+0000146118 00000 n
+0000146184 00000 n
+0000146215 00000 n
+0000146508 00000 n
+0000146583 00000 n
+0000151556 00000 n
+0000153141 00000 n
+0000153207 00000 n
+0000153238 00000 n
+0000153530 00000 n
+0000153605 00000 n
+0000156876 00000 n
+0000178165 00000 n
+0000178231 00000 n
+0000178262 00000 n
+0000178555 00000 n
+0000178630 00000 n
+0000202059 00000 n
+0000228239 00000 n
+0000228305 00000 n
+0000228336 00000 n
+0000228629 00000 n
+0000228704 00000 n
+0000254922 00000 n
+0000255143 00000 n
+0000255262 00000 n
+0000255367 00000 n
+0000255458 00000 n
+0000255549 00000 n
+0000255655 00000 n
+0000255851 00000 n
+0000255883 00000 n
+0000268483 00000 n
+0000268571 00000 n
+0000265290 00000 n
+0000257856 00000 n
+0000258107 00000 n
+0000265561 00000 n
+0000271957 00000 n
+0000271527 00000 n
+0000271093 00000 n
+0000270661 00000 n
+0000269617 00000 n
+0000269672 00000 n
+0000269970 00000 n
+0000270048 00000 n
+0000270205 00000 n
+0000270426 00000 n
+0000270501 00000 n
+0000270581 00000 n
+0000270716 00000 n
+0000271015 00000 n
+0000271148 00000 n
+0000271449 00000 n
+0000271582 00000 n
+0000271879 00000 n
+0000272012 00000 n
+0000272311 00000 n
+0000272465 00000 n
+0000272712 00000 n
+0000273723 00000 n
+0000284180 00000 n
+0000349769 00000 n
+0000415358 00000 n
+0000480947 00000 n
+0000539701 00000 n
+0000536321 00000 n
+0000536385 00000 n
+0000535680 00000 n
+0000535744 00000 n
+0000535040 00000 n
+0000535104 00000 n
+0000534399 00000 n
+0000534463 00000 n
+0000533781 00000 n
+0000533845 00000 n
+0000533161 00000 n
+0000533225 00000 n
+0000532541 00000 n
+0000532605 00000 n
+0000532721 00000 n
+0000532787 00000 n
+0000532818 00000 n
+0000533086 00000 n
+0000533341 00000 n
+0000533407 00000 n
+0000533438 00000 n
+0000533706 00000 n
+0000533961 00000 n
+0000534027 00000 n
+0000534058 00000 n
+0000534324 00000 n
+0000534579 00000 n
+0000534645 00000 n
+0000534676 00000 n
+0000534965 00000 n
+0000535220 00000 n
+0000535286 00000 n
+0000535317 00000 n
+0000535605 00000 n
+0000535860 00000 n
+0000535926 00000 n
+0000535957 00000 n
+0000536246 00000 n
+0000536501 00000 n
+0000536567 00000 n
+0000536598 00000 n
+0000536888 00000 n
+0000539271 00000 n
+0000538842 00000 n
+0000538409 00000 n
+0000537978 00000 n
+0000537548 00000 n
+0000537603 00000 n
+0000537900 00000 n
+0000538033 00000 n
+0000538331 00000 n
+0000538464 00000 n
+0000538764 00000 n
+0000538897 00000 n
+0000539193 00000 n
+0000539326 00000 n
+0000539623 00000 n
+0000540342 00000 n
+trailer <</Size 971/Root 1 0 R/Info 970 0 R/ID[<07511D2C75694DC89622C9F4B35B7624><8DE5252BE861447E9DAABAECF80D68A1>]>> startxref 540533 %%EOF \ No newline at end of file
diff --git a/doc/logos/eps/icon_taler.eps b/doc/logos/eps/icon_taler.eps
new file mode 100644
index 000000000..79ab648a6
--- /dev/null
+++ b/doc/logos/eps/icon_taler.eps
Binary files differ
diff --git a/doc/logos/eps/logo_taler.eps b/doc/logos/eps/logo_taler.eps
new file mode 100644
index 000000000..343128499
--- /dev/null
+++ b/doc/logos/eps/logo_taler.eps
Binary files differ
diff --git a/doc/logos/fonts/OldNewspaperTypes.ttf b/doc/logos/fonts/OldNewspaperTypes.ttf
new file mode 100755
index 000000000..7b9cf31b9
--- /dev/null
+++ b/doc/logos/fonts/OldNewspaperTypes.ttf
Binary files differ
diff --git a/doc/logos/fonts/perpetue/Perpetua Bold Italic.ttf b/doc/logos/fonts/perpetue/Perpetua Bold Italic.ttf
new file mode 100644
index 000000000..3882fe928
--- /dev/null
+++ b/doc/logos/fonts/perpetue/Perpetua Bold Italic.ttf
Binary files differ
diff --git a/doc/logos/fonts/perpetue/Perpetua Bold.ttf b/doc/logos/fonts/perpetue/Perpetua Bold.ttf
new file mode 100644
index 000000000..c73833dbb
--- /dev/null
+++ b/doc/logos/fonts/perpetue/Perpetua Bold.ttf
Binary files differ
diff --git a/doc/logos/fonts/perpetue/Perpetua Italic.ttf b/doc/logos/fonts/perpetue/Perpetua Italic.ttf
new file mode 100644
index 000000000..e4f295ed1
--- /dev/null
+++ b/doc/logos/fonts/perpetue/Perpetua Italic.ttf
Binary files differ
diff --git a/doc/logos/fonts/perpetue/Perpetua.ttf b/doc/logos/fonts/perpetue/Perpetua.ttf
new file mode 100644
index 000000000..846b3dca6
--- /dev/null
+++ b/doc/logos/fonts/perpetue/Perpetua.ttf
Binary files differ
diff --git a/doc/logos/fonts/smoth_bight/End User Licence Agreement.txt b/doc/logos/fonts/smoth_bight/End User Licence Agreement.txt
new file mode 100755
index 000000000..04209197c
--- /dev/null
+++ b/doc/logos/fonts/smoth_bight/End User Licence Agreement.txt
@@ -0,0 +1,133 @@
+End User License Agreement and Software Inclusion Agreement
+
+
+"Purchaser" and "User" may be used interchangeably in this agreement.
+
+
+The official release page is at kustren.deviantart.com/
+
+or
+
+
+
+ Copyright 2013 Kustren
+ Trademark 2013 Kustren licence
+ Commercial distribution, rendering and printing of the font and derived work is prohibited.
+
+
+
+
+
+Usage
+
+Smoth-Bight is freeware Font is free to use for personal and commercial purposes. No payment is necessary, and there is no limit to the amount of prints, pages, or other medium to be produced using them. However, you cannot offer the font for commercial sale, or offer for direct download. The inclusion othe font name and/or site URL in the credits or documentation when it is used is appreciated, but this is not mandatory.
+My font is for personal and commercial use, can be used for any type of work, while nature, neither will be able to be modified in any kind of way.
+This license is multipurpose, my font can be used on multiple computers at once.
+
+
+
+
+
+Payment
+
+Payment is not required for the use of kustren's freeware Font.
+
+
+
+Support
+
+If you experience problems with any Kustren's Freeware font (such as spacing issues or missing characters), please verify that you have the correct and current version of the fonts. In the case of Freeware font, downloading the font directly from the http://kustren.deviantart.com/ site will ensure that the font files have not been altered.
+
+
+
+
+Copyright (c) 2014 by Kustren. All rights reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/logos/fonts/smoth_bight/Licencia de la fuente - Version en Espa§ol.txt b/doc/logos/fonts/smoth_bight/Licencia de la fuente - Version en Espa§ol.txt
new file mode 100755
index 000000000..39833a36a
--- /dev/null
+++ b/doc/logos/fonts/smoth_bight/Licencia de la fuente - Version en Espa§ol.txt
@@ -0,0 +1,77 @@
+Contrato de licencia de usuario final e Inclusin del acuerdo de Software
+
+"Comprador " y "Usuario " se pueden utilizar indistintamente en el presente acuerdo .
+
+
+La pgina oficial de lanzamiento de la fuente es:
+http://kustren.deviantart.com/
+
+
+
+ Derechos de autor 2013 Kustren
+ Marcas 2013 licencia Kustren
+ Se prohbe la distribucin comercial, la representacin y la impresin de la fuente y el trabajo derivado .
+
+
+
+
+
+Uso
+
+La fuente de Kustren llamada Smoth-Bight es freware en su mayoria, de uso libre, pero solo para fines personales y/o comerciales, no hay limite en la cantidad de copias realizadas de la mis ma fuente en los pcs que usted utilice.
+Sin embargo no pueden ofrecer esta tipografia ni como suyas y/o comercializarla, tampoco estara permitida la descarga directa en otras paginas web, a menos que sea con consentimiento de su creador Kustren.
+Si usted usa mi fuente deme creditos por ello y/o coloque la pagina web mia, esto es de agradecer.
+
+licencia del derecho de uso
+
+Esta fuente es para uso personal y/o comercial, puede ser usadas para cualquier tipo de trabajo, pero no podra ser modificadas de ningun tipo de manera.
+Recuerden que si ustedes compran alguna tipografia no se convierten en propietariuo de la fuente, solo obtienen la licencia de uso, es decir la misma que estan leyendo.
+Esta licencia es de multiuso, mi fuente pueden ser usadas en multiples computadores a la vez.
+
+
+
+
+
+pago
+
+El pago no es necesaria para el uso de esta fuente de software gratuito creada por kustren.
+
+
+
+apoyo
+
+Si tienen problema con la fuente (como problemas de espaciado o caracteres que faltan ) , por favor, compruebe que tiene la versin correcta y actualizada de la fuente. En el caso de fuentes de dominio pblico , la descarga de la fuente directamente desde el sitio http://kustren.deviantart.com/ se asegurar de que los archivos de fuentes no han sido alterados.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/logos/fonts/smoth_bight/Smoth-Bight - Por Kustren.otf b/doc/logos/fonts/smoth_bight/Smoth-Bight - Por Kustren.otf
new file mode 100755
index 000000000..33fd97934
--- /dev/null
+++ b/doc/logos/fonts/smoth_bight/Smoth-Bight - Por Kustren.otf
Binary files differ
diff --git a/doc/logos/fonts/smoth_bight/Smoth-Bight Italic - Por Kustren.otf b/doc/logos/fonts/smoth_bight/Smoth-Bight Italic - Por Kustren.otf
new file mode 100755
index 000000000..5fb25e646
--- /dev/null
+++ b/doc/logos/fonts/smoth_bight/Smoth-Bight Italic - Por Kustren.otf
Binary files differ
diff --git a/doc/logos/ico/favicon1616.ico b/doc/logos/ico/favicon1616.ico
new file mode 100644
index 000000000..141b93d3f
--- /dev/null
+++ b/doc/logos/ico/favicon1616.ico
Binary files differ
diff --git a/doc/logos/ico/favicon4848.ico b/doc/logos/ico/favicon4848.ico
new file mode 100644
index 000000000..3c04b1266
--- /dev/null
+++ b/doc/logos/ico/favicon4848.ico
Binary files differ
diff --git a/doc/logos/png/icon_taler.png b/doc/logos/png/icon_taler.png
new file mode 100644
index 000000000..b1dd9a587
--- /dev/null
+++ b/doc/logos/png/icon_taler.png
Binary files differ
diff --git a/doc/logos/png/logo_taler.png b/doc/logos/png/logo_taler.png
new file mode 100644
index 000000000..bdffc3c9c
--- /dev/null
+++ b/doc/logos/png/logo_taler.png
Binary files differ
diff --git a/doc/paper/.latexmkrc b/doc/paper/.latexmkrc
new file mode 100644
index 000000000..16bc358f0
--- /dev/null
+++ b/doc/paper/.latexmkrc
@@ -0,0 +1,15 @@
+add_cus_dep('glo', 'gls', 0, 'run_makeglossaries');
+add_cus_dep('acn', 'acr', 0, 'run_makeglossaries');
+
+sub run_makeglossaries {
+ if ( $silent ) {
+ system "makeglossaries -q '$_[0]'";
+ }
+ else {
+ system "makeglossaries '$_[0]'";
+ };
+}
+
+push @generated_exts, 'glo', 'gls', 'glg';
+push @generated_exts, 'acn', 'acr', 'alg';
+$clean_ext .= ' %R.ist %R.xdy';
diff --git a/doc/paper/taler.bib b/doc/paper/taler.bib
new file mode 100644
index 000000000..ce5b1cb11
--- /dev/null
+++ b/doc/paper/taler.bib
@@ -0,0 +1,94 @@
+@article{nakamoto2008bitcoin,
+ title={Bitcoin: A peer-to-peer electronic cash system},
+ author={Nakamoto, Satoshi},
+ year={2008}
+}
+
+@Article{blum1981,
+ author = {Manuel Blum},
+ title = {Coin Flipping by Telephone},
+ journal = {CRYPTO},
+ year = {1981},
+ pages = {11-15},
+}
+
+@inproceedings{chaum1990untraceable,
+ title={Untraceable electronic cash},
+ author={Chaum, David and Fiat, Amos and Naor, Moni},
+ booktitle={Proceedings on Advances in cryptology},
+ pages={319--327},
+ year={1990},
+ organization={Springer-Verlag New York, Inc.}
+}
+
+@inproceedings{chaum1983blind,
+ title={Blind signatures for untraceable payments},
+ author={Chaum, David},
+ booktitle={Advances in cryptology},
+ pages={199--203},
+ year={1983},
+ organization={Springer}
+}
+
+@inproceedings{rivest2004peppercoin,
+ title={Peppercoin micropayments},
+ author={Rivest, Ronald L},
+ booktitle={Financial Cryptography},
+ pages={2--8},
+ year={2004},
+ organization={Springer}
+}
+
+@inproceedings{miers2013zerocoin,
+ title={Zerocoin: Anonymous distributed e-cash from bitcoin},
+ author={Miers, Ian and Garman, Christina and Green, Matthew and Rubin, Aviel D},
+ booktitle={Security and Privacy (SP), 2013 IEEE Symposium on},
+ pages={397--411},
+ year={2013},
+ organization={IEEE}
+}
+
+@inproceedings{selby2004analyzing,
+ title={Analyzing the Success and Failure of Recent e-Payment Schemes},
+ author={Selby, Jack R},
+ booktitle={Financial Cryptography},
+ pages={1--1},
+ year={2004},
+ organization={Springer}
+}
+
+@misc{brands1993efficient,
+ title={An efficient off-line electronic cash system based on the representation problem},
+ author={Brands, Stefan A},
+ year={1993},
+ publisher={Centrum voor Wiskunde en Informatica}
+}
+
+@article{dent2008extensions,
+ title={Extensions to Chaum's Blind Signature Scheme and OpenCoin Requirements},
+ author={Dent, AW and Paterson, KG and Wild, PR},
+ year={2008}
+}
+
+@article{dent2008preliminary,
+ title={Preliminary Report on Chaum's Online E-Cash Architecture},
+ author={Dent, AW and Paterson, KG and Wild, PR},
+ journal={Royal Holloway, University of London},
+ year={2008}
+}
+
+
+
+@inproceedings{tor-design,
+ title = {Tor: The Second-Generation Onion Router},
+ author = {Roger Dingledine and Nick Mathewson and Paul Syverson},
+ booktitle = {Proceedings of the 13th USENIX Security Symposium},
+ year = {2004},
+ month = {August},
+ www_important = {1},
+ www_tags = {selected},
+ www_html_url = {https://www.torproject.org/svn/trunk/doc/design-paper/tor-design.html},
+ www_pdf_url = {https://www.torproject.org/svn/trunk/doc/design-paper/tor-design.pdf},
+ www_section = {Anonymous communication},
+}
+
diff --git a/doc/paper/taler.tex b/doc/paper/taler.tex
new file mode 100644
index 000000000..7a71d7636
--- /dev/null
+++ b/doc/paper/taler.tex
@@ -0,0 +1,995 @@
+\documentclass{llncs}
+%\usepackage[margin=1in,a4paper]{geometry}
+\usepackage[T1]{fontenc}
+\usepackage{palatino}
+\usepackage{xspace}
+\usepackage{microtype}
+\usepackage{tikz}
+\usepackage{amsmath,amssymb}
+\usepackage{enumitem}
+\usetikzlibrary{shapes,arrows}
+\usetikzlibrary{positioning}
+\usetikzlibrary{calc}
+
+
+
+% Terminology:
+% - SEPA-transfer -- avoid 'SEPA transaction' as we use
+% 'transaction' already when we talk about taxable
+% transfers of Taler coins and database 'transactions'.
+% - wallet = coins at customer
+% - reserve = currency entrusted to mint waiting for withdrawl
+% - deposit = SEPA to mint
+% - withdrawl = mint to customer
+% - spending = customer to merchant
+% - redeeming = merchant to mint (and then mint SEPA to merchant)
+% - refreshing = customer-mint-customer
+% - dirty coin = coin with exposed public key
+% - fresh coin = coin that was refreshed or is new
+% - coin signing key = mint's online key used to (blindly) sign coin
+% - message signing key = mint's online key to sign mint messages
+% - mint master key = mint's key used to sign other mint keys
+% - owner = entity that knows coin private key
+% - transaction = coin ownership transfer that should be taxed
+% - sharing = coin copying that should not be taxed
+
+
+\title{Taler: Taxable Anonymous Libre Electronic Reserves}
+
+\begin{document}
+\mainmatter
+
+%\author{Florian Dold \and Sree Harsha Totakura \and Benedikt M\"uller \and Christian Grothoff}
+%\institute{The GNUnet Project}
+
+
+\maketitle
+
+\begin{abstract}
+This paper introduces Taler, a Chaum-style digital currency using
+blind signatures that enables anonymous payments while ensuring that
+entities that receive payments are auditable and thus taxable. Taler
+differs from Chaum's original proposal in that customers can never defraud anyone,
+merchants can only fail to deliver the merchandise to the customer,
+and mints can be fully audited. Consequently, enforcement of honest
+behavior is better and more timely than with Chaum, and is at least as
+strict as with legacy credit card payment systems that do not provide
+for privacy. Furthermore, Taler allows fractional and incremental
+payments, and even in this case is still able to guarantee
+unlinkability of transactions via a new coin refreshing protocol.
+Finally, Taler also supports microdonations using probabilistic
+transactions. We argue that Taler provides a secure digital currency
+for modern liberal societies as it is a flexible, libre and efficient
+protocol and adequately balances the state's need for monetary control
+with the citizen's needs for private economic activity.
+\end{abstract}
+
+\section{Introduction}
+
+The design of payment systems shapes economies and societies. Strong,
+developed nation states are evolving towards fully transparent payment
+systems, such as the MasterCard and VisaCard credit card schemes and
+computerized bank transactions such as SWIFT. Such systems enable
+mass surveillance and thus extensive government control over the
+economy, from taxation to intrusion into private lives. Bribery and
+corruption are limited to elites that can afford to escape the
+dragnet. The other extreme are economies of developing, weak nation
+states where economic activity is based largely on coins, paper money
+or even barter. Here, the state is often unable to effectively
+monitor or tax economic activity, and this limits the ability of the
+state to shape the society. As bribery is virtually impossible to
+detect, it is widespread and not limited to social elites.
+ZeroCoin~\cite{miers2013zerocoin} is an example for translating such
+an economy into the digital realm.
+
+Taler is supposed to offer a middleground between an authoritarian
+state in total control of the population and weak states with almost
+anarchistic economies. Specifically, we believe that a liberal
+democracy needs a payment system with the following properties:
+
+\begin{description}
+ \item[Customer Anonymity] It must be impossible for mints, merchants
+ and even a global active adversary, to trace the spending behavior
+ of a customer.
+ \item[Unlinkability] Merchants must not be able to tell if two
+ transactions were performed by the same customer. It must be
+ infeasible to link a set of transactions to the same (anonymous)
+ customer. %, even when taking aborted transactions into account.
+ \item[Taxability] In many current legal systems, it is the
+ responsibility of the merchant to deduct (sales) taxes from
+ purchases made by customers, or to pay (income) taxes for payments
+ received for work.
+ %Taxation is neccessary for the state to
+ %provide legitimate social functions, such as education. Thus, a payment
+ %system must facilitate sales, income and transaction taxes.
+ This specifically means that it must be able to audit merchants (or
+ generally anybody receiving money), and thus the receiver of
+ electronic cash must be easily identifiable.
+ %non-anonymous, as this would enable tax fraud.
+ \item[Verifiability] The payment system should try to minimize the
+ trust necessary between the participants. In particular, digital
+ signatures should be used extensively in order to be able to
+ resolve disputes between the involved parties. Nevertheless,
+ customers must never be able to defraud anyone, and merchants must
+ at best be able to defraud their customers by not delivering the
+ on the agreed contract. Neither merchants nor customers must ever
+ be able to commit fraud against the mint. Both customers and
+ merchants must receive cryptographic proofs of bad behavior in
+ case of protocol violations by the mint. Thus, only the mint will
+ have to be tightly audited and regulated. The design must make it
+ easy to audit the finances of the mint.
+ \item[Ease of Deployment] %The system should be easy to deploy for
+ % real-world applications. In order to lower the entry barrier and
+ % acceptance of the system, a gateway to the existing financial
+ % system should be provided, i.e. by integrating internet-banking
+ % protocols such as HBCI/FinTAN.
+ The digital currency should be
+ tied 1:1 to existing currencies (such as EUR or USD) to avoid
+ exposing users to unnecessary risks from currency fluctuations.
+ Moreover, the system must have a free software reference
+ implementation and an open protocol standard.
+% The protocol should
+% be able to run easily over HTTP(S).
+ \item[Low resource consumption] In order to minimize the operating
+ costs and environmental impact of the payment system, it must
+ avoid the reliance on expensive and ``wasteful'' computations
+ such as proof-of-work.
+ \item[Large Payments and Microdonations] The payment system needs to
+ handle large payments in a reliable manner. Furthermore, for
+ microdonations the system should allow sacrificing reliability to
+ achieve economic viability.
+\end{description}
+
+Taler builds on ideas from Chaum~\cite{chaum1983blind}, who proposed a
+digital currency system that would provide (some) customer anonymity
+while disclosing the identity of the merchants. Chaum's digital cash
+system had some limitations and ultimately failed to be widely
+adopted. In our assessment, key reasons include:
+
+\begin{itemize}
+ \item The use of patents to protect the technology; a payment system
+ must be libre --- free software --- to have a chance for widespread
+ adoption.
+ \item The use of off-line payments and thus deferred detection of
+ double-spending, which could require the mint to attempt to recover
+ funds from customers via the legal system. This creates a
+ significant business risk for the mint, as the system is not
+ self-enforcing from the perspective of the mint. In 1983 off-line
+ payments might have been a necessary feature. However, today
+ requiring network connectivity is feasible and avoids the business
+ risks associated with deferred fraud detection.
+ \item % In addition to the risk of legal disputes with fradulent
+ % merchants and customers,
+ Chaum's published design does not clearly
+ limit the financial damage a mint might suffer from the
+ disclosure of its private online signing key.
+% \item Chaum did not support fractional payments, and Brand's
+% extensions for fractional payments broke unlinkability and thus
+% limited anonymity. Chaum also did not support microdonations,
+% leaving an opportunity for expanding payments into additional areas
+% unexplored.
+% \item Chaum's system was implemented at a time where the US market
+% was still dominated by paper checks and the European market was
+% fragmented into dozens of currencies. Today, SEPA provides a
+% unified currency and currency transfer method for most of Europe,
+% significantly lowering the barrier to entry into this domain for
+% a larger market.
+\end{itemize}
+
+This paper describes Taler, a simple and practical payment with the
+above goals in mind. The basic idea is to use Chaum's model of
+customer, merchant and mint (Figure~\ref{fig:cmm}) where the customer
+withdraws digital currency from the mint with unlinkability provided
+via blind signatures. In contrast to Chaum, Taler uses online
+detection of double-spending, thus ensuring the merchant instantly
+that a transaction is valid. Instead of using cryptographic methods
+to enable fractional payments, the customer can simply include
+the fraction of a coin's value that is to be paid to the merchant in
+his message to the merchant.
+
+
+\begin{figure}[h]
+\centering
+\begin{tikzpicture}
+
+
+\tikzstyle{def} = [node distance= 7em and 10em, inner sep=1em, outer sep=.3em];
+\node (origin) at (0,0) {};
+\node (mint) [def,above=of origin,draw]{Mint};
+\node (customer) [def, draw, below left=of origin] {Customer};
+\node (merchant) [def, draw, below right=of origin] {Merchant};
+
+\tikzstyle{C} = [color=black, line width=1pt]
+\draw [<-, C] (customer) -- (mint) node [midway, above, sloped] (TextNode) {withdraw coins};
+\draw [<-, C] (mint) -- (merchant) node [midway, above, sloped] (TextNode) {deposit coins};
+\draw [<-, C] (merchant) -- (customer) node [midway, above, sloped] (TextNode) {spend coins};
+\end{tikzpicture}
+\caption{Taler's system model for the payment system is based on Chaum~\cite{chaum1983blind}.}
+\label{fig:cmm}
+\end{figure}
+
+Online fraud detection can create problems if the network fails during
+the initial steps of a transaction. For example, a law enforcement
+agency might try to entrap a customer by offering illicit goods and
+then aborting the transaction after learning the public key of the
+coin. If the customer were to then later spend that coin on a
+purchase with shipping, the law enforcement agency could link the two
+transactions and might be able to use the shipping to deanonymize the
+customer. Similarly, fractional payments also lead to the
+possibility of customers wanting to legitimately use the same coin
+twice. Taler addresses this problem by allowing customers to {\em
+ refresh} coins. Refreshing means that a customer is able to
+exchange one coin for a fresh coin, with the old and the new coin
+being unlinkable (except for the customer himself). Taler ensures
+that the {\em entity} of the user owning the new coin is the same as the
+entity of the user owning the old coin, thus making sure that the
+refreshing protocol cannot be abused for money laundering or other
+illicit transactions.
+
+
+\section{Related Work}
+
+\subsection{Blockchain-based currencies}
+
+In recent years, a class of decentralized electronic payment systems,
+based on collectively recorded and verified append-only public
+ledgers, have gained immense popularity. The most well-known protocol
+in this class is Bitcoin~\cite{nakamoto2008bitcoin}. An initial
+concern with Bitcoin was the lack of anonymity, as all Bitcoin
+transactions are recorded for eternity, which can enable
+identification of users. In theory, this concern has been addressed
+with the Zerocoin extension to the protocol~\cite{miers2013zerocoin}.
+
+While these protocols dispense with the need for a central, trusted
+authority and provide anonymity, we argue there are some major,
+irredeemable problems inherent in these systems:
+
+\begin{itemize}
+ \item Bitcoins are not (easily) taxable. The legality and legitimacy of
+ this aspect is questionable. The Zerocoin extension would only make
+ this worse.
+ \item Bitcoins can not be bound to any fiat currency, and are subject to
+ significant value fluctuations. While such fluctuations may be
+ acceptable for high-risk investments, they make Bitcoin unsuitable as
+ a medium of exchange.
+ \item The computational puzzles solved by Bitcoin nodes with the purpose
+ of securing the block chain
+ consume a considerable amount of computational resources and thus
+ energy. Thus, Bitcoin does not represent an environmentally responsible
+ design.
+ \item Anyone can easily start an alternative Bitcoin transaction chain
+ (a so-called AltCoin) and, if successful, reap the benefits of the low
+ cost to initially create coins via computation. As a result, dozens of
+ AltCoins have been created, often without any significant changes to the
+ technology. A large number of AltCoins creates additional overheads for
+ currency exchange and exascerbates the problems with currency fluctuations.
+\end{itemize}
+
+\subsection{Chaum-style electronic cash}
+
+Chaum's original digital cash system~\cite{chaum1983blind} was
+extended by Brands~\cite{brands1993efficient} with the ability to
+perform fractional payments; however, the transactions performed with
+the same coin then become linkable.
+%
+%Some argue that the focus on technically perfect but overwhelmingly
+%complex protocols, as well as the the lack of usable, practical
+%solutions lead to an abandonment of these ideas by
+%practitioners~\cite{selby2004analyzing}.
+%
+To our knowledge, the only publicly available effort to implement
+Chaum's idea is
+Opencoin~\cite{dent2008extensions}. However,
+Opencoin seems to be neither actively developed nor used, and it is
+not clear to what degree the implementation is even complete. Only a
+partial description of the Opencoin protocol is available to date.
+
+\subsection{Peppercoin}
+
+Peppercoin~\cite{rivest2004peppercoin} is a microdonation protocol.
+The main idea of the protocol is to reduce transaction costs by
+minimizing the number of transactions that are processed directly by
+the mint. Instead of always paying, the customer ``gambles'' with the
+merchant for each microdonation. Only if the merchant wins, the
+microdonation is upgraded to a macropayment to be deposited at the
+mint. Peppercoin does not provide customer-anonymity. The proposed
+statistical method for mints detecting fraudulent cooperation between
+customers and merchants at the expense of the mint not only creates
+legal risks for the mint (who has to make a statistical argument), but
+also would require the mint to learn about microdonations where the
+merchant did not get upgraded to a macropayment. Thus, it is unclear
+how much Peppercoin would actually do to reduce the computational
+burden on the mint.
+
+
+\section{Design}
+
+The payment system we propose is built on the blind signature
+primitive proposed by Chaum, but extended with additional
+constructions to provide unlinkability, online fraud detection and
+taxability.
+
+As with Chaum, the Taler system comprises three principal types of
+actors: The \emph{customer} is interested in receiving goods or
+services from the \emph{merchant} in exchange for payment. When
+making a transaction, both the customer and the merchant must agree on
+the same \emph{mint}, which serves as an intermediary for the
+financial transaction between the two. The mint is responsible for
+allowing the customer to obtain the anonymous digital currency and for
+enabling the merchant to convert the anonymous digital currency back
+to some traditional currency.
+
+\subsection{Security model}
+
+Taler's security model assumes that cryptographic primitives are
+secure and that each participant is under full control of his system.
+The contact information of the mint is known to both customer and
+merchant from the start. Furthermore, the merchant is known to the
+customer and we assume that an anonymous, reliable bi-directional
+communication channel can be established by the customer to both the
+mint and the merchant.
+
+The mint is trusted to hold funds of its customers and to forward them
+when receiving the respective deposit instructions from the merchants.
+Customer and merchant can have some assurances about the mint's
+liquidity and operation, as the mint has proven reserves, is subject
+to the law, and can have its business is regularly audited (for
+example, by the government or a trusted third party auditor).
+Audits of the mint's accounts must reveal any possible fraud.
+%
+The merchant is trusted to deliver the service or goods to the
+customer upon receiving payment. The customer can seek legal relief
+to achieve this, as he must get cryptographic proofs of the contract
+and that he paid his obligations.
+%
+Neither the merchant nor the customer may have any ability to {\em
+ effectively} defraud the mint or the state collecting taxes. Here,
+``effectively'' means that the expected return for fraud is negative.
+%
+Note that customers do not need to be trusted in any way, and that in
+particular it is never necessary for anyone to try to recover funds
+from customers using legal means.
+
+
+\subsection{Taxability and Entities}
+
+Electronic coins are trivially copied between machines. Thus, we must
+clarify what kinds of operations can even be expected to be taxed.
+After all, without instrusive measures to take away control of the
+computing platform from its users, copying an electronic wallet from
+one computer to another can hardly be prevented by a payment system.
+Furthermore, it would also hardly be appropriate to tax the moving of
+funds between two computers owned by the same individual. We thus
+need to clarify which kinds of transfers we expect to tax.
+
+Taler is supposed to ensure that the state can tax {\em transactions}.
+We define a transaction as the transfer of funds between {\em mutually
+ distrustful} entities. Two entities are assumed to be mutually
+distrustful if they are unwilling to share control over assets. If a
+private key is shared between two entities, then both entities have
+equal access to the credentials represented by the private key. In a
+payment system this means that either entity could spent the
+associated funds. Assuming the payment system has effective
+double-spending detection, this means that either entity has to
+constantly fear that the funds might no longer be available to it.
+Thus, ``transfering'' funds by sharing a private key implies that
+receiving party must trust the sender. In Taler, making funds
+available by sharing a private key and thus sharing control is {\bf
+ not} considered a {\em transaction} and thus {\bf not} recorded for
+taxation.
+
+A {\em transaction} is a transfer where it is assured that one entity
+gains control over funds while at the same time another entity looses
+control over those funds. Taler ensures taxability only when some
+entity acquires exclusive control over digital coins. For
+transactions, the state can obtain information from the mint (or the
+bank) that identifies the entity that received the digital coins as
+well as the exact value of those coins. Taler also allows the mint
+(and thus the state) to learn the value of digital coins withdrawn by
+a customer --- but not how, where or when they were spent. Finally,
+to enable audits, the current balance and profits of the mint are also
+easily determined.
+
+\subsection{Anonymity}
+
+An anonymous communication channel (e.g. via Tor~\cite{tor-design}) is
+used for all communication between the customer and the merchant.
+Thus, the customer can remain anonymous; however, the system does reveal
+that the customer is one of the patrons of the mint. Naturally, the
+customer-merchant operation might leak other information about the
+customer, such as a shipping address. Such purchase-specific
+information leakage is outside of the scope of this work.
+
+The customer may use an anonymous communication channel for the
+communication with the mint to avoid leaking IP address information;
+however, the mint will anyway be able to determine the customer's
+identity from the (SEPA) transfer that the customer initiates to
+obtain anonymous digital cash. The scheme is anonymous
+because the mint will be unable to link the known identity of the
+customer that withdrew anonymous digital currency to the {\em
+ purchase} performed later at the merchant.
+% All the mint will be
+%able to confirm is that the customer is {\em one} of its patrons who
+%previously obtained the anonymous digital currency --- and of course
+%that the coin was not spent before.
+
+While the customer thus has anonymity for his purchase, the mint will
+always learn the merchant's identity (which is necessary for
+taxation), and thus the merchant has no reason to anonymize his
+communication with the mint.
+% Technically, the merchant could still
+%use an anonymous communication channel to communicate with the mint.
+%However, in order to receive the traditional currency the mint will
+%require (SEPA) account details for the deposit.
+
+%As both the initial transaction between the customer and the mint as
+%well as the transactions between the merchant and the mint do not have
+%to be done anonymously, there might be a formal business contract
+%between the customer and the mint and the merchant and the mint. Such
+%a contract may provide customers and merchants some assurance that
+%they will actually receive the traditional currency from the mint
+%given cryptographic proof about the validity of the transaction(s).
+%However, given the business overheads for establishing such contracts
+%and the natural goal for the mint to establish a reputation and to
+%minimize cost, it is more likely that the mint will advertise its
+%external auditors and proven reserves and thereby try to convince
+%customers and merchants to trust it without a formal contract.
+
+
+\subsection{Coins}
+
+A \emph{coin} is a digital token which derives its financial value
+from a signature on the coin's identifier by a mint. The mint is
+expected to have multiple {\em coin signing key} pairs available for
+signing, each representing a different coin denomination.
+
+The coin signing keys have an expiration date (typically measured in
+years), and coins signed with a coin signing key must be spent (or
+exchanged for new coins) before that expiration date. This allows the
+mint to limit the amount of state it needs to keep to detect
+double spending attempts. Furthermore, the mint is expected to use each coin
+signing key only for a limited number of coins, for example by
+limiting its use to sign coins to a week or a month. That way, if the
+private coin signing key were to be compromised, the mint can detect
+this once more coins are redeemed than the total that was signed into
+existence using the respective coin signing key. In this case, the
+mint can allow the original set of customers to exchange the coins
+that were signed with the compromised private key, while refusing
+further transactions from merchants if they involve those coins. As a
+result, the financial damage of loosing a private signing key can be
+limited to at most twice the amount originally signed with that key.
+To ensure that the mint does not enable deanonymization of users by
+signing each coin with a fresh coin signing key, the mint must
+publicly announce the coin signing keys in advance. Those
+announcements are expected to be signed with an off-line long-term
+private {\em master signing key} of the mint and possibly the auditor.
+
+Before a customer can withdraw a coin from the mint, he has to pay the
+mint the value of the coin, as well as processing fees. This is done
+using other means of payments, such as SEPA transfers~\cite{sepa}.
+The subject line of the transfer must contains {\em withdrawal
+ authorization key}, a public key for digital signatures generated by
+the customer. When the mint receives a transfer with a public key in
+the subject, it adds the funds to a {\em reserve} identified by the
+withdrawl authorization key. By signing the withdrawl messages using
+the withdrawl authorization key, the customer can prove to the mint
+that he is authorized to withdraw anonymous digital coins from the
+reserve. The mint will record the withdrawl messages with the reserve
+record as proof that the anonymous digital coin was created for the
+correct customer.
+
+After a coin is minted, the customer is the only entity that knows the
+private key of the coin, making him the \emph{owner} of the coin. The
+coin can be identified by the mint by its public key; however, due to
+the use of blind signatures, the mint does not learn the public key
+during the minting process. Knowledge of the private key of the coin
+enables the owner to spent the coin. If the private key is shared
+with others, they also become owners of the coin.
+
+\subsection{Coin spending}
+
+To spent a coin, the coin's owner needs to sign a {\em deposit
+ request} specifying the amount, the merchant's account information
+and a {\em business transaction-specific hash} using the coin's
+private key. A merchant can then transfer this permission of the
+coin's owner to the mint to obtain the amount in traditional currency.
+If the customer is cheating and the coin was already spent, the mint
+provides cryptographic proof of the fraud to the merchant, who will
+then refuse the transaction.
+% The mint is typically expected
+%to transfer the funds to the merchant using a SEPA transfer or similar
+%methods appropriate to the domain of the traditional currency.
+
+%The mint needs to ensure that a coin can only be spent once. This is
+%done by storing the public keys of all deposited coins (together with
+%the deposit request and the owner's signature confirming the
+%transaction). The mint's state can be limited as coins signed with
+%expired coin sigining keys do not have to be retained.
+
+\paragraph{Partial spending.}
+
+To allow exact payments without requiring the customer to keep a large
+amount of ``change'' in stock, the payment systems allows partial
+spending of coins. Consequently, the mint the must not only store the
+identifiers of spent coins, but also the fraction of the coin that has
+been spent.
+
+%\paragraph{Online checks.}
+%
+%For secure transactions (non-microdonations), the merchant is expected
+%to perform an online check to detect double-spending. In the simplest
+%case, the merchant simply directly confirms the validity of the
+%deposit permission signed by the coin's owner with the mint, and then
+%proceeds with the contract.
+
+\paragraph{Incremental payments.}
+
+For services that include pay-as-you-go billing, customers can over
+time sign deposit permissions for an increasing fraction of the value
+of a coin to be paid to a particular merchant. As checking with the
+mint for each increment might be expensive, the coin's owner can
+instead sign a {\em lock permission}, which allows the merchant to get
+an exclusive right to redeem deposit permissions for the coin for a
+limited duration. The merchant uses the lock permission to determine
+if the coin has already been spent and to ensure that it cannot be
+spent by another merchant for the {\em duration} of the lock as
+specified in the lock permission. If the coin has been spent or is
+already locked, the mint provides the owner's deposit or locking
+request and signature to prove the attempted fraud by the customer.
+Otherwise, the mint locks the coin for the expected duration of the
+transaction (and remembers the lock permission). The merchant and the
+customer can then finalize the business transaction, possibly
+exchanging a series of incremental payment permissions for services.
+Finally, the merchant then redeems the coin at the mint before the
+lock permission expires to ensure that no other merchant spends the
+coin first.
+
+
+\paragraph{Probabilistic spending.}
+
+Similar to Peppercoin, Taler supports probabilistic spending of coins to
+support cost-effective transactions for small amounts. Here, an
+ordinary transaction is performed based on the result of a biased coin
+flip with a probability related to the desired transaction amount in
+relation to the value of the coin. Unlike Peppercoin, in Taler either
+the merchant wins and the customer looses the coin, or the merchant
+looses and the customer keeps the coin. Thus, there is no opportunity
+for the merchant and the customer to conspire against the mint. To
+determine if the coin is to be transferred, merchant and customer
+execute a secure coin flipping protocol~\cite{blum1981}. The commit
+values are included in the business contract and are revealed after
+the contract has been signed using the private key of the coin. If
+the coin flip is decided in favor of the merchant, the merchant can
+redeem the coin at the mint.
+
+One issue in this protocol is that the customer may use a worthless
+coin by offering a coin that has already been spent. This kind of
+fraud would only be detected if the customer actually lost the coin
+flip, and at this point the merchant might not be able to recover from
+the loss. A fradulent anonymous customer may run the protocol using
+already spent coins until the coin flip is in his favor. As with
+incremental spending, lock permissions could be used to ensure that
+the customer cannot defraud the merchant by offering a coin that has
+already been spent. However, as this means involving the mint even if
+the merchant looses the coin flip, such a scheme is unsuitable for
+microdonations as the transaction costs from involving the mint might
+be disproportionate to the value of the transaction, and thus with
+locking the probabilistic scheme has no advantage over simply using
+fractional payments.
+
+Hence, Taler uses probabilistic transactions {\em without} the online
+double-spending detection. This enables the customer to defraud the
+merchant by paying with a coin that was already spent. However, as,
+by definition, such microdonations are for tiny amounts, the incentive
+for customers to pursue this kind of fraud is limited.
+
+
+\subsection{Refreshing Coins}
+
+In the payment scenarios there are several cases where a customer will
+reveal the public key of a coin to a merchant, but not ultimately sign
+over the full value of the coin. If the customer then continues to
+use the remainder of the value of the coin in other transactions,
+merchants and the mint could link the various transactions as they all
+share the same public key for the coin.
+
+Thus, the owner might want to exchange such a {\em dirty} coin for a
+{\em fresh} coin to ensure unlinkability of future transactions with
+the previous operation. Even if a coin is not dirty, the owner of a
+coin may want to exchange a coin if the respective coin signing key is
+about to expire. All of these operations are supported with the {\em
+ coin refreshing protocol}, which allows the owner of a coin to
+exchange existing coins (or their remaining value) for fresh coins
+with a new public-private key pairs. Refreshing does not use the
+ordinary spending operation as the owner of a coin should not have to
+pay taxes on this operation. Because of this, the refreshing protocol
+must assure that owner stays the same. After all, the coin refreshing
+protocol must not be usable for transactions, as transactions in Taler
+must be taxable.
+
+Thus, one main goal of the refreshing protocol is that the mint must
+not be able to link the fresh coin's public key to the public key of
+the dirty coin. The second main goal is to enable the mint to ensure
+that the owner of the dirty coin can determine the private key of the
+fresh coin. This way, refreshing cannot be used to construct a
+transaction --- the owner of the dirty coin remains in control of the
+fresh coin.
+
+As with other operations, the refreshing protocol must also protect
+the mint from double-spending; similarly, the customer has to have
+cryptographic evidence if there is any misbehaviour by the mint.
+Finally, the mint may choose to charge a transaction fee for
+refreshing by reducing the value of the generated fresh coins
+in relation to the value of the melted coins.
+%Naturally, all such transaction fees should be clearly stated as part
+%of the business contract offered by the mint to customers and
+%merchants.
+
+
+\section{Taler's Cryptographic Protocols}
+
+% In this section, we describe the protocols for Taler in detail.
+
+For the sake of brevity, we do not specifically state that the
+recipient of a signed message always first checks that the signature
+is valid. Also, whenever a signed message is transmitted, it is
+assumed that the receiver is told the public key (or knows it from the
+context) and that the signature contains additional identification as
+to the purpose of the signature (such that it is not possible to
+use a signature from one protocol step in a different context).
+
+When the mint signs messages (not coins), an {\em online message
+ signing key} of the mint is used. The mint's long-term offline key
+is used to certify both the coin signing keys as well as the online
+message signing key of the mint. The mint's long-term offline key is
+assumed to be well-known to both customers and merchants, for example
+because it is certified by the auditors.
+
+As we are dealing with financial transactions, we explicitly state
+whenever entities need to safely commit data to persistent storage.
+As long as those commitments persist, the protocol can be safely
+resumed at any step. Commitments to disk are cummulative, that is an
+additional commitment does not erase the previously committed
+information. Keys and thus coins always have a well-known expiration
+date; information committed to disk can be discarded after the
+expiration date of the respective public key. Customers can also
+discard information once the respective coins have been fully spent,
+and merchants may discard information once payments from the mint have
+been received (assuming records are also no longer needed for tax
+authorities). The mint's bank transfers dealing in traditional
+currency are expected to be recorded for tax authorities to ensure
+taxability.
+
+\subsection{Withdrawal}
+
+To withdraw anonymous digital coins, the customer performs the
+following interaction with the mint:
+
+\begin{enumerate}
+ \item The customer identifies a mint with an auditor-approved
+ coin signing public-private key pair $K := (K_s, K_p)$
+ and randomly generates:
+ \begin{itemize}
+ \item withdrawal key $W := (W_s,W_p)$ with private key $W_s$ and public key $W_p$,
+ \item coin key $C := (C_s,C_p)$ with private key $C_s$ and public key $C_p$,
+ \item blinding factor $b$,
+ \end{itemize}
+ and commits $\langle W, C, b \rangle$ to disk.
+ \item The customer transfers an amount of money corresponding to (at least) $K_p$ to the mint, with $W_p$ in the subject line of the transaction.
+ \item The mint receives the transaction and credits the $W_p$ reserve with the respective amount in its database.
+ \item The customer sends $S_W(E_b(C_p))$ to the mint to request withdrawl of $C$; here, $E_b$ denotes Chaum-style blinding with blinding factor $b$.
+ \item The mint checks if the same withdrawl request was issued before; in this case, it sends $S_{K}(E_b(C_p))$ to the customer.\footnote{Here $S_K$
+ denotes a Chaum-style blind signature with private key $K_s$.}
+ If this is a fresh withdrawl request, the mint performs the following transaction:
+ \begin{enumerate}
+ \item checks if the reserve $W_p$ has sufficient funds for a coin of value corresponding to $K_p$
+ \item stores the withdrawl request $\langle S_W(E_b(C_p)), S_K(E_b(C_p)) \rangle$ in its database for future reference,
+ \item deducts the amount corresponding to $K_p$ from the reserve,
+ \item and sends $S_{K}(E_b(C_p))$ to the customer.
+ \end{enumerate}
+ If the guards for the transaction fail, the mint sends an descriptive error back to the customer,
+ with proof that it operated correctly (i.e. by showing the transaction history for the reserve).
+ \item The customer computes (and verifies) the unblind signature $S_K(C_p) = D_b(S_K(E_b(C_p)))$.
+ The customer writes $\langle S_K(C_p), C_s \rangle$ to disk (effectively adding the coin to the
+ local wallet) for future use.
+\end{enumerate}
+
+\subsection{Exact, partial and incremental spending}
+
+A customer can spend coins at a merchant, under the condition that the
+merchant trusts the mint that minted the coin. Merchants are
+identified by their public key $M := (M_s, M_p)$, which must be known
+to the customer apriori.
+
+The following steps describe the protocol between customer, merchant and mint
+for a transaction involving a coin $C := (C_s, C_p)$ which is previously signed
+by a mint's denomination key $K$, i.e. the customer posses
+$\widetilde{C} := S_K(C_p)$:
+
+\begin{enumerate}
+\item\label{offer} The merchant sends an \emph{offer:} $\langle S_M(m, f),
+ \vec{D} \rangle$ containing the price of the offer $f$, a transaction
+ ID $m$ and the list of mints $D_1, \ldots, D_n$ accepted by the merchant
+ where each $D_i$ is a mint's public key.
+\item\label{lock} The customer must possess or acquire a coin minted by a mint that is
+ accepted by the merchant, i.e. $K$ should be publicly signed by some $D_i
+ \in \{D_1, D_2, \ldots, D_n\}$, and has a value $\geq f$.
+
+ Customer then generates a \emph{lock-permission} $\mathcal{L} :=
+ S_c(\widetilde{C}, t, m, f, M_p)$ where $t$ specifies the time until which the
+ lock is valid and sends $\langle \mathcal{L}, D_i\rangle$ to the merchant,
+ where $D_i$ is the mint which signed $K$.
+\item The merchant asks the mint to apply the lock by sending $\langle
+ \mathcal{L} \rangle$ to the mint.
+\item The mint validates $\widetilde{C}$ and detects double spending if there is
+ a lock-permission record $S_c(\widetilde{C}, t', m', f', M_p')$ where $(t',
+ m', f', M_p') \neq (t, m, f, M_p)$ or a \emph{deposit-permission} record for
+ $C$ and sends it to the merchant, who can then use it prove to the customer
+ and subsequently ask the customer to issue a new lock-permission.
+
+ If double spending is not found, the mint commits $\langle \mathcal{L} \rangle$ to disk
+ and notifies the merchant that locking was successful.
+\item\label{contract} The merchant creates a digitally signed contract
+ $\mathcal{A} := S_M(m, f, a, H(p, r))$ where $a$ is data relevant to the contract
+ indicating which services or goods the merchant will deliver to the customer, and $p$ is the
+ merchant's payment information (e.g. his IBAN number) and $r$ is an random nounce.
+ The merchant commits $\langle \mathcal{A} \rangle$ to disk and sends it to the customer.
+\item The customer creates a
+ \emph{deposit-permission} $\mathcal{D} := S_c(\widetilde{C}, f, m, M_p, H(a), H(p, r))$, commits
+ $\langle \mathcal{A}, \mathcal{D} \rangle$ to disk and sends $\mathcal{D}$ to the merchant.
+\item\label{invoice_paid} The merchant commits the received $\langle \mathcal{D} \rangle$ to disk.
+\item The merchant gives $(\mathcal{D}, p, r)$ to the mint, revealing his
+ payment information.
+\item The mint verifies $(\mathcal{D}, p, r)$ for its validity. A
+ \emph{deposit-permission} for a coin $C$ is valid if:
+ \begin{itemize}
+ \item $C$ is not refreshed already
+ \item there exists no other \emph{deposit-permission} on disk for \\
+ $\mathcal{D'} := S_c(\widetilde{C}, f', m', M_p', H(a'), H(p', r'))$ for $C$
+ such that \\ $(f', m',M_p', H(a')) \neq (f, m, M_p, H(a))$
+ \item $H(p, r) := H(p', r')$
+ \end{itemize}
+ If $C$ is valid and no other \emph{deposit-permission} for $C$ exists on disk, the
+ mint does the following:
+ \begin{enumerate}
+ \item if a \emph{lock-permission} exists for $C$, it is deleted from disk
+ \item\label{transfer} transfers an amount of $f$ to the merchant's bank account
+ given in $p$. The subject line of the transaction to $p$ must contain
+ $H(\mathcal{D})$.
+ \item $\langle \mathcal{D}, p, r \rangle$ is commited to disk.
+ \end{enumerate}
+ If the deposit record $\langle \mathcal{D}, p, r \rangle$ already exists,
+ the mint sends it to the merchant, but does not transfer money to $p$ again.
+\end{enumerate}
+
+To facilitate incremental spending of a coin $C$ in a single transaction, the
+merchant makes an offer in Step~\ref{offer} with a maximum amount $f_{max}$ he
+is willing to charge in this transaction from the coin $C$. After obtaining the
+lock on $C$ for $f_{max}$, the merchant makes a contract in Step~\ref{contract}
+with an amount $f \leq f_{max}$. The protocol follows with the following steps
+repeated after Step~\ref{invoice_paid} whenever the merchant wants to charge an
+incremental amount up to $f_{max}$:
+
+\begin{enumerate}
+ \setcounter{enumi}{4}
+\item The merchant generates a new contract $ \mathcal{A}' := S_M(m, f', a', H(p,
+ r)) $ after obtaining the deposit-permission for a previous contract. Here
+ $f'$ is the accumulated sum the merchant is charging the customer, of which
+ the merchant has received a deposit-permission for $f$ from the previous
+ contract \textit{i.e.}~$f <f' \leq f_{max}$. Similarly $a'$ is the new
+ contract data appended to older contract data $a$.
+ The merchant commits $\langle \mathcal{A}' \rangle$ to disk and sends it to the customer.
+\item Customer commits $\langle \mathcal{A}' \rangle$ to disk, creates
+ $\mathcal{D}' := S_c(\widetilde{C}, f', m, M_p, H(a'), H(p, r))$, commits
+ $\langle \mathcal{D'} \rangle$ and sends it to the merchant.
+\item The merchant commits the received $\langle \mathcal{D'} \rangle$ and
+ deletes the older $\mathcal{D}$
+\end{enumerate}
+
+%Figure~\ref{fig:spending_protocol_interactions} summarizes the interactions of the
+%coin spending protocol.
+
+For transactions with multiple coins, the steps of the protocol are executed in
+parallel for each coin.
+
+During the time a coin is locked, it may not be spent at a
+different merchant. To make the storage costs of the mint more predictable,
+only one lock per coin can be active at any time, even if the lock only covers a
+fraction of the coin's denomination. The mint will delete the locks when they
+expire. Thus the coins can be reused once their locks expire. However, doing
+so may link the new transaction to older transaction.
+
+Similarly, if a transaction is aborted after Step 2, subsequent transactions
+with the same coin can be linked to the coin, but not directly to the coin's
+owner. The same applies to partially spent coins. To unlink subsequent
+transactions from a coin, the customer has to execute the coin refreshing
+protocol with the mint.
+
+%\begin{figure}[h]
+%\centering
+%\begin{tikzpicture}
+%
+%\tikzstyle{def} = [node distance= 1em, inner sep=.5em, outer sep=.3em];
+%\node (origin) at (0,0) {};
+%\node (offer) [def,below=of origin]{make offer (merchant $\rightarrow$ customer)};
+%\node (A) [def,below=of offer]{permit lock (customer $\rightarrow$ merchant)};
+%\node (B) [def,below=of A]{apply lock (merchant $\rightarrow$ mint)};
+%\node (C) [def,below=of B]{confirm (or refuse) lock (mint $\rightarrow$ merchant)};
+%\node (D) [def,below=of C]{sign contract (merchant $\rightarrow$ customer)};
+%\node (E) [def,below=of D]{permit deposit (customer $\rightarrow$ merchant)};
+%\node (F) [def,below=of E]{make deposit (merchant $\rightarrow$ mint)};
+%\node (G) [def,below=of F]{transfer confirmation (mint $\rightarrow$ merchant)};
+%
+%\tikzstyle{C} = [color=black, line width=1pt]
+%\draw [->,C](offer) -- (A);
+%\draw [->,C](A) -- (B);
+%\draw [->,C](B) -- (C);
+%\draw [->,C](C) -- (D);
+%\draw [->,C](D) -- (E);
+%\draw [->,C](E) -- (F);
+%\draw [->,C](F) -- (G);
+%
+%\draw [->,C, bend right, shorten <=2mm] (E.east)
+% to[out=-135,in=-45,distance=3.8cm] node[left] {aggregate} (D.east);
+%\end{tikzpicture}
+%\caption{Interactions between a customer, merchant and mint in the coin spending
+% protocol}
+%\label{fig:spending_protocol_interactions}
+%\end{figure}
+
+
+\subsection{Probabilistic spending}
+
+The following steps are executed for microdonations with upgrade probability $p$:
+\begin{enumerate}
+ \item The merchant sends an offer to the customer.
+ \item The customer sends a commitment $H(r_c)$ to a random
+ value $r_c \in [0,2^R)$, where $R$ is a system parameter.
+ \item The merchant sends random $r_m \in [0,2^R)$ to the customer.
+ \item The customer computes $p' := (|r_c - r_m|) / (2^R)$.
+ If $p' < p$, the customer sends a coin with deposit-permission to the merchant.
+ Otherwise, the customer sends $r_c$ to the merchant.
+ \item The merchant deposits the coin, or checks if $r_c$ is consistent
+ with $H(r_c)$.
+\end{enumerate}
+
+\subsection{Refreshing}
+
+The following protocol is executed in order to refresh a coin $C'$ of denomination $K$ to
+a fresh coin $\widetilde{C}$ with the same denomination. In the protocol, $\kappa \ge 3$ is a security parameter.
+
+\begin{enumerate}
+ \item For each $i = 1,\ldots,\kappa$, the customer
+ \begin{itemize}
+ \item randomly generates transfer key $T^{(i)} := \left(t^{(i)}_s,T^{(i)}_p\right)$ where $T^{(i)}_p := t^{(i)}_s \cdot G$,
+ \item randomly generates coin key pair $C^{(i)} := \left(c_s^{(i)}, C_p^{(i)}\right)$ where $C^{(i)}_p := c^{(i)}_s \cdot G$,
+ \item randomly generates blinding factors $b_i$,
+ \item computes $E_i := E_{K_i}\left(c_s^{(i)}, b_i\right)$ where $K_i := c'_s \cdot T_p^{(i)}$ (The encryption key $K_i$ is
+ computed by multiplying the private key $c'_s$ of the original coin with the point on the curve
+ that represents the public key of the transfer key $T^{(i)}$.),
+ \end{itemize}
+ and commits $\langle C', \vec{T}, \vec{C}, \vec{b} \rangle$ to disk.
+ \item The customer computes $B_i := E_{b_i}(C^{(i)}_p)$ and sends commitments
+ $S_{C'}(\vec{E}, \vec{B}, \vec{T}))$ for $i=1,\ldots,\kappa$ to the mint;
+ here $E_{b_i}$ denotes Chaum-style blinding with blinding factor $b_i$.
+ \item The mint generates a random $\gamma$ with $1 \le \gamma \le \kappa$ and
+ marks $C'_p$ as spent by committing
+ $\langle C', \gamma, S_{C'}(\vec{E}, \vec{B}, \vec{T}) \rangle$ to disk
+ \item The mint sends $S_K(C'_p, \gamma)$ to the customer.\footnote{Instead of $K$, it is also
+ possible to use any equivalent mint signing key known to the customer here, as $K$ merely
+ serves as proof to the customer that the mint selected this particular $\gamma$.}
+ \item The customer commits $\langle C', S_K(C'_p, \gamma) \rangle$ to disk.
+ \item The customer computes $\mathfrak{R} := \left(t_s^{(i)}, C_p^{(i)}, b_i\right)_{i \ne \gamma}$
+ and sends $S_{C'}(\mathfrak{R})$ to the mint.
+ \item \label{step:refresh-ccheck} The mint checks whether $\mathfrak{R}$ is consistent with the commitments;
+ specifically, it computes for $i \not= \gamma$:
+ \begin{itemize}
+ \item $\overline{K}_i := t_s^{(i)} \cdot C_p'$,
+ \item $(\overline{c}_s^{(i)}, \overline{b}_i) := D_{\overline{K}_i}(E_i)$,
+ \item $\overline{C}^{(i)}_p := \overline{c}_s^{(i)} \cdot G$,
+ \item $\overline{B}_i := E_{b_i}(C_p^{(i)})$,
+ \item $\overline{T}_i := t_s^{(i)} G$,
+ \end{itemize}
+ and checks if $\overline{C}^{(i)}_p = C^{(i)}_p$ and $H(E_i, \overline{B}_i, \overline{T}^{(i)}_p) = H(E_i, B_i, T^{(i)}_p)$
+ and $\overline{T}_i = T_i$.
+
+ \item \label{step:refresh-done} If the commitments were consistent, the mint sends the blind signature
+ $\widetilde{C} := S_{K}(B_\gamma)$ to the customer.
+ Otherwise, the mint responds with an error and confiscates the value of $C'$,
+ committing $\langle C', \gamma, S_{C'}(\mathfrak{R}) \rangle$ to disk as proof for the attempted fraud.
+\end{enumerate}
+
+%\subsection{N-to-M Refreshing}
+%
+%TODO: Explain, especially subtleties regarding session key / the spoofing attack that requires signature.
+
+\subsection{Linking}
+
+For a coin that was successfully refreshed, the mint responds to
+a request $S_{C'}(\mathtt{link})$ with $(T^{(\gamma)}_p$, $E_{\gamma}, \widetilde{C})$.
+
+This allows the owner of the old coin to also obtain the private key
+of the new coin, even if the refreshing protocol was illicitly
+executed by another party who learned $C'_s$ from the old owner.
+
+
+\section{Discussion}
+
+\subsection{Offline Payments}
+
+Chaum's original proposals for anonymous digital cash avoided the
+locking and online spending steps detailed in this proposal by
+providing a means to deanonymize customers involved in
+double-spending. We believe that this is problematic as the mint or
+the merchant will then still need out-of-band means to recover funds
+from the customer, which may be impossible in practice. In contrast,
+in our design only the mint may try to defraud the other participants
+and disappear. While this is still a risk, this is likely manageable,
+especially compared to recovering funds via the court system from
+customers.
+
+
+\subsection{Bona-fide microdonations}
+
+Evidently the customer can ``cheat'' by aborting the transaction in
+Step 3 of the microdonation protocol if the outcome is unfavourable ---
+and repeat until he wins. This is why Taler is suitable for
+microdonations --- where the customer voluntarily contributes ---
+and not for micropayments.
+
+Naturally, if the donations requested are small, the incentive to
+cheat for minimal gain should be quite low. Payment software could
+embrace this fact by providing an appeal to conscience in form of an
+option labeled ``I am unethical and want to cheat'', which executes
+the dishonest version of the payment protocol.
+
+If an organization detects that it cannot support itself with
+microdonations, it can always choose to switch to the macropayment
+system with slightly higher transaction costs to remain in business.
+
+\subsection{Merchant Tax Audits}
+
+For a tax audit on the merchant, the mint includes the business
+transaction-specific hash in the transfer of the traditional
+currency. A tax auditor can then request the merchant to reveal
+(meaningful) details about the business transaction ($\mathcal{D}$,
+$a$, $p$, $r$), including proof that applicable taxes were paid.
+
+If a merchant is not able to provide theses values, he can be punished
+in relation to the amount transferred by the traditional currency
+transfer.
+
+
+\section{Future Work}
+
+%The legal status of the system needs to be investigated in the various
+%legal systems of the world. However, given that the system enables
+%taxation and is able to impose withdrawl limits and thus is not
+%suitable for money laundering, we are optimistic that states will find
+%the design desirable.
+
+We did not yet perform performance measurements for the various
+operations. However, we are pretty sure that the computational and
+bandwidth cost for transactions described in this paper is likely
+small compared to other business costs for the mint. We expect costs
+within the system to be dominated by the (replicated, transactional)
+database. However, these expenses are again likely small in relation
+to the business cost of currency transfers using traditional banking.
+Here, mint operators should be able to reduce their expenses by
+aggregating multiple transfers to the same merchant.
+
+
+\section{Conclusion}
+
+We have presented an efficient electronic payment system that
+simultaneously addresses the conflicting objectives created by the
+citizen's need for privacy and the state's need for taxation. The
+coin refreshing protocol makes the design flexible and enables a
+variety of payment methods. The libre implementation and open
+protocol may finally enable modern society to upgrade to proper
+electronic wallets with efficient, secure and privacy-preserving
+transactions.
+
+\bibliographystyle{alpha}
+\bibliography{taler}
+\end{document}
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 000000000..90ea1a047
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+*.o
+*.deps
+*.libs
+*.lo
+*.la
+*.log
+*.trs
+*/__pycache__
+test-* \ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 000000000..485c4f9d7
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,2 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+SUBDIRS = include util mint
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
new file mode 100644
index 000000000..d10d6d70e
--- /dev/null
+++ b/src/include/Makefile.am
@@ -0,0 +1,7 @@
+EXTRA_DIST = \
+ platform.h \
+ taler_blind.h \
+ taler_signatures.h \
+ taler_types.h \
+ taler_util.h \
+ taler_rsa.h
diff --git a/src/include/platform.h b/src/include/platform.h
new file mode 100644
index 000000000..4cba7abfd
--- /dev/null
+++ b/src/include/platform.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2014 Chrisitan Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/platform.h
+ * @brief This file contains the includes and definitions which are used by the
+ * rest of the modules
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+/* Include our configuration header */
+#ifndef HAVE_USED_CONFIG_H
+# define HAVE_USED_CONFIG_H
+# ifdef HAVE_CONFIG_H
+# include "taler_config.h"
+# endif
+#endif
+
+
+#if (GNUNET_EXTRA_LOGGING >= 1)
+#define VERBOSE(cmd) cmd
+#else
+#define VERBOSE(cmd) do { break; }while(0)
+#endif
+
+/* Include the features available for GNU source */
+#define _GNU_SOURCE
+
+/* Include GNUnet's platform file */
+#include <gnunet/platform.h>
+
+/* Do not use shortcuts for gcrypt mpi */
+#define GCRYPT_NO_MPI_MACROS 1
+
+/* Do not use deprecated functions from gcrypt */
+#define GCRYPT_NO_DEPRECATED 1
+
+#endif /* PLATFORM_H_ */
+
+/* end of platform.h */
diff --git a/src/include/taler_db_lib.h b/src/include/taler_db_lib.h
new file mode 100644
index 000000000..41b46264e
--- /dev/null
+++ b/src/include/taler_db_lib.h
@@ -0,0 +1,132 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+
+/**
+ * @file include/taler_db_lib.h
+ * @brief helper functions for DB interactions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ */
+
+#ifndef TALER_DB_LIB_H_
+#define TALER_DB_LIB_H_
+
+#include <libpq-fe.h>
+#include "taler_util.h"
+
+#define TALER_DB_QUERY_PARAM_END { NULL, 0, 0 }
+#define TALER_DB_QUERY_PARAM_PTR(x) { (x), sizeof (*(x)), 1 }
+#define TALER_DB_QUERY_PARAM_PTR_SIZED(x, s) { (x), (s), 1 }
+
+
+#define TALER_DB_RESULT_SPEC_END { NULL, 0, NULL }
+#define TALER_DB_RESULT_SPEC(name, dst) { (void *) (dst), sizeof (*(dst)), (name) }
+#define TALER_DB_RESULT_SPEC_SIZED(name, dst, s) { (void *) (dst), (s), (name) }
+
+
+/**
+ * Description of a DB query parameter.
+ */
+struct TALER_DB_QueryParam
+{
+ /**
+ * Data or NULL
+ */
+ const void *data;
+ /**
+ * Size of 'data'
+ */
+ size_t size;
+ /**
+ * Non-null if this is not the last parameter.
+ * This allows for null as sentinal value.
+ */
+ int more;
+};
+
+
+/**
+ * Description of a DB result cell.
+ */
+struct TALER_DB_ResultSpec
+{
+ /**
+ * Destination for the data.
+ */
+ void *dst;
+
+ /**
+ * Allowed size for the data.
+ */
+ size_t dst_size;
+
+ /**
+ * Field name of the desired result.
+ */
+ char *fname;
+};
+
+
+/**
+ * Execute a prepared statement.
+ */
+PGresult *
+TALER_DB_exec_prepared (PGconn *db_conn,
+ const char *name,
+ const struct TALER_DB_QueryParam *params);
+
+
+/**
+ * Extract results from a query result according to the given specification.
+ * If colums are NULL, the destination is not modified, and GNUNET_NO
+ * is returned.
+ *
+ * @return
+ * GNUNET_YES if all results could be extracted
+ * GNUNET_NO if at least one result was NULL
+ * GNUNET_SYSERR if a result was invalid (non-existing field)
+ */
+int
+TALER_DB_extract_result (PGresult *result, struct TALER_DB_ResultSpec *rs, int row);
+
+
+int
+TALER_DB_field_isnull (PGresult *result,
+ int row,
+ const char *fname);
+
+
+int
+TALER_DB_extract_amount_nbo (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_AmountNBO *r_amount_nbo);
+
+
+int
+TALER_DB_extract_amount (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_Amount *r_amount);
+
+#endif /* TALER_DB_LIB_H_ */
+
+/* end of include/taler_db_lib.h */
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
new file mode 100644
index 000000000..b224c4b33
--- /dev/null
+++ b/src/include/taler_json_lib.h
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_json_lib.h
+ * @brief helper functions for JSON processing using libjansson
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef TALER_JSON_LIB_H_
+#define TALER_JSON_LIB_H_
+
+#include <jansson.h>
+
+
+/**
+ * Convert a TALER amount to a JSON
+ * object.
+ *
+ * @param amount the amount
+ * @return a json object describing the amount
+ */
+json_t *
+TALER_JSON_from_amount (struct TALER_Amount amount);
+
+
+/**
+ * Convert absolute timestamp to a json string.
+ *
+ * @param the time stamp
+ * @return a json string with the timestamp in @a stamp
+ */
+json_t *
+TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp);
+
+
+
+/**
+ * Convert binary data to a JSON string
+ * with the base32crockford encoding.
+ *
+ * @param data binary data
+ * @param size size of @a data in bytes
+ * @return json string that encodes @a data
+ */
+json_t *
+TALER_JSON_from_data (const void *data, size_t size);
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param r_amount where the amount has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_amount (json_t *json,
+ struct TALER_Amount *r_amount);
+
+/**
+ * Parse given JSON object to absolute time.
+ *
+ * @param json the json object representing absolute time in seconds
+ * @param r_abs where the time has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_abs (json_t *json,
+ struct GNUNET_TIME_Absolute *r_abs);
+
+/**
+ * Parse given JSON object to data
+ *
+ * @param json the json object representing data
+ * @param out the pointer to hold the parsed data.
+ * @param out_size the size of r_data.
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_data (json_t *json,
+ void *out,
+ size_t out_size);
+
+
+#endif /* TALER_JSON_LIB_H_ */
+
+/* End of taler_json_lib.h */
diff --git a/src/include/taler_microhttpd_lib.h b/src/include/taler_microhttpd_lib.h
new file mode 100644
index 000000000..da601401f
--- /dev/null
+++ b/src/include/taler_microhttpd_lib.h
@@ -0,0 +1,119 @@
+
+
+#ifndef TALER_MICROHTTPD_LIB_H_
+#define TALER_MICROHTTPD_LIB_H_
+
+
+#include <microhttpd.h>
+#include <jansson.h>
+
+
+/**
+ * Constants for JSON navigation description.
+ */
+enum
+{
+ /**
+ * Access a field.
+ * Param: const char *
+ */
+ JNAV_FIELD,
+ /**
+ * Access an array index.
+ * Param: int
+ */
+ JNAV_INDEX,
+ /**
+ * Return base32crockford encoded data of
+ * constant size.
+ * Params: (void *, size_t)
+ */
+ JNAV_RET_DATA,
+ /**
+ * Return base32crockford encoded data of
+ * variable size.
+ * Params: (void **, size_t *)
+ */
+ JNAV_RET_DATA_VAR,
+ /**
+ * Return a json object, which must be
+ * of the given type (JSON_* type constants,
+ * or -1 for any type).
+ * Params: (int, json_t **)
+ */
+ JNAV_RET_TYPED_JSON
+};
+
+
+
+/**
+ * Send JSON object as response. Decreases
+ * the reference count of the JSON object.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param status_code the http status code
+ * @return MHD result code (MHD_YES on success)
+ */
+int
+send_response_json (struct MHD_Connection *connection,
+ json_t *json,
+ unsigned int status_code);
+
+
+/**
+ * Send a JSON object via an MHD connection,
+ * specified with the JANSSON pack syntax (see json_pack).
+ *
+ * @param connection connection to send the JSON over
+ * @param http_code HTTP status for the response
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD_YES on success or MHD_NO on error
+ */
+int
+request_send_json_pack (struct MHD_Connection *connection,
+ unsigned int http_code,
+ const char *fmt, ...);
+
+
+/**
+ * Process a POST request containing a JSON object.
+ *
+ * @param connection the MHD connection
+ * @param con_cs the closure (contains a 'struct Buffer *')
+ * @param upload_data the POST data
+ * @param upload_data_size the POST data size
+ * @param json the JSON object for a completed request
+ *
+ * @returns
+ * GNUNET_YES if json object was parsed
+ * GNUNET_NO is request incomplete or invalid
+ * GNUNET_SYSERR on internal error
+ */
+int
+process_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json);
+
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see JNAV_*)
+ * @return GNUNET_YES if navigation was successful
+ * GNUNET_NO if json is malformed, error response was generated
+ * GNUNET_SYSERR on internal error
+ */
+int
+request_json_require_nav (struct MHD_Connection *connection,
+ const json_t *root, ...);
+
+#endif /* TALER_MICROHTTPD_LIB_H_ */
diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h
new file mode 100644
index 000000000..ee3b30e39
--- /dev/null
+++ b/src/include/taler_mint_service.h
@@ -0,0 +1,303 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_mint_service.h
+ * @brief C interface to the mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef _TALER_MINT_SERVICE_H
+#define _TALER_MINT_SERVICE_H
+
+#include "taler_rsa.h"
+#include "taler_util.h"
+#include <jansson.h>
+
+/**
+ * Handle to this library context
+ */
+struct TALER_MINT_Context;
+
+/**
+ * Handle to the mint
+ */
+struct TALER_MINT_Handle;
+
+/**
+ * Mint's signature key
+ */
+struct TALER_MINT_SigningPublicKey
+{
+ /**
+ * The signing public key
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey key;
+
+ /**
+ * Validity start time
+ */
+ struct GNUNET_TIME_Absolute valid_from;
+
+ /**
+ * Validity expiration time
+ */
+ struct GNUNET_TIME_Absolute valid_until;
+};
+
+
+/**
+ * Mint's denomination key
+ */
+struct TALER_MINT_DenomPublicKey
+{
+ /**
+ * The public key
+ */
+ struct TALER_RSA_PublicKeyBinaryEncoded key;
+
+ /**
+ * Timestamp indicating when the denomination key becomes valid
+ */
+ struct GNUNET_TIME_Absolute valid_from;
+
+ /**
+ * Timestamp indicating when the denomination key can’t be used anymore to
+ * withdraw new coins.
+ */
+ struct GNUNET_TIME_Absolute withdraw_valid_until;
+
+ /**
+ * Timestamp indicating when coins of this denomination become invalid.
+ */
+ struct GNUNET_TIME_Absolute deposit_valid_until;
+
+ /**
+ * The value of this denomination
+ */
+ struct TALER_Amount value;
+
+ /**
+ * The applicable fee for withdrawing a coin of this denomination
+ */
+ struct TALER_Amount fee_withdraw;
+
+ /**
+ * The applicable fee to spend a coin of this denomination
+ */
+ struct TALER_Amount fee_deposit;
+
+ /**
+ *The applicable fee to refresh a coin of this denomination
+ */
+ struct TALER_Amount fee_refresh;
+};
+
+
+/**
+ * Initialise a context. A context should be used for each thread and should
+ * not be shared among multiple threads.
+ *
+ * @return the context
+ */
+struct TALER_MINT_Context *
+TALER_MINT_init ();
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MINT_cleanup (struct TALER_MINT_Context *ctx);
+
+
+/**
+ * Initialise a connection to the mint.
+ *
+ * @param ctx the context
+ * @param hostname the hostname of the mint
+ * @param port the point where the mint's HTTP service is running. If port is
+ * given as 0, ports 80 or 443 are chosen depending on @a url.
+ * @param mint_key the public key of the mint. This is used to verify the
+ * responses of the mint.
+ * @return the mint handle; NULL upon error
+ */
+struct TALER_MINT_Handle *
+TALER_MINT_connect (struct TALER_MINT_Context *ctx,
+ const char *hostname,
+ uint16_t port,
+ struct GNUNET_CRYPTO_EddsaPublicKey *mint_key);
+
+/**
+ * Disconnect from the mint
+ *
+ * @param mint the mint handle
+ */
+void
+TALER_MINT_disconnect (struct TALER_MINT_Handle *mint);
+
+
+/**
+ * A handle to get the keys of a mint
+ */
+struct TALER_MINT_KeysGetHandle;
+
+/**
+ * Functions of this type are called to signal completion of an asynchronous call.
+ *
+ * @param cls closure
+ * @param emsg if the asynchronous call could not be completed due to an error,
+ * this parameter contains a human readable error message
+ */
+typedef void (*TALER_MINT_ContinuationCallback) (void *cls,
+ const char *emsg);
+
+/**
+ * Functions of this type are called to provide the retrieved signing and
+ * denomination keys of the mint. No TALER_MINT_*() functions should be called
+ * in this callback.
+ *
+ * @param cls closure passed to TALER_MINT_keys_get()
+ * @param sign_keys NULL-terminated array of pointers to the mint's signing
+ * keys. NULL if no signing keys are retrieved.
+ * @param denom_keys NULL-terminated array of pointers to the mint's
+ * denomination keys; will be NULL if no signing keys are retrieved.
+ */
+typedef void (*TALER_MINT_KeysGetCallback) (void *cls,
+ struct TALER_MINT_SigningPublicKey **sign_keys,
+ struct TALER_MINT_DenomPublicKey **denom_keys);
+
+
+/**
+ * Get the signing and denomination key of the mint.
+ *
+ * @param mint handle to the mint
+ * @param cb the callback to call with the keys
+ * @param cls closure for the above callback
+ * @param cont_cb the callback to call after completing this asynchronous call
+ * @param cont_cls the closure for the continuation callback
+ * @return a handle to this asynchronous call; NULL upon eror
+ */
+struct TALER_MINT_KeysGetHandle *
+TALER_MINT_keys_get (struct TALER_MINT_Handle *mint,
+ TALER_MINT_KeysGetCallback cb, void *cls,
+ TALER_MINT_ContinuationCallback cont_cb, void *cont_cls);
+
+/**
+ * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This should
+ * not be called if either of the @a TALER_MINT_KeysGetCallback or @a
+ * TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have been
+ * called.
+ *
+ * @param get the handle for retrieving the keys
+ */
+void
+TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get);
+
+
+/**
+ * A Deposit Handle
+ */
+struct TALER_MINT_DepositHandle;
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a deposit
+ * permission object to a mint
+ *
+ * @param cls closure
+ * @param status 1 for successful deposit, 2 for retry, 0 for failure
+ * @param obj the received JSON object; can be NULL if it cannot be constructed
+ * from the reply
+ * @param emsg in case of unsuccessful deposit, this contains a human readable
+ * explanation.
+ */
+typedef void (*TALER_MINT_DepositResultCallback) (void *cls,
+ int status,
+ json_t *obj,
+ char *emsg);
+
+/**
+ * Submit a deposit permission to the mint and get the mint's response
+ *
+ * @param mint the mint handle
+ * @param cb the callback to call when a reply for this request is available
+ * @param cls closure for the above callback
+ * @param deposit_obj the deposit permission received from the customer along
+ * with the wireformat JSON object
+ * @return a handle for this request; NULL if the JSON object could not be
+ * parsed or is of incorrect format or any other error. In this case,
+ * the callback is not called.
+ */
+struct TALER_MINT_DepositHandle *
+TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint,
+ TALER_MINT_DepositResultCallback cb,
+ void *cls,
+ json_t *deposit_obj);
+
+
+#if 0
+/**
+ * Submit a deposit permission to the mint and get the mint's response.
+ *
+ * @param mint the mint handle
+ * @param cb the callback to call when a reply for this request is available
+ * @param cls closure for the above callback
+ * @param coin the public key of the coin
+ * @param denom_key denomination key of the mint which is used to blind-sign the
+ * coin
+ * @param ubsig the mint's unblinded signature
+ * @param transaction_id transaction identifier
+ * @param amount the amount to deposit
+ * @param merchant_pub the public key of the merchant
+ * @param h_contract hash of the contract
+ * @param h_wire hash of the wire format used
+ * @param csig signature of the coin over the transaction_id, amount,
+ * merchant_pub, h_contract and, h_wire
+ * @param wire_obj the wireformat object corresponding to h_wire
+ * @return a handle for this request
+ */
+struct TALER_MINT_DepositHandle *
+TALER_MINT_deposit_submit_json_ (struct TALER_MINT_Handle *mint,
+ TALER_MINT_DepositResultCallback *cb,
+ void *cls,
+ struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
+ struct TALER_BLIND_SigningPublicKey *denom_pub,
+ struct TALER_BLIND_Signature *ubsig,
+ uint64_t transaction_id,
+ struct TALER_Amount *amount,
+ struct GNUNET_CRYPTO_EddsaPublicKey *merchant_pub,
+ struct GNUNET_HashCode *h_contract,
+ struct GNUNET_HashCode *h_wire,
+ struct GNUNET_CRYPTO_EddsaSignature *csig,
+ json_t *wire_obj);
+#endif
+
+
+/**
+ * Cancel a deposit permission request. This function cannot be used on a
+ * request handle if a response is already served for it.
+ *
+ * @param the deposit permission request handle
+ */
+void
+TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit);
+
+#endif /* _TALER_MINT_SERVICE_H */
diff --git a/src/include/taler_rsa.h b/src/include/taler_rsa.h
new file mode 100644
index 000000000..1ed530013
--- /dev/null
+++ b/src/include/taler_rsa.h
@@ -0,0 +1,357 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_rsa.h
+ * @brief RSA key management utilities. Some code is taken from gnunet-0.9.5a
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ *
+ * Authors of the gnunet code:
+ * Christian Grothoff
+ * Krista Bennett
+ * Gerd Knorr <kraxel@bytesex.org>
+ * Ioana Patrascu
+ * Tzvetan Horozov
+ */
+
+#ifndef TALER_RSA_H
+#define TALER_RSA_H
+
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_crypto_lib.h>
+
+/**
+ * Length of an RSA KEY (n,e,len), 2048 bit (=256 octests) key n, 2 byte e
+ */
+#define TALER_RSA_KEY_LENGTH 258
+
+/**
+ * @brief Length of RSA encrypted data (2048 bit)
+ *
+ * We currently do not handle encryption of data
+ * that can not be done in a single call to the
+ * RSA methods (read: large chunks of data).
+ * We should never need that, as we can use
+ * the GNUNET_CRYPTO_hash for larger pieces of data for signing,
+ * and for encryption, we only need to encode sessionkeys!
+ */
+#define TALER_RSA_DATA_ENCODING_LENGTH 256
+
+/**
+ * The private information of an RSA key pair.
+ */
+struct TALER_RSA_PrivateKey;
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * GNUnet mandates a certain format for the encoding
+ * of private RSA key information that is provided
+ * by the RSA implementations. This format is used
+ * to serialize a private RSA key (typically when
+ * writing it to disk).
+ */
+struct TALER_RSA_PrivateKeyBinaryEncoded
+{
+ /**
+ * Total size of the structure, in bytes, in big-endian!
+ */
+ uint16_t len GNUNET_PACKED;
+ uint16_t sizen GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizee GNUNET_PACKED; /* in big-endian! */
+ uint16_t sized GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizep GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizeq GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */
+ /* followed by the actual values */
+};
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * @brief an RSA signature
+ */
+struct TALER_RSA_Signature
+{
+ unsigned char sig[TALER_RSA_DATA_ENCODING_LENGTH];
+};
+
+GNUNET_NETWORK_STRUCT_BEGIN
+/**
+ * @brief header of what an RSA signature signs
+ * this must be followed by "size - 8" bytes of
+ * the actual signed data
+ */
+struct TALER_RSA_SignaturePurpose
+{
+ /**
+ * How many bytes does this signature sign?
+ * (including this purpose header); in network
+ * byte order (!).
+ */
+ uint32_t size GNUNET_PACKED;
+
+ /**
+ * What does this signature vouch for? This
+ * must contain a GNUNET_SIGNATURE_PURPOSE_XXX
+ * constant (from gnunet_signatures.h). In
+ * network byte order!
+ */
+ uint32_t purpose GNUNET_PACKED;
+
+};
+
+
+struct TALER_RSA_BlindedSignaturePurpose
+{
+ unsigned char data[TALER_RSA_DATA_ENCODING_LENGTH];
+};
+
+
+/**
+ * @brief A public key.
+ */
+struct TALER_RSA_PublicKeyBinaryEncoded
+{
+ /**
+ * In big-endian, must be GNUNET_CRYPTO_RSA_KEY_LENGTH+4
+ */
+ uint16_t len GNUNET_PACKED;
+
+ /**
+ * Size of n in key; in big-endian!
+ */
+ uint16_t sizen GNUNET_PACKED;
+
+ /**
+ * The key itself, contains n followed by e.
+ */
+ unsigned char key[TALER_RSA_KEY_LENGTH];
+
+ /**
+ * Padding (must be 0)
+ */
+ uint16_t padding GNUNET_PACKED;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Create a new private key. Caller must free return value.
+ *
+ * @return fresh private key
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_key_create ();
+
+
+/**
+ * Free memory occupied by the private key.
+ *
+ * @param key pointer to the memory to free
+ */
+void
+TALER_RSA_key_free (struct TALER_RSA_PrivateKey *key);
+
+
+/**
+ * Encode the private key in a format suitable for
+ * storing it into a file.
+ * @return encoding of the private key
+ */
+struct TALER_RSA_PrivateKeyBinaryEncoded *
+TALER_RSA_encode_key (const struct TALER_RSA_PrivateKey *hostkey);
+
+
+/**
+ * Extract the public key of the given private key.
+ *
+ * @param priv the private key
+ * @param pub where to write the public key
+ */
+void
+TALER_RSA_key_get_public (const struct TALER_RSA_PrivateKey *priv,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub);
+
+
+/**
+ * Decode the private key from the data-format back
+ * to the "normal", internal format.
+ *
+ * @param buf the buffer where the private key data is stored
+ * @param len the length of the data in 'buffer'
+ * @return NULL on error
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_decode_key (const char *buf, uint16_t len);
+
+
+/**
+ * Convert a public key to a string.
+ *
+ * @param pub key to convert
+ * @return string representing 'pub'
+ */
+char *
+TALER_RSA_public_key_to_string (const struct TALER_RSA_PublicKeyBinaryEncoded *pub);
+
+
+/**
+ * Convert a string representing a public key to a public key.
+ *
+ * @param enc encoded public key
+ * @param enclen number of bytes in enc (without 0-terminator)
+ * @param pub where to store the public key
+ * @return GNUNET_OK on success
+ */
+int
+TALER_RSA_public_key_from_string (const char *enc,
+ size_t enclen,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub);
+
+
+/**
+ * Sign a given block.h
+ *
+ * @param key private key to use for the signing
+ * @param msg the message
+ * @param size the size of the message
+ * @param sig where to write the signature
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+TALER_RSA_sign (const struct TALER_RSA_PrivateKey *key,
+ const void *msg,
+ size_t size,
+ struct TALER_RSA_Signature *sig);
+
+
+/**
+ * Verify signature with the given hash.
+ *
+ * @param hash the hash code to verify against the signature
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_hash_verify (const struct GNUNET_HashCode *hash,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey);
+
+
+/**
+ * Verify signature on the given message
+ *
+ * @param msg the message
+ * @param size the size of the message
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_verify (const void *msg, size_t size,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey);
+
+/**
+ * Key used to blind a message
+ */
+struct TALER_RSA_BlindingKey;
+
+/**
+ * Create a blinding key
+ *
+ * @return the newly created blinding key
+ */
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_create ();
+
+
+/**
+ * Destroy a blinding key
+ *
+ * @param bkey the blinding key to destroy
+ */
+void
+TALER_RSA_blinding_key_destroy (struct TALER_RSA_BlindingKey *bkey);
+
+
+/**
+ * Binary encoding for TALER_RSA_BlindingKey
+ */
+struct TALER_RSA_BlindingKeyBinaryEncoded
+{
+ unsigned char data[TALER_RSA_DATA_ENCODING_LENGTH];
+};
+
+
+/**
+ * Encode a blinding key
+ *
+ * @param bkey the blinding key to encode
+ * @param bkey_enc where to store the encoded binary key
+ * @return #GNUNET_OK upon successful encoding; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_RSA_blinding_key_encode (struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc);
+
+
+/**
+ * Decode a blinding key from its encoded form
+ *
+ * @param bkey_enc the encoded blinding key
+ * @return the decoded blinding key; NULL upon error
+ */
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_decode (struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc);
+
+
+/**
+ * Blinds the given message with the given blinding key
+ *
+ * @param msg the message
+ * @param size the size of the message
+ * @param bkey the blinding key
+ * @param pkey the public key of the signer
+ * @return the blinding signature purpose; NULL upon any error
+ */
+struct TALER_RSA_BlindedSignaturePurpose *
+TALER_RSA_message_blind (const void *msg, size_t size,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey);
+
+
+/**
+ * Unblind a signature made on blinding signature purpose. The signature
+ * purpose should have been generated with TALER_RSA_message_blind() function.
+ *
+ * @param sig the signature made on the blinded signature purpose
+ * @param bkey the blinding key used to blind the signature purpose
+ * @param pkey the public key of the signer
+ * @return GNUNET_SYSERR upon error; GNUNET_OK upon success.
+ */
+int
+TALER_RSA_unblind (struct TALER_RSA_Signature *sig,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey);
+
+#endif /* TALER_RSA_H */
+
+/* end of include/taler_rsa.h */
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
new file mode 100644
index 000000000..8c142f61f
--- /dev/null
+++ b/src/include/taler_signatures.h
@@ -0,0 +1,106 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-keyup.c
+ * @brief Update the mint's keys for coins and signatures,
+ * using the mint's offline master key.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#ifndef TALER_SIGNATURES_H
+#define TALER_SIGNATURES_H
+
+/**
+ * Purpose for signing public keys signed
+ * by the mint master key.
+ */
+#define TALER_SIGNATURE_MASTER_SIGNKEY 1
+
+/**
+ * Purpose for denomination keys signed
+ * by the mint master key.
+ */
+#define TALER_SIGNATURE_MASTER_DENOM 2
+
+/**
+ * Purpose for the state of a reserve,
+ * signed by the mint's signing key.
+ */
+#define TALER_SIGNATURE_RESERVE_STATUS 3
+
+/**
+ * Signature where the reserve key
+ * confirms a withdraw request.
+ */
+#define TALER_SIGNATURE_WITHDRAW 4
+
+/**
+ * Signature where the refresh session confirms
+ * the list of melted coins and requested denominations.
+ */
+#define TALER_SIGNATURE_REFRESH_MELT 5
+
+/**
+ * Signature where the refresh session confirms
+ * the commits.
+ */
+#define TALER_SIGNATURE_REFRESH_COMMIT 6
+
+/**
+ * Signature where the mint (current signing key)
+ * confirms the list of blind session keys.
+ */
+#define TALER_SIGNATURE_REFRESH_MELT_RESPONSE 7
+
+/**
+ * Signature where the mint (current signing key)
+ * confirms the no-reveal index for cut-and-choose.
+ */
+#define TALER_SIGNATURE_REFRESH_COMMIT_RESPONSE 8
+
+/**
+ * Signature where coins confirm that they want
+ * to be melted into a certain session.
+ */
+#define TALER_SIGNATURE_REFRESH_MELT_CONFIRM 9
+
+/***********************/
+/* Merchant signatures */
+/***********************/
+
+/**
+ * Signature where the merchant confirms a contract
+ */
+#define TALER_SIGNATURE_MERCHANT_CONTRACT 101
+
+/*********************/
+/* Wallet signatures */
+/*********************/
+
+/**
+ * Signature made by the wallet of a user to confirm a deposit permission
+ */
+#define TALER_SIGNATURE_DEPOSIT 201
+
+/**
+ * Signature made by the wallet of a user to confirm a incremental deposit permission
+ */
+#define TALER_SIGNATURE_INCREMENTAL_DEPOSIT 202
+
+#endif
+
diff --git a/src/include/taler_types.h b/src/include/taler_types.h
new file mode 100644
index 000000000..c6c2c0209
--- /dev/null
+++ b/src/include/taler_types.h
@@ -0,0 +1,120 @@
+/**
+ * @file include/types.h
+ * @brief This files defines the various data and message types in TALER.
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ */
+
+#ifndef TYPES_H_
+#define TYPES_H_
+
+#include "taler_rsa.h"
+
+
+/**
+ * Public information about a coin.
+ */
+struct TALER_CoinPublicInfo
+{
+ /**
+ * The coin's public key.
+ */
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+
+ /*
+ * The public key signifying the coin's denomination.
+ */
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+
+ /**
+ * Signature over coin_pub by denom_pub.
+ */
+ struct TALER_RSA_Signature denom_sig;
+};
+
+
+/**
+ * Request to withdraw coins from a reserve.
+ */
+struct TALER_WithdrawRequest
+{
+ /**
+ * Signature over the rest of the message
+ * by the withdraw public key.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+
+ /**
+ * Purpose must be TALER_SIGNATURE_WITHDRAW.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Reserve public key.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+
+ /**
+ * Denomination public key for the coin that is withdrawn.
+ */
+ struct TALER_RSA_PublicKeyBinaryEncoded denomination_pub;
+
+ /**
+ * Purpose containing coin's blinded public key.
+ */
+ struct TALER_RSA_BlindedSignaturePurpose coin_envelope;
+};
+
+
+
+/**
+ * Data type for messages
+ */
+struct TALER_MessageHeader
+{
+ /**
+ * The type of the message in Network-byte order (NBO)
+ */
+ uint16_t type;
+
+ /**
+ * The size of the message in NBO
+ */
+ uint16_t size;
+};
+
+/*****************/
+/* Message types */
+/*****************/
+
+/**
+ * The message type of a blind signature
+ */
+#define TALER_MSG_TYPE_BLINDED_SIGNATURE 1
+
+/**
+ * The message type of a blinded message
+ */
+#define TALER_MSG_TYPE_BLINDED_MESSAGE 2
+
+/**
+ * The message type of an unblinded signature
+ * @FIXME: Not currently used
+ */
+#define TALER_MSG_TYPE_UNBLINDED_SIGNATURE 3
+
+/**
+ * The type of a blinding residue message
+ * @FIXME: Not currently used
+ */
+#define TALER_MSG_TYPE_BLINDING_RESIDUE 4
+
+/**
+ * The type of a message containing the blinding factor
+ */
+#define TALER_MSG_TYPE_BLINDING_FACTOR 5
+
+
+#endif /* TYPES_H_ */
+
+/* end of include/types.h */
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
new file mode 100644
index 000000000..a8a7c2013
--- /dev/null
+++ b/src/include/taler_util.h
@@ -0,0 +1,255 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_util.h
+ * @brief Interface for common utility functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <gnunet/gnunet_util_lib.h>
+#include <gcrypt.h>
+
+/* Define logging functions */
+#define LOG_DEBUG(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
+
+#define LOG_WARNING(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, __VA_ARGS__)
+
+#define LOG_ERROR(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__)
+
+
+/**
+ * Tests a given as assertion and if failed prints it as a warning with the
+ * given reason
+ *
+ * @param EXP the expression to test as assertion
+ * @param reason string to print as warning
+ */
+#define TALER_assert_as(EXP, reason) \
+ do { \
+ if (EXP) break; \
+ LOG_ERROR("%s at %s:%d\n", reason, __FILE__, __LINE__); \
+ abort(); \
+ } while(0)
+
+
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' with the message given
+ * by gcry_strerror(rc).
+ */
+#define LOG_GCRY_ERROR(cmd, rc) do { LOG_ERROR("`%s' failed at %s:%d with error: %s\n", cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0)
+
+
+#define TALER_gcry_ok(cmd) \
+ do {int rc; rc = cmd; if (!rc) break; LOG_ERROR("A Gcrypt call failed at %s:%d with error: %s\n", __FILE__, __LINE__, gcry_strerror(rc)); abort(); } while (0)
+
+
+#define TALER_CURRENCY_LEN 4
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct TALER_AmountNBO
+{
+ uint32_t value;
+ uint32_t fraction;
+ char currency[TALER_CURRENCY_LEN];
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+struct TALER_HashContext
+{
+ gcry_md_hd_t hd;
+};
+
+
+
+/**
+ * Representation of monetary value in a given currency.
+ */
+struct TALER_Amount
+{
+ /**
+ * Value (numerator of fraction)
+ */
+ uint32_t value;
+ /**
+ * Fraction (denominator of fraction)
+ */
+ uint32_t fraction;
+ /**
+ * Currency string, left adjusted and padded with zeros.
+ */
+ char currency[4];
+};
+
+
+/**
+ * Initialize Gcrypt library.
+ */
+void
+TALER_gcrypt_init();
+
+
+/**
+ * Generate a ECC private key.
+ *
+ * @return the s-expression representing the generated ECC private key; NULL
+ * upon error
+ */
+gcry_sexp_t
+TALER_genkey ();
+
+
+/**
+ * Parse denomination description, in the format "T : V : F".
+ *
+ * @param str denomination description
+ * @param denom denomination to write the result to
+ * @return GNUNET_OK if the string is a valid denomination specification,
+ * GNUNET_SYSERR if it is invalid.
+ */
+int
+TALER_string_to_amount (const char *str, struct TALER_Amount *denom);
+
+
+/**
+ * FIXME
+ */
+struct TALER_AmountNBO
+TALER_amount_hton (struct TALER_Amount d);
+
+
+/**
+ * FIXME
+ */
+struct TALER_Amount
+TALER_amount_ntoh (struct TALER_AmountNBO dn);
+
+/**
+ * Compare the value/fraction of two amounts. Does not compare the currency,
+ * i.e. comparing amounts with the same value and fraction but different
+ * currency would return 0.
+ *
+ * @param a1 first amount
+ * @param a2 second amount
+ * @return result of the comparison
+ */
+int
+TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2);
+
+
+/**
+ * Perform saturating subtraction of amounts.
+ *
+ * @param a1 amount to subtract from
+ * @param a2 amount to subtract
+ * @return (a1-a2) or 0 if a2>=a1
+ */
+struct TALER_Amount
+TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2);
+
+
+/**
+ * Perform saturating addition of amounts
+ *
+ * @param a1 first amount to add
+ * @param a2 second amount to add
+ * @return sum of a1 and a2
+ */
+struct TALER_Amount
+TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2);
+
+
+/**
+ * Normalize the given amount.
+ *
+ * @param amout amount to normalize
+ * @return normalized amount
+ */
+struct TALER_Amount
+TALER_amount_normalize (struct TALER_Amount amount);
+
+
+/**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return freshly allocated string representation
+ */
+char *
+TALER_amount_to_string (struct TALER_Amount amount);
+
+
+/**
+ * Return the base32crockford encoding of the given buffer.
+ *
+ * The returned string will be freshly allocated, and must be free'd
+ * with GNUNET_free.
+ *
+ * @param buffer with data
+ * @param size size of the buffer
+ * @return freshly allocated, null-terminated string
+ */
+char *
+TALER_data_to_string_alloc (const void *buf, size_t size);
+
+
+/**
+ * Get encoded binary data from a configuration.
+ *
+ * @return GNUNET_OK on success
+ * GNUNET_NO is the value does not exist
+ * GNUNET_SYSERR on encoding error
+ */
+int
+TALER_configuration_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section, const char *option,
+ void *buf, size_t buf_size);
+
+
+
+
+int
+TALER_refresh_decrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result);
+
+int
+TALER_refresh_encrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result);
+
+
+
+void
+TALER_hash_context_start (struct TALER_HashContext *hc);
+
+
+void
+TALER_hash_context_read (struct TALER_HashContext *hc, void *buf, size_t size);
+
+
+void
+TALER_hash_context_finish (struct TALER_HashContext *hc,
+ struct GNUNET_HashCode *r_hash);
+
+#endif
diff --git a/src/mint/.gitignore b/src/mint/.gitignore
new file mode 100644
index 000000000..a2e71d5da
--- /dev/null
+++ b/src/mint/.gitignore
@@ -0,0 +1,6 @@
+taler-mint-dbinit
+taler-mint-keycheck
+taler-mint-keyup
+taler-mint-pursemod
+taler-mint-reservemod
+taler-mint-httpd \ No newline at end of file
diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am
new file mode 100644
index 000000000..2ae153485
--- /dev/null
+++ b/src/mint/Makefile.am
@@ -0,0 +1,131 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
+
+lib_LTLIBRARIES = \
+ libtalermint.la \
+ libtalermintapi.la
+
+libtalermint_la_SOURCES = \
+ mint_common.c \
+ mint_db.c
+
+libtalermint_la_LIBADD = \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -lpq
+
+libtalermint_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtalermintapi_la_SOURCES = \
+ mint_api.c
+
+libtalermintapi_la_LIBADD = \
+ -lgnunetutil \
+ -ljansson \
+ -lcurl
+
+libtalermintapi_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+
+bin_PROGRAMS = \
+ taler-mint-keyup \
+ taler-mint-keycheck \
+ taler-mint-reservemod \
+ taler-mint-httpd \
+ taler-mint-dbinit
+
+taler_mint_keyup_SOURCES = \
+ taler-mint-keyup.c
+
+taler_mint_keyup_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+
+taler_mint_keycheck_SOURCES = \
+ taler-mint-keycheck.c
+
+taler_mint_keycheck_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lgnunetutil \
+ -lpq
+taler_mint_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+taler_mint_reservemod_SOURCES = \
+ taler-mint-reservemod.c
+taler_mint_reservemod_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_reservemod_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+
+taler_mint_httpd_SOURCES = \
+ taler-mint-httpd.c \
+ taler-mint-httpd_mhd.c \
+ taler-mint-httpd_keys.c \
+ taler-mint-httpd_deposit.c \
+ taler-mint-httpd_withdraw.c \
+ taler-mint-httpd_refresh.c
+taler_mint_httpd_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lmicrohttpd \
+ -ljansson \
+ -lgnunetutil \
+ -lpthread
+taler_mint_httpd_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+
+
+taler_mint_dbinit_SOURCES = \
+ taler-mint-dbinit.c
+taler_mint_dbinit_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_dbinit_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+check_PROGRAMS = \
+ test-mint-api \
+ test-mint-deposits \
+ test-mint-common
+
+test_mint_api_SOURCES = test_mint_api.c
+test_mint_api_LDADD = \
+ libtalermintapi.la \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -ljansson
+
+test_mint_deposits_SOURCES = \
+ test_mint_deposits.c
+test_mint_deposits_LDADD = \
+ libtalermint.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -lpq
+
+test_mint_common_SOURCES = \
+ test_mint_common.c
+test_mint_common_LDADD = \
+ libtalermint.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ -lgnunetutil
diff --git a/src/mint/mint.h b/src/mint/mint.h
new file mode 100644
index 000000000..5adce03c6
--- /dev/null
+++ b/src/mint/mint.h
@@ -0,0 +1,198 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler_mint.h
+ * @brief Common functionality for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#ifndef _MINT_H
+#define _MINT_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_common.h>
+#include <libpq-fe.h>
+#include "taler_util.h"
+#include "taler_rsa.h"
+
+#define DIR_SIGNKEYS "signkeys"
+#define DIR_DENOMKEYS "denomkeys"
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+
+/**
+ * FIXME
+ */
+struct TALER_MINT_SignKeyIssue
+{
+ struct GNUNET_CRYPTO_EddsaPrivateKey signkey_priv;
+ struct GNUNET_CRYPTO_EddsaSignature signature;
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_CRYPTO_EddsaPublicKey master_pub;
+ struct GNUNET_TIME_AbsoluteNBO start;
+ struct GNUNET_TIME_AbsoluteNBO expire;
+ struct GNUNET_CRYPTO_EddsaPublicKey signkey_pub;
+};
+
+struct TALER_MINT_DenomKeyIssue
+{
+ /**
+ * The private key of the denomination. Will be NULL if the private key is
+ * not available.
+ */
+ struct TALER_RSA_PrivateKey *denom_priv;
+ struct GNUNET_CRYPTO_EddsaSignature signature;
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_CRYPTO_EddsaPublicKey master;
+ struct GNUNET_TIME_AbsoluteNBO start;
+ struct GNUNET_TIME_AbsoluteNBO expire_withdraw;
+ struct GNUNET_TIME_AbsoluteNBO expire_spend;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_AmountNBO value;
+ struct TALER_AmountNBO fee_withdraw;
+ struct TALER_AmountNBO fee_deposit;
+ struct TALER_AmountNBO fee_refresh;
+};
+
+struct RefreshMeltSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode melt_hash;
+};
+
+struct RefreshCommitSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode commit_hash;
+};
+
+struct RefreshCommitResponseSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ uint16_t noreveal_index;
+};
+
+struct RefreshMeltResponseSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode melt_response_hash;
+};
+
+
+struct RefreshMeltConfirmSignRequestBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+
+/**
+ * Iterator for sign keys.
+ *
+ * @param cls closure
+ * @param ski the sign key issue
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int (*TALER_MINT_SignkeyIterator)(void *cls,
+ const struct TALER_MINT_SignKeyIssue *ski);
+
+/**
+ * Iterator for denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key issue
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int (*TALER_MINT_DenomkeyIterator)(void *cls,
+ const char *alias,
+ const struct TALER_MINT_DenomKeyIssue *dki);
+
+
+
+/**
+ * FIXME
+ */
+int
+TALER_MINT_signkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_SignkeyIterator it, void *cls);
+
+
+/**
+ * FIXME
+ */
+int
+TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_DenomkeyIterator it, void *cls);
+
+
+/**
+ * Exports a denomination key to the given file
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINT_write_denom_key (const char *filename,
+ const struct TALER_MINT_DenomKeyIssue *dki);
+
+
+/**
+ * Import a denomination key from the given file
+ *
+ * @param filename the file to import the key from
+ * @param dki pointer to return the imported denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINT_read_denom_key (const char *filename,
+ struct TALER_MINT_DenomKeyIssue *dki);
+
+
+/**
+ * Load the configuration for the mint in the given
+ * directory.
+ *
+ * @param mint_base_dir the mint's base directory
+ * @return the mint configuratin, or NULL on error
+ */
+struct GNUNET_CONFIGURATION_Handle *
+TALER_MINT_config_load (const char *mint_base_dir);
+
+
+int
+TALER_TALER_DB_extract_amount (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_Amount *denom);
+
+int
+TALER_TALER_DB_extract_amount_nbo (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_AmountNBO *denom_nbo);
+
+#endif /* _MINT_H */
+
diff --git a/src/mint/mint_api.c b/src/mint/mint_api.c
new file mode 100644
index 000000000..b8d42b274
--- /dev/null
+++ b/src/mint/mint_api.c
@@ -0,0 +1,1121 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/mint_api.c
+ * @brief Implementation of the client interface to mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mint_service.h"
+#include "taler_signatures.h"
+#include "mint.h"
+
+#define CURL_STRERROR(TYPE, FUNCTION, CODE) \
+ GNUNET_log (TYPE, "cURL function `%s' has failed at `%s:%d' with error: %s", \
+ FUNCTION, __FILE__, __LINE__, curl_easy_strerror (CODE));
+
+
+
+/**
+ * Print JSON parsing related error information
+ */
+#define JSON_WARN(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)", \
+ __FILE__, __LINE__, error.text, error.source)
+
+/**
+ * Failsafe flag
+ */
+static int fail;
+
+/**
+ * Context
+ */
+struct TALER_MINT_Context
+{
+ /**
+ * CURL multi handle
+ */
+ CURLM *multi;
+
+ /**
+ * CURL share handle
+ */
+ CURLSH *share;
+
+ /**
+ * Perform task handle
+ */
+ struct GNUNET_SCHEDULER_Task *perform_task;
+};
+
+/**
+ * Type of requests we currently have
+ */
+enum RequestType
+{
+ /**
+ * No request
+ */
+ REQUEST_TYPE_NONE,
+
+ /**
+ * Current request is to receive mint's keys
+ */
+ REQUEST_TYPE_KEYSGET,
+
+ /**
+ * Current request is to submit a deposit permission and get its status
+ */
+ REQUEST_TYPE_DEPOSIT
+};
+
+
+/**
+ * Handle to the mint
+ */
+struct TALER_MINT_Handle
+{
+ /**
+ * The context of this handle
+ */
+ struct TALER_MINT_Context *ctx;
+
+ /**
+ * The hostname of the mint
+ */
+ char *hostname;
+
+ /**
+ * The CURL handle
+ */
+ CURL *curl;
+
+ /**
+ * Error buffer for CURL
+ */
+ char emsg[CURL_ERROR_SIZE];
+
+ /**
+ * Download buffer
+ */
+ void *buf;
+
+ /**
+ * The currently active request
+ */
+ union {
+ /**
+ * Used to denote no request if set to NULL
+ */
+ void *none;
+
+ /**
+ * Denom keys get request if REQUEST_TYPE_KEYSGET
+ */
+ struct TALER_MINT_KeysGetHandle *keys_get;
+
+ /**
+ * Deposit request if REQUEST_TYPE_DEPOSIT
+ */
+ struct TALER_MINT_DepositHandle *deposit;
+ } req;
+
+ /**
+ * The size of the download buffer
+ */
+ size_t buf_size;
+
+ /**
+ * Active request type
+ */
+ enum RequestType req_type;
+
+ /**
+ * The service port of the mint
+ */
+ uint16_t port;
+
+ /**
+ * Are we connected to the mint?
+ */
+ uint8_t connected;
+
+};
+
+
+/**
+ * A handle to get the keys of a mint
+ */
+struct TALER_MINT_KeysGetHandle
+{
+ /**
+ * The connection to mint this request handle will use
+ */
+ struct TALER_MINT_Handle *mint;
+
+ /**
+ * The url for this handle
+ */
+ char *url;
+
+ TALER_MINT_KeysGetCallback cb;
+ void *cls;
+
+ TALER_MINT_ContinuationCallback cont_cb;
+ void *cont_cls;
+};
+
+
+/**
+ * A handle to submit a deposit permission and get its status
+ */
+struct TALER_MINT_DepositHandle
+{
+ /**
+ *The connection to mint this request handle will use
+ */
+ struct TALER_MINT_Handle *mint;
+
+ /**
+ * The url for this handle
+ */
+ char *url;
+
+ TALER_MINT_DepositResultCallback cb;
+ void *cls;
+
+ char *json_enc;
+
+ struct curl_slist *headers;
+
+};
+
+
+/**
+ * Parses the timestamp encoded as ASCII string as UNIX timstamp.
+ *
+ * @param abs successfully parsed timestamp will be returned thru this parameter
+ * @param tstamp_enc the ASCII encoding of the timestamp
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+parse_timestamp (struct GNUNET_TIME_Absolute *abs, const char *tstamp_enc)
+{
+ unsigned long tstamp;
+
+ if (1 != sscanf (tstamp_enc, "%lu", &tstamp))
+ return GNUNET_SYSERR;
+ *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (),
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, tstamp));
+ return GNUNET_OK;
+}
+
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+
+static int
+parse_json_signkey (struct TALER_MINT_SigningPublicKey **_sign_key,
+ json_t *sign_key_obj,
+ struct GNUNET_CRYPTO_EddsaPublicKey *master_key)
+{
+ json_t *valid_from_obj;
+ json_t *valid_until_obj;
+ json_t *key_obj;
+ json_t *sig_obj;
+ const char *valid_from_enc;
+ const char *valid_until_enc;
+ const char *key_enc;
+ const char *sig_enc;
+ struct TALER_MINT_SigningPublicKey *sign_key;
+ struct TALER_MINT_SignKeyIssue sign_key_issue;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+ struct GNUNET_TIME_Absolute valid_from;
+ struct GNUNET_TIME_Absolute valid_until;
+
+ EXITIF (JSON_OBJECT != json_typeof (sign_key_obj));
+ EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj,
+ "stamp_start")));
+ EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj,
+ "stamp_expire")));
+ EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key")));
+ EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig")));
+ EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj)));
+ EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj)));
+ EXITIF (NULL == (key_enc = json_string_value (key_obj)));
+ EXITIF (NULL == (sig_enc = json_string_value (sig_obj)));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from,
+ valid_from_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until,
+ valid_until_enc));
+ EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */
+ EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103,
+ &sig, sizeof (sig)));
+ (void) memset (&sign_key_issue, 0, sizeof (sign_key_issue));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc,
+ 52,
+ &sign_key_issue.signkey_pub));
+ sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY);
+ sign_key_issue.purpose.size =
+ htonl (sizeof (sign_key_issue)
+ - offsetof (struct TALER_MINT_SignKeyIssue, purpose));
+ sign_key_issue.master_pub = *master_key;
+ sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
+ sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until);
+ EXITIF (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNKEY,
+ &sign_key_issue.purpose,
+ &sig,
+ master_key));
+ sign_key = GNUNET_new (struct TALER_MINT_SigningPublicKey);
+ sign_key->valid_from = valid_from;
+ sign_key->valid_until = valid_until;
+ sign_key->key = sign_key_issue.signkey_pub;
+ *_sign_key = sign_key;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+
+static int
+parse_json_amount (json_t *amount_obj, struct TALER_Amount *amt)
+{
+ json_t *obj;
+ const char *currency_str;
+ int value;
+ int fraction;
+
+ EXITIF (NULL == (obj = json_object_get (amount_obj, "currency")));
+ EXITIF (NULL == (currency_str = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (amount_obj, "value")));
+ EXITIF (JSON_INTEGER != json_typeof (obj));
+ EXITIF (0 > (value = json_integer_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction")));
+ EXITIF (JSON_INTEGER != json_typeof (obj));
+ EXITIF (0 > (fraction = json_integer_value (obj)));
+ (void) memset (amt->currency, 0, sizeof (amt->currency));
+ (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1);
+ amt->value = (uint32_t) value;
+ amt->fraction = (uint32_t) fraction;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+static int
+parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key,
+ json_t *denom_key_obj,
+ struct GNUNET_CRYPTO_EddsaPublicKey *master_key)
+{
+ json_t *obj;
+ const char *sig_enc;
+ const char *deposit_valid_until_enc;
+ const char *withdraw_valid_until_enc;
+ const char *valid_from_enc;
+ const char *key_enc;
+ struct TALER_MINT_DenomPublicKey *denom_key;
+ struct GNUNET_TIME_Absolute valid_from;
+ struct GNUNET_TIME_Absolute withdraw_valid_until;
+ struct GNUNET_TIME_Absolute deposit_valid_until;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_deposit;
+ struct TALER_Amount fee_refresh;
+ struct TALER_MINT_DenomKeyIssue denom_key_issue;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+
+ EXITIF (JSON_OBJECT != json_typeof (denom_key_obj));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig")));
+ EXITIF (NULL == (sig_enc = json_string_value (obj)));
+ EXITIF (103 != strlen (sig_enc));
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103,
+ &sig, sizeof (sig)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit")));
+ EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw")));
+ EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start")));
+ EXITIF (NULL == (valid_from_enc = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub")));
+ EXITIF (NULL == (key_enc = json_string_value (obj)));
+ EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until,
+ withdraw_valid_until_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until,
+ deposit_valid_until_enc));
+
+ (void) memset (&denom_key_issue, 0, sizeof (denom_key_issue));
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (key_enc, 52,
+ &denom_key_issue.denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh));
+ denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM);
+ denom_key_issue.purpose.size = htonl
+ (sizeof (struct TALER_MINT_DenomKeyIssue) -
+ offsetof (struct TALER_MINT_DenomKeyIssue, purpose));
+ denom_key_issue.master = *master_key;
+ denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
+ denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until);
+ denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until);
+ denom_key_issue.value = TALER_amount_hton (value);
+ denom_key_issue.fee_withdraw = TALER_amount_hton (fee_withdraw);
+ denom_key_issue.fee_deposit = TALER_amount_hton (fee_deposit);
+ denom_key_issue.fee_refresh = TALER_amount_hton (fee_refresh);
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM,
+ &denom_key_issue.purpose,
+ &sig,
+ master_key));
+ denom_key = GNUNET_new (struct TALER_MINT_DenomPublicKey);
+ denom_key->key = denom_key_issue.denom_pub;
+ denom_key->valid_from = valid_from;
+ denom_key->withdraw_valid_until = withdraw_valid_until;
+ denom_key->deposit_valid_until = deposit_valid_until;
+ denom_key->value = value;
+ denom_key->fee_withdraw = fee_withdraw;
+ denom_key->fee_deposit = fee_deposit;
+ denom_key->fee_refresh = fee_refresh;
+ *_denom_key = denom_key;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+static int
+parse_response_keys_get (const char *in, size_t size,
+ struct TALER_MINT_SigningPublicKey ***_sign_keys,
+ unsigned int *_n_sign_keys,
+ struct TALER_MINT_DenomPublicKey ***_denom_keys,
+ unsigned int *_n_denom_keys)
+{
+ json_t *resp_obj;
+ struct TALER_MINT_DenomPublicKey **denom_keys;
+ struct GNUNET_CRYPTO_EddsaPublicKey master_key;
+ struct GNUNET_TIME_Absolute list_issue_date;
+ struct TALER_MINT_SigningPublicKey **sign_keys;
+ unsigned int n_denom_keys;
+ unsigned int n_sign_keys;
+ json_error_t error;
+ unsigned int index;
+ int OK;
+
+ denom_keys = NULL;
+ n_denom_keys = 0;
+ sign_keys = NULL;
+ n_sign_keys = 0;
+ OK = 0;
+ resp_obj = json_loadb (in, size,
+ JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
+ &error);
+ if (NULL == resp_obj)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unable to parse received data as JSON object\n");
+ return GNUNET_SYSERR;
+ }
+
+ EXITIF (JSON_OBJECT != json_typeof (resp_obj));
+ {
+ /* parse the master public key */
+ json_t *master_key_obj;
+ const char *master_key_enc;
+
+ EXITIF (NULL == (master_key_obj = json_object_get (resp_obj, "master_pub")));
+ EXITIF (NULL == (master_key_enc = json_string_value (master_key_obj)));
+ EXITIF (52 != strlen (master_key_enc)); /* strlen(base32(char[32])) = 52 */
+ EXITIF (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc,
+ 52,
+ &master_key));
+ }
+ {
+ /* parse the issue date of the response */
+ json_t *list_issue_date_obj;
+ const char *tstamp_enc;
+
+ EXITIF (NULL == (list_issue_date_obj =
+ json_object_get(resp_obj, "list_issue_date")));
+ EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj)));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date, tstamp_enc));
+ }
+ {
+ /* parse the signing keys */
+ json_t *sign_keys_array;
+ json_t *sign_key_obj;
+
+ EXITIF (NULL == (sign_keys_array =
+ json_object_get (resp_obj, "signkeys")));
+ EXITIF (JSON_ARRAY != json_typeof (sign_keys_array));
+ EXITIF (0 == (n_sign_keys = json_array_size (sign_keys_array)));
+ sign_keys = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey *)
+ * (n_sign_keys + 1));
+ index = 0;
+ json_array_foreach (sign_keys_array, index, sign_key_obj) {
+ EXITIF (GNUNET_SYSERR == parse_json_signkey (&sign_keys[index],
+ sign_key_obj,
+ &master_key));
+ }
+ }
+ {
+ /* parse the denomination keys */
+ json_t *denom_keys_array;
+ json_t *denom_key_obj;
+
+ EXITIF (NULL == (denom_keys_array = json_object_get (resp_obj, "denoms")));
+ EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
+ EXITIF (0 == (n_denom_keys = json_array_size (denom_keys_array)));
+ denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey *)
+ * (n_denom_keys + 1));
+ index = 0;
+ json_array_foreach (denom_keys_array, index, denom_key_obj) {
+ EXITIF (GNUNET_SYSERR == parse_json_denomkey (&denom_keys[index],
+ denom_key_obj,
+ &master_key));
+ }
+ }
+ OK = 1;
+
+ EXITIF_exit:
+ json_decref (resp_obj);
+ if (!OK)
+ {
+ if (NULL != sign_keys)
+ {
+ for (index=0; NULL != sign_keys[index]; index++)
+ GNUNET_free_non_null (sign_keys[index]);
+ GNUNET_free (sign_keys);
+ }
+ if (NULL != denom_keys)
+ {
+ for (index=0; NULL != denom_keys[index]; index++)
+ GNUNET_free_non_null (denom_keys[index]);
+ GNUNET_free (denom_keys);
+ }
+ return GNUNET_SYSERR;
+ }
+
+ *_sign_keys = sign_keys;
+ *_n_sign_keys = n_sign_keys;
+ *_denom_keys = denom_keys;
+ *_n_denom_keys = n_denom_keys;
+ return GNUNET_OK;
+}
+
+
+int
+parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj)
+{
+ json_t *obj;
+ const char *status_str;
+ json_error_t error;
+
+ status_str = NULL;
+ obj = NULL;
+ obj = json_loadb (buf, size,
+ JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, &error);
+ if (NULL == obj)
+ {
+ JSON_WARN (error);
+ return GNUNET_SYSERR;
+ }
+ EXITIF (-1 == json_unpack (obj, "{s:s}", "status", &status_str));
+ LOG_DEBUG ("Received deposit response: %s from mint\n", status_str);
+ if (0 == strcmp ("DEPOSIT_OK", status_str))
+ *r_status = 1;
+ else if (0 == strcmp ("DEPOSIT_QUEUED", status_str))
+ *r_status = 2;
+ else
+ *r_status = 0;
+ *r_obj = obj;
+
+ return GNUNET_OK;
+ EXITIF_exit:
+ json_decref (obj);
+ return GNUNET_SYSERR;
+}
+
+#undef EXITIF
+
+static void
+mint_connect (struct TALER_MINT_Handle *mint)
+{
+ struct TALER_MINT_Context *ctx = mint->ctx;
+
+ GNUNET_assert (0 == mint->connected);
+ GNUNET_assert (CURLM_OK == curl_multi_add_handle (ctx->multi, mint->curl));
+ mint->connected = GNUNET_YES;
+}
+
+static void
+mint_disconnect (struct TALER_MINT_Handle *mint)
+{
+ struct TALER_MINT_Context *ctx = mint->ctx;
+
+ GNUNET_assert (GNUNET_YES == mint->connected);
+ GNUNET_break (CURLM_OK == curl_multi_remove_handle (ctx->multi,
+ mint->curl));
+ mint->connected = GNUNET_NO;
+ GNUNET_free_non_null (mint->buf);
+ mint->buf = NULL;
+ mint->buf_size = 0;
+ mint->req_type = REQUEST_TYPE_NONE;
+ mint->req.none = NULL;
+}
+
+static void
+cleanup_keys_get (struct TALER_MINT_KeysGetHandle *gh)
+{
+ GNUNET_free (gh->url);
+ GNUNET_free (gh);
+}
+
+static void
+cleanup_deposit (struct TALER_MINT_DepositHandle *dh)
+{
+ curl_slist_free_all (dh->headers);
+ GNUNET_free_non_null (dh->json_enc);
+ GNUNET_free (dh->url);
+ GNUNET_free (dh);
+}
+
+static void
+request_failed (struct TALER_MINT_Handle *mint, long resp_code)
+{
+ switch (mint->req_type)
+ {
+ case REQUEST_TYPE_NONE:
+ GNUNET_assert (0);
+ break;
+ case REQUEST_TYPE_KEYSGET:
+ {
+ struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get;
+ TALER_MINT_ContinuationCallback cont_cb;
+ void *cont_cls;
+ GNUNET_assert (NULL != gh);
+ cont_cb = gh->cont_cb;
+ cont_cls = gh->cont_cls;
+ cleanup_keys_get (gh);
+ mint_disconnect (mint);
+ cont_cb (cont_cls, mint->emsg);
+ }
+ break;
+ case REQUEST_TYPE_DEPOSIT:
+ {
+ struct TALER_MINT_DepositHandle *dh = mint->req.deposit;
+ TALER_MINT_DepositResultCallback cb = dh->cb;
+ void *cls = dh->cls;
+ GNUNET_assert (NULL != dh);
+ cleanup_deposit (dh);
+ mint_disconnect (mint);
+ cb (cls, 0, NULL, mint->emsg);
+ }
+ break;
+ }
+}
+
+static void
+request_succeeded (struct TALER_MINT_Handle *mint, long resp_code)
+{
+ char *emsg;
+
+ emsg = NULL;
+ switch (mint->req_type)
+ {
+ case REQUEST_TYPE_NONE:
+ GNUNET_assert (0);
+ break;
+ case REQUEST_TYPE_KEYSGET:
+ {
+ struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get;
+ TALER_MINT_ContinuationCallback cont_cb;
+ void *cont_cls;
+ struct TALER_MINT_SigningPublicKey **sign_keys;
+ struct TALER_MINT_DenomPublicKey **denom_keys;
+ unsigned int n_sign_keys;
+ unsigned int n_denom_keys;
+
+ GNUNET_assert (NULL != gh);
+ cont_cb = gh->cont_cb;
+ cont_cls = gh->cont_cls;
+ if (200 == resp_code)
+ {
+ /* parse JSON object from the mint->buf which is of size mint->buf_size */
+ if (GNUNET_OK ==
+ parse_response_keys_get (mint->buf, mint->buf_size,
+ &sign_keys, &n_sign_keys,
+ &denom_keys, &n_denom_keys))
+ gh->cb (gh->cls, sign_keys, denom_keys);
+ else
+ emsg = GNUNET_strdup ("Error parsing response");
+ }
+ else
+ GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code);
+ cleanup_keys_get (gh);
+ mint_disconnect (mint);
+ cont_cb (cont_cls, emsg);
+ }
+ break;
+ case REQUEST_TYPE_DEPOSIT:
+ {
+ struct TALER_MINT_DepositHandle *dh = mint->req.deposit;
+ TALER_MINT_DepositResultCallback cb;
+ void *cls;
+ int status;
+ json_t *obj;
+
+ GNUNET_assert (NULL != dh);
+ obj = NULL;
+ cb = dh->cb;
+ cls = dh->cls;
+ status = 0;
+ if (200 == resp_code)
+ {
+ /* parse JSON object from the mint->buf which is of size mint->buf_size */
+ if (GNUNET_OK !=
+ parse_deposit_response (mint->buf, mint->buf_size,
+ &status, &obj))
+ emsg = GNUNET_strdup ("Error parsing response");
+ }
+ else
+ GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code);
+ cleanup_deposit (dh);
+ mint_disconnect (mint);
+ cb (cls, status, obj, emsg);
+ }
+ break;
+ }
+ GNUNET_free_non_null (emsg);
+}
+
+
+static void
+do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+static void
+perform (struct TALER_MINT_Context *ctx)
+{
+ fd_set fd_rs;
+ fd_set fd_ws;
+ struct GNUNET_NETWORK_FDSet rs;
+ struct GNUNET_NETWORK_FDSet ws;
+ CURLMsg *cmsg;
+ struct TALER_MINT_Handle *mint;
+ long timeout;
+ long resp_code;
+ static unsigned int n_old;
+ int n_running;
+ int n_completed;
+ int max_fd;
+
+ n_completed = 0;
+ curl_multi_perform (ctx->multi, &n_running);
+ GNUNET_assert (0 <= n_running);
+ if ((0 == n_running) || (n_running < n_old))
+ {
+ /* some requests were completed -- handle them */
+ while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed)))
+ {
+ GNUNET_break (CURLMSG_DONE == cmsg->msg); /* curl only has CURLMSG_DONE */
+ GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
+ CURLINFO_PRIVATE,
+ (char *) &mint));
+ GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
+ CURLINFO_RESPONSE_CODE,
+ &resp_code));
+ GNUNET_assert (ctx == mint->ctx); /* did we get the correct one? */
+ if (CURLE_OK == cmsg->data.result)
+ request_succeeded (mint, resp_code);
+ else
+ request_failed (mint, resp_code);
+ }
+ }
+ n_old = n_running;
+ /* reschedule perform() */
+ if (0 != n_old)
+ {
+ FD_ZERO (&fd_rs);
+ FD_ZERO (&fd_ws);
+ GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi,
+ &fd_rs,
+ &fd_ws,
+ NULL,
+ &max_fd));
+ if (-1 == max_fd)
+ {
+ ctx->perform_task = GNUNET_SCHEDULER_add_delayed
+ (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100),
+ &do_perform, ctx);
+ return;
+ }
+ GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, &timeout));
+ if (-1 == timeout)
+ {
+ timeout = 1000 * 60 * 5;
+ }
+ GNUNET_NETWORK_fdset_zero (&rs);
+ GNUNET_NETWORK_fdset_zero (&ws);
+ GNUNET_NETWORK_fdset_copy_native (&rs, &fd_rs, max_fd + 1);
+ GNUNET_NETWORK_fdset_copy_native (&ws, &fd_ws, max_fd + 1);
+ ctx->perform_task = GNUNET_SCHEDULER_add_select
+ (GNUNET_SCHEDULER_PRIORITY_KEEP,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout),
+ &rs, &ws,
+ &do_perform, ctx);
+ }
+}
+
+
+static void
+do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct TALER_MINT_Context *ctx = cls;
+
+ GNUNET_assert (NULL != ctx->perform_task);
+ ctx->perform_task = NULL;
+ perform (ctx);
+}
+
+static void
+perform_now (struct TALER_MINT_Context *ctx)
+{
+ if (NULL != ctx->perform_task)
+ {
+ GNUNET_SCHEDULER_cancel (ctx->perform_task);
+ ctx->perform_task = NULL;
+ }
+ ctx->perform_task = GNUNET_SCHEDULER_add_now (&do_perform, ctx);
+}
+
+
+/* This function gets called by libcurl as soon as there is data received that */
+/* needs to be saved. The size of the data pointed to by ptr is size */
+/* multiplied with nmemb, it will not be zero terminated. Return the number */
+/* of bytes actually taken care of. If that amount differs from the amount passed */
+/* to your function, it'll signal an error to the library. This will abort the */
+/* transfer and return CURLE_WRITE_ERROR. */
+
+/* From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will */
+/* cause writing to this connection to become paused. See */
+/* curl_easy_pause(3) for further details. */
+
+/* This function may be called with zero bytes data if the transferred file is */
+/* empty. */
+
+/* Set this option to NULL to get the internal default function. The internal */
+/* default function will write the data to the FILE * given with */
+/* CURLOPT_WRITEDATA. */
+
+/* Set the userdata argument with the CURLOPT_WRITEDATA option. */
+
+/* The callback function will be passed as much data as possible in all invokes, */
+/* but you cannot possibly make any assumptions. It may be one byte, it may be */
+/* thousands. The maximum amount of body data that can be passed to the write */
+/* callback is defined in the curl.h header file: CURL_MAX_WRITE_SIZE (the usual */
+/* default is 16K). If you however have CURLOPT_HEADER set, which sends */
+/* header data to the write callback, you can get up to */
+/* CURL_MAX_HTTP_HEADER bytes of header data passed into it. This usually */
+/* means 100K. */
+static size_t
+download (char *bufptr, size_t size, size_t nitems, void *cls)
+{
+ struct TALER_MINT_Handle *mint = cls;
+ size_t msize;
+ void *buf;
+
+ if (0 == size * nitems)
+ {
+ /* file is empty */
+ return 0;
+ }
+ msize = size * nitems;
+ mint->buf = GNUNET_realloc (mint->buf, mint->buf_size + msize);
+ buf = mint->buf + mint->buf_size;
+ memcpy (buf, bufptr, msize);
+ mint->buf_size += msize;
+ return msize;
+}
+
+
+/**
+ * Initialise a connection to the mint.
+ *
+ * @param ctx the context
+ * @param hostname the hostname of the mint
+ * @param port the point where the mint's HTTP service is running.
+ * @param mint_key the public key of the mint. This is used to verify the
+ * responses of the mint.
+ * @return the mint handle; NULL upon error
+ */
+struct TALER_MINT_Handle *
+TALER_MINT_connect (struct TALER_MINT_Context *ctx,
+ const char *hostname,
+ uint16_t port,
+ struct GNUNET_CRYPTO_EddsaPublicKey *mint_key)
+{
+ struct TALER_MINT_Handle *mint;
+
+ mint = GNUNET_new (struct TALER_MINT_Handle);
+ mint->ctx = ctx;
+ mint->hostname = GNUNET_strdup (hostname);
+ mint->port = (0 != port) ? port : 80;
+ mint->curl = curl_easy_init ();
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_SHARE, ctx->share));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_ERRORBUFFER, mint->emsg));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_WRITEFUNCTION, &download));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_WRITEDATA, mint));
+ GNUNET_assert (CURLE_OK == curl_easy_setopt (mint->curl, CURLOPT_PRIVATE, mint));
+ return mint;
+}
+
+/**
+ * Disconnect from the mint
+ *
+ * @param mint the mint handle
+ */
+void
+TALER_MINT_disconnect (struct TALER_MINT_Handle *mint)
+{
+ if (GNUNET_YES == mint->connected)
+ mint_disconnect (mint);
+ curl_easy_cleanup (mint->curl);
+ GNUNET_free (mint->hostname);
+ GNUNET_free (mint);
+}
+
+/**
+ * Get the signing and denomination key of the mint.
+ *
+ * @param mint handle to the mint
+ * @param cb the callback to call with each retrieved denomination key
+ * @param cls closure for the above callback
+ * @param cont_cb the callback to call after completing this asynchronous call
+ * @param cont_cls the closure for the continuation callback
+ * @return a handle to this asynchronous call; NULL upon eror
+ */
+struct TALER_MINT_KeysGetHandle *
+TALER_MINT_keys_get (struct TALER_MINT_Handle *mint,
+ TALER_MINT_KeysGetCallback cb, void *cls,
+ TALER_MINT_ContinuationCallback cont_cb, void *cont_cls)
+{
+ struct TALER_MINT_KeysGetHandle *gh;
+
+ GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type);
+ gh = GNUNET_new (struct TALER_MINT_KeysGetHandle);
+ gh->mint = mint;
+ mint->req_type = REQUEST_TYPE_KEYSGET;
+ mint->req.keys_get = gh;
+ gh->cb = cb;
+ gh->cls = cls;
+ gh->cont_cb = cont_cb;
+ gh->cont_cls = cont_cls;
+ GNUNET_asprintf (&gh->url, "http://%s:%hu/keys", mint->hostname, mint->port);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_URL, gh->url));
+ if (GNUNET_NO == mint->connected)
+ mint_connect (mint);
+ perform_now (mint->ctx);
+ return gh;
+}
+
+
+/**
+ * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This
+ * should not be called if either of the @a TALER_MINT_KeysGetCallback or
+ * @a TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have
+ * been called.
+ *
+ * @param get the handle for retrieving the keys
+ */
+void
+TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get)
+{
+ struct TALER_MINT_Handle *mint = get->mint;
+
+ mint_disconnect (mint);
+ cleanup_keys_get (get);
+}
+
+/**
+ * Submit a deposit permission to the mint and get the mint's response
+ *
+ * @param mint the mint handle
+ * @param cb the callback to call when a reply for this request is available
+ * @param cls closure for the above callback
+ * @param deposit_obj the deposit permission received from the customer along
+ * with the wireformat JSON object
+ * @return a handle for this request; NULL if the JSON object could not be
+ * parsed or is of incorrect format or any other error. In this case,
+ * the callback is not called.
+ */
+struct TALER_MINT_DepositHandle *
+TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint,
+ TALER_MINT_DepositResultCallback cb,
+ void *cls,
+ json_t *deposit_obj)
+{
+ struct TALER_MINT_DepositHandle *dh;
+
+ GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type);
+ dh = GNUNET_new (struct TALER_MINT_DepositHandle);
+ dh->mint = mint;
+ mint->req_type = REQUEST_TYPE_DEPOSIT;
+ mint->req.deposit = dh;
+ dh->cb = cb;
+ dh->cls = cls;
+ GNUNET_asprintf (&dh->url, "http://%s:%hu/deposit", mint->hostname, mint->port);
+ GNUNET_assert (NULL != (dh->json_enc = json_dumps (deposit_obj, JSON_COMPACT)));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_URL, dh->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDS,
+ dh->json_enc));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDSIZE,
+ strlen (dh->json_enc)));
+ GNUNET_assert (NULL != (dh->headers =
+ curl_slist_append (dh->headers, "Content-Type: application/json")));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_HTTPHEADER, dh->headers));
+ if (GNUNET_NO == mint->connected)
+ mint_connect (mint);
+ perform_now (mint->ctx);
+ return dh;
+}
+
+
+/**
+ * Cancel a deposit permission request. This function cannot be used on a
+ * request handle if a response is already served for it.
+ *
+ * @param the deposit permission request handle
+ */
+void
+TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit)
+{
+ struct TALER_MINT_Handle *mint = deposit->mint;
+
+ mint_disconnect (mint);
+ cleanup_deposit (deposit);
+}
+
+
+/**
+ * Initialise this library. This function should be called before using any of
+ * the following functions.
+ *
+ * @return library context
+ */
+struct TALER_MINT_Context *
+TALER_MINT_init ()
+{
+ struct TALER_MINT_Context *ctx;
+ CURLM *multi;
+ CURLSH *share;
+
+ if (fail)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "cURL was not initialised properly\n");
+ return NULL;
+ }
+ if (NULL == (multi = curl_multi_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL multi handle\n");
+ return NULL;
+ }
+ if (NULL == (share = curl_share_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL share handle\n");
+ return NULL;
+ }
+ ctx = GNUNET_new (struct TALER_MINT_Context);
+ ctx->multi = multi;
+ ctx->share = share;
+ return ctx;
+}
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MINT_cleanup (struct TALER_MINT_Context *ctx)
+{
+ curl_share_cleanup (ctx->share);
+ curl_multi_cleanup (ctx->multi);
+ if (NULL != ctx->perform_task)
+ {
+ GNUNET_break (0); /* investigate why this happens */
+ GNUNET_SCHEDULER_cancel (ctx->perform_task);
+ }
+ GNUNET_free (ctx);
+}
+
+
+__attribute__ ((constructor))
+void
+TALER_MINT_constructor__ (void)
+{
+ CURLcode ret;
+ if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
+ {
+ CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, "curl_global_init", ret);
+ fail = 1;
+ }
+}
+
+__attribute__ ((destructor))
+void
+TALER_MINT_destructor__ (void)
+{
+ if (fail)
+ return;
+ curl_global_cleanup ();
+}
diff --git a/src/mint/mint_common.c b/src/mint/mint_common.c
new file mode 100644
index 000000000..4afbf072b
--- /dev/null
+++ b/src/mint/mint_common.c
@@ -0,0 +1,283 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint_common.c
+ * @brief Common functionality for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Sree Harsha Totakura
+ */
+
+#include "platform.h"
+#include "mint.h"
+
+struct SignkeysIterateContext
+{
+ TALER_MINT_SignkeyIterator it;
+ void *it_cls;
+};
+
+
+struct DenomkeysIterateContext
+{
+ const char *alias;
+ TALER_MINT_DenomkeyIterator it;
+ void *it_cls;
+};
+
+
+static int
+signkeys_iterate_dir_iter (void *cls,
+ const char *filename)
+{
+
+ struct SignkeysIterateContext *skc = cls;
+ ssize_t nread;
+ struct TALER_MINT_SignKeyIssue issue;
+ nread = GNUNET_DISK_fn_read (filename,
+ &issue,
+ sizeof (struct TALER_MINT_SignKeyIssue));
+ if (nread != sizeof (struct TALER_MINT_SignKeyIssue))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid signkey file: '%s'\n", filename);
+ return GNUNET_OK;
+ }
+ return skc->it (skc->it_cls, &issue);
+}
+
+
+int
+TALER_MINT_signkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_SignkeyIterator it, void *cls)
+{
+ char *signkey_dir;
+ size_t len;
+ struct SignkeysIterateContext skc;
+
+ len = GNUNET_asprintf (&signkey_dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mint_base_dir);
+ GNUNET_assert (len > 0);
+
+ skc.it = it;
+ skc.it_cls = cls;
+
+ return GNUNET_DISK_directory_scan (signkey_dir, &signkeys_iterate_dir_iter, &skc);
+}
+
+
+/**
+ * Import a denomination key from the given file
+ *
+ * @param filename the file to import the key from
+ * @param dki pointer to return the imported denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINT_read_denom_key (const char *filename,
+ struct TALER_MINT_DenomKeyIssue *dki)
+{
+ uint64_t size;
+ size_t offset;
+ void *data;
+ struct TALER_RSA_PrivateKey *priv;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ data = NULL;
+ offset = sizeof (struct TALER_MINT_DenomKeyIssue)
+ - offsetof (struct TALER_MINT_DenomKeyIssue, signature);
+ if (GNUNET_OK != GNUNET_DISK_file_size (filename,
+ &size,
+ GNUNET_YES,
+ GNUNET_YES))
+ goto cleanup;
+ if (size <= offset)
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ data = GNUNET_malloc (size);
+ if (size != GNUNET_DISK_fn_read (filename,
+ data,
+ size))
+ goto cleanup;
+ if (NULL == (priv = TALER_RSA_decode_key (data + offset, size - offset)))
+ goto cleanup;
+ dki->denom_priv = priv;
+ (void) memcpy (&dki->signature, data, offset);
+ ret = GNUNET_OK;
+
+ cleanup:
+ GNUNET_free_non_null (data);
+ return ret;
+}
+
+
+/**
+ * Exports a denomination key to the given file
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINT_write_denom_key (const char *filename,
+ const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ struct TALER_RSA_PrivateKeyBinaryEncoded *priv_enc;
+ struct GNUNET_DISK_FileHandle *fh;
+ ssize_t wrote;
+ size_t wsize;
+ int ret;
+
+ fh = NULL;
+ priv_enc = NULL;
+ ret = GNUNET_SYSERR;
+ if (NULL == (fh = GNUNET_DISK_file_open
+ (filename,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE,
+ GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE)))
+ goto cleanup;
+ if (NULL == (priv_enc = TALER_RSA_encode_key (dki->denom_priv)))
+ goto cleanup;
+ wsize = sizeof (struct TALER_MINT_DenomKeyIssue)
+ - offsetof (struct TALER_MINT_DenomKeyIssue, signature);
+ if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh,
+ &dki->signature,
+ wsize)))
+ goto cleanup;
+ if (wrote != wsize)
+ goto cleanup;
+ wsize = ntohs (priv_enc->len);
+ if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh,
+ priv_enc,
+ wsize)))
+ goto cleanup;
+ if (wrote != wsize)
+ goto cleanup;
+ ret = GNUNET_OK;
+ cleanup:
+ GNUNET_free_non_null (priv_enc);
+ if (NULL != fh)
+ (void) GNUNET_DISK_file_close (fh);
+ return ret;
+}
+
+
+static int
+denomkeys_iterate_keydir_iter (void *cls,
+ const char *filename)
+{
+
+ struct DenomkeysIterateContext *dic = cls;
+ struct TALER_MINT_DenomKeyIssue issue;
+
+ if (GNUNET_OK != TALER_MINT_read_denom_key (filename, &issue))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid denomkey file: '%s'\n", filename);
+ return GNUNET_OK;
+ }
+ return dic->it (dic->it_cls, dic->alias, &issue);
+}
+
+
+static int
+denomkeys_iterate_topdir_iter (void *cls,
+ const char *filename)
+{
+
+ struct DenomkeysIterateContext *dic = cls;
+ dic->alias = GNUNET_STRINGS_get_short_name (filename);
+
+ // FIXME: differentiate between error case and normal iteration abortion
+ if (0 > GNUNET_DISK_directory_scan (filename, &denomkeys_iterate_keydir_iter, dic))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_DenomkeyIterator it, void *cls)
+{
+ char *dir;
+ size_t len;
+ struct DenomkeysIterateContext dic;
+ len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS),
+ mint_base_dir);
+ GNUNET_assert (len > 0);
+
+ dic.it = it;
+ dic.it_cls = cls;
+
+ // scan over alias dirs
+ return GNUNET_DISK_directory_scan (dir, &denomkeys_iterate_topdir_iter, &dic);
+}
+
+
+struct GNUNET_CONFIGURATION_Handle *
+TALER_MINT_config_load (const char *mint_base_dir)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ char *cfg_dir;
+ int res;
+
+ res = GNUNET_asprintf (&cfg_dir, "%s" DIR_SEPARATOR_STR "config", mint_base_dir);
+ GNUNET_assert (res > 0);
+
+ cfg = GNUNET_CONFIGURATION_create ();
+ res = GNUNET_CONFIGURATION_load_from (cfg, cfg_dir);
+ GNUNET_free (cfg_dir);
+ if (GNUNET_OK != res)
+ return NULL;
+ return cfg;
+}
+
+int
+TALER_TALER_DB_extract_amount_nbo (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_AmountNBO *denom_nbo)
+{
+ if ((indices[0] < 0) || (indices[1] < 0) || (indices[2] < 0))
+ return GNUNET_NO;
+ if (sizeof (uint32_t) != PQgetlength (result, row, indices[0]))
+ return GNUNET_SYSERR;
+ if (sizeof (uint32_t) != PQgetlength (result, row, indices[1]))
+ return GNUNET_SYSERR;
+ if (PQgetlength (result, row, indices[2]) > TALER_CURRENCY_LEN)
+ return GNUNET_SYSERR;
+ denom_nbo->value = *(uint32_t *) PQgetvalue (result, row, indices[0]);
+ denom_nbo->fraction = *(uint32_t *) PQgetvalue (result, row, indices[1]);
+ memset (denom_nbo->currency, 0, TALER_CURRENCY_LEN);
+ memcpy (denom_nbo->currency, PQgetvalue (result, row, indices[2]), PQgetlength (result, row, indices[2]));
+ return GNUNET_OK;
+}
+
+
+int
+TALER_TALER_DB_extract_amount (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_Amount *denom)
+{
+ struct TALER_AmountNBO denom_nbo;
+ int res;
+
+ res = TALER_TALER_DB_extract_amount_nbo (result, row, indices, &denom_nbo);
+ if (GNUNET_OK != res)
+ return res;
+ *denom = TALER_amount_ntoh (denom_nbo);
+ return GNUNET_OK;
+}
+
+/* end of mint_common.c */
diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c
new file mode 100644
index 000000000..6dc025877
--- /dev/null
+++ b/src/mint/mint_db.c
@@ -0,0 +1,1838 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint_db.c
+ * @brief Database access for the mint
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include "taler_db_lib.h"
+#include "taler_signatures.h"
+#include "mint_db.h"
+#include "mint.h"
+#include <pthread.h>
+
+/**
+ * Thread-local database connection.
+ * Contains a pointer to PGconn or NULL.
+ */
+static pthread_key_t db_conn_threadlocal;
+
+
+/**
+ * Database connection string, as read from
+ * the configuration.
+ */
+static char *TALER_MINT_db_connection_cfg_str;
+
+
+#define break_db_err(result) do { \
+ GNUNET_break(0); \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
+ } while (0)
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+int
+TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,
+ struct TALER_RSA_BlindedSignaturePurpose *blind_ev,
+ struct CollectableBlindcoin *collectable)
+{
+ PGresult *result;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (blind_ev),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (db_conn, "get_collectable_blindcoins", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("blind_ev_sig", &collectable->ev_sig),
+ TALER_DB_RESULT_SPEC("denom_pub", &collectable->denom_pub),
+ TALER_DB_RESULT_SPEC("reserve_sig", &collectable->reserve_sig),
+ TALER_DB_RESULT_SPEC("reserve_pub", &collectable->reserve_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ (void) memcpy (&collectable->ev, blind_ev, sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,
+ const struct CollectableBlindcoin *collectable)
+{
+ PGresult *result;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (&collectable->ev),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->ev_sig),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->denom_pub),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_pub),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (db_conn, "insert_collectable_blindcoins", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Insert failed (updated '%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_reserve (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub,
+ struct Reserve *reserve)
+{
+ PGresult *result;
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ result = TALER_DB_exec_prepared (db_conn, "get_reserve", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ reserve->reserve_pub = *reserve_pub;
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("status_sig", &reserve->status_sig),
+ TALER_DB_RESULT_SPEC("status_sign_pub", &reserve->status_sign_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ res = TALER_DB_extract_result (result, rs, 0);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ int fnums[] = {
+ PQfnumber (result, "balance_value"),
+ PQfnumber (result, "balance_fraction"),
+ PQfnumber (result, "balance_currency"),
+ };
+ if (GNUNET_OK != TALER_TALER_DB_extract_amount_nbo (result, 0, fnums, &reserve->balance))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ /* FIXME: Add expiration?? */
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/* If fresh is GNUNET_YES, set some fields to NULL as they are not actually valid */
+int
+TALER_MINT_DB_update_reserve (PGconn *db_conn,
+ const struct Reserve *reserve,
+ int fresh)
+{
+ PGresult *result;
+ uint64_t stamp_sec;
+
+ stamp_sec = GNUNET_ntohll (GNUNET_TIME_absolute_ntoh (reserve->expiration).abs_value_us / 1000000);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (&reserve->reserve_pub),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->balance.value),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->balance.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (&reserve->balance.currency,
+ strlen (reserve->balance.currency)),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->status_sig),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->status_sign_pub),
+ TALER_DB_QUERY_PARAM_PTR (&stamp_sec),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ /* set some fields to NULL if they are not actually valid */
+
+ if (GNUNET_YES == fresh)
+ {
+ unsigned i;
+ for (i = 4; i <= 7; i += 1)
+ {
+ params[i].data = NULL;
+ params[i].size = 0;
+ }
+ }
+
+ result = TALER_DB_exec_prepared (db_conn, "update_reserve", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Update failed (updated '%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+
+int
+TALER_MINT_DB_prepare (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQprepare (db_conn, "get_reserve",
+ "SELECT "
+ " balance_value, balance_fraction, balance_currency "
+ ",expiration_date, blind_session_pub, blind_session_priv"
+ ",status_sig, status_sign_pub "
+ "FROM reserves "
+ "WHERE reserve_pub=$1 "
+ "LIMIT 1; ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "update_reserve",
+ "UPDATE reserves "
+ "SET"
+ " balance_value=$2 "
+ ",balance_fraction=$3 "
+ ",balance_currency=$4 "
+ ",status_sig=$5 "
+ ",status_sign_pub=$6 "
+ ",expiration_date=$7 "
+ "WHERE reserve_pub=$1 ",
+ 9, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ result = PQprepare (db_conn, "insert_collectable_blindcoins",
+ "INSERT INTO collectable_blindcoins ( "
+ " blind_ev, blind_ev_sig "
+ ",denom_pub, reserve_pub, reserve_sig) "
+ "VALUES ($1, $2, $3, $4, $5)",
+ 6, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_collectable_blindcoins",
+ "SELECT "
+ "blind_ev_sig, denom_pub, reserve_sig, reserve_pub "
+ "FROM collectable_blindcoins "
+ "WHERE blind_ev = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_reserve_order",
+ "SELECT "
+ " blind_ev, blind_ev_sig, denom_pub, reserve_sig, reserve_pub "
+ "FROM collectable_blindcoins "
+ "WHERE blind_session_pub = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ /* FIXME: does it make sense to store these computed values in the DB? */
+ result = PQprepare (db_conn, "get_refresh_session",
+ "SELECT "
+ " (SELECT count(*) FROM refresh_melt WHERE session_pub = $1)::INT2 as num_oldcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_pub = $1 and cnc_index = 0)::INT2 as num_newcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_pub = $1 and newcoin_index = 0)::INT2 as kappa "
+ ",noreveal_index"
+ ",session_commit_sig "
+ ",reveal_ok "
+ "FROM refresh_sessions "
+ "WHERE session_pub = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_known_coin",
+ "SELECT "
+ " coin_pub, denom_pub, denom_sig "
+ ",expended_value, expended_fraction, expended_currency "
+ ",refresh_session_pub "
+ "FROM known_coins "
+ "WHERE coin_pub = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "update_known_coin",
+ "UPDATE known_coins "
+ "SET "
+ " denom_pub = $2 "
+ ",denom_sig = $3 "
+ ",expended_value = $4 "
+ ",expended_fraction = $5 "
+ ",expended_currency = $6 "
+ ",refresh_session_pub = $7 "
+ "WHERE "
+ " coin_pub = $1 ",
+ 7, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_known_coin",
+ "INSERT INTO known_coins ("
+ " coin_pub"
+ ",denom_pub"
+ ",denom_sig"
+ ",expended_value"
+ ",expended_fraction"
+ ",expended_currency"
+ ",refresh_session_pub"
+ ")"
+ "VALUES ($1,$2,$3,$4,$5,$6,$7)",
+ 7, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_commit_link",
+ "SELECT "
+ " transfer_pub "
+ ",link_secret_enc "
+ "FROM refresh_commit_link "
+ "WHERE session_pub = $1 AND cnc_index = $2 AND oldcoin_index = $3",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_commit_coin",
+ "SELECT "
+ " link_vector_enc "
+ ",coin_ev "
+ "FROM refresh_commit_coin "
+ "WHERE session_pub = $1 AND cnc_index = $2 AND newcoin_index = $3",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_order",
+ "INSERT INTO refresh_order ( "
+ " newcoin_index "
+ ",session_pub "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_melt",
+ "INSERT INTO refresh_melt ( "
+ " session_pub "
+ ",oldcoin_index "
+ ",coin_pub "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3, $4) ",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_order",
+ "SELECT denom_pub "
+ "FROM refresh_order "
+ "WHERE session_pub = $1 AND newcoin_index = $2",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_collectable",
+ "SELECT ev_sig "
+ "FROM refresh_collectable "
+ "WHERE session_pub = $1 AND newcoin_index = $2",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_melt",
+ "SELECT coin_pub "
+ "FROM refresh_melt "
+ "WHERE session_pub = $1 AND oldcoin_index = $2",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_session",
+ "INSERT INTO refresh_sessions ( "
+ " session_pub "
+ ",noreveal_index "
+ ") "
+ "VALUES ($1, $2) ",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_commit_link",
+ "INSERT INTO refresh_commit_link ( "
+ " session_pub "
+ ",transfer_pub "
+ ",cnc_index "
+ ",oldcoin_index "
+ ",link_secret_enc "
+ ") "
+ "VALUES ($1, $2, $3, $4, $5) ",
+ 5, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_commit_coin",
+ "INSERT INTO refresh_commit_coin ( "
+ " session_pub "
+ ",coin_ev "
+ ",cnc_index "
+ ",newcoin_index "
+ ",link_vector_enc "
+ ") "
+ "VALUES ($1, $2, $3, $4, $5) ",
+ 5, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_collectable",
+ "INSERT INTO refresh_collectable ( "
+ " session_pub "
+ ",newcoin_index "
+ ",ev_sig "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "set_reveal_ok",
+ "UPDATE refresh_sessions "
+ "SET reveal_ok = TRUE "
+ "WHERE session_pub = $1 ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_link",
+ "SELECT link_vector_enc, ro.denom_pub, ev_sig "
+ "FROM refresh_melt rm "
+ " JOIN refresh_order ro USING (session_pub) "
+ " JOIN refresh_commit_coin rcc USING (session_pub) "
+ " JOIN refresh_sessions rs USING (session_pub) "
+ " JOIN refresh_collectable rc USING (session_pub) "
+ "WHERE rm.coin_pub = $1 "
+ "AND ro.newcoin_index = rcc.newcoin_index "
+ "AND ro.newcoin_index = rc.newcoin_index "
+ "AND rcc.cnc_index = rs.noreveal_index % ( "
+ " SELECT count(*) FROM refresh_commit_coin rcc2 "
+ " WHERE rcc2.newcoin_index = 0 AND rcc2.session_pub = rs.session_pub "
+ " ) ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_transfer",
+ "SELECT transfer_pub, link_secret_enc "
+ "FROM refresh_melt rm "
+ " JOIN refresh_commit_link rcl USING (session_pub) "
+ " JOIN refresh_sessions rs USING (session_pub) "
+ "WHERE rm.coin_pub = $1 "
+ "AND rm.oldcoin_index = rcl.oldcoin_index "
+ "AND rcl.cnc_index = rs.noreveal_index % ( "
+ " SELECT count(*) FROM refresh_commit_coin rcc2 "
+ " WHERE newcoin_index = 0 AND rcc2.session_pub = rm.session_pub "
+ " ) ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ if (GNUNET_OK != TALER_MINT_DB_prepare_deposits (db_conn))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Roll back the current transaction of a database connection.
+ *
+ * @param db_conn the database connection
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_rollback (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQexec(db_conn, "ROLLBACK");
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Roll back the current transaction of a database connection.
+ *
+ * @param db_conn the database connection
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_commit (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQexec(db_conn, "COMMIT");
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Start a transaction.
+ *
+ * @param db_conn the database connection
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_transaction (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQexec(db_conn, "BEGIN");
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't start transaction: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Insert a refresh order into the database.
+ */
+int
+TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(denom_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_order", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ struct RefreshSession *session)
+{
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_session", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ return GNUNET_NO;
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ /* We're done if the caller is only interested in
+ * whether the session exists or not */
+
+ if (NULL == session)
+ return GNUNET_YES;
+
+ memset (session, 0, sizeof (struct RefreshSession));
+
+ session->session_pub = *refresh_session_pub;
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("num_oldcoins", &session->num_oldcoins),
+ TALER_DB_RESULT_SPEC("num_newcoins", &session->num_newcoins),
+ TALER_DB_RESULT_SPEC("kappa", &session->kappa),
+ TALER_DB_RESULT_SPEC("noreveal_index", &session->noreveal_index),
+ TALER_DB_RESULT_SPEC("reveal_ok", &session->reveal_ok),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ res = TALER_DB_extract_result (result, rs, 0);
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (TALER_DB_field_isnull (result, 0, "session_commit_sig"))
+ session->has_commit_sig = GNUNET_NO;
+ else
+ session->has_commit_sig = GNUNET_YES;
+
+ session->num_oldcoins = ntohs (session->num_oldcoins);
+ session->num_newcoins = ntohs (session->num_newcoins);
+ session->kappa = ntohs (session->kappa);
+ session->noreveal_index = ntohs (session->noreveal_index);
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_get_known_coin (PGconn *db_conn, struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct KnownCoin *known_coin)
+{
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_known_coin", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ return GNUNET_NO;
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ /* extract basic information about the known coin */
+
+ {
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("coin_pub", &known_coin->public_info.coin_pub),
+ TALER_DB_RESULT_SPEC("denom_pub", &known_coin->public_info.denom_pub),
+ TALER_DB_RESULT_SPEC("denom_sig", &known_coin->public_info.denom_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != (res = TALER_DB_extract_result (result, rs, 0)))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ /* extract the expended amount of the coin */
+
+ if (GNUNET_OK != TALER_DB_extract_amount (result, 0,
+ "expended_value",
+ "expended_fraction",
+ "expended_currency",
+ &known_coin->expended_balance))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ /* extract the refresh session of the coin or mark it as missing */
+
+ {
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("refresh_session_pub", &known_coin->refresh_session_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_SYSERR == (res = TALER_DB_extract_result (result, rs, 0)))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_NO == res)
+ {
+ known_coin->is_refreshed = GNUNET_NO;
+ memset (&known_coin->refresh_session_pub, 0, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ }
+ else
+ {
+ known_coin->is_refreshed = GNUNET_YES;
+ }
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_create_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ uint16_t noreveal_index;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&noreveal_index),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);
+ noreveal_index = htonl (noreveal_index);
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_session", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_set_commit_signature (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct GNUNET_CRYPTO_EddsaSignature *commit_sig)
+{
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+int
+TALER_MINT_DB_set_reveal_ok (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "set_reveal_ok", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_update_known_coin (PGconn *db_conn,
+ const struct KnownCoin *known_coin)
+{
+ struct TALER_AmountNBO expended_nbo = TALER_amount_hton (known_coin->expended_balance);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_sig),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(expended_nbo.currency, strlen (expended_nbo.currency)),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ if (GNUNET_NO == known_coin->is_refreshed)
+ {
+ // Mind the magic index!
+ params[6].data = NULL;
+ params[6].size = 0;
+ }
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "update_known_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ PQclear (result);
+ // return 'no' here (don't fail) so that we can
+ // insert if update fails (=> "upsert")
+ return GNUNET_NO;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+int
+TALER_MINT_DB_insert_known_coin (PGconn *db_conn,
+ const struct KnownCoin *known_coin)
+{
+ struct TALER_AmountNBO expended_nbo = TALER_amount_hton (known_coin->expended_balance);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_sig),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(&expended_nbo.currency, strlen (expended_nbo.currency)),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ if (GNUNET_NO == known_coin->is_refreshed)
+ {
+ // Mind the magic index!
+ params[6].data = NULL;
+ params[6].size = 0;
+ }
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_known_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ PQclear (result);
+ // return 'no' here (don't fail) so that we can
+ // update if insert fails (=> "upsert")
+ return GNUNET_NO;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_upsert_known_coin (PGconn *db_conn, struct KnownCoin *known_coin)
+{
+ int ret;
+ ret = TALER_MINT_DB_update_known_coin (db_conn, known_coin);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_YES == ret)
+ return GNUNET_YES;
+ return TALER_MINT_DB_insert_known_coin (db_conn, known_coin);
+}
+
+
+int
+TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, struct RefreshCommitLink *commit_link)
+{
+ uint16_t cnc_index_nbo = htons (commit_link->cnc_index);
+ uint16_t oldcoin_index_nbo = htons (commit_link->oldcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&commit_link->session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&commit_link->transfer_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(&commit_link->shared_secret_enc, sizeof (struct GNUNET_HashCode)),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_link", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, struct RefreshCommitCoin *commit_coin)
+{
+ uint16_t cnc_index_nbo = htons (commit_coin->cnc_index);
+ uint16_t newcoin_index_nbo = htons (commit_coin->newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&commit_coin->session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&commit_coin->coin_ev),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(&commit_coin->link_enc, sizeof (struct LinkData)),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int cnc_index, int oldcoin_index,
+ struct RefreshCommitLink *cc)
+{
+ uint16_t cnc_index_nbo = htons (cnc_index);
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ cc->cnc_index = cnc_index;
+ cc->oldcoin_index = oldcoin_index;
+ cc->session_pub = *refresh_session_pub;
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_link", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("transfer_pub", &cc->transfer_pub),
+ TALER_DB_RESULT_SPEC_SIZED("link_secret_enc", &cc->shared_secret_enc,
+ TALER_REFRESH_SHARED_SECRET_LENGTH),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_free (cc);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int cnc_index, int newcoin_index,
+ struct RefreshCommitCoin *cc)
+{
+ uint16_t cnc_index_nbo = htons (cnc_index);
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ cc->cnc_index = cnc_index;
+ cc->newcoin_index = newcoin_index;
+ cc->session_pub = *refresh_session_pub;
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_coin", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("coin_ev", &cc->coin_ev),
+ TALER_DB_RESULT_SPEC_SIZED("link_vector_enc", &cc->link_enc,
+ TALER_REFRESH_LINK_LENGTH),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_order", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("denom_pub", denom_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_Signature *ev_sig)
+{
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(ev_sig),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_collectable", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_Signature *ev_sig)
+{
+
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_collectable", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("ev_sig", ev_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+
+int
+TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_PTR(denom_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_melt", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+
+int
+TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
+{
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_melt", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("coin_pub", coin_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_db_get_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ LinkIterator link_iter,
+ void *cls)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_link", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+
+ int i = 0;
+ int res;
+
+ for (i = 0; i < PQntuples (result); i++)
+ {
+ struct LinkDataEnc link_data_enc;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_RSA_Signature ev_sig;
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("link_vector_enc", &link_data_enc),
+ TALER_DB_RESULT_SPEC("denom_pub", &denom_pub),
+ TALER_DB_RESULT_SPEC("ev_sig", &ev_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, i))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != (res = link_iter (cls, &link_data_enc, &denom_pub, &ev_sig)))
+ {
+ GNUNET_assert (GNUNET_SYSERR != res);
+ PQclear (result);
+ return res;
+ }
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_db_get_transfer (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
+ struct SharedSecretEnc *shared_secret_enc)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_transfer", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ if (1 != PQntuples (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "got %d tuples for get_transfer\n", PQntuples (result));
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("transfer_pub", transfer_pub),
+ TALER_DB_RESULT_SPEC("link_secret_enc", shared_secret_enc),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_init_deposits (PGconn *conn, int tmp)
+{
+ const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
+ char *sql;
+ PGresult *res;
+ int ret;
+
+ res = NULL;
+ (void) GNUNET_asprintf (&sql,
+ "CREATE %1$s TABLE IF NOT EXISTS deposits ("
+ " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
+ ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
+ ",transaction_id INT8 NOT NULL"
+ ",amount_value INT4 NOT NULL"
+ ",amount_fraction INT4 NOT NULL"
+ ",amount_currency VARCHAR(4) NOT NULL"
+ ",merchant_pub BYTEA NOT NULL"
+ ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
+ ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
+ ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
+ ",wire TEXT NOT NULL"
+ ")",
+ tmp_str);
+ res = PQexec (conn, sql);
+ GNUNET_free (sql);
+ if (PGRES_COMMAND_OK != PQresultStatus (res))
+ {
+ break_db_err (res);
+ ret = GNUNET_SYSERR;
+ }
+ else
+ ret = GNUNET_OK;
+ PQclear (res);
+ return ret;
+}
+
+int
+TALER_MINT_DB_prepare_deposits (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQprepare (db_conn, "insert_deposit",
+ "INSERT INTO deposits ("
+ "coin_pub,"
+ "denom_pub,"
+ "transaction_id,"
+ "amount_value,"
+ "amount_fraction,"
+ "amount_currency,"
+ "merchant_pub,"
+ "h_contract,"
+ "h_wire,"
+ "coin_sig,"
+ "wire"
+ ") VALUES ("
+ "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11"
+ ")",
+ 11, NULL);
+ EXITIF (PGRES_COMMAND_OK != PQresultStatus(result));
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_deposit",
+ "SELECT "
+ "coin_pub,"
+ "denom_pub,"
+ "transaction_id,"
+ "amount_value,"
+ "amount_fraction,"
+ "amount_currency,"
+ "merchant_pub,"
+ "h_contract,"
+ "h_wire,"
+ "coin_sig"
+ " FROM deposits WHERE ("
+ "coin_pub = $1"
+ ")",
+ 1, NULL);
+ EXITIF (PGRES_COMMAND_OK != PQresultStatus(result));
+ PQclear (result);
+
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+}
+
+
+int
+TALER_MINT_DB_insert_deposit (PGconn *db_conn,
+ const struct Deposit *deposit)
+{
+ struct TALER_DB_QueryParam params[]= {
+ TALER_DB_QUERY_PARAM_PTR (&deposit->coin_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->denom_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->amount.value),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->amount.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->amount.currency, strlen (deposit->amount.currency)),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->coin_sig),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->wire, strlen(deposit->wire)),
+ TALER_DB_QUERY_PARAM_END
+ };
+ PGresult *result;
+
+ result = TALER_DB_exec_prepared (db_conn, "insert_deposit", params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+int
+TALER_MINT_DB_get_deposit (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
+ struct Deposit **r_deposit)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+ PGresult *result;
+ struct Deposit *deposit;
+
+ deposit = NULL;
+ result = TALER_DB_exec_prepared (db_conn, "get_deposit", params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ goto EXITIF_exit;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ if (1 != PQntuples (result))
+ {
+ GNUNET_break (0);
+ goto EXITIF_exit;
+ }
+
+ {
+ deposit = GNUNET_malloc (sizeof (struct Deposit)); /* Without wire data */
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC ("coin_pub", &deposit->coin_pub),
+ TALER_DB_RESULT_SPEC ("denom_pub", &deposit->denom_pub),
+ TALER_DB_RESULT_SPEC ("coin_sig", &deposit->coin_sig),
+ TALER_DB_RESULT_SPEC ("transaction_id", &deposit->transaction_id),
+ TALER_DB_RESULT_SPEC ("merchant_pub", &deposit->merchant_pub),
+ TALER_DB_RESULT_SPEC ("h_contract", &deposit->h_contract),
+ TALER_DB_RESULT_SPEC ("h_wire", &deposit->h_wire),
+ TALER_DB_RESULT_SPEC_END
+ };
+ EXITIF (GNUNET_OK != TALER_DB_extract_result (result, rs, 0));
+ EXITIF (GNUNET_OK != TALER_DB_extract_amount_nbo (result, 0,
+ "amount_value",
+ "amount_fraction",
+ "amount_currency",
+ &deposit->amount));
+ deposit->purpose.purpose = htonl (TALER_SIGNATURE_DEPOSIT);
+ deposit->purpose.size = htonl (sizeof (struct Deposit)
+ - offsetof (struct Deposit, purpose));
+ }
+
+ PQclear (result);
+ *r_deposit = deposit;
+ return GNUNET_OK;
+
+EXITIF_exit:
+ PQclear (result);
+ GNUNET_free_non_null (deposit);
+ deposit = NULL;
+ return GNUNET_SYSERR;
+}
+
+
+
+/**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param the database connection, or NULL on error
+ */
+PGconn *
+TALER_MINT_DB_get_connection (void)
+{
+ PGconn *db_conn;
+
+ if (NULL != (db_conn = pthread_getspecific (db_conn_threadlocal)))
+ return db_conn;
+
+ db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
+
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "db connection failed: %s\n",
+ PQerrorMessage (db_conn));
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_prepare (db_conn))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (0 != pthread_setspecific (db_conn_threadlocal, db_conn))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return db_conn;
+}
+
+
+/**
+ * Close thread-local database connection when a thread is destroyed.
+ *
+ * @param closure we get from pthreads (the db handle)
+ */
+static void
+db_conn_destroy (void *cls)
+{
+ PGconn *db_conn = cls;
+ if (NULL != db_conn)
+ PQfinish (db_conn);
+}
+
+
+/**
+ * Initialize database subsystem.
+ *
+ * @param connection_cfg configuration to use to talk to DB
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_init (const char *connection_cfg)
+{
+
+ if (0 != pthread_key_create (&db_conn_threadlocal, &db_conn_destroy))
+ {
+ fprintf (stderr,
+ "Can't create pthread key.\n");
+ return GNUNET_SYSERR;
+ }
+ TALER_MINT_db_connection_cfg_str = GNUNET_strdup (connection_cfg);
+ return GNUNET_OK;
+}
diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h
new file mode 100644
index 000000000..4f47aac1c
--- /dev/null
+++ b/src/mint/mint_db.h
@@ -0,0 +1,344 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/mint_db.h
+ * @brief Mint-specific database access
+ * @author Florian Dold
+ */
+
+#ifndef _NEURO_MINT_DB_H
+#define _NEURO_MINT_DB_H
+
+#include <libpq-fe.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_types.h"
+#include "taler_rsa.h"
+
+
+/**
+ * Reserve row. Corresponds to table 'reserves' in
+ * the mint's database.
+ */
+struct Reserve
+{
+ /**
+ * Signature over the purse.
+ * Only valid if (blind_session_missing==GNUNET_YES).
+ */
+ struct GNUNET_CRYPTO_EddsaSignature status_sig;
+ /**
+ * Signature with purpose TALER_SIGNATURE_PURSE.
+ * Only valid if (blind_session_missing==GNUNET_YES).
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose status_sig_purpose;
+ /**
+ * Signing key used to sign the purse.
+ * Only valid if (blind_session_missing==GNUNET_YES).
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey status_sign_pub;
+ /**
+ * Withdraw public key, identifies the purse.
+ * Only the customer knows the corresponding private key.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+ /**
+ * Remaining balance in the purse.
+ */
+ struct TALER_AmountNBO balance;
+
+ /**
+ * Expiration date for the purse.
+ */
+ struct GNUNET_TIME_AbsoluteNBO expiration;
+};
+
+
+struct CollectableBlindcoin
+{
+ struct TALER_RSA_BlindedSignaturePurpose ev;
+ struct TALER_RSA_Signature ev_sig;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+ struct GNUNET_CRYPTO_EddsaSignature reserve_sig;
+};
+
+
+struct RefreshSession
+{
+ int has_commit_sig;
+ struct GNUNET_CRYPTO_EddsaSignature commit_sig;
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+ uint16_t num_oldcoins;
+ uint16_t num_newcoins;
+ uint16_t kappa;
+ uint16_t noreveal_index;
+ uint8_t reveal_ok;
+};
+
+
+#define TALER_REFRESH_SHARED_SECRET_LENGTH (sizeof (struct GNUNET_HashCode))
+#define TALER_REFRESH_LINK_LENGTH (sizeof (struct LinkData))
+
+struct RefreshCommitLink
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
+ uint16_t cnc_index;
+ uint16_t oldcoin_index;
+ char shared_secret_enc[sizeof (struct GNUNET_HashCode)];
+};
+
+struct LinkData
+{
+ struct GNUNET_CRYPTO_EcdsaPrivateKey coin_priv;
+ struct TALER_RSA_BlindingKeyBinaryEncoded bkey_enc;
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct SharedSecretEnc
+{
+ char data[TALER_REFRESH_SHARED_SECRET_LENGTH];
+};
+
+
+struct LinkDataEnc
+{
+ char data[sizeof (struct LinkData)];
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+struct RefreshCommitCoin
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+ struct TALER_RSA_BlindedSignaturePurpose coin_ev;
+ uint16_t cnc_index;
+ uint16_t newcoin_index;
+ char link_enc[sizeof (struct LinkData)];
+};
+
+
+struct KnownCoin
+{
+ struct TALER_CoinPublicInfo public_info;
+ struct TALER_Amount expended_balance;
+ int is_refreshed;
+ /**
+ * Refreshing session, only valid if
+ * is_refreshed==1.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+};
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct Deposit
+{
+ /* FIXME: should be TALER_CoinPublicInfo */
+ struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_RSA_Signature coin_sig;
+ struct TALER_RSA_SignaturePurpose purpose;
+ uint64_t transaction_id;
+ struct TALER_AmountNBO amount;
+ struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub;
+ struct GNUNET_HashCode h_contract;
+ struct GNUNET_HashCode h_wire;
+ /* TODO: uint16_t wire_size */
+ char wire[]; /* string encoded wire JSON object */
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+int
+TALER_MINT_DB_prepare (PGconn *db_conn);
+
+int
+TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,
+ struct TALER_RSA_BlindedSignaturePurpose *blind_ev,
+ struct CollectableBlindcoin *collectable);
+
+int
+TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,
+ const struct CollectableBlindcoin *collectable);
+
+
+int
+TALER_MINT_DB_rollback (PGconn *db_conn);
+
+
+int
+TALER_MINT_DB_transaction (PGconn *db_conn);
+
+
+int
+TALER_MINT_DB_commit (PGconn *db_conn);
+
+
+int
+TALER_MINT_DB_get_reserve (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub,
+ struct Reserve *reserve_res);
+
+int
+TALER_MINT_DB_update_reserve (PGconn *db_conn,
+ const struct Reserve *reserve,
+ int fresh);
+
+
+int
+TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+int
+TALER_MINT_DB_get_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ struct RefreshSession *r_session);
+
+
+int
+TALER_MINT_DB_get_known_coin (PGconn *db_conn, struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct KnownCoin *known_coin);
+
+
+int
+TALER_MINT_DB_upsert_known_coin (PGconn *db_conn, struct KnownCoin *known_coin);
+
+
+int
+TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, struct RefreshCommitLink *commit_link);
+
+int
+TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, struct RefreshCommitCoin *commit_coin);
+
+
+int
+TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int i, int j,
+ struct RefreshCommitLink *commit_link);
+
+
+int
+TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int i, int j,
+ struct RefreshCommitCoin *commit_coin);
+
+
+int
+TALER_MINT_DB_create_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey
+ *session_pub);
+
+
+int
+TALER_MINT_DB_get_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+
+int
+TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_Signature *ev_sig);
+int
+TALER_MINT_DB_get_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_Signature *ev_sig);
+int
+TALER_MINT_DB_set_reveal_ok (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub);
+
+int
+TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+
+int
+TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub);
+
+
+typedef
+int (*LinkIterator) (void *cls,
+ const struct LinkDataEnc *link_data_enc,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub,
+ const struct TALER_RSA_Signature *ev_sig);
+
+int
+TALER_db_get_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ LinkIterator link_iter,
+ void *cls);
+
+
+int
+TALER_db_get_transfer (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
+ struct SharedSecretEnc *shared_secret_enc);
+
+int
+TALER_MINT_DB_init_deposits (PGconn *db_conn, int temporary);
+
+int
+TALER_MINT_DB_prepare_deposits (PGconn *db_conn);
+
+int
+TALER_MINT_DB_insert_deposit (PGconn *db_conn,
+ const struct Deposit *deposit);
+
+int
+TALER_MINT_DB_get_deposit (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
+ struct Deposit **r_deposit);
+int
+TALER_MINT_DB_insert_known_coin (PGconn *db_conn,
+ const struct KnownCoin *known_coin);
+
+
+
+/**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param the database connection, or NULL on error
+ */
+PGconn *
+TALER_MINT_DB_get_connection (void);
+
+
+int
+TALER_MINT_DB_init (const char *connection_cfg);
+
+
+
+#endif /* _NEURO_MINT_DB_H */
diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c
new file mode 100644
index 000000000..d877f62c6
--- /dev/null
+++ b/src/mint/taler-mint-dbinit.c
@@ -0,0 +1,285 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-dbinit.c
+ * @brief Create tables for the mint database.
+ * @author Florian Dold
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "mint.h"
+
+
+#define break_db_err(result) do { \
+ GNUNET_break(0); \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
+ PQclear (result); \
+ } while (0)
+
+
+static char *mint_base_dir;
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+static PGconn *db_conn;
+static char *TALER_MINT_db_connection_cfg_str;
+
+
+int
+TALER_MINT_init_withdraw_tables (PGconn *conn)
+{
+ PGresult *result;
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS reserves"
+ "("
+ " reserve_pub BYTEA PRIMARY KEY"
+ ",balance_value INT4 NOT NULL"
+ ",balance_fraction INT4 NOT NULL"
+ ",balance_currency VARCHAR(4) NOT NULL"
+ ",status_sig BYTEA"
+ ",status_sign_pub BYTEA"
+ ",expiration_date INT8 NOT NULL"
+ ")");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS collectable_blindcoins"
+ "("
+ "blind_ev BYTEA PRIMARY KEY"
+ ",blind_ev_sig BYTEA NOT NULL"
+ ",denom_pub BYTEA NOT NULL"
+ ",reserve_sig BYTEA NOT NULL"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub)"
+ ")");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS known_coins "
+ "("
+ " coin_pub BYTEA NOT NULL PRIMARY KEY"
+ ",denom_pub BYTEA NOT NULL"
+ ",denom_sig BYTEA NOT NULL"
+ ",expended_value INT4 NOT NULL"
+ ",expended_fraction INT4 NOT NULL"
+ ",expended_currency VARCHAR(4) NOT NULL"
+ ",refresh_session_pub BYTEA"
+ ")");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_sessions "
+ "("
+ " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)"
+ ",session_melt_sig BYTEA"
+ ",session_commit_sig BYTEA"
+ ",noreveal_index INT2 NOT NULL"
+ // non-zero if all reveals were ok
+ // and the new coin signatures are ready
+ ",reveal_ok BOOLEAN NOT NULL DEFAULT false"
+ ") ");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_order "
+ "( "
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
+ ",newcoin_index INT2 NOT NULL "
+ ",denom_pub BYTEA NOT NULL "
+ ",PRIMARY KEY (session_pub, newcoin_index)"
+ ") ");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_commit_link"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
+ ",transfer_pub BYTEA NOT NULL"
+ ",link_secret_enc BYTEA NOT NULL"
+ // index of the old coin in the customer's request
+ ",oldcoin_index INT2 NOT NULL"
+ // index for cut and choose,
+ // ranges from 0 to kappa-1
+ ",cnc_index INT2 NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_commit_coin"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",link_vector_enc BYTEA NOT NULL"
+ // index of the new coin in the customer's request
+ ",newcoin_index INT2 NOT NULL"
+ // index for cut and choose,
+ ",cnc_index INT2 NOT NULL"
+ ",coin_ev BYTEA NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_melt"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) "
+ ",denom_pub BYTEA NOT NULL "
+ ",oldcoin_index INT2 NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_collectable"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",ev_sig BYTEA NOT NULL"
+ ",newcoin_index INT2 NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS deposits "
+ "( "
+ " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
+ ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
+ ",transaction_id INT8 NOT NULL"
+ ",amount_currency VARCHAR(4) NOT NULL"
+ ",amount_value INT4 NOT NULL"
+ ",amount_fraction INT4 NOT NULL"
+ ",merchant_pub BYTEA NOT NULL"
+ ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
+ ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
+ ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
+ ",wire TEXT NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory", 1,
+ &GNUNET_GETOPT_set_filename, &mint_base_dir},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (GNUNET_GETOPT_run ("taler-mint-serve", options, argc, argv) < 0)
+ return 1;
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-dbinit", "INFO", NULL));
+
+ if (NULL == mint_base_dir)
+ {
+ fprintf (stderr, "Mint base directory not given.\n");
+ return 1;
+ }
+
+ cfg = TALER_MINT_config_load (mint_base_dir);
+ if (NULL == cfg)
+ {
+ fprintf (stderr, "Can't load mint configuration.\n");
+ return 1;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "mint", "db", &TALER_MINT_db_connection_cfg_str))
+ {
+ fprintf (stderr, "Configuration 'mint.db' not found.\n");
+ return 42;
+ }
+ db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ fprintf (stderr, "Database connection failed: %s\n", PQerrorMessage (db_conn));
+ return 1;
+ }
+
+ if (GNUNET_OK != TALER_MINT_init_withdraw_tables (db_conn))
+ {
+ fprintf (stderr, "Failed to initialize database.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c
new file mode 100644
index 000000000..6d69813c0
--- /dev/null
+++ b/src/mint/taler-mint-httpd.c
@@ -0,0 +1,376 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd.c
+ * @brief Serve the HTTP interface of the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_deposit.h"
+#include "taler-mint-httpd_withdraw.h"
+#include "taler-mint-httpd_refresh.h"
+
+
+/**
+ * Base directory of the mint (global)
+ */
+char *mintdir;
+
+/**
+ * The mint's configuration (global)
+ */
+struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Master public key (according to the
+ * configuration in the mint directory).
+ */
+struct GNUNET_CRYPTO_EddsaPublicKey master_pub;
+
+/**
+ * The HTTP Daemon.
+ */
+static struct MHD_Daemon *mydaemon;
+
+/**
+ * The kappa value for refreshing.
+ */
+static unsigned int refresh_security_parameter;
+
+/**
+ * Port to run the daemon on.
+ */
+static uint16_t serve_port;
+
+
+/**
+ * Convert a string representing an EdDSA signature to an EdDSA
+ * signature.
+ *
+ * FIXME: this should be in GNUnet.
+ * FIXME: why? this code is dead, even here!
+ *
+ * @param enc encoded EdDSA signature
+ * @param enclen number of bytes in @a enc (without 0-terminator)
+ * @param pub where to store the EdDSA signature
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_eddsa_signature_from_string (const char *enc,
+ size_t enclen,
+ struct GNUNET_CRYPTO_EddsaSignature *sig)
+{
+ size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaSignature)) * 8;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ if (enclen != keylen)
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen,
+ sig,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature)))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle a request coming from libmicrohttpd.
+ *
+ * @param cls closure for MHD daemon (unused)
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct Buffer *`)
+ * @return MHD result code
+ */
+static int
+handle_mhd_request (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ static struct RequestHandler handlers[] =
+ {
+ { "/", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, I'm the mint\n", 0,
+ &TALER_MINT_handler_static_response, MHD_HTTP_OK },
+ { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
+ NULL, 0,
+ &TALER_MINT_handler_agpl_redirect, MHD_HTTP_FOUND },
+ { "/keys", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_keys, MHD_HTTP_OK },
+ { "/keys", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/withdraw/status", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_withdraw_status, MHD_HTTP_OK },
+ { "/withdraw/status", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/withdraw/sign", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_withdraw_sign, MHD_HTTP_OK },
+ { "/withdraw/sign", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/melt", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_melt, MHD_HTTP_OK },
+ { "/refresh/melt", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/commit", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_commit, MHD_HTTP_OK },
+ { "/refresh/commit", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/reveal", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_melt, MHD_HTTP_OK },
+ { "/refresh/reveal", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/link", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_link, MHD_HTTP_OK },
+ { "/refresh/link", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/reveal", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_reveal, MHD_HTTP_OK },
+ { "/refresh/reveal", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/deposit", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_deposit, MHD_HTTP_OK },
+ { "/deposit", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { NULL, NULL, NULL, NULL, 0, 0 }
+ };
+ static struct RequestHandler h404 =
+ {
+ "", NULL, "text/html",
+ "<html><title>404: not found</title></html>", 0,
+ &TALER_MINT_handler_static_response, MHD_HTTP_NOT_FOUND
+ };
+ struct RequestHandler *rh;
+ unsigned int i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling request for URL '%s'\n",
+ url);
+ for (i=0;NULL != handlers[i].url;i++)
+ {
+ rh = &handlers[i];
+ if ( (0 == strcasecmp (url,
+ rh->url)) &&
+ ( (NULL == rh->method) ||
+ (0 == strcasecmp (method,
+ rh->method)) ) )
+ return rh->handler (rh,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
+ }
+ return TALER_MINT_handler_static_response (&h404,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
+}
+
+
+
+/**
+ * Load configuration parameters for the mint
+ * server into the corresponding global variables.
+ *
+ * @param param mint_directory the mint's directory
+ * @return GNUNET_OK on success
+ */
+static int
+mint_serve_process_config (const char *mint_directory)
+{
+ unsigned long long port;
+ unsigned long long kappa;
+ char *master_pub_str;
+ char *db_cfg;
+
+ cfg = TALER_MINT_config_load (mint_directory);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "can't load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint", "master_pub",
+ &master_pub_str))
+ {
+ fprintf (stderr,
+ "No master public key given in mint configuration.");
+ return GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (master_pub_str,
+ strlen (master_pub_str),
+ &master_pub))
+ {
+ fprintf (stderr,
+ "Invalid master public key given in mint configuration.");
+ return GNUNET_NO;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint", "db",
+ &db_cfg))
+ {
+ fprintf (stderr,
+ "invalid configuration: mint.db\n");
+ return GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ TALER_MINT_DB_init (db_cfg))
+ {
+ fprintf (stderr,
+ "failed to initialize DB subsystem\n");
+ return GNUNET_NO;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "mint", "port",
+ &port))
+ {
+ fprintf (stderr,
+ "invalid configuration: mint.port\n");
+ return GNUNET_NO;
+ }
+
+ if ((port == 0) || (port > UINT16_MAX))
+ {
+ fprintf (stderr,
+ "invalid configuration (value out of range): mint.port\n");
+ return GNUNET_NO;
+ }
+ serve_port = port;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "mint", "refresh_security_parameter",
+ &kappa))
+ {
+ fprintf (stderr,
+ "invalid configuration: mint.refresh_security_parameter\n");
+ return GNUNET_NO;
+ }
+ refresh_security_parameter = kappa;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ GNUNET_GETOPT_OPTION_END
+ };
+ int ret;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-serve",
+ "INFO",
+ NULL));
+ if (GNUNET_GETOPT_run ("taler-mint-serve",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr,
+ "no mint dir given\n");
+ return 1;
+ }
+
+ if (GNUNET_OK != mint_serve_process_config (mintdir))
+ return 1;
+
+
+ mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+ serve_port,
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == mydaemon)
+ {
+ fprintf (stderr,
+ "Failed to start MHD.\n");
+ return 1;
+ }
+
+ ret = TALER_MINT_key_reload_loop ();
+ MHD_stop_daemon (mydaemon);
+ return (GNUNET_OK == ret) ? 0 : 1;
+}
+
diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h
new file mode 100644
index 000000000..59f38aadb
--- /dev/null
+++ b/src/mint/taler-mint-httpd.h
@@ -0,0 +1,106 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd.h
+ * @brief Global declarations for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_H
+#define TALER_MINT_HTTPD_H
+
+
+/**
+ * Cut-and-choose size for refreshing.
+ * FIXME: maybe make it a config option?
+ */
+#define KAPPA 3
+
+
+/**
+ * The mint's configuration.
+ */
+extern struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Main directory with mint data.
+ */
+extern char *mintdir;
+
+/**
+ * Master public key (according to the
+ * configuration in the mint directory).
+ */
+extern struct GNUNET_CRYPTO_EddsaPublicKey master_pub;
+
+
+/**
+ * Struct describing an URL and the handler for it.
+ */
+struct RequestHandler
+{
+
+ /**
+ * URL the handler is for.
+ */
+ const char *url;
+
+ /**
+ * Method the handler is for, NULL for "all".
+ */
+ const char *method;
+
+ /**
+ * Mime type to use in reply (hint, can be NULL).
+ */
+ const char *mime_type;
+
+ /**
+ * Raw data for the @e handler
+ */
+ const void *data;
+
+ /**
+ * Number of bytes in @e data, 0 for 0-terminated.
+ */
+ size_t data_size;
+
+ /**
+ * Function to call to handle the request.
+ *
+ * @param rh this struct
+ * @param mime_type the @e mime_type for the reply (hint, can be NULL)
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+ int (*handler)(struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+ /**
+ * Default response code.
+ */
+ int response_code;
+};
+
+
+#endif
diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c
new file mode 100644
index 000000000..ecbc5c13b
--- /dev/null
+++ b/src/mint/taler-mint-httpd_deposit.c
@@ -0,0 +1,270 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_deposit.c
+ * @brief Handle /deposit requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_deposit.h"
+
+
+/**
+ * Send confirmation of deposit success to client.
+ *
+ * @param connection connection to the client
+ * @param deposit deposit request to confirm
+ * @return MHD result code
+ */
+static int
+helper_deposit_send_response_success (struct MHD_Connection *connection,
+ struct Deposit *deposit)
+{
+ // FIXME: return more information here
+ return request_send_json_pack (connection, MHD_HTTP_OK,
+ "{s:s}", "status", "DEPOSIT_OK");
+}
+
+
+/**
+ * Handle a "/deposit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_deposit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *json;
+ struct Deposit *deposit;
+ json_t *wire;
+ json_t *resp;
+ char *wire_enc = NULL;
+ const char *deposit_type;
+ struct MintKeyState *key_state;
+ struct TALER_CoinPublicInfo coin_info;
+ struct TALER_RSA_Signature ubsig;
+ size_t len;
+ int resp_code;
+ PGconn *db_conn;
+ int res;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data, upload_data_size,
+ &json);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ deposit = NULL;
+ wire = NULL;
+ resp = NULL;
+ if (-1 == json_unpack (json,
+ "{s:s s:o}",
+ "type", &deposit_type,
+ "wire", &wire))
+ {
+ GNUNET_break_op (0);
+ resp = json_pack ("{s:s}", "error", "Bad format");
+ resp_code = MHD_HTTP_BAD_REQUEST;
+ goto EXITIF_exit;
+ }
+ if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT|JSON_SORT_KEYS)))
+ {
+ GNUNET_break_op (0);
+ resp = json_pack ("{s:s}", "error", "Bad format");
+ resp_code = MHD_HTTP_BAD_REQUEST;
+ goto EXITIF_exit;
+ }
+ len = strlen (wire_enc) + 1;
+ deposit = GNUNET_malloc (sizeof (struct Deposit) + len);
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+#define PARSE_DATA(field, addr) \
+ EXITIF (GNUNET_OK != request_json_require_nav \
+ (connection, json, \
+ JNAV_FIELD, field, JNAV_RET_DATA, addr, sizeof (*addr)))
+ PARSE_DATA ("coin_pub", &deposit->coin_pub);
+ PARSE_DATA ("denom_pub", &deposit->denom_pub);
+ PARSE_DATA ("ubsig", &ubsig);
+ PARSE_DATA ("merchant_pub", &deposit->merchant_pub);
+ PARSE_DATA ("H_a", &deposit->h_contract);
+ PARSE_DATA ("H_wire", &deposit->h_wire);
+ PARSE_DATA ("csig", &deposit->coin_sig);
+ PARSE_DATA ("transaction_id", &deposit->transaction_id);
+#undef PARSE_DATA
+ if (0 == strcmp ("DIRECT_DEPOSIT", deposit_type))
+ deposit->purpose.purpose = htonl (TALER_SIGNATURE_DEPOSIT);
+ else if (0 == strcmp ("INCREMENTAL_DEPOSIT", deposit_type))
+ deposit->purpose.purpose = htonl (TALER_SIGNATURE_INCREMENTAL_DEPOSIT);
+ else
+ {
+ GNUNET_break_op (0);
+ resp = json_pack ("{s:s}", "error", "Bad format");
+ resp_code = MHD_HTTP_BAD_REQUEST;
+ goto EXITIF_exit;
+ }
+ deposit->purpose.size = htonl (sizeof (struct Deposit)
+ - offsetof (struct Deposit, purpose));
+ memcpy (&coin_info.coin_pub,
+ &deposit->coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+ coin_info.denom_pub = deposit->denom_pub;
+ coin_info.denom_sig = ubsig;
+ key_state = TALER_MINT_key_state_acquire ();
+ if (GNUNET_YES != TALER_MINT_test_coin_valid (key_state,
+ &coin_info))
+ {
+ TALER_MINT_key_state_release (key_state);
+ resp = json_pack ("{s:s}", "error", "Coin is not valid");
+ resp_code = MHD_HTTP_NOT_FOUND;
+ goto EXITIF_exit;
+ }
+ TALER_MINT_key_state_release (key_state);
+ /*
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_DEPOSIT,
+ &deposit->purpose,
+ &deposit->coin_sig,
+ &deposit->coin_pub))
+ {
+ resp = json_pack ("{s:s}", "error", "Signature verfication failed");
+ resp_code = MHD_HTTP_NOT_FOUND;
+ goto EXITIF_exit;
+ }
+ */
+
+ /* Check if we already received the same deposit permission,
+ * or the coin was already deposited */
+
+ {
+ struct Deposit *existing_deposit;
+ int res;
+
+ res = TALER_MINT_DB_get_deposit (db_conn,
+ &deposit->coin_pub,
+ &existing_deposit);
+ if (GNUNET_YES == res)
+ {
+ // FIXME: memory leak
+ if (0 == memcmp (existing_deposit, deposit, sizeof (struct Deposit)))
+ return helper_deposit_send_response_success (connection, deposit);
+ // FIXME: in the future, check if there's enough credits
+ // left on the coin. For now: refuse
+ // FIXME: return more information here
+ return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN,
+ "{s:s}",
+ "error", "double spending");
+ }
+
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ {
+ struct KnownCoin known_coin;
+ int res;
+
+ res = TALER_MINT_DB_get_known_coin (db_conn, &coin_info.coin_pub, &known_coin);
+ if (GNUNET_YES == res)
+ {
+ // coin must have been refreshed
+ // FIXME: check
+ // FIXME: return more information here
+ return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN,
+ "{s:s}",
+ "error", "coin was refreshed");
+ }
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* coin valid but not known => insert into DB */
+ known_coin.is_refreshed = GNUNET_NO;
+ known_coin.expended_balance = TALER_amount_ntoh (deposit->amount);
+ known_coin.public_info = coin_info;
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_known_coin (db_conn, &known_coin))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_deposit (db_conn, deposit))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return helper_deposit_send_response_success (connection, deposit);
+
+ EXITIF_exit:
+ if (NULL != resp)
+ res = send_response_json (connection, resp, resp_code);
+ else
+ res = MHD_NO;
+ if (NULL != wire)
+ json_decref (wire);
+ if (NULL != deposit)
+ GNUNET_free (deposit);
+ if (NULL != wire_enc)
+ GNUNET_free (wire_enc);
+ return res;
+#undef EXITIF
+#undef PARSE_DATA
+}
+
+/* end of taler-mint-httpd_deposit.c */
diff --git a/src/mint/taler-mint-httpd_deposit.h b/src/mint/taler-mint-httpd_deposit.h
new file mode 100644
index 000000000..dd7b8c133
--- /dev/null
+++ b/src/mint/taler-mint-httpd_deposit.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_deposit.h
+ * @brief Handle /deposit requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_DEPOSIT_H
+#define TALER_MINT_HTTPD_DEPOSIT_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Handle a "/deposit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_deposit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c
new file mode 100644
index 000000000..ba023fe69
--- /dev/null
+++ b/src/mint/taler-mint-httpd_keys.c
@@ -0,0 +1,512 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_keys.c
+ * @brief Handle /keys requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+
+
+/**
+ * Mint key state. Never use directly, instead access via
+ * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release.
+ */
+static struct MintKeyState *internal_key_state;
+
+/**
+ * Mutex protecting access to #internal_key_state.
+ */
+static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Pipe used for signaling reloading of our key state.
+ */
+static int reload_pipe[2];
+
+
+/**
+ * Convert the public part of a denomination key
+ * issue to a JSON object.
+ *
+ * @param dki the denomination key issue
+ * @return a JSON object describing the denomination key isue (public part)
+ */
+static json_t *
+denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ json_t *dk_json = json_object ();
+ json_object_set_new (dk_json, "master_sig",
+ TALER_JSON_from_data (&dki->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
+ json_object_set_new (dk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start)));
+ json_object_set_new (dk_json, "stamp_expire_withdraw", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw)));
+ json_object_set_new (dk_json, "stamp_expire_deposit", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend)));
+ json_object_set_new (dk_json, "denom_pub",
+ TALER_JSON_from_data (&dki->denom_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)));
+ json_object_set_new (dk_json, "value",
+ TALER_JSON_from_amount (TALER_amount_ntoh (dki->value)));
+ json_object_set_new (dk_json,
+ "fee_withdraw",
+ TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_withdraw)));
+ json_object_set_new (dk_json,
+ "fee_deposit",
+ TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_deposit)));
+ json_object_set_new (dk_json,
+ "fee_refresh",
+ TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_refresh)));
+ return dk_json;
+}
+
+
+/**
+ * Convert the public part of a sign key
+ * issue to a JSON object.
+ *
+ * @param ski the sign key issue
+ * @return a JSON object describing the sign key isue (public part)
+ */
+static json_t *
+sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski)
+{
+ json_t *sk_json = json_object ();
+ json_object_set_new (sk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start)));
+ json_object_set_new (sk_json, "stamp_expire", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire)));
+ json_object_set_new (sk_json, "master_sig",
+ TALER_JSON_from_data (&ski->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
+ json_object_set_new (sk_json, "key",
+ TALER_JSON_from_data (&ski->signkey_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)));
+ return sk_json;
+}
+
+
+/**
+ * Get the relative time value that describes how
+ * far in the future do we want to provide coin keys.
+ *
+ * @return the provide duration
+ */
+static struct GNUNET_TIME_Relative
+TALER_MINT_conf_duration_provide ()
+{
+ struct GNUNET_TIME_Relative rel;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (cfg,
+ "mint_keys",
+ "lookahead_provide",
+ &rel))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "mint_keys.lookahead_provide not valid or not given\n");
+ GNUNET_abort ();
+ }
+ return rel;
+}
+
+
+/**
+ * Iterator for denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key issue
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+reload_keys_denom_iter (void *cls,
+ const char *alias,
+ const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ struct MintKeyState *ctx = cls;
+ struct GNUNET_TIME_Absolute stamp_provide;
+ struct GNUNET_HashCode denom_key_hash;
+ int res;
+
+ stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time,
+ TALER_MINT_conf_duration_provide ());
+
+ if (GNUNET_TIME_absolute_ntoh (dki->expire_spend).abs_value_us < ctx->reload_time.abs_value_us)
+ {
+ // this key is expired
+ return GNUNET_OK;
+ }
+ if (GNUNET_TIME_absolute_ntoh (dki->start).abs_value_us > stamp_provide.abs_value_us)
+ {
+ // we are to early for this key
+ return GNUNET_OK;
+ }
+
+ GNUNET_CRYPTO_hash (&dki->denom_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), &denom_key_hash);
+
+ res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map,
+ &denom_key_hash,
+ GNUNET_memdup (dki, sizeof (struct TALER_MINT_DenomKeyIssue)),
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ if (GNUNET_OK != res)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Duplicate denomination key\n");
+
+ json_array_append_new (ctx->denom_keys_array,
+ denom_key_issue_to_json (dki));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Iterator for sign keys.
+ *
+ * @param cls closure
+ * @param ski the sign key issue
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+reload_keys_sign_iter (void *cls,
+ const struct TALER_MINT_SignKeyIssue *ski)
+{
+ struct MintKeyState *ctx = cls;
+ struct GNUNET_TIME_Absolute stamp_provide;
+
+ stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, TALER_MINT_conf_duration_provide (cfg));
+
+ if (GNUNET_TIME_absolute_ntoh (ski->expire).abs_value_us < ctx->reload_time.abs_value_us)
+ {
+ // this key is expired
+ return GNUNET_OK;
+ }
+
+ if (GNUNET_TIME_absolute_ntoh (ski->start).abs_value_us > stamp_provide.abs_value_us)
+ {
+ // we are to early for this key
+ return GNUNET_OK;
+ }
+
+ // the signkey is valid for now, check
+ // if it's more recent than the current one!
+ if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.start).abs_value_us >
+ GNUNET_TIME_absolute_ntoh (ski->start).abs_value_us)
+ ctx->current_sign_key_issue = *ski;
+
+
+ ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload,
+ GNUNET_TIME_absolute_ntoh (ski->expire));
+
+ json_array_append_new (ctx->sign_keys_array,
+ sign_key_issue_to_json (ski));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Load the mint's key state from disk.
+ *
+ * @return fresh key state (with reference count 1)
+ */
+static struct MintKeyState *
+reload_keys ()
+{
+ struct MintKeyState *key_state;
+ json_t *keys;
+
+ key_state = GNUNET_new (struct MintKeyState);
+ key_state->refcnt = 1;
+
+ key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS;
+
+ key_state->denom_keys_array = json_array ();
+ GNUNET_assert (NULL != key_state->denom_keys_array);
+
+ key_state->sign_keys_array = json_array ();
+ GNUNET_assert (NULL != key_state->sign_keys_array);
+
+ key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
+ GNUNET_assert (NULL != key_state->denomkey_map);
+
+ key_state->reload_time = GNUNET_TIME_absolute_get ();
+
+ TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state);
+ TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state);
+
+ keys = json_pack ("{s:o, s:o, s:o, s:o}",
+ "master_pub", TALER_JSON_from_data (&master_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)),
+ "signkeys", key_state->sign_keys_array,
+ "denoms", key_state->denom_keys_array,
+ "list_issue_date", TALER_JSON_from_abs (key_state->reload_time));
+
+ key_state->keys_json = json_dumps (keys, JSON_INDENT(2));
+
+ return key_state;
+}
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ *
+ * @param key_state the key state to release
+ */
+void
+TALER_MINT_key_state_release (struct MintKeyState *key_state)
+{
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ GNUNET_assert (0 != key_state->refcnt);
+ key_state->refcnt += 1;
+ if (key_state->refcnt == 0) {
+ GNUNET_free (key_state);
+ }
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+}
+
+
+/**
+ * Acquire the key state of the mint. Updates keys if necessary.
+ * For every call to #TALER_MINT_key_state_acquire, a matching call
+ * to #TALER_MINT_key_state_release must be made.
+ *
+ * @return the key state
+ */
+struct MintKeyState *
+TALER_MINT_key_state_acquire (void)
+{
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ struct MintKeyState *key_state;
+
+ // FIXME: the locking we have is very coarse-grained,
+ // using multiple locks might be nicer ...
+
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ if (NULL == internal_key_state)
+ {
+ internal_key_state = reload_keys ();
+ }
+ else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us)
+ {
+ GNUNET_assert (0 != internal_key_state->refcnt);
+ internal_key_state->refcnt--;
+ if (0 == internal_key_state->refcnt)
+ GNUNET_free (internal_key_state);
+ internal_key_state = reload_keys ();
+ }
+ key_state = internal_key_state;
+ key_state->refcnt += 1;
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+
+ return key_state;
+}
+
+
+/**
+ * Look up the issue for a denom public key.
+ *
+ * @param key state to look in
+ * @param denom_pub denomination public key
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found
+ */
+struct TALER_MINT_DenomKeyIssue *
+TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ struct TALER_MINT_DenomKeyIssue *issue;
+ struct GNUNET_HashCode hash;
+
+ GNUNET_CRYPTO_hash (denom_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded), &hash);
+ issue = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, &hash);
+ return issue;
+}
+
+
+/**
+ * Check if a coin is valid; that is, whether the denomination key exists,
+ * is not expired, and the signature is correct.
+ *
+ * @param key_state the key state to use for checking the coin's validity
+ * @param coin_public_info the coin public info to check for validity
+ * @return GNUNET_YES if the coin is valid,
+ * GNUNET_NO if it is invalid
+ * GNUNET_SYSERROR if an internal error occured
+ */
+int
+TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
+ struct TALER_CoinPublicInfo *coin_public_info)
+{
+ struct TALER_MINT_DenomKeyIssue *dki;
+
+ dki = TALER_MINT_get_denom_key (key_state, &coin_public_info->denom_pub);
+ if (NULL == dki)
+ return GNUNET_NO;
+ if (GNUNET_OK != TALER_RSA_verify (&coin_public_info->coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+ &coin_public_info->denom_sig,
+ &dki->denom_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "coin signature is invalid\n");
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_keys (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct MintKeyState *key_state;
+ struct MHD_Response *response;
+ int ret;
+
+ key_state = TALER_MINT_key_state_acquire ();
+ response = MHD_create_response_from_buffer (strlen (key_state->keys_json),
+ key_state->keys_json,
+ MHD_RESPMEM_MUST_COPY);
+ TALER_MINT_key_state_release (key_state);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ (void) MHD_add_response_header (response,
+ "Content-Type",
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Handle a signal, writing relevant signal numbers
+ * (currently just SIGUSR1) to a pipe.
+ *
+ * @param signal_number the signal number
+ */
+static void
+handle_signal (int signal_number)
+{
+ size_t res;
+ char c = signal_number;
+
+ if (SIGUSR1 == signal_number)
+ {
+ errno = 0;
+ res = write (reload_pipe[1], &c, 1);
+ if ((res < 0) && (EINTR != errno))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (0 == res)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is read from the pipe.
+ */
+int
+TALER_MINT_key_reload_loop (void)
+{
+ struct sigaction act;
+
+ if (0 != pipe (reload_pipe))
+ {
+ fprintf (stderr,
+ "Failed to create pipe.\n");
+ return GNUNET_SYSERR;
+ }
+ memset (&act, 0, sizeof (struct sigaction));
+ act.sa_handler = &handle_signal;
+
+ if (0 != sigaction (SIGUSR1, &act, NULL))
+ {
+ fprintf (stderr,
+ "Failed to set signal handler.\n");
+ return GNUNET_SYSERR;
+ }
+
+ while (1)
+ {
+ char c;
+ ssize_t res;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "(re-)loading keys\n");
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ if (NULL != internal_key_state)
+ {
+ GNUNET_assert (0 != internal_key_state->refcnt);
+ internal_key_state->refcnt -= 1;
+ if (0 == internal_key_state->refcnt)
+ GNUNET_free (internal_key_state);
+ }
+ internal_key_state = reload_keys ();
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+read_again:
+ errno = 0;
+ res = read (reload_pipe[0], &c, 1);
+ if ((res < 0) && (EINTR != errno))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (EINTR == errno)
+ goto read_again;
+ }
+ return GNUNET_OK;
+}
+
+
+/* end of taler-mint-httpd_keys.c */
diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h
new file mode 100644
index 000000000..640a9c916
--- /dev/null
+++ b/src/mint/taler-mint-httpd_keys.h
@@ -0,0 +1,155 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_keys.h
+ * @brief Handle /keys requests and manage key state
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_KEYS_H
+#define TALER_MINT_HTTPD_KEYS_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Snapshot of the (coin and signing)
+ * keys (including private keys) of the mint.
+ */
+struct MintKeyState
+{
+ /**
+ * When did we initiate the key reloading?
+ */
+ struct GNUNET_TIME_Absolute reload_time;
+
+ /**
+ * JSON array with denomination keys.
+ */
+ json_t *denom_keys_array;
+
+ /**
+ * JSON array with signing keys.
+ */
+ json_t *sign_keys_array;
+
+ /**
+ * Mapping from denomination keys to denomination key issue struct.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
+
+ /**
+ * When is the next key invalid and we have to reload?
+ */
+ struct GNUNET_TIME_Absolute next_reload;
+
+ /**
+ * Mint signing key that should be used currently.
+ */
+ struct TALER_MINT_SignKeyIssue current_sign_key_issue;
+
+ /**
+ * Cached JSON text that the mint will send for
+ * a /keys request.
+ */
+ char *keys_json;
+
+ /**
+ * Reference count.
+ */
+ unsigned int refcnt;
+};
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ *
+ * @param key_state the key state to release
+ */
+void
+TALER_MINT_key_state_release (struct MintKeyState *key_state);
+
+
+/**
+ * Acquire the key state of the mint. Updates keys if necessary.
+ * For every call to #TALER_MINT_key_state_acquire, a matching call
+ * to #TALER_MINT_key_state_release must be made.
+ *
+ * @return the key state
+ */
+struct MintKeyState *
+TALER_MINT_key_state_acquire (void);
+
+
+/**
+ * Look up the issue for a denom public key.
+ *
+ * @param key state to look in
+ * @param denom_pub denomination public key
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found
+ */
+struct TALER_MINT_DenomKeyIssue *
+TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+
+/**
+ * Check if a coin is valid; that is, whether the denomination key exists,
+ * is not expired, and the signature is correct.
+ *
+ * @param key_state the key state to use for checking the coin's validity
+ * @param coin_public_info the coin public info to check for validity
+ * @return GNUNET_YES if the coin is valid,
+ * GNUNET_NO if it is invalid
+ * GNUNET_SYSERROR if an internal error occured
+ */
+int
+TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
+ struct TALER_CoinPublicInfo *coin_public_info);
+
+
+/**
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is read from the pipe.
+ *
+ * @return GNUNET_OK if we terminated normally, GNUNET_SYSERR on error
+ */
+int
+TALER_MINT_key_reload_loop (void);
+
+
+/**
+ * Handle a "/keys" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_keys (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/mint/taler-mint-httpd_mhd.c b/src/mint/taler-mint-httpd_mhd.c
new file mode 100644
index 000000000..09f3025b8
--- /dev/null
+++ b/src/mint/taler-mint-httpd_mhd.c
@@ -0,0 +1,300 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd_mhd.c
+ * @brief helpers for MHD interaction
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd.h"
+#include "taler-mint-httpd_mhd.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_static_response (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct MHD_Response *response;
+ int ret;
+
+ if (0 == rh->data_size)
+ rh->data_size = strlen ((const char *) rh->data);
+ response = MHD_create_response_from_buffer (rh->data_size,
+ (void *) rh->data,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_agpl_redirect (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ const char *agpl =
+ "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
+ struct MHD_Response *response;
+ int ret;
+
+ response = MHD_create_response_from_buffer (strlen (agpl),
+ (void *) agpl,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_LOCATION,
+ "http://www.git.taler.net/?p=mint.git");
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from varargs.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param response_code HTTP response code to use
+ * @param do_cache can the response be cached? (0: no, 1: yes)
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TALER_MINT_helper_send_json_pack (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void *connection_cls,
+ int response_code,
+ int do_cache,
+ const char *fmt,
+ ...)
+{
+ int ret;
+ json_t *json;
+ va_list argp;
+ char *json_str;
+ struct MHD_Response *response;
+
+ va_start (argp, fmt);
+ json = json_vpack_ex (NULL, 0, fmt, argp);
+ va_end (argp);
+ if (NULL == json)
+ return MHD_NO;
+ json_str = json_dumps (json, JSON_INDENT(2));
+ json_decref (json);
+ if (NULL == json_str)
+ return MHD_NO;
+ response = MHD_create_response_from_buffer (strlen (json_str),
+ json_str,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL == response)
+ {
+ free (json_str);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_send_json_pack_error (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ return TALER_MINT_helper_send_json_pack (rh,
+ connection,
+ connection_cls,
+ 1, /* caching enabled */
+ rh->response_code,
+ "{s:s}",
+ "error",
+ rh->data);
+}
+
+
+/**
+ * Send a response for an invalid argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is missing
+ * @return a GNUnet result code
+ */
+static int
+request_arg_invalid (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ json_t *json;
+ json = json_pack ("{ s:s, s:s }",
+ "error", "invalid parameter",
+ "parameter", param_name);
+ if (MHD_YES != send_response_json (connection, json, MHD_HTTP_BAD_REQUEST))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Get a GET paramater that is a string,
+ * or send an error response if the parameter is missing.
+ *
+ * @param connection the connection to get the parameter from /
+ * send the error response to
+ * @param param_name the parameter name
+ * @param str pointer to store the parameter string,
+ * must be freed by the caller
+ * @return GNUNET_YES if the parameter is present and valid,
+ * GNUNET_NO if the parameter is missing
+ * GNUNET_SYSERR on internal error
+ */
+static int
+request_arg_require_string (struct MHD_Connection *connection,
+ const char *param_name,
+ const char **str)
+{
+ *str = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, param_name);
+ if (NULL == *str)
+ {
+ if (MHD_NO ==
+ request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ "{ s:s, s:s }",
+ "error", "missing parameter",
+ "parameter", param_name))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Extraxt base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of data
+ * @return
+ * GNUNET_YES if the the argument is present
+ * GNUNET_NO if the argument is absent or malformed
+ * GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size)
+{
+ const char *str;
+ int ret;
+
+ if (GNUNET_OK != (ret = request_arg_require_string (connection, param_name, &str)))
+ return ret;
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (str, strlen (str), out_data, out_size))
+ return request_arg_invalid (connection, param_name);
+ return GNUNET_OK;
+}
+
+
+
+/* end of taler-mint-httpd_mhd.c */
diff --git a/src/mint/taler-mint-httpd_mhd.h b/src/mint/taler-mint-httpd_mhd.h
new file mode 100644
index 000000000..29ab7f64b
--- /dev/null
+++ b/src/mint/taler-mint-httpd_mhd.h
@@ -0,0 +1,132 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd_mhd.h
+ * @brief helpers for MHD interaction
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_MHD_H
+#define TALER_MINT_HTTPD_MHD_H
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_static_response (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_agpl_redirect (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from varargs.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param response_code HTTP response code to use
+ * @param do_cache can the response be cached? (0: no, 1: yes)
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TALER_MINT_helper_send_json_pack (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void *connection_cls,
+ int response_code,
+ int do_cache,
+ const char *fmt,
+ ...);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_send_json_pack_error (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Extraxt base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of data
+ * @return
+ * GNUNET_YES if the the argument is present
+ * GNUNET_NO if the argument is absent or malformed
+ * GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size);
+
+#endif
diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c
new file mode 100644
index 000000000..8121bb234
--- /dev/null
+++ b/src/mint/taler-mint-httpd_refresh.c
@@ -0,0 +1,1497 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_refresh.c
+ * @brief Handle /refresh/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_refresh.h"
+
+
+/**
+ * Sign the message in @a purpose with the mint's signing
+ * key and encode the signature as a JSON object.
+ *
+ * @param purpose the message to sign
+ * @return signature as JSON object
+ */
+static json_t *
+sign_as_json (struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
+{
+ json_t *sig_json;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+ struct MintKeyState *key_state;
+
+ key_state = TALER_MINT_key_state_acquire ();
+
+ sig_json = json_object ();
+
+ GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv,
+ purpose,
+ &sig));
+
+ TALER_MINT_key_state_release (key_state);
+
+ json_object_set (sig_json, "sig", TALER_JSON_from_data (&sig, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
+ json_object_set (sig_json, "purpose", json_integer (ntohl (purpose->purpose)));
+ json_object_set (sig_json, "size", json_integer (ntohl (purpose->size)));
+
+ return sig_json;
+}
+
+
+static int
+link_iter (void *cls,
+ const struct LinkDataEnc *link_data_enc,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub,
+ const struct TALER_RSA_Signature *ev_sig)
+{
+ json_t *list = cls;
+ json_t *obj = json_object ();
+
+ json_array_append_new (list, obj);
+
+ json_object_set_new (obj, "link_enc",
+ TALER_JSON_from_data (link_data_enc,
+ sizeof (struct LinkDataEnc)));
+
+ json_object_set_new (obj, "denom_pub",
+ TALER_JSON_from_data (denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)));
+
+ json_object_set_new (obj, "ev_sig",
+ TALER_JSON_from_data (ev_sig,
+ sizeof (struct TALER_RSA_Signature)));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Insert all requested denominations into the db, and compute the
+ * required cost of the denominations, including fees.
+ *
+ * @param connection the connection to send an error response to
+ * @param db_conn the database connection
+ * @param key_state the mint's key state to use
+ * @param session_pub the refresh session public key
+ * @param root the request JSON object
+ * @param hash_context the hash context where accepted
+ * denominations will be hased into
+ * @param r_amount the sum of the cost (value+fee) for
+ * all requested coins
+ * @return FIXME!
+ */
+static int
+refresh_accept_denoms (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ const struct MintKeyState *key_state,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const json_t *root,
+ struct TALER_HashContext *hash_context,
+ struct TALER_Amount *r_amount)
+{
+ unsigned i;
+ int res;
+ json_t *new_denoms;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "new_denoms",
+ JNAV_RET_TYPED_JSON,
+ JSON_ARRAY,
+ &new_denoms);
+ if (GNUNET_OK != res)
+ return res;
+
+ memset (r_amount, 0, sizeof (struct TALER_Amount));
+
+ for (i = 0; i < json_array_size (new_denoms); i++)
+ {
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ int res;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct TALER_Amount cost;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "new_denoms",
+ JNAV_INDEX, (int) i,
+ JNAV_RET_DATA,
+ &denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+
+ if (GNUNET_OK != res)
+ return res;
+
+ dki = TALER_MINT_get_denom_key (key_state, &denom_pub);
+
+ TALER_hash_context_read (hash_context,
+ &denom_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+
+ cost = TALER_amount_add (TALER_amount_ntoh (dki->value),
+ TALER_amount_ntoh (dki->fee_withdraw));
+
+ *r_amount = TALER_amount_add (cost, *r_amount);
+
+ /* Insert the requested coin into the DB, so we'll know later
+ * what denomination the request had */
+
+ if (GNUNET_OK !=
+ TALER_MINT_DB_insert_refresh_order (db_conn,
+ i,
+ session_pub,
+ &denom_pub))
+ return res; // ???
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get an amount in the mint's currency
+ * that is zero.
+ *
+ * @return zero amount in the mint's currency
+ */
+static struct TALER_Amount
+mint_amount_native_zero ()
+{
+ struct TALER_Amount amount;
+
+ memset (&amount, 0, sizeof (amount));
+ // FIXME: load from config
+ memcpy (amount.currency, "EUR", 3);
+
+ return amount;
+}
+
+
+static int
+check_confirm_signature (struct MHD_Connection *connection,
+ json_t *coin_info,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ struct RefreshMeltConfirmSignRequestBody body;
+ struct GNUNET_CRYPTO_EcdsaSignature sig;
+ int res;
+
+ body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM);
+ body.session_pub = *session_pub;
+
+ res = request_json_require_nav (connection, coin_info,
+ JNAV_FIELD, "confirm_sig",
+ JNAV_RET_DATA,
+ &sig,
+ sizeof (struct GNUNET_CRYPTO_EcdsaSignature));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM,
+ &body.purpose,
+ &sig,
+ coin_pub))
+ {
+ if (MHD_YES !=
+ request_send_json_pack (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ "{s:s}",
+ "error", "signature invalid"))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Extract public coin information from a JSON object.
+ *
+ * @param connection the connection to send error responses to
+ * @param root the JSON object to extract the coin info from
+ * @return GNUNET_YES if coin public info in JSON was valid
+ * GNUNET_NO otherwise
+ * GNUNET_SYSERR on internal error
+ */
+static int
+request_json_require_coin_public_info (struct MHD_Connection *connection,
+ json_t *root,
+ struct TALER_CoinPublicInfo *r_public_info)
+{
+ int ret;
+
+ GNUNET_assert (NULL != root);
+
+ ret = request_json_require_nav (connection, root,
+ JNAV_FIELD, "coin_pub",
+ JNAV_RET_DATA,
+ &r_public_info->coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+ if (GNUNET_OK != ret)
+ return ret;
+
+ ret = request_json_require_nav (connection, root,
+ JNAV_FIELD, "denom_sig",
+ JNAV_RET_DATA,
+ &r_public_info->denom_sig,
+ sizeof (struct TALER_RSA_Signature));
+ if (GNUNET_OK != ret)
+ return ret;
+
+ ret = request_json_require_nav (connection, root,
+ JNAV_FIELD, "denom_pub",
+ JNAV_RET_DATA,
+ &r_public_info->denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+ if (GNUNET_OK != ret)
+ return ret;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse coin melt requests from a JSON object and write them to
+ * the database.
+ *
+ * @param connection the connection to send errors to
+ * @param db_conn the database connection
+ * @param key_state the mint's key state
+ * @param session_pub the refresh session's public key
+ * @param root the JSON object
+ * @param hash_context the hash context that will receive
+ * the coin public keys of the melted coin
+ * @return a GNUnet result code, GNUNET_OK on success,
+ * GNUNET_NO if an error message was generated,
+ * GNUNET_SYSERR on internal errors (no response generated)
+ */
+static int
+refresh_accept_melts (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ const struct MintKeyState *key_state,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ json_t *root,
+ struct TALER_HashContext *hash_context,
+ struct TALER_Amount *r_melt_balance)
+{
+ size_t i;
+ int res;
+ json_t *melt_coins;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "melt_coins",
+ JNAV_RET_TYPED_JSON,
+ JSON_ARRAY,
+ &melt_coins);
+ if (GNUNET_OK != res)
+ return res;
+
+ memset (r_melt_balance, 0, sizeof (struct TALER_Amount));
+
+ for (i = 0; i < json_array_size (melt_coins); i++)
+ {
+ struct TALER_CoinPublicInfo coin_public_info;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct KnownCoin known_coin;
+ // money the customer gets by melting the current coin
+ struct TALER_Amount coin_gain;
+
+ res = request_json_require_coin_public_info (connection,
+ json_array_get (melt_coins, i),
+ &coin_public_info);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ if (GNUNET_OK != (res = check_confirm_signature (connection,
+ json_array_get (melt_coins, i),
+ &coin_public_info.coin_pub,
+ session_pub)))
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ TALER_hash_context_read (hash_context,
+ &coin_public_info.coin_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+
+ dki = TALER_MINT_get_denom_key (key_state, &coin_public_info.denom_pub);
+
+ if (NULL == dki)
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "denom not found"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+
+ if (GNUNET_OK != TALER_MINT_test_coin_valid (key_state, &coin_public_info))
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "coin invalid"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+
+ res = TALER_MINT_DB_get_known_coin (db_conn, &coin_public_info.coin_pub,
+ &known_coin);
+
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_YES == res)
+ {
+ if (GNUNET_YES == known_coin.is_refreshed)
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "coin already refreshed")) ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ else
+ {
+ known_coin.expended_balance = mint_amount_native_zero ();
+ known_coin.public_info = coin_public_info;
+ }
+
+ known_coin.is_refreshed = GNUNET_YES;
+ known_coin.refresh_session_pub = *session_pub;
+
+ if (GNUNET_OK != TALER_MINT_DB_upsert_known_coin (db_conn, &known_coin))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_refresh_melt (db_conn, session_pub, i,
+ &coin_public_info.coin_pub,
+ &coin_public_info.denom_pub))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ coin_gain = TALER_amount_ntoh (dki->value);
+ coin_gain = TALER_amount_subtract (coin_gain, known_coin.expended_balance);
+
+ /* Refuse to refresh when the coin does not have enough money left to
+ * pay the refreshing fees of the coin. */
+
+ if (TALER_amount_cmp (coin_gain, TALER_amount_ntoh (dki->fee_refresh)) < 0)
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "depleted")) ? GNUNET_NO : GNUNET_SYSERR;
+
+ coin_gain = TALER_amount_subtract (coin_gain, TALER_amount_ntoh (dki->fee_refresh));
+
+ *r_melt_balance = TALER_amount_add (*r_melt_balance, coin_gain);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Send a response for "/refresh/melt".
+ *
+ * @param connection the connection to send the response to
+ * @param db_conn the database connection to fetch values from
+ * @param session_pub the refresh session public key.
+ * @return a MHD result code
+ */
+static int
+helper_refresh_send_melt_response (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ struct RefreshSession session;
+ int res;
+ json_t *root;
+ json_t *list;
+ struct TALER_HashContext hash_context;
+
+ if (GNUNET_OK !=
+ (res = TALER_MINT_DB_get_refresh_session (db_conn,
+ session_pub,
+ &session)))
+ {
+ // FIXME: send internal error
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ root = json_object ();
+ list = json_array ();
+ json_object_set_new (root, "blind_session_pubs", list);
+
+ TALER_hash_context_start (&hash_context);
+
+ {
+ struct RefreshMeltResponseSignatureBody body;
+ json_t *sig_json;
+
+ body.purpose.size = htonl (sizeof (struct RefreshMeltResponseSignatureBody));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_RESPONSE);
+ TALER_hash_context_finish (&hash_context, &body.melt_response_hash);
+ sig_json = sign_as_json (&body.purpose);
+ GNUNET_assert (NULL != sig_json);
+ json_object_set (root, "signature", sig_json);
+ }
+
+ return send_response_json (connection,
+ root,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Verify a signature that is encoded in a JSON object
+ *
+ * @param connection the connection to send errors to
+ * @param root the JSON object with the signature
+ * @param the public key that the signature was created with
+ * @param purpose the signed message
+ * @return GNUNET_YES if the signature was valid
+ * GNUNET_NO if the signature was invalid
+ * GNUNET_SYSERR on internal error
+ */
+static int
+request_json_check_signature (struct MHD_Connection *connection,
+ json_t *root,
+ struct GNUNET_CRYPTO_EddsaPublicKey *pub,
+ struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
+{
+ struct GNUNET_CRYPTO_EddsaSignature signature;
+ int size;
+ uint32_t purpose_num;
+ int res;
+ json_t *el;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "sig",
+ JNAV_RET_DATA,
+ &signature,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+
+ if (GNUNET_OK != res)
+ return res;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "purpose",
+ JNAV_RET_TYPED_JSON,
+ JSON_INTEGER,
+ &el);
+
+ if (GNUNET_OK != res)
+ return res;
+
+ purpose_num = json_integer_value (el);
+
+ if (purpose_num != ntohl (purpose->purpose))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (purpose wrong)\n");
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "signature invalid (purpose)");
+ }
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "size",
+ JNAV_RET_TYPED_JSON,
+ JSON_INTEGER,
+ &el);
+
+ if (GNUNET_OK != res)
+ return res;
+
+ size = json_integer_value (el);
+
+ if (size != ntohl (purpose->size))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (size wrong)\n");
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ GNUNET_NO, GNUNET_SYSERR,
+ "{s:s}",
+ "error", "signature invalid (size)");
+ }
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (purpose_num,
+ purpose,
+ &signature,
+ pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (did not verify)\n");
+ return request_send_json_pack (connection, MHD_HTTP_UNAUTHORIZED,
+ "{s:s}",
+ "error", "invalid signature (verification)");
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *root;
+ PGconn *db_conn;
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+ int res;
+ struct MintKeyState *key_state;
+ struct TALER_Amount requested_cost;
+ struct TALER_Amount melt_balance;
+ struct TALER_HashContext hash_context;
+ struct GNUNET_HashCode melt_hash;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size, &root);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ return GNUNET_SYSERR;
+
+ /* session_pub field must always be present */
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "session_pub",
+ JNAV_RET_DATA,
+ &refresh_session_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+ /* Send response immediately if we already know the session.
+ * Do _not_ care about fields other than session_pub in this case. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ &refresh_session_pub,
+ NULL);
+ if (GNUNET_YES == res)
+ return helper_refresh_send_melt_response (connection,
+ db_conn,
+ &refresh_session_pub);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* We incrementally update the db with other parameters in a transaction.
+ * The transaction is aborted if some parameter does not validate. */
+
+ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_create_refresh_session (db_conn,
+ &refresh_session_pub))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ TALER_MINT_DB_rollback (db_conn);
+ return MHD_NO;
+ }
+
+ /* The next two operations must see the same key state,
+ * thus we acquire it here. */
+
+ key_state = TALER_MINT_key_state_acquire ();
+
+ /* Write requested denominations to the DB,
+ * and sum the costs (value plus fees) */
+
+ TALER_hash_context_start (&hash_context);
+
+ if (GNUNET_OK != (res = refresh_accept_denoms (connection, db_conn, key_state,
+ &refresh_session_pub, root,
+ &hash_context,
+ &requested_cost)))
+ {
+ TALER_MINT_key_state_release (key_state);
+ TALER_MINT_DB_rollback (db_conn);
+ // FIXME: hash_context_end?
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ /* Write old coins to db and sum their value */
+
+ if (GNUNET_OK != (res = refresh_accept_melts (connection, db_conn, key_state,
+ &refresh_session_pub, root,
+ &hash_context,
+ &melt_balance)))
+ {
+ TALER_MINT_key_state_release (key_state);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_finish (&hash_context, &melt_hash);
+
+ TALER_MINT_key_state_release (key_state);
+
+ /* check that signature from the session public key is ok */
+ {
+ struct RefreshMeltSignatureBody body;
+ json_t *melt_sig_json;
+
+ melt_sig_json = json_object_get (root, "melt_signature");
+ if (NULL == melt_sig_json)
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return request_send_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "melt_signature missing");
+ }
+
+ body.melt_hash = melt_hash;
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT);
+ body.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody));
+
+ if (GNUNET_OK != (res = request_json_check_signature (connection,
+ melt_sig_json,
+ &refresh_session_pub,
+ &body.purpose)))
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ }
+
+
+ /* Request is only ok if cost of requested coins
+ * does not exceed value of melted coins. */
+
+ // FIXME: also, consider fees?
+ if (TALER_amount_cmp (melt_balance, requested_cost) < 0)
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+
+ return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN,
+ "{s:s}",
+ "error", "not enough coins melted");
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_commit (db_conn))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ return helper_refresh_send_melt_response (connection,
+ db_conn,
+ &refresh_session_pub);
+}
+
+
+/**
+ * Send a response to a "/refresh/commit" request.
+ *
+ * @param connection the connection to send the response to
+ * @param db_conn the mint database
+ * @param refresh_session the refresh session
+ * @return a MHD status code
+ */
+static int
+refresh_send_commit_response (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ struct RefreshSession *refresh_session)
+{
+ struct RefreshCommitResponseSignatureBody body;
+ json_t *sig_json;
+
+ body.purpose.size = htonl (sizeof (struct RefreshCommitResponseSignatureBody));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_COMMIT_RESPONSE);
+ body.noreveal_index = htons (refresh_session->noreveal_index);
+ sig_json = sign_as_json (&body.purpose);
+ GNUNET_assert (NULL != sig_json);
+ return request_send_json_pack (connection, MHD_HTTP_OK,
+ "{s:i, s:o}",
+ "noreveal_index", (int) refresh_session->noreveal_index,
+ "signature", sig_json);
+}
+
+
+/**
+ * Handle a "/refresh/commit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_commit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+ int res;
+ PGconn *db_conn;
+ struct RefreshSession refresh_session;
+ int i;
+ struct GNUNET_HashCode commit_hash;
+ struct TALER_HashContext hash_context;
+ json_t *root;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size, &root);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "session_pub",
+ JNAV_RET_DATA,
+ &refresh_session_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Send response immediately if we already know the session.
+ * Do _not_ care about fields other than session_pub in this case. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ &refresh_session_pub,
+ &refresh_session);
+ if ( (GNUNET_YES == res) &&
+ (GNUNET_YES == refresh_session.has_commit_sig) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "sending cached commit response\n");
+ res = refresh_send_commit_response (connection,
+ db_conn,
+ &refresh_session);
+ GNUNET_break (res != GNUNET_SYSERR);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Re-fetch the session information from the database,
+ * in case a concurrent transaction modified it. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ &refresh_session_pub,
+ &refresh_session);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return MHD_NO;
+ }
+
+ TALER_hash_context_start (&hash_context);
+
+ for (i = 0; i < refresh_session.kappa; i++)
+ {
+ unsigned int j;
+
+ for (j = 0; j < refresh_session.num_newcoins; j++)
+ {
+ struct RefreshCommitCoin commit_coin;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "coin_evs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "link_encs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ commit_coin.link_enc,
+ TALER_REFRESH_LINK_LENGTH);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ commit_coin.link_enc,
+ TALER_REFRESH_LINK_LENGTH);
+
+ commit_coin.cnc_index = i;
+ commit_coin.newcoin_index = j;
+ commit_coin.session_pub = refresh_session_pub;
+
+ if (GNUNET_OK !=
+ TALER_MINT_DB_insert_refresh_commit_coin (db_conn,
+ &commit_coin))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return MHD_NO;
+ }
+ }
+ }
+
+ for (i = 0; i < refresh_session.kappa; i++)
+ {
+ unsigned int j;
+ for (j = 0; j < refresh_session.num_oldcoins; j++)
+ {
+ struct RefreshCommitLink commit_link;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "transfer_pubs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ &commit_link.transfer_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ &commit_link.transfer_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "secret_encs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ commit_link.shared_secret_enc,
+ TALER_REFRESH_SHARED_SECRET_LENGTH);
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ commit_link.shared_secret_enc,
+ TALER_REFRESH_SHARED_SECRET_LENGTH);
+
+ commit_link.cnc_index = i;
+ commit_link.oldcoin_index = j;
+ commit_link.session_pub = refresh_session_pub;
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_refresh_commit_link (db_conn, &commit_link))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return MHD_NO;
+ }
+ }
+ }
+
+ TALER_hash_context_finish (&hash_context, &commit_hash);
+
+ {
+ struct RefreshCommitSignatureBody body;
+ json_t *commit_sig_json;
+
+ commit_sig_json = json_object_get (root, "commit_signature");
+ if (NULL == commit_sig_json)
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "commit_signature missing");
+ }
+
+ body.commit_hash = commit_hash;
+
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_COMMIT);
+ body.purpose.size = htonl (sizeof (struct RefreshCommitSignatureBody));
+
+ if (GNUNET_OK != (res = request_json_check_signature (connection,
+ commit_sig_json,
+ &refresh_session_pub,
+ &body.purpose)))
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_commit (db_conn))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ return refresh_send_commit_response (connection, db_conn, &refresh_session);
+}
+
+
+/**
+ * Send response for "/refresh/reveal".
+ *
+ * @param connection the MHD connection
+ * @param db_conn the connection to the mint's db
+ * @param refresh_session_pub the refresh session's public key
+ * @return a MHD result code
+ */
+static int
+helper_refresh_reveal_send_response (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub)
+{
+ int res;
+ int newcoin_index;
+ struct RefreshSession refresh_session;
+ json_t *root;
+ json_t *list;
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ refresh_session_pub,
+ &refresh_session);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ GNUNET_assert (0 != refresh_session.reveal_ok);
+
+ root = json_object ();
+ list = json_array ();
+ json_object_set_new (root, "ev_sigs", list);
+
+ for (newcoin_index = 0; newcoin_index < refresh_session.num_newcoins; newcoin_index++)
+ {
+ struct TALER_RSA_Signature ev_sig;
+
+ res = TALER_MINT_DB_get_refresh_collectable (db_conn,
+ newcoin_index,
+ refresh_session_pub,
+ &ev_sig);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ json_array_append_new (list,
+ TALER_JSON_from_data (&ev_sig,
+ sizeof (struct TALER_RSA_Signature)));
+ }
+ return send_response_json (connection,
+ root,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+ int res;
+ PGconn *db_conn;
+ struct RefreshSession refresh_session;
+ struct MintKeyState *key_state;
+ int i;
+ int j;
+ json_t *root;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data, upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "session_pub",
+ JNAV_RET_DATA,
+ &refresh_session_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Send response immediately if we already know the session,
+ * and the session commited already.
+ * Do _not_ care about fields other than session_pub in this case. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn, &refresh_session_pub, &refresh_session);
+ if (GNUNET_YES == res && 0 != refresh_session.reveal_ok)
+ return helper_refresh_reveal_send_response (connection, db_conn, &refresh_session_pub);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Check that the transfer private keys match their commitments.
+ * Then derive the shared secret for each kappa, and check that they match. */
+
+ for (i = 0; i < refresh_session.kappa; i++)
+ {
+ struct GNUNET_HashCode last_shared_secret;
+ int secret_initialized = GNUNET_NO;
+
+ if (i == (refresh_session.noreveal_index % refresh_session.kappa))
+ continue;
+
+ for (j = 0; j < refresh_session.num_oldcoins; j++)
+ {
+ struct GNUNET_CRYPTO_EcdsaPrivateKey transfer_priv;
+ struct RefreshCommitLink commit_link;
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+ struct GNUNET_HashCode transfer_secret;
+ struct GNUNET_HashCode shared_secret;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "transfer_privs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ &transfer_priv,
+ sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ res = TALER_MINT_DB_get_refresh_commit_link (db_conn,
+ &refresh_session_pub,
+ i, j,
+ &commit_link);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ res = TALER_MINT_DB_get_refresh_melt (db_conn, &refresh_session_pub, j, &coin_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* We're converting key types here, which is not very nice
+ * but necessary and harmless (keys will be thrown away later). */
+ if (GNUNET_OK != GNUNET_CRYPTO_ecc_ecdh ((struct GNUNET_CRYPTO_EcdhePrivateKey *) &transfer_priv,
+ (struct GNUNET_CRYPTO_EcdhePublicKey *) &coin_pub,
+ &transfer_secret))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 >= TALER_refresh_decrypt (commit_link.shared_secret_enc, TALER_REFRESH_SHARED_SECRET_LENGTH,
+ &transfer_secret, &shared_secret))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decryption failed\n");
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_NO == secret_initialized)
+ {
+ secret_initialized = GNUNET_YES;
+ last_shared_secret = shared_secret;
+ }
+ else if (0 != memcmp (&shared_secret, &last_shared_secret, sizeof (struct GNUNET_HashCode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "shared secrets do not match\n");
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check;
+ GNUNET_CRYPTO_ecdsa_key_get_public (&transfer_priv, &transfer_pub_check);
+ if (0 != memcmp (&transfer_pub_check, &commit_link.transfer_pub, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "transfer keys do not match\n");
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+
+ /* Check that the commitments for all new coins were correct */
+
+ for (j = 0; j < refresh_session.num_newcoins; j++)
+ {
+ struct RefreshCommitCoin commit_coin;
+ struct LinkData link_data;
+ struct TALER_RSA_BlindedSignaturePurpose *coin_ev_check;
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+ struct TALER_RSA_BlindingKey *bkey;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+
+ bkey = NULL;
+ res = TALER_MINT_DB_get_refresh_commit_coin (db_conn,
+ &refresh_session_pub,
+ i, j,
+ &commit_coin);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+
+ if (0 >= TALER_refresh_decrypt (commit_coin.link_enc, sizeof (struct LinkData),
+ &last_shared_secret, &link_data))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decryption failed\n");
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_CRYPTO_ecdsa_key_get_public (&link_data.coin_priv, &coin_pub);
+ if (NULL == (bkey = TALER_RSA_blinding_key_decode (&link_data.bkey_enc)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid blinding key\n");
+ return GNUNET_SYSERR;
+ }
+ res = TALER_MINT_DB_get_refresh_order (db_conn, j, &refresh_session_pub, &denom_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == (coin_ev_check =
+ TALER_RSA_message_blind (&coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+ bkey,
+ &denom_pub)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "blind failed\n");
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != memcmp (&coin_ev_check,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "blind envelope does not match for kappa=%d, old=%d\n",
+ (int) i, (int) j);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+
+
+ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ for (j = 0; j < refresh_session.num_newcoins; j++)
+ {
+ struct RefreshCommitCoin commit_coin;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct TALER_RSA_Signature ev_sig;
+
+ res = TALER_MINT_DB_get_refresh_commit_coin (db_conn,
+ &refresh_session_pub,
+ refresh_session.noreveal_index % refresh_session.kappa,
+ j,
+ &commit_coin);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ res = TALER_MINT_DB_get_refresh_order (db_conn, j, &refresh_session_pub, &denom_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+
+ key_state = TALER_MINT_key_state_acquire ();
+ dki = TALER_MINT_get_denom_key (key_state, &denom_pub);
+ TALER_MINT_key_state_release (key_state);
+ if (NULL == dki)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_RSA_sign (dki->denom_priv,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &ev_sig))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ res = TALER_MINT_DB_insert_refresh_collectable (db_conn,
+ j,
+ &refresh_session_pub,
+ &ev_sig);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ /* mark that reveal was successful */
+
+ res = TALER_MINT_DB_set_reveal_ok (db_conn, &refresh_session_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_commit (db_conn))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ return helper_refresh_reveal_send_response (connection, db_conn, &refresh_session_pub);
+}
+
+
+/**
+ * Handle a "/refresh/link" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_link (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+ int res;
+ json_t *root;
+ json_t *list;
+ PGconn *db_conn;
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
+ struct SharedSecretEnc shared_secret_enc;
+
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "coin_pub",
+ &coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ list = json_array ();
+ root = json_object ();
+ json_object_set_new (root, "new_coins", list);
+
+ res = TALER_db_get_transfer (db_conn,
+ &coin_pub,
+ &transfer_pub,
+ &shared_secret_enc);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ {
+ return request_send_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "link data not found (transfer)");
+ }
+ GNUNET_assert (GNUNET_OK == res);
+
+ res = TALER_db_get_link (db_conn, &coin_pub, link_iter, list);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ {
+ return request_send_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "link data not found (link)");
+ }
+ GNUNET_assert (GNUNET_OK == res);
+ json_object_set_new (root, "transfer_pub",
+ TALER_JSON_from_data (&transfer_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)));
+ json_object_set_new (root, "secret_enc",
+ TALER_JSON_from_data (&shared_secret_enc,
+ sizeof (struct SharedSecretEnc)));
+ return send_response_json (connection, root, MHD_HTTP_OK);
+}
+
+
+/* end of taler-mint-httpd_refresh.c */
diff --git a/src/mint/taler-mint-httpd_refresh.h b/src/mint/taler-mint-httpd_refresh.h
new file mode 100644
index 000000000..20e7d97c2
--- /dev/null
+++ b/src/mint/taler-mint-httpd_refresh.h
@@ -0,0 +1,103 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_refresh.h
+ * @brief Handle /refresh/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_REFRESH_H
+#define TALER_MINT_HTTPD_REFRESH_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Handle a "/refresh/melt" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/commit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_commit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/link" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_link (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/reveal" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+#endif
diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c
new file mode 100644
index 000000000..7ffa45706
--- /dev/null
+++ b/src/mint/taler-mint-httpd_withdraw.c
@@ -0,0 +1,400 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_withdraw.c
+ * @brief Handle /withdraw/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_withdraw.h"
+
+
+/**
+ * Convert a signature (with purpose) to
+ * a JSON object representation.
+ *
+ * @param purpose purpose of the signature
+ * @param signature the signature
+ * @return the JSON reporesentation of the signature with purpose
+ */
+static json_t *
+sig_to_json (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+ const struct GNUNET_CRYPTO_EddsaSignature *signature)
+{
+ json_t *root;
+ json_t *el;
+
+ root = json_object ();
+
+ el = json_integer ((json_int_t) ntohl (purpose->size));
+ json_object_set_new (root, "size", el);
+
+ el = json_integer ((json_int_t) ntohl (purpose->purpose));
+ json_object_set_new (root, "purpose", el);
+
+ el = TALER_JSON_from_data (signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+ json_object_set_new (root, "sig", el);
+
+ return root;
+}
+
+
+/**
+ * Sign a reserve's status with the current signing key.
+ *
+ * @param reserve the reserve to sign
+ * @param key_state the key state containing the current
+ * signing private key
+ */
+static void
+sign_reserve (struct Reserve *reserve,
+ struct MintKeyState *key_state)
+{
+ reserve->status_sign_pub = key_state->current_sign_key_issue.signkey_pub;
+ reserve->status_sig_purpose.purpose = htonl (TALER_SIGNATURE_RESERVE_STATUS);
+ reserve->status_sig_purpose.size = htonl (sizeof (struct Reserve) -
+ offsetof (struct Reserve, status_sig_purpose));
+ GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv,
+ &reserve->status_sig_purpose,
+ &reserve->status_sig);
+}
+
+
+/**
+ * Handle a "/withdraw/status" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+ PGconn *db_conn;
+ int res;
+ struct Reserve reserve;
+ struct MintKeyState *key_state;
+ int must_update = GNUNET_NO;
+ json_t *json;
+
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "reserve_pub",
+ &reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ res = TALER_MINT_DB_get_reserve (db_conn,
+ &reserve_pub,
+ &reserve);
+ if (GNUNET_SYSERR == res)
+ return TALER_MINT_helper_send_json_pack (rh,
+ connection,
+ connection_cls,
+ 0 /* no caching */,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error",
+ "Reserve not found");
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ key_state = TALER_MINT_key_state_acquire ();
+ if (0 != memcmp (&key_state->current_sign_key_issue.signkey_pub,
+ &reserve.status_sign_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ sign_reserve (&reserve, key_state);
+ must_update = GNUNET_YES;
+ }
+ if ((GNUNET_YES == must_update) &&
+ (GNUNET_OK != TALER_MINT_DB_update_reserve (db_conn, &reserve, !must_update)))
+ {
+ GNUNET_break (0);
+ return MHD_YES;
+ }
+
+ /* Convert the public information of a reserve (i.e.
+ excluding private key) to a JSON object. */
+ json = json_object ();
+ json_object_set_new (json,
+ "balance",
+ TALER_JSON_from_amount (TALER_amount_ntoh (reserve.balance)));
+ json_object_set_new (json,
+ "expiration",
+ TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (reserve.expiration)));
+ json_object_set_new (json,
+ "signature",
+ sig_to_json (&reserve.status_sig_purpose,
+ &reserve.status_sig));
+
+ return send_response_json (connection,
+ json,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Send positive, normal response for "/withdraw/sign".
+ *
+ * @param connection the connection to send the response to
+ * @param collectable the collectable blindcoin (i.e. the blindly signed coin)
+ * @return a MHD result code
+ */
+static int
+helper_withdraw_sign_send_reply (struct MHD_Connection *connection,
+ const struct CollectableBlindcoin *collectable)
+{
+ json_t *root = json_object ();
+
+ json_object_set_new (root, "ev_sig",
+ TALER_JSON_from_data (&collectable->ev_sig,
+ sizeof (struct TALER_RSA_Signature)));
+ return send_response_json (connection,
+ root,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Handle a "/withdraw/sign" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct TALER_WithdrawRequest wsrd;
+ int res;
+ PGconn *db_conn;
+ struct Reserve reserve;
+ struct MintKeyState *key_state;
+ struct CollectableBlindcoin collectable;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct TALER_RSA_Signature ev_sig;
+ struct TALER_Amount amount_required;
+
+ memset (&wsrd,
+ 0,
+ sizeof (struct TALER_WithdrawRequest));
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "reserve_pub",
+ &wsrd.reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "denom_pub",
+ &wsrd.denomination_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "coin_ev",
+ &wsrd.coin_envelope,
+ sizeof (struct TALER_RSA_Signature));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "reserve_sig",
+ &wsrd.sig,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ res = TALER_MINT_DB_get_collectable_blindcoin (db_conn,
+ &wsrd.coin_envelope,
+ &collectable);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Don't sign again if we have already signed the coin */
+ if (GNUNET_YES == res)
+ return helper_withdraw_sign_send_reply (connection,
+ &collectable);
+ GNUNET_assert (GNUNET_NO == res);
+ res = TALER_MINT_DB_get_reserve (db_conn,
+ &wsrd.reserve_pub,
+ &reserve);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return request_send_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "Reserve not found");
+
+ // fill out all the missing info in the request before
+ // we can check the signature on the request
+
+ wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW);
+ wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest) -
+ offsetof (struct TALER_WithdrawRequest, purpose));
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WITHDRAW,
+ &wsrd.purpose,
+ &wsrd.sig,
+ &wsrd.reserve_pub))
+ return request_send_json_pack (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ "{s:s}",
+ "error", "Invalid Signature");
+
+ key_state = TALER_MINT_key_state_acquire ();
+ dki = TALER_MINT_get_denom_key (key_state,
+ &wsrd.denomination_pub);
+ TALER_MINT_key_state_release (key_state);
+ if (NULL == dki)
+ return request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "Denomination not found");
+
+ amount_required = TALER_amount_ntoh (dki->value);
+ amount_required = TALER_amount_add (amount_required,
+ TALER_amount_ntoh (dki->fee_withdraw));
+
+ if (0 < TALER_amount_cmp (amount_required,
+ TALER_amount_ntoh (reserve.balance)))
+ return request_send_json_pack (connection,
+ MHD_HTTP_PAYMENT_REQUIRED,
+ "{s:s}",
+ "error", "Insufficient funds");
+ if (GNUNET_OK != TALER_RSA_sign (dki->denom_priv,
+ &wsrd.coin_envelope,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &ev_sig))
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ reserve.balance = TALER_amount_hton (TALER_amount_subtract (TALER_amount_ntoh (reserve.balance),
+ amount_required));
+ if (GNUNET_OK !=
+ TALER_MINT_DB_update_reserve (db_conn,
+ &reserve,
+ GNUNET_YES))
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ collectable.ev = wsrd.coin_envelope;
+ collectable.ev_sig = ev_sig;
+ collectable.reserve_pub = wsrd.reserve_pub;
+ collectable.reserve_sig = wsrd.sig;
+ if (GNUNET_OK !=
+ TALER_MINT_DB_insert_collectable_blindcoin (db_conn,
+ &collectable))
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return GNUNET_NO;;
+ }
+ return helper_withdraw_sign_send_reply (connection,
+ &collectable);
+}
+
+/* end of taler-mint-httpd_withdraw.c */
diff --git a/src/mint/taler-mint-httpd_withdraw.h b/src/mint/taler-mint-httpd_withdraw.h
new file mode 100644
index 000000000..1d292ebd9
--- /dev/null
+++ b/src/mint/taler-mint-httpd_withdraw.h
@@ -0,0 +1,65 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_withdraw.h
+ * @brief Handle /withdraw/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_WITHDRAW_H
+#define TALER_MINT_HTTPD_WITHDRAW_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+/**
+ * Handle a "/withdraw/status" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/withdraw/sign" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/mint/taler-mint-keycheck.c b/src/mint/taler-mint-keycheck.c
new file mode 100644
index 000000000..c6186859c
--- /dev/null
+++ b/src/mint/taler-mint-keycheck.c
@@ -0,0 +1,169 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-keycheck.c
+ * @brief Check mint keys for validity.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include <platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "mint.h"
+#include "taler_signatures.h"
+
+
+static char *mintdir;
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+
+static int
+signkeys_iter (void *cls, const struct TALER_MINT_SignKeyIssue *ski)
+{
+ struct GNUNET_TIME_Absolute start;
+
+ printf ("iterating over key for start time %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (ski->start)));
+
+ start = GNUNET_TIME_absolute_ntoh (ski->start);
+
+ if (ntohl (ski->purpose.size) !=
+ (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signkey with start %s has invalid purpose field (timestamp: %llu)\n",
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNKEY,
+ &ski->purpose,
+ &ski->signature,
+ &ski->master_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signkey with start %s has invalid signature (timestamp: %llu)\n",
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+ printf ("key valid\n");
+ return GNUNET_OK;
+}
+
+
+static int
+mint_signkeys_check ()
+{
+ if (0 > TALER_MINT_signkeys_iterate (mintdir, signkeys_iter, NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+static int denomkeys_iter (void *cls,
+ const char *alias,
+ const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ struct GNUNET_TIME_Absolute start;
+
+ start = GNUNET_TIME_absolute_ntoh (dki->start);
+
+ if (ntohl (dki->purpose.size) !=
+ (sizeof (struct TALER_MINT_DenomKeyIssue) - offsetof (struct TALER_MINT_DenomKeyIssue, purpose)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Denomkey for '%s' with start %s has invalid purpose field (timestamp: %llu)\n",
+ alias,
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM,
+ &dki->purpose,
+ &dki->signature,
+ &dki->master))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Denomkey for '%s'with start %s has invalid signature (timestamp: %llu)\n",
+ alias,
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+ printf ("denom key valid\n");
+
+ return GNUNET_OK;
+}
+
+
+static int
+mint_denomkeys_check ()
+{
+ if (0 > TALER_MINT_denomkeys_iterate (mintdir, denomkeys_iter, NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+static int
+mint_keys_check (void)
+{
+ if (GNUNET_OK != mint_signkeys_check ())
+ return GNUNET_NO;
+ return mint_denomkeys_check ();
+}
+
+
+/**
+ * The main function of the keyup tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keycheck", "WARNING", NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr, "mint directory not given\n");
+ return 1;
+ }
+
+ kcfg = TALER_MINT_config_load (mintdir);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr, "can't load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK != mint_keys_check ())
+ return 1;
+ return 0;
+}
+
diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c
new file mode 100644
index 000000000..8a1a77882
--- /dev/null
+++ b/src/mint/taler-mint-keyup.c
@@ -0,0 +1,657 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-keyup.c
+ * @brief Update the mint's keys for coins and signatures,
+ * using the mint's offline master key.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include <platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "mint.h"
+
+#define HASH_CUTOFF 20
+
+/**
+ * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
+ */
+#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000);
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct CoinTypeNBO
+{
+ struct GNUNET_TIME_RelativeNBO duration_spend;
+ struct GNUNET_TIME_RelativeNBO duration_withdraw;
+ struct TALER_AmountNBO value;
+ struct TALER_AmountNBO fee_withdraw;
+ struct TALER_AmountNBO fee_deposit;
+ struct TALER_AmountNBO fee_refresh;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+struct CoinTypeParams
+{
+ struct GNUNET_TIME_Relative duration_spend;
+ struct GNUNET_TIME_Relative duration_withdraw;
+ struct GNUNET_TIME_Relative duration_overlap;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_deposit;
+ struct TALER_Amount fee_refresh;
+ struct GNUNET_TIME_Absolute anchor;
+};
+
+
+/**
+ * Filename of the master private key.
+ */
+static char *masterkeyfile;
+
+/**
+ * Director of the mint, containing the keys.
+ */
+static char *mintdir;
+
+/**
+ * Time to pretend when the key update is executed.
+ */
+static char *pretend_time_str;
+
+/**
+ * Handle to the mint's configuration
+ */
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+/**
+ * Time when the key update is executed. Either the actual current time, or a
+ * pretended time.
+ */
+static struct GNUNET_TIME_Absolute now;
+
+/**
+ * Master private key of the mint.
+ */
+static struct GNUNET_CRYPTO_EddsaPrivateKey *master_priv;
+
+/**
+ * Master public key of the mint.
+ */
+static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub;
+
+/**
+ * Until what time do we provide keys?
+ */
+static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
+
+
+int
+config_get_denom (const char *section, const char *option, struct TALER_Amount *denom)
+{
+ char *str;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, section, option, &str))
+ return GNUNET_NO;
+ if (GNUNET_OK != TALER_string_to_amount (str, denom))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+char *
+get_signkey_dir ()
+{
+ char *dir;
+ size_t len;
+ len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mintdir);
+ GNUNET_assert (len > 0);
+ return dir;
+}
+
+
+char *
+get_signkey_file (struct GNUNET_TIME_Absolute start)
+{
+ char *dir;
+ size_t len;
+ len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu"),
+ mintdir, (long long) start.abs_value_us);
+ GNUNET_assert (len > 0);
+ return dir;
+}
+
+
+
+/**
+ * Hash the data defining the coin type.
+ * Exclude information that may not be the same for all
+ * instances of the coin type (i.e. the anchor, overlap).
+ */
+void
+hash_coin_type (const struct CoinTypeParams *p, struct GNUNET_HashCode *hash)
+{
+ struct CoinTypeNBO p_nbo;
+
+ memset (&p_nbo, 0, sizeof (struct CoinTypeNBO));
+
+ p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
+ p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
+ p_nbo.value = TALER_amount_hton (p->value);
+ p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw);
+ p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit);
+ p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh);
+
+ GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), hash);
+}
+
+
+static const char *
+get_cointype_dir (const struct CoinTypeParams *p)
+{
+ static char dir[4096];
+ size_t len;
+ struct GNUNET_HashCode hash;
+ char *hash_str;
+ char *val_str;
+ unsigned int i;
+
+ hash_coin_type (p, &hash);
+ hash_str = TALER_data_to_string_alloc (&hash, sizeof (struct GNUNET_HashCode));
+ GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
+ GNUNET_assert (NULL != hash_str);
+ hash_str[HASH_CUTOFF] = 0;
+
+ val_str = TALER_amount_to_string (p->value);
+ for (i = 0; i < strlen (val_str); i++)
+ if (':' == val_str[i] || '.' == val_str[i])
+ val_str[i] = '_';
+
+ len = GNUNET_snprintf (dir, sizeof (dir),
+ ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s"),
+ mintdir, val_str, hash_str);
+ GNUNET_assert (len > 0);
+ GNUNET_free (hash_str);
+ return dir;
+}
+
+
+static const char *
+get_cointype_file (struct CoinTypeParams *p,
+ struct GNUNET_TIME_Absolute start)
+{
+ const char *dir;
+ static char filename[4096];
+ size_t len;
+ dir = get_cointype_dir (p);
+ len = GNUNET_snprintf (filename, sizeof (filename), ("%s" DIR_SEPARATOR_STR "%llu"),
+ dir, (unsigned long long) start.abs_value_us);
+ GNUNET_assert (len > 0);
+ return filename;
+}
+
+
+/**
+ * Get the latest key file from the past.
+ *
+ * @param cls closure
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+get_anchor_iter (void *cls,
+ const char *filename)
+{
+ struct GNUNET_TIME_Absolute stamp;
+ struct GNUNET_TIME_Absolute *anchor = cls;
+ const char *base;
+ char *end = NULL;
+
+ base = GNUNET_STRINGS_get_short_name (filename);
+ stamp.abs_value_us = strtol (base, &end, 10);
+
+ if ((NULL == end) || (0 != *end))
+ {
+ fprintf(stderr, "Ignoring unexpected file '%s'.\n", filename);
+ return GNUNET_OK;
+ }
+
+ // TODO: check if it's actually a valid key file
+
+ if ((stamp.abs_value_us <= now.abs_value_us) && (stamp.abs_value_us > anchor->abs_value_us))
+ *anchor = stamp;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the timestamp where the first new key should be generated.
+ * Relies on correctly named key files.
+ *
+ * @param dir directory with the signed stuff
+ * @param duration how long is one key valid?
+ * @param overlap what's the overlap between the keys validity period?
+ * @param[out] anchor the timestamp where the first new key should be generated
+ */
+void
+get_anchor (const char *dir,
+ struct GNUNET_TIME_Relative duration,
+ struct GNUNET_TIME_Relative overlap,
+ struct GNUNET_TIME_Absolute *anchor)
+{
+ GNUNET_assert (0 == duration.rel_value_us % 1000000);
+ GNUNET_assert (0 == overlap.rel_value_us % 1000000);
+ if (GNUNET_YES != GNUNET_DISK_directory_test (dir, GNUNET_YES))
+ {
+ *anchor = now;
+ printf ("Can't look for anchor (%s)\n", dir);
+ return;
+ }
+
+ *anchor = GNUNET_TIME_UNIT_ZERO_ABS;
+ if (-1 == GNUNET_DISK_directory_scan (dir, &get_anchor_iter, anchor))
+ {
+ *anchor = now;
+ return;
+ }
+
+ if ((GNUNET_TIME_absolute_add (*anchor, duration)).abs_value_us < now.abs_value_us)
+ {
+ // there's no good anchor, start from now
+ // (existing keys are too old)
+ *anchor = now;
+ }
+ else if (anchor->abs_value_us != now.abs_value_us)
+ {
+ // we have a good anchor
+ *anchor = GNUNET_TIME_absolute_add (*anchor, duration);
+ *anchor = GNUNET_TIME_absolute_subtract (*anchor, overlap);
+ }
+ // anchor is now the stamp where we need to create a new key
+}
+
+static void
+create_signkey_issue (struct GNUNET_TIME_Absolute start,
+ struct GNUNET_TIME_Relative duration,
+ struct TALER_MINT_SignKeyIssue *issue)
+{
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ GNUNET_assert (NULL != priv);
+ issue->signkey_priv = *priv;
+ GNUNET_free (priv);
+ issue->master_pub = *master_pub;
+ issue->start = GNUNET_TIME_absolute_hton (start);
+ issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, duration));
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&issue->signkey_priv, &issue->signkey_pub);
+
+ issue->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY);
+ issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose));
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (master_priv, &issue->purpose, &issue->signature))
+ {
+ GNUNET_abort ();
+ }
+}
+
+
+static int
+check_signkey_valid (const char *signkey_filename)
+{
+ // FIXME: do real checks
+ return GNUNET_OK;
+}
+
+
+int
+mint_keys_update_signkeys ()
+{
+ struct GNUNET_TIME_Relative signkey_duration;
+ struct GNUNET_TIME_Absolute anchor;
+ char *signkey_dir;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "signkey_duration", &signkey_duration))
+ {
+ fprintf (stderr, "Can't read config value mint_keys.signkey_duration\n");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (signkey_duration, rel_value_us);
+ signkey_dir = get_signkey_dir ();
+ // make sure the directory exists
+ if (GNUNET_OK != GNUNET_DISK_directory_create (signkey_dir))
+ {
+ fprintf (stderr, "Cant create signkey dir\n");
+ return GNUNET_SYSERR;
+ }
+
+ get_anchor (signkey_dir, signkey_duration, GNUNET_TIME_UNIT_ZERO, &anchor);
+
+ while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) {
+ char *skf;
+ skf = get_signkey_file (anchor);
+ if (GNUNET_YES != GNUNET_DISK_file_test (skf))
+ {
+ struct TALER_MINT_SignKeyIssue signkey_issue;
+ ssize_t nwrite;
+ printf ("Generating signing key for %s.\n", GNUNET_STRINGS_absolute_time_to_string (anchor));
+ create_signkey_issue (anchor, signkey_duration, &signkey_issue);
+ nwrite = GNUNET_DISK_fn_write (skf, &signkey_issue, sizeof (struct TALER_MINT_SignKeyIssue),
+ (GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ));
+ if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue))
+ {
+ fprintf (stderr, "Can't write to file '%s'\n", skf);
+ return GNUNET_SYSERR;
+ }
+ }
+ else if (GNUNET_OK != check_signkey_valid (skf))
+ {
+ return GNUNET_SYSERR;
+ }
+ anchor = GNUNET_TIME_absolute_add (anchor, signkey_duration);
+ }
+ return GNUNET_OK;
+}
+
+
+int
+get_cointype_params (const char *ct, struct CoinTypeParams *params)
+{
+ const char *dir;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_withdraw", ct, &params->duration_withdraw))
+ {
+ fprintf (stderr, "Withdraw duration not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_withdraw, rel_value_us);
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_spend", ct, &params->duration_spend))
+ {
+ fprintf (stderr, "Spend duration not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_spend, rel_value_us);
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_overlap", ct, &params->duration_overlap))
+ {
+ fprintf (stderr, "Overlap duration not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_overlap, rel_value_us);
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_value", ct, &params->value))
+ {
+ fprintf (stderr, "Value not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_fee_withdraw", ct, &params->fee_withdraw))
+ {
+ fprintf (stderr, "Withdraw fee not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_fee_deposit", ct, &params->fee_deposit))
+ {
+ fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_fee_refresh", ct, &params->fee_refresh))
+ {
+ fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ dir = get_cointype_dir (params);
+ get_anchor (dir, params->duration_spend, params->duration_overlap, &params->anchor);
+ return GNUNET_OK;
+}
+
+
+static void
+create_denomkey_issue (struct CoinTypeParams *params, struct TALER_MINT_DenomKeyIssue *dki)
+{
+ GNUNET_assert (NULL != (dki->denom_priv = TALER_RSA_key_create ()));
+ TALER_RSA_key_get_public (dki->denom_priv, &dki->denom_pub);
+ dki->master = *master_pub;
+ dki->start = GNUNET_TIME_absolute_hton (params->anchor);
+ dki->expire_withdraw =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
+ params->duration_withdraw));
+ dki->expire_spend =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
+ params->duration_spend));
+ dki->value = TALER_amount_hton (params->value);
+ dki->fee_withdraw = TALER_amount_hton (params->fee_withdraw);
+ dki->fee_deposit = TALER_amount_hton (params->fee_deposit);
+ dki->fee_refresh = TALER_amount_hton (params->fee_refresh);
+
+ dki->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM);
+ dki->purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssue) - offsetof (struct TALER_MINT_DenomKeyIssue, purpose));
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (master_priv, &dki->purpose, &dki->signature))
+ {
+ GNUNET_abort ();
+ }
+}
+
+
+static int
+check_cointype_valid (const char *filename, struct CoinTypeParams *params)
+{
+ // FIXME: add real checks
+ return GNUNET_OK;
+}
+
+
+int
+mint_keys_update_cointype (const char *coin_alias)
+{
+ struct CoinTypeParams p;
+ const char *cointype_dir;
+
+ if (GNUNET_OK != get_cointype_params (coin_alias, &p))
+ return GNUNET_SYSERR;
+
+ cointype_dir = get_cointype_dir (&p);
+ if (GNUNET_OK != GNUNET_DISK_directory_create (cointype_dir))
+ return GNUNET_SYSERR;
+
+ while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) {
+ const char *dkf;
+ dkf = get_cointype_file (&p, p.anchor);
+
+ if (GNUNET_YES != GNUNET_DISK_file_test (dkf))
+ {
+ struct TALER_MINT_DenomKeyIssue denomkey_issue;
+ int ret;
+ printf ("Generating denomination key for type '%s', start %s.\n",
+ coin_alias, GNUNET_STRINGS_absolute_time_to_string (p.anchor));
+ printf ("Target path: %s\n", dkf);
+ create_denomkey_issue (&p, &denomkey_issue);
+ ret = TALER_MINT_write_denom_key (dkf, &denomkey_issue);
+ TALER_RSA_key_free (denomkey_issue.denom_priv);
+ if (GNUNET_OK != ret)
+ {
+ fprintf (stderr, "Can't write to file '%s'\n", dkf);
+ return GNUNET_SYSERR;
+ }
+ }
+ else if (GNUNET_OK != check_cointype_valid (dkf, &p))
+ {
+ return GNUNET_SYSERR;
+ }
+ p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend);
+ p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, p.duration_overlap);
+ }
+ return GNUNET_OK;
+}
+
+
+int
+mint_keys_update_denomkeys ()
+{
+ char *coin_types;
+ char *ct;
+ char *tok_ctx;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint_keys", "coin_types", &coin_types))
+ {
+ fprintf (stderr, "mint_keys.coin_types not in configuration\n");
+ return GNUNET_SYSERR;
+ }
+
+ for (ct = strtok_r (coin_types, " ", &tok_ctx);
+ ct != NULL;
+ ct = strtok_r (NULL, " ", &tok_ctx))
+ {
+ if (GNUNET_OK != mint_keys_update_cointype (ct))
+ {
+ GNUNET_free (coin_types);
+ return GNUNET_SYSERR;
+ }
+ }
+ GNUNET_free (coin_types);
+ return GNUNET_OK;
+}
+
+
+static int
+mint_keys_update ()
+{
+ int ret;
+ struct GNUNET_TIME_Relative lookahead_sign;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "lookahead_sign", &lookahead_sign))
+ {
+ fprintf (stderr, "mint_keys.lookahead_sign not found\n");
+ return GNUNET_SYSERR;
+ }
+ if (lookahead_sign.rel_value_us == 0)
+ {
+ fprintf (stderr, "mint_keys.lookahead_sign must not be zero\n");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (lookahead_sign, rel_value_us);
+ lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, lookahead_sign);
+
+ ret = mint_keys_update_signkeys ();
+ if (GNUNET_OK != ret)
+ return GNUNET_SYSERR;
+
+ return mint_keys_update_denomkeys ();
+}
+
+
+/**
+ * The main function of the keyup tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'m', "master-key", "FILE",
+ "master key file (private key)", 1,
+ &GNUNET_GETOPT_set_filename, &masterkeyfile},
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ {'t', "time", "TIMESTAMP",
+ "pretend it is a different time for the update", 0,
+ &GNUNET_GETOPT_set_string, &pretend_time_str},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keyup", "WARNING", NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr, "mint directory not given\n");
+ return 1;
+ }
+
+ if (NULL != pretend_time_str)
+ {
+ if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, &now))
+ {
+ fprintf (stderr, "timestamp invalid\n");
+ return 1;
+ }
+ }
+ else
+ {
+ now = GNUNET_TIME_absolute_get ();
+ }
+ ROUND_TO_SECS (now, abs_value_us);
+
+ kcfg = TALER_MINT_config_load (mintdir);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr, "can't load mint configuration\n");
+ return 1;
+ }
+
+ if (NULL == masterkeyfile)
+ {
+ fprintf (stderr, "master key file not given\n");
+ return 1;
+ }
+ master_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
+ if (NULL == master_priv)
+ {
+ fprintf (stderr, "master key invalid\n");
+ return 1;
+ }
+
+ master_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
+ GNUNET_CRYPTO_eddsa_key_get_public (master_priv, master_pub);
+
+ // check if key from file matches the one from the configuration
+ {
+ struct GNUNET_CRYPTO_EddsaPublicKey master_pub_from_cfg;
+ if (GNUNET_OK != TALER_configuration_get_data (kcfg, "mint", "master_pub",
+ &master_pub_from_cfg,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ fprintf (stderr, "master key missing in configuration (mint.master_pub)\n");
+ return 1;
+ }
+ if (0 != memcmp (master_pub, &master_pub_from_cfg, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ fprintf (stderr, "Mismatch between key from mint configuration and master private key file from command line.\n");
+ return 1;
+ }
+ }
+
+ if (GNUNET_OK != mint_keys_update ())
+ return 1;
+ return 0;
+}
+
diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c
new file mode 100644
index 000000000..3dd94f84b
--- /dev/null
+++ b/src/mint/taler-mint-reservemod.c
@@ -0,0 +1,215 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-reservemod.c
+ * @brief Modify reserves.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "mint.h"
+
+static char *mintdir;
+static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub;
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+static PGconn *db_conn;
+
+
+
+/**
+ * Create a new or add to existing reserve.
+ * Fails if currencies do not match.
+ *
+ * @param denom denomination to add
+ *
+ * @return ...
+ */
+int
+reservemod_add (struct TALER_Amount denom)
+{
+ PGresult *result;
+ {
+ const void *param_values[] = { reserve_pub };
+ int param_lengths[] = {sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)};
+ int param_formats[] = {1};
+ result = PQexecParams (db_conn,
+ "select balance_value, balance_fraction, balance_currency from reserves where reserve_pub=$1 limit 1;",
+ 1, NULL, (const char * const *) param_values, param_lengths, param_formats, 1);
+ }
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ fprintf (stderr, "Select failed: %s\n", PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ if (0 == PQntuples (result))
+ {
+ struct GNUNET_TIME_AbsoluteNBO exnbo;
+ exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add ( GNUNET_TIME_absolute_get (), GNUNET_TIME_UNIT_YEARS));
+
+ uint32_t value = htonl (denom.value);
+ uint32_t fraction = htonl (denom.fraction);
+ const void *param_values[] = {
+ reserve_pub,
+ &value,
+ &fraction,
+ denom.currency,
+ &exnbo};
+ int param_lengths[] = {32, 4, 4, strlen(denom.currency), 8};
+ int param_formats[] = {1, 1, 1, 1, 1};
+ result = PQexecParams (db_conn,
+ "insert into reserves (reserve_pub, balance_value, balance_fraction, balance_currency, "
+ " expiration_date )"
+ "values ($1,$2,$3,$4,$5);",
+ 5, NULL, (const char **) param_values, param_lengths, param_formats, 1);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ fprintf (stderr, "Insert failed: %s\n", PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ struct TALER_Amount old_denom;
+ struct TALER_Amount new_denom;
+ struct TALER_AmountNBO new_denom_nbo;
+ int denom_indices[] = {0, 1, 2};
+ int param_lengths[] = {4, 4, 32};
+ int param_formats[] = {1, 1, 1};
+ const void *param_values[] = {
+ &new_denom_nbo.value,
+ &new_denom_nbo.fraction,
+ reserve_pub
+ };
+
+ GNUNET_assert (GNUNET_OK == TALER_TALER_DB_extract_amount (result, 0, denom_indices, &old_denom));
+ new_denom = TALER_amount_add (old_denom, denom);
+ new_denom_nbo = TALER_amount_hton (new_denom);
+ result = PQexecParams (db_conn,
+ "UPDATE reserves "
+ "SET balance_value = $1, balance_fraction = $2, "
+ " status_sig = NULL, status_sign_pub = NULL "
+ "WHERE reserve_pub = $3 ",
+ 3, NULL, (const char **) param_values, param_lengths, param_formats, 1);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ fprintf (stderr, "Update failed: %s\n", PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ fprintf (stderr, "Update failed (updated '%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ return GNUNET_SYSERR;
+ }
+
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the reservemod tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static char *reserve_pub_str;
+ static char *add_str;
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ {'R', "reserve", "KEY",
+ "reserve (public key) to modify", 1,
+ &GNUNET_GETOPT_set_string, &reserve_pub_str},
+ {'a', "add", "DENOM",
+ "value to add", 1,
+ &GNUNET_GETOPT_set_string, &add_str},
+ GNUNET_GETOPT_OPTION_END
+ };
+ char *TALER_MINT_db_connection_cfg_str;
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keycheck", "WARNING", NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr, "mint directory not given\n");
+ return 1;
+ }
+
+ reserve_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
+ if ((NULL == reserve_pub_str) ||
+ (GNUNET_OK != GNUNET_STRINGS_string_to_data (reserve_pub_str,
+ strlen (reserve_pub_str),
+ reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))))
+ {
+ fprintf (stderr, "reserve key invalid\n");
+ return 1;
+ }
+
+ kcfg = TALER_MINT_config_load (mintdir);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr, "can't load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint", "db", &TALER_MINT_db_connection_cfg_str))
+ {
+ fprintf (stderr, "db configuration string not found\n");
+ return 42;
+ }
+ db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ fprintf (stderr, "db connection failed: %s\n", PQerrorMessage (db_conn));
+ return 1;
+ }
+
+ if (NULL != add_str)
+ {
+ struct TALER_Amount add_value;
+ if (GNUNET_OK != TALER_string_to_amount (add_str, &add_value))
+ {
+ fprintf (stderr, "could not read value\n");
+ return 1;
+ }
+ if (GNUNET_OK != reservemod_add (add_value))
+ {
+ fprintf (stderr, "adding value failed\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
diff --git a/src/mint/test_mint_api.c b/src/mint/test_mint_api.c
new file mode 100644
index 000000000..965d607f5
--- /dev/null
+++ b/src/mint/test_mint_api.c
@@ -0,0 +1,211 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/test_mint_api.c
+ * @brief testcase to test mint's HTTP API interface
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_mint_service.h"
+
+struct TALER_MINT_Context *ctx;
+
+struct TALER_MINT_Handle *mint;
+
+struct TALER_MINT_KeysGetHandle *dkey_get;
+
+struct TALER_MINT_DepositHandle *dh;
+
+static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
+
+static int result;
+
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ shutdown_task = GNUNET_SCHEDULER_NO_TASK;
+ if (NULL != dkey_get)
+ TALER_MINT_keys_get_cancel (dkey_get);
+ dkey_get = NULL;
+ if (NULL != dh)
+ TALER_MINT_deposit_submit_cancel (dh);
+ dh = NULL;
+ TALER_MINT_disconnect (mint);
+ mint = NULL;
+ TALER_MINT_cleanup (ctx);
+ ctx = NULL;
+}
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a deposit
+ * permission object to a mint
+ *
+ * @param cls closure
+ * @param status 1 for successful deposit, 2 for retry, 0 for failure
+ * @param obj the received JSON object; can be NULL if it cannot be constructed
+ * from the reply
+ * @param emsg in case of unsuccessful deposit, this contains a human readable
+ * explanation.
+ */
+static void
+deposit_status (void *cls,
+ int status,
+ json_t *obj,
+ char *emsg)
+{
+ char *json_enc;
+
+ dh = NULL;
+ json_enc = NULL;
+ if (NULL != obj)
+ {
+ json_enc = json_dumps (obj, JSON_INDENT(2));
+ fprintf (stderr, "%s", json_enc);
+ }
+ if (1 == status)
+ result = GNUNET_OK;
+ else
+ GNUNET_break (0);
+ if (NULL != emsg)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deposit failed: %s\n", emsg);
+ GNUNET_SCHEDULER_shutdown ();
+}
+/**
+ * Functions of this type are called to signal completion of an asynchronous call.
+ *
+ * @param cls closure
+ * @param emsg if the asynchronous call could not be completed due to an error,
+ * this parameter contains a human readable error message
+ */
+static void
+cont (void *cls, const char *emsg)
+{
+ json_t *dp;
+ char rnd_32[32];
+ char rnd_64[64];
+ char *enc_32;
+ char *enc_64;
+
+ GNUNET_assert (NULL == cls);
+ dkey_get = NULL;
+ if (NULL != emsg)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
+
+ enc_32 = TALER_data_to_string_alloc (rnd_32, sizeof (rnd_32));
+ enc_64 = TALER_data_to_string_alloc (rnd_64, sizeof (rnd_64));
+ dp = json_pack ("{s:s s:o s:s s:s s:s s:s s:s s:s s:s s:s}",
+ "type", "DIRECT_DEPOSIT",
+ "wire", json_pack ("{s:s}", "type", "SEPA"),
+ "C", enc_32,
+ "K", enc_32,
+ "ubsig", enc_64,
+ "M", enc_32,
+ "H_a", enc_64,
+ "H_wire", enc_64,
+ "csig", enc_64,
+ "m", "B1C5GP2RB1C5G");
+ GNUNET_free (enc_32);
+ GNUNET_free (enc_64);
+ dh = TALER_MINT_deposit_submit_json (mint,
+ deposit_status,
+ NULL,
+ dp);
+ json_decref (dp);
+}
+
+
+/**
+ * Functions of this type are called to provide the retrieved signing and
+ * denomination keys of the mint. No TALER_MINT_*() functions should be called
+ * in this callback.
+ *
+ * @param cls closure passed to TALER_MINT_keys_get()
+ * @param sign_keys NULL-terminated array of pointers to the mint's signing
+ * keys. NULL if no signing keys are retrieved.
+ * @param denom_keys NULL-terminated array of pointers to the mint's
+ * denomination keys; will be NULL if no signing keys are retrieved.
+ */
+static void
+read_denom_key (void *cls,
+ struct TALER_MINT_SigningPublicKey **sign_keys,
+ struct TALER_MINT_DenomPublicKey **denom_keys)
+{
+ unsigned int cnt;
+ GNUNET_assert (NULL == cls);
+#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); return; } while (0)
+ ERR (NULL == sign_keys);
+ ERR (NULL == denom_keys);
+ for (cnt = 0; NULL != sign_keys[cnt]; cnt++)
+ GNUNET_free (sign_keys[cnt]);
+ ERR (0 == cnt);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u signing keys\n", cnt);
+ GNUNET_free (sign_keys);
+ for (cnt = 0; NULL != denom_keys[cnt]; cnt++)
+ GNUNET_free (denom_keys[cnt]);
+ ERR (0 == cnt);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u denomination keys\n", cnt);
+ GNUNET_free (denom_keys);
+#undef ERR
+ return;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+{
+ ctx = TALER_MINT_init ();
+ mint = TALER_MINT_connect (ctx, "localhost", 4241, NULL);
+ GNUNET_assert (NULL != mint);
+ dkey_get = TALER_MINT_keys_get (mint,
+ &read_denom_key, NULL,
+ &cont, NULL);
+ GNUNET_assert (NULL != dkey_get);
+ shutdown_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 5),
+ &do_shutdown, NULL);
+}
+
+int
+main (int argc, char * const *argv)
+{
+ static struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ result = GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv, "test-mint-api",
+ gettext_noop
+ ("Testcase to test mint's HTTP API interface"),
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+}
diff --git a/src/mint/test_mint_common.c b/src/mint/test_mint_common.c
new file mode 100644
index 000000000..b7cad3ea4
--- /dev/null
+++ b/src/mint/test_mint_common.c
@@ -0,0 +1,83 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e. V. (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/test_mint_common.c
+ * @brief test cases for some functions in mint/mint_common.c
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include "gnunet/gnunet_util_lib.h"
+#include "taler_rsa.h"
+#include "mint.h"
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+int
+main (int argc, const char *const argv[])
+{
+ struct TALER_MINT_DenomKeyIssue dki;
+ struct TALER_RSA_PrivateKeyBinaryEncoded *enc;
+ struct TALER_MINT_DenomKeyIssue dki_read;
+ struct TALER_RSA_PrivateKeyBinaryEncoded *enc_read;
+ char *tmpfile;
+
+ int ret;
+
+ ret = 1;
+ enc = NULL;
+ enc_read = NULL;
+ tmpfile = NULL;
+ dki.denom_priv = NULL;
+ dki_read.denom_priv = NULL;
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &dki.signature,
+ sizeof (dki) - offsetof (struct TALER_MINT_DenomKeyIssue,
+ signature));
+ dki.denom_priv = TALER_RSA_key_create ();
+ EXITIF (NULL == (enc = TALER_RSA_encode_key (dki.denom_priv)));
+ EXITIF (NULL == (tmpfile = GNUNET_DISK_mktemp ("test_mint_common")));
+ EXITIF (GNUNET_OK != TALER_MINT_write_denom_key (tmpfile, &dki));
+ EXITIF (GNUNET_OK != TALER_MINT_read_denom_key (tmpfile, &dki_read));
+ EXITIF (NULL == (enc_read = TALER_RSA_encode_key (dki_read.denom_priv)));
+ EXITIF (enc->len != enc_read->len);
+ EXITIF (0 != memcmp (enc,
+ enc_read,
+ ntohs(enc->len)));
+ EXITIF (0 != memcmp (&dki.signature,
+ &dki_read.signature,
+ sizeof (dki) - offsetof (struct TALER_MINT_DenomKeyIssue,
+ signature)));
+ ret = 0;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (enc);
+ if (NULL != tmpfile)
+ {
+ (void) unlink (tmpfile);
+ GNUNET_free (tmpfile);
+ }
+ GNUNET_free_non_null (enc_read);
+ if (NULL != dki.denom_priv)
+ TALER_RSA_key_free (dki.denom_priv);
+ if (NULL != dki_read.denom_priv)
+ TALER_RSA_key_free (dki_read.denom_priv);
+ return ret;
+}
diff --git a/src/mint/test_mint_deposits.c b/src/mint/test_mint_deposits.c
new file mode 100644
index 000000000..776bc15d2
--- /dev/null
+++ b/src/mint/test_mint_deposits.c
@@ -0,0 +1,149 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/test_mint_deposits.c
+ * @brief testcase for mint deposits
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <libpq-fe.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "mint_db.h"
+
+#define DB_URI "postgres:///taler"
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * DB connection handle
+ */
+static PGconn *conn;
+
+/**
+ * Should we not interact with a temporary table?
+ */
+static int persistent;
+
+/**
+ * Testcase result
+ */
+static int result;
+
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ if (NULL != conn)
+ PQfinish (conn);
+ conn = NULL;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+{
+ static const char wire[] = "{"
+ "\"type\":\"SEPA\","
+ "\"IBAN\":\"DE67830654080004822650\","
+ "\"NAME\":\"GNUNET E.V\","
+ "\"BIC\":\"GENODEF1SRL\""
+ "}";
+ struct Deposit *deposit;
+ struct Deposit *q_deposit;
+ uint64_t transaction_id;
+
+ deposit = NULL;
+ q_deposit = NULL;
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown, NULL);
+ EXITIF (NULL == (conn = PQconnectdb(DB_URI)));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_init_deposits (conn, !persistent));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_prepare_deposits (conn));
+ deposit = GNUNET_malloc (sizeof (struct Deposit) + sizeof (wire));
+ /* Makeup a random coin public key */
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ deposit,
+ sizeof (struct Deposit));
+ /* Makeup a random 64bit transaction ID */
+ transaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT64_MAX);
+ deposit->transaction_id = GNUNET_htonll (transaction_id);
+ /* Random amount */
+ deposit->amount.value =
+ htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
+ deposit->amount.fraction =
+ htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
+ strcpy (deposit->amount.currency, "EUR");
+ /* Copy wireformat */
+ (void) memcpy (deposit->wire, wire, sizeof (wire));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_insert_deposit (conn,
+ deposit));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_get_deposit (conn,
+ &deposit->coin_pub,
+ &q_deposit));
+ EXITIF (0 != memcmp (deposit,
+ q_deposit,
+ sizeof (struct Deposit) - offsetof(struct Deposit,
+ wire)));
+ EXITIF (transaction_id != GNUNET_ntohll (q_deposit->transaction_id));
+ result = GNUNET_OK;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (deposit);
+ GNUNET_free_non_null (q_deposit);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+}
+
+
+int main(int argc, char *const argv[])
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'T', "persist", NULL,
+ gettext_noop ("Use a persistent database table instead of a temporary one"),
+ GNUNET_NO, &GNUNET_GETOPT_set_one, &persistent},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+
+ persistent = GNUNET_NO;
+ result = GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv,
+ "test-mint-deposits",
+ "testcase for mint deposits",
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+}
diff --git a/src/mint/test_mint_nayapaisa.ecc b/src/mint/test_mint_nayapaisa.ecc
new file mode 100644
index 000000000..942110b5c
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa.ecc
Binary files differ
diff --git a/src/mint/test_mint_nayapaisa/README b/src/mint/test_mint_nayapaisa/README
new file mode 100644
index 000000000..fce5e0180
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa/README
@@ -0,0 +1 @@
+This directory is a template for the mint directory.
diff --git a/src/mint/test_mint_nayapaisa/config/mint-common.conf b/src/mint/test_mint_nayapaisa/config/mint-common.conf
new file mode 100644
index 000000000..c1fede7a2
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa/config/mint-common.conf
@@ -0,0 +1,6 @@
+[mint]
+db = postgres:///taler
+port = 4241
+master_pub = 6ZE0HEY2M0FWP61M0470HYBF4K6RRD5DP54372PD2TN9N9VX2VJG
+refresh_security_parameter = 3
+
diff --git a/src/mint/test_mint_nayapaisa/config/mint-keyup.conf b/src/mint/test_mint_nayapaisa/config/mint-keyup.conf
new file mode 100644
index 000000000..1542d1a63
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa/config/mint-keyup.conf
@@ -0,0 +1,79 @@
+[mint_keys]
+
+# how long is one signkey valid?
+signkey_duration = 4 weeks
+
+# how long do we generate denomination and signing keys
+# ahead of time?
+lookahead_sign = 32 weeks 1 day
+
+# how long do we provide to clients denomination and signing keys
+# ahead of time?
+lookahead_provide = 4 weeks 1 day
+
+# what coin types do we have available?
+coin_types = default_eur_ct_10 default_eur_5 default_eur_10 default_eur_1000
+
+
+
+[mint_denom_duration_overlap]
+default_eur_ct_10 = 5 minutes
+default_eur_5 = 5 minutes
+default_eur_10 = 5 minutes
+default_eur_1000 = 5 minutes
+
+
+
+[mint_denom_value]
+default_eur_ct_10 = EUR:0.10
+default_eur_5 = EUR:5
+default_eur_10 = EUR:10
+default_eur_1000 = EUR:1000
+
+
+
+[mint_denom_duration_withdraw]
+default_eur_ct_10 = 7 days
+default_eur_5 = 7 days
+default_eur_10 = 7 days
+default_eur_1000 = 1 day
+
+
+
+[mint_denom_duration_spend]
+default_eur_ct_10 = 30 days
+default_eur_5 = 30 days
+default_eur_10 = 30 days
+default_eur_1000 = 30 day
+
+
+
+[mint_denom_fee_withdraw]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+[mint_denom_fee_deposit]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_fee_refresh]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_kappa]
+default_eur_ct_10 = 3
+default_eur_5 = 3
+default_eur_10 = 3
+default_eur_1000 = 5
+
diff --git a/src/mint/test_mint_nyadirahim.ecc b/src/mint/test_mint_nyadirahim.ecc
new file mode 100644
index 000000000..9db920894
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim.ecc
@@ -0,0 +1 @@
+WBf r񷕊Ќ:Vj \ No newline at end of file
diff --git a/src/mint/test_mint_nyadirahim/README b/src/mint/test_mint_nyadirahim/README
new file mode 100644
index 000000000..fce5e0180
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim/README
@@ -0,0 +1 @@
+This directory is a template for the mint directory.
diff --git a/src/mint/test_mint_nyadirahim/config/mint-common.conf b/src/mint/test_mint_nyadirahim/config/mint-common.conf
new file mode 100644
index 000000000..c4d528634
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim/config/mint-common.conf
@@ -0,0 +1,6 @@
+[mint]
+db = postgres:///taler
+port = 4241
+master_pub = 7995WKK71KPKTBBMA5BHNBSZFGNRZPYNXDJMQ8EK86V9598H03TG
+refresh_security_parameter = 3
+
diff --git a/src/mint/test_mint_nyadirahim/config/mint-keyup.conf b/src/mint/test_mint_nyadirahim/config/mint-keyup.conf
new file mode 100644
index 000000000..1542d1a63
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim/config/mint-keyup.conf
@@ -0,0 +1,79 @@
+[mint_keys]
+
+# how long is one signkey valid?
+signkey_duration = 4 weeks
+
+# how long do we generate denomination and signing keys
+# ahead of time?
+lookahead_sign = 32 weeks 1 day
+
+# how long do we provide to clients denomination and signing keys
+# ahead of time?
+lookahead_provide = 4 weeks 1 day
+
+# what coin types do we have available?
+coin_types = default_eur_ct_10 default_eur_5 default_eur_10 default_eur_1000
+
+
+
+[mint_denom_duration_overlap]
+default_eur_ct_10 = 5 minutes
+default_eur_5 = 5 minutes
+default_eur_10 = 5 minutes
+default_eur_1000 = 5 minutes
+
+
+
+[mint_denom_value]
+default_eur_ct_10 = EUR:0.10
+default_eur_5 = EUR:5
+default_eur_10 = EUR:10
+default_eur_1000 = EUR:1000
+
+
+
+[mint_denom_duration_withdraw]
+default_eur_ct_10 = 7 days
+default_eur_5 = 7 days
+default_eur_10 = 7 days
+default_eur_1000 = 1 day
+
+
+
+[mint_denom_duration_spend]
+default_eur_ct_10 = 30 days
+default_eur_5 = 30 days
+default_eur_10 = 30 days
+default_eur_1000 = 30 day
+
+
+
+[mint_denom_fee_withdraw]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+[mint_denom_fee_deposit]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_fee_refresh]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_kappa]
+default_eur_ct_10 = 3
+default_eur_5 = 3
+default_eur_10 = 3
+default_eur_1000 = 5
+
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
new file mode 100644
index 000000000..f935802a6
--- /dev/null
+++ b/src/util/Makefile.am
@@ -0,0 +1,39 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LIBGCRYPT_CFLAGS) $(POSTGRESQL_CPPFLAGS)
+
+lib_LTLIBRARIES = \
+ libtalerutil.la
+
+libtalerutil_la_SOURCES = \
+ util.c \
+ json.c \
+ db.c \
+ microhttpd.c \
+ rsa.c
+
+libtalerutil_la_LIBADD = \
+ -lgnunetutil \
+ $(LIBGCRYPT_LIBS) \
+ -ljansson \
+ -lmicrohttpd \
+ -lpq
+
+libtalerutil_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -export-dynamic -no-undefined
+
+check_PROGRAMS = \
+ test-hash-context \
+ test-rsa
+
+TESTS = \
+ $(check_PROGRAMS)
+
+test_hash_context_SOURCES = test_hash_context.c
+test_hash_context_CPPFLAGS = $(AM_CPPFLAGS) $(LIBGCRYPT_CFLAGS)
+test_hash_context_LDADD = libtalerutil.la \
+ -lgnunetutil $(LIBGCRYPT_LIBS)
+
+test_rsa_SOURCES = test_rsa.c
+test_rsa_LDADD = libtalerutil.la \
+ -lgnunetutil $(LIBGCRYPT_LIBS)
diff --git a/src/util/db.c b/src/util/db.c
new file mode 100644
index 000000000..a0b234a06
--- /dev/null
+++ b/src/util/db.c
@@ -0,0 +1,196 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+
+/**
+ * @file util/db.c
+ * @brief helper functions for DB interactions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_db_lib.h"
+
+
+/**
+ * Execute a prepared statement.
+ */
+PGresult *
+TALER_DB_exec_prepared (PGconn *db_conn,
+ const char *name,
+ const struct TALER_DB_QueryParam *params)
+{
+ unsigned len;
+ unsigned i;
+
+ /* count the number of parameters */
+
+ {
+ const struct TALER_DB_QueryParam *x;
+ for (len = 0, x = params;
+ x->more;
+ len +=1, x += 1);
+ }
+
+ /* new scope to allow stack allocation without alloca */
+
+ {
+ void *param_values[len];
+ int param_lengths[len];
+ int param_formats[len];
+
+ for (i = 0; i < len; i += 1)
+ {
+ param_values[i] = (void *) params[i].data;
+ param_lengths[i] = params[i].size;
+ param_formats[i] = 1;
+ }
+ return PQexecPrepared (db_conn, name, len,
+ (const char **) param_values, param_lengths, param_formats, 1);
+ }
+}
+
+
+/**
+ * Extract results from a query result according to the given specification.
+ * If colums are NULL, the destination is not modified, and GNUNET_NO
+ * is returned.
+ *
+ * @return
+ * GNUNET_YES if all results could be extracted
+ * GNUNET_NO if at least one result was NULL
+ * GNUNET_SYSERR if a result was invalid (non-existing field)
+ */
+int
+TALER_DB_extract_result (PGresult *result,
+ struct TALER_DB_ResultSpec *rs,
+ int row)
+{
+ int had_null = GNUNET_NO;
+
+ for (; NULL != rs->fname; rs += 1)
+ {
+ int fnum;
+ fnum = PQfnumber (result, rs->fname);
+ if (fnum < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "field '%s' does not exist in result\n", rs->fname);
+ return GNUNET_SYSERR;
+ }
+
+ /* if a field is null, continue but
+ * remember that we now return a different result */
+
+ if (PQgetisnull (result, row, fnum))
+ {
+ had_null = GNUNET_YES;
+ continue;
+ }
+ const char *res;
+ if (rs->dst_size != PQgetlength (result, row, fnum))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "field '%s' has wrong size (got %u, expected %u)\n",
+ rs->fname, (int) PQgetlength (result, row, fnum), (int) rs->dst_size);
+ return GNUNET_SYSERR;
+ }
+ res = PQgetvalue (result, row, fnum);
+ GNUNET_assert (NULL != res);
+ memcpy (rs->dst, res, rs->dst_size);
+ }
+ if (GNUNET_YES == had_null)
+ return GNUNET_NO;
+ return GNUNET_YES;
+}
+
+
+int
+TALER_DB_field_isnull (PGresult *result,
+ int row,
+ const char *fname)
+{
+ int fnum;
+ fnum = PQfnumber (result, fname);
+ GNUNET_assert (fnum >= 0);
+ if (PQgetisnull (result, row, fnum))
+ return GNUNET_YES;
+ return GNUNET_NO;
+}
+
+
+int
+TALER_DB_extract_amount_nbo (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_AmountNBO *r_amount_nbo)
+{
+ int val_num;
+ int frac_num;
+ int curr_num;
+ int len;
+
+ GNUNET_assert (NULL != strstr (val_name, "_val"));
+ GNUNET_assert (NULL != strstr (frac_name, "_frac"));
+ GNUNET_assert (NULL != strstr (curr_name, "_curr"));
+
+ val_num = PQfnumber (result, val_name);
+ GNUNET_assert (val_num >= 0);
+ frac_num = PQfnumber (result, frac_name);
+ GNUNET_assert (frac_num >= 0);
+ curr_num = PQfnumber (result, curr_name);
+ GNUNET_assert (curr_num >= 0);
+
+ r_amount_nbo->value = *(uint32_t *) PQgetvalue (result, row, val_num);
+ r_amount_nbo->fraction = *(uint32_t *) PQgetvalue (result, row, frac_num);
+ memset (r_amount_nbo->currency, 0, TALER_CURRENCY_LEN);
+ // FIXME: overflow?
+ len = PQgetlength (result, row, curr_num);
+ len = GNUNET_MIN (TALER_CURRENCY_LEN, len);
+ memcpy (r_amount_nbo->currency, PQgetvalue (result, row, curr_num), len);
+ r_amount_nbo->currency[TALER_CURRENCY_LEN - 1] = '\0';
+
+ return GNUNET_OK;
+}
+
+
+int
+TALER_DB_extract_amount (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_Amount *r_amount)
+{
+ struct TALER_AmountNBO amount_nbo;
+
+ (void)
+ TALER_DB_extract_amount_nbo (result,
+ row,
+ val_name,
+ frac_name,
+ curr_name,
+ &amount_nbo);
+ r_amount->value = ntohl (amount_nbo.value);
+ r_amount->fraction = ntohl (amount_nbo.fraction);
+ (void) strncpy (r_amount->currency, amount_nbo.currency, TALER_CURRENCY_LEN);
+
+ return GNUNET_OK;
+}
+
+/* end of util/db.c */
diff --git a/src/util/json.c b/src/util/json.c
new file mode 100644
index 000000000..269e6cf26
--- /dev/null
+++ b/src/util/json.c
@@ -0,0 +1,194 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/json.c
+ * @brief helper functions for JSON processing using libjansson
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_json_lib.h"
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+/**
+ * Print JSON parsing related error information
+ */
+#define WARN_JSON(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)", \
+ __FILE__, __LINE__, error.text, error.source)
+
+/**
+ * Shorthand for JSON parsing related exit jumps.
+ */
+#define UNPACK_EXITIF(cond) \
+ do { \
+ if (cond) { WARN_JSON(error); goto EXITIF_exit; } \
+ } while (0)
+
+/**
+ * Convert a TALER amount to a JSON
+ * object.
+ *
+ * @param amount the amount
+ * @return a json object describing the amount
+ */
+json_t *
+TALER_JSON_from_amount (struct TALER_Amount amount)
+{
+ json_t *j;
+ j = json_pack ("{s: s, s:I, s:I}",
+ "currency", amount.currency,
+ "value", (json_int_t) amount.value,
+ "fraction", (json_int_t) amount.fraction);
+ GNUNET_assert (NULL != j);
+ return j;
+}
+
+
+/**
+ * Convert absolute timestamp to a json string.
+ *
+ * @param the time stamp
+ * @return a json string with the timestamp in @a stamp
+ */
+json_t *
+TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp)
+{
+ json_t *j;
+ char *mystr;
+ int ret;
+ ret = GNUNET_asprintf (&mystr, "%llu",
+ (long long) (stamp.abs_value_us / (1000 * 1000)));
+ GNUNET_assert (ret > 0);
+ j = json_string (mystr);
+ GNUNET_free (mystr);
+ return j;
+}
+
+
+
+/**
+ * Convert binary data to a JSON string
+ * with the base32crockford encoding.
+ *
+ * @param data binary data
+ * @param size size of @a data in bytes
+ * @return json string that encodes @a data
+ */
+json_t *
+TALER_JSON_from_data (const void *data, size_t size)
+{
+ char *buf;
+ json_t *json;
+ buf = TALER_data_to_string_alloc (data, size);
+ json = json_string (buf);
+ GNUNET_free (buf);
+ return json;
+}
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param r_amount where the amount has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_amount (json_t *json,
+ struct TALER_Amount *r_amount)
+{
+ char *currency;
+ json_int_t value;
+ json_int_t fraction;
+ json_error_t error;
+
+ UNPACK_EXITIF (0 != json_unpack_ex (json, &error, JSON_STRICT,
+ "{s:s, s:I, s:I}",
+ "curreny", &currency,
+ "value", &value,
+ "fraction", &fraction));
+ EXITIF (3 < strlen (currency));
+ r_amount->value = (uint32_t) value;
+ r_amount->fraction = (uint32_t) fraction;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param r_amount where the amount has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_abs (json_t *json,
+ struct GNUNET_TIME_Absolute *abs)
+{
+ const char *str;
+ unsigned long long abs_value_s;
+
+ GNUNET_assert (NULL != abs);
+ EXITIF (NULL == (str = json_string_value (json)));
+ EXITIF (1 > sscanf (str, "%llu", &abs_value_s));
+ abs->abs_value_us = abs_value_s * 1000 * 1000;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+/**
+ * Parse given JSON object to data
+ *
+ * @param json the json object representing data
+ * @param out the pointer to hold the parsed data.
+ * @param out_size the size of r_data.
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_data (json_t *json,
+ void *out,
+ size_t out_size)
+{
+ const char *enc;
+ unsigned int len;
+
+ EXITIF (NULL == (enc = json_string_value (json)));
+ len = strlen (enc);
+ EXITIF ((((len * 5) / 8) + ((((len * 5) % 8) == 0) ? 0 : 1)) == out_size);
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, len, out, out_size));
+ return GNUNET_OK;
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+/* End of util/json.c */
diff --git a/src/util/microhttpd.c b/src/util/microhttpd.c
new file mode 100644
index 000000000..f3bea74f8
--- /dev/null
+++ b/src/util/microhttpd.c
@@ -0,0 +1,417 @@
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_microhttpd_lib.h"
+
+
+
+/**
+ * Initial size for POST
+ * request buffer.
+ */
+#define REQUEST_BUFFER_INITIAL 1024
+
+/**
+ * Maximum POST request size
+ */
+#define REQUEST_BUFFER_MAX (1024*1024)
+
+
+/**
+ * Buffer for POST requests.
+ */
+struct Buffer
+{
+ /**
+ * Allocated memory
+ */
+ char *data;
+
+ /**
+ * Number of valid bytes in buffer.
+ */
+ size_t fill;
+
+ /**
+ * Number of allocated bytes in buffer.
+ */
+ size_t alloc;
+};
+
+
+/**
+ * Initialize a buffer.
+ *
+ * @param buf the buffer to initialize
+ * @param data the initial data
+ * @param data_size size of the initial data
+ * @param alloc_size size of the buffer
+ * @param max_size maximum size that the buffer can grow to
+ * @return a GNUnet result code
+ */
+static int
+buffer_init (struct Buffer *buf, const void *data, size_t data_size, size_t alloc_size, size_t max_size)
+{
+ if (data_size > max_size || alloc_size > max_size)
+ return GNUNET_SYSERR;
+ if (data_size > alloc_size)
+ alloc_size = data_size;
+ buf->data = GNUNET_malloc (alloc_size);
+ memcpy (buf->data, data, data_size);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Free the data in a buffer. Does *not* free
+ * the buffer object itself.
+ *
+ * @param buf buffer to de-initialize
+ */
+static void
+buffer_deinit (struct Buffer *buf)
+{
+ GNUNET_free (buf->data);
+ buf->data = NULL;
+}
+
+
+/**
+ * Append data to a buffer, growing the buffer if necessary.
+ *
+ * @param buf the buffer to append to
+ * @param data the data to append
+ * @param size the size of @a data
+ * @param max_size maximum size that the buffer can grow to
+ * @return GNUNET_OK on success,
+ * GNUNET_NO if the buffer can't accomodate for the new data
+ * GNUNET_SYSERR on fatal error (out of memory?)
+ */
+static int
+buffer_append (struct Buffer *buf, const void *data, size_t data_size, size_t max_size)
+{
+ if (buf->fill + data_size > max_size)
+ return GNUNET_NO;
+ if (data_size + buf->fill > buf->alloc)
+ {
+ char *new_buf;
+ size_t new_size = buf->alloc;
+ while (new_size < buf->fill + data_size)
+ new_size += 2;
+ if (new_size > max_size)
+ return GNUNET_NO;
+ new_buf = GNUNET_malloc (new_size);
+ memcpy (new_buf, buf->data, buf->fill);
+ buf->data = new_buf;
+ buf->alloc = new_size;
+ }
+ memcpy (buf->data + buf->fill, data, data_size);
+ buf->fill += data_size;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Send JSON object as response. Decreases the reference count of the
+ * JSON object.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param status_code the http status code
+ * @return MHD result code
+ */
+int
+send_response_json (struct MHD_Connection *connection,
+ json_t *json,
+ unsigned int status_code)
+{
+ struct MHD_Response *resp;
+ char *json_str;
+
+ json_str = json_dumps (json, JSON_INDENT(2));
+ json_decref (json);
+ resp = MHD_create_response_from_buffer (strlen (json_str), json_str,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL == resp)
+ return MHD_NO;
+ return MHD_queue_response (connection, status_code, resp);
+}
+
+
+/**
+ * Send a JSON object via an MHD connection,
+ * specified with the JANSSON pack syntax (see json_pack).
+ *
+ * @param connection connection to send the JSON over
+ * @param http_code HTTP status for the response
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD_YES on success or MHD_NO on error
+ */
+int
+request_send_json_pack (struct MHD_Connection *connection,
+ unsigned int http_code,
+ const char *fmt, ...)
+{
+ json_t *msg;
+ va_list argp;
+ int ret;
+
+ va_start(argp, fmt);
+ msg = json_vpack_ex (NULL, 0, fmt, argp);
+ va_end(argp);
+ if (NULL == msg)
+ return MHD_NO;
+ ret = send_response_json (connection, msg, http_code);
+ json_decref (msg);
+ return ret;
+}
+
+
+/**
+ * Process a POST request containing a JSON object.
+ *
+ * @param connection the MHD connection
+ * @param con_cs the closure (contains a 'struct Buffer *')
+ * @param upload_data the POST data
+ * @param upload_data_size the POST data size
+ * @param json the JSON object for a completed request
+ *
+ * @returns
+ * GNUNET_YES if json object was parsed
+ * GNUNET_NO is request incomplete or invalid
+ * GNUNET_SYSERR on internal error
+ */
+int
+process_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json)
+{
+ struct Buffer *r = *con_cls;
+
+ if (NULL == *con_cls)
+ {
+ /* We are seeing a fresh POST request. */
+
+ r = GNUNET_new (struct Buffer);
+ if (GNUNET_OK != buffer_init (r, upload_data, *upload_data_size,
+ REQUEST_BUFFER_INITIAL, REQUEST_BUFFER_MAX))
+ {
+ *con_cls = NULL;
+ buffer_deinit (r);
+ GNUNET_free (r);
+ return GNUNET_SYSERR;
+ }
+ *upload_data_size = 0;
+ *con_cls = r;
+ return GNUNET_NO;
+ }
+ if (0 != *upload_data_size)
+ {
+ /* We are seeing an old request with more data available. */
+
+ if (GNUNET_OK != buffer_append (r, upload_data, *upload_data_size,
+ REQUEST_BUFFER_MAX))
+ {
+ /* Request too long or we're out of memory. */
+
+ *con_cls = NULL;
+ buffer_deinit (r);
+ GNUNET_free (r);
+ return GNUNET_SYSERR;
+ }
+ *upload_data_size = 0;
+ return GNUNET_NO;
+ }
+
+ /* We have seen the whole request. */
+
+ *json = json_loadb (r->data, r->fill, 0, NULL);
+ buffer_deinit (r);
+ GNUNET_free (r);
+ if (NULL == *json)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Can't parse JSON request body\n");
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ GNUNET_NO, GNUNET_SYSERR,
+ "{s:s}",
+ "error", "invalid json");
+ }
+ *con_cls = NULL;
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see JNAV_*)
+ * @return GNUNET_YES if navigation was successful
+ * GNUNET_NO if json is malformed, error response was generated
+ * GNUNET_SYSERR on internal error
+ */
+int
+request_json_require_nav (struct MHD_Connection *connection,
+ const json_t *root, ...)
+{
+ va_list argp;
+ int ignore = GNUNET_NO;
+ // what's our current path from 'root'?
+ json_t *path;
+
+ path = json_array ();
+
+ va_start(argp, root);
+
+ while (1)
+ {
+ int command = va_arg(argp, int);
+ switch (command)
+ {
+ case JNAV_FIELD:
+ {
+ const char *fname = va_arg(argp, const char *);
+ if (GNUNET_YES == ignore)
+ break;
+ json_array_append_new (path, json_string (fname));
+ root = json_object_get (root, fname);
+ if (NULL == root)
+ {
+
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s,s:o}",
+ "error", "missing field in JSON",
+ "path", path);
+ ignore = GNUNET_YES;
+ break;
+ }
+ }
+ break;
+ case JNAV_INDEX:
+ {
+ int fnum = va_arg(argp, int);
+ if (GNUNET_YES == ignore)
+ break;
+ json_array_append_new (path, json_integer (fnum));
+ root = json_array_get (root, fnum);
+ if (NULL == root)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:o}",
+ "error", "missing index in JSON",
+ "path", path);
+ ignore = GNUNET_YES;
+ break;
+ }
+ }
+ break;
+ case JNAV_RET_DATA:
+ {
+ void *where = va_arg (argp, void *);
+ size_t len = va_arg (argp, size_t);
+ const char *str;
+ int res;
+
+ va_end(argp);
+ if (GNUNET_YES == ignore)
+ return GNUNET_NO;
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:o}",
+ "error", "string expected",
+ "path", path);
+ return GNUNET_NO;
+ }
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ where, len);
+ if (GNUNET_OK != res)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s,s:o}",
+ "error", "malformed binary data in JSON",
+ "path", path);
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+ }
+ break;
+ case JNAV_RET_DATA_VAR:
+ {
+ void **where = va_arg (argp, void **);
+ size_t *len = va_arg (argp, size_t *);
+ const char *str;
+
+ va_end(argp);
+ if (GNUNET_YES == ignore)
+ return GNUNET_NO;
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ *len = (strlen (str) * 5) / 8;
+ if (where != NULL)
+ {
+ int res;
+ *where = GNUNET_malloc (*len);
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ *where, *len);
+ if (GNUNET_OK != res)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:o}",
+ "error", "malformed binary data in JSON",
+ "path", path);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+ }
+ break;
+ case JNAV_RET_TYPED_JSON:
+ {
+ int typ = va_arg (argp, int);
+ const json_t **r_json = va_arg (argp, const json_t **);
+
+ va_end(argp);
+ if (GNUNET_YES == ignore)
+ return GNUNET_NO;
+ if (typ != -1 && json_typeof (root) != typ)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:i, s:i s:o}",
+ "error", "wrong JSON field type",
+ "type_expected", typ,
+ "type_actual", json_typeof (root),
+ "path", path);
+ return GNUNET_NO;
+ }
+ *r_json = root;
+ return GNUNET_OK;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ }
+ GNUNET_assert (0);
+}
+
+
+
diff --git a/src/util/misc.supp b/src/util/misc.supp
new file mode 100644
index 000000000..afcac6128
--- /dev/null
+++ b/src/util/misc.supp
@@ -0,0 +1,28 @@
+{
+ <gnunet_gcrypt_init>
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ ...
+ fun:GNUNET_CRYPTO_random_init
+ fun:call_init.part.0
+ ...
+}
+
+{
+ <mpi_ec_new>
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ ...
+ fun:point_from_keyparam
+ fun:_gcry_mpi_ec_new
+ ...
+}
+
+{
+ <gnunet_log_setup>
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ ...
+ fun:GNUNET_log_setup
+ ...
+} \ No newline at end of file
diff --git a/src/util/rsa.c b/src/util/rsa.c
new file mode 100644
index 000000000..cde56be9e
--- /dev/null
+++ b/src/util/rsa.c
@@ -0,0 +1,925 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/rsa.c
+ * @brief RSA key management utilities. Most of the code here is taken from
+ * gnunet-0.9.5a
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ *
+ * Authors of the gnunet code:
+ * Christian Grothoff
+ * Krista Bennett
+ * Gerd Knorr <kraxel@bytesex.org>
+ * Ioana Patrascu
+ * Tzvetan Horozov
+ */
+
+#include "platform.h"
+#include "gcrypt.h"
+#include "gnunet/gnunet_util_lib.h"
+#include "taler_rsa.h"
+
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' with the message given
+ * by gcry_strerror(rc).
+ */
+#define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0)
+
+/**
+ * Shorthand to cleanup non null mpi data types
+ */
+#define mpi_release_non_null(mpi) \
+ if (NULL != mpi) gcry_mpi_release (mpi);
+
+/**
+ * The private information of an RSA key pair.
+ * NOTE: this must match the definition in crypto_ksk.c and gnunet-rsa.c!
+ */
+struct TALER_RSA_PrivateKey
+{
+ /**
+ * Libgcrypt S-expression for the ECC key.
+ */
+ gcry_sexp_t sexp;
+};
+
+
+/**
+ * Extract values from an S-expression.
+ *
+ * @param array where to store the result(s)
+ * @param sexp S-expression to parse
+ * @param topname top-level name in the S-expression that is of interest
+ * @param elems names of the elements to extract
+ * @return 0 on success
+ */
+static int
+key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname,
+ const char *elems)
+{
+ gcry_sexp_t list;
+ gcry_sexp_t l2;
+ const char *s;
+ unsigned int i;
+ unsigned int idx;
+
+ if (! (list = gcry_sexp_find_token (sexp, topname, 0)))
+ return 1;
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ if (! list)
+ return 2;
+ idx = 0;
+ for (s = elems; *s; s++, idx++)
+ {
+ if (! (l2 = gcry_sexp_find_token (list, s, 1)))
+ {
+ for (i = 0; i < idx; i++)
+ {
+ gcry_free (array[i]);
+ array[i] = NULL;
+ }
+ gcry_sexp_release (list);
+ return 3; /* required parameter not found */
+ }
+ array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (l2);
+ if (! array[idx])
+ {
+ for (i = 0; i < idx; i++)
+ {
+ gcry_free (array[i]);
+ array[i] = NULL;
+ }
+ gcry_sexp_release (list);
+ return 4; /* required parameter is invalid */
+ }
+ }
+ gcry_sexp_release (list);
+ return 0;
+}
+
+/**
+ * If target != size, move target bytes to the
+ * end of the size-sized buffer and zero out the
+ * first target-size bytes.
+ *
+ * @param buf original buffer
+ * @param size number of bytes in the buffer
+ * @param target target size of the buffer
+ */
+static void
+adjust (unsigned char *buf, size_t size, size_t target)
+{
+ if (size < target)
+ {
+ memmove (&buf[target - size], buf, size);
+ memset (buf, 0, target - size);
+ }
+}
+
+
+/**
+ * Create a new private key. Caller must free return value.
+ *
+ * @return fresh private key
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_key_create ()
+{
+ struct TALER_RSA_PrivateKey *ret;
+ gcry_sexp_t s_key;
+ gcry_sexp_t s_keyparam;
+
+ GNUNET_assert (0 ==
+ gcry_sexp_build (&s_keyparam, NULL,
+ "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))",
+ 2048));
+ GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
+ gcry_sexp_release (s_keyparam);
+#if EXTRA_CHECKS
+ GNUNET_assert (0 == gcry_pk_testkey (s_key));
+#endif
+ ret = GNUNET_malloc (sizeof (struct TALER_RSA_PrivateKey));
+ ret->sexp = s_key;
+ return ret;
+}
+
+
+/**
+ * Free memory occupied by the private key.
+ *
+ * @param key pointer to the memory to free
+ */
+void
+TALER_RSA_key_free (struct TALER_RSA_PrivateKey *key)
+{
+ gcry_sexp_release (key->sexp);
+ GNUNET_free (key);
+}
+
+
+/**
+ * Encode the private key in a format suitable for
+ * storing it into a file.
+ * @return encoding of the private key
+ */
+struct TALER_RSA_PrivateKeyBinaryEncoded *
+TALER_RSA_encode_key (const struct TALER_RSA_PrivateKey *hostkey)
+{
+ struct TALER_RSA_PrivateKeyBinaryEncoded *retval;
+ gcry_mpi_t pkv[6];
+ void *pbu[6];
+ size_t sizes[6];
+ int rc;
+ int i;
+ int size;
+
+#if EXTRA_CHECKS
+ if (gcry_pk_testkey (hostkey->sexp))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+#endif
+
+ memset (pkv, 0, sizeof (gcry_mpi_t) * 6);
+ rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned");
+ GNUNET_assert (0 == rc);
+ size = sizeof (struct TALER_RSA_PrivateKeyBinaryEncoded);
+ for (i = 0; i < 6; i++)
+ {
+ if (NULL != pkv[i])
+ {
+ GNUNET_assert (0 ==
+ gcry_mpi_aprint (GCRYMPI_FMT_USG,
+ (unsigned char **) &pbu[i], &sizes[i],
+ pkv[i]));
+ size += sizes[i];
+ }
+ else
+ {
+ pbu[i] = NULL;
+ sizes[i] = 0;
+ }
+ }
+ GNUNET_assert (size < 65536);
+ retval = GNUNET_malloc (size);
+ retval->len = htons (size);
+ i = 0;
+ retval->sizen = htons (sizes[0]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]);
+ i += sizes[0];
+ retval->sizee = htons (sizes[1]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]);
+ i += sizes[1];
+ retval->sized = htons (sizes[2]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]);
+ i += sizes[2];
+ /* swap p and q! */
+ retval->sizep = htons (sizes[4]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]);
+ i += sizes[4];
+ retval->sizeq = htons (sizes[3]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]);
+ i += sizes[3];
+ retval->sizedmp1 = htons (0);
+ retval->sizedmq1 = htons (0);
+ memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]);
+ for (i = 0; i < 6; i++)
+ {
+ if (pkv[i] != NULL)
+ gcry_mpi_release (pkv[i]);
+ if (pbu[i] != NULL)
+ free (pbu[i]);
+ }
+ return retval;
+}
+
+
+/**
+ * Extract the public key of the given private key.
+ *
+ * @param priv the private key
+ * @param pub where to write the public key
+ */
+void
+TALER_RSA_key_get_public (const struct TALER_RSA_PrivateKey *priv,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub)
+{
+ gcry_mpi_t skey[2];
+ size_t size;
+ int rc;
+
+ rc = key_from_sexp (skey, priv->sexp, "public-key", "ne");
+ if (0 != rc)
+ rc = key_from_sexp (skey, priv->sexp, "private-key", "ne");
+ if (0 != rc)
+ rc = key_from_sexp (skey, priv->sexp, "rsa", "ne");
+ GNUNET_assert (0 == rc);
+ pub->len =
+ htons (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded) -
+ sizeof (pub->padding));
+ pub->sizen = htons (TALER_RSA_DATA_ENCODING_LENGTH);
+ pub->padding = 0;
+ size = TALER_RSA_DATA_ENCODING_LENGTH;
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG, &pub->key[0], size, &size,
+ skey[0]));
+ adjust (&pub->key[0], size, TALER_RSA_DATA_ENCODING_LENGTH);
+ size = TALER_RSA_KEY_LENGTH - TALER_RSA_DATA_ENCODING_LENGTH;
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG,
+ &pub->key
+ [TALER_RSA_DATA_ENCODING_LENGTH], size,
+ &size, skey[1]));
+ adjust (&pub->key[TALER_RSA_DATA_ENCODING_LENGTH], size,
+ TALER_RSA_KEY_LENGTH -
+ TALER_RSA_DATA_ENCODING_LENGTH);
+ gcry_mpi_release (skey[0]);
+ gcry_mpi_release (skey[1]);
+}
+
+
+/**
+ * Decode the private key from the data-format back
+ * to the "normal", internal format.
+ *
+ * @param buf the buffer where the private key data is stored
+ * @param len the length of the data in 'buffer'
+ * @return NULL on error
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_decode_key (const char *buf, uint16_t len)
+{
+ struct TALER_RSA_PrivateKey *ret;
+ const struct TALER_RSA_PrivateKeyBinaryEncoded *encoding =
+ (const struct TALER_RSA_PrivateKeyBinaryEncoded *) buf;
+ gcry_sexp_t res;
+ gcry_mpi_t n;
+ gcry_mpi_t e;
+ gcry_mpi_t d;
+ gcry_mpi_t p;
+ gcry_mpi_t q;
+ gcry_mpi_t u;
+ int rc;
+ size_t size;
+ size_t pos;
+ uint16_t enc_len;
+ size_t erroff;
+
+ enc_len = ntohs (encoding->len);
+ if (len != enc_len)
+ return NULL;
+
+ pos = 0;
+ size = ntohs (encoding->sizen);
+ rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizen);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ return NULL;
+ }
+ size = ntohs (encoding->sizee);
+ rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizee);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ return NULL;
+ }
+ size = ntohs (encoding->sized);
+ rc = gcry_mpi_scan (&d, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sized);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ return NULL;
+ }
+ /* swap p and q! */
+ size = ntohs (encoding->sizep);
+ if (size > 0)
+ {
+ rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizep);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ return NULL;
+ }
+ }
+ else
+ q = NULL;
+ size = ntohs (encoding->sizeq);
+ if (size > 0)
+ {
+ rc = gcry_mpi_scan (&p, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizeq);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ if (NULL != q)
+ gcry_mpi_release (q);
+ return NULL;
+ }
+ }
+ else
+ p = NULL;
+ pos += ntohs (encoding->sizedmp1);
+ pos += ntohs (encoding->sizedmq1);
+ size =
+ ntohs (encoding->len) - sizeof (struct TALER_RSA_PrivateKeyBinaryEncoded) - pos;
+ if (size > 0)
+ {
+ rc = gcry_mpi_scan (&u, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ if (NULL != p)
+ gcry_mpi_release (p);
+ if (NULL != q)
+ gcry_mpi_release (q);
+ return NULL;
+ }
+ }
+ else
+ u = NULL;
+
+ if ((NULL != p) && (NULL != q) && (NULL != u))
+ {
+ rc = gcry_sexp_build (&res, &erroff,
+ "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))",
+ n, e, d, p, q, u);
+ }
+ else
+ {
+ if ((NULL != p) && (NULL != q))
+ {
+ rc = gcry_sexp_build (&res, &erroff,
+ "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))",
+ n, e, d, p, q);
+ }
+ else
+ {
+ rc = gcry_sexp_build (&res, &erroff,
+ "(private-key(rsa(n %m)(e %m)(d %m)))", n, e, d);
+ }
+ }
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ if (NULL != p)
+ gcry_mpi_release (p);
+ if (NULL != q)
+ gcry_mpi_release (q);
+ if (NULL != u)
+ gcry_mpi_release (u);
+
+ if (0 != rc)
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
+ if (0 != (rc = gcry_pk_testkey (res)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
+ return NULL;
+ }
+ ret = GNUNET_malloc (sizeof (struct TALER_RSA_PrivateKey));
+ ret->sexp = res;
+ return ret;
+}
+
+
+/**
+ * Convert a public key to a string.
+ *
+ * @param pub key to convert
+ * @return string representing 'pub'
+ */
+char *
+TALER_RSA_public_key_to_string (const struct TALER_RSA_PublicKeyBinaryEncoded *pub)
+{
+ char *pubkeybuf;
+ size_t keylen = (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) * 8;
+ char *end;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ pubkeybuf = GNUNET_malloc (keylen + 1);
+ end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded),
+ pubkeybuf,
+ keylen);
+ if (NULL == end)
+ {
+ GNUNET_free (pubkeybuf);
+ return NULL;
+ }
+ *end = '\0';
+ return pubkeybuf;
+}
+
+
+/**
+ * Convert a string representing a public key to a public key.
+ *
+ * @param enc encoded public key
+ * @param enclen number of bytes in enc (without 0-terminator)
+ * @param pub where to store the public key
+ * @return GNUNET_OK on success
+ */
+int
+TALER_RSA_public_key_from_string (const char *enc,
+ size_t enclen,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub)
+{
+ size_t keylen = (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) * 8;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ if (enclen != keylen)
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen,
+ (unsigned char*) pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)))
+ return GNUNET_SYSERR;
+ if ( (ntohs (pub->len) != sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) ||
+ (ntohs (pub->padding) != 0) ||
+ (ntohs (pub->sizen) != TALER_RSA_DATA_ENCODING_LENGTH) )
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert the data specified in the given purpose argument to an
+ * S-expression suitable for signature operations.
+ *
+ * @param ptr pointer to the data to convert
+ * @param size the size of the data
+ * @return converted s-expression
+ */
+static gcry_sexp_t
+data_to_sexp (const void *ptr, size_t size)
+{
+ gcry_mpi_t value;
+ gcry_sexp_t data;
+
+ value = NULL;
+ data = NULL;
+ GNUNET_assert (0 == gcry_mpi_scan (&value, GCRYMPI_FMT_USG, ptr, size, NULL));
+ GNUNET_assert (0 == gcry_sexp_build (&data, NULL, "(data (flags raw) (value %M))", value));
+ gcry_mpi_release (value);
+ return data;
+}
+
+
+/**
+ * Sign the given hash block.
+ *
+ * @param key private key to use for the signing
+ * @param hash the block containing the hash of the message to sign
+ * @param hash_size the size of the hash block
+ * @param sig where to write the signature
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+TALER_RSA_sign (const struct TALER_RSA_PrivateKey *key,
+ const void *hash,
+ size_t hash_size,
+ struct TALER_RSA_Signature *sig)
+{
+ gcry_sexp_t result;
+ gcry_sexp_t data;
+ size_t ssize;
+ gcry_mpi_t rval;
+
+ data = data_to_sexp (hash, hash_size);
+ GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp));
+ gcry_sexp_release (data);
+ GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "s"));
+ gcry_sexp_release (result);
+ ssize = sizeof (struct TALER_RSA_Signature);
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) sig, ssize,
+ &ssize, rval));
+ gcry_mpi_release (rval);
+ adjust (sig->sig, ssize, sizeof (struct TALER_RSA_Signature));
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert the given public key from the network format to the
+ * S-expression that can be used by libgcrypt.
+ *
+ * @param publicKey public key to decode
+ * @return NULL on error
+ */
+static gcry_sexp_t
+decode_public_key (const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey)
+{
+ gcry_sexp_t result;
+ gcry_mpi_t n;
+ gcry_mpi_t e;
+ size_t size;
+ size_t erroff;
+ int rc;
+
+ if ((ntohs (publicKey->sizen) != TALER_RSA_DATA_ENCODING_LENGTH) ||
+ (ntohs (publicKey->len) !=
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded) -
+ sizeof (publicKey->padding)))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ size = TALER_RSA_DATA_ENCODING_LENGTH;
+ if (0 != (rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ return NULL;
+ }
+ size = TALER_RSA_KEY_LENGTH - TALER_RSA_DATA_ENCODING_LENGTH;
+ if (0 != (rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG,
+ &publicKey->key[TALER_RSA_DATA_ENCODING_LENGTH],
+ size, &size)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ return NULL;
+ }
+ rc = gcry_sexp_build (&result, &erroff, "(public-key(rsa(n %m)(e %m)))", n,
+ e);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */
+ return NULL;
+ }
+ return result;
+}
+
+
+/**
+ * Verify signature with the given hash.
+ *
+ * @param hash the hash code to verify against the signature
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_hash_verify (const struct GNUNET_HashCode *hash,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey)
+{
+ gcry_sexp_t data;
+ gcry_sexp_t sigdata;
+ size_t size;
+ gcry_mpi_t val;
+ gcry_sexp_t psexp;
+ size_t erroff;
+ int rc;
+
+ size = sizeof (struct TALER_RSA_Signature);
+ GNUNET_assert (0 ==
+ gcry_mpi_scan (&val, GCRYMPI_FMT_USG,
+ (const unsigned char *) sig, size, &size));
+ GNUNET_assert (0 ==
+ gcry_sexp_build (&sigdata, &erroff, "(sig-val(rsa(s %m)))",
+ val));
+ gcry_mpi_release (val);
+ data = data_to_sexp (hash, sizeof (struct GNUNET_HashCode));
+ if (! (psexp = decode_public_key (publicKey)))
+ {
+ gcry_sexp_release (data);
+ gcry_sexp_release (sigdata);
+ return GNUNET_SYSERR;
+ }
+ rc = gcry_pk_verify (sigdata, data, psexp);
+ gcry_sexp_release (psexp);
+ gcry_sexp_release (data);
+ gcry_sexp_release (sigdata);
+ if (rc)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("RSA signature verification failed at %s:%d: %s\n"), __FILE__,
+ __LINE__, gcry_strerror (rc));
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Verify signature on the given message
+ *
+ * @param msg the message
+ * @param size the size of the message
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_verify (const void *msg, size_t size,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey)
+{
+ struct GNUNET_HashCode hash;
+
+ GNUNET_CRYPTO_hash (msg, size, &hash);
+ return TALER_RSA_hash_verify (&hash, sig, publicKey);
+}
+
+/**
+ * The blinding key is equal in length to the RSA modulus
+ */
+#define TALER_RSA_BLINDING_KEY_LEN TALER_RSA_DATA_ENCODING_LENGTH
+
+struct TALER_RSA_BlindingKey
+{
+ /**
+ * The blinding factor
+ */
+ gcry_mpi_t r;
+};
+
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_create ()
+{
+ struct TALER_RSA_BlindingKey *blind;
+
+ blind = GNUNET_new (struct TALER_RSA_BlindingKey);
+ blind->r = gcry_mpi_new (TALER_RSA_BLINDING_KEY_LEN * 8);
+ gcry_mpi_randomize (blind->r, TALER_RSA_BLINDING_KEY_LEN * 8, GCRY_STRONG_RANDOM);
+ return blind;
+}
+
+
+void
+TALER_RSA_blinding_key_destroy (struct TALER_RSA_BlindingKey *bkey)
+{
+ gcry_mpi_release (bkey->r);
+ GNUNET_free (bkey);
+}
+
+
+struct TALER_RSA_BlindedSignaturePurpose *
+TALER_RSA_message_blind (const void *msg, size_t size,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey)
+{
+ struct TALER_RSA_BlindedSignaturePurpose *bsp;
+ struct GNUNET_HashCode hash;
+ gcry_sexp_t psexp;
+ gcry_mpi_t data;
+ gcry_mpi_t skey[2];
+ gcry_mpi_t r_e;
+ gcry_mpi_t data_r_e;
+ size_t rsize;
+ gcry_error_t rc;
+ int ret;
+
+ bsp = NULL;
+ psexp = NULL;
+ data = NULL;
+ skey[0] = skey[1] = NULL;
+ r_e = NULL;
+ data_r_e = NULL;
+ rsize = 0;
+ rc = 0;
+ ret = 0;
+ if (! (psexp = decode_public_key (pkey)))
+ return NULL;
+ ret = key_from_sexp (skey, psexp, "public-key", "ne");
+ if (0 != ret)
+ ret = key_from_sexp (skey, psexp, "rsa", "ne");
+ gcry_sexp_release (psexp);
+ psexp = NULL;
+ GNUNET_assert (0 == ret);
+ GNUNET_CRYPTO_hash (msg, size, &hash);
+ if (0 != (rc=gcry_mpi_scan (&data, GCRYMPI_FMT_USG,
+ (const unsigned char *) msg, size, &rsize)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_WARNING, "gcry_mpi_scan", rc);
+ goto cleanup;
+ }
+ r_e = gcry_mpi_new (0);
+ gcry_mpi_powm (r_e, bkey->r,
+ skey[1], /* e */
+ skey[0]); /* n */
+
+ data_r_e = gcry_mpi_new (0);
+ gcry_mpi_mulm (data_r_e, data, r_e, skey[0]);
+
+ bsp = GNUNET_new (struct TALER_RSA_BlindedSignaturePurpose);
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG,
+ (unsigned char *) bsp,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &rsize,
+ data_r_e);
+ GNUNET_assert (0 == rc);
+ adjust ((unsigned char *) bsp, rsize,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+
+ cleanup:
+ if (NULL != psexp) gcry_sexp_release (psexp);
+ mpi_release_non_null (skey[0]);
+ mpi_release_non_null (skey[1]);
+ mpi_release_non_null (data);
+ mpi_release_non_null (r_e);
+ mpi_release_non_null (data_r_e);
+ return bsp;
+}
+
+
+int
+TALER_RSA_unblind (struct TALER_RSA_Signature *sig,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey)
+{
+ gcry_sexp_t psexp;
+ gcry_mpi_t skey;
+ gcry_mpi_t sigval;
+ gcry_mpi_t r_inv;
+ gcry_mpi_t ubsig;
+ size_t rsize;
+ gcry_error_t rc;
+ int ret;
+
+ psexp = NULL;
+ skey = NULL;
+ sigval = NULL;
+ r_inv = NULL;
+ ubsig = NULL;
+ rsize = 0;
+ rc = 0;
+ ret = GNUNET_SYSERR;
+ if (! (psexp = decode_public_key (pkey)))
+ return GNUNET_SYSERR;
+ ret = key_from_sexp (&skey, psexp, "public-key", "n");
+ if (0 != ret)
+ ret = key_from_sexp (&skey, psexp, "rsa", "n");
+ gcry_sexp_release (psexp);
+ psexp = NULL;
+ if (0 != (rc = gcry_mpi_scan (&sigval, GCRYMPI_FMT_USG,
+ (const unsigned char *) sig,
+ sizeof (struct TALER_RSA_Signature),
+ &rsize)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ goto cleanup;
+ }
+ r_inv = gcry_mpi_new (0);
+ GNUNET_assert (1 == gcry_mpi_invm (r_inv, bkey->r, skey)); /* n: skey */
+ ubsig = gcry_mpi_new (0);
+ gcry_mpi_mulm (ubsig, sigval, r_inv, skey);
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG,
+ (unsigned char *) sig,
+ sizeof (struct TALER_RSA_Signature),
+ &rsize,
+ ubsig);
+ GNUNET_assert (0 == rc);
+ adjust ((unsigned char *) sig, rsize, sizeof (struct TALER_RSA_Signature));
+ ret = GNUNET_OK;
+
+ cleanup:
+ if (NULL != psexp) gcry_sexp_release (psexp);
+ mpi_release_non_null (skey);
+ mpi_release_non_null (sigval);
+ mpi_release_non_null (r_inv);
+ mpi_release_non_null (ubsig);
+ return ret;
+}
+
+
+/**
+ * Encode a blinding key
+ *
+ * @param bkey the blinding key to encode
+ * @param bkey_enc where to store the encoded binary key
+ * @return #GNUNET_OK upon successful encoding; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_RSA_blinding_key_encode (struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc)
+{
+ GNUNET_abort (); /* FIXME: not implemented */
+}
+
+
+/**
+ * Decode a blinding key from its encoded form
+ *
+ * @param bkey_enc the encoded blinding key
+ * @return the decoded blinding key; NULL upon error
+ */
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_decode (struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc)
+{
+ GNUNET_abort (); /* FIXME: not implemented */
+}
+
+/* end of util/rsa.c */
diff --git a/src/util/test_hash_context.c b/src/util/test_hash_context.c
new file mode 100644
index 000000000..e5110f212
--- /dev/null
+++ b/src/util/test_hash_context.c
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/test_hash_context.c
+ * @brief test case for incremental hashing
+ * @author Florian Dold
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include <gcrypt.h>
+
+#define LEN 1234
+
+int main()
+{
+ char data[1234];
+ struct GNUNET_HashCode hc1;
+ struct GNUNET_HashCode hc2;
+ struct TALER_HashContext hctx;
+
+ memset (data, 42, LEN);
+
+ TALER_hash_context_start (&hctx);
+ TALER_hash_context_read (&hctx, data, LEN);
+ TALER_hash_context_finish (&hctx, &hc1);
+
+ GNUNET_CRYPTO_hash (data, LEN, &hc2);
+
+ if (0 == memcmp (&hc1, &hc2, sizeof (struct GNUNET_HashCode)))
+ return 0;
+ return 1;
+}
+
diff --git a/src/util/test_rsa.c b/src/util/test_rsa.c
new file mode 100644
index 000000000..ac3ae2cd4
--- /dev/null
+++ b/src/util/test_rsa.c
@@ -0,0 +1,112 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/test_rsa.c
+ * @brief testcase for utility functions for RSA cryptography
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include "taler_rsa.h"
+#include <gnunet/gnunet_util_lib.h>
+
+#define TEST_PURPOSE UINT32_MAX
+
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+int
+main (int argc, char *argv[])
+{
+#define RND_BLK_SIZE 4096
+ unsigned char rnd_blk[RND_BLK_SIZE];
+ struct TALER_RSA_PrivateKey *priv;
+ struct TALER_RSA_PrivateKeyBinaryEncoded *priv_enc;
+ struct TALER_RSA_PublicKeyBinaryEncoded pubkey;
+ struct TALER_RSA_BlindingKey *bkey;
+ struct TALER_RSA_BlindedSignaturePurpose *bsp;
+ struct TALER_RSA_Signature sig;
+ struct GNUNET_HashCode hash;
+ int ret;
+
+ priv = NULL;
+ bsp = NULL;
+ bkey = NULL;
+ ret = 1;
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rnd_blk,
+ RND_BLK_SIZE);
+ GNUNET_CRYPTO_hash (rnd_blk, RND_BLK_SIZE, &hash);
+ priv = TALER_RSA_key_create ();
+ GNUNET_assert (NULL != priv);
+ EXITIF (GNUNET_OK != TALER_RSA_sign (priv,
+ &hash, sizeof (hash),
+ &sig));
+ TALER_RSA_key_get_public (priv, &pubkey);
+ EXITIF (NULL == (priv_enc = TALER_RSA_encode_key (priv)));
+ TALER_RSA_key_free (priv);
+ priv = NULL;
+ EXITIF (NULL == (priv = TALER_RSA_decode_key ((const char *) priv_enc,
+ ntohs (priv_enc->len))));
+ GNUNET_free (priv_enc);
+ priv_enc = NULL;
+ EXITIF (GNUNET_OK != TALER_RSA_hash_verify (&hash,
+ &sig,
+ &pubkey));
+ EXITIF (GNUNET_OK != TALER_RSA_verify (rnd_blk,
+ RND_BLK_SIZE,
+ &sig,
+ &pubkey));
+
+ /* test blind signing */
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rnd_blk,
+ RND_BLK_SIZE);
+ GNUNET_CRYPTO_hash (rnd_blk, RND_BLK_SIZE, &hash);
+ (void) memset (&sig, 0, sizeof (struct TALER_RSA_Signature));
+ EXITIF (NULL == (bkey = TALER_RSA_blinding_key_create ()));
+ EXITIF (NULL == (bsp =
+ TALER_RSA_message_blind (&hash, sizeof (hash),
+ bkey, &pubkey)));
+ EXITIF (GNUNET_OK != TALER_RSA_sign (priv,
+ bsp,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &sig));
+ EXITIF (GNUNET_OK != TALER_RSA_unblind (&sig,
+ bkey,
+ &pubkey));
+ EXITIF (GNUNET_OK != TALER_RSA_hash_verify (&hash,
+ &sig,
+ &pubkey));
+ ret = 0; /* all OK */
+
+ EXITIF_exit:
+ if (NULL != priv)
+ {
+ TALER_RSA_key_free (priv);
+ priv = NULL;
+ }
+ if (NULL != priv_enc)
+ {
+ GNUNET_free (priv_enc);
+ priv_enc = NULL;
+ }
+ if (NULL != bkey)
+ TALER_RSA_blinding_key_destroy (bkey);
+ GNUNET_free_non_null (bsp);
+ return ret;
+}
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 000000000..3677bcbde
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,528 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util.c
+ * @brief Common utility functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gcrypt.h>
+
+#define CURVE "Ed25519"
+
+#define AMOUNT_FRAC_BASE 1000000
+#define AMOUNT_FRAC_LEN 6
+
+
+
+static void
+fatal_error_handler (void *cls, int wtf, const char *msg)
+{
+ LOG_ERROR("Fatal error in Gcrypt: %s\n", msg);
+ abort();
+}
+
+
+/**
+ * Initialize Gcrypt library.
+ */
+void
+TALER_gcrypt_init()
+{
+ gcry_set_fatalerror_handler (&fatal_error_handler, NULL);
+ TALER_assert_as(gcry_check_version(NEED_LIBGCRYPT_VERSION),
+ "libgcrypt version mismatch");
+ /* Disable secure memory. */
+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+
+/**
+ * Generate a ECC private key.
+ *
+ * @return the s-expression representing the generated ECC private key; NULL
+ * upon error
+ */
+gcry_sexp_t
+TALER_genkey ()
+{
+ gcry_sexp_t priv_sexp;
+ gcry_sexp_t s_keyparam;
+ int rc;
+
+ if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL,
+ "(genkey(ecc(curve \"" CURVE "\")"
+ "(flags eddsa)))")))
+ {
+ LOG_GCRY_ERROR ("gcry_sexp_build", rc);
+ return NULL;
+ }
+ if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam)))
+ {
+ LOG_GCRY_ERROR ("gcry_pk_genkey", rc);
+ gcry_sexp_release (s_keyparam);
+ return NULL;
+ }
+ gcry_sexp_release (s_keyparam);
+ if (0 != (rc = gcry_pk_testkey (priv_sexp)))
+ {
+ LOG_GCRY_ERROR("gcry_pk_testkey", rc);
+ gcry_sexp_release (priv_sexp);
+ return NULL;
+ }
+ return priv_sexp;
+}
+
+
+/**
+ * Parse money amount description, in the format "A:B.C".
+ *
+ * @param str amount description
+ * @param denom amount to write the result to
+ * @return GNUNET_OK if the string is a valid amount specification,
+ * GNUNET_SYSERR if it is invalid.
+ */
+int
+TALER_string_to_amount (const char *str, struct TALER_Amount *denom)
+{
+ unsigned int i; // pos in str
+ int n; // number tmp
+ unsigned int c; // currency pos
+ uint32_t b; // base for suffix
+
+ memset (denom, 0, sizeof (struct TALER_Amount));
+
+ i = n = c = 0;
+
+ while (isspace(str[i]))
+ i++;
+
+ if (0 == str[i])
+ {
+ printf("null before currency\n");
+ return GNUNET_SYSERR;
+ }
+
+ while (str[i] != ':')
+ {
+ if (0 == str[i])
+ {
+ printf("null before colon");
+ return GNUNET_SYSERR;
+ }
+ if (c > 3)
+ {
+ printf("currency too long\n");
+ return GNUNET_SYSERR;
+ }
+ denom->currency[c] = str[i];
+ c++;
+ i++;
+ }
+
+ // skip colon
+ i++;
+
+ if (0 == str[i])
+ {
+ printf("null before value\n");
+ return GNUNET_SYSERR;
+ }
+
+ while (str[i] != '.')
+ {
+ if (0 == str[i])
+ {
+ return GNUNET_OK;
+ }
+ n = str[i] - '0';
+ if (n < 0 || n > 9)
+ {
+ printf("invalid character '%c' before comma at %u\n", (char) n, i);
+ return GNUNET_SYSERR;
+ }
+ denom->value = (denom->value * 10) + n;
+ i++;
+ }
+
+ // skip the dot
+ i++;
+
+ if (0 == str[i])
+ {
+ printf("null after dot");
+ return GNUNET_SYSERR;
+ }
+
+ b = 100000;
+
+ while (0 != str[i])
+ {
+ n = str[i] - '0';
+ if (b == 0 || n < 0 || n > 9)
+ {
+ printf("error after comma");
+ return GNUNET_SYSERR;
+ }
+ denom->fraction += n * b;
+ b /= 10;
+ i++;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * FIXME
+ */
+struct TALER_AmountNBO
+TALER_amount_hton (struct TALER_Amount d)
+{
+ struct TALER_AmountNBO dn;
+ dn.value = htonl (d.value);
+ dn.fraction = htonl (d.fraction);
+ memcpy (dn.currency, d.currency, TALER_CURRENCY_LEN);
+
+ return dn;
+}
+
+
+/**
+ * FIXME
+ */
+struct TALER_Amount
+TALER_amount_ntoh (struct TALER_AmountNBO dn)
+{
+ struct TALER_Amount d;
+ d.value = ntohl (dn.value);
+ d.fraction = ntohl (dn.fraction);
+ memcpy (d.currency, dn.currency, sizeof(dn.currency));
+
+ return d;
+}
+
+
+/**
+ * Compare the value/fraction of two amounts. Does not compare the currency,
+ * i.e. comparing amounts with the same value and fraction but different
+ * currency would return 0.
+ *
+ * @param a1 first amount
+ * @param a2 second amount
+ * @return result of the comparison
+ */
+int
+TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2)
+{
+ a1 = TALER_amount_normalize (a1);
+ a2 = TALER_amount_normalize (a2);
+ if (a1.value == a2.value)
+ {
+ if (a1.fraction < a2.fraction)
+ return -1;
+ if (a1.fraction > a2.fraction)
+ return 1;
+ return 0;
+ }
+ if (a1.value < a2.value)
+ return -1;
+ return 1;
+}
+
+
+/**
+ * Perform saturating subtraction of amounts.
+ *
+ * @param a1 amount to subtract from
+ * @param a2 amount to subtract
+ * @return (a1-a2) or 0 if a2>=a1
+ */
+struct TALER_Amount
+TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2)
+{
+ a1 = TALER_amount_normalize (a1);
+ a2 = TALER_amount_normalize (a2);
+
+ if (a1.value < a2.value)
+ {
+ a1.value = 0;
+ a1.fraction = 0;
+ return a1;
+ }
+
+ if (a1.fraction < a2.fraction)
+ {
+ if (0 == a1.value)
+ {
+ a1.fraction = 0;
+ return a1;
+ }
+ a1.fraction += AMOUNT_FRAC_BASE;
+ a1.value -= 1;
+ }
+
+ a1.fraction -= a2.fraction;
+ a1.value -= a2.value;
+
+ return a1;
+}
+
+
+/**
+ * Perform saturating addition of amounts.
+ *
+ * @param a1 first amount to add
+ * @param a2 second amount to add
+ * @return sum of a1 and a2
+ */
+struct TALER_Amount
+TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2)
+{
+ a1 = TALER_amount_normalize (a1);
+ a2 = TALER_amount_normalize (a2);
+
+ a1.value += a2.value;
+ a1.fraction += a2.fraction;
+
+ if (0 == a1.currency[0])
+ {
+ memcpy (a2.currency, a1.currency, TALER_CURRENCY_LEN);
+ }
+
+ if (0 == a2.currency[0])
+ {
+ memcpy (a1.currency, a2.currency, TALER_CURRENCY_LEN);
+ }
+
+ if (0 != a1.currency[0] && 0 != memcmp (a1.currency, a2.currency, TALER_CURRENCY_LEN))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "adding mismatching currencies\n");
+ }
+
+ if (a1.value < a2.value)
+ {
+ a1.value = UINT32_MAX;
+ a2.value = UINT32_MAX;
+ return a1;
+ }
+
+ return TALER_amount_normalize (a1);
+}
+
+
+/**
+ * Normalize the given amount.
+ *
+ * @param amout amount to normalize
+ * @return normalized amount
+ */
+struct TALER_Amount
+TALER_amount_normalize (struct TALER_Amount amount)
+{
+ while (amount.value != UINT32_MAX && amount.fraction >= AMOUNT_FRAC_BASE)
+ {
+ amount.fraction -= AMOUNT_FRAC_BASE;
+ amount.value += 1;
+ }
+ return amount;
+}
+
+
+/**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return freshly allocated string representation
+ */
+char *
+TALER_amount_to_string (struct TALER_Amount amount)
+{
+ char tail[AMOUNT_FRAC_LEN + 1] = { 0 };
+ char curr[TALER_CURRENCY_LEN + 1] = { 0 };
+ char *result = NULL;
+ int len;
+
+ memcpy (curr, amount.currency, TALER_CURRENCY_LEN);
+
+ amount = TALER_amount_normalize (amount);
+ if (0 != amount.fraction)
+ {
+ unsigned int i;
+ uint32_t n = amount.fraction;
+ for (i = 0; (i < AMOUNT_FRAC_LEN) && (n != 0); i++)
+ {
+ tail[i] = '0' + (n / (AMOUNT_FRAC_BASE / 10));
+ n = (n * 10) % (AMOUNT_FRAC_BASE);
+ }
+ tail[i] = 0;
+ len = GNUNET_asprintf (&result, "%s:%lu.%s", curr, (unsigned long) amount.value, tail);
+ }
+ else
+ {
+ len = GNUNET_asprintf (&result, "%s:%lu", curr, (unsigned long) amount.value);
+ }
+ GNUNET_assert (len > 0);
+ return result;
+}
+
+
+
+/**
+ * Return the base32crockford encoding of the given buffer.
+ *
+ * The returned string will be freshly allocated, and must be free'd
+ * with GNUNET_free.
+ *
+ * @param buffer with data
+ * @param size size of the buffer
+ * @return freshly allocated, null-terminated string
+ */
+char *
+TALER_data_to_string_alloc (const void *buf, size_t size)
+{
+ char *str_buf;
+ size_t len = size * 8;
+ char *end;
+
+ if (len % 5 > 0)
+ len += 5 - len % 5;
+ len /= 5;
+ str_buf = GNUNET_malloc (len + 1);
+ end = GNUNET_STRINGS_data_to_string (buf, size, str_buf, len);
+ if (NULL == end)
+ {
+ GNUNET_free (str_buf);
+ return NULL;
+ }
+ *end = '\0';
+ return str_buf;
+}
+
+
+/**
+ * Get encoded binary data from a configuration.
+ *
+ * @return GNUNET_OK on success
+ * GNUNET_NO is the value does not exist
+ * GNUNET_SYSERR on encoding error
+ */
+int
+TALER_configuration_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section, const char *option,
+ void *buf, size_t buf_size)
+{
+ char *enc;
+ int res;
+ size_t data_size;
+ if (GNUNET_OK != (res = GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &enc)))
+ return res;
+ data_size = (strlen (enc) * 5) / 8;
+ if (data_size != buf_size)
+ {
+ GNUNET_free (enc);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, strlen (enc),
+ buf, buf_size))
+ {
+ GNUNET_free (enc);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+static void
+derive_refresh_key (const struct GNUNET_HashCode *secret,
+ struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
+ struct GNUNET_CRYPTO_SymmetricSessionKey *skey)
+{
+ static const char ctx_key[] = "taler-key-skey";
+ static const char ctx_iv[] = "taler-key-iv";
+
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
+ ctx_key, strlen (ctx_key),
+ secret, sizeof (struct GNUNET_HashCode),
+ NULL, 0));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
+ ctx_iv, strlen (ctx_iv),
+ secret, sizeof (struct GNUNET_HashCode),
+ NULL, 0));
+}
+
+
+int
+TALER_refresh_decrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ struct GNUNET_CRYPTO_SymmetricSessionKey skey;
+
+ derive_refresh_key (secret, &iv, &skey);
+
+ return GNUNET_CRYPTO_symmetric_decrypt (input, input_size, &skey, &iv, result);
+}
+
+
+int
+TALER_refresh_encrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ struct GNUNET_CRYPTO_SymmetricSessionKey skey;
+
+ derive_refresh_key (secret, &iv, &skey);
+
+ return GNUNET_CRYPTO_symmetric_encrypt (input, input_size, &skey, &iv, result);
+}
+
+
+void
+TALER_hash_context_start (struct TALER_HashContext *hc)
+{
+ GNUNET_assert (0 == gcry_md_open (&hc->hd, GCRY_MD_SHA512, 0));
+}
+
+
+void
+TALER_hash_context_read (struct TALER_HashContext *hc, void *buf, size_t size)
+{
+ gcry_md_write (hc->hd, buf, size);
+}
+
+
+void
+TALER_hash_context_finish (struct TALER_HashContext *hc,
+ struct GNUNET_HashCode *r_hash)
+{
+ void *res = gcry_md_read (hc->hd, 0);
+ GNUNET_assert (NULL != res);
+ if (NULL != r_hash)
+ memcpy (r_hash, res, sizeof (struct GNUNET_HashCode));
+ gcry_md_close (hc->hd);
+}
+
+
+/* end of util.c */