/**
  * Executes a request.
  * This is a pretty generic PHP function for the most part.  The only parts that are specific to Picasa
  * is the errors that are thrown.  PHP doesn't have a built in function that does this very well (although there are packages
  * for it that can be installed), so this manually sets the HTTP headers and parses the whole response.
  *
  * @access protected
  * @static
  * @param string $host       The destination address to send the request to.  It should not include a protocol.
  * @param string $path       The path on the host to send the request to.  It should start with a "/"
  * @param string $data       Any data that should be sent along with the request.  If there is no data, leave it as null.
  * @param string $request    The type of request to perform.  Most common are GET, POST, PUT, and DELETE.  The type of
  *                           request to use for each Picasa function is defined in Picasa's official documentation.
  * @param array $specialHeaders  An array of strings of headers that should be sent with the request.  The headers that are
  *                               always sent are Host, Content-Type, and Content-Length.  The most common type of special
  *                               header is an authorization header from Google.  Note that even if there is only one special header,
  *                               it still must be in an array.  Also note that each line in the array should end in "\r\n" and
  *                               should be set in the array with double quotes, not single quotes, because "\r\n" is interpreted
  *                               differently by PHP if single quotes are used.  Optional, the default is null.
  * @param string $type       The type of content that will is being sent through the request.  Optional, the default is
  *                           "application/x-www-form-urlencoded".
  * @param string $protocol   The protocol to use when sending the request.  Secure requests should use "ssl://".  Note that
  *                           the protocol must end in "://" unless it's an empty string.  For HTTP, this parameter can be left
  *                           as an empty string.  Optional, the default is an empty string.
  * @param int $port          The port number to send the request to.  Different protocols have different port numbers.  HTTP protocol
  *                           uses port 80 and SSL uses port 443.  Optional, the default is 80.
  * @return string            The entire response, including headers, that was recieved from the host.
  * @throws {@link Picasa_Exception}  If a response of "200" or "201" is not recieved.  In this case, the entire contents of the response,
  *                                   including headers, is set to the $response field of the exception and the error supplied by
  *                                   Picasa is set as the exceptions message.  The idea is that the calling method can search the
  *                                   response for a specific return code (for instance, a File Not Found or Forbidden error) and throw
  *                                   a more specific exception.  The caller can also search the response for values that are specific
  *                                   to its request, such as a Captcha URL.
  */
 protected static function do_request($host, $path, $data, $request, $specialHeaders = null, $type = "application/x-www-form-urlencoded", $protocol = "", $port = "80")
 {
     $contentlen = strlen($data);
     $req = "{$request} {$path} HTTP/1.1\r\nHost: {$host}\r\nContent-Type: {$type}\r\nContent-Length: {$contentlen}\r\n";
     if (is_array($specialHeaders)) {
         foreach ($specialHeaders as $header) {
             $req .= $header;
         }
     }
     $req .= "Connection: close\r\n\r\n";
     if ($data != null) {
         $req .= $data;
     }
     Picasa_Logger::getLogger()->logIfEnabled("Request to do: " . $request);
     $fp = fsockopen($protocol . $host, $port, $errno, $errstr);
     if (!$fp) {
         throw new Picasa_Exception($errstr);
     }
     fputs($fp, $req);
     $buf = "";
     if (!feof($fp)) {
         $buf = @fgets($fp);
     }
     Picasa_Logger::getLogger()->logIfEnabled("Buffer returned: " . $buf);
     // If either a 200 or 201 response is not found, there was a problem so throw an exception
     if (preg_match("/200 /i", $buf) || preg_match("/201 /i", $buf)) {
         while (!feof($fp)) {
             $buf .= @fgets($fp);
         }
         fclose($fp);
         return $buf;
     } else {
         /* In the response returned from Picasa, it is really hard to pull out the error message.
          * Its location is two lines below the "Connection: Close" line.  So that message is pulled
          * out, if possible, and set as the Exception's message.  Also, the entire buffer is sent.
          * This way, the caller can throw it's own message by looking for its own response code.
          */
         $expMessage = "An unknown error has occured while sending a {$request} request.";
         $break = false;
         $tmpBuf = "";
         while (!feof($fp)) {
             $tmpBuf = @fgets($fp);
             $buf .= $tmpBuf;
             if (strcmp($tmpBuf, "Connection: Close\r\n") == 0) {
                 for ($i = 0; !feof($fp); $i++) {
                     if ($i == 2) {
                         $expMessage = @fgets($fp);
                         $buf .= $expMessage;
                         $break = true;
                     } else {
                         $buf .= @fgets($fp);
                     }
                 }
             }
         }
         if (!$break) {
             $msg = Picasa::getResponseValue($buf, "Error");
             if ($msg != null) {
                 $expMessage = $msg;
             }
         }
         throw new Picasa_Exception($expMessage, $buf, $host . $path);
     }
 }