����JFIFXX�����    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222����"��4�� ���,�PG"Z_�4�˷����kjز�Z�,F+��_z�,�© �����zh6�٨�ic�fu���#ډb���_�N�?��wQ���5-�~�I���8����TK<5o�Iv-�����k�_U_�����~b�M��d����Ӝ�U�Hh��?]��E�w��Q���k�{��_}qFW7HTՑ��Y��F�?_�'ϔ��_�Ջt��=||I ��6�έ"�����D���/[�k�9���Y�8ds|\���Ҿp6�Ҵ���]��.����6�z<�v��@]�i%��$j��~�g��J>��no����pM[me�i$[����s�o�ᘨ�˸ nɜG-�ĨU�ycP�3.DB�li�;��hj���x7Z^�N�h������N3u{�:j�x�힞��#M&��jL P@_���� P��&��o8������9�����@Sz6�t7#O�ߋ �s}Yf�T���lmr����Z)'N��k�۞p����w\�Tȯ?�8`�O��i{wﭹW�[�r�� ��Q4F�׊���3m&L�=��h3����z~��#�\�l :�F,j@�� ʱ�wQT����8�"kJO���6�֚l����}���R�>ډK���]��y����&����p�}b��;N�1�m�r$�|��7�>e�@B�TM*-iH��g�D�)� E�m�|�ؘbҗ�a��Ҿ����t4���o���G��*oCN�rP���Q��@z,|?W[0�����:�n,jWiE��W��$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S����VN;�}�s?.����� w�9��˟<���Mq4�Wv'��{)0�1mB��V����W[�����8�/<� �%���wT^�5���b��)iM� pg�N�&ݝ��VO~�q���u���9� ����!��J27����$O-���! �:�%H��� ـ����y�ΠM=t{!S�� oK8������t<����è:a������[�����ա�H���~��w��Qz`�po�^ ����Q��n� �,uu�C�$ ^���,������8�#��:�6��e�|~���!�3�3.�\0��q��o�4`.|� ����y�Q�`~;�d�ׯ,��O�Zw�������`73�v�܋�<���Ȏ�� ـ4k��5�K�a�u�=9Yd��$>x�A�&�� j0� ���vF��� Y�|�y��� ~�6�@c��1vOp�Ig����4��l�OD���L����� R���c���j�_�uX6��3?nk��Wy�f;^*B� ��@�~a�`��Eu������+���6�L��.ü>��}y���}_�O�6�͐�:�YrG�X��kG�����l^w���~㒶sy��Iu�!� W ��X��N�7BV��O��!X�2����wvG�R�f�T#�����t�/?���%8�^�W�aT��G�cL�M���I��(J����1~�8�?aT ���]����AS�E��(��*E}� 2��#I/�׍qz��^t�̔���b�Yz4x���t�){ OH��+(E��A&�N�������XT��o��"�XC��'���)}�J�z�p� ��~5�}�^����+�6����w��c��Q�|Lp�d�H��}�(�.|����k��c4^�"�����Z?ȕ ��a<�L�!039C� �Eu�C�F�Ew�ç ;�n?�*o���B�8�bʝ���'#Rqf���M}7����]����s2tcS{�\icTx;�\��7K���P���ʇ Z O-��~��c>"��?�������P��E��O�8��@�8��G��Q�g�a�Վ���󁶠�䧘��_%#r�>�1�z�a��eb��qcPѵ��n���#L��� =��׀t� L�7�`��V���A{�C:�g���e@�w1 Xp3�c3�ġ����p��M"'-�@n4���fG��B3�DJ�8[Jo�ߐ���gK)ƛ��$���� ���8�3�����+���� �����6�ʻ���� ���S�kI�*KZlT _`���?��K����QK�d����B`�s}�>���`��*�>��,*@J�d�oF*����弝��O}�k��s��]��y�ߘ��c1G�V���<=�7��7����6�q�PT��tXԀ�!9*4�4Tހ3XΛex�46���Y��D ����� �BdemDa����\�_l,��G�/���֌7���Y�](�xTt^%�GE�����4�}bT���ڹ�����;Y)���B�Q��u��>J/J �⮶.�XԄ��j�ݳ�+E��d ��r�5�_D�1 ��o�� �B�x�΢�#���<��W�����8���R6�@g�M�.��� dr�D��>(otU��@x=��~v���2� ӣ�d�oBd��3�eO�6�㣷�����ݜ6��6Y��Qz`��S��{���\P�~z m5{J/L��1������<�e�ͅPu�b�]�ϔ���'������f�b� Zpw��c`"��i���BD@:)ִ�:�]��hv�E�w���T�l��P���"Ju�}��وV J��G6��. J/�Qgl߭�e�����@�z�Zev2u�)]կ�����7x���s�M�-<ɯ�c��r�v�����@��$�ޮ}lk���a���'����>x��O\�ZFu>�����ck#��&:��`�$�ai�>2Δ����l���oF[h��lE�ܺ�Πk:)���`�� $[6�����9�����kOw�\|���8}������ބ:��񶐕��I�A1/�=�2[�,�!��.}gN#�u����b��� ~��݊��}34q����d�E��Lc��$��"�[q�U�硬g^��%B �z���r�pJ�ru%v\h1Y�ne`ǥ:g���pQM~�^�Xi� ��`S�:V29.�P���V�?B�k�� AEvw%�_�9C�Q����wKekPؠ�\�;Io d�{ ߞo�c1eP����\� `����E=���@K<�Y���eڼ�J���w����{av�F�'�M�@/J��+9p���|]�����Iw &`��8���&M�hg��[�{��Xj��%��Ӓ�$��(����ʹN���<>�I���RY���K2�NPlL�ɀ)��&e����B+ь����( � �JTx���_?EZ� }@ 6�U���뙢ط�z��dWI�n` D����噥�[��uV��"�G&Ú����2g�}&m��?ċ�"����Om#��������� ��{�ON��"S�X��Ne��ysQ���@Fn��Vg���dX�~nj�]J�<�K]:��FW��b�������62�=��5f����JKw��bf�X�55��~J �%^����:�-�QIE��P��v�nZum� z � ~ə ���� ���ة����;�f��\v���g�8�1��f24;�V���ǔ�)����9���1\��c��v�/'Ƞ�w�������$�4�R-��t���� e�6�/�ġ �̕Ecy�J���u�B���<�W�ַ~�w[B1L۲�-JS΂�{���΃������A��20�c#��@ 0!1@AP"#2Q`$3V�%45a6�FRUq��� ����^7ׅ,$n�������+��F�`��2X'��0vM��p�L=������5��8������u�p~���.�`r�����\���O��,ư�0oS ��_�M�����l���4�kv\JSd���x���SW�<��Ae�IX����������$I���w�:S���y���›R��9�Q[���,�5�;�@]�%���u�@ *ro�lbI �� ��+���%m:�͇ZV�����u�̉����θau<�fc�.����{�4Ա� �Q����*�Sm��8\ujqs]{kN���)qO�y�_*dJ�b�7���yQqI&9�ԌK!�M}�R�;������S�T���1���i[U�ɵz�]��U)V�S6���3$K{�ߊ<�(� E]Զ[ǼENg�����'�\?#)Dkf��J���o��v���'�%ƞ�&K�u�!��b�35LX�Ϸ��63$K�a�;�9>,R��W��3�3� d�JeTYE.Mϧ��-�o�j3+y��y^�c�������VO�9NV\nd�1 ��!͕_)a�v;����թ�M�lWR1��)El��P;��yوÏ�u 3�k�5Pr6<�⒲l�!˞*��u־�n�!�l:����UNW ��%��Chx8vL'��X�@��*��)���̮��ˍ��� ���D-M�+J�U�kvK����+�x8��cY������?�Ԡ��~3mo��|�u@[XeY�C�\Kp�x8�oC�C�&����N�~3-H���� ��MX�s�u<`���~"WL��$8ξ��3���a�)|:@�m�\���^�`�@ҷ)�5p+��6���p�%i)P M���ngc�����#0Aruz���RL+xSS?���ʮ}()#�t��mˇ!��0}}y����<�e� �-ή�Ԩ��X������ MF���ԙ~l L.3���}�V뽺�v�����멬��Nl�)�2����^�Iq��a��M��qG��T�����c3#������3U�Ǎ���}��לS�|qa��ڃ�+���-��2�f����/��bz��ڐ�� �ݼ[2�ç����k�X�2�* �Z�d���J�G����M*9W���s{��w���T��x��y,�in�O�v��]���n����P�$�JB@=4�OTI�n��e�22a\����q�d���%�$��(���:���: /*�K[PR�fr\nڙdN���F�n�$�4�[�� U�zƶ����� �mʋ���,�ao�u 3�z� �x��Kn����\[��VFmbE;�_U��&V�Gg�]L�۪&#n%�$ɯ�dG���D�TI=�%+AB�Ru#��b4�1�»x�cs�YzڙJG��f��Il��d�eF'T� iA��T���uC�$����Y��H?����[!G`}���ͪ� �纤Hv\������j�Ex�K���!���OiƸ�Yj�+u-<���'q����uN�*�r\��+�]���<�wOZ.fp�ێ��,-*)V?j-kÊ#�`�r��dV����(�ݽBk�����G�ƛk�QmUڗe��Z���f}|����8�8��a���i��3'J�����~G_�^���d�8w������ R�`(�~�.��u���l�s+g�bv���W���lGc}��u���afE~1�Ue������Z�0�8�=e�� f@/�jqEKQQ�J��oN��J���W5~M>$6�Lt�;$ʳ{���^��6�{����v6���ķܰg�V�cnn �~z�x�«�,2�u�?cE+Ș�H؎�%�Za�)���X>uW�Tz�Nyo����s���FQƤ��$��*�&�LLXL)�1�" L��eO��ɟ�9=���:t��Z���c��Ž���Y?�ӭV�wv�~,Y��r�ۗ�|�y��GaF�����C�����.�+� ���v1���fήJ�����]�S��T��B��n5sW}y�$��~z�'�c ��8 ��� ,! �p��VN�S��N�N�q��y8z˱�A��4��*��'������2n<�s���^ǧ˭P�Jޮɏ�U�G�L�J�*#��<�V��t7�8����TĜ>��i}K%,���)[��z�21z ?�N�i�n1?T�I�R#��m-�����������������1����lA�`��fT5+��ܐ�c�q՝��ʐ��,���3�f2U�եmab��#ŠdQ�y>\��)�SLY����w#��.���ʑ�f��� ,"+�w�~�N�'�c�O�3F�������N<���)j��&��,-� �љ���֊�_�zS���TǦ����w�>��?�������n��U仆�V���e�����0���$�C�d���rP �m�׈e�Xm�Vu� �L��.�bֹ��� �[Դaզ���*��\y�8�Է:�Ez\�0�Kq�C b��̘��cө���Q��=0Y��s�N��S.���3.���O�o:���#���v7�[#߫ ��5�܎�L���Er4���9n��COWlG�^��0k�%<���ZB���aB_���������'=��{i�v�l�$�uC���mƎҝ{�c㱼�y]���W�i ��ߧc��m�H� m�"�"�����;Y�ߝ�Z�Ǔ�����:S#��|}�y�,/k�Ld� TA�(�AI$+I3��;Y*���Z��}|��ӧO��d�v��..#:n��f>�>���ȶI�TX��� 8��y����"d�R�|�)0���=���n4��6ⲑ�+��r<�O�܂~zh�z����7ܓ�HH�Ga롏���nCo�>������a ���~]���R���̲c?�6(�q�;5%� |�uj�~z8R=X��I�V=�|{v�Gj\gc��q����z�؋%M�ߍ����1y��#��@f^���^�>N�����#x#۹��6�Y~�?�dfPO��{��P�4��V��u1E1J �*|���%���JN��`eWu�zk M6���q t[�� ��g�G���v��WIG��u_ft����5�j�"�Y�:T��ɐ���*�;� e5���4����q$C��2d�}���� _S�L#m�Yp��O�.�C�;��c����Hi#֩%+) �Ӎ��ƲV���SYź��g |���tj��3�8���r|���V��1#;.SQ�A[���S������#���`n�+���$��$I �P\[�@�s��(�ED�z���P��])8�G#��0B��[ى��X�II�q<��9�~[Z멜�Z�⊔IWU&A>�P~�#��dp<�?����7���c��'~���5 ��+$���lx@�M�dm��n<=e�dyX��?{�|Aef ,|n3�<~z�ƃ�uۧ�����P��Y,�ӥQ�*g�#먙R�\���;T��i,��[9Qi歉����c>]9�� ��"�c��P�� �Md?٥��If�ت�u��k��/����F��9�c*9��Ǎ:�ØF���z�n*�@|I�ށ9����N3{'��[�'ͬ�Ҳ4��#}��!�V� Fu��,�,mTIk���v C�7v���B�6k�T9��1�*l� '~��ƞF��lU��'�M ����][ΩũJ_�{�i�I�n��$���L�� j��O�dx�����kza۪��#�E��Cl����x˘�o�����V���ɞ�ljr��)�/,�߬h�L��#��^��L�ф�,íMƁe�̩�NB�L�����iL����q�}��(��q��6IçJ$�W�E$��:������=#����(�K�B����zђ <��K(�N�۫K�w��^O{!����)�H���>x�������lx�?>Պ�+�>�W���,Ly!_�D���Ō�l���Q�!�[ �S����J��1��Ɛ�Y}��b,+�Lo�x�ɓ)����=�y�oh�@�꥟/��I��ѭ=��P�y9��� �ۍYӘ�e+�p�Jnϱ?V\SO%�(�t� ���=?MR�[Ș�����d�/ ��n�l��B�7j� ��!�;ӥ�/�[-���A�>�dN�sLj ��,ɪv��=1c�.SQ�O3�U���ƀ�ܽ�E����������̻��9G�ϷD�7(�}��Ävӌ\�y�_0[w ���<΍>����a_��[0+�L��F.�޺��f�>oN�T����q;���y\��bՃ��y�jH�<|q-eɏ�_?_9+P���Hp$�����[ux�K w�Mw��N�ی'$Y2�=��q���KB��P��~������Yul:�[<����F1�2�O���5=d����]Y�sw:���Ϯ���E��j,_Q��X��z`H1,#II ��d�wr��P˂@�ZJV����y$�\y�{}��^~���[:N����ߌ�U�������O��d�����ؾe��${p>G��3c���Ė�lʌ�� ת��[��`ϱ�-W����dg�I��ig2��� ��}s ��ؤ(%#sS@���~���3�X�nRG�~\jc3�v��ӍL��M[JB�T��s3}��j�Nʖ��W����;7��ç?=X�F=-�=����q�ߚ���#���='�c��7���ڑW�I(O+=:uxq�������������e2�zi+�kuG�R��������0�&e�n���iT^J����~\jy���p'dtG��s����O��3����9* �b#Ɋ�� p������[Bws�T�>d4�ۧs���nv�n���U���_�~,�v����ƜJ1��s�� �QIz��)�(lv8M���U=�;����56��G���s#�K���MP�=��LvyGd��}�VwWBF�'�à �?MH�U�g2�� ����!�p�7Q��j��ڴ����=��j�u��� Jn�A s���uM������e��Ɔ�Ҕ�!)'��8Ϣ�ٔ��ޝ(��Vp���צ֖d=�IC�J�Ǡ{q������kԭ�߸���i��@K����u�|�p=..�*+����x�����z[Aqġ#s2a�Ɗ���RR�)*HRsi�~�a &f��M��P����-K�L@��Z��Xy�'x�{}��Zm+���:�)�) IJ�-i�u���� ���ܒH��'�L(7�y�GӜq���� j��� 6ߌg1�g�o���,kر���tY�?W,���p���e���f�OQS��!K�۟cҒA�|ս�j�>��=⬒��˧L[�� �߿2JaB~R��u�:��Q�] �0H~���]�7��Ƽ�I���(}��cq '�ήET���q�?f�ab���ӥvr� �)o��-Q��_'����ᴎo��K������;��V���o��%���~OK ����*��b�f:���-ťIR��`B�5!RB@���ï�� �u �̯e\�_U�_������� g�ES��3�������QT��a����x����U<~�c?�*�#]�MW,[8O�a�x��]�1bC|踤�P��lw5V%�)�{t�<��d��5���0i�XSU��m:��Z�┵�i�"��1�^B�-��P�hJ��&)O��*�D��c�W��vM��)����}���P��ܗ-q����\mmζZ-l@�}��a��E�6��F�@��&Sg@���ݚ�M����� ȹ 4����#p�\H����dYDo�H���"��\��..R�B�H�z_�/5˘����6��KhJR��P�mƶi�m���3�,#c�co��q�a)*Pt����R�m�k�7x�D�E�\Y�閣_X�<���~�)���c[[�BP����6�Yq���S��0����%_����;��Àv�~�| VS؇ ��'O0��F0��\���U�-�d@�����7�SJ*z��3n��y��P����O���������m�~�P�3|Y��ʉr#�C�<�G~�.,! ���bqx���h~0=��!ǫ�jy����l�O,�[B��~��|9��ٱ����Xly�#�i�B��g%�S��������tˋ���e���ې��\[d�t)��.+u�|1 ������#�~Oj����hS�%��i.�~X���I�H�m��0n���c�1uE�q��cF�RF�o���7� �O�ꮧ� ���ۛ{��ʛi5�rw?׌#Qn�TW��~?y$��m\�\o����%W� ?=>S�N@�� �Ʈ���R����N�)�r"C�:��:����� �����#��qb��Y�. �6[��2K����2u�Ǧ�HYR��Q�MV��� �G�$��Q+.>�����nNH��q�^��� ����q��mM��V��D�+�-�#*�U�̒ ���p욳��u:�������IB���m���PV@O���r[b= �� ��1U�E��_Nm�yKbN�O���U�}�the�`�|6֮P>�\2�P�V���I�D�i�P�O;�9�r�mAHG�W�S]��J*�_�G��+kP�2����Ka�Z���H�'K�x�W�MZ%�O�YD�Rc+o��?�q��Ghm��d�S�oh�\�D�|:W������UA�Qc yT�q������~^�H��/��#p�CZ���T�I�1�ӏT����4��"�ČZ�����}��`w�#�*,ʹ�� ��0�i��課�Om�*�da��^gJ݅{���l�e9uF#T�ֲ��̲�ٞC"�q���ߍ ոޑ�o#�XZTp����@ o�8��(jd��xw�]�,f���`~�|,s��^����f�1���t��|��m�򸄭/ctr��5s��7�9Q�4�H1꠲BB@l9@���C�����+�wp�xu�£Yc�9��?`@#�o�mH�s2��)�=��2�.�l����jg�9$�Y�S�%*L������R�Y������7Z���,*=�䷘$�������arm�o�ϰ���UW.|�r�uf����IGw�t����Zwo��~5 ��YյhO+=8fF�)�W�7�L9lM�̘·Y���֘YLf�큹�pRF���99.A �"wz��=E\Z���'a� 2��Ǚ�#;�'}�G���*��l��^"q��+2FQ� hj��kŦ��${���ޮ-�T�٭cf�|�3#~�RJ����t��$b�(R��(����r���dx� >U b�&9,>���%E\� Ά�e�$��'�q't��*�א���ެ�b��-|d���SB�O�O��$�R+�H�)�܎�K��1m`;�J�2�Y~9��O�g8=vqD`K[�F)k�[���1m޼c��n���]s�k�z$@��)!I �x՝"v��9=�ZA=`Ɠi �:�E��)`7��vI��}d�YI�_ �o�:ob���o ���3Q��&D&�2=�� �Ά��;>�h����y.*ⅥS������Ӭ�+q&����j|UƧ����}���J0��WW< ۋS�)jQR�j���Ư��rN)�Gű�4Ѷ(�S)Ǣ�8��i��W52���No˓� ۍ%�5brOn�L�;�n��\G����=�^U�dI���8$�&���h��'���+�(������cȁ߫k�l��S^���cƗjԌE�ꭔ��gF���Ȓ��@���}O���*;e�v�WV���YJ\�]X'5��ղ�k�F��b 6R�o՜m��i N�i����>J����?��lPm�U��}>_Z&�KK��q�r��I�D�Չ~�q�3fL�:S�e>���E���-G���{L�6p�e,8��������QI��h��a�Xa��U�A'���ʂ���s�+טIjP�-��y�8ۈZ?J$��W�P� ��R�s�]��|�l(�ԓ��sƊi��o(��S0��Y� 8�T97.�����WiL��c�~�dxc�E|�2!�X�K�Ƙਫ਼�$((�6�~|d9u+�qd�^3�89��Y�6L�.I�����?���iI�q���9�)O/뚅����O���X��X�V��ZF[�یgQ�L��K1���RҖr@v�#��X�l��F���Нy�S�8�7�kF!A��sM���^rkp�jP�DyS$N���q��nxҍ!U�f�!eh�i�2�m���`�Y�I�9r�6� �TF���C}/�y�^���Η���5d�'��9A-��J��>{�_l+�`��A���[�'��յ�ϛ#w:݅�%��X�}�&�PSt�Q�"�-��\縵�/����$Ɨh�Xb�*�y��BS����;W�ջ_mc�����vt?2}1�;qS�d�d~u:2k5�2�R�~�z+|HE!)�Ǟl��7`��0�<�,�2*���Hl-��x�^����'_TV�gZA�'j� ^�2Ϊ��N7t�����?w�� �x1��f��Iz�C-Ȗ��K�^q�;���-W�DvT�7��8�Z�������� hK�(P:��Q- �8�n�Z���܃e貾�<�1�YT<�,�����"�6{/ �?�͟��|1�:�#g��W�>$����d��J��d�B��=��jf[��%rE^��il:��B���x���Sּ�1հ��,�=��*�7 fcG��#q� �eh?��2�7�����,�!7x��6�n�LC�4x��},Geǝ�tC.��vS �F�43��zz\��;QYC,6����~;RYS/6���|2���5���v��T��i����������mlv��������&� �nRh^ejR�LG�f���? �ۉҬܦƩ��|��Ȱ����>3����!v��i�ʯ�>�v��オ�X3e���_1z�Kȗ\<������!�8���V��]��?b�k41�Re��T�q��mz��TiOʦ�Z��Xq���L������q"+���2ۨ��8}�&N7XU7Ap�d�X��~�׿��&4e�o�F��� �H����O���č�c�� 懴�6���͉��+)��v;j��ݷ�� �UV�� i��� j���Y9GdÒJ1��詞�����V?h��l����l�cGs�ځ�������y�Ac�����\V3�? �� ܙg�>qH�S,�E�W�[�㺨�uch�⍸�O�}���a��>�q�6�n6����N6�q������N ! 1AQaq�0@����"2BRb�#Pr���3C`��Scst���$4D���%Td�� ?���N����a��3��m���C���w��������xA�m�q�m���m������$����4n淿t'��C"w��zU=D�\R+w�p+Y�T�&�պ@��ƃ��3ޯ?�Aﶂ��aŘ���@-�����Q�=���9D��ռ�ѻ@��M�V��P��܅�G5�f�Y<�u=,EC)�<�Fy'�"�&�չ�X~f��l�KԆV��?�� �W�N����=(� �;���{�r����ٌ�Y���h{�١������jW����P���Tc�����X�K�r��}���w�R��%��?���E��m�� �Y�q|����\lEE4���r���}�lsI�Y������f�$�=�d�yO����p�����yBj8jU�o�/�S��?�U��*������ˍ�0������u�q�m [�?f����a�� )Q�>����6#������� ?����0UQ����,IX���(6ڵ[�DI�MNލ�c&���υ�j\��X�R|,4��� j������T�hA�e��^���d���b<����n�� �즇�=!���3�^�`j�h�ȓr��jẕ�c�,ٞX����-����a�ﶔ���#�$��]w�O��Ӫ�1y%��L�Y<�wg#�ǝ�̗`�x�xa�t�w��»1���o7o5��>�m뭛C���Uƃߜ}�C���y1Xνm�F8�jI���]����H���ۺиE@I�i;r�8ӭ����V�F�Շ| ��&?�3|x�B�MuS�Ge�=Ӕ�#BE5G�����Y!z��_e��q�р/W>|-�Ci߇�t�1ޯќd�R3�u��g�=0 5��[?�#͏��q�cf���H��{ ?u�=?�?ǯ���}Z��z���hmΔ�BFTW�����<�q�(v� ��!��z���iW]*�J�V�z��gX֧A�q�&��/w���u�gYӘa���; �i=����g:��?2�dž6�ى�k�4�>�Pxs����}������G�9��3 ���)gG�R<>r h�$��'nc�h�P��Bj��J�ҧH� -��N1���N��?��~��}-q!=��_2hc�M��l�vY%UE�@|�v����M2�.Y[|y�"Eï��K�ZF,�ɯ?,q�?v�M 80jx�"�;�9vk�����+ ֧�� �ȺU��?�%�vcV��mA�6��Qg^M����A}�3�nl� QRN�l8�kkn�'�����(��M�7m9و�q���%ޟ���*h$Zk"��$�9��: �?U8�Sl��,,|ɒ��xH(ѷ����Gn�/Q�4�P��G�%��Ա8�N��!� �&�7�;���eKM7�4��9R/%����l�c>�x;������>��C�:�����t��h?aKX�bhe�ᜋ^�$�Iհ �hr7%F$�E��Fd���t��5���+�(M6�t����Ü�UU|zW�=a�Ts�Tg������dqP�Q����b'�m���1{|Y����X�N��b �P~��F^F:����k6�"�j!�� �I�r�`��1&�-$�Bevk:y���#yw��I0��x��=D�4��tU���P�ZH��ڠ底taP��6����b>�xa����Q�#� WeF��ŮNj�p�J* mQ�N����*I�-*�ȩ�F�g�3 �5��V�ʊ�ɮ�a��5F���O@{���NX��?����H�]3��1�Ri_u��������ѕ�� ����0��� F��~��:60�p�͈�S��qX#a�5>���`�o&+�<2�D����: �������ڝ�$�nP���*)�N�|y�Ej�F�5ټ�e���ihy�Z �>���k�bH�a�v��h�-#���!�Po=@k̆IEN��@��}Ll?j�O������߭�ʞ���Q|A07x���wt!xf���I2?Z��<ץ�T���cU�j��]��陎Ltl �}5�ϓ��$�,��O�mˊ�;�@O��jE��j(�ا,��LX���LO���Ц�90�O �.����a��nA���7������j4 ��W��_ٓ���zW�jcB������y՗+EM�)d���N�g6�y1_x��p�$Lv:��9�"z��p���ʙ$��^��JԼ*�ϭ����o���=x�Lj�6�J��u82�A�H�3$�ٕ@�=Vv�]�'�qEz�;I˼��)��=��ɯ���x �/�W(V���p�����$ �m�������u�����񶤑Oqˎ�T����r��㠚x�sr�GC��byp�G��1ߠ�w e�8�$⿄����/�M{*}��W�]˷.�CK\�ުx���/$�WPw���r� |i���&�}�{�X� �>��$-��l���?-z���g����lΆ���(F���h�vS*���b���߲ڡn,|)mrH[���a�3�ר�[1��3o_�U�3�TC�$��(�=�)0�kgP���� ��u�^=��4 �WYCҸ:��vQ�ר�X�à��tk�m,�t*��^�,�}D*� �"(�I��9R����>`�`��[~Q]�#af��i6l��8���6�:,s�s�N6�j"�A4���IuQ��6E,�GnH��zS�HO�uk�5$�I�4��ؤ�Q9�@��C����wp�BGv[]�u�Ov���0I4���\��y�����Q�Ѹ��~>Z��8�T��a��q�ޣ;z��a���/��S��I:�ܫ_�|������>=Z����8:�S��U�I�J��"IY���8%b8���H��:�QO�6�;7�I�S��J��ҌAά3��>c���E+&jf$eC+�z�;��V����� �r���ʺ������my�e���aQ�f&��6�ND��.:��NT�vm�<- u���ǝ\MvZY�N�NT��-A�>jr!S��n�O 1�3�Ns�%�3D@���`������ܟ 1�^c<���� �a�ɽ�̲�Xë#�w�|y�cW�=�9I*H8�p�^(4���՗�k��arOcW�tO�\�ƍR��8����'�K���I�Q�����?5�>[�}��yU�ײ -h��=��% q�ThG�2�)���"ו3]�!kB��*p�FDl�A���,�eEi�H�f�Ps�����5�H:�Փ~�H�0Dت�D�I����h�F3�������c��2���E��9�H��5�zԑ�ʚ�i�X�=:m�xg�hd(�v����׊�9iS��O��d@0ڽ���:�p�5�h-��t�&���X�q�ӕ,��ie�|���7A�2���O%P��E��htj��Y1��w�Ѓ!����  ���� ࢽ��My�7�\�a�@�ţ�J �4�Ȼ�F�@o�̒?4�wx��)��]�P��~�����u�����5�����7X ��9��^ܩ�U;Iꭆ 5 �������eK2�7(�{|��Y׎ �V��\"���Z�1� Z�����}��(�Ǝ"�1S���_�vE30>���p;� ΝD��%x�W�?W?v����o�^V�i�d��r[��/&>�~`�9Wh��y�;���R��� ;;ɮT��?����r$�g1�K����A��C��c��K��l:�'��3 c�ﳯ*"t8�~l��)���m��+U,z��`(�>yJ�?����h>��]��v��ЍG*�{`��;y]��I�T� ;c��NU�fo¾h���/$���|NS���1�S�"�H��V���T���4��uhǜ�]�v;���5�͠x��'C\�SBpl���h}�N����� A�Bx���%��ޭ�l��/����T��w�ʽ]D�=����K���ž�r㻠l4�S�O?=�k �M:� ��c�C�a�#ha���)�ѐxc�s���gP�iG��{+���x���Q���I= �� z��ԫ+ �8"�k�ñ�j=|����c ��y��CF��/��*9ж�h{ �?4�o� ��k�m�Q�N�x��;�Y��4膚�a�w?�6�>e]�����Q�r�:����g�,i"�����ԩA�*M�<�G��b�if��l^M��5� �Ҩ�{����6J��ZJ�����P�*�����Y���ݛu�_4�9�I8�7���������,^ToR���m4�H��?�N�S�ѕw��/S��甍�@�9H�S�T��t�ƻ���ʒU��*{Xs�@����f�����֒Li�K{H�w^���������Ϥm�tq���s� ���ք��f:��o~s��g�r��ט� �S�ѱC�e]�x���a��) ���(b-$(�j>�7q�B?ӕ�F��hV25r[7 Y� }L�R��}����*sg+��x�r�2�U=�*'WS��ZDW]�WǞ�<��叓���{�$�9Ou4��y�90-�1�'*D`�c�^o?(�9��u���ݐ��'PI&� f�Jݮ�������:wS����jfP1F:X �H�9dԯ���˝[�_54 �}*;@�ܨ�� ð�yn�T���?�ןd�#���4rG�ͨ��H�1�|-#���Mr�S3��G�3�����)�.᧏3v�z֑��r����$G"�`j �1t��x0<Ɔ�Wh6�y�6��,œ�Ga��gA����y��b��)��h�D��ß�_�m��ü �gG;��e�v��ݝ�nQ� ��C����-�*��o���y�a��M��I�>�<���]obD��"�:���G�A��-\%LT�8���c�)��+y76���o�Q�#*{�(F�⽕�y����=���rW�\p���۩�c���A���^e6��K������ʐ�cVf5$�'->���ՉN"���F�"�UQ@�f��Gb~��#�&�M=��8�ט�JNu9��D��[̤�s�o�~������ G��9T�tW^g5y$b��Y'��س�Ǵ�=��U-2 #�MC�t(�i� �lj�@Q 5�̣i�*�O����s�x�K�f��}\��M{E�V�{�υ��Ƈ�����);�H����I��fe�Lȣr�2��>��W�I�Ȃ6������i��k�� �5�YOxȺ����>��Y�f5'��|��H+��98pj�n�.O�y�������jY��~��i�w'������l�;�s�2��Y��:'lg�ꥴ)o#'Sa�a�K��Z� �m��}�`169�n���"���x��I ��*+� }F<��cГ���F�P�������ֹ*�PqX�x۩��,� ��N�� �4<-����%����:��7����W���u�`����� $�?�I��&����o��o��`v�>��P��"��l���4��5'�Z�gE���8���?��[�X�7(��.Q�-��*���ތL@̲����v��.5���[��=�t\+�CNܛ��,g�SQnH����}*F�G16���&:�t��4ُ"A��̣��$�b �|����#rs��a�����T�� ]�<�j��BS�('$�ɻ� �wP;�/�n��?�ݜ��x�F��yUn�~mL*-�������Xf�wd^�a�}��f�,=t�׵i�.2/wpN�Ep8�OР���•��R�FJ� 55TZ��T �ɭ�<��]��/�0�r�@�f��V��V����Nz�G��^���7hZi����k��3�,kN�e|�vg�1{9]_i��X5y7� 8e]�U����'�-2,���e"����]ot�I��Y_��n�(JҼ��1�O ]bXc���Nu�No��pS���Q_���_�?i�~�x h5d'�(qw52] ��'ޤ�q��o1�R!���`ywy�A4u���h<קy���\[~�4�\ X�Wt/� 6�����n�F�a8��f���z �3$�t(���q��q�x��^�XWeN'p<-v�!�{�(>ӽDP7��ո0�y)�e$ٕv�Ih'Q�EA�m*�H��RI��=:��� ���4牢) �%_iN�ݧ�l]� �Nt���G��H�L��� ɱ�g<���1V�,�J~�ٹ�"K��Q�� 9�HS�9�?@��k����r�;we݁�]I�!{ �@�G�[�"��`���J:�n]�{�cA�E����V��ʆ���#��U9�6����j�#Y�m\��q�e4h�B�7��C�������d<�?J����1g:ٳ���=Y���D�p�ц� ׈ǔ��1�]26؜oS�'��9�V�FVu�P�h�9�xc�oq�X��p�o�5��Ա5$�9W�V(�[Ak�aY錎qf;�'�[�|���b�6�Ck��)��#a#a˙��8���=äh�4��2��C��4tm^ �n'c���]GQ$[Wҿ��i���vN�{Fu ��1�gx��1┷���N�m��{j-,��x�� Ūm�ЧS�[�s���Gna���䑴�� x�p 8<������97�Q���ϴ�v�aϚG��Rt�Һ׈�f^\r��WH�JU�7Z���y)�vg=����n��4�_)y��D'y�6�]�c�5̪�\� �PF�k����&�c;��cq�$~T�7j ���nç]�<�g ":�to�t}�159�<�/�8������m�b�K#g'I'.W�����6��I/��>v��\�MN��g���m�A�yQL�4u�Lj�j9��#44�t��l^�}L����n��R��!��t��±]��r��h6ٍ>�yҏ�N��fU�� ���� Fm@�8}�/u��jb9������he:A�y�ծw��GpΧh�5����l}�3p468��)U��d��c����;Us/�֔�YX�1�O2��uq�s��`hwg�r~�{ R��mhN��؎*q 42�*th��>�#���E����#��Hv�O����q�}�����6�e��\�,Wk�#���X��b>��p}�դ��3���T5��†��6��[��@�P�y*n��|'f�֧>�lư΂�̺����SU�'*�q�p�_S�����M�� '��c�6�����m�� ySʨ;M��r���Ƌ�m�Kxo,���Gm�P��A�G�:��i��w�9�}M(�^�V��$ǒ�ѽ�9���|���� �a����J�SQ�a���r�B;����}���ٻ֢�2�%U���c�#�g���N�a�ݕ�'�v�[�OY'��3L�3�;,p�]@�S��{ls��X�'���c�jw�k'a�.��}�}&�� �dP�*�bK=ɍ!����;3n�gΊU�ߴmt�'*{,=SzfD� A��ko~�G�aoq�_mi}#�m�������P�Xhύ����mxǍ�΂���巿zf��Q���c���|kc�����?���W��Y�$���_Lv����l߶��c���`?����l�j�ݲˏ!V��6����U�Ђ(A���4y)H���p�Z_�x��>���e��R��$�/�`^'3qˏ�-&Q�=?��CFVR �D�fV�9��{�8g�������n�h�(P"��6�[�D���< E�����~0<@�`�G�6����Hг�cc�� �c�K.5��D��d�B���`?�XQ��2��ٿyqo&+�1^� DW�0�ꊩ���G�#��Q�nL3��c���������/��x ��1�1[y�x�პCW��C�c�UĨ80�m�e�4.{�m��u���I=��f�����0QRls9���f���������9���~f�����Ǩ��a�"@�8���ȁ�Q����#c�ic������G��$���G���r/$W�(��W���V�"��m�7�[m�A�m����bo��D� j����۳� l���^�k�h׽����� ��#� iXn�v��eT�k�a�^Y�4�BN��ĕ��0 !01@Q"2AaPq3BR������?���@4�Q�����T3,���㺠�W�[=JK�Ϟ���2�r^7��vc�:�9 �E�ߴ�w�S#d���Ix��u��:��Hp��9E!�� V 2;73|F��9Y���*ʬ�F��D����u&���y؟��^EA��A��(ɩ���^��GV:ݜDy�`��Jr29ܾ�㝉��[���E;Fzx��YG��U�e�Y�C���� ����v-tx����I�sם�Ę�q��Eb�+P\ :>�i�C'�;�����k|z�رn�y]�#ǿb��Q��������w�����(�r|ӹs��[�D��2v-%��@;�8<a���[\o[ϧw��I!��*0�krs)�[�J9^��ʜ��p1)� "��/_>��o��<1����A�E�y^�C��`�x1'ܣn�p��s`l���fQ��):�l����b>�Me�jH^?�kl3(�z:���1ŠK&?Q�~�{�ٺ�h�y���/�[��V�|6��}�KbX����mn[-��7�5q�94�������dm���c^���h� X��5��<�eޘ>G���-�}�دB�ޟ� ��|�rt�M��V+�]�c?�-#ڛ��^ǂ}���Lkr���O��u�>�-D�ry� D?:ޞ�U��ǜ�7�V��?瓮�"�#���r��չģVR;�n���/_� ؉v�ݶe5d�b9��/O��009�G���5n�W����JpA�*�r9�>�1��.[t���s�F���nQ� V 77R�]�ɫ8����_0<՜�IF�u(v��4��F�k�3��E)��N:��yڮe��P�`�1}�$WS��J�SQ�N�j�ٺ��޵�#l���ј(�5=��5�lǏmoW�v-�1����v,W�mn��߀$x�<����v�j(����c]��@#��1������Ǔ���o'��u+����;G�#�޸��v-lη��/(`i⣍Pm^���ԯ̾9Z��F��������n��1��� ��]�[��)�'������:�֪�W��FC����� �B9،!?���]��V��A�Վ�M��b�w��G F>_DȬ0¤�#�QR�[V��kz���m�w�"��9ZG�7'[��=�Q����j8R?�zf�\a�=��O�U����*oB�A�|G���2�54 �p��.w7� �� ��&������ξxGHp� B%��$g�����t�Џ򤵍z���HN�u�Я�-�'4��0��;_��3 !01"@AQa2Pq#3BR������?��ʩca��en��^��8���<�u#��m*08r��y�N"�<�Ѳ0��@\�p��� �����Kv�D��J8�Fҽ� �f�Y��-m�ybX�NP����}�!*8t(�OqѢ��Q�wW�K��ZD��Δ^e��!� ��B�K��p~�����e*l}z#9ң�k���q#�Ft�o��S�R����-�w�!�S���Ӥß|M�l޶V��!eˈ�8Y���c�ЮM2��tk���� ������J�fS����Ö*i/2�����n]�k�\���|4yX�8��U�P.���Ы[���l��@"�t�<������5�lF���vU�����W��W��;�b�cД^6[#7@vU�xgZv��F�6��Q,K�v��� �+Ъ��n��Ǣ��Ft���8��0��c�@�!�Zq s�v�t�;#](B��-�nῃ~���3g������5�J�%���O������n�kB�ĺ�.r��+���#�N$?�q�/�s�6��p��a����a��J/��M�8��6�ܰ"�*������ɗud"\w���aT(����[��F��U՛����RT�b���n�*��6���O��SJ�.�ij<�v�MT��R\c��5l�sZB>F��<7�;EA��{��E���Ö��1U/�#��d1�a�n.1ě����0�ʾR�h��|�R��Ao�3�m3 ��%�� ���28Q� ��y��φ���H�To�7�lW>����#i`�q���c����a��� �m,B�-j����݋�'mR1Ήt�>��V��p���s�0IbI�C.���1R�ea�����]H�6����������4B>��o��](��$B���m�����a�!=��?�B� K�Ǿ+�Ծ"�n���K��*��+��[T#�{E�J�S����Q�����s�5�:�U�\wĐ�f�3����܆&�)����I���Ԇw��E T�lrTf6Q|R�h:��[K�� �z��c֧�G�C��%\��_�a�84��HcO�bi��ؖV��7H �)*ģK~Xhչ0��4?�0��� �E<���}3���#���u�?�� ��|g�S�6ꊤ�|�I#Hڛ� �ա��w�X��9��7���Ŀ%�SL��y6č��|�F�a 8���b��$�sק�h���b9RAu7�˨p�Č�_\*w��묦��F ����4D~�f����|(�"m���NK��i�S�>�$d7SlA��/�²����SL��|6N�}���S�˯���g��]6��; �#�.��<���q'Q�1|KQ$�����񛩶"�$r�b:���N8�w@��8$�� �AjfG|~�9F ���Y��ʺ��Bwؒ������M:I岎�G��`s�YV5����6��A �b:�W���G�q%l�����F��H���7�������Fsv7��k�� 403WebShell
403Webshell
Server IP : 14.139.229.36  /  Your IP : 10.1.1.9
Web Server : Apache
System : Linux gbpuat-tech.ac.in 4.18.0-240.15.1.el8_3.x86_64 #1 SMP Mon Mar 1 17:16:16 UTC 2021 x86_64
User : apache ( 48)
PHP Version : 7.2.24
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/lib64/python3.6/site-packages/pyanaconda/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/lib64/python3.6/site-packages/pyanaconda/geoloc.py
#
# Copyright (C) 2013, 2017  Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties 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 this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#

"""
A GeoIP and WiFi location module - location detection based on IP address

How to use the geolocation module
   First call init_geolocation() with appropriate parameters - this creates
   the Geolocation singleton and specifies what geolocation provider will be
   used.

   To actually look up current position, call the refresh() function of the
   singleton, this will trigger the actual online geolocation query, which
   runs in a thread.

   It's possible to wait for the lookup to finish by calling the
   wait_for_refresh_to_finish() method of the singleton. If a lookup is in
   progress it will block until the lookup finishes or a timeout is reached.
   If no lookup is in progress it will return at once.

   After the look-up thread finishes, the results are stored in the singleton
   and can be retrieved using the territory, timezone and result properties.

   If you use these properties without calling refresh() first or if the
   look-up is currently in progress or failed to return any results, all
   properties will return None.

====================
Geolocation backends
====================

This module currently supports three geolocation backends:
   * Fedora GeoIP API
   * Hostip GeoIP
   * Google WiFi

Fedora GeoIP backend
   This is the default backend. It queries the Fedora GeoIP API for location
   data based on current public IP address. The reply is JSON formatted and
   contains the following fields:
   postal_code, latitude, longitude, region, city, country_code, country_name,
   time_zone, country_code3, area_code, metro_code, region_name and dma_code
   Anaconda currently uses just time_zone and country_code.

Hostip backend
   A GeoIP look-up backend that can be used to determine current country code
   from current public IP address. The public IP address is determined
   automatically when calling the API.
   GeoIP results from Hostip contain the current public IP and an approximate
   address. To get this detail location info, use the result property to get
   an instance of the LocationResult class, used to wrap the lookup result.

Google WiFi backend
   This backend is probably the most accurate one, at least as long as the
   computer has a working WiFi hardware and there are some WiFi APs nearby.
   It sends data about nearby APs (ssid, MAC address & signal strength)
   acquired from Network Manager to a Google API to get approximate
   geographic coordinates. If there are enough AP nearby (such as in a
   normal city) it can be very accurate, even up to currently determining
   which building is the computer currently in.
   But this only returns current geographic coordinates, to get country code
   the Nominatim reverse-geocoding API is called to convert the coordinates
   to an address, which includes a country code.

While having many advantages, this backend also has some severe disadvantages:
   * needs working WiFi hardware
   * tells your public IP address & possibly quite precise geographic coordinates
     to two external entities (Google and Nominatim)

This could have severe privacy issues and should be carefully considered before
enabling it to be used by default. Also the Google WiFi geolocation API seems to
lack official documentation.

As a result its long-term stability might not be guaranteed.



Possible issues with GeoIP
   "I'm in Switzerland connected to corporate VPN and anaconda tells me
   I'm in the Netherlands."
   The public IP address is not directly mapped to the physical location
   of a computer. So while your world visible IP address is registered to
   an IP block assigned to an ISP in Netherlands, it is just the external
   address of the Internet gateway of  your corporate network.
   As VPNs and proxies can connect two computers anywhere on Earth,
   this issue is unfortunately probably unsolvable.


Backends that could possibly be used in the future
   * GPS geolocation

      * (+) doesn't leak your coordinates to a third party
        (not entirely true for assisted GPS)
      * (-) unassisted cold GPS startup can take tens of minutes to acquire a GPS fix
      * (+) assisted GPS startup (as used in most smartphones) can acquire a fix
        in a couple seconds

   * cell tower geolocation

"""
from pyanaconda.core.util import requests_session
import requests
import urllib.parse
import dbus
import threading
import time
from pyanaconda import network

from pyanaconda.anaconda_loggers import get_module_logger, get_sensitive_info_logger
log = get_module_logger(__name__)
sensitive_info_log = get_sensitive_info_logger()

from pyanaconda.core import constants
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.threading import AnacondaThread, threadMgr
from pyanaconda.timezone import get_preferred_timezone, is_valid_timezone
from pyanaconda.flags import flags

OFFICIALLY_SUPPORTED_GEOLOCATION_PROVIDER_IDS = {
    constants.GEOLOC_PROVIDER_FEDORA_GEOIP,
    constants.GEOLOC_PROVIDER_HOSTIP
}


class Geolocation(object):
    """Top level geolocation handler."""

    def __init__(self, geoloc_option=None, options_override=False):
        """Prepare the geolocation module for handling geolocation queries.

        This sets-up the Geolocation instance with the given geolocation_provider (or using the
        default one if no provider is given. Please note that calling this method doesn't actually
        execute any queries by itself, you need to call refresh() to do that.

        :param geoloc_option: what was passed in boot or command line options
        :type geoloc_option: str or None
        :param options_override:
        :type options_override: bool
        """
        self._geolocation_enabled = self._check_if_geolocation_should_be_used(geoloc_option,
                                                                              options_override)
        provider_id = constants.GEOLOC_DEFAULT_PROVIDER

        # check if a provider was specified by an option
        if geoloc_option is not None and self._geolocation_enabled:
            parsed_id = self._get_provider_id_from_option(geoloc_option)
            if parsed_id is None:
                log.error('geoloc: wrong provider id specified: %s', geoloc_option)
            else:
                provider_id = parsed_id

        self._location_info = LocationInfo(provider_id=provider_id)

    def _check_if_geolocation_should_be_used(self, geoloc_option, options_override):
        """Check if geolocation can be used during this installation run.

        And set the geolocation_enabled module attribute accordingly.

        The result is based on current installation type - fully interactive vs
        fully or partially automated kickstart installation and on the state of the
        "geoloc*" boot/CLI options.

        By default geolocation is not enabled during a kickstart based installation,
        unless the geoloc_use_with_ks boot/CLI option is used.

        Also the geoloc boot/CLI option can be used to make sure geolocation
        will not be used during an installation, like this:

        inst.geoloc=0

        :param geoloc_option: what was passed in boot or command line options
        :type geoloc_option: str or None
        :param options_override: use with kickstart due to CLI/boot option override
        :type options_override: bool
        """
        # don't use geolocation during image and directory installation
        if not conf.target.is_hardware:
            log.info("Geolocation is disabled for image or directory installation.")
            return False

        # check if geolocation was not disabled by boot or command line option
        # our documentation mentions only "0" as the way to disable it
        if str(geoloc_option).strip() == "0":
            log.info("Geolocation is disabled by the geoloc option.")
            return False

        # don't use geolocation during kickstart installation unless explicitly
        # requested by the user
        if flags.automatedInstall:
            if options_override:
                # check for use-with-kickstart overrides
                log.info("Geolocation is enabled during kickstart installation due to use of "
                         "the geoloc-use-with-ks option.")
                return True
            else:
                # otherwise disable geolocation during a kickstart installation
                log.info("Geolocation is disabled due to automated kickstart based installation.")
                return False

        log.info("Geolocation is enabled.")
        return True

    def refresh(self):
        """Refresh information about current location."""
        self._location_info.refresh()

    def wait_for_refresh_to_finish(self, timeout=constants.GEOLOC_TIMEOUT):
        """Wait for the Geolocation lookup to finish.

        If there is no lookup in progress (no Geolocation refresh thread
        is running), this function returns at once.

        :param float timeout: how many seconds to wait before timing out
        """
        start_time = time.time()
        self._location_info.refresh_condition.acquire()
        if self._location_info.refresh_in_progress:
            # calling wait releases the lock and blocks,
            # after the thread is notified, it unblocks and
            # reacquires the lock
            self._location_info.refresh_condition.wait(timeout=timeout)
            if self._location_info.refresh_in_progress:
                log.info("Waiting for Geolocation timed out after %d seconds.", timeout)
                # please note that this does not mean that the actual
                # geolocation lookup was stopped in any way, it just
                # means the caller was unblocked after the waiting period
                # ended while the lookup thread is still running
            else:
                elapsed_time = time.time() - start_time
                log.info("Waited %1.2f seconds for Geolocation", elapsed_time)
        self._location_info.refresh_condition.release()

    @property
    def enabled(self):
        """Report if geolocation is enabled."""
        return self._geolocation_enabled

    @property
    def result(self):
        """Returns the current geolocation result wrapper.

        None might be returned if:

        - no results were found
        - the refresh is still in progress

        :return: :class:LocationResult instance or None if location is unknown
        :rtype: :class:LocationResult instance or None
        """
        return self._location_info.result

    def _get_provider_id_from_option(self, option_string):
        """Get a valid provider id from a string.

        This function is used to parse command line
        arguments/boot options for the geolocation module.

        :param str option_string: option specifying the provider
        :return: provider id
        """
        # normalize the option string, just in case
        option_string = option_string.lower()
        if option_string.lower() in OFFICIALLY_SUPPORTED_GEOLOCATION_PROVIDER_IDS:
            return option_string
        else:
            # fall back to the default provider
            return None


class LocationInfo(object):
    """Determines current location.

    Determines current location based on IP address or
    nearby WiFi access points (depending on what backend is used)
    """

    def __init__(self, provider_id=constants.GEOLOC_DEFAULT_PROVIDER):
        """
        :param str provider_id: GeoIP provider id
        """
        available_providers = {
            constants.GEOLOC_PROVIDER_FEDORA_GEOIP: FedoraGeoIPProvider,
            constants.GEOLOC_PROVIDER_HOSTIP: HostipGeoIPProvider,
            constants.GEOLOC_PROVIDER_GOOGLE_WIFI: GoogleWiFiLocationProvider
        }
        provider = available_providers.get(provider_id, FedoraGeoIPProvider)
        self._provider = provider()

    @property
    def result(self):
        """Return the lookup result."""
        return self._provider.result

    def refresh(self):
        """Refresh location info."""
        # check if a refresh is already in progress
        if threadMgr.get(constants.THREAD_GEOLOCATION_REFRESH):
            log.debug("Geoloc: refresh already in progress")
        else:  # wait for Internet connectivity
            if network.wait_for_connectivity():
                threadMgr.add(AnacondaThread(
                    name=constants.THREAD_GEOLOCATION_REFRESH,
                    target=self._provider.refresh))
            else:
                log.error("Geolocation refresh failed"
                          " - no connectivity")

    @property
    def refresh_in_progress(self):
        """Report if refresh is in progress."""
        return self._provider.refresh_in_progress

    @property
    def refresh_condition(self):
        """Provide access to the Refresh condition of the provider.

        So that users of this class can wait for the location lookup to finish.
        """
        return self._provider.refresh_condition


class LocationResult(object):
    """Encapsulates the result from GeoIP lookup."""

    def __init__(self, territory_code=None, timezone=None,
                 timezone_source="unknown", public_ip_address=None, city=None):
        """
        :param territory_code: the territory code from GeoIP lookup
        :type territory_code: string
        :param timezone: the time zone from GeoIP lookup
        :type timezone: string
        :param timezone_source: specifies source of the time zone string
        :type timezone_source: string
        :param public_ip_address: current public IP address
        :type public_ip_address: string
        :param city: current city
        :type city: string
        """
        self._territory_code = territory_code
        self._timezone = timezone
        self._timezone_source = timezone_source
        self._public_ip_address = public_ip_address
        self._city = city

    @property
    def territory_code(self):
        return self._territory_code

    @property
    def timezone(self):
        return self._timezone

    @property
    def public_ip_address(self):
        return self._public_ip_address

    @property
    def city(self):
        return self._city

    def __str__(self):
        if self.territory_code:
            result_string = "territory: %s" % self.territory_code
            if self.timezone:
                result_string += "\ntime zone: %s (from %s)" % (
                    self.timezone, self._timezone_source
                )
            if self.public_ip_address:
                result_string += "\npublic IP address: "
                result_string += "%s" % self.public_ip_address
            if self.city:
                result_string += "\ncity: %s" % self.city
            return result_string
        else:
            return "Position unknown"


class GeolocationBackend(object):
    """Base class for GeoIP backends."""
    def __init__(self):
        self._result = LocationResult()
        self._result_lock = threading.Lock()
        self._session = requests_session()
        self._refresh_condition = threading.Condition()
        self._refresh_in_progress = False

    @property
    def name(self):
        """Get name of the backend

        :return: name of the backend
        :rtype: string
        """
        pass

    def refresh(self):
        """Refresh the geolocation data."""
        # check if refresh is needed
        log.info("Starting geolocation lookup")
        log.info("Geolocation provider: %s", self.name)
        with self._refresh_condition:
            self._refresh_in_progress = True

        start_time = time.time()
        self._refresh()
        log.info("Geolocation lookup finished in %1.1f seconds",
                 time.time() - start_time)

        with self._refresh_condition:
            self._refresh_in_progress = False
            self._refresh_condition.notify_all()
        # check if there were any results
        if self.result:
            log.info("got results from geolocation")
            sensitive_info_log.info("geolocation result:\n%s", self.result)
        else:
            log.info("no results from geolocation")

    def _refresh(self):
        pass

    @property
    def refresh_in_progress(self):
        """Report if location refresh is in progress."""
        with self._refresh_condition:
            return self._refresh_in_progress

    @property
    def refresh_condition(self):
        """Return a Condition instance that can be used to wait for the refresh to finish."""
        return self._refresh_condition

    @property
    def result(self):
        """Current location.

        :return: geolocation lookup result
        :rtype: LocationResult
        """
        with self._result_lock:
            return self._result

    def _set_result(self, new_result):
        with self._result_lock:
            self._result = new_result

    def __str__(self):
        return self.name


class FedoraGeoIPProvider(GeolocationBackend):
    """The Fedora GeoIP service provider."""

    API_URL = "https://geoip.fedoraproject.org/city"

    @property
    def name(self):
        return "Fedora GeoIP"

    def _refresh(self):
        try:
            reply = self._session.get(self.API_URL,
                                      timeout=constants.NETWORK_CONNECTION_TIMEOUT,
                                      verify=True)
            if reply.status_code == requests.codes.ok:
                json_reply = reply.json()
                territory = json_reply.get("country_code", None)
                timezone_source = "GeoIP"
                timezone_code = json_reply.get("time_zone", None)

                # check if the timezone returned by the API is valid
                if not is_valid_timezone(timezone_code):
                    # try to get a timezone from the territory code
                    timezone_code = get_preferred_timezone(territory)
                    timezone_source = "territory code"
                if territory or timezone_code:
                    self._set_result(LocationResult(territory_code=territory,
                                                    timezone=timezone_code,
                                                    timezone_source=timezone_source))
            else:
                log.error("Geoloc: Fedora GeoIP API lookup failed with status code: %s",
                          reply.status_code)
        except requests.exceptions.RequestException as e:
            log.debug("Geoloc: RequestException for Fedora GeoIP API lookup:\n%s", e)
        except ValueError as e:
            log.debug("Geoloc: Unable to decode GeoIP JSON:\n%s", e)


class HostipGeoIPProvider(GeolocationBackend):
    """The Hostip GeoIP service provider."""

    API_URL = "http://api.hostip.info/get_json.php"

    @property
    def name(self):
        return "Hostip.info"

    def _refresh(self):
        try:
            reply = self._session.get(self.API_URL,
                                      timeout=constants.NETWORK_CONNECTION_TIMEOUT,
                                      verify=True)
            if reply.status_code == requests.codes.ok:
                reply_dict = reply.json()
                territory = reply_dict.get("country_code", None)

                # unless at least country_code is available,
                # we don't return any results
                if territory is not None:
                    self._set_result(LocationResult(territory_code=territory,
                                                    public_ip_address=reply_dict.get("ip", None),
                                                    city=reply_dict.get("city", None)))
            else:
                log.error("Geoloc: Hostip lookup failed with status code: %s", reply.status_code)
        except requests.exceptions.RequestException as e:
            log.debug("Geoloc: RequestException during Hostip lookup:\n%s", e)
        except ValueError as e:
            log.debug("Geoloc: Unable to decode Hostip JSON:\n%s", e)


class GoogleWiFiLocationProvider(GeolocationBackend):
    """The Google WiFi location service provider."""

    API_URL = "https://maps.googleapis.com/" \
              "maps/api/browserlocation/json?browser=firefox&sensor=true"

    @property
    def name(self):
        return "Google WiFi"

    def _refresh(self):
        log.info("Scanning for WiFi access points.")
        scanner = WifiScanner(scan_now=True)
        access_points = scanner.get_results()
        if access_points:
            try:
                reply = self._session.get(self._get_url(access_points),
                                          timeout=constants.NETWORK_CONNECTION_TIMEOUT,
                                          verify=True)
                result_dict = reply.json()
                status = result_dict.get('status', 'NOT OK')
                if status == 'OK':
                    lat = result_dict['location']['lat']
                    lon = result_dict['location']['lng']
                    log.info("Found current location.")
                    coords = Coordinates(lat=lat, lon=lon)
                    geocoder = Geocoder()
                    geocoding_result = geocoder.reverse_geocode_coords(coords)
                    # for compatibility, return GeoIP result instead
                    # of GeocodingResult
                    t_code = geocoding_result.territory_code
                    self._set_result(LocationResult(territory_code=t_code))
                else:
                    log.info("Geoloc: Service couldn't find current location.")
            except requests.exceptions.RequestException as e:
                log.debug("Geoloc: RequestException during Google Wifi lookup:\n%s", e)
            except ValueError as e:
                log.debug("Geoloc: Unable to decode Google Wifi JSON:\n%s", e)
        else:
            log.info("Geoloc: No WiFi access points found - can't detect location.")

    def _get_url(self, access_points):
        """Generate Google API URL for the given access points

        :param access_points: a list of WiFiAccessPoint objects
        :return Google WiFi location API URL
        :rtype: string
        """
        url = self.API_URL
        for ap in access_points:
            url += self._describe_access_point(ap)
        return url

    def _describe_access_point(self, access_point):
        """Describe an access point in a format compatible with the API call

        :param access_point: a WiFiAccessPoint instance
        :return: API compatible AP description
        :rtype: string
        """
        quoted_ssid = urllib.parse.quote_plus(access_point.ssid)
        return "&wifi=mac:%s|ssid:%s|ss:%d" % (access_point.bssid,
                                               quoted_ssid, access_point.rssi)


class Geocoder(object):
    """Provides online geocoding services.

    (only reverse geocoding at the moment)
    """

    # MapQuest Nominatim instance without (?) rate limiting
    NOMINATIM_API_URL = "http://open.mapquestapi.com/" \
                        "nominatim/v1/reverse.php?format=json"
    # Alternative OSM hosted Nominatim instance (with rate limiting):
    # http://nominatim.openstreetmap.org/reverse?format=json

    def __init__(self, geocoder=constants.GEOLOC_DEFAULT_GEOCODER):
        """:param geocoder: a constant selecting what geocoder to use"""
        self._geocoder = geocoder

    def reverse_geocode_coords(self, coordinates):
        """Turn geographic coordinates to address

        :param coordinates: Coordinates (geographic coordinates)
        :type coordinates: Coordinates
        :return: GeocodingResult if the lookup succeeds or None if it fails
        """
        if self._geocoder == constants.GEOLOC_GEOCODER_NOMINATIM:
            return self._reverse_geocode_nominatim(coordinates)
        else:
            log.error("Wrong Geocoder specified!")
            return None  # unknown geocoder specified

    def _reverse_geocode_nominatim(self, coordinates):
        """Reverse geocoding using the Nominatim API.

        Reverse geocoding tries to convert geographic coordinates
        to an accurate address.

        :param coordinates: input coordinates
        :type coordinates: Coordinates
        :return: an address or None if no address was found
        :rtype: GeocodingResult or None
        """
        url = "%s&addressdetails=1&lat=%f&lon=%f" % (
            self.NOMINATIM_API_URL,
            coordinates.latitude,
            coordinates.longitude)
        try:
            reply = requests_session().get(url,
                                           timeout=constants.NETWORK_CONNECTION_TIMEOUT,
                                           verify=True)
            if reply.status_code == requests.codes.ok:
                reply_dict = reply.json()
                territory_code = reply_dict['address']['country_code'].upper()
                return GeocodingResult(coordinates=coordinates,
                                       territory_code=territory_code)
            else:
                log.error("Geoloc: Nominatim reverse geocoding failed with status code: %s",
                          reply.status_code)
                return None
        except requests.exceptions.RequestException as e:
            log.debug("Geoloc: RequestException during Nominatim reverse geocoding:\n%s", e)
        except ValueError as e:
            log.debug("Geoloc: Unable to decode Nominatim reverse geocoding JSON:\n%s", e)


class GeocodingResult(object):
    """A result from geocoding lookup."""

    def __init__(self, coordinates=None, territory_code=None, address=None):
        """
        :param coordinates: geographic coordinates
        :type coordinates: Coordinates
        :param territory_code: territory code of the result
        :type territory_code: string
        :param address: a (street) address string
        :type address: string
        """
        self._coords = coordinates
        self._territory_code = territory_code
        self._address = address

    @property
    def coordinates(self):
        return self._coords

    @property
    def territory_code(self):
        return self._territory_code

    @property
    def address(self):
        return self._address


class Coordinates(object):
    """A set of geographic coordinates."""

    def __init__(self, lat=None, lon=None):
        """
        :param lat: WGS84 latitude
        :type lat: float
        :param lon: WGS84 longitude
        :type lon: float
        """
        self._lat = lat
        self._lon = lon

    @property
    def latitude(self):
        return self._lat

    @property
    def longitude(self):
        return self._lon

    def __str__(self):
        return "lat,lon: %f,%f" % (self.latitude, self.longitude)


class WifiScanner(object):
    """Use the Network Manager DBus API to provide information about nearby WiFi access points."""

    NETWORK_MANAGER_DEVICE_TYPE_WIFI = 2

    def __init__(self, scan_now=True):
        """
        :param scan_now: if an initial scan should be done
        :type scan_now: bool
        """
        self._scan_results = []
        if scan_now:
            self.scan()

    def scan(self):
        """Scan for WiFi access points"""
        devices = ""
        access_points = []
        # connect to network manager
        try:
            bus = dbus.SystemBus()
            network_manager = bus.get_object('org.freedesktop.NetworkManager',
                                             '/org/freedesktop/NetworkManager')
            devices = network_manager.GetDevices()
        except dbus.DBusException as e:
            log.debug("Exception caught during WiFi AP scan: %s", e)

        # iterate over all devices
        for device_path in devices:
            device = bus.get_object('org.freedesktop.NetworkManager',
                                    device_path)
            # get type of the device
            device_type = device.Get("org.freedesktop.NetworkManager.Device",
                                     'DeviceType')
            # iterate over all APs
            if device_type == self.NETWORK_MANAGER_DEVICE_TYPE_WIFI:

                dbus_iface_id = 'org.freedesktop.DBus.Properties'
                ap_id = "org.freedesktop.NetworkManager.AccessPoint"

                for ap_path in device.GetAccessPoints():
                    # drill down to the DBus object for the AP
                    net = bus.get_object('org.freedesktop.NetworkManager', ap_path)
                    network_properties = dbus.Interface(
                        net, dbus_interface=dbus_iface_id)

                    # get the MAC, name & signal strength
                    bssid = str(network_properties.Get(ap_id, "HwAddress"))
                    essid = str(network_properties.Get(
                        ap_id, "Ssid", byte_arrays=True))
                    rssi = int(network_properties.Get(ap_id, "Strength"))

                    # create a new AP object and add it to the
                    # list of discovered APs
                    ap = WiFiAccessPoint(bssid=bssid, ssid=essid, rssi=rssi)
                    access_points.append(ap)
        self._scan_results = access_points

    def get_results(self):
        """
        :return: a list of WiFiAccessPoint objects or
                 an empty list if no APs were found or the scan failed
        """
        return self._scan_results


class WiFiAccessPoint(object):
    """Encapsulates information about a WiFi access point."""

    def __init__(self, bssid, ssid=None, rssi=None):
        """
        :param bssid: MAC address of the access point
        :param ssid: name of the access point
        :param rssi: signal strength
        """
        self._bssid = bssid
        self._ssid = ssid
        self._rssi = rssi

    @property
    def bssid(self):
        return self._bssid

    @property
    def ssid(self):
        return self._ssid

    @property
    def rssi(self):
        return self._rssi

    def __str__(self):
        return "bssid (MAC): %s ssid: %s rssi " \
               "(signal strength): %d" % (self.bssid, self.ssid, self.rssi)


geoloc = None


def init_geolocation(geoloc_option, options_override):
    """Initialize the geolocation singleton."""
    global geoloc
    geoloc = Geolocation(geoloc_option=geoloc_option, options_override=options_override)

Youez - 2016 - github.com/yon3zu
LinuXploit