���� JFIF  XX �� �� �     $.' ",#(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�8 ds|\���Ҿp6�Ҵ���]��.����6� z<�v��@]�i% �� $j��~ �g��J>��no����pM[me�i$[�� �� s�o�ᘨ�˸ nɜG-�ĨU�ycP� 3.DB�li�;� �hj���x 7Z^�N�h��� ���N3u{�:j �x�힞��#M &��jL P@ _���� P�� &��o8 ������9 �����@Sz 6�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*-i H��g�D�)� E�m�|�ؘbҗ�a ��Ҿ���� t4��� o���G��*oCN�rP���Q��@z,|?W[0 �����:�n,j WiE��W� �$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S�� ��VN;� }�s?.����� w �9��˟<���Mq4�Wv' ��{)0�1mB ��V����W[� ����8�/<� �%���wT^�5���b��)iM� p g�N�&ݝ� �VO~� q���u���9� ����!��J27��� �$ O-���! �: �%H��� ـ ����y�ΠM=t{!S�� oK8������ t<����è :a�� ����[���� �ա�H���~��w��Qz`�p o�^ �� ��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�_�uX 6��3?nk��Wy�f;^*B� ��@ �~a�`��Eu������ +� �� 6�L��.ü>��}y���}_�O�6�͐�:�Yr G�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�!0 39C� �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 Xp 3�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@:)ִ�:�]��h v�E� w���T�l ��P� ��"Ju�}��وV J��G6��. J/�Qgl߭�e�����@�z�Zev2u� )]կ��� ��7x�� �s�M�-<ɯ�c��r� v�����@��$�ޮ}lk���a�� �'����>x��O\�Z Fu>��� ��ck#��&:��`�$ �ai�>2Δ����l���oF[h� �lE�ܺ�Π k:)���` �� $[6�����9�����kOw�\|��� 8}������ބ:��񶐕� �I�A1/� =�2[�,�!��.}gN#�u����b ��� ~� �݊��}34q��� �d�E��L c��$ ��"�[q�U�硬g^��%B � z���r�p J�ru%v\h 1Y�ne` ǥ:g�� �pQM~�^� Xi� ��`S�:V2 9.�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& Ú����2 g�}&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��f2 4;�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)*P t����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���#y w��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���/$�WP w���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��B S�('$�ɻ� �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 : 127.0.0.1  /  Your IP : 10.100.1.254
Web Server : Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.0.30
System : Windows NT WIZC-EXTRANET 10.0 build 19045 (Windows 10) AMD64
User : SYSTEM ( 0)
PHP Version : 8.0.30
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  C:/xampp/FileZillaFTP/source/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : C:/xampp/FileZillaFTP/source/ControlSocket.cpp
// FileZilla Server - a Windows ftp server

// Copyright (C) 2002-2004 - Tim Kosse <tim.kosse@gmx.de>

// This program 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 2
// 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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// ControlSocket.cpp: Implementierungsdatei
//

#include "stdafx.h"
#include "ControlSocket.h"
#include "transfersocket.h"
#include "ServerThread.h"
#include "Options.h"
#include "Permissions.h"
#include "AsyncGssSocketLayer.h"
#include "AsyncSslSocketLayer.h"
#include <math.h>
#include "iputils.h"
#include "autobanmanager.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CControlSocket

std::map<CStdString, int> CControlSocket::m_UserCount;
CCriticalSectionWrapper CControlSocket::m_Sync;

CControlSocket::CControlSocket(CServerThread *pOwner)
	: m_hash_algorithm(CHashThread::SHA1)
{
	m_status.loggedon = FALSE;
	m_status.hammerValue = 0;
	m_transferstatus.socket = NULL;
	m_transferstatus.ip = _T("");
	m_transferstatus.port = -1;
	m_transferstatus.pasv = -1;
	m_transferstatus.rest = 0;
	m_transferstatus.type = -1;
	m_transferstatus.family = AF_UNSPEC;
	m_bWaitGoOffline = FALSE;
	GetSystemTime(&m_LastTransferTime);
	GetSystemTime(&m_LastCmdTime);
	GetSystemTime(&m_LoginTime);
	m_bQuitCommand = FALSE;

	ASSERT(pOwner);
	m_pOwner = pOwner;

	m_nTelnetSkip = 0;
	m_nRecvBufferPos = 0;

	m_pSendBuffer = NULL;
	m_nSendBufferLen = 0;

	m_pGssLayer = NULL;
	m_pSslLayer = NULL;

	for (int i = 0; i < 2; i++)
	{
		m_SlQuotas[i].bContinue = false;
		m_SlQuotas[i].nBytesAllowedToTransfer = -1;
		m_SlQuotas[i].nTransferred = 0;
		m_SlQuotas[i].bBypassed = true;
	}

	m_transferMode = mode_stream;

	m_zlibLevel = 8;

	m_antiHammeringWaitTime = 0;
	m_bProtP = false;

	m_useUTF8 = true;

	for (int i = 0; i < 3; i++)
		m_facts[i] = true;
	m_facts[fact_perm] = false;

	m_shutdown = false;

	m_hash_id = 0;
}

CControlSocket::~CControlSocket()
{
	if (m_status.loggedon)
	{
		DecUserCount(m_status.user);
		m_pOwner->DecIpCount(m_status.ip);
		m_status.loggedon = FALSE;
	}
	t_connop *op = new t_connop;
	op->data = 0;
	op->op = USERCONTROL_CONNOP_REMOVE;
	op->userid = m_userid;
	m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);
	if (m_transferstatus.socket)
		delete m_transferstatus.socket;
	m_transferstatus.socket=0;

	delete [] m_pSendBuffer;
	m_nSendBufferLen = 0;

	RemoveAllLayers();
	delete m_pGssLayer;
	delete m_pSslLayer;
}

/////////////////////////////////////////////////////////////////////////////
// Member-Funktion CControlSocket

#define BUFFERSIZE 500
void CControlSocket::OnReceive(int nErrorCode)
{
	if (m_antiHammeringWaitTime)
	{
		if (nErrorCode)
		{
			//Control connection has been closed
			Close();
			SendStatus(_T("disconnected."), 0);
			m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
		}
		return;
	}

	int len = BUFFERSIZE;
	int nLimit = GetSpeedLimit(upload);
	if (!nLimit)
	{
		ParseCommand();
		return;
	}
	if (len > nLimit && nLimit != -1)
		len = nLimit;

	unsigned char *buffer = new unsigned char[BUFFERSIZE];
	int numread = Receive(buffer, len);
	if (numread != SOCKET_ERROR && numread)
	{
		if (nLimit != -1)
			m_SlQuotas[upload].nTransferred += numread;

		m_pOwner->IncRecvCount(numread);
		//Parse all received bytes
		for (int i = 0; i<numread; i++)
		{
			if (!m_nRecvBufferPos)
			{
				//Remove telnet characters
				if (m_nTelnetSkip)
				{
					if (buffer[i] < 240)
						m_nTelnetSkip = 0;
					else
						continue;
				}
				else if (buffer[i] == 255)
				{
					m_nTelnetSkip = 1;
					continue;
				}
			}

			//Check for line endings
			if ((buffer[i] == '\r')||(buffer[i] == 0)||(buffer[i] == '\n'))
			{
				//If input buffer is not empty...
				if (m_nRecvBufferPos)
				{
					m_RecvBuffer[m_nRecvBufferPos] = 0;
					m_RecvLineBuffer.push_back(m_RecvBuffer);
					m_nRecvBufferPos = 0;

					//Signal that there is a new command waiting to be processed.
					GetSystemTime(&m_LastCmdTime);
				}
			}
			else
				//The command may only be 2000 chars long. This ensures that a malicious user can't
				//send extremely large commands to fill the memory of the server
				if (m_nRecvBufferPos < 2000)
					m_RecvBuffer[m_nRecvBufferPos++] = buffer[i];
		}
	}
	else
	{
		if (!numread || GetLastError() != WSAEWOULDBLOCK)
		{
			//Control connection has been closed
			Close();
			SendStatus(_T("disconnected."), 0);
			m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

			delete [] buffer;
			return;
		}
	}

	ParseCommand();

	delete [] buffer;
}

BOOL CControlSocket::GetCommand(CStdString &command, CStdString &args)
{
	//Get first command from input buffer
	CStdStringA str;
	if (m_RecvLineBuffer.empty())
		return FALSE;
	str = m_RecvLineBuffer.front();
	m_RecvLineBuffer.pop_front();

	//Output command in status window
	CStdString str2;
	if (m_useUTF8)
	{
#ifdef _UNICODE
	str2 = ConvFromNetwork(str);
#else
	str2 = ConvToLocal(ConvFromNetwork(str));
#endif
	}
	else
	{
#ifdef _UNICODE
	str2 = ConvFromLocal(str);
#else
	str2 = str;
#endif
	}
	
	//Hide passwords if the server admin wants to.
	if (!str2.Left(5).CompareNoCase(_T("PASS ")))
	{	if (m_pOwner->m_pOptions->GetOptionVal(OPTION_LOGSHOWPASS))
			SendStatus(str2, 2);
		else
		{
			CStdString msg = str2;
			for (int i = 5; i < msg.GetLength(); i++)
				msg[i] = '*';
			SendStatus(msg, 2);
		}
	}
	else
		SendStatus(str2, 2);

	//Split command and arguments
	int pos = str2.Find(_T(" "));
	if (pos != -1)
	{
		command = str2.Left(pos);
		if (pos == str2.GetLength() - 1)
			args = _T("");
		else
		{
			args = str2.Mid(pos + 1);
			if (args == _T(""))
			{
				Send(_T("501 Syntax error, failed to decode string"));
				return FALSE;
			}
		}
	}
	else
	{
		args = _T("");
		command = str2;
	}
	if (command == _T(""))
		return FALSE;
	command.MakeUpper();
	return TRUE;
}

void CControlSocket::SendStatus(LPCTSTR status, int type)
{
	t_statusmsg *msg = new t_statusmsg;
	_tcscpy(msg->ip, m_RemoteIP);
	GetLocalTime(&msg->time);
	if (!m_status.loggedon)
	{
		msg->user = new TCHAR[16];
		_tcscpy(msg->user, _T("(not logged in)"));
	}
	else
	{
		msg->user = new TCHAR[_tcslen(m_status.user) + 1];
		_tcscpy(msg->user, m_status.user);
	}
	msg->userid = m_userid;
	msg->type = type;
	msg->status = new TCHAR[_tcslen(status) + 1];
	_tcscpy(msg->status, status);
	m_pOwner->SendNotification(FSM_STATUSMESSAGE, (LPARAM)msg);
}

BOOL CControlSocket::Send(LPCTSTR str, bool sendStatus /*=true*/)
{
	if (sendStatus)
		SendStatus(str, 3);

	char* buffer;
	int len;
	if (m_useUTF8)
	{
		char* utf8 = ConvToNetwork(str);
		if (!utf8)
		{
			Close();
			SendStatus(_T("Failed to convert reply to UTF-8"), 1);
			m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

			return false;
		}

		buffer = new char[strlen(utf8) + 3];
		strcpy(buffer, utf8);
		strcat(buffer, "\r\n");
		len = strlen(buffer);
		delete [] utf8;
	}
	else
	{
		CStdStringA local = ConvToLocal(str);
		if (local == "")
		{
			Close();
			SendStatus(_T("Failed to convert reply to local charset"), 1);
			m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

			return false;
		}

		buffer = new char[strlen(local) + 3];
		strcpy(buffer, local);
		strcat(buffer, "\r\n");
		len = strlen(buffer);
	}


	//Add line to back of send buffer if it's not empty
	if (m_pSendBuffer)
	{
		char *tmp = m_pSendBuffer;
		m_pSendBuffer = new char[m_nSendBufferLen + len];
		memcpy(m_pSendBuffer, tmp, m_nSendBufferLen);
		memcpy(m_pSendBuffer+m_nSendBufferLen, buffer, len);
		delete [] tmp;
		m_nSendBufferLen += len;
		delete [] buffer;
		return TRUE;
	}

	int nLimit = GetSpeedLimit(download);
	if (!nLimit)
	{
		if (!m_pSendBuffer)
		{
			m_pSendBuffer = buffer;
			m_nSendBufferLen = len;
		}
		else
		{
			char *tmp = m_pSendBuffer;
			m_pSendBuffer = new char[m_nSendBufferLen + len];
			memcpy(m_pSendBuffer, tmp, m_nSendBufferLen);
			memcpy(m_pSendBuffer+m_nSendBufferLen, buffer, len);
			delete [] tmp;
			m_nSendBufferLen += len;
			delete [] buffer;
		}
		return TRUE;
	}
	int numsend = nLimit;
	if (numsend == -1 || len < numsend)
		numsend = len;

	int res = CAsyncSocketEx::Send(buffer, numsend);
	if (res==SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK)
	{
		res = 0;
	}
	else if (!res || res==SOCKET_ERROR)
	{
		delete [] buffer;
		Close();
		SendStatus(_T("could not send reply, disconnected."), 0);
		m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
		return FALSE;
	}

	if (nLimit != -1)
		m_SlQuotas[download].nTransferred += res;

	if (res != len)
	{
		if (!m_pSendBuffer)
		{
			m_pSendBuffer = new char[len-res];
			memcpy(m_pSendBuffer, buffer+res, len-res);
			m_nSendBufferLen = len-res;
		}
		else
		{
			char *tmp = m_pSendBuffer;
			m_pSendBuffer = new char[m_nSendBufferLen + len - res];
			memcpy(m_pSendBuffer, tmp, m_nSendBufferLen);
			memcpy(m_pSendBuffer+m_nSendBufferLen, buffer+res, len-res);
			delete [] tmp;
			m_nSendBufferLen += len-res;
		}
		TriggerEvent(FD_WRITE);
	}
	delete [] buffer;

	m_pOwner->IncSendCount(res);
	return TRUE;
}

void CControlSocket::OnClose(int nErrorCode)
{
	Close();
	SendStatus(_T("disconnected."), 0);
	m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
	CAsyncSocketEx::OnClose(nErrorCode);
}

#define COMMAND_USER	0
#define COMMAND_PASS	1
#define COMMAND_QUIT	2
#define COMMAND_CWD		3
#define COMMAND_PWD		4
#define COMMAND_PORT	5
#define COMMAND_PASV	6
#define COMMAND_TYPE	7
#define COMMAND_LIST	8
#define COMMAND_REST	9
#define COMMAND_CDUP	10
#define COMMAND_RETR	11
#define COMMAND_STOR	12
#define COMMAND_SIZE	13
#define COMMAND_DELE	14
#define COMMAND_RMD		15
#define COMMAND_MKD		16
#define COMMAND_RNFR	17
#define COMMAND_RNTO	18
#define COMMAND_ABOR	19
#define COMMAND_SYST	20
#define COMMAND_NOOP	21
#define COMMAND_APPE	22
#define COMMAND_NLST	23
#define COMMAND_MDTM	24
#define COMMAND_XPWD	25
#define COMMAND_XCUP	26
#define COMMAND_XMKD	27
#define COMMAND_XRMD	28
#define COMMAND_NOP		29
#define COMMAND_EPSV	30
#define COMMAND_EPRT	31
#define COMMAND_AUTH	32
#define COMMAND_ADAT	33
#define COMMAND_PBSZ	34
#define COMMAND_PROT	35
#define COMMAND_FEAT	36
#define COMMAND_MODE	37
#define COMMAND_OPTS	38
#define COMMAND_HELP	39
#define COMMAND_ALLO	40
#define COMMAND_MLST	41
#define COMMAND_MLSD	42
#define COMMAND_SITE	43
#define COMMAND_PASVSMC	44 // some bugged SMC routers convert incoming PASV into P@SW
#define COMMAND_STRU	45
#define COMMAND_CLNT	46
#define COMMAND_MFMT	47
#define COMMAND_HASH	48

typedef struct
{
	int nID;
	TCHAR command[5];
	BOOL bHasargs;
	BOOL bValidBeforeLogon;
} t_command;

static const t_command commands[]={	COMMAND_USER, _T("USER"), TRUE,	 TRUE,
									COMMAND_PASS, _T("PASS"), FALSE, TRUE,
									COMMAND_QUIT, _T("QUIT"), FALSE, TRUE,
									COMMAND_CWD,  _T("CWD"),  FALSE, FALSE,
									COMMAND_PWD,  _T("PWD"),  FALSE, FALSE,
									COMMAND_PORT, _T("PORT"), TRUE,  FALSE,
									COMMAND_PASV, _T("PASV"), FALSE, FALSE,
									COMMAND_TYPE, _T("TYPE"), TRUE,  FALSE,
									COMMAND_LIST, _T("LIST"), FALSE, FALSE,
									COMMAND_REST, _T("REST"), TRUE,  FALSE,
									COMMAND_CDUP, _T("CDUP"), FALSE, FALSE,
									COMMAND_RETR, _T("RETR"), TRUE,  FALSE,
									COMMAND_STOR, _T("STOR"), TRUE,  FALSE,
									COMMAND_SIZE, _T("SIZE"), TRUE,  FALSE,
									COMMAND_DELE, _T("DELE"), TRUE,  FALSE,
									COMMAND_RMD,  _T("RMD"),  TRUE,  FALSE,
									COMMAND_MKD,  _T("MKD"),  TRUE,  FALSE,
									COMMAND_RNFR, _T("RNFR"), TRUE,  FALSE,
									COMMAND_RNTO, _T("RNTO"), TRUE,  FALSE,
									COMMAND_ABOR, _T("ABOR"), FALSE, FALSE,
									COMMAND_SYST, _T("SYST"), FALSE, TRUE,
									COMMAND_NOOP, _T("NOOP"), FALSE, FALSE,
									COMMAND_APPE, _T("APPE"), TRUE,  FALSE,
									COMMAND_NLST, _T("NLST"), FALSE, FALSE,
									COMMAND_MDTM, _T("MDTM"), TRUE,  FALSE,
									COMMAND_XPWD, _T("XPWD"), FALSE, FALSE,
									COMMAND_XCUP, _T("XCUP"), FALSE, FALSE,
									COMMAND_XMKD, _T("XMKD"), TRUE,  FALSE,
									COMMAND_XRMD, _T("XRMD"), TRUE,  FALSE,
									COMMAND_NOP,  _T("NOP"),  FALSE, FALSE,
									COMMAND_EPSV, _T("EPSV"), FALSE, FALSE,
									COMMAND_EPRT, _T("EPRT"), TRUE,  FALSE,
									COMMAND_AUTH, _T("AUTH"), TRUE,  TRUE,
									COMMAND_ADAT, _T("ADAT"), TRUE,  TRUE,
									COMMAND_PBSZ, _T("PBSZ"), TRUE,  TRUE,
									COMMAND_PROT, _T("PROT"), TRUE,  TRUE,
									COMMAND_FEAT, _T("FEAT"), FALSE, TRUE,
									COMMAND_MODE, _T("MODE"), TRUE,  FALSE,
									COMMAND_OPTS, _T("OPTS"), TRUE,  FALSE,
									COMMAND_HELP, _T("HELP"), FALSE, TRUE,
									COMMAND_ALLO, _T("ALLO"), FALSE, FALSE,
									COMMAND_MLST, _T("MLST"), FALSE, FALSE,
									COMMAND_MLSD, _T("MLSD"), FALSE, FALSE,
									COMMAND_SITE, _T("SITE"), TRUE,  TRUE,
									COMMAND_PASVSMC, _T("P@SW"), FALSE, FALSE,
									COMMAND_STRU, _T("STRU"), TRUE, FALSE,
									COMMAND_CLNT, _T("CLNT"), TRUE, TRUE,
									COMMAND_MFMT, _T("MFMT"), TRUE, FALSE,
									COMMAND_HASH, _T("HASH"), TRUE, FALSE
						};

void CControlSocket::ParseCommand()
{
	if (m_antiHammeringWaitTime)
		return;

	//Get command
	CStdString command;
	CStdString args;
	if (!GetCommand(command, args))
		return;

	//Check if command is valid
	int nCommandID = -1;
	for (int i = 0; i < (sizeof(commands)/sizeof(t_command)); i++)
	{
		if (command == commands[i].command)
		{
			//Does the command needs an argument?
			if (commands[i].bHasargs && (args == _T("")))
			{
				Send(_T("501 Syntax error"));
				if (!m_RecvLineBuffer.empty())
					m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);
				return;
			}
			//Can it be issued before logon?
			else if (!m_status.loggedon && !commands[i].bValidBeforeLogon)
			{
				Send(_T("530 Please log in with USER and PASS first."));
				if (!m_RecvLineBuffer.empty())
					m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);
				return;
			}
			nCommandID = commands[i].nID;
			break;
		}
	}
	//Command not recognized
	if (nCommandID == -1)
	{
		Send(_T("500 Syntax error, command unrecognized."));
		if (!m_RecvLineBuffer.empty())
			m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);
		return;
	}

	//Now process the commands
	switch (nCommandID)
	{
	case COMMAND_USER:
		{
			AntiHammerIncrease();

			if (m_status.loggedon)
			{
				GetSystemTime(&m_LoginTime);
				DecUserCount(m_status.user);
				m_pOwner->DecIpCount(m_status.ip);
				t_connop *op = new t_connop;
				op->op = USERCONTROL_CONNOP_CHANGEUSER;
				t_connectiondata_changeuser *conndata = new t_connectiondata_changeuser;
				op->data = conndata;
				op->userid = m_userid;
				m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);

				m_status.loggedon = FALSE;
				m_CurrentServerDir = _T("");
			}
			if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLESSL) && m_pOwner->m_pOptions->GetOptionVal(OPTION_ALLOWEXPLICITSSL) &&
				m_pOwner->m_pOptions->GetOptionVal(OPTION_SSLFORCEEXPLICIT) && !m_pSslLayer)
			{
				Send(_T("530 Have to use explicit SSL/TLS before logging on."));
				break;
			}
			RenName = _T("");
			args.MakeLower();
			m_status.user = args;
			if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
			{
				char sendme[4096];

#ifdef _UNICODE
				int res = m_pGssLayer->ProcessCommand("USER", ConvToLocal(args), sendme);
#else
				int res = m_pGssLayer->ProcessCommand("USER", args, sendme);
#endif
				if (res != -1)
				{
					if (DoUserLogin(0, true))
					{
#ifdef _UNICODE
						Send(ConvFromLocal(sendme));
#else
						Send(sendme);
#endif
					}
					else
						m_status.user = _T("");
				}
				break;
			}
			if (!m_pSslLayer)
			{
				CUser user;
				if (m_pOwner->m_pPermissions->CheckUserLogin(m_status.user, _T(""), user, true) && user.ForceSsl())
				{
					m_status.user = _T("");
					Send(_T("530 SSL required"));
					break;
				}
			}
			Send(_T("331 Password required for ") + args);
		}
		break;
	case COMMAND_PASS:
		AntiHammerIncrease();

		if (m_status.loggedon)
			Send(_T("503 Bad sequence of commands."));
		else if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
		{
			char sendme[4096];
#ifdef _UNICODE
			int res = m_pGssLayer->ProcessCommand("PASS", ConvToLocal(m_status.user), ConvToLocal(args), sendme);
#else
			int res = m_pGssLayer->ProcessCommand("PASS", m_status.user, args, sendme);
#endif

			if (res != -1)
			{
				if (DoUserLogin(_T(""), true))
#ifdef _UNICODE
                    Send(ConvFromLocal(sendme));
#else
                    Send(sendme);
#endif
			}
		}
		else if (DoUserLogin(args))
			Send(_T("230 Logged on"));
		break;
	case COMMAND_QUIT:
		m_bQuitCommand = TRUE;
		if (!m_transferstatus.socket || !m_transferstatus.socket->InitCalled())
		{
			Send(_T("221 Goodbye"));
			if (m_pSslLayer)
			{
				if (ShutDown() || WSAGetLastError() != WSAEWOULDBLOCK)
					ForceClose(5);
			}
			else if (CanQuit())
				ForceClose(5);
		}
		break;
	case COMMAND_CWD:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send(_T("501 Syntax error"));
				break;
			}

			if (args == _T(""))
			{
				CStdString str;
				str.Format(_T("250 Broken client detected, missing argument to CWD. \"%s\" is current directory."), m_CurrentServerDir);
				Send(str);
				break;
			}

			int res = m_pOwner->m_pPermissions->ChangeCurrentDir(m_status.user, m_CurrentServerDir, args);
			if (!res)
			{
				CStdString str;
				str.Format(_T("250 CWD successful. \"%s\" is current directory."), m_CurrentServerDir);
				Send(str);
			}
			else if (res & PERMISSION_DENIED)
			{
				CStdString str;
				str.Format(_T("550 CWD failed. \"%s\": Permission denied."), args);
				Send(str);
			}
			else if (res & PERMISSION_INVALIDNAME)
			{
				CStdString str;
				str.Format(_T("550 CWD failed. \"%s\": Filename invalid."), args);
				Send(str);
			}
			else if (res)
			{
				CStdString str;
				str.Format(_T("550 CWD failed. \"%s\": directory not found."), args);
				Send(str);
			}
		}
		break;
	case COMMAND_PWD:
	case COMMAND_XPWD:
		{
			CStdString str;
			str.Format(_T("257 \"%s\" is current directory."), m_CurrentServerDir);
			Send(str);
		}
		break;
	case COMMAND_PORT:
		{
			if (GetFamily() != AF_INET)
			{
				Send(_T("500 You are connected using IPv6. PORT is only for IPv4. You have to use the EPRT command instead."));
				break;
			}

			if (m_transferstatus.socket)
			{
				SendTransferinfoNotification();
				delete m_transferstatus.socket;
				m_transferstatus.socket = 0;
			}
			int count = 0;
			int pos = 0;
			//Convert commas to dots
			args.Replace(_T(","), _T("."));
			while (1)
			{
				pos = args.Find(_T("."), pos);
				if (pos != -1)
					count++;
				else
					break;
				pos++;
			}
			if (count != 5)
			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}
			CStdString ip;
			int port = 0;
			int i = args.ReverseFind('.');
			port = _ttoi(args.Right(args.GetLength() - (i + 1))); //get ls byte of server socket
			args = args.Left(i);
			i = args.ReverseFind('.');
			port += 256 * _ttoi(args.Right(args.GetLength() - (i + 1))); // add ms byte to server socket
			ip = args.Left(i);

#ifdef _UNICODE
			int res = inet_addr(ConvToLocal(ip));
#else
			int res = inet_addr(ip);
#endif
			if (res == INADDR_NONE)
			{
				// Fix: Convert IP in PORT command to int and back to string (strips
				// leading zeros) because some FTP clients prepend zeros to it.
				// inet_addr() thinks this is an octal number and will return INADDR_NONE
				// if 8 or 9 are encountered.
				CStdString decIP;
				ip += _T(".");
				int pos = ip.Find('.');
				while (pos != -1)
				{
					CStdString tmp;
					tmp.Format(_T("%d."), _ttoi(ip.Left(pos)));
					decIP += tmp;

					ip = ip.Mid(pos + 1);
					pos = ip.Find('.');
				}

				ip = decIP.Left(decIP.GetLength() - 1);
#ifdef _UNICODE
				res = inet_addr(ConvToLocal(ip));
#else
				res = inet_addr(ip);
#endif
			}

			if (res == INADDR_NONE || port < 1 || port > 65535)
 			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}

			if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ACTIVE_IGNORELOCAL))
			{
				CStdString peerAddr;
				UINT peerPort = 0;
				if (GetPeerName(peerAddr, peerPort))
				{
					if (!IsRoutableAddress(ip) && IsRoutableAddress(peerAddr))
						ip = peerAddr;
				}
			}

			m_transferstatus.ip = ip;
			m_transferstatus.port = port;
			m_transferstatus.pasv = 0;
			m_transferstatus.family = AF_INET;
			Send(_T("200 Port command successful"));
			break;
		}
	case COMMAND_PASV:
	case COMMAND_PASVSMC:
		{
			if (GetFamily() != AF_INET)
			{
				Send(_T("500 You are connected using IPv6. PASV is only for IPv4. You have to use the EPSV command instead."));
				break;
			}

			if (m_transferstatus.socket)
			{
				SendTransferinfoNotification();
				delete m_transferstatus.socket;
			}
			m_transferstatus.family = AF_INET;
			m_transferstatus.socket = new CTransferSocket(this);

			unsigned int retries = 3;
			unsigned int port = 0;

			CStdString pasvIP = GetPassiveIP();
			if (pasvIP == _T(""))
			{
				delete m_transferstatus.socket;
				m_transferstatus.socket = NULL;
				Send(_T("421 Can't create socket"));
				m_transferstatus.pasv = -1;
				break;
			}

			while (retries > 0)
			{
				if (m_pOwner->m_pOptions->GetOptionVal(OPTION_USECUSTOMPASVPORT))
				{
					static UINT customPort = 0;
					unsigned int minPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMINPORT);
					unsigned int maxPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMAXPORT);
					if (minPort > maxPort) {
						unsigned int temp = minPort;
						minPort = maxPort;
						maxPort = temp;
					}
					if (customPort < minPort || customPort > maxPort) {
						customPort = minPort;
					}
					port = customPort;

					++customPort;
				} else {
					port = 0;
				}
				if (m_transferstatus.socket->Create(port, SOCK_STREAM, FD_ACCEPT))
					break;
				--retries;
			}
			if (retries <= 0) {
				delete m_transferstatus.socket;
				m_transferstatus.socket = NULL;
				Send(_T("421 Can't create socket"));
				m_transferstatus.pasv = -1;
				break;
			}

			if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
				m_transferstatus.socket->UseGSS(m_pGssLayer);

			if (m_pSslLayer && m_bProtP)
				m_transferstatus.socket->UseSSL(m_pSslLayer->GetContext());

			if (!m_transferstatus.socket->Listen())
			{
				delete m_transferstatus.socket;
				m_transferstatus.socket = NULL;
				Send(_T("421 Can't create socket"));
				m_transferstatus.pasv = -1;
				break;
			}

			//Now retrieve the port
			CStdString dummy;
			if (m_transferstatus.socket->GetSockName(dummy, port))
			{
				//Reformat the ip
				pasvIP.Replace(_T("."), _T(","));
				//Put the answer together
				CStdString str;
				if (nCommandID == COMMAND_PASVSMC)
					str.Format(_T("227 Warning: Router with P@SW bug detected. Entering Passive Mode (%s,%d,%d)"), pasvIP, port / 256, port % 256);
				else
					str.Format(_T("227 Entering Passive Mode (%s,%d,%d)"), pasvIP, port / 256, port % 256);
				Send(str);
				m_transferstatus.pasv = 1;
			}
			else
			{
				delete m_transferstatus.socket;
				m_transferstatus.socket = NULL;
				Send(_T("421 Can't create socket"));
				m_transferstatus.pasv = -1;
				break;
			}
			
			break;
		}
	case COMMAND_TYPE:
		{
			args.MakeUpper();
			if (args[0] != 'I' && args[0] != 'A' && args != _T("L 8"))
			{
				Send(_T("501 Unsupported type. Supported types are I, A and L 8"));
				break;
			}
			m_transferstatus.type = (args[0] == 'A') ? 1 : 0;
			CStdString str;
			str.Format(_T("200 Type set to %s"), args);
			Send(str);
		}
		break;
	case COMMAND_LIST:
		if (m_transferstatus.pasv == -1)
		{
			Send(_T("503 Bad sequence of commands."));
			break;
		}
		if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
		{
			Send(_T("503 Bad sequence of commands."));
			break;
		}
		if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
		{
			Send(_T("521 PROT P required"));
			break;
		}
		else
		{
			//Check args, currently only supported argument is the directory which will be listed.
			CStdString dirToList;
			args.TrimLeft(_T(" "));
			args.TrimRight(_T(" "));
			if (args != _T(""))
			{
				BOOL bBreak = FALSE;
				while (args[0] == '-') //No parameters supported
				{
					if (args.GetLength() < 2)
					{ //Dash without param
						Send(_T("501 Syntax error"));
						bBreak = TRUE;
						break;
					}

					int pos = args.Find(' ');
					CStdString params;
					if (pos != -1)
					{
						params = args.Left(1);
						params = params.Left(pos - 1);
						args = args.Mid(pos + 1);
						args.TrimLeft(_T(" "));
					}
					else
						args = _T("");
					while (params != _T(""))
					{
						//Some parameters are not support
						if (params[0] == 'R')
						{
							Send(_T("504 Command not implemented for that parameter"));
							bBreak = TRUE;
							break;
						}
						//Ignore other parameters
						params = params.Mid(1);
					}

					if (args == _T(""))
						break;
				}
				if (bBreak)
					break;
				if (args != _T(""))
				{
					//Unquote args
					if (!UnquoteArgs(args))
					{
						Send(_T("501 Syntax error"));
						break;
					}

					dirToList = args;
				}
			}

			t_dirlisting *pResult;
			CStdString physicalDir, logicalDir;
			int error = m_pOwner->m_pPermissions->GetDirectoryListing(m_status.user, m_CurrentServerDir, dirToList, pResult, physicalDir, logicalDir, CPermissions::AddLongListingEntry, m_useUTF8);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied."));
				ResetTransferstatus();
				break;
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
				break;
			}
			else if (error)
			{
				Send(_T("550 Directory not found."));
				ResetTransferstatus();
				break;
			}

			if (!m_transferstatus.pasv)
			{
				if (m_transferstatus.socket)
				{
					SendTransferinfoNotification();
					delete m_transferstatus.socket;
				}
				CTransferSocket *transfersocket = new CTransferSocket(this);
				m_transferstatus.socket = transfersocket;
				transfersocket->Init(pResult, TRANSFERMODE_LIST);
				if (m_transferMode == mode_zlib)
				{
					if (!transfersocket->InitZLib(m_zlibLevel))
					{
						Send(_T("550 could not initialize zlib, please use MODE S instead"));
						ResetTransferstatus();
						break;
					}
				}

				if (!CreateTransferSocket(transfersocket))
					break;

				SendTransferinfoNotification(TRANSFERMODE_LIST, ConvToLocal(physicalDir), ConvToLocal(logicalDir));
				Send(_T("150 Opening data channel for directory list."));
			}
			else
			{
				if (!m_transferstatus.socket)
				{
					CPermissions::DestroyDirlisting(pResult);
					Send(_T("503 Bad sequence of commands."));
					break;
				}
				m_transferstatus.socket->Init(pResult, TRANSFERMODE_LIST);
				if (m_transferMode == mode_zlib)
				{
					if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
					{
						Send(_T("550 could not initialize zlib, please use MODE S instead"));
						ResetTransferstatus();
						break;
					}
				}

				SendTransferinfoNotification(TRANSFERMODE_LIST, ConvToLocal(physicalDir), ConvToLocal(logicalDir));
				m_transferstatus.socket->PasvTransfer();
			}
		}
		break;
	case COMMAND_REST:
		{
			BOOL error = FALSE;
			for (int i = 0; i < args.GetLength(); i++)
				if (args[i] < '0' || args[i] > '9')
				{
					error = TRUE;
					break;
				}
			if (error)
			{
				Send(_T("501 Bad parameter. Numeric value required"));
				break;
			}
			m_transferstatus.rest = _ttoi64(args);
			CStdString str;
			str.Format(_T("350 Rest supported. Restarting at %I64d"), m_transferstatus.rest);
			Send(str);
		}
		break;
	case COMMAND_CDUP:
	case COMMAND_XCUP:
		{
			CStdString dir = _T("..");
			int res = m_pOwner->m_pPermissions->ChangeCurrentDir(m_status.user, m_CurrentServerDir, dir);
			if (!res)
			{
				CStdString str;
				str.Format(_T("200 CDUP successful. \"%s\" is current directory."), m_CurrentServerDir);
				Send(str);
			}
			else if (res & PERMISSION_DENIED)
				Send(_T("550 CDUP failed, permission denied."));
			else if (res & PERMISSION_INVALIDNAME)
				Send(_T("550 CDUP failed, filename invalid."));
			else if (res)
				Send(_T("550 CDUP failed, directory not found."));
		}
		break;
	case COMMAND_RETR:
		{
			if (m_transferstatus.pasv == -1)
			{
				Send(_T("503 Bad sequence of commands."));
				break;
			}
			if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
			{
				Send(_T("503 Bad sequence of commands."));
				break;
			}
			if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
			{
				Send(_T("521 PROT P required"));
				break;
			}
			//Much more checks

			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}


			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_READ, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied"));
				ResetTransferstatus();
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
			}
			else if (error)
			{
				Send(_T("550 File not found"));
				ResetTransferstatus();
			}
			else
			{
				if (!m_transferstatus.pasv)
				{
					if (m_transferstatus.socket)
					{
						SendTransferinfoNotification();
						delete m_transferstatus.socket;
					}
					CTransferSocket *transfersocket = new CTransferSocket(this);
					m_transferstatus.socket = transfersocket;
					transfersocket->Init(physicalFile, TRANSFERMODE_SEND, m_transferstatus.rest);
					if (m_transferMode == mode_zlib)
					{
						if (!transfersocket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					if (!CreateTransferSocket(transfersocket))
						break;

					__int64 totalSize;
					if (!GetLength64(physicalFile, totalSize))
						totalSize = -1;
					SendTransferinfoNotification(TRANSFERMODE_SEND, physicalFile, logicalFile, m_transferstatus.rest, totalSize);

					Send(_T("150 Opening data channel for file transfer."));
				}
				else
				{
					if (!m_transferstatus.socket)
					{
						Send(_T("503 Bad sequence of commands."));
						break;
					}

					m_transferstatus.socket->Init(physicalFile, TRANSFERMODE_SEND, m_transferstatus.rest);
					if (m_transferMode == mode_zlib)
					{
						if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					__int64 totalSize;
					if (!GetLength64(physicalFile, totalSize))
						totalSize = -1;
					SendTransferinfoNotification(TRANSFERMODE_SEND, physicalFile, logicalFile, m_transferstatus.rest, totalSize);

					m_transferstatus.socket->PasvTransfer();
				}
				GetSystemTime(&m_LastTransferTime);
			}
			break;
		}
	case COMMAND_STOR:
		{
			if (m_transferstatus.pasv == -1)
			{
				Send(_T("503 Bad sequence of commands."));
				break;
			}
			if(!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
			{
				Send(_T("503 Bad sequence of commands."));
				break;
			}
			if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
			{
				Send(_T("521 PROT P required"));
				break;
			}
			//Much more checks

			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, m_transferstatus.rest ? FOP_APPEND : FOP_WRITE, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied"));
				ResetTransferstatus();
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
			}
			else if (error)
			{
				Send(_T("550 Filename invalid"));
				ResetTransferstatus();
			}
			else
			{
				if (!m_transferstatus.pasv)
				{
					CTransferSocket *transfersocket = new CTransferSocket(this);
					transfersocket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
					m_transferstatus.socket = transfersocket;
					if (m_transferMode == mode_zlib)
					{
						if (!transfersocket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					if (!CreateTransferSocket(transfersocket))
						break;

					SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);
					Send(_T("150 Opening data channel for file transfer."));
				}
				else
				{
					if (!m_transferstatus.socket)
					{
						Send(_T("503 Bad sequence of commands."));
						break;
					}

					m_transferstatus.socket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
                    if (m_transferMode == mode_zlib)
					{
						if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}
					SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);
					m_transferstatus.socket->PasvTransfer();
				}
				GetSystemTime(&m_LastTransferTime);
			}
		}
		break;
	case COMMAND_SIZE:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_LIST, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
				Send(_T("550 Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else if (error)
				Send(_T("550 File not found"));
			else
			{
				CStdString str;
				_int64 length;
				if (GetLength64(physicalFile, length))
					str.Format(_T("213 %I64d"), length);
				else
					str = _T("550 File not found");
				Send(str);
			}
		}
		break;
	case COMMAND_DELE:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send(_T("501 Syntax error"));
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_DELETE, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
				Send(_T("550 Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else if (error)
				Send(_T("550 File not found"));
			else
			{
				bool success = DeleteFile(physicalFile) == TRUE;
				if (!success && GetLastError() == ERROR_ACCESS_DENIED)
				{
					DWORD attr = GetFileAttributes(physicalFile);
					if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_READONLY)
					{
						attr &= ~FILE_ATTRIBUTE_READONLY;
						SetFileAttributes(physicalFile, attr);

						success = DeleteFile(physicalFile) == TRUE;
					}
				}
				if (!success)
					Send(_T("500 Failed to delete the file."));
				else
					Send(_T("250 File deleted successfully"));
			}
		}
		break;
	case COMMAND_RMD:
	case COMMAND_XRMD:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_DELETE, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
				Send(_T("550 Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else if (error)
				Send(_T("550 Directory not found"));
			else
			{
				if (!RemoveDirectory(physicalFile))
				{
					if (GetLastError() == ERROR_DIR_NOT_EMPTY)
						Send(_T("550 Directory not empty."));
					else
						Send(_T("450 Internal error deleting the directory."));
				}
				else
					Send(_T("250 Directory deleted successfully"));
			}
		}
		break;
	case COMMAND_MKD:
	case COMMAND_XMKD:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_CREATE, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
				Send(_T("550 Can't create directory. Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else if (error & PERMISSION_DOESALREADYEXIST && (error & PERMISSION_FILENOTDIR)!=PERMISSION_FILENOTDIR)
				Send(_T("550 Directory already exists"));
			else if (error & PERMISSION_FILENOTDIR)
				Send(_T("550 File with same name already exists"));
			else if (error)
				Send(_T("550 Directoryname not valid"));
			else
			{
				CStdString str;
				BOOL res = FALSE;
				BOOL bReplySent = FALSE;
				physicalFile += _T("\\");
				while (physicalFile != _T(""))
				{
					CStdString piece = physicalFile.Left(physicalFile.Find('\\')+1);
					if (piece.Right(2) == _T(".\\"))
					{
						Send(_T("550 Directoryname not valid"));
						bReplySent = TRUE;
						break;
					}
					str += piece;
					physicalFile = physicalFile.Mid(physicalFile.Find('\\') + 1);
					res = CreateDirectory(str, 0);
				}
				if (!bReplySent)
					if (!res)//CreateDirectory(result+"\\",0))
					{
						int error = GetLastError();
						if (error == ERROR_ALREADY_EXISTS)
							Send(_T("550 Directory already exists"));
						else
							Send(_T("450 Internal error creating the directory."));
					}
					else
						Send(_T("257 \"") + logicalFile + _T("\" created successfully"));
			}
		}
		break;
	case COMMAND_RNFR:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			RenName = _T("");

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_DELETE, physicalFile, logicalFile);
			if (!error)
			{
				RenName = physicalFile;
				bRenFile = TRUE;
				Send(_T("350 File exists, ready for destination name."));
				break;
			}
			else if (error & PERMISSION_DENIED)
				Send(_T("550 Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else
			{
				int error2 = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_DELETE, physicalFile, logicalFile);
				if (!error2)
				{
					RenName = physicalFile;
					bRenFile = FALSE;
					Send(_T("350 Directory exists, ready for destination name."));
				}
				else if (error2 & PERMISSION_DENIED)
					Send(_T("550 Permission denied"));
				else if (error2 & PERMISSION_INVALIDNAME)
					Send(_T("550 Filename invalid."));
				else
					Send(_T("550 file/directory not found"));
				break;
			}
		}
		break;
	case COMMAND_RNTO:
		{
			if (RenName == _T(""))
			{
				Send(_T("503 Bad sequence of commands!"));
				break;
			}

			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			if (bRenFile)
			{
				CStdString physicalFile, logicalFile;
				int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_CREATENEW, physicalFile, logicalFile);
				if (error)
					RenName = _T("");
				if (error & PERMISSION_DENIED)
					Send(_T("550 Permission denied"));
				else if (error & PERMISSION_INVALIDNAME)
					Send(_T("553 Filename invalid."));
				else if (error & PERMISSION_DOESALREADYEXIST && (error & PERMISSION_DIRNOTFILE)!=PERMISSION_DIRNOTFILE)
					Send(_T("553 file exists"));
				else if (error)
					Send(_T("553 Filename invalid"));
				else
				{
					if (!MoveFile(RenName, physicalFile))
						Send(_T("450 Internal error renaming the file"));
					else
						Send(_T("250 file renamed successfully"));
				}
			}
			else
			{
				CStdString physicalFile, logicalFile;
				int error = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_CREATE, physicalFile, logicalFile);
				if (error)
					RenName = _T("");
				if (error & PERMISSION_DENIED)
					Send(_T("550 Permission denied"));
				else if (error & PERMISSION_INVALIDNAME)
					Send(_T("550 Filename invalid."));
				else if (error & PERMISSION_DOESALREADYEXIST && (error & PERMISSION_FILENOTDIR)!=PERMISSION_FILENOTDIR)
					Send(_T("550 file exists"));
				else if (error)
					Send(_T("550 Filename invalid"));
				else
				{
					if (!MoveFile(RenName, physicalFile))
						Send(_T("450 Internal error renaming the file"));
					else
						Send(_T("250 file renamed successfully"));
				}
			}
		}
		break;
	case COMMAND_ABOR:
		{
			if (m_transferstatus.socket)
			{
				if (m_transferstatus.socket->Started())
					Send(_T("426 Connection closed; transfer aborted."));
			}
			Send(_T("226 ABOR command successful"));
			ResetTransferstatus();
		break;
		}
	case COMMAND_SYST:
		Send(_T("215 UNIX emulated by FileZilla"));
		break;
	case COMMAND_NOOP:
	case COMMAND_NOP:
		Send(_T("200 OK"));
		break;
	case COMMAND_APPE:
		{
			if (m_transferstatus.pasv == -1)
			{
				Send(_T("503 Bad sequence of commands."));
				break;
			}
			if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
			{
				Send(_T("503 Bad sequence of commands."));
				break;
			}
			if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
			{
				Send(_T("521 PROT P required"));
				break;
			}
			//Much more checks

			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_APPEND, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied"));
				ResetTransferstatus();
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
			}
			else if (error)
			{
				Send(_T("550 Filename invalid"));
				ResetTransferstatus();
			}
			else
			{
				_int64 size = 0;
				if (!GetLength64(physicalFile, size))
					size = 0;

				m_transferstatus.rest = size;

				if (!m_transferstatus.pasv)
				{
					CTransferSocket *transfersocket = new CTransferSocket(this);
					transfersocket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
					m_transferstatus.socket = transfersocket;
					if (m_transferMode == mode_zlib)
					{
						if (!transfersocket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					if (!CreateTransferSocket(transfersocket))
						break;

					SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);

					CStdString str;
					str.Format(_T("150 Opening data channel for file transfer, restarting at offset %I64d"), size);
					Send(str);
				}
				else
				{
					if (!m_transferstatus.socket)
					{
						Send(_T("503 Bad sequence of commands."));
						break;
					}
					m_transferstatus.socket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
					if (m_transferMode == mode_zlib)
					{
						if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}
					SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);
					m_transferstatus.socket->PasvTransfer();
				}
				GetSystemTime(&m_LastTransferTime);
			}
		}
		break;
	case COMMAND_NLST:
		if (m_transferstatus.pasv == -1)
		{
			Send(_T("503 Bad sequence of commands."));
			break;
		}
		if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
		{
			Send(_T("503 Bad sequence of commands."));
			break;
		}
		if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
		{
			Send(_T("521 PROT P required"));
			break;
		}
		//Much more checks
		else
		{
			//Check args, currently only supported argument is the directory which will be listed.
			if (args != _T(""))
			{
				BOOL bBreak = FALSE;
				while (args[0] == '-') //No parameters supported
				{
					if (args.GetLength() < 2)
					{ //Dash without param
						Send(_T("501 Syntax error"));
						bBreak = TRUE;
						break;
					}

					int pos = args.Find(' ');
					CStdString params;
					if (pos != -1)
					{
						params = args.Left(1);
						params = params.Left(pos-1);
						args = args.Mid(pos+1);
						args.TrimLeft(_T(" "));
					}
					else
						args = _T("");
					while (params != _T(""))
					{
						//Some parameters are not support
						if (params[0] == 'R')
						{
							Send(_T("504 Command not implemented for that parameter"));
							bBreak = TRUE;
							break;
						}
						//Ignore other parameters
						params = params.Mid(1);
					}

					if (args == _T(""))
						break;
				}
				if (bBreak)
					break;
				if (args != _T(""))
				{
					//Unquote args
					if (!UnquoteArgs(args))
					{
						Send( _T("501 Syntax error") );
						break;
					}
				}
			}

			t_dirlisting *pResult;
			CStdString physicalDir, logicalDir;
			int error = m_pOwner->m_pPermissions->GetDirectoryListing(m_status.user, m_CurrentServerDir, args, pResult, physicalDir, logicalDir, CPermissions::AddShortListingEntry, m_useUTF8);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied"));
				ResetTransferstatus();
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
			}
			else if (error)
			{
				Send(_T("550 Directory not found"));
				ResetTransferstatus();
			}
			else
			{
				if (!m_transferstatus.pasv)
				{
					CTransferSocket *transfersocket = new CTransferSocket(this);
					m_transferstatus.socket = transfersocket;
					transfersocket->Init(pResult, TRANSFERMODE_NLST);
					if (m_transferMode == mode_zlib)
					{
						if (!transfersocket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					if (!CreateTransferSocket(transfersocket))
						break;

					SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir); // Use TRANSFERMODE_LIST instead of TRANSFERMODE_NLST.
					Send(_T("150 Opening data channel for directory list."));
				}
				else
				{
					if (!m_transferstatus.socket)
					{
						CPermissions::DestroyDirlisting(pResult);
						Send(_T("503 Bad sequence of commands."));
						break;
					}
					m_transferstatus.socket->Init(pResult, TRANSFERMODE_NLST );
					if (m_transferMode == mode_zlib)
					{
						if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}
					SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir); // Use TRANSFERMODE_LIST instead of TRANSFERMODE_NLST.
					m_transferstatus.socket->PasvTransfer();
				}
			}
		}
		break;
	case COMMAND_MDTM:
		{
			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_LIST, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
				Send(_T("550 Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else if (error & 2)
				Send(_T("550 File not found"));
			else
			{
				CFileStatus64 status;
				GetStatus64(physicalFile, status);
				CStdString str;
				SYSTEMTIME time;
				FileTimeToSystemTime(&status.m_mtime, &time);
				str.Format(_T("213 %04d%02d%02d%02d%02d%02d"),
							time.wYear,
							time.wMonth,
							time.wDay,
							time.wHour,
							time.wMinute,
							time.wSecond);
				Send(str);
			}
		}
		break;
	case COMMAND_EPSV:
		{
			if (m_transferstatus.socket)
			{
				SendTransferinfoNotification();
				delete m_transferstatus.socket;
			}
			m_transferstatus.family = (GetFamily() == AF_INET) ? 1 : 2;
			m_transferstatus.socket = new CTransferSocket(this);

			unsigned int port = 0;
			unsigned int retries = 3;
			while (retries > 0) {
				if (m_pOwner->m_pOptions->GetOptionVal(OPTION_USECUSTOMPASVPORT)) {
					static UINT customPort = 0;
					unsigned int minPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMINPORT);
					unsigned int maxPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMAXPORT);
					if (minPort > maxPort) {
						unsigned int temp = minPort;
						minPort = maxPort;
						maxPort = temp;
					}
					if (customPort < minPort || customPort > maxPort) {
						customPort = minPort;
					}
					port = customPort;
					++customPort;
				}
				if (m_transferstatus.socket->Create(port, SOCK_STREAM, FD_ACCEPT, 0, GetFamily()))
				{
					break;
				}
				--retries;
			}
			if (retries <= 0) {
				delete m_transferstatus.socket;
				m_transferstatus.socket=0;
				Send(_T("421 Can't create socket"));
				break;
			}

			if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
				m_transferstatus.socket->UseGSS(m_pGssLayer);

			if (m_pSslLayer && m_bProtP)
				m_transferstatus.socket->UseSSL(m_pSslLayer->GetContext());

			if (!m_transferstatus.socket->Listen())
			{
				delete m_transferstatus.socket;
				m_transferstatus.socket=0;
				Send(_T("421 Can't create socket"));
				m_transferstatus.pasv = -1;
				break;
			}

			//Now retrieve the port
			CStdString dummy;
			if (m_transferstatus.socket->GetSockName(dummy, port))
			{
				//Put the answer together
				CStdString str;
				str.Format(_T("229 Entering Extended Passive Mode (|||%d|)"), port);
				Send(str);
				m_transferstatus.pasv=1;
			}
			else
			{
				delete m_transferstatus.socket;
				m_transferstatus.socket=0;
				Send(_T("421 Can't create socket"));
				m_transferstatus.pasv = -1;
			}
			break;
		}
	case COMMAND_EPRT:
		{
			if (m_transferstatus.socket)
			{
				SendTransferinfoNotification();
				delete m_transferstatus.socket;
				m_transferstatus.socket=0;
			}

			if (args[0] != '|')
			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}
			args = args.Mid(1);

			int pos = args.Find('|');
			if (pos < 1 || (pos>=(args.GetLength()-1)))
			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}
			int protocol = _ttoi(args.Left(pos));

			bool ipv6Allowed = m_pOwner->m_pOptions->GetOptionVal(OPTION_DISABLE_IPV6) == 0;
			if (protocol != 1 && (protocol != 2 || !ipv6Allowed))
			{
				if (ipv6Allowed)
					Send(_T("522 Extended Port Failure - unknown network protocol. Supported protocols: (1,2)"));
				else
					Send(_T("522 Extended Port Failure - unknown network protocol. Supported protocols: (1)"));
				m_transferstatus.pasv = -1;
				break;
			}
			args = args.Mid(pos + 1);

			pos = args.Find('|');
			if (pos < 1 || (pos>=(args.GetLength()-1)))
			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}
			CStdString ip = args.Left(pos);
			if (protocol == 1)
			{
#ifdef _UNICODE
				if (inet_addr(ConvToLocal(ip)) == INADDR_NONE)
#else
				if (inet_addr(ip) == INADDR_NONE)
#endif
				{
					Send(_T("501 Syntax error, not a valid IPv4 address"));
					m_transferstatus.pasv = -1;
					break;
				}
			}
			else
			{
				ip = GetIPV6LongForm(ip);
				if (ip.IsEmpty())
				{
					Send(_T("501 Syntax error, not a valid IPv6 address"));
					m_transferstatus.pasv = -1;
					break;
				}
			}
			args = args.Mid(pos + 1);

			pos = args.Find('|');
			if (pos < 1)
			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}
			int port = _ttoi(args.Left(pos));
			if (port < 1 || port > 65535)
			{
				Send(_T("501 Syntax error"));
				m_transferstatus.pasv = -1;
				break;
			}

			m_transferstatus.port = port;
			m_transferstatus.ip = ip;
			m_transferstatus.family = (protocol == 1) ? AF_INET : AF_INET6;

			m_transferstatus.pasv = 0;
			Send(_T("200 Port command successful"));
			break;
		}
	case COMMAND_AUTH:
		{
			if (m_nRecvBufferPos || m_RecvLineBuffer.size() ) {
				Send(_T("503 Bad sequence of commands. Received additional data after the AUTH command before this reply could be sent."));
				ForceClose(-1);
				break;
			}

			if (m_pGssLayer)
			{
				Send(_T("534 Authentication type already set to GSSAPI"));
				break;
			}
			else if (m_pSslLayer)
			{
				Send(_T("534 Authentication type already set to SSL"));
				break;
			}
			args.MakeUpper();

			if (args == _T("GSSAPI"))
			{
				if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_USEGSS))
				{
					Send(_T("502 GSSAPI authentication not implemented"));
					break;
				}

				m_pGssLayer = new CAsyncGssSocketLayer;
				BOOL res = AddLayer(m_pGssLayer);
				if (res)
				{
					res = m_pGssLayer->InitGSS(FALSE, (BOOL)m_pOwner->m_pOptions->GetOptionVal(OPTION_GSSPROMPTPASSWORD));
					if (!res)
						SendStatus(_T("Unable to init GSS"), 1);
				}
				if (!res)
				{
					RemoveAllLayers();
					delete m_pGssLayer;
					m_pGssLayer = NULL;
					Send(_T("431 Could not initialize GSSAPI libraries"));
					break;
				}

				Send(_T("334 Using authentication type GSSAPI; ADAT must follow"));
			}
			else if (args == _T("SSL") || args == _T("TLS"))
			{
				if (m_pSslLayer)
				{
					Send(_T("503 Already using SSL/TLS"));
					break;
				}

				if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLESSL) || !m_pOwner->m_pOptions->GetOptionVal(OPTION_ALLOWEXPLICITSSL))
				{
					Send(_T("502 SSL/TLS authentication not allowed"));
					break;
				}

				m_pSslLayer = new CAsyncSslSocketLayer;
				BOOL res = AddLayer(m_pSslLayer);

				if (res)
				{
					CString error;
#ifdef _UNICODE
					int res = m_pSslLayer->SetCertKeyFile(ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS)), &error);
#else
					int res = m_pSslLayer->SetCertKeyFile(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS), &error);
#endif
					if (res == SSL_FAILURE_LOADDLLS)
						SendStatus(_T("Failed to load SSL libraries"), 1);
					else if (res == SSL_FAILURE_INITSSL)
						SendStatus(_T("Failed to initialize SSL libraries"), 1);
					else if (res == SSL_FAILURE_VERIFYCERT)
					{
						if (error != _T(""))
							SendStatus(error, 1);
						else
							SendStatus(_T("Failed to set certificate and private key"), 1);
					}
					if (res)
					{
						RemoveAllLayers();
						delete m_pSslLayer;
						m_pSslLayer = NULL;
						Send(_T("431 Could not initialize SSL connection"));
						break;
					}
				}

				if (res)
				{
					int code = m_pSslLayer->InitSSLConnection(false);
					if (code == SSL_FAILURE_LOADDLLS)
						SendStatus(_T("Failed to load SSL libraries"), 1);
					else if (code == SSL_FAILURE_INITSSL)
						SendStatus(_T("Failed to initialize SSL library"), 1);

					res = (code == 0);
				}

				if (res)
				{
					if (args == _T("SSL"))
					{
						SendStatus(_T("234 Using authentication type SSL"), 3);
						static const char* reply = "234 Using authentication type SSL\r\n";
						const int len = strlen(reply);
						res = (m_pSslLayer->SendRaw(reply, len) == len);
					}
					else // TLS
					{
						SendStatus(_T("234 Using authentication type TLS"), 3);
						static const char* reply = "234 Using authentication type TLS\r\n";
						const int len = strlen(reply);
						res = (m_pSslLayer->SendRaw(reply, len) == len);
					}
				}

				if (!res)
				{
					RemoveAllLayers();
					delete m_pSslLayer;
					m_pSslLayer = NULL;
					Send(_T("431 Could not initialize SSL connection"));
					break;
				}
			}
			else
			{
				Send(_T("504 Auth type not supported"));
				break;
			}

			break;
		}
	case COMMAND_ADAT:
		if (m_pGssLayer)
		{
			char sendme[4096];
#ifdef _UNICODE
			m_pGssLayer->ProcessCommand("ADAT", ConvToLocal(args), sendme);
			Send(ConvFromLocal(sendme));
#else
			m_pGssLayer->ProcessCommand("ADAT", args, sendme);
			Send(sendme);
#endif
			
		}
		else
			Send(_T("502 Command not implemented for this authentication type"));
		break;
	case COMMAND_PBSZ:
		if (m_pGssLayer)
		{
			char sendme[4096];
#ifdef _UNICODE
			m_pGssLayer->ProcessCommand("PBSZ", ConvToLocal(args), sendme);
			Send(ConvFromLocal(sendme));
#else
			m_pGssLayer->ProcessCommand("PBSZ", args, sendme);
			Send(sendme);
#endif
		}
		else if (m_pSslLayer)
			Send(_T("200 PBSZ=0"));
		else
			Send(_T("502 Command not implemented for this authentication type"));
		break;
	case COMMAND_PROT:
		if (m_pGssLayer)
		{
			char sendme[4096];

#ifdef _UNICODE
			m_pGssLayer->ProcessCommand("PROT", ConvToLocal(args), sendme);
			Send(ConvFromLocal(sendme));
#else
			m_pGssLayer->ProcessCommand("PROT", args, sendme);
			Send(sendme);
#endif
		}
		else if (m_pSslLayer)
		{
			args.MakeUpper();
			if (args == _T("C"))
			{
				if (m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP))
					Send(_T("534 This server requires an encrypted data connection with PROT P"));
				else
				{
					if (m_transferstatus.socket)
						m_transferstatus.socket->UseSSL(0);

					Send(_T("200 Protection level set to C"));
					m_bProtP = false;
				}
			}
			else if (args == _T("P"))
			{
				if (m_transferstatus.socket)
					m_transferstatus.socket->UseSSL(m_pSslLayer->GetContext());

				Send(_T("200 Protection level set to P"));
				m_bProtP = true;
			}
			else if (args == _T("S") || args == _T("E"))
				Send(_T("504 Protection level ") + args + _T(" not supported"));
			else
				Send(_T("504 Protection level ") + args + _T(" not recognized"));
		}
		else
			Send(_T("502 Command not implemented for this authentication type"));
		break;
	case COMMAND_FEAT:
		{
		if (!Send(_T("211-Features:")))
			break;
		if (!Send(_T(" MDTM")))
			break;
		if (!Send(_T(" REST STREAM")))
			break;
		if (!Send(_T(" SIZE")))
			break;
		if (m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_USE))
		{
			if (!Send(_T(" MODE Z")))
				break;
		}
		if (!Send(_T(" MLST type*;size*;modify*;")))
			break;
		if (!Send(_T(" MLSD")))
			break;
		if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLESSL) && (m_pOwner->m_pOptions->GetOptionVal(OPTION_ALLOWEXPLICITSSL) || m_pSslLayer))
		{
			if (!Send(_T(" AUTH SSL")))
				break;
			if (!Send(_T(" AUTH TLS")))
				break;
			if (!Send(_T(" PROT")))
				break;
			if (!Send(_T(" PBSZ")))
				break;
		}
		if (!Send(_T(" UTF8")))
			break;
		if (!Send(_T(" CLNT")))
			break;
		if (!Send(_T(" MFMT")))
			break;
		if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLE_HASH))
		{
			CStdString hash = _T(" HASH ");
			hash += _T("SHA-1");
			if (m_hash_algorithm == CHashThread::SHA1)
				hash += _T("*");
			hash += _T(";SHA-512");
			if (m_hash_algorithm == CHashThread::SHA512)
				hash += _T("*");
			hash += _T(";MD5");
			if (m_hash_algorithm == CHashThread::MD5)
				hash += _T("*");
			if (!Send(hash))
				break;
		}
		if (!Send(_T("211 End")))
			break;
		break;
		}
	case COMMAND_MODE:
		if (args == _T("S") || args == _T("s"))
		{
			m_transferMode = mode_stream;
			Send(_T("200 MODE set to S."));
		}
		else if (args == _T("Z") || args == _T("z"))
		{
			if (m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_USE) || m_transferMode == mode_zlib)
			{
				if (m_transferMode == mode_zlib || CheckIpForZlib())
				{
					m_transferMode = mode_zlib;
					Send(_T("200 MODE set to Z."));
				}
				else
					Send(_T("504 MODE Z not allowed from your IP"));
			}
			else
				Send(_T("504 MODE Z not enabled"));
		}
		else if (args == _T("C") || args == _T("c") || args == _T("B") || args == _T("b"))
			Send(_T("502 Unimplemented MODE type"));
		else
			Send(_T("504 Unknown MODE type"));
		break;
	case COMMAND_OPTS:
		args.MakeUpper();
		if (args.Left(13) == _T("MODE Z LEVEL "))
		{
			int level = _ttoi(args.Mid(13));
			if (m_zlibLevel == level || (level >= m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_LEVELMIN) && level <= m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_LEVELMAX)))
			{
				m_zlibLevel = level;
				CString str;
				str.Format(_T("200 MODE Z LEVEL set to %d"), level);
				Send(str);
			}
			else
				Send(_T("501 can't change MODE Z LEVEL do desired value"));
		}
		else if (args == _T("UTF8 ON"))
		{
			m_useUTF8 = true;
			Send(_T("200 UTF8 mode enabled"));
		}
		else if (args == _T("UTF8 OFF"))
		{
			m_useUTF8 = false;
			Send(_T("200 UTF8 mode disabled"));
		}
		else if (args.Left(4) == _T("MLST"))
			ParseMlstOpts(args.Mid(4));
		else if (args.Left(4) == _T("HASH"))
			ParseHashOpts(args.Mid(4));
		else
			Send(_T("501 Option not understood"));
		break;
	case COMMAND_HELP:
		if (args == _T(""))
		{
			Send(_T("214-The following commands are recognized:"));
			CString str;
			for (int i = 0; i < (sizeof(commands)/sizeof(t_command)); i++)
			{
				CString cmd = commands[i].command;
				while (cmd.GetLength() < 4)
					cmd += _T(" ");
				str += _T("   ") + cmd;
				if (!((i + 1) % 8))
				{
					Send(str);
					str = _T("");
				}
			}
			if (str != _T(""))
				Send(str);
			Send(_T("214 Have a nice day."));
		}
		else
		{
			args.MakeUpper();

			int i;
			for (i = 0; i < (sizeof(commands)/sizeof(t_command)); i++)
			{
				if (args == commands[i].command)
				{
					CStdString str;
					str.Format(_T("214 Command %s is supported by FileZilla Server"), args);
					Send(str);
					break;
				}
			}
			if (i == (sizeof(commands)/sizeof(t_command)))
			{
				CStdString str;
				str.Format(_T("502 Command %s is not recognized or supported by FileZilla Server"), args);
				Send(str);
			}
		}
		break;
	case COMMAND_ALLO:
		Send(_T("202 No storage allocation neccessary."));
		break;
	case COMMAND_MLST:
		{
			CStdString fact;
			CStdString logicalName;
			int res = m_pOwner->m_pPermissions->GetFact(m_status.user, m_CurrentServerDir, args, fact, logicalName, m_facts);
			if (res & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied."));
				break;
			}
			else if (res & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				break;
			}
			else if (res)
			{
				Send(_T("550 File or directory not found."));
				break;
			}
			CStdString str;
			str.Format(_T("250-Listing %s"), logicalName);
			if (!Send(str))
				break;
			fact = _T(" ") + fact;
			if (!Send(fact))
				break;

			Send(_T("250 End"));
		}
		break;
	case COMMAND_MLSD:
		if (m_transferstatus.pasv == -1)
		{
			Send(_T("503 Bad sequence of commands."));
			break;
		}
		if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
			Send(_T("503 Bad sequence of commands."));
		//Much more checks
		else
		{
			if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
			{
				Send(_T("521 PROT P required"));
				break;
			}
			if (args != _T(""))
			{
				//Unquote args
				if (!UnquoteArgs(args))
				{
					Send(_T("501 Syntax error"));
					break;
				}
			}

			t_dirlisting *pResult;
			CStdString physicalDir, logicalDir;
			int error = m_pOwner->m_pPermissions->GetDirectoryListing(m_status.user, m_CurrentServerDir, args, pResult, physicalDir, logicalDir, CPermissions::AddFactsListingEntry, m_useUTF8, m_facts);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied"));
				ResetTransferstatus();
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
			}
			else if (error)
			{
				Send(_T("550 Directory not found"));
				ResetTransferstatus();
			}
			else
			{
				if (!m_transferstatus.pasv)
				{
					if (m_transferstatus.socket)
					{
						SendTransferinfoNotification();
						delete m_transferstatus.socket;
					}
					CTransferSocket *transfersocket = new CTransferSocket(this);
					m_transferstatus.socket = transfersocket;
					transfersocket->Init(pResult, TRANSFERMODE_LIST);
					if (m_transferMode == mode_zlib)
					{
						if (!transfersocket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					if (!CreateTransferSocket(transfersocket))
						break;

					SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir);
					Send(_T("150 Opening data channel for directory list."));
				}
				else
				{
					if (!m_transferstatus.socket)
					{
						CPermissions::DestroyDirlisting(pResult);
						Send(_T("503 Bad sequence of commands."));
						break;
					}
					m_transferstatus.socket->Init(pResult, TRANSFERMODE_LIST );
					if (m_transferMode == mode_zlib)
					{
						if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
						{
							Send(_T("550 could not initialize zlib, please use MODE S instead"));
							ResetTransferstatus();
							break;
						}
					}

					SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir);
					m_transferstatus.socket->PasvTransfer();
				}
			}

		}
		break;
	case COMMAND_SITE:
		{
			CStdString cmd;

			args.MakeUpper();
			
			int pos = args.Find(' ');
			if (pos != -1)
			{
				cmd = args.Left(pos);
				args = args.Mid(pos + 1);
				args.TrimLeft(_T(" "));
			}
			else
			{
				cmd = args;
				args = _T("");
			}

			if (cmd == _T("NAMEFMT"))
			{
				if (args == _T("") || args == _T("1"))
					Send(_T("200 Now using naming format \"1\""));
				else
					Send(_T("504 Naming format not implemented"));
				break;
			}
			else
			{
				Send(_T("504 Command not implemented for that parameter"));
				break;
			}
			break;
		}
	case COMMAND_STRU:
		args.MakeUpper();
		if (args == _T("F"))
			Send(_T("200 Using file structure 'File'"));
		else
			Send(_T("504 Command not implemented for that parameter"));
		break;
	case COMMAND_CLNT:
		Send(_T("200 Don't care"));
		break;
	case COMMAND_MFMT:
		{
			int pos = args.find(' ');
			if (pos < 1)
			{
				Send(_T("501 Syntax error"));
				break;
			}

			CStdString timeval = args.Left(pos);
			args = args.Mid(pos + 1);

			if (timeval.GetLength() < 14)
			{
				Send( _T("501 Syntax error") );
				break;
			}

			bool numbersOnly = true;
			for (int i = 0; i < 14; i++)
			{
				if (timeval[i] < '0' || timeval[i] > '9')
				{
					numbersOnly = false;
					break;
				}
			}
			if (!numbersOnly)
			{
				Send( _T("501 Syntax error") );
				break;
			}

			int year = (timeval[0] - '0') * 1000 +
					(timeval[1] - '0') * 100 +
					(timeval[2] - '0') * 10 +
					timeval[3] - '0';

			int month = (timeval[4] - '0') * 10 + timeval[5] - '0';
			int day = (timeval[6] - '0') * 10 + timeval[7] - '0';
			int hour = (timeval[8] - '0') * 10 + timeval[9] - '0';
			int minute = (timeval[10] - '0') * 10 + timeval[11] - '0';
			int second = (timeval[12] - '0') * 10 + timeval[13] - '0';

			if (year < 1000 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 ||
				minute > 59 || second > 59)
			{
				Send( _T("501 Not a valid date") );
				break;
			}

			SYSTEMTIME st = {0};
			st.wYear = year;
			st.wMonth = month;
			st.wDay = day;
			st.wHour = hour;
			st.wMinute = minute;
			st.wSecond = second;

			FILETIME ft;
			if (!SystemTimeToFileTime(&st, &ft))
			{
				Send( _T("501 Not a valid date") );
				break;
			}

			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			if (args == _T(""))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_LIST, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
				Send(_T("550 Permission denied"));
			else if (error & PERMISSION_INVALIDNAME)
				Send(_T("550 Filename invalid."));
			else if (error & 2)
				Send(_T("550 File not found"));
			else
			{
				HANDLE hFile = CreateFile(physicalFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
				if (hFile == INVALID_HANDLE_VALUE)
					Send(_T("550 Cannot access file"));
				else
				{
					if (!SetFileTime(hFile, 0, 0, &ft))
						Send(_T("550 Failed to set file modification time"));
					else
						Send(_T("213 modify=") + timeval.Left(14) + _T("; ") + logicalFile);

					CloseHandle(hFile);
				}
			}
		}
		break;
	case COMMAND_HASH:
		{
			if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLE_HASH))
			{
				Send(_T("500 Syntax error, command unrecognized."));
				break;
			}

			//Unquote args
			if (!UnquoteArgs(args))
			{
				Send( _T("501 Syntax error") );
				break;
			}

			CStdString physicalFile, logicalFile;
			int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_READ, physicalFile, logicalFile);
			if (error & PERMISSION_DENIED)
			{
				Send(_T("550 Permission denied"));
				ResetTransferstatus();
			}
			else if (error & PERMISSION_INVALIDNAME)
			{
				Send(_T("550 Filename invalid."));
				ResetTransferstatus();
			}
			else if (error)
			{
				Send(_T("550 File not found"));
				ResetTransferstatus();
			}
			else
			{
				int hash_res = m_pOwner->GetHashThread().Hash(physicalFile, m_hash_algorithm, m_hash_id, m_pOwner);
				if (hash_res == CHashThread::BUSY)
					Send(_T("450 Another hash operation is already in progress."));
				else if (hash_res != CHashThread::PENDING)
					Send(_T("550 Failed to hash file"));
			}
		}
		break;
	default:
		Send(_T("502 Command not implemented."));
	}

	if (!m_RecvLineBuffer.empty())
		m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);

	return;
}

void CControlSocket::ProcessTransferMsg()
{
	if (!m_transferstatus.socket)
		return;
	int status = m_transferstatus.socket->GetStatus();

	GetSystemTime(&m_LastCmdTime);
	if (m_transferstatus.socket)
		if (m_transferstatus.socket->GetMode()==TRANSFERMODE_SEND || m_transferstatus.socket->GetMode()==TRANSFERMODE_RECEIVE)
			GetSystemTime(&m_LastTransferTime);

	if (status == 2 && m_transferstatus.pasv && m_transferstatus.usedResolvedIP)
		m_pOwner->ExternalIPFailed();
	
	int mode = m_transferstatus.socket->GetMode();
	_int64 zlibBytesIn = 0;
	_int64 zlibBytesOut = 0;
	if (m_transferMode == mode_zlib)
		m_transferstatus.socket->GetZlibStats(zlibBytesIn, zlibBytesOut);
	ResetTransferstatus();

	if (!status)
	{
		if ((mode == TRANSFERMODE_LIST || mode == TRANSFERMODE_NLST || mode == TRANSFERMODE_SEND) && zlibBytesIn && zlibBytesOut)
		{
			if (zlibBytesIn >= zlibBytesOut)
			{
				CString str;
				_int64 percent = 10000 - (zlibBytesOut * 10000 / zlibBytesIn);
				str.Format(_T("226 Transfer OK, compression saved %I64d of %I64d bytes (%I64d.%02I64d%%)"), zlibBytesIn - zlibBytesOut, zlibBytesIn, percent / 100, percent % 100);
				Send(str);
			}
			else
			{
				CString str;
				_int64 percent = (zlibBytesOut * 10000 / zlibBytesIn) - 10000;
				str.Format(_T("226 Transfer OK, unfortunately compression did increase the transfer size by %I64d bytes to %I64d bytes (%I64d.%02I64d%%)"), zlibBytesOut - zlibBytesIn, zlibBytesOut, percent / 100, percent % 100);
				Send(str);
			}
		}
		else if (mode == TRANSFERMODE_RECEIVE && zlibBytesIn && zlibBytesOut)
		{
			if (zlibBytesOut >= zlibBytesIn)
			{
				CString str;
				_int64 percent = 10000 - (zlibBytesIn * 10000 / zlibBytesOut);
				str.Format(_T("226 Transfer OK, compression saved %I64d of %I64d bytes (%I64d.%02I64d%%)"), zlibBytesOut - zlibBytesIn, zlibBytesOut, percent / 100, percent % 100);
				Send(str);
			}
			else
			{
				CString str;
				_int64 percent = (zlibBytesIn * 10000 / zlibBytesOut) - 10000;
				str.Format(_T("226 Transfer OK, unfortunately compression did increase the transfer size by %I64d bytes to %I64d bytes (%I64d.%02I64d%%)"), zlibBytesIn - zlibBytesOut, zlibBytesIn, percent / 100, percent % 100);
				Send(str);
			}
		}
		else
			Send(_T("226 Transfer OK"));
	}
	else if (status==1)
		Send(_T("426 Connection closed; transfer aborted."));
	else if (status==2)
		Send(_T("425 Can't open data connection."));
	else if (status==3)
		Send(_T("550 can't access file."));
	else if (status==4)
	{
		Send(_T("426 Connection timed out, aborting transfer"));
		ForceClose(1);
		return;
	}
	else if (status==5)
		Send(_T("425 Can't open data connection"));
	else if (status==6)
		Send(_T("450 zlib error"));
	if (status>=0 && m_bWaitGoOffline)
		ForceClose(0);
	else if (m_bQuitCommand)
	{
		Send(_T("221 Goodbye"));
		if (CanQuit())
			ForceClose(5);
	}
}

CTransferSocket* CControlSocket::GetTransferSocket()
{
	return m_transferstatus.socket;
}

void CControlSocket::ForceClose(int nReason)
{
	if (m_transferstatus.socket)
	{
		// Don't call SendTransferInfoNotification, since connection
		// does get removed real soon.
		m_transferstatus.socket->Close();
		delete m_transferstatus.socket;
		m_transferstatus.socket = 0;
	}
	if (m_shutdown && nReason == 1)
	{
		Close();
		m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
		return;
	}

	if (!nReason)
		Send(_T("421 Server is going offline"));
	else if (nReason == 1)
		Send(_T("421 Connection timed out."));
	else if (nReason == 2)
		Send(_T("421 No-transfer-time exceeded. Closing control connection."));
	else if (nReason == 3)
		Send(_T("421 Login time exceeded. Closing control connection."));
	else if (nReason == 4)
		Send(_T("421 Kicked by Administrator"));
	else if (nReason == 5)
	{
		// 221 Goodbye
	}
	SendStatus(_T("disconnected."), 0);
	m_shutdown = true;
	int res = ShutDown();
	if (m_pSslLayer)
	{
		if (!res && GetLastError() == WSAEWOULDBLOCK)
			return;
	}
	Close();
	m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
}

void CControlSocket::IncUserCount(const CStdString &user)
{
	int curcount=GetUserCount(user)+1;
	EnterCritSection(m_Sync);
	m_UserCount[user]=curcount;
	LeaveCritSection(m_Sync);
}

void CControlSocket::DecUserCount(const CStdString &user)
{
	int curcount=GetUserCount(user)-1;
	if (curcount<0)
		return;
	EnterCritSection(m_Sync);
	m_UserCount[user]=curcount;
	LeaveCritSection(m_Sync);
}

int CControlSocket::GetUserCount(const CStdString &user)
{
	EnterCritSection(m_Sync);
	int count=0;
	std::map<CStdString, int>::iterator iter = m_UserCount.find(user);
	if (iter!=m_UserCount.end())
		count = iter->second;
	LeaveCritSection(m_Sync);
	return count;
}

void CControlSocket::CheckForTimeout()
{
	if (m_antiHammeringWaitTime)
	{
		m_antiHammeringWaitTime -= 1000;
		if (m_antiHammeringWaitTime <= 0)
		{
			m_antiHammeringWaitTime = 0;
			TriggerEvent(FD_FORCEREAD);
		}
	}
	if (m_status.hammerValue > 0)
		m_status.hammerValue--;

	if (m_transferstatus.socket)
	{
		if (m_transferstatus.socket->CheckForTimeout())
			return;
	}
	_int64 timeout;
	if (m_shutdown)
		timeout = 3;
	else
		timeout = m_pOwner->m_pOptions->GetOptionVal(OPTION_TIMEOUT);
	if (!timeout)
		return;
	SYSTEMTIME sCurrentTime;
	GetSystemTime(&sCurrentTime);
	FILETIME fCurrentTime;
	SystemTimeToFileTime(&sCurrentTime, &fCurrentTime);
	FILETIME fLastTime;
	SystemTimeToFileTime(&m_LastCmdTime, &fLastTime);
	_int64 elapsed = ((_int64)(fCurrentTime.dwHighDateTime - fLastTime.dwHighDateTime) << 32) + fCurrentTime.dwLowDateTime - fLastTime.dwLowDateTime;
	if (elapsed > (timeout*10000000))
	{
		ForceClose(1);
		return;
	}
	if (m_status.loggedon)
	{ //Transfer timeout
		_int64 nNoTransferTimeout=m_pOwner->m_pOptions->GetOptionVal(OPTION_NOTRANSFERTIMEOUT);
		if (!nNoTransferTimeout)
			return;
		SystemTimeToFileTime(&m_LastTransferTime, &fLastTime);
		elapsed = ((_int64)(fCurrentTime.dwHighDateTime - fLastTime.dwHighDateTime) << 32) + fCurrentTime.dwLowDateTime - fLastTime.dwLowDateTime;
		if (elapsed>(nNoTransferTimeout*10000000))
		{
			ForceClose(2);
			return;
		}
	}
	else
	{ //Login timeout
		_int64 nLoginTimeout=m_pOwner->m_pOptions->GetOptionVal(OPTION_LOGINTIMEOUT);
		if (!nLoginTimeout)
			return;
		SystemTimeToFileTime(&m_LoginTime, &fLastTime);
		elapsed = ((_int64)(fCurrentTime.dwHighDateTime - fLastTime.dwHighDateTime) << 32) + fCurrentTime.dwLowDateTime - fLastTime.dwLowDateTime;
		if (elapsed>(nLoginTimeout*10000000))
		{
			ForceClose(3);
			return;
		}
	}
}

void CControlSocket::WaitGoOffline(bool wait /*=true*/)
{
	if (!wait)
	{
		m_bWaitGoOffline = FALSE;
		return;
	}

	if (m_transferstatus.socket)
	{
		if (!m_transferstatus.socket->Started())
			ForceClose(0);
		else
			m_bWaitGoOffline=TRUE;
	}
	else
		ForceClose(0);

}

void CControlSocket::ResetTransferstatus()
{
	if (m_transferstatus.socket)
	{
		SendTransferinfoNotification();
		delete m_transferstatus.socket;
	}
	m_transferstatus.socket = 0;
	m_transferstatus.ip = _T("");
	m_transferstatus.port = -1;
	m_transferstatus.pasv = -1;
	m_transferstatus.rest = 0;
}

BOOL CControlSocket::UnquoteArgs(CStdString &args)
{
	args.TrimLeft( _T(" ") );
	args.TrimRight( _T(" ") );
	int pos1 = args.Find('"');
	int pos2 = args.ReverseFind('"');
	if (pos1 == -1 && pos2 == -1)
		return TRUE;
	if (pos1 || pos2 != (args.GetLength()-1) || pos1 >= (pos2-1))
		return FALSE;
	args = args.Mid(1, args.GetLength() - 2);
	return TRUE;
}

void CControlSocket::OnSend(int nErrorCode)
{
	if (m_nSendBufferLen && m_pSendBuffer)
	{
		int nLimit = GetSpeedLimit(download);
		if (!nLimit)
			return;
		int numsend = nLimit;
		if (nLimit == -1 || nLimit > m_nSendBufferLen)
			numsend = m_nSendBufferLen;


		int numsent = CAsyncSocketEx::Send(m_pSendBuffer, numsend);

		if (numsent==SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK)
			return;
		if (!numsent || numsent == SOCKET_ERROR)
		{
			Close();
			SendStatus(_T("could not send reply, disconnected."), 0);
			m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

			delete [] m_pSendBuffer;
			m_pSendBuffer = NULL;
			m_nSendBufferLen = 0;

			return;
		}

		if (nLimit != -1)
			m_SlQuotas[download].nTransferred += numsent;

		if (numsent == m_nSendBufferLen)
		{
			delete [] m_pSendBuffer;
			m_pSendBuffer = NULL;
			m_nSendBufferLen = 0;
		}
		else
		{
			char *tmp = m_pSendBuffer;
			m_pSendBuffer = new char[m_nSendBufferLen-numsent];
			memcpy(m_pSendBuffer, tmp+numsent, m_nSendBufferLen-numsent);
			delete [] tmp;
			m_nSendBufferLen -= numsent;
			TriggerEvent(FD_WRITE);
		}
	}
}

BOOL CControlSocket::DoUserLogin(LPCTSTR password, bool skipPass /*=false*/)
{
	CUser user;
	if (!m_pOwner->m_pPermissions->CheckUserLogin(m_status.user, password, user, skipPass))
	{
		AntiHammerIncrease(2);
		m_pOwner->AntiHammerIncrease(m_RemoteIP);

		if (m_pOwner->m_pAutoBanManager->RegisterAttempt(m_RemoteIP))
		{
			Send(_T("421 Temporarily banned for too many failed login attempts"));
			ForceClose(-1);
			return FALSE;
		}

		Send(_T("530 Login or password incorrect!"));
		return FALSE;
	}

	if (!user.IsEnabled())
	{
		Send(_T("530 Not logged in, user account has been disabled"));
		ForceClose(-1);
		return FALSE;
	}
	if (!user.BypassUserLimit())
	{
		int nMaxUsers = (int)m_pOwner->m_pOptions->GetOptionVal(OPTION_MAXUSERS);
		if (m_pOwner->GetGlobalNumConnections()>nMaxUsers&&nMaxUsers)
		{
			SendStatus(_T("Refusing connection. Reason: Max. connection count reached."), 1);
			Send(_T("421 Too many users are connected, please try again later."));
			ForceClose(-1);
			return FALSE;
		}
	}
	if (user.GetUserLimit() && GetUserCount(m_status.user)>=user.GetUserLimit())
	{
			CStdString str;
			str.Format(_T("Refusing connection. Reason: Max. connection count reached for the user \"%s\"."), m_status.user);
			SendStatus(str,1);
			Send(_T("421 Too many users logged in for this account. Try again later."));
			ForceClose(-1);
			return FALSE;
	}

	CStdString peerIP;
	UINT port = 0;

	BOOL bResult = GetPeerName(peerIP, port);
	if (bResult)
	{
		if (!user.AccessAllowed(peerIP))
		{
			Send(_T("521 This user is not allowed to connect from this IP"));
			ForceClose(-1);
			return FALSE;
		}
	}
	else
	{
		SendStatus(_T("Could not get peer name"), 1);
		Send(_T("421 Refusing connection. Could not get peer name."));
		ForceClose(-1);
		return FALSE;
	}

	int count = m_pOwner->GetIpCount(peerIP);
	if (user.GetIpLimit() && count >= user.GetIpLimit())
	{
		CStdString str;
		if (count==1)
			str.Format(_T("Refusing connection. Reason: No more connections allowed from this IP. (%s already connected once)"), peerIP.c_str());
		else
			str.Format(_T("Refusing connection. Reason: No more connections allowed from this IP. (%s already connected %d times)"), peerIP.c_str(), count);
		SendStatus(str, 1);
		Send(_T("421 Refusing connection. No more connections allowed from your IP."));
		ForceClose(-1);
		return FALSE;
	}

	m_CurrentServerDir = m_pOwner->m_pPermissions->GetHomeDir(m_status.user);
	if (m_CurrentServerDir == _T(""))
	{
		Send(_T("550 Could not get home dir!"));
		ForceClose(-1);
		return FALSE;
	}

	m_status.ip = peerIP;

	count = GetUserCount(user.user);
	if (user.GetUserLimit() && count >= user.GetUserLimit())
	{
		CStdString str;
		str.Format(_T("Refusing connection. Reason: Maximum connection count (%d) reached for this user"), user.GetUserLimit());
		SendStatus(str, 1);
		str.Format(_T("421 Refusing connection. Maximum connection count reached for the user '%s'"), user.user);
		Send(str);
		ForceClose(-1);
		return FALSE;
	}

	m_pOwner->IncIpCount(peerIP);
	IncUserCount(m_status.user);
	m_status.loggedon = TRUE;

	GetSystemTime(&m_LastTransferTime);

	m_pOwner->m_pPermissions->AutoCreateDirs(m_status.user);

	t_connectiondata_changeuser *conndata = new t_connectiondata_changeuser;
	t_connop *op = new t_connop;
	op->data = conndata;
	op->op = USERCONTROL_CONNOP_CHANGEUSER;
	op->userid = m_userid;
	conndata->user = m_status.user;

	m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);

	return TRUE;
}

void CControlSocket::Continue()
{
	if (m_SlQuotas[download].bContinue)
	{
		TriggerEvent(FD_WRITE);
		if (m_transferstatus.socket && m_transferstatus.socket->Started())
			m_transferstatus.socket->TriggerEvent(FD_WRITE);
		m_SlQuotas[download].bContinue = false;
	}

	if (m_SlQuotas[upload].bContinue)
	{
		TriggerEvent(FD_READ);
		if (m_transferstatus.socket && m_transferstatus.socket->Started())
			m_transferstatus.socket->TriggerEvent(FD_READ);
		m_SlQuotas[upload].bContinue = false;
	}
}

int CControlSocket::GetSpeedLimit(sltype mode)
{
	CUser user;
	int nLimit = -1;
	if (m_status.loggedon && m_pOwner->m_pPermissions->GetUser(m_status.user, user))
	{
		nLimit = user.GetCurrentSpeedLimit(mode);
	}
	if (nLimit > 0)
	{
		nLimit *= 100;
		if (m_SlQuotas[mode].nTransferred >= nLimit)
		{
			m_SlQuotas[mode].bContinue = TRUE;
			return 0;
		}
		else
			nLimit -= m_SlQuotas[mode].nTransferred;
	}
	else
		nLimit = -1;
	if (user.BypassServerSpeedLimit(mode))
		m_SlQuotas[mode].bBypassed = TRUE;
	else if (m_SlQuotas[mode].nBytesAllowedToTransfer != -1)
	{
		if (nLimit == -1 || nLimit > (m_SlQuotas[mode].nBytesAllowedToTransfer - m_SlQuotas[mode].nTransferred))
			nLimit = m_SlQuotas[mode].nBytesAllowedToTransfer - m_SlQuotas[mode].nTransferred;
	}

	if (!nLimit)
		m_SlQuotas[mode].bContinue = TRUE;

	return nLimit;
}

BOOL CControlSocket::CreateTransferSocket(CTransferSocket *pTransferSocket)
{
	/* Create socket
	 * First try control connection port - 1, if that fails try
	 * control connection port + 1. If that fails as well, let
	 * the OS decide.
	 */
	bool bFallback = false;
	BOOL bCreated = FALSE;

	// Fix: Formerly, the data connection would always be opened using the server's default (primary) IP.
	// This would cause Windows Firewall to freak out if control connection was opened on a secondary IP.
	// When using Active FTP behind Windows Firewall, no connection could be made. This fix ensures the data
	// socket is on the same IP as the control socket.
	CStdString controlIP;
	UINT controlPort = 0;
	BOOL bResult = this->GetSockName(controlIP, controlPort);

	if (bResult)
	{
		// Try create control conn. port - 1
		if (controlPort > 1)
			if (pTransferSocket->Create(controlPort - 1, SOCK_STREAM, FD_CONNECT, controlIP, m_transferstatus.family, true))
				bCreated = TRUE;
	}
	if (!bCreated)
	{
creation_fallback:
		bFallback = true;
		// Let the OS find a valid port
		if (!pTransferSocket->Create(0, SOCK_STREAM, FD_CONNECT, controlIP, m_transferstatus.family, true))
		{
			// Give up
			Send(_T("421 Can't create socket"));
			ResetTransferstatus();
			return FALSE;
		}
	}
	if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
		m_transferstatus.socket->UseGSS(m_pGssLayer);

	if (pTransferSocket->Connect(m_transferstatus.ip, m_transferstatus.port) == 0)
	{
		if (!bFallback && GetLastError() == WSAEADDRINUSE)
		{
			pTransferSocket->Close();
			goto creation_fallback;
		}

		if (GetLastError() != WSAEWOULDBLOCK)
		{
			Send(_T("425 Can't open data connection"));
			ResetTransferstatus();
			return FALSE;
		}
	}

	if (m_pSslLayer && m_bProtP)
		pTransferSocket->UseSSL(m_pSslLayer->GetContext());

	return TRUE;
}

bool CControlSocket::CheckIpForZlib()
{
	CStdString peerIP;
	UINT port = 0;
	BOOL bResult = GetPeerName(peerIP, port);
	if (!bResult)
		return false;

	if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_ALLOWLOCAL) && !IsRoutableAddress(peerIP))
		return false;

	CStdString ips = m_pOwner->m_pOptions->GetOption(OPTION_MODEZ_DISALLOWED_IPS);
	ips += " ";

	int pos = ips.Find(' ');
	while (pos != -1)
	{
		CStdString blockedIP = ips.Left(pos);
		ips = ips.Mid(pos + 1);
		pos = ips.Find(' ');

		if (MatchesFilter(blockedIP, peerIP))
			return false;
	}

	return true;
}

void CControlSocket::AntiHammerIncrease(int amount /*=1*/)
{
	if (m_status.hammerValue < 8000)
		m_status.hammerValue += amount * 200;

	if (m_status.hammerValue > 2000)
		m_antiHammeringWaitTime += 1000 * (int)pow(1.3, (m_status.hammerValue / 400) - 5);
}

void CControlSocket::SendTransferinfoNotification(const char transfermode, const CStdString& physicalFile, const CStdString& logicalFile, __int64 startOffset, __int64 totalSize)
{
	t_connop *op = new t_connop;
	op->op = USERCONTROL_CONNOP_TRANSFERINIT;
	op->userid = m_userid;

	t_connectiondata_transferinfo *conndata = new t_connectiondata_transferinfo;
	conndata->transferMode = transfermode;
	conndata->physicalFile = physicalFile;
	conndata->logicalFile = logicalFile;
	conndata->startOffset = startOffset;
	conndata->totalSize = totalSize;
	op->data = conndata;

	m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);
}

int CControlSocket::OnLayerCallback(std::list<t_callbackMsg>& callbacks)
{
	for (std::list<t_callbackMsg>::iterator iter = callbacks.begin(); iter != callbacks.end(); iter++)
	{
		if (m_pSslLayer && iter->pLayer == m_pSslLayer)
		{
			if (iter->nType == LAYERCALLBACK_LAYERSPECIFIC && iter->nParam1 == SSL_INFO && iter->nParam2 == SSL_INFO_ESTABLISHED)
				SendStatus(_T("SSL connection established"), 0);
			else if (iter->nType == LAYERCALLBACK_LAYERSPECIFIC && iter->nParam1 == SSL_INFO && iter->nParam2 == SSL_INFO_SHUTDOWNCOMPLETE)
			{
				if (m_shutdown)
				{
					delete [] iter->str;
					Close();
					m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
					return 0;
				}
				if (!m_bQuitCommand)
				{
					delete [] iter->str;
					continue;
				}

				do
				{
					delete [] iter->str;
					iter++;
				} while (iter != callbacks.end());

				ForceClose(5);

				return 0;
			}
		}
		else if (iter->nType == LAYERCALLBACK_LAYERSPECIFIC && iter->nParam1 == SSL_VERBOSE_WARNING)
		{
			if (iter->str)
			{
				CStdString str = "SSL warning: ";
				str += iter->str;

				SendStatus(str, 1);
			}
		}
		delete [] iter->str;
	}
	return 0;//CAsyncSocketEx::OnLayerCallback(pLayer, nType, nParam1, nParam2);
}

bool CControlSocket::InitImplicitSsl()
{
	m_pSslLayer = new CAsyncSslSocketLayer;
	int res = AddLayer(m_pSslLayer) ? 1 : 0;
	if (!res)
	{
		delete m_pSslLayer;
		m_pSslLayer = 0;
		return false;
	}

	CString error;
#ifdef _UNICODE
	res = m_pSslLayer->SetCertKeyFile(ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS)), &error);
#else
	res = m_pSslLayer->SetCertKeyFile(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS), &error);
#endif
	if (res == SSL_FAILURE_LOADDLLS)
		SendStatus(_T("Failed to load SSL libraries"), 1);
	else if (res == SSL_FAILURE_INITSSL)
		SendStatus(_T("Failed to initialize SSL libraries"), 1);
	else if (res == SSL_FAILURE_VERIFYCERT)
	{
		if (error != _T(""))
			SendStatus(error, 1);
		else
			SendStatus(_T("Failed to set certificate and private key"), 1);
	}
	if (res)
	{
		RemoveAllLayers();
		delete m_pSslLayer;
		m_pSslLayer = NULL;
		Send(_T("431 Could not initialize SSL connection"), 1);
		return false;
	}

	int code = m_pSslLayer->InitSSLConnection(false);
	if (code == SSL_FAILURE_LOADDLLS)
		SendStatus(_T("Failed to load SSL libraries"), 1);
	else if (code == SSL_FAILURE_INITSSL)
		SendStatus(_T("Failed to initialize SSL library"), 1);

	if (!code)
		return true;

	RemoveAllLayers();
	delete m_pSslLayer;
	m_pSslLayer = NULL;
	Send(_T("431 Could not initialize SSL connection"));

	//Close socket
	Close();
	SendStatus(_T("disconnected."), 0);
	m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

	return false;
}

bool CControlSocket::CanQuit()
{
	if (m_pSslLayer)
		return false;
	return true;
}

CStdString CControlSocket::GetPassiveIP()
{
	//Get the ip of the control socket
	CStdString localIP;
	UINT localPort;
	BOOL bValidSockAddr = GetSockName(localIP, localPort);

	//Get peer ip
	CStdString peerIP;
	UINT peerPort = 0;
	BOOL bResult = GetPeerName(peerIP, peerPort);
	if (bResult)
	{
		if (m_pOwner->m_pOptions->GetOptionVal(OPTION_NOEXTERNALIPONLOCAL) && !IsRoutableAddress(peerIP))
		{
			// Remote IP address from an unroutable subnet
			
			// Inside a NAT-in-NAT environment, two different unroutable address ranges are used.
			// If remote address comes from a different unroutable subnet, don't use local
			// address.
			// Note that in a NAT-in-NAT environment, the external IP address specified will either
			// be the worldwide one or the NAT one. Either external or single-NATed users won't be able
			// to use passive mode.
			m_transferstatus.usedResolvedIP = false;

			if (!bValidSockAddr)
				return _T("");
			return localIP;
		}
	}

	if (m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVIPTYPE))
	{
		CStdString pasvIP = m_pOwner->GetExternalIP(localIP);
		if (pasvIP != _T("") && pasvIP != localIP)
		{
			m_transferstatus.usedResolvedIP = true;
			return pasvIP;
		}
	}

	m_transferstatus.usedResolvedIP = false;

	if (!bValidSockAddr)
		return _T("");

	return localIP;
}

void CControlSocket::ParseMlstOpts(CStdString args)
{
	if (args == _T(""))
	{
		for (int i = 0; i < 4; i++)
			m_facts[i] = false;
		Send(_T("200 MLST OPTS"));
		return;
	}
	if (args[0] != ' ')
	{
		Send(_T("501 Invalid MLST options"));
		return;
	}
	args = args.Mid(1);
	if (args.Find(' ') != -1)
	{
		Send(_T("501 Invalid MLST options"));
		return;
	}

	bool facts[4] = {0};
	while (args != _T(""))
	{
		int pos = args.Find(';');
		if (pos < 1)
		{
			Send(_T("501 Invalid MLST options"));
			return;
		}

		CStdString fact = args.Left(pos);
		args = args.Mid(pos + 1);

		if (fact == _T("TYPE"))
			facts[fact_type] = true;
		else if (fact == _T("SIZE"))
			facts[fact_size] = true;
		else if (fact == _T("MODIFY"))
			facts[fact_modify] = true;
		//else if (fact == _T("PERM"))
		//	facts[fact_perm] = true;
	}

	for (int i = 0; i < 4; i++)
		m_facts[i] = facts[i];

	CStdString factstr;
	if (facts[fact_type])
		factstr += _T("type;");
	if (facts[fact_size])
		factstr += _T("size;");
	if (facts[fact_modify])
		factstr += _T("modify;");
	if (facts[fact_perm])
		factstr += _T("perm;");
	
	CStdString result = _T("200 MLST OPTS");
	if (factstr != _T(""))
		result += _T(" ") + factstr;

	Send(result);
}

void CControlSocket::ParseHashOpts(CStdString args)
{
	if (args == _T(""))
	{
		switch (m_hash_algorithm)
		{
		case CHashThread::MD5:
			Send(_T("200 MD5"));
			break;
		case CHashThread::SHA512:
			Send(_T("200 SHA-512"));
			break;
		default:
			Send(_T("200 SHA-1"));
			break;
		}
		return;
	}
	if (args[0] != ' ')
	{
		Send(_T("501 Invalid HASH options"));
		return;
	}
	args = args.Mid(1);
	if (args.Find(' ') != -1)
	{
		Send(_T("501 Invalid HASH options"));
		return;
	}

	if (args == _T("SHA-1"))
	{
		m_hash_algorithm = CHashThread::SHA1;
		Send(_T("200 Hash algorithm set to SHA-1"));
	}
	else if (args == _T("SHA-512"))
	{
		m_hash_algorithm = CHashThread::SHA512;
		Send(_T("200 Hash algorithm set to SHA-512"));
	}
	else if (args == _T("MD5"))
	{
		m_hash_algorithm = CHashThread::MD5;
		Send(_T("200 Hash algorithm set to MD5"));
	}
	else
		Send(_T("501 Unknown algorithm"));
}

void CControlSocket::ProcessHashResult(int hash_id, int res, CHashThread::_algorithm alg, const CStdString& hash, const CStdString& file)
{
	if (hash_id != m_hash_id)
		return;
	
	m_hash_id = 0;

	if (res == CHashThread::BUSY)
		Send(_T("450 Another hash operation is already in progress."));
	else if (res == CHashThread::FAILURE_OPEN)
		Send(_T("550 Failed to open file"));
	else if (res == CHashThread::FAILURE_READ)
		Send(_T("550 Could not read from file"));
	else
	{
		CStdString algname;
		switch (alg)
		{
		case CHashThread::SHA1:
			algname = "SHA-1";
			break;
		case CHashThread::SHA512:
			algname = "SHA-512";
			break;
		case CHashThread::MD5:
			algname = "MD5";
			break;
		}
		Send(_T("213 ") + algname + _T(" ") + hash + _T(" ") + file);
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit