예제 #1
0
void
BHttpRequest::_ParseStatus()
{
	// Status line should be formatted like: HTTP/M.m SSS ...
	// With:   M = Major version of the protocol
	//         m = Minor version of the protocol
	//       SSS = three-digit status code of the response
	//       ... = additional text info
	BString statusLine;
	if (_GetLine(statusLine) == B_ERROR)
		return;

	if (statusLine.CountChars() < 12)
		return;

	fRequestStatus = kRequestStatusReceived;

	BString statusCodeStr;
	BString statusText;
	statusLine.CopyInto(statusCodeStr, 9, 3);
	_SetResultStatusCode(atoi(statusCodeStr.String()));

	statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13);

	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)",
		atoi(statusCodeStr.String()), _ResultStatusText().String());
}
예제 #2
0
void
BHttpRequest::_ParseHeaders()
{
	BString currentHeader;
	while (_GetLine(currentHeader) != B_ERROR) {
		// An empty line means the end of the header section
		if (currentHeader.Length() == 0) {
			fRequestStatus = kRequestHeadersReceived;
			return;
		}

		_EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s",
			currentHeader.String());
		fHeaders.AddHeader(currentHeader.String());
	}
}
예제 #3
0
status_t
BHttpRequest::_MakeRequest()
{
	delete fSocket;

	if (fSSL)
		fSocket = new(std::nothrow) CheckedSecureSocket(this);
	else
		fSocket = new(std::nothrow) BSocket();

	if (fSocket == NULL)
		return B_NO_MEMORY;

	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.",
		fUrl.Authority().String(), fRemoteAddr.Port());
	status_t connectError = fSocket->Connect(fRemoteAddr);

	if (connectError != B_OK) {
		_EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s",
			strerror(connectError));
		return connectError;
	}

	//! ProtocolHook:ConnectionOpened
	if (fListener != NULL)
		fListener->ConnectionOpened(this);

	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
		"Connection opened, sending request.");

	_SendRequest();
	_SendHeaders();
	fSocket->Write("\r\n", 2);
	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent.");

	_SendPostData();
	fRequestStatus = kRequestInitialState;



	// Receive loop
	bool receiveEnd = false;
	bool parseEnd = false;
	bool readByChunks = false;
	bool decompress = false;
	status_t readError = B_OK;
	ssize_t bytesRead = 0;
	ssize_t bytesReceived = 0;
	ssize_t bytesTotal = 0;
	off_t bytesUnpacked = 0;
	char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize];
	ssize_t inputTempSize = kHttpBufferSize;
	ssize_t chunkSize = -1;
	DynamicBuffer decompressorStorage;
	BDataIO* decompressingStream;
	ObjectDeleter<BDataIO> decompressingStreamDeleter;

	while (!fQuit && !(receiveEnd && parseEnd)) {
		if (!receiveEnd) {
			fSocket->WaitForReadable();
			BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize);
			bytesRead = fSocket->Read(chunk, kHttpBufferSize);

			if (bytesRead < 0) {
				readError = bytesRead;
				break;
			} else if (bytesRead == 0)
				receiveEnd = true;

			fInputBuffer.AppendData(chunk, bytesRead);
		} else
			bytesRead = 0;

		if (fRequestStatus < kRequestStatusReceived) {
			_ParseStatus();

			//! ProtocolHook:ResponseStarted
			if (fRequestStatus >= kRequestStatusReceived && fListener != NULL)
				fListener->ResponseStarted(this);
		}

		if (fRequestStatus < kRequestHeadersReceived) {
			_ParseHeaders();

			if (fRequestStatus >= kRequestHeadersReceived) {
				_ResultHeaders() = fHeaders;

				//! ProtocolHook:HeadersReceived
				if (fListener != NULL)
					fListener->HeadersReceived(this);

				// Parse received cookies
				if (fContext != NULL) {
					for (int32 i = 0;  i < fHeaders.CountHeaders(); i++) {
						if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) {
							fContext->GetCookieJar().AddCookie(
								fHeaders.HeaderAt(i).Value(), fUrl);
						}
					}
				}

				if (BString(fHeaders["Transfer-Encoding"]) == "chunked")
					readByChunks = true;

				BString contentEncoding(fHeaders["Content-Encoding"]);
				// We don't advertise "deflate" support (see above), but we
				// still try to decompress it, if a server ever sends a deflate
				// stream despite it not being in our Accept-Encoding list.
				if (contentEncoding == "gzip"
						|| contentEncoding == "deflate") {
					decompress = true;
					readError = BZlibCompressionAlgorithm()
						.CreateDecompressingOutputStream(&decompressorStorage,
							NULL, decompressingStream);
					if (readError != B_OK)
						break;

					decompressingStreamDeleter.SetTo(decompressingStream);
				}

				int32 index = fHeaders.HasHeader("Content-Length");
				if (index != B_ERROR)
					bytesTotal = atoi(fHeaders.HeaderAt(index).Value());
				else
					bytesTotal = -1;
			}
		}

		if (fRequestStatus >= kRequestHeadersReceived) {
			// If Transfer-Encoding is chunked, we should read a complete
			// chunk in buffer before handling it
			if (readByChunks) {
				if (chunkSize >= 0) {
					if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) {
							// 2 more bytes to handle the closing CR+LF
						bytesRead = chunkSize;
						if (inputTempSize < chunkSize + 2) {
							delete[] inputTempBuffer;
							inputTempSize = chunkSize + 2;
							inputTempBuffer
								= new(std::nothrow) char[inputTempSize];
						}

						if (inputTempBuffer == NULL) {
							readError = B_NO_MEMORY;
							break;
						}

						fInputBuffer.RemoveData(inputTempBuffer,
							chunkSize + 2);
						chunkSize = -1;
					} else {
						// Not enough data, try again later
						bytesRead = -1;
					}
				} else {
					BString chunkHeader;
					if (_GetLine(chunkHeader) == B_ERROR) {
						chunkSize = -1;
						bytesRead = -1;
					} else {
						// Format of a chunk header:
						// <chunk size in hex>[; optional data]
						int32 semiColonIndex = chunkHeader.FindFirst(';', 0);

						// Cut-off optional data if present
						if (semiColonIndex != -1) {
							chunkHeader.Remove(semiColonIndex,
								chunkHeader.Length() - semiColonIndex);
						}

						chunkSize = strtol(chunkHeader.String(), NULL, 16);
						PRINT(("BHP[%p] Chunk %s=%ld\n", this,
							chunkHeader.String(), chunkSize));
						if (chunkSize == 0)
							fRequestStatus = kRequestContentReceived;

						bytesRead = -1;
					}
				}

				// A chunk of 0 bytes indicates the end of the chunked transfer
				if (bytesRead == 0)
					receiveEnd = true;
			} else {
				bytesRead = fInputBuffer.Size();

				if (bytesRead > 0) {
					if (inputTempSize < bytesRead) {
						inputTempSize = bytesRead;
						delete[] inputTempBuffer;
						inputTempBuffer = new(std::nothrow) char[bytesRead];
					}

					if (inputTempBuffer == NULL) {
						readError = B_NO_MEMORY;
						break;
					}
					fInputBuffer.RemoveData(inputTempBuffer, bytesRead);
				}
			}

			if (bytesRead >= 0) {
				bytesReceived += bytesRead;

				if (fListener != NULL) {
					if (decompress) {
						readError = decompressingStream->WriteExactly(
							inputTempBuffer, bytesRead);
						if (readError != B_OK)
							break;

						ssize_t size = decompressorStorage.Size();
						BStackOrHeapArray<char, 4096> buffer(size);
						size = decompressorStorage.Read(buffer, size);
						if (size > 0) {
							fListener->DataReceived(this, buffer, bytesUnpacked,
								size);
							bytesUnpacked += size;
						}
					} else if (bytesRead > 0) {
						fListener->DataReceived(this, inputTempBuffer,
							bytesReceived - bytesRead, bytesRead);
					}
					fListener->DownloadProgress(this, bytesReceived,
						std::max((ssize_t)0, bytesTotal));
				}

				if (bytesTotal >= 0 && bytesReceived >= bytesTotal)
					receiveEnd = true;

				if (decompress && receiveEnd) {
					readError = decompressingStream->Flush();

					if (readError == B_BUFFER_OVERFLOW)
						readError = B_OK;

					if (readError != B_OK)
						break;

					ssize_t size = decompressorStorage.Size();
					BStackOrHeapArray<char, 4096> buffer(size);
					size = decompressorStorage.Read(buffer, size);
					if (fListener != NULL && size > 0) {
						fListener->DataReceived(this, buffer,
							bytesUnpacked, size);
						bytesUnpacked += size;
					}
				}
			}
		}

		parseEnd = (fInputBuffer.Size() == 0);
	}

	fSocket->Disconnect();
	delete[] inputTempBuffer;

	if (readError != B_OK)
		return readError;

	return fQuit ? B_INTERRUPTED : B_OK;
}
예제 #4
0
status_t
BHttpRequest::_MakeRequest()
{
	if (fSocket == NULL)
		return B_NO_MEMORY;

	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.",
		fUrl.Authority().String(), fRemoteAddr.Port());
	status_t connectError = fSocket->Connect(fRemoteAddr);

	if (connectError != B_OK) {
		_EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s",
			strerror(connectError));
		return connectError;
	}

	//! ProtocolHook:ConnectionOpened
	if (fListener != NULL)
		fListener->ConnectionOpened(this);

	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
		"Connection opened, sending request.");

	_SendRequest();
	_SendHeaders();
	fSocket->Write("\r\n", 2);
	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent.");

	if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) {
		if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) {
			BString outputBuffer = fOptPostFields->RawData();
			_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
				"%s", outputBuffer.String());
			fSocket->Write(outputBuffer.String(), outputBuffer.Length());
		} else {
			for (BHttpForm::Iterator it = fOptPostFields->GetIterator();
				const BHttpFormData* currentField = it.Next();
				) {
				_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
					it.MultipartHeader().String());
				fSocket->Write(it.MultipartHeader().String(),
					it.MultipartHeader().Length());

				switch (currentField->Type()) {
					default:
					case B_HTTPFORM_UNKNOWN:
						ASSERT(0);
						break;

					case B_HTTPFORM_STRING:
						fSocket->Write(currentField->String().String(),
							currentField->String().Length());
						break;

					case B_HTTPFORM_FILE:
						{
							BFile upFile(currentField->File().Path(),
								B_READ_ONLY);
							char readBuffer[kHttpBufferSize];
							ssize_t readSize;

							readSize = upFile.Read(readBuffer,
								sizeof(readBuffer));
							while (readSize > 0) {
								fSocket->Write(readBuffer, readSize);
								readSize = upFile.Read(readBuffer,
									sizeof(readBuffer));
							}

							break;
						}
					case B_HTTPFORM_BUFFER:
						fSocket->Write(currentField->Buffer(),
							currentField->BufferSize());
						break;
				}

				fSocket->Write("\r\n", 2);
			}

			BString footer = fOptPostFields->GetMultipartFooter();
			fSocket->Write(footer.String(), footer.Length());
		}
	} else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT)
		&& fOptInputData != NULL) {

		for (;;) {
			char outputTempBuffer[kHttpBufferSize];
			ssize_t read = fOptInputData->Read(outputTempBuffer,
				sizeof(outputTempBuffer));

			if (read <= 0)
				break;

			if (fOptInputDataSize < 0) {
				// Chunked transfer
				char hexSize[16];
				size_t hexLength = sprintf(hexSize, "%ld", read);

				fSocket->Write(hexSize, hexLength);
				fSocket->Write("\r\n", 2);
				fSocket->Write(outputTempBuffer, read);
				fSocket->Write("\r\n", 2);
			} else {
				fSocket->Write(outputTempBuffer, read);
			}
		}

		if (fOptInputDataSize < 0) {
			// Chunked transfer terminating sequence
			fSocket->Write("0\r\n\r\n", 5);
		}
	}

	fRequestStatus = kRequestInitialState;

	// Receive loop
	bool receiveEnd = false;
	bool parseEnd = false;
	bool readByChunks = false;
	bool decompress = false;
	status_t readError = B_OK;
	ssize_t bytesRead = 0;
	ssize_t bytesReceived = 0;
	ssize_t bytesTotal = 0;
	char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize];
	ssize_t inputTempSize = kHttpBufferSize;
	ssize_t chunkSize = -1;
	DynamicBuffer decompressorStorage;
	BPrivate::ZlibDecompressor decompressor(&decompressorStorage);

	while (!fQuit && !(receiveEnd && parseEnd)) {
		if (!receiveEnd) {
			fSocket->WaitForReadable();
			BNetBuffer chunk(kHttpBufferSize);
			bytesRead = fSocket->Read(chunk.Data(), kHttpBufferSize);

			if (bytesRead < 0) {
				readError = bytesRead;
				break;
			} else if (bytesRead == 0)
				receiveEnd = true;

			fInputBuffer.AppendData(chunk.Data(), bytesRead);
		} else
			bytesRead = 0;

		if (fRequestStatus < kRequestStatusReceived) {
			_ParseStatus();

			//! ProtocolHook:ResponseStarted
			if (fRequestStatus >= kRequestStatusReceived && fListener != NULL)
				fListener->ResponseStarted(this);
		}

		if (fRequestStatus < kRequestHeadersReceived) {
			_ParseHeaders();

			if (fRequestStatus >= kRequestHeadersReceived) {
				_ResultHeaders() = fHeaders;

				//! ProtocolHook:HeadersReceived
				if (fListener != NULL)
					fListener->HeadersReceived(this);

				// Parse received cookies
				if (fContext != NULL) {
					for (int32 i = 0;  i < fHeaders.CountHeaders(); i++) {
						if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) {
							fContext->GetCookieJar().AddCookie(
								fHeaders.HeaderAt(i).Value(), fUrl);
						}
					}
				}

				if (BString(fHeaders["Transfer-Encoding"]) == "chunked")
					readByChunks = true;

				BString contentEncoding(fHeaders["Content-Encoding"]);
				if (contentEncoding == "gzip"
						|| contentEncoding == "deflate") {
					decompress = true;
					decompressor.Init();
				}

				int32 index = fHeaders.HasHeader("Content-Length");
				if (index != B_ERROR)
					bytesTotal = atoi(fHeaders.HeaderAt(index).Value());
				else
					bytesTotal = 0;
			}
		}

		if (fRequestStatus >= kRequestHeadersReceived) {
			// If Transfer-Encoding is chunked, we should read a complete
			// chunk in buffer before handling it
			if (readByChunks) {
				if (chunkSize >= 0) {
					if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) {
							// 2 more bytes to handle the closing CR+LF
						bytesRead = chunkSize;
						if (inputTempSize < chunkSize + 2) {
							delete[] inputTempBuffer;
							inputTempSize = chunkSize + 2;
							inputTempBuffer
								= new(std::nothrow) char[inputTempSize];
						}

						if (inputTempBuffer == NULL) {
							readError = B_NO_MEMORY;
							break;
						}

						fInputBuffer.RemoveData(inputTempBuffer,
							chunkSize + 2);
						chunkSize = -1;
					} else {
						// Not enough data, try again later
						bytesRead = -1;
					}
				} else {
					BString chunkHeader;
					if (_GetLine(chunkHeader) == B_ERROR) {
						chunkSize = -1;
						bytesRead = -1;
					} else {
						// Format of a chunk header:
						// <chunk size in hex>[; optional data]
						int32 semiColonIndex = chunkHeader.FindFirst(';', 0);

						// Cut-off optional data if present
						if (semiColonIndex != -1) {
							chunkHeader.Remove(semiColonIndex,
								chunkHeader.Length() - semiColonIndex);
						}

						chunkSize = strtol(chunkHeader.String(), NULL, 16);
						PRINT(("BHP[%p] Chunk %s=%ld\n", this,
							chunkHeader.String(), chunkSize));
						if (chunkSize == 0)
							fRequestStatus = kRequestContentReceived;

						bytesRead = -1;
					}
				}

				// A chunk of 0 bytes indicates the end of the chunked transfer
				if (bytesRead == 0)
					receiveEnd = true;
			} else {
				bytesRead = fInputBuffer.Size();

				if (bytesRead > 0) {
					if (inputTempSize < bytesRead) {
						inputTempSize = bytesRead;
						delete[] inputTempBuffer;
						inputTempBuffer = new(std::nothrow) char[bytesRead];
					}

					if (inputTempBuffer == NULL) {
						readError = B_NO_MEMORY;
						break;
					}
					fInputBuffer.RemoveData(inputTempBuffer, bytesRead);
				}
			}

			if (bytesRead > 0) {
				bytesReceived += bytesRead;

				if (fListener != NULL) {
					if (decompress) {
						decompressor.DecompressNext(inputTempBuffer,
							bytesRead);
						ssize_t size = decompressorStorage.Size();
						BStackOrHeapArray<char, 4096> buffer(size);
						size = decompressorStorage.Read(buffer, size);
						if (size > 0) {
							fListener->DataReceived(this, buffer, size);
						}
					} else {
						fListener->DataReceived(this, inputTempBuffer,
							bytesRead);
					}
					fListener->DownloadProgress(this, bytesReceived,
						bytesTotal);
				}

				if (bytesTotal > 0 && bytesReceived >= bytesTotal) {
					receiveEnd = true;

					if (decompress) {
						decompressor.Finish();
						ssize_t size = decompressorStorage.Size();
						BStackOrHeapArray<char, 4096> buffer(size);
						size = decompressorStorage.Read(buffer, size);
						if (fListener != NULL && size > 0) {
							fListener->DataReceived(this, buffer, size);
						}
					}
				}
			}
		}

		parseEnd = (fInputBuffer.Size() == 0);
	}

	fSocket->Disconnect();
	delete[] inputTempBuffer;

	if (readError != B_OK)
		return readError;

	return fQuit ? B_INTERRUPTED : B_OK;
}
예제 #5
0
void
BGopherRequest::_ParseInput(bool last)
{
    BString line;

    while (_GetLine(line) == B_OK) {
        char type = GOPHER_TYPE_NONE;
        BStringList fields;

        line.MoveInto(&type, 0, 1);

        line.Split("\t", false, fields);

        if (type != GOPHER_TYPE_ENDOFPAGE
                && fields.CountStrings() < FIELD_GPFLAG)
            _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
                       "Unterminated gopher item (type '%c')", type);

        BString pageTitle;
        BString item;
        BString title = fields.StringAt(FIELD_NAME);
        BString link("gopher://");
        BString user;
        if (fields.CountStrings() > 3) {
            link << fields.StringAt(FIELD_HOST);
            if (fields.StringAt(FIELD_PORT).Length())
                link << ":" << fields.StringAt(FIELD_PORT);
            link << "/" << type;
            //if (fields.StringAt(FIELD_SELECTOR).ByteAt(0) != '/')
            //	link << "/";
            link << fields.StringAt(FIELD_SELECTOR);
        }
        _HTMLEscapeString(title);
        _HTMLEscapeString(link);

        switch (type) {
        case GOPHER_TYPE_ENDOFPAGE:
            /* end of the page */
            break;
        case GOPHER_TYPE_TEXTPLAIN:
            item << "<a href=\"" << link << "\">"
                 "<span class=\"text\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_BINARY:
        case GOPHER_TYPE_BINHEX:
        case GOPHER_TYPE_BINARCHIVE:
        case GOPHER_TYPE_UUENCODED:
            item << "<a href=\"" << link << "\">"
                 "<span class=\"binary\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_DIRECTORY:
            /*
             * directory link
             */
            item << "<a href=\"" << link << "\">"
                 "<span class=\"dir\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_ERROR:
            item << "<span class=\"error\">" << title << "</span>"
                 "<br/>\n";
            if (fPosition == 0 && pageTitle.Length() == 0)
                pageTitle << "Error: " << title;
            break;
        case GOPHER_TYPE_QUERY:
            /* TODO: handle search better.
             * For now we use an unnamed input field and accept sending ?=foo
             * as it seems at least Veronica-2 ignores the = but it's unclean.
             */
            item << "<form method=\"get\" action=\"" << link << "\" "
                 "onsubmit=\"window.location = this.action + '?' + "
                 "this.elements['q'].value; return false;\">"
                 "<span class=\"query\">"
                 "<label>" << title << " "
                 "<input id=\"q\" name=\"\" type=\"text\" align=\"right\" />"
                 "</label>"
                 "</span></form>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_TELNET:
            /* telnet: links
             * cf. gopher://78.80.30.202/1/ps3
             * -> gopher://78.80.30.202:23/8/ps3/new -> [email protected]
             */
            link = "telnet://";
            user = fields.StringAt(FIELD_SELECTOR);
            if (user.FindLast('/') > -1) {
                user.Remove(0, user.FindLast('/'));
                link << user << "@";
            }
            link << fields.StringAt(FIELD_HOST);
            if (fields.StringAt(FIELD_PORT) != "23")
                link << ":" << fields.StringAt(FIELD_PORT);

            item << "<a href=\"" << link << "\">"
                 "<span class=\"telnet\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_TN3270:
            /* tn3270: URI scheme, cf. http://tools.ietf.org/html/rfc6270 */
            link = "tn3270://";
            user = fields.StringAt(FIELD_SELECTOR);
            if (user.FindLast('/') > -1) {
                user.Remove(0, user.FindLast('/'));
                link << user << "@";
            }
            link << fields.StringAt(FIELD_HOST);
            if (fields.StringAt(FIELD_PORT) != "23")
                link << ":" << fields.StringAt(FIELD_PORT);

            item << "<a href=\"" << link << "\">"
                 "<span class=\"telnet\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_CSO_SEARCH:
            /* CSO search.
             * At least Lynx supports a cso:// URI scheme:
             * http://lynx.isc.org/lynx2.8.5/lynx2-8-5/lynx_help/lynx_url_support.html
             */
            link = "cso://";
            user = fields.StringAt(FIELD_SELECTOR);
            if (user.FindLast('/') > -1) {
                user.Remove(0, user.FindLast('/'));
                link << user << "@";
            }
            link << fields.StringAt(FIELD_HOST);
            if (fields.StringAt(FIELD_PORT) != "105")
                link << ":" << fields.StringAt(FIELD_PORT);

            item << "<a href=\"" << link << "\">"
                 "<span class=\"cso\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_GIF:
        case GOPHER_TYPE_IMAGE:
        case GOPHER_TYPE_PNG:
        case GOPHER_TYPE_BITMAP:
            /* quite dangerous, cf. gopher://namcub.accela-labs.com/1/pics */
            if (kInlineImages) {
                item << "<a href=\"" << link << "\">"
                     "<span class=\"img\">" << title << " "
                     "<img src=\"" << link << "\" "
                     "alt=\"" << title << "\"/>"
                     "</span></a>"
                     "<br/>\n";
                break;
            }
            /* fallback to default, link them */
            item << "<a href=\"" << link << "\">"
                 "<span class=\"img\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_HTML:
            /* cf. gopher://pineapple.vg/1 */
            if (fields.StringAt(FIELD_SELECTOR).StartsWith("URL:")) {
                link = fields.StringAt(FIELD_SELECTOR);
                link.Remove(0, 4);
            }
            /* cf. gopher://sdf.org/1/sdf/classes/ */

            item << "<a href=\"" << link << "\">"
                 "<span class=\"html\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_INFO:
            // TITLE resource, cf.
            // gopher://gophernicus.org/0/doc/gopher/gopher-title-resource.txt
            if (fPosition == 0 && pageTitle.Length() == 0
                    && fields.StringAt(FIELD_SELECTOR) == "TITLE") {
                pageTitle = title;
                break;
            }
            item << "<span class=\"info\">" << title << "</span>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_AUDIO:
        case GOPHER_TYPE_SOUND:
            item << "<a href=\"" << link << "\">"
                 "<span class=\"audio\">" << title << "</span></a>"
                 "<audio src=\"" << link << "\" "
                 //TODO:Fix crash in WebPositive with these
                 //"controls=\"controls\" "
                 //"width=\"300\" height=\"50\" "
                 "alt=\"" << title << "\"/>"
                 "<span>[player]</span></audio>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_PDF:
        case GOPHER_TYPE_DOC:
            /* generic case for known-to-work items */
            item << "<a href=\"" << link << "\">"
                 "<span class=\"document\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        case GOPHER_TYPE_MOVIE:
            item << "<a href=\"" << link << "\">"
                 "<span class=\"video\">" << title << "</span></a>"
                 "<video src=\"" << link << "\" "
                 //TODO:Fix crash in WebPositive with these
                 //"controls=\"controls\" "
                 //"width=\"300\" height=\"300\" "
                 "alt=\"" << title << "\"/>"
                 "<span>[player]</span></audio>"
                 "<br/>\n";
            break;
        default:
            _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
                       "Unknown gopher item (type 0x%02x '%c')", type, type);
            item << "<a href=\"" << link << "\">"
                 "<span class=\"unknown\">" << title << "</span></a>"
                 "<br/>\n";
            break;
        }

        if (fPosition == 0) {
            if (pageTitle.Length() == 0)
                pageTitle << "Index of " << Url();

            const char *uplink = ".";
            if (fPath.EndsWith("/"))
                uplink = "..";

            // emit header
            BString header;
            header <<
                   "<html>\n"
                   "<head>\n"
                   "<meta http-equiv=\"Content-Type\""
                   " content=\"text/html; charset=UTF-8\" />\n"
                   //FIXME: fix links
                   //"<link rel=\"icon\" type=\"image/png\""
                   //	" href=\"resource:icons/directory.png\">\n"
                   "<style type=\"text/css\">\n" << kStyleSheet << "</style>\n"
                   "<title>" << pageTitle << "</title>\n"
                   "</head>\n"
                   "<body id=\"gopher\">\n"
                   "<div class=\"uplink dontprint\">\n"
                   "<a href=" << uplink << ">[up]</a>\n"
                   "<a href=\"/\">[top]</a>\n"
                   "</div>\n"
                   "<h1>" << pageTitle << "</h1>\n";

            fListener->DataReceived(this, header.String(), fPosition,
                                    header.Length());

            fPosition += header.Length();
        }

        if (item.Length()) {
            fListener->DataReceived(this, item.String(), fPosition,
                                    item.Length());

            fPosition += item.Length();
        }
    }

    if (last) {
        // emit footer
        BString footer =
            "</div>\n"
            "</body>\n"
            "</html>\n";

        fListener->DataReceived(this, footer.String(), fPosition,
                                footer.Length());

        fPosition += footer.Length();
    }
}
예제 #6
0
static Boolean _GetNextRecord(
    fstream& fs,
    Array<char>& line,
    Uint32& freeFlag,
    Uint32& hashCode,
    Uint32& index,
    Uint32& size,
    const char*& instanceName,
    Boolean& error)
{
    error = false;

    //
    // Get next line:
    //

    if (!_GetLine(fs, line))
        return false;

    //
    // Get the free flag field:
    //

    char* end = (char*)line.getData();

    if (!_GetIntField(end, error, freeFlag, 10))
        return false;

    if (freeFlag != 0 && freeFlag != 1)
    {
        error = true;
        return false;
    }

    //
    // Get the hash-code field:
    //

    if (!_GetIntField(end, error, hashCode, 16))
        return false;

    //
    // Get index field:
    //

    if (!_GetIntField(end, error, index, 10))
        return false;

    //
    // Get size field:
    //

    if (!_GetIntField(end, error, size, 10))
        return false;

    //
    // Get instance name:
    //

    instanceName = end;

    return true;
}