Keywords in pg_hba.conf should be field-specific
Hi folks,
One of the speedbumps I hit when setting up HS+SR was naming the user
the slave would connect as for streaming replication. At first I
picked 'replication', which seemed quite natural to me (and I don't
doubt will seem natural to others as well).
When I started up the slave, I got this error:
FATAL: could not connect to the primary server: FATAL: no
pg_hba.conf entry for replication connection from host
"192.168.21.10", user "replication", SSL on
Bzzzt. Wrong. There *was* such an entry in pg_hba.conf. I wasted a
lot of time checking my conf files for typos before I wondered whether
there might be something wrong with using 'replication' as a username.
I changed the username to 'streamrep' and it all started working
perfectly.
I understand that 'replication' is a keyword as far as the database
name is concerned, but I was surprised to find that it was treated as
a keyword in the username field also. I had a look in
src/backend/libpq/hba.c, and next_token() appears to be completely
naive about this. 'replication' (along with 'all', 'sameuser',
'samegroup' and 'samerole') is treated as a keyword wherever it
appears, not just in the field where it is relevant. next_token()
appends a newline to the end of the 'replication' username token, and
that's why the entry doesn't match my connection attempt.
I suspect this is going to trip a lot of people up. We could just
document it and tell people that if they want to use 'replication' as
a username, they'd better quote it in pg_hba.conf. But I'd prefer to
actually solve the problem. We could do this by teaching next_token
to be sensitive to which field it is parsing, and limit keyword
detection appropriately. Or, if that's awkward, we could teach
parse_hba_line to ignore keyword markers where the keyword is not
applicable. The more I think about it, the more I incline towards the
latter.
Thoughts?
Cheers,
BJ
On Fri, Oct 8, 2010 at 10:13 PM, Brendan Jurd <direvus@gmail.com> wrote:
One of the speedbumps I hit when setting up HS+SR was naming the user
the slave would connect as for streaming replication. At first I
picked 'replication', which seemed quite natural to me (and I don't
doubt will seem natural to others as well).When I started up the slave, I got this error:
FATAL: could not connect to the primary server: FATAL: no
pg_hba.conf entry for replication connection from host
"192.168.21.10", user "replication", SSL onBzzzt. Wrong. There *was* such an entry in pg_hba.conf. I wasted a
lot of time checking my conf files for typos before I wondered whether
there might be something wrong with using 'replication' as a username.
I changed the username to 'streamrep' and it all started working
perfectly.I understand that 'replication' is a keyword as far as the database
name is concerned, but I was surprised to find that it was treated as
a keyword in the username field also.
Yikes. That does seems surprising.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sat, Oct 9, 2010 at 11:13 AM, Brendan Jurd <direvus@gmail.com> wrote:
I understand that 'replication' is a keyword as far as the database
name is concerned, but I was surprised to find that it was treated as
a keyword in the username field also. I had a look in
src/backend/libpq/hba.c, and next_token() appears to be completely
naive about this. 'replication' (along with 'all', 'sameuser',
'samegroup' and 'samerole') is treated as a keyword wherever it
appears, not just in the field where it is relevant. next_token()
appends a newline to the end of the 'replication' username token, and
that's why the entry doesn't match my connection attempt.I suspect this is going to trip a lot of people up. We could just
document it and tell people that if they want to use 'replication' as
a username, they'd better quote it in pg_hba.conf. But I'd prefer to
actually solve the problem.
Agreed. We should address that.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On 13 October 2010 00:28, Fujii Masao <masao.fujii@gmail.com> wrote:
On Sat, Oct 9, 2010 at 11:13 AM, Brendan Jurd <direvus@gmail.com> wrote:
I understand that 'replication' is a keyword as far as the database
name is concerned, but I was surprised to find that it was treated as
a keyword in the username field also. I had a look in
src/backend/libpq/hba.c, and next_token() appears to be completely
naive about this. 'replication' (along with 'all', 'sameuser',
'samegroup' and 'samerole') is treated as a keyword wherever it
appears, not just in the field where it is relevant. next_token()
appends a newline to the end of the 'replication' username token, and
that's why the entry doesn't match my connection attempt.I suspect this is going to trip a lot of people up. We could just
document it and tell people that if they want to use 'replication' as
a username, they'd better quote it in pg_hba.conf. But I'd prefer to
actually solve the problem.Agreed. We should address that.
Hi folks,
Per the above discussion, I've prepared a patch to make keywords in
pg_hba.conf field-specific.
This patch takes a least-resistance approach to solving the problem.
next_token() continues to blithely append a newline character to any
token which *might* be a keyword, but the patch teaches
hba_parse_line() to be a bit more savvy about whether to take
next_token's word for it.
In the Database field, hba_parse_line() acknowledges 'all',
'sameuser', 'samegroup', 'samerole' and 'replication' as legitimate
keywords. Anything else is considered not a keyword, and if there is
a trailing newline it is stripped from the token (by just reassigning
it to NUL).
Likewise, in the Role field, only 'all' is considered legitimate.
In the Host field, we allow 'samehost' and 'samenet'.
The bottom line is that this makes 'replication' safe to use as a
username, no quoting required.
It seemed a little bit inelegant to allow next_token() to mark up a
keyword, only to ignore that marking later on, but as the number of
fields in each line is not decided until we parse it, there was no
sensible way to teach next_token() which field it was evaluating.
Added to the November CF.
Cheers,
BJ
On 17 October 2010 01:52, Brendan Jurd <direvus@gmail.com> wrote:
Per the above discussion, I've prepared a patch to make keywords in
pg_hba.conf field-specific.
Try New and Improved This Message (tm), now with attachment!
Cheers,
BJ
Attachments:
hba-keywords.difftext/plain; charset=US-ASCII; name=hba-keywords.diffDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 855,861 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
line_num, HbaFileName)));
return false;
}
! parsedline->database = pstrdup(lfirst(line_item));
/* Get the role. */
line_item = lnext(line_item);
--- 855,873 ----
line_num, HbaFileName)));
return false;
}
! token = pstrdup(lfirst(line_item));
!
! /* Filter out inapplicable keywords for the database field. */
! if (token[strlen(token) - 1] == '\n'
! && strcmp(token, "all\n") != 0
! && strcmp(token, "sameuser\n") != 0
! && strcmp(token, "samerole\n") != 0
! && strcmp(token, "samegroup\n") != 0
! && strcmp(token, "replication\n") != 0)
! {
! token[strlen(token) - 1] = '\0';
! }
! parsedline->database = token;
/* Get the role. */
line_item = lnext(line_item);
***************
*** 868,874 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
line_num, HbaFileName)));
return false;
}
! parsedline->role = pstrdup(lfirst(line_item));
if (parsedline->conntype != ctLocal)
{
--- 880,894 ----
line_num, HbaFileName)));
return false;
}
! token = pstrdup(lfirst(line_item));
!
! /* Filter out inapplicable keywords for the role field. */
! if (token[strlen(token) - 1] == '\n'
! && strcmp(token, "all\n") != 0)
! {
! token[strlen(token) - 1] = '\0';
! }
! parsedline->role = token;
if (parsedline->conntype != ctLocal)
{
***************
*** 904,909 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 924,937 ----
/* need a modifiable copy of token */
token = pstrdup(token);
+ /*
+ * Filter out any remaining keywords, as the only valid keywords
+ * for this context ('samehost' and 'samenet') have already been
+ * handled above.
+ */
+ if (token[strlen(token) - 1] = '\n')
+ token[strlen(token) - 1] = '\0';
+
/* Check if it has a CIDR suffix and if so isolate it */
cidr_slash = strchr(token, '/');
if (cidr_slash)
Excerpts from Brendan Jurd's message of sáb oct 16 11:53:31 -0300 2010:
On 17 October 2010 01:52, Brendan Jurd <direvus@gmail.com> wrote:
Per the above discussion, I've prepared a patch to make keywords in
pg_hba.conf field-specific.Try New and Improved This Message (tm), now with attachment!
Hmm. Would it be possible to list keywords _applicable_ to each field,
and have these passed down to next_token by the caller instead? This
seems backwards, but I'm not sure if the other way is really workable.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On 17 October 2010 02:27, Alvaro Herrera <alvherre@commandprompt.com> wrote:
Hmm. Would it be possible to list keywords _applicable_ to each field,
and have these passed down to next_token by the caller instead? This
seems backwards, but I'm not sure if the other way is really workable.
Short answer: I don't think it is workable, or I would have done it
that way in the first place.
Full answer: The problem is that pg_hba.conf doesn't have a fixed
structure. Each line can be 4, 5 or 6 fields (not including the final
'options' field) long, and which of these structures apply to any
given line isn't decided until parse_hba_line goes to work on it.
At the time that next_token gets called, we have no way of knowing
which field is currently being tokenised, at least not without doing
some serious rearrangement of hba.c, so that it tokenises and then
parses one token at a time, instead of tokenising the whole file and
then parsing all the lines.
Cheers,
BJ
Alvaro Herrera <alvherre@commandprompt.com> writes:
Excerpts from Brendan Jurd's message of sáb oct 16 11:53:31 -0300 2010:
Try New and Improved This Message (tm), now with attachment!
Hmm. Would it be possible to list keywords _applicable_ to each field,
and have these passed down to next_token by the caller instead?
+1. That newline thing is a crude hack and always has been. If people
are dissatisfied with it, it's time to start over rather than piling
additional crockery on top.
regards, tom lane
Brendan Jurd <direvus@gmail.com> writes:
Full answer: The problem is that pg_hba.conf doesn't have a fixed
structure. Each line can be 4, 5 or 6 fields (not including the final
'options' field) long, and which of these structures apply to any
given line isn't decided until parse_hba_line goes to work on it.
At the time that next_token gets called, we have no way of knowing
which field is currently being tokenised, at least not without doing
some serious rearrangement of hba.c, so that it tokenises and then
parses one token at a time, instead of tokenising the whole file and
then parsing all the lines.
Good point. Maybe the correct fix is to remember whether each token was
quoted or not, so that keyword detection can be done safely after the
initial lexing. I still think that the current method is impossibly
ugly ...
regards, tom lane
On 17 October 2010 09:59, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Brendan Jurd <direvus@gmail.com> writes:
At the time that next_token gets called, we have no way of knowing
which field is currently being tokenised, at least not without doing
some serious rearrangement of hba.c, so that it tokenises and then
parses one token at a time, instead of tokenising the whole file and
then parsing all the lines.Good point. Maybe the correct fix is to remember whether each token was
quoted or not, so that keyword detection can be done safely after the
initial lexing. I still think that the current method is impossibly
ugly ...
I'm happy to revise the patch on that basis. Any suggestions about
how to communicate the 'quotedness' of each token? We could make each
token a struct consisting of the token itself, plus a boolean flag to
indicate whether it had been quoted. Does that work for you?
Cheers,
BJ
Brendan Jurd <direvus@gmail.com> writes:
On 17 October 2010 09:59, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Good point. �Maybe the correct fix is to remember whether each token was
quoted or not, so that keyword detection can be done safely after the
initial lexing. �I still think that the current method is impossibly
ugly ...
I'm happy to revise the patch on that basis. Any suggestions about
how to communicate the 'quotedness' of each token? We could make each
token a struct consisting of the token itself, plus a boolean flag to
indicate whether it had been quoted. Does that work for you?
Seems reasonable. I had the idea of a parallel list of booleans in the
back of my mind, but a list of structs is probably easier to understand,
and to extend further if necessary.
regards, tom lane
On 18 October 2010 01:19, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Brendan Jurd <direvus@gmail.com> writes:
On 17 October 2010 09:59, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Good point. Maybe the correct fix is to remember whether each token was
quoted or not, so that keyword detection can be done safely after the
initial lexing. I still think that the current method is impossibly
ugly ...I'm happy to revise the patch on that basis. Any suggestions about
how to communicate the 'quotedness' of each token? We could make each
token a struct consisting of the token itself, plus a boolean flag to
indicate whether it had been quoted. Does that work for you?Seems reasonable. I had the idea of a parallel list of booleans in the
back of my mind, but a list of structs is probably easier to understand,
and to extend further if necessary.
Okay, I've taken the red pill and I'm finding out how deep the rabbit
hole goes ...
The logical structure of pg_hba.conf is a set of lines, each line
containing a set of fields, each field containing a set of tokens.
The way the existing implementation handles this is to create a list
of lines containing sublists of fields, containing comma-separated
strings for the set of tokens, with newlines embedded next to tokens
which might be keywords.
The tokeniser breaks apart the comma-separated tokens ... and then
reassembles them into a comma-separated string. Which the db/role
matching functions then have to break apart *again*.
In order to keep track of whether each individual token was quoted, I
first need to impose some sanity here. Rather than using a magical
string for each field, I intend to use a List of HbaToken structs
which explicitly note whether quoting was used.
Introducing an extra List level does mean a bit more work copying and
freeing, and it makes the patch really quite intrusive. I have to
touch a lot of lines in hba.c, but I think the additional clarity is
worth it. If nobody dissuades me from this approach I hope to post a
patch in a couple of days.
Cheers,
BJ
Any progress on this?
---------------------------------------------------------------------------
Brendan Jurd wrote:
On 18 October 2010 01:19, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Brendan Jurd <direvus@gmail.com> writes:
On 17 October 2010 09:59, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Good point. ?Maybe the correct fix is to remember whether each token was
quoted or not, so that keyword detection can be done safely after the
initial lexing. ?I still think that the current method is impossibly
ugly ...I'm happy to revise the patch on that basis. ?Any suggestions about
how to communicate the 'quotedness' of each token? ?We could make each
token a struct consisting of the token itself, plus a boolean flag to
indicate whether it had been quoted. ?Does that work for you?Seems reasonable. ?I had the idea of a parallel list of booleans in the
back of my mind, but a list of structs is probably easier to understand,
and to extend further if necessary.Okay, I've taken the red pill and I'm finding out how deep the rabbit
hole goes ...The logical structure of pg_hba.conf is a set of lines, each line
containing a set of fields, each field containing a set of tokens.
The way the existing implementation handles this is to create a list
of lines containing sublists of fields, containing comma-separated
strings for the set of tokens, with newlines embedded next to tokens
which might be keywords.The tokeniser breaks apart the comma-separated tokens ... and then
reassembles them into a comma-separated string. Which the db/role
matching functions then have to break apart *again*.In order to keep track of whether each individual token was quoted, I
first need to impose some sanity here. Rather than using a magical
string for each field, I intend to use a List of HbaToken structs
which explicitly note whether quoting was used.Introducing an extra List level does mean a bit more work copying and
freeing, and it makes the patch really quite intrusive. I have to
touch a lot of lines in hba.c, but I think the additional clarity is
worth it. If nobody dissuades me from this approach I hope to post a
patch in a couple of days.Cheers,
BJ--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
On 26 February 2011 18:06, Bruce Momjian <bruce@momjian.us> wrote:
Any progress on this?
I ended up doing most of the work, but never got around to finishing
it off. Thanks for the reminder, though. I'll get that one ready and
drop it onto the next CF.
Cheers,
BJ
On Sat, Feb 26, 2011 at 10:57, Brendan Jurd <direvus@gmail.com> wrote:
On 26 February 2011 18:06, Bruce Momjian <bruce@momjian.us> wrote:
Any progress on this?
I ended up doing most of the work, but never got around to finishing
it off. Thanks for the reminder, though. I'll get that one ready and
drop it onto the next CF.
Just as a point, which i proably moot if you've done most of the work
already :) But at some point it might be worthwhile to see if our life
would be mad easier by using flex (with or without bison) to parse
pg_hba.conf as wel, since we do it for postgresql.conf and other
things. If it makes maintenance easier, it's probably worth it.
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
Brendan Jurd wrote:
On 26 February 2011 18:06, Bruce Momjian <bruce@momjian.us> wrote:
Any progress on this?
I ended up doing most of the work, but never got around to finishing
it off. Thanks for the reminder, though. I'll get that one ready and
drop it onto the next CF.
Added to TODO:
Have pg_hba.conf consider "replication" special only in the database
field
* http://archives.postgresql.org/pgsql-hackers/2010-10/msg00632.php
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
On 29 October 2010 09:59, Brendan Jurd <direvus@gmail.com> wrote:
On 18 October 2010 01:19, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Brendan Jurd <direvus@gmail.com> writes:
On 17 October 2010 09:59, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Good point. Maybe the correct fix is to remember whether each token was
quoted or not, so that keyword detection can be done safely after the
initial lexing. I still think that the current method is impossibly
ugly ...I'm happy to revise the patch on that basis. Any suggestions about
how to communicate the 'quotedness' of each token? We could make each
token a struct consisting of the token itself, plus a boolean flag to
indicate whether it had been quoted. Does that work for you?Seems reasonable. I had the idea of a parallel list of booleans in the
back of my mind, but a list of structs is probably easier to understand,
and to extend further if necessary.Okay, I've taken the red pill and I'm finding out how deep the rabbit
hole goes ...The logical structure of pg_hba.conf is a set of lines, each line
containing a set of fields, each field containing a set of tokens.
The way the existing implementation handles this is to create a list
of lines containing sublists of fields, containing comma-separated
strings for the set of tokens, with newlines embedded next to tokens
which might be keywords.The tokeniser breaks apart the comma-separated tokens ... and then
reassembles them into a comma-separated string. Which the db/role
matching functions then have to break apart *again*.In order to keep track of whether each individual token was quoted, I
first need to impose some sanity here. Rather than using a magical
string for each field, I intend to use a List of HbaToken structs
which explicitly note whether quoting was used.Introducing an extra List level does mean a bit more work copying and
freeing, and it makes the patch really quite intrusive. I have to
touch a lot of lines in hba.c, but I think the additional clarity is
worth it. If nobody dissuades me from this approach I hope to post a
patch in a couple of days.
Hi folks,
I've finally managed to get a patch together for the above. It ended
up being a much more difficult project than I anticipated.
Just to recap, the HEAD code in hba.c parses pg_hba.conf (and
ident.conf) into a List of lines, each line being a List of strings.
These strings have various magical properties, including commas to
separate individual tokens and newlines to mark up keywords.
I have replaced this behaviour with something which more closely
represents the true logical structure of the file, and is (hopefully)
much more intelligible. The patch introduces the HbaToken struct,
which consists of a string and a boolean to indicate whether the
string was parsed inside quotes. The parser now produces a
triple-nested list -- a List of lines, each line being a List of
fields, each field being a List of tokens.
It sounds straightforward enough, but to make this happen I had to
change a very substantial fraction of pg_hba.c.
I have tested a few basic pg_hba.conf setups and everything seems to
be working as intended. There is much more testing to be done, it
probably leaks memory and there is a lot more room for cleanup in
hba.c, but I would like to open the patch up for review and comment as
a WIP.
Posting it to the 2011-Next CF.
Cheers,
BJ
Attachments:
hba-keywords.diff.bz2application/x-bzip2; name=hba-keywords.diff.bz2Download
BZh91AY&SY��� #�_�8{�����������`:�� �{�������x
����+���v���B��(��g����B@� ��zy;��w[7��hSB(�k(��:G���(k�����i��Z�a;2(���npPm���4 ���R:��a�.��)��h�D����
��Q���(�� h �)�� � �����S�4�Di��
M z� zA)�*jz���hh Mh� BRDE<*~���i�Q����z4�@
P )RhL =F��F��S��b2� 4i�
�! ��)�a4��7�A=M'�=&��h6��;7C�X10���J�� ��� �%D��&P�R��)J�$�"}�LD��u �������%�$0�MQG��F!�%)d�`)�
I�b��%&I�4D@DIMCS3��a�(!�b"�a�;�C|�2UO���{�y�/>��<��f"\Hf `��i|�~.#�$� �������7�v��
TQ���!�i!jj)�
!d������i�A;�d�"����`d����e�&�HB$$ ����b ��ib"X�������Ia��dH � @�$`�)RF�E�����T�Y�?��
��C��n��DC���U�l��x�p��+g��Uw�����$Ct�&F����`r����k�b0 L����L���B�
�3���*�Nl���������nZ�73X�E{� �B0#%�!�1�"$[���eOe���30��E�����b��������?n'c82`�D���U�b?��|��N�9��8�m����G��[!�h�8�[3����=&]��w��`���������3����L2$��?�����o��������N�C.�<��T" ����k������q3�[�:�DEc��1�N�IK���G;v��7r���2=�����;tkFM�h����LM'�;�������[r�kN��yz~/?�'�����5}Ks$����4\S��D9��,���U�h�.�=��Z�;�:����/XS�xg��,���*]��0K^�=�u���\&G#�����xC2���n�J��u�6��
��N��l��ABfHH�BM�c�q`�sH1�-:[6��37I.� �Sf7�����G'[�U�p��H����j!3$%�
w�3
�����YCN��<�)x�J���fL��9�6�(&�R:� �0�N��l�?XY����7g��Ic �����pq��N�n��+U��9�pp�<��xcg����8 �p�p�C�`��'AM�C<a��;Xk �5Y&�XR��a�`�����<@�n<��(��< �
�1�bx&k8C�������`5��`���01�e��Le�Q@�U��r� � ������G#��K�2��#��4Ss-(��b �
R�"b�p%�9-Ip�1+��t�T�J(����p���@/��������PQ$|i�]L�7O$�����G��uJ[���:"Jc)�5��IU��L,A�G�����:!��^m;6?
�,-MXS�,��������a�K�����I?�R�����T�3a+2x�,0�}G�>������:ii��J����~g��'�������k UEj�z�~������{��T�����o�d>�����%�`�IUf�k���k��=�+�l^Ex�7�X�Z��GB����pp�r�9NK��N����d/���'*w E����xR�
7BR��|�5��a��SI���=��<�aQ�m��>�n�����+66X$�������iiI�6������j�e�%(rk�\q�-���Y�?��eX����c��>���"��b"q���Q3PUUU}�z<�[������|�n��7si�O�����Y��pQ��v$��Bf��`@W8f���c_r����������X��3�����:����9��s���;n����,�*�U���/$I�1c�b����&��5��II�eOh�0vdNC33A������L��[#�6 ]����ik|
'v�_��X��w��i(�������gO�g�bc�y�����B�� ���7����jI=N�r�9��X���QJ��M'����;��hrA�I��<�CA��i�:G�tv�c���Xx��}�X�8�Q� ����[mbD���A���sc�
������;��0fX�+��A�4Z������(�1���������ad�b�C��i��m;��������k����������z7}gfa�x�oCn�.������C�lAA=��G&NG�$�����>2+��Ol�G���w�`�|vTE+��0Yi;����%d���w�����ynO���mR�Vo��#�q�`�^�<.� Fo��\�8�($�c���u"q���r�B�����6�r�A��41q�����X;��b����7v)Q��xZ�.(�R'��V"�c�*��*�D=X����������:y��.0�sJ��DA��I|�������+�8�=96�(G���V���!����k���Ql�A����a�����q������0u �
�Y�,'��35�vsAL�\�Et��M�������''S��G:$�7HE���2X��Y�2TQ�������I����O�3��s"����.p����:�)�L�a�LA�x���0t��'��I���
��"1#�4�_!��T�[�v���S3kN�!6y�T�2�?���H��C�t���������Xq#A�/[E�I
� �LM��yaJ����\r���H�)�A�����f[Y�&HDL51�n�9��=��{�����V^d�.L���G�a�������x|�oQ+�����Iqt���7 ��%�"�I(�)�I�A�E�I��������%������Nz��;�����;D��������Q�z�7���r4d�V�.�����9�E���@���I�$���F}���a�g�ad@n��dC�I���H����������Xu�"����{~���b�S�����Z+�1 ;/O�1���"�Az��6�g�P&��$�� ?�����c>�6��;��c$e�&o���"E��F��F�5~6���LD�����^ ��t���vHH@����=���3���kM��<B��2d2L�
�����UUU�����a�0��D!�"�Y(R� ���I?hRUUV�<��=���{�i9\I�Ez�5�T�W�;
�Y?��dd�F��O���
6i����$v�_���7����i����:5~*W�V�S�F����e����j��j�)R�8�F
v?\�����j4f�n���#P�+�u�@��b��!e�+6�0W���`@�{!����Km���<_796;��a����� BI"R�m� *��[l������i8�L�
�:d�C�$�'88����:����[-�^;;�M��q��<8���R����Kc=���7&�M�((|�������\,@#���w�q������I\� ���m�2���~� 7L�XE�v�����������qE[d��G���t������N�.feg,\����^}9��pw*O{��H�m��BBLn�"`��G� 12H���@�q�HKF�! K��I��w(�,p-i8�'�I���M�3t��
��<�L.��g�y������`�m�3�����YC�f�R���=�h���;F�r�l+pC��m�K�Xv0�
prln�����GT�a����;$��l�N3kbf-5��c����i0�3U�8���!�����!�"��2L���$0�PA���0�!`��)l}�#"��@���6i�F�Y kl�M�6�y$
��a����������t�<����g&+4�G�C^��&��6��7W��M�{fq���#{����2���1�k��}/��|q��j"���9������clm���6��������+I+���clW�($(Z�����<��{�����H��o67q�8u~������d�/y�d6��M�E�����.�;��k�n�����6�6N�- 6���5�<�nM�$��� �s74�ei�8�HW�T���*jX�c�=������������zm��zd����K�f1O����]��r��x�����X0}#���%M
�(`C�y�����G,�}T�~���)�{Tkv����13�� �{� ���NY�����Y�������x���D�q2�C3����k� ������0������+X@�`��F�}�K�8���v���,�0r;0
��Ek8����P��h��?JYo����@�`���fs��}�HI
�D$���V��6�$����F���iC����������/���i���z�c�d�K+����F#z�����)ff��j���B����'u����[Y�0l8��V8���m���\f�r���GG&]Z���~�Gnkz��Q)h���ru���w�f����� |TFs����N���'�m9���������������<���im���yP���x��g�b��$����h�g������?K� ��������b��yi������!�����z$9q��3�`�&9IZUmBm���{��hnG�1����W.k�o6���HB�5�* B�b�A)< �#��<f�l.w��]fH�KO���=�j�+7l�oF*���N�Gq��X�����t� ��"�� �� !P��!�l�6P��4!� �7
dj��!�@������s2�2a�`t��W��`�J�5!%�����o�`0L)�Q0��!�`�I� T�P=�<���UJ��Y$��I���1��g�5���?�Y�����h���`�7���0�����?P����]S�S��ejN�����/��M�J�40?+*~�c[�����lf�f�������5�8B�oX696�Nm�J�7��S�k6:���
�����,�?�P�X����Mm�����|�n�=��?MZ�J��b�&��^�W�OV�<���������6���Y�0i�����K���'z�I_1�sS���eM���>����\�Y ����@����7��|���g>�0a�����i,���t��!sh�=UMOUS��m����O�����lL6�[�����:D�h6�4�hh���OB����UUUUUUU� �[�MKk�Xx�2���N�t���l��?��'�/�����G1<�|�v�C����-~�?�$�������&��3�����c�1�����;����uV�Z��O�=���0�S��'��%�*�Y��de<�3G��
)����/K�<��d?�������|�J�G�+RsL9���+��9&���H���|���u�R�c$���1�gEL����!�X83�z��]�S���?��*���j����u<��t:�8?qR��6L�<�J���]�M>�q��Y�V���SM%*�c���@�"J01@1�
j#�����g3�zI�Q:��p�l�ei�����`��,p��Cu;����w�a���cS|������k0n��%��b�B�Y���{t:�s�vJ�"�������-��F!H�b?��������X���v�d>��������>�T*��r[���A3�:��������
����5[�y*����:��9�IZ����Id:7Y�����WS]�&�����S��:;&��|�/]��l-n�}�v���4>����`XU��&Q=��XL�����;KYf��=�S�9�<���jbk����C��LzDk�U��`UFB�>��lF�O)2h9S������!1�V�wB}�����+���[7jx��U�qj�v/��{0���*��R��I��5�:�r����;�����h��x�x*�V;�aS��N�By�L�G&&��+�Z�#6rx�������� >����S�:�8L�����<
�K��������*����i������<��^��u8t���u�"W��i����urrk������;����%M\�USv�&����?����E���rm
�=��
��h�\VbD�Sr�k��RVCU����z�m��8��NNQ�h#O��+��+���NN�;�A=��WZ��G�W���LZ���m#��"L��j���}����&��8��tZ���������?v��mp��&f�l]4����5y�yJ�V�,V�i���b6��^<������#p��D`�l@�Qb��INjh;�C��,��uC3��5�0������&���L��:h$C`Z9-{�����s�L�7\��{`�llQ,��F�j�������nA�A���\�(����X��z�33^J��f9��<����4�[Ud�%���S\�1��}���n�mtn�� �3;����*�1I���b��Y ������p� ��5�,���w��>�s�O$$#�
<p��b���W\���YE,-[\;��jI�I�F���
����l�����{8��zz��#�rd�z��i���;�,��;����vi����G��.y�{2�2���\$HB��<MI��$��/��+���6sa��-�$��'�'�1�X/����tP���rt�N^�5���w�F�Q����z����'e��"U_��MI��*��l��<�f���H�$r}
�0��skJ1V�DTM����&8rW�������N�o�����\��q�A�����.Z��$� B%Q�Sg6*V��t��4�} =��alc��9sT9='�I#�$�:������$�~���d��N��g�qs�S��f�������5*r��\\����sg�rp�������'������Z��bZ���rN���}�&�I�$��#9��f)�������0�,�W��dP|_h� �b����E���W�����������*;�'a�C��p6I���~�N�s���Z�'�Db�\���9�N���5.�F���el�+M��������!����m��dh��l�����]R�G��[m�����yh�8���S8����/"���*�t����x�����w��G)���]��'��dvu�}�����U$��1A0��
�D�$��<T�'�I'�<Kj������D�V"�[�?t����%����V�T4<����I�:NxX������V/�������zw+������=M����4ZR�k2�,K3-��a�h?���D�Sg��z��������,x�#�s�����|y�}�D���D'h ���R�B���m��ir��8n�:�10�WJ�1�;��z���(�Sh>i���VZ{�����i�a�����A. �1�D�A�#�q�0i�pg�����) � ���bGD�|�'�Z����������ru������]�b�����C���5'�{O����\�KD��������������ET���+#��>���'E����>�r�o�g�|��
�DEUUP��,C@���%v^��D/�'�6�:N��B��Z�6:�^XLjR��+�-n��D���=�����4������t�����q� �v_���L�#.�My���-D������y�Tt&��r_�sx��f$���U*��R�T�R�4�YRX�"������cM��b�A$R�3p��Z����r�x�����2p�y��,G�EU_��'�G�6����)���ei4+���y��������S52-U�T%UUDUTTQI`����S�x!���&�>�~�U��]�|H#���� T�Y�n�E]�[��������^?�j�*�K��ya�Oi%�
���H����;�{���9<Y�K��W��J�Uqrq�hA��9'���'�e2(a���+�>��7�����$�L�
+����������Ui���h��stHG�r5:�scS�R�$N!R����Q����o:�/��hfqX�[F��f�K]�& �K�`7J���gD����N�J�r(��DBp��8����#�����:8�L��#��)����M���$2�M�i�%�bY'9�1�H��x�+%i����0����������S��rQ�I$�u�%�i*��bU�������B]�j�����R���OS�|�{���>�����
FF�b������'9 ;����
��m.7X����<�Bq:!�y,�b-��c� l$���Qd����(��U"m�a6�6�"J�1**��%N�5l�LVDiY�������>%jV%k�$���O��z��L0b�5/l���d7b���W6�~Xw������JB�J�)=��8�|�#9�J�k2��Vs >�s�b���ad�=���h�%����������o���������sv����I�1x�������E��6V25����pz��a?�E�R4y)l%�3>����l���&8�{�7�I���I����)B=dPP��� �t�
T�If6UxM�l����b4�G!�����?��y���/�{f�XB�/�-�{H��d/�sl�i6��d�d��������
������b����/���{�c����0�U\���OG���Q[>�$��{m��j�$�OF�����<��a�}��rQ<aE=�Z�;�|���zu�9����,I���+�,�*�|V���L7FLAm�"@�����bX�A�l��bz.����,�#P�=���)T�lp:I����$?�?�*�R}&��%�I�S!3��F�CKF���J���F�wo� OzO����7�w��|v���������"�7y'����L5�F����#i�I��V����)SQ���,�}lM�����J�O ��I��"�a�i
���G'������}�V��k}'���oY2:��=�cgB#��D������6)O���U�E�J��10�I��m�6o�L��~��7#���~�l�0�)O���a�&�K���.IS`��m�3Q�N��V��8k\'%8�1#����r���� �4���.8��z�$Hbf�%Z�T�BS��4C8��)<&�6Cx�8%,=C���U�].�������tO�������"9�G����nIi&�a�O�����U�ks7.�O���8�fe�^I�;#smB49��7V2c���^M�hrGQ����
n��1���=��>�t{-UM'�U5���qU��J�F��W�51M"a�3�}���1���I��y�I:�������S���0_���q�|���4A@D�D���<�X%X� ��w5!�3�x)<I��N���
�I�������?{�Hh���VD�����{��U+�>�9����l��O���������J ���5���
�d��]����N�p�q6k��61�d��z�.�89y���$�%wa�Ei,N��I��r8��j��7Y�*����rf�*�cNI�c� p�����L[w872A���&���W��#i�fCh�jF$���54,:$��MG������Hd��U�U6i�Y�)��6�x$,y�nNi���"�6I��>�l��$�$%C���f��Ib"�Ci��>�a���=�H$�p�K��]��l+�w�E=�Rt���}������OW��#������|��2�G����oYJ�:��Y�E�4@1gg]�����dh��x����~vp����+323}>>�M7+v�Hl� �$P}o7�����f����%Yp���>�N �#����?%M�X��U
UT����>�Z�ble,��A���g�5(�&��������+�������9��.$�Q�2bv3dI�.�p�%{+����jc
c��1�0��{�q�=�W
V'rI�����Yo���o������q]^p�=��R{��x���h��69/���$�,�2��6�[]����0�S]�\-b�\<�^K�@@�
$�qi��Ab������Ks�Z\R��37FLE�E:Q����L��2�,X�a�J��!�9P����)���ud�X