예제 #1
0
/**
 * Lecture de la réponse jusqu'à un certain caractère (exclu) ou la fin de ligne
 * Le caractère '\r' est ignoré
 *	@return int nb de caractères lus (0 signifie que le premier caractère était ce caractère ou une fin de ligne)
 *		 HTTP_ERROR s'il y a eu une erreur
 *		 HTTP_ERROR_TIMEOUT si le timeout a été dépassé
 */
int HttpClient::readLineToChar(char sep, char* key,int maxlen){
	if(debug) Serial.println(F("HttpClient.readLineToChar"));
	for(int nbChars=0;;){
		char c = httpRead();

		if(c==sep || c==0) return nbChars;	//Séparateur ou fin de fichier
		if(c<0) return c;	//httpRead a renvoyé une erreur
		if(c==10) continue;	//on ignore le '\r'
		if(c==13 && nbChars==0 && status!=HTTP_STATUS_RESP_BODY){
			//C'est la fin des en-têtes
			status = HTTP_STATUS_RESP_BODY;
			return 0;
		}
		if(status==HTTP_STATUS_RESP_HEADER && !(c>='A' && c<='Z' || c>='a' && c<='z' || c>='0' && c<='9' || c=='_' || c=='-')){
			//Caractère invalide
			if(debug){
				Serial.print(F("Caractere invalide:")); Serial.print(c); Serial.println(c,DEC);
			}
			return error(HTTP_ERROR);
		}

		nbChars++;
		if(nbChars>maxlen) return error(HTTP_ERROR_BUFFER_OVERFLOW);
		key[nbChars-1] = c;
		key[nbChars] = '\0';
	}
}
예제 #2
0
파일: ejsHttp.c 프로젝트: soffmodd/ejs-2
/*  
    Read the required number of bytes into the response content buffer. Count < 0 means transfer the entire content.
    Returns the number of bytes read. Returns null on EOF.
 */ 
static ssize readHttpData(Ejs *ejs, EjsHttp *hp, ssize count)
{
    MprBuf      *buf;
    HttpConn    *conn;
    ssize       len, space, nbytes;

    conn = hp->conn;
    buf = hp->responseContent;
    mprResetBufIfEmpty(buf);
    while (count < 0 || mprGetBufLength(buf) < count) {
        len = (count < 0) ? BIT_MAX_BUFFER : (count - mprGetBufLength(buf));
        space = mprGetBufSpace(buf);
        if (space < len) {
            mprGrowBuf(buf, len - space);
        }
        if ((nbytes = httpRead(conn, mprGetBufEnd(buf), len)) < 0) {
            ejsThrowIOError(ejs, "Cannot read required data");
            return MPR_ERR_CANT_READ;
        }
        mprAdjustBufEnd(buf, nbytes);
        if (hp->conn->async || (nbytes == 0 && conn->state > HTTP_STATE_CONTENT)) {
            break;
        }
    }
    if (count < 0) {
        return mprGetBufLength(buf);
    }
    return min(count, mprGetBufLength(buf));
}
예제 #3
0
파일: http.c 프로젝트: adammendoza/http
static void readBody(HttpConn *conn, MprFile *outFile)
{
    char        buf[BIT_MAX_BUFFER];
    cchar       *result;
    ssize       bytes;

    while (!conn->error && conn->sock && (bytes = httpRead(conn, buf, sizeof(buf))) > 0) {
        if (!app->noout) {
            result = formatOutput(conn, buf, &bytes);
            if (result) {
                mprWriteFile(outFile, result, bytes);
            }
        }
#if FUTURE
        //  This should be pushed into a range filter.
        //  Buffer all output and then parsing can work
        type = httpGetHeader(conn, "Content-Type");
        if (scontains(type, "multipart/byteranges")) {
            if ((boundary = scontains(type, "boundary=")) != 0) {
                boundary += 9;
                if (*boundary) {
                    boundary = sfmt("--%s\r\n", boundary);
                }
            }
        }
#endif
    }
}
예제 #4
0
파일: gtkcupsutils.c 프로젝트: fluendo/gtk
static void 
_get_read_data (GtkCupsRequest *request)
{
  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
  gsize bytes;
  gsize bytes_written;
  GIOStatus io_status;
  GError *error;

  GTK_NOTE (PRINTING,
            g_print ("CUPS Backend: %s\n", G_STRFUNC));

  error = NULL;

  request->poll_state = GTK_CUPS_HTTP_READ;

#if HAVE_CUPS_API_1_2
  bytes = httpRead2 (request->http, buffer, sizeof (buffer));
#else
  bytes = httpRead (request->http, buffer, sizeof (buffer));
#endif /* HAVE_CUPS_API_1_2 */
  request->bytes_received += bytes;

  GTK_NOTE (PRINTING,
            g_print ("CUPS Backend: %" G_GSIZE_FORMAT " bytes read\n", bytes));
  
  io_status =
    g_io_channel_write_chars (request->data_io, 
                              buffer, 
			      bytes, 
			      &bytes_written,
			      &error);

  if (io_status == G_IO_STATUS_ERROR)
    {
      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;
    
      gtk_cups_result_set_error (request->result,
                                 GTK_CUPS_ERROR_IO,
                                 io_status,
                                 error->code, 
                                 error->message);
      g_error_free (error);
    }

  /* Stop if we do not expect any more data or EOF was received. */
#if HAVE_CUPS_API_1_2
  if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
#else
  if (httpGetLength (request->http) <= request->bytes_received || bytes == 0)
#endif /* HAVE_CUPS_API_1_2 */
    {
      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;

      return;
    }
}
예제 #5
0
파일: http.c 프로젝트: varphone/ejs-2
static void readBody(HttpConn *conn)
{
    char    buf[HTTP_BUFSIZE];
    ssize   bytes;

    while (!conn->error && conn->sock && (bytes = httpRead(conn, buf, sizeof(buf))) > 0) {
        showOutput(conn, buf, bytes);
    }
}
예제 #6
0
파일: client.c 프로젝트: embedthis/http
PUBLIC char *httpReadString(HttpStream *stream)
{
    HttpRx      *rx;
    ssize       sofar, nbytes, remaining;
    char        *content;

    rx = stream->rx;
    if (rx->length < 0) {
        return 0;
    }
    remaining = (ssize) min(MAXSSIZE, rx->length);

    if (remaining > 0) {
        if ((content = mprAlloc(remaining + 1)) == 0) {
            return 0;
        }
        sofar = 0;
        while (remaining > 0) {
            nbytes = httpRead(stream, &content[sofar], remaining);
            if (nbytes < 0) {
                return 0;
            }
            sofar += nbytes;
            remaining -= nbytes;
        }
    } else {
        content = NULL;
        sofar = 0;
        while (1) {
            if ((content = mprRealloc(content, sofar + ME_BUFSIZE)) == 0) {
                return 0;
            }
            nbytes = httpRead(stream, &content[sofar], ME_BUFSIZE);
            if (nbytes < 0) {
                return 0;
            } else if (nbytes == 0) {
                break;
            }
            sofar += nbytes;
        }
    }
    content[sofar] = '\0';
    return content;
}
예제 #7
0
파일: gtkcupsutils.c 프로젝트: krh/gtk
static void 
_get_read_data (GtkCupsRequest *request)
{
  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
  gsize bytes;
  gsize bytes_written;
  GIOStatus io_status;
  GError *error;

  GTK_NOTE (PRINTING,
            g_print ("CUPS Backend: %s\n", G_STRFUNC));

  error = NULL;

  request->poll_state = GTK_CUPS_HTTP_READ;

#if HAVE_CUPS_API_1_2
  bytes = httpRead2 (request->http, buffer, sizeof (buffer));
#else
  bytes = httpRead (request->http, buffer, sizeof (buffer));
#endif /* HAVE_CUPS_API_1_2 */

  GTK_NOTE (PRINTING,
            g_print ("CUPS Backend: %i bytes read\n", bytes));
  
  if (bytes == 0)
    {
      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;

      return;
    }
  
  io_status =
    g_io_channel_write_chars (request->data_io, 
                              buffer, 
			      bytes, 
			      &bytes_written,
			      &error);

  if (io_status == G_IO_STATUS_ERROR)
    {
      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;
    
      gtk_cups_result_set_error (request->result,
                                 GTK_CUPS_ERROR_IO,
                                 io_status,
                                 error->code, 
                                 error->message);
      g_error_free (error);
    }
}
예제 #8
0
파일: queue.c 프로젝트: adammendoza/http
/*
    Read data as a string
 */
PUBLIC char *httpReadString(HttpConn *conn)
{
    HttpRx      *rx;
    ssize       sofar, nbytes, remaining;
    char        *content;

    rx = conn->rx;
    remaining = (ssize) min(MAXSSIZE, rx->length);

    if (remaining > 0) {
        if ((content = mprAlloc(remaining + 1)) == 0) {
            return 0;
        }
        sofar = 0;
        while (remaining > 0) {
            nbytes = httpRead(conn, &content[sofar], remaining);
            if (nbytes < 0) {
                return 0;
            }
            sofar += nbytes;
            remaining -= nbytes;
        }
    } else {
        content = mprAlloc(BIT_MAX_BUFFER);
        sofar = 0;
        while (1) {
            nbytes = httpRead(conn, &content[sofar], BIT_MAX_BUFFER);
            if (nbytes < 0) {
                return 0;
            } else if (nbytes == 0) {
                break;
            }
            sofar += nbytes;
            content = mprRealloc(content, sofar + BIT_MAX_BUFFER);
        }
    }
    content[sofar] = '\0';
    return content;
}
예제 #9
0
int HttpClient::moveToEOL(){
	if(status==HTTP_STATUS_IDLE){
		//La réponse n'a pas commencé
		if(debug) Serial.println("Error: Status not IDLE");
		return error(HTTP_ERROR_STATUS);
	}
	for(int nbChars=0;;nbChars++){
		int c = httpRead();
		if(c<0) return c;	//httpRead a renvoyé une erreur
		if(c=='\n'){
			return 0;
		}
	}
}
예제 #10
0
파일: http.c 프로젝트: gitorup/appweb
static void readBody(HttpConn *conn, MprFile *outFile)
{
    char        buf[ME_MAX_BUFFER];
    cchar       *result;
    ssize       bytes;

    while (!conn->error && (bytes = httpRead(conn, buf, sizeof(buf))) > 0) {
        if (!app->noout) {
            result = formatOutput(conn, buf, &bytes);
            if (result) {
                mprWriteFile(outFile, result, bytes);
            }
        }
    }
}
예제 #11
0
파일: conn.c 프로젝트: DavidQuan/http
/*
    Eat remaining input incase last request did not consume all data
 */
static void consumeLastRequest(HttpConn *conn)
{
    char    junk[4096];

    if (conn->state >= HTTP_STATE_FIRST) {
        while (!httpIsEof(conn) && !httpRequestExpired(conn, 0)) {
            if (httpRead(conn, junk, sizeof(junk)) <= 0) {
                break;
            }
        }
    }
    if (HTTP_STATE_CONNECTED <= conn->state && conn->state < HTTP_STATE_COMPLETE) {
        conn->keepAliveCount = 0;
    }
}
예제 #12
0
/**
 * Attend le début de la réponse, lit la première ligne de la réponse, avec le code réponse
 * Fait suite à l'envoi d'une requête.
 *	@return int code réponse
 *		 HTTP_ERROR s'il y a eu une erreur
 *		 HTTP_ERROR_TIMEOUT si le timeout a été dépassé
 *               HTTP_ERROR_STATUS status du client est anormal
 */
int HttpClient::readResponseCode(){
	if(debug) Serial.print(F("HttpClient.readResponseCode..."));
	if(status!=HTTP_STATUS_RESP_WAIT){
		if(debug) Serial.println(F("Excepted status:WAIT"));
		return error(HTTP_ERROR_STATUS);
	}
	char* intro = "HTTP/1.1 ";
	int result = 0;
	startMillis = millis();
	
	for(int charIdx=0;;charIdx++){
		char c = httpRead();

		if(c<0) return c;	//C'est une erreur
		
		if(charIdx<=8){
			if(debug) Serial.print(c);
			//Vérification du "HTTP/1.1 "
			if(c==intro[charIdx] || (charIdx==7 && c=='0')){
				//OK
			}
			else{
				if(debug) Serial.print(F(": HTTP/1.1 not verified"));
				return error(HTTP_ERROR);
			}
		}
		else if(charIdx<=11){
			//Lecture du code réponse
			if(c<'0' || c >'9'){
				if(debug) Serial.print(F("Invalid char (")); Serial.print(charIdx); Serial.print("):"); Serial.print(c);
				return error(HTTP_ERROR);
			}
			int tmp = 1;
			for(uint8_t i=0;i<11-charIdx;i++) tmp = tmp*10;
			tmp = (c-48)*tmp;
			result += tmp;
			//Serial.print("c=");Serial.print(c-48,DEC);Serial.print(" ");Serial.print(tmp);Serial.print("   idx=");Serial.print(charIdx,DEC);Serial.print("  ->");Serial.println(result,DEC);
			//if(debug) Serial.println(result);
		}
		else{
			//On passe le reste de la ligne, et on renvoie le résultat
			if(int res=moveToEOL()<0) return error(res);
			status = HTTP_STATUS_RESP_HEADER;	//La ligne suivante est une en-tête
			if(debug) Serial.println(result);
			return result;
		}
	}
}
예제 #13
0
/**
 * Passe la suites des en-têtes HTTP
 *	@return 0 si tout s'est bien passé
 *		 HTTP_ERROR s'il y a eu une erreur
 *		 HTTP_ERROR_TIMEOUT si le timeout a été dépassé
 */
int HttpClient::skipHeaders(){
	if(debug) Serial.println(F("HttpClient.skipHeaders"));
	if(status!=HTTP_STATUS_RESP_HEADER){
		//On n'est pas dans les en-têtes
		if(debug) Serial.println("Excepted status:RESP_HEADER");
		return error(HTTP_ERROR_STATUS);
	}
	
	char prevChar;
	char c = 0;
	while(c!=13 || prevChar!=13) //tant qu'il n'y a pas deux caractères '\n' consécutifs
	{
		prevChar = c;
		c = httpRead();
		if(c<0) return error(c);	//httpRead a renvoyé une erreur
		if(c==10){
			c = prevChar;	
			prevChar = 0;
			continue;
		}
	}
	status = HTTP_STATUS_RESP_BODY;
	return 0;
}
예제 #14
0
void FQTermHttp::getLink(const QUrl& url, bool preview) {
  QUrl u=url;
  isExisting_ = false;
  isPreview_ = preview;
  previewEmitted = false;
  lastPercent_ = 0;
  if (u.isRelative() || u.scheme() == "file") {
    emit previewImage(cacheFileName_, false, true);
    emit done(this);
    return ;
  }

  if (QFile::exists(getPath(USER_CONFIG) + "hosts.cfg")) {
    config_ = new FQTermConfig(getPath(USER_CONFIG) + "hosts.cfg");
    QString strTmp = config_->getItemValue("hosts", u.host().toLocal8Bit());
    if (!strTmp.isEmpty()) {
      QString strUrl = u.toString();
      strUrl.replace(QRegExp(u.host(), Qt::CaseInsensitive), strTmp);
      u = strUrl;
    }
  }
  if (!(netreply_ && netreply_->hasRawHeader("Location"))) {
    cacheFileName_ = QFileInfo(u.path()).fileName();
  }
  if(netreply_){
    netreply_->blockSignals(true);
    netreply_.take()->deleteLater();
  }

  netreply_.reset(nam_->get(QNetworkRequest(u)));
  FQ_VERIFY(connect(netreply_.data(), SIGNAL(finished()), this, SLOT(httpDone())));
  FQ_VERIFY(connect(netreply_.data(), SIGNAL(downloadProgress(qint64, qint64)),this, SLOT(httpRead(qint64, qint64))));
  FQ_VERIFY(connect(netreply_.data(), SIGNAL(error( QNetworkReply::NetworkError)), this, SLOT(httpError(QNetworkReply::NetworkError))));
  FQ_VERIFY(connect(netreply_.data(), SIGNAL(metaDataChanged()), this, SLOT(httpResponse())));
}
예제 #15
0
파일: espAbbrev.c 프로젝트: embedthis/esp
PUBLIC ssize receive(char *buf, ssize len)
{
    return httpRead(getStream(), buf, len);
}
예제 #16
0
static void
httpGetObject(App *app, HTTP *http, int sock)
{
	Buf		*buf;
	HTTPNameValue	*h;

	app->printHTML(app, "<h4>Request</h4>");
	app->printHTML(app, "<pre>");

	buf = bufAlloc(sock);

	bufMark(buf, 0);
	bufPutString(buf, (unsigned char *) "GET ");
	if (http->url->path)
	{
		bufPutString(buf, http->url->path);
	}
	if (http->url->params)
	{
		bufPutString(buf, http->url->params);
	}
	if (http->url->query)
	{
		bufPutString(buf, http->url->query);
	}
	bufPutChar(buf, ' ');
	bufPutString(buf, http->version);
	bufPutString(buf, (unsigned char *) "\r\n");
	bufMark(buf, 0);
	app->httpRequest(app, buf);

	h = http->headers;
	if (h)
	{
		while (h->name)
		{
			httpPutHeader(app, buf, (char *) h->name,
				(char *) h->value);
			h++;
		}
	}

	httpPutHeader(app, buf, "Connection", "close");

	httpPutHeader(app, buf, "Host", (char *) http->url->host);

	bufPutString(buf, (unsigned char *) "\r\n");
	bufMark(buf, 0);
	app->httpRequest(app, buf);

	app->printHTML(app, "</pre>");

	if (bufError(buf))
	{
		return;
	}

	bufSend(buf);

	bufFree(buf);

	httpRead(app, http, sock);
}
예제 #17
0
ssize espReceive(HttpConn *conn, char *buf, ssize len)
{
    return httpRead(conn, buf, len);
}
예제 #18
0
파일: espmsg.c 프로젝트: timfel/ftlk-stuff
void
translate_messages(const char *language)/* I - Output language... */
{
 /*
  * Google provides a simple translation/language tool for translating
  * from one language to another.  It is far from perfect, however it
  * can be used to get a basic translation done or update an existing
  * translation when no other resources are available.
  *
  * Translation requests are sent as HTTP POSTs to
  * "http://translate.google.com/translate_t" with the following form
  * variables:
  *
  *   Name      Description                         Value
  *   --------  ----------------------------------  ----------------
  *   hl        Help language?                      "en"
  *   ie        Input encoding                      "UTF8"
  *   langpair  Language pair                       "en|" + language
  *   oe        Output encoding                     "UTF8"
  *   text      Text to translate                   translation string
  */

  int		i;			/* Looping var */
  message_t     *m;			/* Current message */
  int		tries;			/* Number of tries... */
  http_t	*http;			/* HTTP connection */
  http_status_t	status;			/* Status of POST request */
  char		*idptr,			/* Pointer into msgid */
		*strptr,		/* Pointer into msgstr */
		buffer[65536],		/* Input/output buffer */
		*bufptr,		/* Pointer into buffer */
		*bufend,		/* Pointer to end of buffer */
		length[16];		/* Content length */
  unsigned char	*utf8;			/* UTF-8 string */
  int		ch;			/* Current decoded character */
  int		bytes;			/* Number of bytes read */


 /*
  * Connect to translate.google.com...
  */

  puts("Connecting to translate.google.com...");

  if ((http = httpConnect("translate.google.com", 80)) == NULL)
  {
    perror("Unable to connect to translate.google.com");
    return;
  }

 /*
  * Scan the current messages, requesting a translation of any untranslated
  * messages...
  */

  for (i = num_messages, m = messages; i > 0; i --, m ++)
  {
   /*
    * Skip messages that are already translated...
    */

    if (m->str && m->str[0])
      continue;

   /*
    * Encode the form data into the buffer...
    */

    snprintf(buffer, sizeof(buffer),
             "hl=en&ie=UTF8&langpair=en|%s&oe=UTF8&text=", language);
    bufptr = buffer + strlen(buffer);
    bufend = buffer + sizeof(buffer) - 5;

    for (idptr = m->id; *idptr && bufptr < bufend; idptr ++)
      if (*idptr == ' ')
        *bufptr++ = '+';
      else if (*idptr < ' ' || *idptr == '%')
      {
        sprintf(bufptr, "%%%02X", *idptr & 255);
	bufptr += 3;
      }
      else if (*idptr != '&')
        *bufptr++ = *idptr;

    *bufptr++ = '&';
    *bufptr = '\0';

    sprintf(length, "%d", bufptr - buffer);

   /*
    * Send the request...
    */

    printf("\"%s\" = ", m->id);
    fflush(stdout);

    tries = 0;

    do
    {
      httpClearFields(http);
      httpSetField(http, HTTP_FIELD_CONTENT_TYPE,
                   "application/x-www-form-urlencoded");
      httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, length);

      if (httpPost(http, "/translate_t"))
      {
	httpReconnect(http);
	httpPost(http, "/translate_t");
      }

      httpWrite(http, buffer, bufptr - buffer);

      while ((status = httpUpdate(http)) == HTTP_CONTINUE);

      if (status != HTTP_OK && status != HTTP_ERROR)
        httpFlush(http);

      tries ++;
    }
    while (status == HTTP_ERROR && tries < 10);

    if (status == HTTP_OK)
    {
     /*
      * OK, read the translation back...
      */

      bufptr = buffer;
      bufend = buffer + sizeof(buffer) - 1;

      while ((bytes = httpRead(http, bufptr, bufend - bufptr)) > 0)
        bufptr += bytes;

      if (bytes < 0)
      {
       /*
        * Read error, abort!
	*/

        puts("READ ERROR!");
	break;
      }

      *bufptr = '\0';

     /*
      * Find the first textarea element - that will have the translation data...
      */

      if ((bufptr = strstr(buffer, "<textarea")) == NULL)
      {
       /*
        * No textarea, abort!
	*/

        puts("NO TEXTAREA!");
	break;
      }

      if ((bufptr = strchr(bufptr, '>')) == NULL)
      {
       /*
        * textarea doesn't end, abort!
	*/

        puts("TEXTAREA SHORT DATA!");
	break;
      }

      utf8 = (unsigned char *)bufptr + 1;

      if ((bufend = strstr(bufptr, "</textarea>")) == NULL)
      {
       /*
        * textarea doesn't close, abort!
	*/

        puts("/TEXTAREA SHORT DATA!");
	break;
      }

      *bufend = '\0';

     /*
      * Copy the UTF-8 translation to ISO-8859-1 (for now)...
      */

      m->str = malloc(bufend - bufptr);

      for (strptr = m->str; *utf8;)
        if (*utf8 < 0x80)
	  *strptr++ = *utf8++;
	else
	{
	  if ((*utf8 & 0xe0) == 0xc0)
	  {
	   /*
	    * Two-byte encoding...
	    */

            ch   = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f);
	    utf8 += 2;
	  }
	  else if ((ch & 0xf0) == 0xe0)
	  {
	   /*
	    * Three-byte encoding...
	    */

            ch   = ((((utf8[0] & 0x0f) << 6) | (utf8[1] & 0x3f)) << 6) |
	           (utf8[2] & 0x3f);
	    utf8 += 3;
	  }

          if (ch < 256)			/* ISO-8859-1 */
	    *strptr++ = ch;
	  else if (ch == 0x20ac)	/* Euro */
	    *strptr++ = 0xA4;		/* ISO-8859-15 mapping */
	}

      *strptr = '\0';

      printf("\"%s\"\n", m->str);
    }
    else if (status == HTTP_ERROR)
    {
      printf("NETWORK ERROR (%s)!\n", strerror(httpError(http)));
      break;
    }
    else
    {
      printf("HTTP ERROR %d!\n", status);
      break;
    }
  }

  httpClose(http);
}
예제 #19
0
std::string TcpServer::getRequest(int clientFd) const {
    return httpRead(clientFd);
}
예제 #20
0
파일: file.c 프로젝트: svn2github/htmldoc
char *					/* O - Pathname or NULL */
file_find(const char *path,		/* I - Path "dir;dir;dir" */
          const char *s)		/* I - File to find */
{
  int		i;			/* Looping var */
  int		retry;			/* Current retry */
  char		*temp,			/* Current position in filename */
		method[HTTP_MAX_URI],	/* Method/scheme */
		username[HTTP_MAX_URI],	/* Username:password */
		hostname[HTTP_MAX_URI],	/* Hostname */
		resource[HTTP_MAX_URI];	/* Resource */
  int		port;			/* Port number */
  const char	*connhost;		/* Host to connect to */
  int		connport;		/* Port to connect to */
  char		connpath[HTTP_MAX_URI],	/* Path for GET */
		connauth[HTTP_MAX_VALUE];/* Auth string */
  http_status_t	status;			/* Status of request... */
  FILE		*fp;			/* Web file */
  int		bytes,			/* Bytes read */
		count,			/* Number of bytes so far */
		total;			/* Total bytes in file */
  static char	filename[HTTP_MAX_URI];	/* Current filename */


 /*
  * If the filename is NULL, return NULL...
  */

  if (s == NULL)
    return (NULL);

  if (strncmp(s, "http:", 5) == 0 ||
      (path != NULL && strncmp(path, "http:", 5) == 0))
    strcpy(method, "http");
#ifdef HAVE_LIBSSL
  else if (strncmp(s, "https:", 6) == 0 ||
           (path != NULL && strncmp(path, "https:", 6) == 0))
    strcpy(method, "https");
#endif /* HAVE_LIBSSL */
  else
    strcpy(method, "file");

  if (strcmp(method, "file") == 0)
  {
   /*
    * If the path is NULL or empty, return the filename...
    */

    if (path == NULL || !path[0])
      return ((char *)s);

   /*
    * Else loop through the path string until we reach the end...
    */

    while (*path != '\0')
    {
     /*
      * Copy the path directory...
      */

      temp = filename;

      while (*path != ';' && *path && temp < (filename + sizeof(filename) - 1))
	*temp++ = *path++;

      if (*path == ';')
	path ++;

     /*
      * Append a slash as needed...
      */

      if (temp > filename && temp < (filename + sizeof(filename) - 1) &&
          s[0] != '/')
	*temp++ = '/';

     /*
      * Append the filename...
      */

      strncpy(temp, s, sizeof(filename) - (temp - filename));
      filename[sizeof(filename) - 1] = '\0';

     /*
      * See if the file exists...
      */

      if (!access(filename, 0))
	return (filename);
    }
  }
  else
  {
   /*
    * Remote file; look it up in the web cache, and then try getting it
    * from the remote system...
    */

    for (i = 0; i < web_files; i ++)
      if (web_cache[i].url && strcmp(web_cache[i].url, s) == 0)
        return (web_cache[i].name);

#ifdef HAVE_LIBSSL
    if (strncmp(s, "http:", 5) == 0 || strncmp(s, "https:", 6) == 0)
#else
    if (strncmp(s, "http:", 5) == 0)
#endif /* HAVE_LIBSSL */
      httpSeparate(s, method, username, hostname, &port, resource);
    else if (s[0] == '/')
    {
      httpSeparate(path, method, username, hostname, &port, resource);
      strcpy(resource, s);
    }
    else
    {
      if (strncmp(s, "./", 2) == 0)
        snprintf(filename, sizeof(filename), "%s/%s", path, s + 2);
      else
        snprintf(filename, sizeof(filename), "%s/%s", path, s);

      httpSeparate(filename, method, username, hostname, &port, resource);
    }

    for (status = HTTP_ERROR, retry = 0;
         status == HTTP_ERROR && retry < 5;
         retry ++)
    {
      if (proxy_port)
      {
       /*
        * Send request to proxy host...
        */

        connhost = proxy_host;
        connport = proxy_port;
        snprintf(connpath, sizeof(connpath), "%s://%s:%d%s", method,
                 hostname, port, resource);
      }
      else
      {
       /*
        * Send request to host directly...
        */

        connhost = hostname;
        connport = port;
        strcpy(connpath, resource);
      }

      if (http != NULL && strcasecmp(http->hostname, hostname) != 0)
      {
        httpClose(http);
        http = NULL;
      }

      if (http == NULL)
      {
        progress_show("Connecting to %s...", connhost);
        atexit(file_cleanup);

#ifdef HAVE_LIBSSL
        if (strcmp(method, "http") == 0)
          http = httpConnect(connhost, connport);
	else
          http = httpConnectEncrypt(connhost, connport, HTTP_ENCRYPT_ALWAYS);
#else
        http = httpConnect(connhost, connport);
#endif /* HAVE_LIBSSL */

        if (http == NULL)
	{
          progress_hide();
          progress_error("Unable to connect to %s!", connhost);
          return (NULL);
        }
      }

      progress_show("Getting %s...", connpath);

      httpClearFields(http);
      httpSetField(http, HTTP_FIELD_HOST, hostname);
      httpSetField(http, HTTP_FIELD_USER_AGENT, "HTMLDOC v" SVERSION);
      httpSetField(http, HTTP_FIELD_CONNECTION, "Keep-Alive");

      if (username[0])
      {
        strcpy(connauth, "Basic ");
        httpEncode64(connauth + 6, username);
        httpSetField(http, HTTP_FIELD_AUTHORIZATION, connauth);
      }

      if (!httpGet(http, connpath))
	while ((status = httpUpdate(http)) == HTTP_CONTINUE);
      else
	status = HTTP_ERROR;

      if (status >= HTTP_MOVED_PERMANENTLY && status <= HTTP_SEE_OTHER)
      {
       /*
        * Flush text...
	*/

	httpFlush(http);

       /*
        * Grab new location from HTTP data...
	*/

        httpSeparate(httpGetField(http, HTTP_FIELD_LOCATION), method, username,
	             hostname, &port, resource);
        status = HTTP_ERROR;
      }
    }

    if (status != HTTP_OK)
    {
      progress_hide();
      progress_error("HTTP error %d: %s!", status, httpStatus(status));
      httpFlush(http);
      return (NULL);
    }

    if ((fp = file_temp(filename, sizeof(filename))) == NULL)
    {
      progress_hide();
      progress_error("Unable to create temporary file \"%s\": %s", filename,
                     strerror(errno));
      httpFlush(http);
      return (NULL);
    }

    if ((total = atoi(httpGetField(http, HTTP_FIELD_CONTENT_LENGTH))) == 0)
      total = 8192;

    count = 0;
    while ((bytes = httpRead(http, resource, sizeof(resource))) > 0)
    {
      count += bytes;
      progress_update((100 * count / total) % 101);
      fwrite(resource, 1, bytes, fp);
    }

    progress_hide();

    fclose(fp);

    web_cache[web_files - 1].name = strdup(filename);
    web_cache[web_files - 1].url  = strdup(s);

    return (filename);
  }

  return (NULL);
}