Не удается войти в cPanel с помощью C # WebRequest - PullRequest
0 голосов
/ 22 октября 2011

Я пытаюсь разработать класс C # для входа в cPanel на веб-хосте (Hostgator).

В PHP довольно просто использовать расширение Curl следующим образом:

$url = "http://mysite.com:2082/";

$c = curl_init($url);

curl_setopt($c, CURLOPT_USERPWD, 'user:password');
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($c);
if ($result === false)
    $result = curl_error($c);
curl_close($c);

file_put_contents('log.txt', $result);
//print_r($result);

Теперь вот мой класс C # с различными попытками заставить его работать:

class HTTPHandler
{
    public static string Connect (string url, string userName, string password)
    {
        string result;

        try
        {
            // An initial @ symbol in the password must be escaped
            if (password.Length > 0)
                if (password[0] == '@')
                    password = "\\" + password;

            // Create a request for the URL.        
            WebRequest request = WebRequest.Create(url);
            request.PreAuthenticate = true;
            request.Credentials = new NetworkCredential(userName, password);

            /*
            var credCache = new CredentialCache();
            credCache.Add(new Uri(url), "Basic",
                              new NetworkCredential(userName, password));
            request.Credentials = credCache;
            */

            //request.Method = "POST";
            //request.ContentType = "application/x-www-form-urlencoded";

            /*
            // Create POST data and convert it to a byte array.
            string postData = string.Format("user={0}&pass={1}", userName, password);
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);
            request.ContentLength = byteArray.Length;
            request.ContentType = "application/x-www-form-urlencoded";
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
            */

            // Get the response.
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // Get the stream containing content returned by the server.
            Stream dataStream = response.GetResponseStream();
            // Open the stream using a StreamReader for easy access.
            StreamReader reader = new StreamReader(dataStream);
            // Display the content.
            result = string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
            // Cleanup the streams and the response.
            reader.Close();
            dataStream.Close();
            response.Close();
        }
        catch (Exception e)
        {
            result = string.Format("There was an error:\n{0}", e.Message);
        }

        return result;
    }
}

}

Но я продолжаю получать сообщение об ошибке 401 (не авторизовано) на этапе GetResponse.

Когда я сравниваю переменные $ _SERVER на моей тестовой странице локального хоста между представлениями PHP и C #, я получаю те же данные, за исключением того, что порт отправителя немного отличается. Важные PHP_AUTH_USER и PHP_AUTH_PW одинаковы.

Моя ОС - 64-битная Windows 7, и я использую Visual C # 2010.

Полагаю, решение действительно простое, но пока что я сбит с толку. Но относительный новичок в C #. Я надеюсь, что кто-то может помочь.

Ответы [ 2 ]

1 голос
/ 27 октября 2011

Это использует System.Web, где мне пришлось установить свойства проекта для использования полной версии .NET Framework 4, чтобы получить доступ к этой сборке для HttpUtility и добавить ссылку на System.Web в списке литературы.

Я не тестировал все перегруженные методы, но главное - это соединение cPanel, где учетные данные аутентификации добавляются в заголовок http, когда присутствует userName.

Также для cPanel мне нужно было установить запрос.AllowAutoRedirect = false;так что я контролирую постраничный доступ, так как мне не удалось захватить куки.

Вот код для класса HTTP Helper, который я придумал:

class HTTPHandler
{
    // Some default settings
    const string UserAgent = "Bot"; // Change this to something more meaningful
    const int TimeOut = 1000; // Time out in ms

    // Basic connection
    public static string Connect(string url)
    {
        return Connect(url, "", "", UserAgent, "", TimeOut);
    }

    // Connect with post data passed as a key : value pair dictionary
    public static string Connect(string url, Dictionary<string, string> args)
    {
        return Connect(url, "", "", UserAgent, ToQueryString(args), TimeOut);
    }

    // Connect with a custom user agent specified
    public static string Connect(string url, string userAgent)
    {
        return Connect(url, "", "", userAgent, "", TimeOut);
    }

    public static string Connect(string url, string userName, string password, string userAgent, string postData, int timeOut)
    {
        string result;

        try
        {
            // Create a request for the URL.        
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

            if (userAgent == null)
                userAgent = UserAgent;

            request.UserAgent = userAgent;
            request.Timeout = timeOut;

            if (userName.Length > 0)
            {
                string authInfo = userName + ":" + password;
                authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
                request.Headers["Authorization"] = "Basic " + authInfo;
                request.AllowAutoRedirect = false;
            }

            if (postData.Length > 0)
            {
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";

                // Create POST data and convert it to a byte array.
                byte[] byteArray = Encoding.UTF8.GetBytes(postData);
                request.ContentLength = byteArray.Length;
                using (Stream dataStream = request.GetRequestStream())
                {
                    dataStream.Write(byteArray, 0, byteArray.Length);
                }
            }

            // Get the response.
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                // Get the stream containing content returned by the server.
                Stream dataStream = response.GetResponseStream();
                // Open the stream using a StreamReader for easy access.
                using (StreamReader reader = new StreamReader(dataStream))
                {
                    result = string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
                }
            }
        }

        catch (Exception e)
        {
            result = string.Format("There was an error:\n{0}", e.Message);
        }

        return result;
    }

    public static string ToQueryString(Dictionary<string, string> args)
    {
        List<string> encodedData = new List<string>();

        foreach (KeyValuePair<string, string> pair in args)
        {
            encodedData.Add(HttpUtility.UrlEncode(pair.Key) + "=" + HttpUtility.UrlEncode(pair.Value));
        }

        return String.Join("&", encodedData.ToArray());
    }
}
1 голос
/ 22 октября 2011

Вам на самом деле не нужно устанавливать PreAuthenticate, просто дайте запросу разобраться. Также я бы предложил использовать HttpWebRequest вместо WebRequest. Основное отличие состоит в том, что вы можете установить свойство CookieContainer, чтобы включить куки. Это немного сбивает с толку, поскольку по умолчанию в нем отключены файлы cookie, и все, что вам нужно сделать, это установить его на new CookieContainer();, чтобы включить файлы cookie для вашего запроса. Это имеет значение из-за перенаправлений, которые происходят во время аутентификации, и файла cookie для аутентификации, который записывает факт успешной аутентификации.

Также примечание по стилю кодирования: пожалуйста, обязательно включите все IDisposable s (например, ответ, поток и читатель) в оператор using().

Также мне неясно, почему вы экранировали @ в пароле. Запрос должен автоматически позаботиться обо всех ваших потребностях в кодировании.

Полный пример кода:

var request = WebRequest.CreateHttp(url);
request.Credentials = new NetworkCredential(username, password);
request.CookieContainer = new CookieContainer(); // needed to enable cookies

using (var response = (HttpWebResponse)request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet)))
    return string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());

edit: Извините за все правки. Я писал код по памяти и с трудом разбирался в правильной части кодирования.

...