| OLD | NEW |
| 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this | 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this |
| 2 // source code is governed by a BSD-style license that can be found in the | 2 // source code is governed by a BSD-style license that can be found in the |
| 3 // LICENSE file. | 3 // LICENSE file. |
| 4 | 4 |
| 5 #include "net/ftp/ftp_network_transaction.h" | 5 #include "net/ftp/ftp_network_transaction.h" |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
| 8 #include "base/string_util.h" | 8 #include "base/string_util.h" |
| 9 #include "net/base/connection_type_histograms.h" | 9 #include "net/base/connection_type_histograms.h" |
| 10 #include "net/base/escape.h" | 10 #include "net/base/escape.h" |
| 11 #include "net/base/load_log.h" | 11 #include "net/base/load_log.h" |
| 12 #include "net/base/net_errors.h" | 12 #include "net/base/net_errors.h" |
| 13 #include "net/base/net_util.h" | 13 #include "net/base/net_util.h" |
| 14 #include "net/ftp/ftp_network_session.h" | 14 #include "net/ftp/ftp_network_session.h" |
| 15 #include "net/ftp/ftp_request_info.h" | 15 #include "net/ftp/ftp_request_info.h" |
| 16 #include "net/ftp/ftp_util.h" |
| 16 #include "net/socket/client_socket.h" | 17 #include "net/socket/client_socket.h" |
| 17 #include "net/socket/client_socket_factory.h" | 18 #include "net/socket/client_socket_factory.h" |
| 18 | 19 |
| 19 // TODO(ibrar): Try to avoid sscanf. | 20 // TODO(ibrar): Try to avoid sscanf. |
| 20 #if !defined(COMPILER_MSVC) | 21 #if !defined(COMPILER_MSVC) |
| 21 #define sscanf_s sscanf | 22 #define sscanf_s sscanf |
| 22 #endif | 23 #endif |
| 23 | 24 |
| 24 const char kCRLF[] = "\r\n"; | 25 const char kCRLF[] = "\r\n"; |
| 25 | 26 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 53 user_callback_(NULL), | 54 user_callback_(NULL), |
| 54 session_(session), | 55 session_(session), |
| 55 request_(NULL), | 56 request_(NULL), |
| 56 resolver_(session->host_resolver()), | 57 resolver_(session->host_resolver()), |
| 57 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)), | 58 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)), |
| 58 ctrl_response_buffer_(new FtpCtrlResponseBuffer()), | 59 ctrl_response_buffer_(new FtpCtrlResponseBuffer()), |
| 59 read_data_buf_len_(0), | 60 read_data_buf_len_(0), |
| 60 file_data_len_(0), | 61 file_data_len_(0), |
| 61 write_command_buf_written_(0), | 62 write_command_buf_written_(0), |
| 62 last_error_(OK), | 63 last_error_(OK), |
| 64 system_type_(SYSTEM_TYPE_UNKNOWN), |
| 63 retr_failed_(false), | 65 retr_failed_(false), |
| 64 data_connection_port_(0), | 66 data_connection_port_(0), |
| 65 socket_factory_(socket_factory), | 67 socket_factory_(socket_factory), |
| 66 next_state_(STATE_NONE) { | 68 next_state_(STATE_NONE) { |
| 67 } | 69 } |
| 68 | 70 |
| 69 FtpNetworkTransaction::~FtpNetworkTransaction() { | 71 FtpNetworkTransaction::~FtpNetworkTransaction() { |
| 70 } | 72 } |
| 71 | 73 |
| 72 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, | 74 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 316 user_callback_ = NULL; | 318 user_callback_ = NULL; |
| 317 c->Run(rv); | 319 c->Run(rv); |
| 318 } | 320 } |
| 319 | 321 |
| 320 void FtpNetworkTransaction::OnIOComplete(int result) { | 322 void FtpNetworkTransaction::OnIOComplete(int result) { |
| 321 int rv = DoLoop(result); | 323 int rv = DoLoop(result); |
| 322 if (rv != ERR_IO_PENDING) | 324 if (rv != ERR_IO_PENDING) |
| 323 DoCallback(rv); | 325 DoCallback(rv); |
| 324 } | 326 } |
| 325 | 327 |
| 326 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand() const { | 328 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand( |
| 327 std::string path = (request_->url.has_path() ? request_->url.path() : "/"); | 329 bool is_directory) const { |
| 330 std::string path(current_remote_directory_); |
| 331 if (request_->url.has_path()) |
| 332 path.append(request_->url.path()); |
| 333 // Make sure that if the path is expected to be a file, it won't end |
| 334 // with a trailing slash. |
| 335 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/') |
| 336 path.erase(path.length() - 1); |
| 328 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES | | 337 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES | |
| 329 UnescapeRule::URL_SPECIAL_CHARS; | 338 UnescapeRule::URL_SPECIAL_CHARS; |
| 330 // This may unescape to non-ASCII characters, but we allow that. See the | 339 // This may unescape to non-ASCII characters, but we allow that. See the |
| 331 // comment for IsValidFTPCommandString. | 340 // comment for IsValidFTPCommandString. |
| 332 path = UnescapeURLComponent(path, unescape_rules); | 341 path = UnescapeURLComponent(path, unescape_rules); |
| 342 |
| 343 if (system_type_ == SYSTEM_TYPE_VMS) { |
| 344 if (is_directory) |
| 345 path = FtpUtil::UnixDirectoryPathToVMS(path); |
| 346 else |
| 347 path = FtpUtil::UnixFilePathToVMS(path); |
| 348 } |
| 349 |
| 333 DCHECK(IsValidFTPCommandString(path)); | 350 DCHECK(IsValidFTPCommandString(path)); |
| 334 return path; | 351 return path; |
| 335 } | 352 } |
| 336 | 353 |
| 337 int FtpNetworkTransaction::DoLoop(int result) { | 354 int FtpNetworkTransaction::DoLoop(int result) { |
| 338 DCHECK(next_state_ != STATE_NONE); | 355 DCHECK(next_state_ != STATE_NONE); |
| 339 | 356 |
| 340 int rv = result; | 357 int rv = result; |
| 341 do { | 358 do { |
| 342 State state = next_state_; | 359 State state = next_state_; |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 627 std::string command = "SYST"; | 644 std::string command = "SYST"; |
| 628 next_state_ = STATE_CTRL_READ; | 645 next_state_ = STATE_CTRL_READ; |
| 629 return SendFtpCommand(command, COMMAND_SYST); | 646 return SendFtpCommand(command, COMMAND_SYST); |
| 630 } | 647 } |
| 631 | 648 |
| 632 int FtpNetworkTransaction::ProcessResponseSYST( | 649 int FtpNetworkTransaction::ProcessResponseSYST( |
| 633 const FtpCtrlResponse& response) { | 650 const FtpCtrlResponse& response) { |
| 634 switch (GetErrorClass(response.status_code)) { | 651 switch (GetErrorClass(response.status_code)) { |
| 635 case ERROR_CLASS_INITIATED: | 652 case ERROR_CLASS_INITIATED: |
| 636 return Stop(ERR_INVALID_RESPONSE); | 653 return Stop(ERR_INVALID_RESPONSE); |
| 637 case ERROR_CLASS_OK: | 654 case ERROR_CLASS_OK: { |
| 638 // TODO(ibrar): Process SYST response properly. | 655 // All important info should be on the first line. |
| 656 std::string line = response.lines[0]; |
| 657 // The response should be ASCII, which allows us to do case-insensitive |
| 658 // comparisons easily. If it is not ASCII, we leave the system type |
| 659 // as unknown. |
| 660 if (IsStringASCII(line)) { |
| 661 line = StringToLowerASCII(line); |
| 662 // The "magic" strings we test for below have been gathered by an |
| 663 // empirical study. |
| 664 if (line.find("l8") != std::string::npos || |
| 665 line.find("unix") != std::string::npos || |
| 666 line.find("bsd") != std::string::npos) { |
| 667 system_type_ = SYSTEM_TYPE_UNIX; |
| 668 } else if (line.find("win32") != std::string::npos || |
| 669 line.find("windows") != std::string::npos) { |
| 670 system_type_ = SYSTEM_TYPE_WINDOWS; |
| 671 } else if (line.find("os/2") != std::string::npos) { |
| 672 system_type_ = SYSTEM_TYPE_OS2; |
| 673 } else if (line.find("vms") != std::string::npos) { |
| 674 system_type_ = SYSTEM_TYPE_VMS; |
| 675 } |
| 676 } |
| 639 next_state_ = STATE_CTRL_WRITE_PWD; | 677 next_state_ = STATE_CTRL_WRITE_PWD; |
| 640 break; | 678 break; |
| 679 } |
| 641 case ERROR_CLASS_INFO_NEEDED: | 680 case ERROR_CLASS_INFO_NEEDED: |
| 642 return Stop(ERR_INVALID_RESPONSE); | 681 return Stop(ERR_INVALID_RESPONSE); |
| 643 case ERROR_CLASS_TRANSIENT_ERROR: | 682 case ERROR_CLASS_TRANSIENT_ERROR: |
| 644 return Stop(ERR_FAILED); | 683 return Stop(ERR_FAILED); |
| 645 case ERROR_CLASS_PERMANENT_ERROR: | 684 case ERROR_CLASS_PERMANENT_ERROR: |
| 646 // Server does not recognize the SYST command so proceed. | 685 // Server does not recognize the SYST command so proceed. |
| 647 next_state_ = STATE_CTRL_WRITE_PWD; | 686 next_state_ = STATE_CTRL_WRITE_PWD; |
| 648 break; | 687 break; |
| 649 default: | 688 default: |
| 650 NOTREACHED(); | 689 NOTREACHED(); |
| 651 return Stop(ERR_UNEXPECTED); | 690 return Stop(ERR_UNEXPECTED); |
| 652 } | 691 } |
| 653 return OK; | 692 return OK; |
| 654 } | 693 } |
| 655 | 694 |
| 656 // PWD command. | 695 // PWD command. |
| 657 int FtpNetworkTransaction::DoCtrlWritePWD() { | 696 int FtpNetworkTransaction::DoCtrlWritePWD() { |
| 658 std::string command = "PWD"; | 697 std::string command = "PWD"; |
| 659 next_state_ = STATE_CTRL_READ; | 698 next_state_ = STATE_CTRL_READ; |
| 660 return SendFtpCommand(command, COMMAND_PWD); | 699 return SendFtpCommand(command, COMMAND_PWD); |
| 661 } | 700 } |
| 662 | 701 |
| 663 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { | 702 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { |
| 664 switch (GetErrorClass(response.status_code)) { | 703 switch (GetErrorClass(response.status_code)) { |
| 665 case ERROR_CLASS_INITIATED: | 704 case ERROR_CLASS_INITIATED: |
| 666 return Stop(ERR_INVALID_RESPONSE); | 705 return Stop(ERR_INVALID_RESPONSE); |
| 667 case ERROR_CLASS_OK: | 706 case ERROR_CLASS_OK: { |
| 707 // The info we look for should be on the first line. |
| 708 std::string line = response.lines[0]; |
| 709 if (line.empty()) |
| 710 return Stop(ERR_INVALID_RESPONSE); |
| 711 std::string::size_type quote_pos = line.find('"'); |
| 712 if (quote_pos != std::string::npos) { |
| 713 line = line.substr(quote_pos + 1); |
| 714 quote_pos = line.find('"'); |
| 715 if (quote_pos == std::string::npos) |
| 716 return Stop(ERR_INVALID_RESPONSE); |
| 717 line = line.substr(0, quote_pos); |
| 718 } |
| 719 if (system_type_ == SYSTEM_TYPE_VMS) |
| 720 line = FtpUtil::VMSPathToUnix(line); |
| 721 if (line[line.length() - 1] == '/') |
| 722 line.erase(line.length() - 1); |
| 723 current_remote_directory_ = line; |
| 668 next_state_ = STATE_CTRL_WRITE_TYPE; | 724 next_state_ = STATE_CTRL_WRITE_TYPE; |
| 669 break; | 725 break; |
| 726 } |
| 670 case ERROR_CLASS_INFO_NEEDED: | 727 case ERROR_CLASS_INFO_NEEDED: |
| 671 return Stop(ERR_INVALID_RESPONSE); | 728 return Stop(ERR_INVALID_RESPONSE); |
| 672 case ERROR_CLASS_TRANSIENT_ERROR: | 729 case ERROR_CLASS_TRANSIENT_ERROR: |
| 673 return Stop(ERR_FAILED); | 730 return Stop(ERR_FAILED); |
| 674 case ERROR_CLASS_PERMANENT_ERROR: | 731 case ERROR_CLASS_PERMANENT_ERROR: |
| 675 return Stop(ERR_FAILED); | 732 return Stop(ERR_FAILED); |
| 676 default: | 733 default: |
| 677 NOTREACHED(); | 734 NOTREACHED(); |
| 678 return Stop(ERR_UNEXPECTED); | 735 return Stop(ERR_UNEXPECTED); |
| 679 } | 736 } |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 793 return Stop(ERR_FAILED); | 850 return Stop(ERR_FAILED); |
| 794 default: | 851 default: |
| 795 NOTREACHED(); | 852 NOTREACHED(); |
| 796 return Stop(ERR_UNEXPECTED); | 853 return Stop(ERR_UNEXPECTED); |
| 797 } | 854 } |
| 798 return OK; | 855 return OK; |
| 799 } | 856 } |
| 800 | 857 |
| 801 // SIZE command | 858 // SIZE command |
| 802 int FtpNetworkTransaction::DoCtrlWriteSIZE() { | 859 int FtpNetworkTransaction::DoCtrlWriteSIZE() { |
| 803 std::string command = "SIZE " + GetRequestPathForFtpCommand(); | 860 std::string command = "SIZE " + GetRequestPathForFtpCommand(false); |
| 804 next_state_ = STATE_CTRL_READ; | 861 next_state_ = STATE_CTRL_READ; |
| 805 return SendFtpCommand(command, COMMAND_SIZE); | 862 return SendFtpCommand(command, COMMAND_SIZE); |
| 806 } | 863 } |
| 807 | 864 |
| 808 int FtpNetworkTransaction::ProcessResponseSIZE( | 865 int FtpNetworkTransaction::ProcessResponseSIZE( |
| 809 const FtpCtrlResponse& response) { | 866 const FtpCtrlResponse& response) { |
| 810 switch (GetErrorClass(response.status_code)) { | 867 switch (GetErrorClass(response.status_code)) { |
| 811 case ERROR_CLASS_INITIATED: | 868 case ERROR_CLASS_INITIATED: |
| 812 break; | 869 break; |
| 813 case ERROR_CLASS_OK: | 870 case ERROR_CLASS_OK: |
| (...skipping 13 matching lines...) Expand all Loading... |
| 827 default: | 884 default: |
| 828 NOTREACHED(); | 885 NOTREACHED(); |
| 829 return Stop(ERR_UNEXPECTED); | 886 return Stop(ERR_UNEXPECTED); |
| 830 } | 887 } |
| 831 next_state_ = STATE_CTRL_WRITE_MDTM; | 888 next_state_ = STATE_CTRL_WRITE_MDTM; |
| 832 return OK; | 889 return OK; |
| 833 } | 890 } |
| 834 | 891 |
| 835 // RETR command | 892 // RETR command |
| 836 int FtpNetworkTransaction::DoCtrlWriteRETR() { | 893 int FtpNetworkTransaction::DoCtrlWriteRETR() { |
| 837 std::string command = "RETR " + GetRequestPathForFtpCommand(); | 894 std::string command = "RETR " + GetRequestPathForFtpCommand(false); |
| 838 next_state_ = STATE_CTRL_READ; | 895 next_state_ = STATE_CTRL_READ; |
| 839 return SendFtpCommand(command, COMMAND_RETR); | 896 return SendFtpCommand(command, COMMAND_RETR); |
| 840 } | 897 } |
| 841 | 898 |
| 842 int FtpNetworkTransaction::ProcessResponseRETR( | 899 int FtpNetworkTransaction::ProcessResponseRETR( |
| 843 const FtpCtrlResponse& response) { | 900 const FtpCtrlResponse& response) { |
| 844 switch (GetErrorClass(response.status_code)) { | 901 switch (GetErrorClass(response.status_code)) { |
| 845 case ERROR_CLASS_INITIATED: | 902 case ERROR_CLASS_INITIATED: |
| 846 // We want the client to start reading the response at this point. | 903 // We want the client to start reading the response at this point. |
| 847 // It got here either through Start or RestartWithAuth. We want that | 904 // It got here either through Start or RestartWithAuth. We want that |
| (...skipping 26 matching lines...) Expand all Loading... |
| 874 break; | 931 break; |
| 875 default: | 932 default: |
| 876 NOTREACHED(); | 933 NOTREACHED(); |
| 877 return Stop(ERR_UNEXPECTED); | 934 return Stop(ERR_UNEXPECTED); |
| 878 } | 935 } |
| 879 return OK; | 936 return OK; |
| 880 } | 937 } |
| 881 | 938 |
| 882 // MDMT command | 939 // MDMT command |
| 883 int FtpNetworkTransaction::DoCtrlWriteMDTM() { | 940 int FtpNetworkTransaction::DoCtrlWriteMDTM() { |
| 884 std::string command = "MDTM " + GetRequestPathForFtpCommand(); | 941 std::string command = "MDTM " + GetRequestPathForFtpCommand(false); |
| 885 next_state_ = STATE_CTRL_READ; | 942 next_state_ = STATE_CTRL_READ; |
| 886 return SendFtpCommand(command, COMMAND_MDTM); | 943 return SendFtpCommand(command, COMMAND_MDTM); |
| 887 } | 944 } |
| 888 | 945 |
| 889 int FtpNetworkTransaction::ProcessResponseMDTM( | 946 int FtpNetworkTransaction::ProcessResponseMDTM( |
| 890 const FtpCtrlResponse& response) { | 947 const FtpCtrlResponse& response) { |
| 891 switch (GetErrorClass(response.status_code)) { | 948 switch (GetErrorClass(response.status_code)) { |
| 892 case ERROR_CLASS_INITIATED: | 949 case ERROR_CLASS_INITIATED: |
| 893 return Stop(ERR_FAILED); | 950 return Stop(ERR_FAILED); |
| 894 case ERROR_CLASS_OK: | 951 case ERROR_CLASS_OK: |
| 895 next_state_ = STATE_CTRL_WRITE_RETR; | 952 next_state_ = STATE_CTRL_WRITE_RETR; |
| 896 break; | 953 break; |
| 897 case ERROR_CLASS_INFO_NEEDED: | 954 case ERROR_CLASS_INFO_NEEDED: |
| 898 return Stop(ERR_FAILED); | 955 return Stop(ERR_FAILED); |
| 899 case ERROR_CLASS_TRANSIENT_ERROR: | 956 case ERROR_CLASS_TRANSIENT_ERROR: |
| 900 return Stop(ERR_FAILED); | 957 return Stop(ERR_FAILED); |
| 901 case ERROR_CLASS_PERMANENT_ERROR: | 958 case ERROR_CLASS_PERMANENT_ERROR: |
| 902 next_state_ = STATE_CTRL_WRITE_RETR; | 959 next_state_ = STATE_CTRL_WRITE_RETR; |
| 903 break; | 960 break; |
| 904 default: | 961 default: |
| 905 NOTREACHED(); | 962 NOTREACHED(); |
| 906 return Stop(ERR_UNEXPECTED); | 963 return Stop(ERR_UNEXPECTED); |
| 907 } | 964 } |
| 908 return OK; | 965 return OK; |
| 909 } | 966 } |
| 910 | 967 |
| 911 | 968 |
| 912 // CWD command | 969 // CWD command |
| 913 int FtpNetworkTransaction::DoCtrlWriteCWD() { | 970 int FtpNetworkTransaction::DoCtrlWriteCWD() { |
| 914 std::string command = "CWD " + GetRequestPathForFtpCommand(); | 971 std::string command = "CWD " + GetRequestPathForFtpCommand(true); |
| 915 next_state_ = STATE_CTRL_READ; | 972 next_state_ = STATE_CTRL_READ; |
| 916 return SendFtpCommand(command, COMMAND_CWD); | 973 return SendFtpCommand(command, COMMAND_CWD); |
| 917 } | 974 } |
| 918 | 975 |
| 919 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) { | 976 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) { |
| 920 switch (GetErrorClass(response.status_code)) { | 977 switch (GetErrorClass(response.status_code)) { |
| 921 case ERROR_CLASS_INITIATED: | 978 case ERROR_CLASS_INITIATED: |
| 922 return Stop(ERR_INVALID_RESPONSE); | 979 return Stop(ERR_INVALID_RESPONSE); |
| 923 case ERROR_CLASS_OK: | 980 case ERROR_CLASS_OK: |
| 924 next_state_ = STATE_CTRL_WRITE_LIST; | 981 next_state_ = STATE_CTRL_WRITE_LIST; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 938 return Stop(ERR_FAILED); | 995 return Stop(ERR_FAILED); |
| 939 default: | 996 default: |
| 940 NOTREACHED(); | 997 NOTREACHED(); |
| 941 return Stop(ERR_UNEXPECTED); | 998 return Stop(ERR_UNEXPECTED); |
| 942 } | 999 } |
| 943 return OK; | 1000 return OK; |
| 944 } | 1001 } |
| 945 | 1002 |
| 946 // LIST command | 1003 // LIST command |
| 947 int FtpNetworkTransaction::DoCtrlWriteLIST() { | 1004 int FtpNetworkTransaction::DoCtrlWriteLIST() { |
| 948 std::string command = "LIST"; | 1005 std::string command(system_type_ == SYSTEM_TYPE_VMS ? "LIST *.*;0" : "LIST"); |
| 949 next_state_ = STATE_CTRL_READ; | 1006 next_state_ = STATE_CTRL_READ; |
| 950 return SendFtpCommand(command, COMMAND_LIST); | 1007 return SendFtpCommand(command, COMMAND_LIST); |
| 951 } | 1008 } |
| 952 | 1009 |
| 953 int FtpNetworkTransaction::ProcessResponseLIST( | 1010 int FtpNetworkTransaction::ProcessResponseLIST( |
| 954 const FtpCtrlResponse& response) { | 1011 const FtpCtrlResponse& response) { |
| 955 switch (GetErrorClass(response.status_code)) { | 1012 switch (GetErrorClass(response.status_code)) { |
| 956 case ERROR_CLASS_INITIATED: | 1013 case ERROR_CLASS_INITIATED: |
| 957 response_.is_directory_listing = true; | 1014 response_.is_directory_listing = true; |
| 958 break; | 1015 break; |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1028 read_data_buf_->data()[0] = 0; | 1085 read_data_buf_->data()[0] = 0; |
| 1029 return data_socket_->Read(read_data_buf_, read_data_buf_len_, | 1086 return data_socket_->Read(read_data_buf_, read_data_buf_len_, |
| 1030 &io_callback_); | 1087 &io_callback_); |
| 1031 } | 1088 } |
| 1032 | 1089 |
| 1033 int FtpNetworkTransaction::DoDataReadComplete(int result) { | 1090 int FtpNetworkTransaction::DoDataReadComplete(int result) { |
| 1034 return result; | 1091 return result; |
| 1035 } | 1092 } |
| 1036 | 1093 |
| 1037 } // namespace net | 1094 } // namespace net |
| OLD | NEW |