/*static*/ off_t AVFormatWriter::_Seek(void* cookie, off_t offset, int whence) { TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n", cookie, offset, whence); AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); BPositionIO* positionIO = dynamic_cast<BPositionIO*>(writer->fTarget); if (positionIO == NULL) return -1; // Support for special file size retrieval API without seeking anywhere: if (whence == AVSEEK_SIZE) { off_t size; if (positionIO->GetSize(&size) == B_OK) return size; return -1; } off_t position = positionIO->Seek(offset, whence); TRACE_IO(" position: %lld\n", position); if (position < 0) return -1; return position; }
/*static*/ status_t FSUtils::CompareFileContent(BPositionIO& content1, BPositionIO& content2, bool& _equal) { // get and compare content size off_t size1; status_t error = content1.GetSize(&size1); if (error != B_OK) return error; off_t size2; error = content2.GetSize(&size2); if (error != B_OK) return error; if (size1 != size2) { _equal = false; return B_OK; } if (size1 == 0) { _equal = true; return B_OK; } // allocate a data buffer uint8* buffer1 = new(std::nothrow) uint8[2 * kCompareDataBufferSize]; if (buffer1 == NULL) return B_NO_MEMORY; ArrayDeleter<uint8> bufferDeleter(buffer1); uint8* buffer2 = buffer1 + kCompareDataBufferSize; // compare the data off_t offset = 0; while (offset < size1) { size_t toCompare = std::min(size_t(size1 - offset), kCompareDataBufferSize); ssize_t bytesRead = content1.ReadAt(offset, buffer1, toCompare); if (bytesRead < 0) return bytesRead; if ((size_t)bytesRead != toCompare) return B_ERROR; bytesRead = content2.ReadAt(offset, buffer2, toCompare); if (bytesRead < 0) return bytesRead; if ((size_t)bytesRead != toCompare) return B_ERROR; if (memcmp(buffer1, buffer2, toCompare) != 0) { _equal = false; return B_OK; } offset += bytesRead; } _equal = true; return B_OK; }
/* static */ int WebPTranslator::_EncodedWriter(const uint8_t* data, size_t dataSize, const WebPPicture* const picture) { BPositionIO* target = (BPositionIO*)picture->custom_ptr; return dataSize ? (target->Write(data, dataSize) == (ssize_t)dataSize) : 1; }
void TiffUintField::LoadByte(IFDEntry &entry, BPositionIO &io, swap_action swp) { // Make certain there is enough memory // before trying to do anything else fpByte = new uint8[entry.count]; if (!fpByte) { finitStatus = B_NO_MEMORY; return; } if (entry.count <= 4) { // If all of the byte values can fit into the // IFD entry value bytes memcpy(fpByte, entry.bytevals, entry.count); finitStatus = B_OK; } else { // entry.count > 4, use longval to find offset for byte data if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK) finitStatus = B_ERROR; else { ssize_t read; read = io.ReadAt(entry.longval, fpByte, entry.count); if (read != static_cast<ssize_t>(entry.count)) finitStatus = B_IO_ERROR; else finitStatus = B_OK; } } }
SharedBitmap::SharedBitmap(BPositionIO& data) : BReferenceable(), fResourceID(-1), fBuffer(NULL), fSize(0), fMimeType() { status_t status = data.GetSize(&fSize); const off_t kMaxSize = 1024 * 1024; if (status == B_OK && fSize > 0 && fSize <= kMaxSize) { fBuffer = new(std::nothrow) uint8[fSize]; if (fBuffer != NULL) { data.Seek(0, SEEK_SET); off_t bytesRead = 0; size_t chunkSize = std::min((off_t)4096, fSize); while (bytesRead < fSize) { ssize_t read = data.Read(fBuffer + bytesRead, chunkSize); if (read > 0) bytesRead += read; else break; } if (bytesRead != fSize) { delete[] fBuffer; fBuffer = NULL; fSize = 0; } } else fSize = 0; } else { fprintf(stderr, "SharedBitmap(): Stream too large: %" B_PRIi64 ", max: %" B_PRIi64 "\n", fSize, kMaxSize); } fBitmap[0] = NULL; fBitmap[1] = NULL; fBitmap[2] = NULL; }
status_t MusePackReader::Sniff(int32 *_streamCount) { BPositionIO *file = dynamic_cast<BPositionIO *>(Source()); if (file == NULL) // we cannot handle non seekable files for now return B_ERROR; file->Seek(0, SEEK_SET); int error = fInfo.ReadStreamInfo(file); if (error > B_OK) { // error came from engine TRACE("MusePackReader: ReadStreamInfo() engine error %d\n", error); return B_ERROR; } else if (error < B_OK) return error; TRACE("MusePackReader: recognized MPC file\n"); *_streamCount = 1; return B_OK; }
// read_exactly static void read_exactly(BPositionIO& file, off_t position, void* buffer, size_t size, const char* errorMessage = NULL) { ssize_t read = file.ReadAt(position, buffer, size); if (read < 0) throw Exception(read, errorMessage); else if ((size_t)read != size) { if (errorMessage) { throw Exception("%s Read to few bytes (%ld/%lu).", errorMessage, read, size); } else throw Exception("Read to few bytes (%ld/%lu).", read, size); } }
static BPositionIO * get_seekable(BDataIO * data) { // try to cast to BPositionIO then perform a series of // sanity checks to ensure that seeking is reliable BPositionIO * seekable = dynamic_cast<BPositionIO *>(data); if (seekable == 0) { return 0; } // first try to get our current location off_t current = seekable->Seek(0, SEEK_CUR); if (current < 0) { return 0; } // check it against position if (current != seekable->Position()) { return 0; } // next try to seek to our current location (absolutely) if (seekable->Seek(current, SEEK_SET) < 0) { return 0; } // next try to seek to the start of the stream (absolutely) if (seekable->Seek(0, SEEK_SET) < 0) { return 0; } // then seek back to where we started if (seekable->Seek(current, SEEK_SET) < 0) { // really screwed return 0; } // next try to seek to the end of the stream (absolutely) if (seekable->Seek(0, SEEK_END) < 0) { return 0; } // then seek back to where we started if (seekable->Seek(current, SEEK_SET) < 0) { // really screwed return 0; } return seekable; }
void TiffUintField::LoadShort(IFDEntry &entry, BPositionIO &io, swap_action swp) { // Make certain there is enough memory // before trying to do anything else fpShort = new uint16[entry.count]; if (!fpShort) { finitStatus = B_NO_MEMORY; return; } if (entry.count <= 2) { // If all of the byte values can fit into the // IFD entry value bytes memcpy(fpShort, entry.shortvals, entry.count * 2); finitStatus = B_OK; } else { // entry.count > 2, use longval to find offset for short data if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK) finitStatus = B_ERROR; else { ssize_t read; read = io.ReadAt(entry.longval, fpShort, entry.count * 2); if (read != static_cast<ssize_t>(entry.count) * 2) finitStatus = B_IO_ERROR; else finitStatus = B_OK; } } // If short values were successfully read in, swap them to // the correct byte order if (finitStatus == B_OK && swap_data(B_UINT16_TYPE, fpShort, entry.count * 2, swp) != B_OK) finitStatus = B_ERROR; }
void TiffUintField::LoadLong(IFDEntry &entry, BPositionIO &io, swap_action swp) { // Make certain there is enough memory // before trying to do anything else fpLong = new uint32[entry.count]; if (!fpLong) { finitStatus = B_NO_MEMORY; return; } if (entry.count == 1) { fpLong[0] = entry.longval; finitStatus = B_OK; } else { // entry.count > 1, use longval to find offset for long data if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK) finitStatus = B_ERROR; else { ssize_t read; read = io.ReadAt(entry.longval, fpLong, entry.count * 4); if (read != static_cast<ssize_t>(entry.count) * 4) finitStatus = B_IO_ERROR; else finitStatus = B_OK; } } // If long values were successfully read in, swap them to // the correct byte order if (finitStatus == B_OK && swap_data(B_UINT32_TYPE, fpLong, entry.count * 4, swp) != B_OK) finitStatus = B_ERROR; }
_EXPORT ssize_t readfoldedline(BPositionIO &in, char **buffer, size_t *buflen) { ssize_t len = buflen && *buflen ? *buflen : 0; char * buf = buffer && *buffer ? *buffer : NULL; ssize_t cnt = 0; // Number of characters currently in the buffer. char c; status_t errorCode; while (true) { // Make sure there is space in the buffer for two more characters (one // for the next character, and one for the end of string NUL byte). if (buf == NULL || cnt + 2 >= len) { char *temp = (char *)realloc(buf, len + 64); if (temp == NULL) { // Out of memory, however existing buffer remains allocated. cnt = ENOMEM; break; } len += 64; buf = temp; } errorCode = in.Read (&c,1); // A really slow way of reading - unbuffered. if (errorCode != 1) { if (errorCode < 0) { cnt = errorCode; // IO error encountered, just return the code. } else { // Really is end of file. Also make it end of line if there is // some text already read in. If the first thing read was EOF, // just return an empty string. if (cnt > 0) { buf[cnt++] = '\n'; if (buf[cnt-2] == '\r') { buf[cnt-2] = '\n'; --cnt; } } } break; } buf[cnt++] = c; if (c == '\n') { // Convert CRLF end of line to just a LF. Do it before folding, in // case we don't need to fold. if (cnt >= 2 && buf[cnt-2] == '\r') { buf[cnt-2] = '\n'; --cnt; } // If the current line is empty then return it (so that empty lines // don't disappear if the next line starts with a space). if (cnt <= 1) break; // if first character on the next line is whitespace, fold lines errorCode = in.Read(&c,1); if (errorCode == 1) { if (c == ' ' || c == '\t') buf[cnt-1] = c; // Replace \n with the white space character. else { // Not folding, we finished reading a whole line. in.Seek(-1,SEEK_CUR); // Undo the look-ahead character read. break; } } else if (errorCode < 0) { cnt = errorCode; break; } else // No next line; at the end of the file. Return the line. break; } } if (buf != NULL && cnt >= 0) buf[cnt] = '\0'; if (buffer) *buffer = buf; else if (buf) free(buf); if (buflen) *buflen = len; return cnt; }
status_t StreamBase::Seek(uint32 flags, int64* frame, bigtime_t* time) { BAutolock _(fStreamLock); if (fContext == NULL || fStream == NULL) return B_NO_INIT; TRACE_SEEK("StreamBase::Seek(%ld,%s%s%s%s, %lld, " "%lld)\n", VirtualIndex(), (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", *frame, *time); double frameRate = FrameRate(); if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) { // Seeking is always based on time, initialize it when client seeks // based on frame. *time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5); } int64_t timeStamp = *time; int searchFlags = AVSEEK_FLAG_BACKWARD; if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0) searchFlags = 0; if (fSeekByBytes) { searchFlags |= AVSEEK_FLAG_BYTE; BAutolock _(fSourceLock); int64_t fileSize; if (fSource->GetSize(&fileSize) != B_OK) return B_NOT_SUPPORTED; int64_t duration = Duration(); if (duration == 0) return B_NOT_SUPPORTED; timeStamp = int64_t(fileSize * ((double)timeStamp / duration)); if ((flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) != 0) { timeStamp -= 65536; if (timeStamp < 0) timeStamp = 0; } bool seekAgain = true; bool seekForward = true; bigtime_t lastFoundTime = -1; int64_t closestTimeStampBackwards = -1; while (seekAgain) { if (avformat_seek_file(fContext, -1, INT64_MIN, timeStamp, INT64_MAX, searchFlags) < 0) { TRACE(" avformat_seek_file() (by bytes) failed.\n"); return B_ERROR; } seekAgain = false; // Our last packet is toast in any case. Read the next one so we // know where we really seeked. fReusePacket = false; if (_NextPacket(true) == B_OK) { while (fPacket.pts == kNoPTSValue) { fReusePacket = false; if (_NextPacket(true) != B_OK) return B_ERROR; } if (fPacket.pos >= 0) timeStamp = fPacket.pos; bigtime_t foundTime = _ConvertFromStreamTimeBase(fPacket.pts); if (foundTime != lastFoundTime) { lastFoundTime = foundTime; if (foundTime > *time) { if (closestTimeStampBackwards >= 0) { timeStamp = closestTimeStampBackwards; seekAgain = true; seekForward = false; continue; } int64_t diff = int64_t(fileSize * ((double)(foundTime - *time) / (2 * duration))); if (diff < 8192) break; timeStamp -= diff; TRACE_SEEK(" need to seek back (%lld) (time: %.2f " "-> %.2f)\n", timeStamp, *time / 1000000.0, foundTime / 1000000.0); if (timeStamp < 0) foundTime = 0; else { seekAgain = true; continue; } } else if (seekForward && foundTime < *time - 100000) { closestTimeStampBackwards = timeStamp; int64_t diff = int64_t(fileSize * ((double)(*time - foundTime) / (2 * duration))); if (diff < 8192) break; timeStamp += diff; TRACE_SEEK(" need to seek forward (%lld) (time: " "%.2f -> %.2f)\n", timeStamp, *time / 1000000.0, foundTime / 1000000.0); if (timeStamp > duration) foundTime = duration; else { seekAgain = true; continue; } } } TRACE_SEEK(" found time: %lld -> %lld (%.2f)\n", *time, foundTime, foundTime / 1000000.0); *time = foundTime; *frame = (uint64)(*time * frameRate / 1000000LL + 0.5); TRACE_SEEK(" seeked frame: %lld\n", *frame); } else { TRACE_SEEK(" _NextPacket() failed!\n"); return B_ERROR; } } } else { // We may not get a PTS from the next packet after seeking, so // we try to get an expected time from the index. int64_t streamTimeStamp = _ConvertToStreamTimeBase(*time); int index = av_index_search_timestamp(fStream, streamTimeStamp, searchFlags); if (index < 0) { TRACE(" av_index_search_timestamp() failed\n"); } else { if (index > 0) { const AVIndexEntry& entry = fStream->index_entries[index]; streamTimeStamp = entry.timestamp; } else { // Some demuxers use the first index entry to store some // other information, like the total playing time for example. // Assume the timeStamp of the first entry is alays 0. // TODO: Handle start-time offset? streamTimeStamp = 0; } bigtime_t foundTime = _ConvertFromStreamTimeBase(streamTimeStamp); bigtime_t timeDiff = foundTime > *time ? foundTime - *time : *time - foundTime; if (timeDiff > 1000000 && (fStreamBuildsIndexWhileReading || index == fStream->nb_index_entries - 1)) { // If the stream is building the index on the fly while parsing // it, we only have entries in the index for positions already // decoded, i.e. we cannot seek into the future. In that case, // just assume that we can seek where we want and leave // time/frame unmodified. Since successfully seeking one time // will generate index entries for the seeked to position, we // need to remember this in fStreamBuildsIndexWhileReading, // since when seeking back there will be later index entries, // but we still want to ignore the found entry. fStreamBuildsIndexWhileReading = true; TRACE_SEEK(" Not trusting generic index entry. " "(Current count: %d)\n", fStream->nb_index_entries); } else { // If we found a reasonably time, write it into *time. // After seeking, we will try to read the sought time from // the next packet. If the packet has no PTS value, we may // still have a more accurate time from the index lookup. *time = foundTime; } } if (avformat_seek_file(fContext, -1, INT64_MIN, timeStamp, INT64_MAX, searchFlags) < 0) { TRACE(" avformat_seek_file() failed.\n"); // Try to fall back to av_seek_frame() timeStamp = _ConvertToStreamTimeBase(timeStamp); if (av_seek_frame(fContext, fStream->index, timeStamp, searchFlags) < 0) { TRACE(" avformat_seek_frame() failed as well.\n"); // Fall back to seeking to the beginning by bytes timeStamp = 0; if (av_seek_frame(fContext, fStream->index, timeStamp, AVSEEK_FLAG_BYTE) < 0) { TRACE(" avformat_seek_frame() by bytes failed as " "well.\n"); // Do not propagate error in any case. We fail if we can't // read another packet. } else *time = 0; } } // Our last packet is toast in any case. Read the next one so // we know where we really sought. bigtime_t foundTime = *time; fReusePacket = false; if (_NextPacket(true) == B_OK) { if (fPacket.pts != kNoPTSValue) foundTime = _ConvertFromStreamTimeBase(fPacket.pts); else TRACE_SEEK(" no PTS in packet after seeking\n"); } else TRACE_SEEK(" _NextPacket() failed!\n"); *time = foundTime; TRACE_SEEK(" sought time: %.2fs\n", *time / 1000000.0); *frame = (uint64)(*time * frameRate / 1000000.0 + 0.5); TRACE_SEEK(" sought frame: %lld\n", *frame); } return B_OK; }
void BHttpRequest::_SendPostData() { 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) { // If the input data is seekable, we rewind it for each new request. BPositionIO* seekableData = dynamic_cast<BPositionIO*>(fOptInputData); if (seekableData) seekableData->Seek(0, SEEK_SET); 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); } } }
static status_t convert_data_to_bits(pcx_header &header, StreamBuffer &source, BPositionIO &target) { uint16 bitsPerPixel = header.bitsPerPixel; uint16 bytesPerLine = header.bytesPerLine; uint32 width = header.xMax - header.xMin + 1; uint32 height = header.yMax - header.yMin + 1; uint16 numPlanes = header.numPlanes; uint32 scanLineLength = numPlanes * bytesPerLine; // allocate buffers TempAllocator scanLineAllocator; TempAllocator paletteAllocator; uint8 *scanLineData[height]; uint8 *palette = (uint8 *)paletteAllocator.Allocate(3 * 256); status_t status = B_OK; for (uint32 row = 0; row < height; row++) { TRACE("scanline %ld\n", row); scanLineData[row] = (uint8 *)scanLineAllocator.Allocate(scanLineLength); if (scanLineData[row] == NULL) return B_NO_MEMORY; uint8 *line = scanLineData[row]; uint32 index = 0; uint8 x; do { if (source.Read(&x, 1) != 1) { status = B_IO_ERROR; break; } if ((x & 0xc0) == 0xc0) { uint32 count = x & 0x3f; if (index + count - 1 > scanLineLength) { status = B_BAD_DATA; break; } if (source.Read(&x, 1) != 1) { status = B_IO_ERROR; break; } for (uint32 i = 0; i < count; i++) line[index++] = x; } else { line[index++] = x; } } while (index < scanLineLength); if (status != B_OK) { // If we've already read more than a third of the file, display // what we have, ie. ignore the error. if (row < height / 3) return status; memset(scanLineData + row, 0, sizeof(uint8*) * (height - row)); break; } } if (bitsPerPixel == 8 && numPlanes == 1) { TRACE("palette reading %p 8\n", palette); uint8 x; if (status != B_OK || source.Read(&x, 1) != 1 || x != 12) { // Try again by repositioning the file stream if (source.Seek(-3 * 256 - 1, SEEK_END) < 0) return B_BAD_DATA; if (source.Read(&x, 1) != 1) return B_IO_ERROR; if (x != 12) return B_BAD_DATA; } if (source.Read(palette, 256 * 3) != 256 * 3) return B_IO_ERROR; } else { TRACE("palette reading %p palette\n", palette); memcpy(palette, &header.paletteInfo, 48); } uint8 alpha = 255; if (bitsPerPixel == 1 && numPlanes == 1) { TRACE("target writing 1\n"); palette[0] = palette[1] = palette[2] = 0; palette[3] = palette[4] = palette[5] = 0xff; for (uint32 row = 0; row < height; row++) { uint8 *line = scanLineData[row]; if (line == NULL) break; uint8 mask[] = { 128, 64, 32, 16, 8, 4, 2, 1 }; for (uint32 i = 0; i < width; i++) { bool isBit = ((line[i >> 3] & mask[i & 7]) != 0) ? true : false; target.Write(&palette[!isBit ? 2 : 5], 1); target.Write(&palette[!isBit ? 1 : 4], 1); target.Write(&palette[!isBit ? 0 : 3], 1); target.Write(&alpha, 1); } } } else if (bitsPerPixel == 4 && numPlanes == 1) {
void CCellView::Read(BPositionIO& stream) { CSwapStream str(stream); scChunk chunk; long offset; int *funcList, *styleList, *fontList, *formatList; int funcCount, styleCount, fontCount, formatCount, borderFont = fBorderFontID; bool warnForIncorrectFormula = true; scCell cl; styleCount = 0; styleList = (int *)MALLOC(0); fontCount = 0; fontList = (int *)MALLOC(0); formatCount = 0; formatList = (int *)MALLOC(0); funcList = NULL; scCSElement *colStyles = NULL; int colStyleCount = 0; offset = 0; StProgress progress(this, 1, pColorYellow, false); StWriteLock lock(fContainer); try { stream.Seek(offset, SEEK_SET); str >> chunk; if (chunk.type == kscVersion) { scVersion vers; str >> vers; if (vers.major != 3) THROW((errTooNewFileFormat)); } else THROW((errUnknownFileFormat, ((CCellWindow *)Window())->EntryRef()->name)); do { if (stream.Seek(offset, SEEK_SET) < offset) { MStopAlert("File is too short").Go(); break; } str >> chunk; offset += 4 + chunk.size; switch (chunk.type) { case kscVersion: break; case kscHeader: { scHeader head; str >> head; funcList = (int *)CALLOC(head.functionCount, sizeof(int)); FailNil(funcList); funcCount = head.functionCount; progress.NewMax(head.cellCount); break; } case kscView: { scView view; str >> view; if (view.windowRect.left > 0 && view.windowRect.top > 0) { BRect r; { r = BScreen().Frame(); } r.InsetBy(4, 4); r = r & view.windowRect; if (r.IsValid() && r.Width() >= 300 && r.Height() >= 100) { Window()->MoveTo(r.left, r.top); Window()->ResizeTo(r.Width(), r.Height()); } } if (view.position.h > 0 && view.position.v > 0) fPosition = view.position; fFrozen = view.frozen; if (view.selection.IsValid()) fSelection = view.selection; if (fSelection.Contains(view.curCell)) fCurCell = view.curCell; borderFont = view.headingFont; fShowGrid = (view.flags & kscShowGrid) != 0; fShowBorders = (view.flags & kscShowHeadings) != 0; if (!fShowBorders) { fCellBounds.left -= fBorderWidth; fCellBounds.top -= fBorderHeight; } Window()->Show(); Window()->UpdateIfNeeded(); break; } case kscPrint: break; case kscWidths: case kscHeights: { int count = chunk.size/(sizeof(short)*2); scWidthElement *elems = (scWidthElement *)MALLOC(chunk.size); FailNil(elems); int k = count * sizeof(scWidthElement); stream.Read(elems, k); if (chunk.type == kscWidths) fCellWidths.Read(count, elems); else fCellHeights.Read(count, elems); FREE(elems); break; } case kscColStyles: { colStyleCount = chunk.size/(sizeof(short)*2); colStyles = (scCSElement *)MALLOC(chunk.size); FailNil(colStyles); for (int i = 0; i < colStyleCount; i++) str >> colStyles[i]; break; } case kscName: { char buf[50]; scName *name = (scName *)buf; int l = std::min((int)chunk.size, 50); stream.Read(name, l); range r; memcpy(&r, name->reference+1, sizeof(cell)); if (name->reference[0] == valRange) memcpy(&r.bottom, name->reference+5, sizeof(cell)); else r.BotRight() = r.TopLeft(); swap_order(r); if (fNames->count(name->name) == 0) (*fNames)[CName(name->name)] = r; break; } case kscFunc: { scFunc func; str >> func; if (!funcList) THROW((errCorruptedFile)); if (func.funcNr >= funcCount) THROW((errCorruptedFile)); funcList[func.funcNr] = GetFunctionNr(func.name); if (funcList[func.funcNr] == -1) WarnForMissingFunction(func.name); break; } case kscFont: { scFont font; str >> font; font_family fam; font_style sty; ReadCString(stream, sizeof(font_style), sty); ReadCString(stream, sizeof(font_family), fam); int *t = (int *)REALLOC(fontList, (fontCount+1)*sizeof(int)); FailNil(t); fontList = t; fontList[fontCount] = gFontSizeTable.GetFontID( fam, sty, font.size, font.color); fontCount++; break; } case kscFormat: { scFormat format; str >> format; int *t = (int *)REALLOC(formatList, (formatCount+1)*sizeof(int)); FailNil(t); formatList = t; if (format.nr < eFirstNewFormat) formatList[formatCount] = format.nr; else { char fs[256]; ReadCString(stream, 255, fs); formatList[formatCount] = gFormatTable.GetFormatID(fs); } formatCount++; break; } case kscStyle: { scStyle style; str >> style; int *t = (int *)REALLOC(styleList, (styleCount+1)*sizeof(int)); FailNil(t); styleList = t; CellStyle cs; if (style.font >= fontCount) THROW((errCorruptedFile)); cs.fFont = fontList[style.font]; if (style.format >= formatCount) THROW((errCorruptedFile)); cs.fFormat = formatList[style.format]; cs.fAlignment = style.align; cs.fLowColor = style.lowColor; cs.fLocked = (style.flags & kscLocked) != 0; cs.fHidden = (style.flags & kscHidden) != 0; styleList[styleCount] = gStyleTable.GetStyleID(cs); styleCount++; break; } case kscCellEmpty: case kscCellBool: case kscCellNumber: case kscCellDateTime: case kscCellText: { str >> cl; Value val; switch (chunk.type) { case kscCellBool: { bool b; str >> b; val = b; break; } case kscCellNumber: { double d; str >> d; val = d; break; } case kscCellDateTime: { time_t t; str >> t; val = t; break; } case kscCellText: { // do nothing yet... } } fContainer->NewCell(cl.loc, val, NULL); if (cl.style >= styleCount) /*THROW((errCorruptedFile))*/; else fContainer->SetCellStyleNr(cl.loc, styleList[cl.style]); progress.Step(); break; } case kscString: { char s[256]; Value val; stream.Read(s, std::min((int)chunk.size, 255)); s[std::min((int)chunk.size, 255)] = 0; val = s; fContainer->SetValue(cl.loc, val); break; } case kscFormula: { CFormula form; try { if (!funcList) THROW((errCorruptedFile)); form.Read(stream, funcList); Value v; form.Calculate(cl.loc, v, fContainer); fContainer->SetCellFormula(cl.loc, form.CopyString()); } catch (CErr& e) { CATCHED; char s[32]; cl.loc.GetName(s); #if DEBUG printf("%s in formula of cell %s\n", (char *)e, s); #endif if (warnForIncorrectFormula) { char m[256]; sprintf(m, GetMessage(msgIncorrectFormula), s); MAlert *a = new MWarningAlert(m, GetMessage(msgOK), GetMessage(msgNoMoreWarnings)); if (a->Go() == 2) warnForIncorrectFormula = false; } } form.Clear(); break; } case kscChart: ReadChart(stream, chunk.size); case kscEnd: break; default: MStopAlert("File contains errors").Go(); chunk.type = kscEnd; break; } } while (chunk.type != kscEnd); // adjust the fields that couldn't be adjusted before if (fontCount && borderFont < fontCount) fBorderFontID = fontList[borderFont]; if (colStyles && styleList) { for (int i = 0; i < colStyleCount; i++) { if (colStyles[i].style >= 0 && colStyles[i].style < styleCount) colStyles[i].style = styleList[colStyles[i].style]; } fContainer->GetColumnStyles().Read(colStyleCount, colStyles); } }
status_t AGMSBayesianSpamFilter::ProcessMailMessage ( BPositionIO** io_message, BEntry* io_entry, BMessage* io_headers, BPath* io_folder, const char* io_uid) { ssize_t amountRead; attr_info attributeInfo; const char *classificationString; off_t dataSize; BPositionIO *dataStreamPntr = *io_message; status_t errorCode = B_OK; int32 headerLength; BString headerString; BString newSubjectString; BNode nodeForOutputFile; bool nodeForOutputFileInitialised = false; const char *oldSubjectStringPntr; char percentageString [30]; BMessage replyMessage; BMessage scriptingMessage; team_id serverTeam; float spamRatio; char *stringBuffer = NULL; char tempChar; status_t tempErrorCode; const char *tokenizeModeStringPntr; // Set up a BNode to the final output file so that we can write custom // attributes to it. Non-custom attributes are stored separately in // io_headers. if (io_entry != NULL && B_OK == nodeForOutputFile.SetTo (io_entry)) nodeForOutputFileInitialised = true; // Get a connection to the spam database server. Launch if needed, should // only need it once, unless another e-mail thread shuts down the server // inbetween messages. This code used to be in InitCheck, but apparently // that isn't called. printf("Checking for Spam Server.\n"); if (fLaunchAttemptCount == 0 || !fMessengerToServer.IsValid ()) { if (fLaunchAttemptCount > 3) goto ErrorExit; // Don't try to start the server too many times. fLaunchAttemptCount++; // Make sure the server is running. if (!be_roster->IsRunning (kServerSignature)) { errorCode = be_roster->Launch (kServerSignature); if (errorCode != B_OK) { BPath path; entry_ref ref; directory_which places[] = {B_COMMON_BIN_DIRECTORY,B_BEOS_BIN_DIRECTORY}; for (int32 i = 0; i < 2; i++) { find_directory(places[i],&path); path.Append("spamdbm"); if (!BEntry(path.Path()).Exists()) continue; get_ref_for_path(path.Path(),&ref); if ((errorCode = be_roster->Launch (&ref)) == B_OK) break; } if (errorCode != B_OK) goto ErrorExit; } } // Set up the messenger to the database server. serverTeam = be_roster->TeamFor (kServerSignature); if (serverTeam < 0) goto ErrorExit; fMessengerToServer = BMessenger (kServerSignature, serverTeam, &errorCode); if (!fMessengerToServer.IsValid ()) goto ErrorExit; // Check if the server is running in headers only mode. If so, we only // need to download the header rather than the entire message. scriptingMessage.MakeEmpty (); scriptingMessage.what = B_GET_PROPERTY; scriptingMessage.AddSpecifier ("TokenizeMode"); replyMessage.MakeEmpty (); if ((errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage)) != B_OK) goto ErrorExit; if ((errorCode = replyMessage.FindInt32 ("error", &tempErrorCode)) != B_OK) goto ErrorExit; if ((errorCode = tempErrorCode) != B_OK) goto ErrorExit; if ((errorCode = replyMessage.FindString ("result", &tokenizeModeStringPntr)) != B_OK) goto ErrorExit; fHeaderOnly = (tokenizeModeStringPntr != NULL && strcmp (tokenizeModeStringPntr, "JustHeader") == 0); } // See if the message has already been classified. Happens for messages // which are partially downloaded when you have auto-training on. Could // untrain the partial part before training on the complete message, but we // don't know how big it was, so instead just ignore the message. if (nodeForOutputFileInitialised) { if (nodeForOutputFile.GetAttrInfo ("MAIL:classification", &attributeInfo) == B_OK) return B_OK; } // Copy the message to a string so that we can pass it to the spam database // (the even messier alternative is a temporary file). Do it in a fashion // which allows NUL bytes in the string. This method of course limits the // message size to a few hundred megabytes. If we're using header mode, // only read the header rather than the full message. if (fHeaderOnly) { // Read just the header, it ends with an empty CRLF line. dataStreamPntr->Seek (0, SEEK_SET); while ((errorCode = dataStreamPntr->Read (&tempChar, 1)) == 1) { headerString.Append (tempChar, 1); headerLength = headerString.Length(); if (headerLength >= 4 && strcmp (headerString.String() + headerLength - 4, "\r\n\r\n") == 0) break; } if (errorCode < 0) goto ErrorExit; dataSize = headerString.Length(); stringBuffer = new char [dataSize + 1]; memcpy (stringBuffer, headerString.String(), dataSize); stringBuffer[dataSize] = 0; } else { // Read the whole file. The seek to the end may take a while since // that triggers downloading of the entire message (and caching in a // slave file - see the MessageIO class). dataSize = dataStreamPntr->Seek (0, SEEK_END); if (dataSize <= 0) goto ErrorExit; try { stringBuffer = new char [dataSize + 1]; } catch (...) { errorCode = ENOMEM; goto ErrorExit; } dataStreamPntr->Seek (0, SEEK_SET); amountRead = dataStreamPntr->Read (stringBuffer, dataSize); if (amountRead != dataSize) goto ErrorExit; stringBuffer[dataSize] = 0; // Add an end of string NUL, just in case. } // Send off a scripting command to the database server, asking it to // evaluate the string for spaminess. Note that it can return ENOMSG // when there are no words (a good indicator of spam which is pure HTML // if you are using plain text only tokenization), so we could use that // as a spam marker too. Code copied for the reevaluate stuff below. scriptingMessage.MakeEmpty (); scriptingMessage.what = B_SET_PROPERTY; scriptingMessage.AddSpecifier ("EvaluateString"); errorCode = scriptingMessage.AddData ("data", B_STRING_TYPE, stringBuffer, dataSize + 1, false /* fixed size */); if (errorCode != B_OK) goto ErrorExit; replyMessage.MakeEmpty (); errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage); if (errorCode != B_OK || replyMessage.FindInt32 ("error", &errorCode) != B_OK) goto ErrorExit; // Unable to read the return code. if (errorCode == ENOMSG && fNoWordsMeansSpam) spamRatio = fSpamCutoffRatio; // Yes, no words and that means spam. else if (errorCode != B_OK || replyMessage.FindFloat ("result", &spamRatio) != B_OK) goto ErrorExit; // Classification failed in one of many ways. // If we are auto-training, feed back the message to the server as a // training example (don't train if it is uncertain). Also redo the // evaluation after training. if (fAutoTraining) { if (spamRatio >= fSpamCutoffRatio || spamRatio < fGenuineCutoffRatio) { scriptingMessage.MakeEmpty (); scriptingMessage.what = B_SET_PROPERTY; scriptingMessage.AddSpecifier ((spamRatio >= fSpamCutoffRatio) ? "SpamString" : "GenuineString"); errorCode = scriptingMessage.AddData ("data", B_STRING_TYPE, stringBuffer, dataSize + 1, false /* fixed size */); if (errorCode != B_OK) goto ErrorExit; replyMessage.MakeEmpty (); errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage); if (errorCode != B_OK || replyMessage.FindInt32 ("error", &errorCode) != B_OK) goto ErrorExit; // Unable to read the return code. if (errorCode != B_OK) goto ErrorExit; // Failed to set a good example. } // Note the kind of example made so that the user doesn't reclassify // the message twice (the spam server looks for this attribute). classificationString = (spamRatio >= fSpamCutoffRatio) ? "Spam" : ((spamRatio < fGenuineCutoffRatio) ? "Genuine" : "Uncertain"); if (nodeForOutputFileInitialised) nodeForOutputFile.WriteAttr ("MAIL:classification", B_STRING_TYPE, 0 /* offset */, classificationString, strlen (classificationString) + 1); // Now that the database has changed due to training, recompute the // spam ratio. Hopefully it will have become more extreme in the // correct direction (not switched from being spam to being genuine). // Code copied from above. scriptingMessage.MakeEmpty (); scriptingMessage.what = B_SET_PROPERTY; scriptingMessage.AddSpecifier ("EvaluateString"); errorCode = scriptingMessage.AddData ("data", B_STRING_TYPE, stringBuffer, dataSize + 1, false /* fixed size */); if (errorCode != B_OK) goto ErrorExit; replyMessage.MakeEmpty (); errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage); if (errorCode != B_OK || replyMessage.FindInt32 ("error", &errorCode) != B_OK) goto ErrorExit; // Unable to read the return code. if (errorCode == ENOMSG && fNoWordsMeansSpam) spamRatio = fSpamCutoffRatio; // Yes, no words and that means spam. else if (errorCode != B_OK || replyMessage.FindFloat ("result", &spamRatio) != B_OK) goto ErrorExit; // Classification failed in one of many ways. } // Store the spam ratio in an attribute called MAIL:ratio_spam, // attached to the eventual output file. if (nodeForOutputFileInitialised) nodeForOutputFile.WriteAttr ("MAIL:ratio_spam", B_FLOAT_TYPE, 0 /* offset */, &spamRatio, sizeof (spamRatio)); // Also add it to the subject, if requested. if (fAddSpamToSubject && spamRatio >= fSpamCutoffRatio && io_headers->FindString ("Subject", &oldSubjectStringPntr) == B_OK) { newSubjectString.SetTo ("[Spam "); sprintf (percentageString, "%05.2f", spamRatio * 100.0); newSubjectString << percentageString << "%] "; newSubjectString << oldSubjectStringPntr; io_headers->ReplaceString ("Subject", newSubjectString); } // Beep using different sounds for spam and genuine, as Jeremy Friesner // nudged me to get around to implementing. And add uncertain to that, as // "BiPolar" suggested. If the user doesn't want to hear the sound, they // can turn it off in the system sound preferences. if (spamRatio >= fSpamCutoffRatio) { system_beep (kAGMSBayesBeepSpamName); } else if (spamRatio < fGenuineCutoffRatio) { system_beep (kAGMSBayesBeepGenuineName); } else { system_beep (kAGMSBayesBeepUncertainName); } return B_OK; ErrorExit: fprintf (stderr, "Error exit from " "SpamFilter::ProcessMailMessage, code maybe %ld (%s).\n", errorCode, strerror (errorCode)); delete [] stringBuffer; return B_OK; // Not MD_ERROR so the message doesn't get left on server. }