increase version number to 0.1
[ecparse.git] / CollectionReader.cpp
index 27f9ca6..7d47ea1 100644 (file)
  * © 2008 by David Roden <droden@gmail.com>
  */
 
+#include <stdlib.h>
+#include <string.h>
 #include "CollectionReader.h"
+#include "GlobalSettings.h"
 
 CollectionReader::CollectionReader(ReaderInput* readerInput) {
        this->readerInput = readerInput;
+       firstLink = true;
 }
 
 CollectionReader::~CollectionReader() {
-       delete readerInput;
+}
+
+bool CollectionReader::isLineBreakPresent() {
+       size_t indexOfLineBreak = growingBuffer.indexOf('\n');
+       GlobalSettings::isVerbose() && (indexOfLineBreak != (size_t) -1) && fprintf(stderr, "[%s:%d] found line break at %d.\n", __FILE__, __LINE__, indexOfLineBreak);
+       GlobalSettings::isVerbose() && (indexOfLineBreak == (size_t) -1) && fprintf(stderr, "[%s:%d] could not find line break.\n", __FILE__, __LINE__);
+       return indexOfLineBreak != (size_t) -1;
+}
+
+StringTag* CollectionReader::readStringTag(bool header) {
+       if (header) {
+               uint16_t unknown = 0;
+               if (!ensureBufferCapacity(2)) {
+                       return NULL;
+               }
+               growingBuffer.read(&unknown, 2);
+       }
+       uint8_t tagName = 0;
+       if (!ensureBufferCapacity(1)) {
+               return NULL;
+       }
+       growingBuffer.read(&tagName, 1);
+       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read tag name %d.\n", __FILE__, __LINE__, tagName);
+       uint16_t tagLength = 0;
+       if (!ensureBufferCapacity(2)) {
+               return NULL;
+       }
+       growingBuffer.read(&tagLength, 2);
+       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read tag length %d.\n", __FILE__, __LINE__, tagLength);
+       char* tagValue = (char*) malloc(tagLength + 1);
+       if (!ensureBufferCapacity(tagLength) || !tagValue) {
+               return NULL;
+       }
+       growingBuffer.read(tagValue, tagLength);
+       tagValue[tagLength] = '\0';
+       StringTag* stringTag = new StringTag(tagName, tagValue);
+       free(tagValue);
+       return stringTag;
+}
+
+BlobTag* CollectionReader::readBlobTag(bool header) {
+       if (header) {
+               uint16_t unknown = 0;
+               if (!ensureBufferCapacity(2)) {
+                       return NULL;
+               }
+               growingBuffer.read(&unknown, 2);
+       }
+       uint8_t tagName = 0;
+       if (!ensureBufferCapacity(1)) {
+               return NULL;
+       }
+       growingBuffer.read(&tagName, 1);
+       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read tag name %d.\n", __FILE__, __LINE__, tagName);
+       uint32_t tagLength = 0;
+       if (!ensureBufferCapacity(4)) {
+               return NULL;
+       }
+       growingBuffer.read(&tagLength, 4);
+       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read tag length %d.\n", __FILE__, __LINE__, tagLength);
+       void* tagValue = (char*) malloc(tagLength);
+       if (!ensureBufferCapacity(tagLength)) {
+               return NULL;
+       }
+       growingBuffer.read(tagValue, tagLength);
+       BlobTag* blobTag = new BlobTag(tagName, tagValue, tagLength);
+       free(tagValue);
+       return blobTag;
+}
+
+HashTag* CollectionReader::readHashTag() {
+       if (!ensureBufferCapacity(1)) {
+               return NULL;
+       }
+       uint8_t tagId = 0;
+       growingBuffer.read(&tagId, 1);
+       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read tag id %d.\n", __FILE__, __LINE__, tagId);
+       if (!ensureBufferCapacity(16)) {
+               return NULL;
+       }
+       char hash[16];
+       growingBuffer.read(hash, 16);
+       return new HashTag(tagId, hash);
+}
+
+ED2KLink* CollectionReader::getNextLink() {
+       if (readerInput->isEOF() && !growingBuffer.getRemaining()) {
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] readInput EOF reached.\n", __FILE__, __LINE__);
+               return NULL;
+       }
+       if (firstLink) {
+               identifyCollectionType();
+               if (readerInput->isEOF() && !growingBuffer.getRemaining()) {
+                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] readInput EOF reached.\n", __FILE__, __LINE__);
+                       return NULL;
+               }
+       }
+       if (isTextCollection) {
+               while (!readerInput->isEOF() && !isLineBreakPresent()) {
+                       readMoreBytes();
+               }
+               if (readerInput->isEOF() && !growingBuffer.getRemaining()) {
+                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] readInput EOF reached.\n", __FILE__, __LINE__);
+                       return NULL;
+               }
+               size_t indexOfLineBreak = growingBuffer.indexOf('\n');
+               char* line;
+               if (indexOfLineBreak == (size_t) -1) {
+                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] could not find line break, using remainder of file.\n", __FILE__, __LINE__);
+                       indexOfLineBreak = growingBuffer.getRemaining();
+                       line = (char*) malloc(indexOfLineBreak + 1);
+                       growingBuffer.read(line, indexOfLineBreak);
+                       line[indexOfLineBreak] = '\0';
+               } else {
+                       line = (char*) malloc(indexOfLineBreak + 1);
+                       growingBuffer.read(line, indexOfLineBreak + 1);
+                       if (line[indexOfLineBreak] == '\n') {
+                               line[indexOfLineBreak] = '\0';
+                       }
+                       if (line[indexOfLineBreak - 1] == '\r') {
+                               line[indexOfLineBreak - 1] = '\0';
+                       }
+               }
+               growingBuffer.cut();
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] got line: %s\n", __FILE__, __LINE__, line);
+               ED2KLink* ed2kLink = ED2KLink::parseED2KLink(line);
+               free(line);
+               firstLink = false;
+               return ed2kLink;
+       }
+       if (firstLink) {
+               /* read header */
+               if (!ensureBufferCapacity(4)) {
+                       return NULL;
+               }
+               uint32_t headerTagCount = 0;
+               growingBuffer.read(&headerTagCount, 4);
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] will read %d header tags.\n", __FILE__, __LINE__, headerTagCount);
+               for (uint32_t headerTagIndex = 0; headerTagIndex < headerTagCount; headerTagIndex++) {
+                       uint8_t tagType = 0;
+                       if (!ensureBufferCapacity(1)) {
+                               return NULL;
+                       }
+                       growingBuffer.read(&tagType, 1);
+                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read tag type %d.\n", __FILE__, __LINE__, tagType);
+                       if (tagType == 0x02) {
+                               StringTag* stringTag = readStringTag(true);
+                               if (stringTag->getId() == 0x01) {
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] FT_FILENAME: “%s”\n", __FILE__, __LINE__, (char*) stringTag->getValue());
+                               } else if (stringTag->getId() == 0x31) {
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] FT_COLLECTIONAUTHOR: “%s”\n", __FILE__, __LINE__, (char*) stringTag->getValue());
+                               } else {
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] unknown string in header: “%s”\n", __FILE__, __LINE__, (char*) stringTag->getValue());
+                               }
+                       } else if (tagType == 0x07) {
+                               BlobTag* blobTag = readBlobTag(true);
+                               if (blobTag->getId() == 0x32) {
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] FT_COLLECTIONAUTHORKEY: %d bytes.\n", __FILE__, __LINE__, blobTag->getSize());
+                               } else {
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] unknown block in header: %d bytes.\n", __FILE__, __LINE__, blobTag->getSize());
+                               }
+                       } else {
+                               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] unknown tag type: %02x.\n", __FILE__, __LINE__, tagType);
+                       }
+               }
+               fileCollectionCount = 0;
+               if (!ensureBufferCapacity(4)) {
+                       return NULL;
+               }
+               growingBuffer.read(&fileCollectionCount, 4);
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] will read %d files.\n", __FILE__, __LINE__, fileCollectionCount);
+               collectionFileIndex = 0;
+               firstLink = false;
+       }
+       if (collectionFileIndex < fileCollectionCount) {
+               uint32_t fileTagCount = 0;
+               if (!ensureBufferCapacity(4)) {
+                       return NULL;
+               }
+               growingBuffer.read(&fileTagCount, 4);
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] will read %d file tags.\n", __FILE__, __LINE__, fileTagCount);
+               StringTag* filenameTag = NULL;
+               uint64_t size = 0;
+               HashTag* hashTag = NULL;
+               for (uint32_t fileTagIndex = 0; fileTagIndex < fileTagCount; fileTagIndex++) {
+                       uint8_t tagType = 0;
+                       if (!ensureBufferCapacity(1)) {
+                               return NULL;
+                       }
+                       growingBuffer.read(&tagType, 1);
+                       tagType &= 0x7f;
+                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] reading tag type %d.\n", __FILE__, __LINE__, tagType);
+                       if (tagType == 0x01) {
+                               hashTag = readHashTag();
+                               if (!hashTag) {
+                                       return NULL;
+                               }
+                       } else if (tagType == 0x02) {
+                               StringTag* stringTag = readStringTag();
+                               if (!stringTag) {
+                                       return NULL;
+                               }
+                               if (stringTag->getId() == 0x01) {
+                                       filenameTag = stringTag;
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read file name “%s”.\n", __FILE__, __LINE__, (char*) stringTag->getValue());
+                               } else if (stringTag->getId() == 0xf6) {
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read file comment “%s”.\n", __FILE__, __LINE__, (char*) stringTag->getValue());
+                                       delete stringTag;
+                               }
+                       } else if ((tagType == 0x03) || (tagType == 0x08) || (tagType == 0x09) || (tagType == 0x0b)) {
+                               if (!ensureBufferCapacity(1)) {
+                                       return NULL;
+                               }
+                               uint8_t id = 0;
+                               growingBuffer.read(&id, 1);
+                               if (id == 0x02) {
+                                       int neededBytes = (tagType == 0x03) ? 4 : ((tagType == 0x08) ? 2 : ((tagType == 0x09) ? 1 : 8));
+                                       if (!ensureBufferCapacity(neededBytes)) {
+                                               return NULL;
+                                       }
+                                       growingBuffer.read(&size, neededBytes);
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] file size is %llu bytes.\n", __FILE__, __LINE__, size);
+                               } else if (id == 0xf7) {
+                                       uint8_t fileRating = 0;
+                                       if (!ensureBufferCapacity(1)) {
+                                               return NULL;
+                                       }
+                                       growingBuffer.read(&fileRating, 1);
+                                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] read file rating %d.\n", __FILE__, __LINE__, fileRating);
+                               }
+                       }
+               }
+               collectionFileIndex++;
+               growingBuffer.cut();
+               if (filenameTag && hashTag) {
+                       ED2KLink* ed2kLink = new ED2KLink((char*) filenameTag->getValue(), size, hashTag->getValue());
+                       delete filenameTag;
+                       delete hashTag;
+                       return ed2kLink;
+               } else {
+                       GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] not enough data to decode file.\n", __FILE__, __LINE__);
+               }
+               if (filenameTag) {
+                       delete filenameTag;
+               }
+               if (hashTag) {
+                       delete hashTag;
+               }
+       } else {
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] reached end of collection.\n", __FILE__, __LINE__);
+       }
+       return NULL;
+}
+
+bool CollectionReader::ensureBufferCapacity(size_t byteCount) {
+       while (!readerInput->isEOF() && (growingBuffer.getRemaining() < byteCount)) {
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] only %d bytes remaning, need at least %d, reading more bytes.\n", __FILE__, __LINE__, growingBuffer.getRemaining(), byteCount);
+               readMoreBytes();
+       }
+       if (readerInput->isEOF() && (growingBuffer.getRemaining() < byteCount)) {
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] need %d more bytes, but file is EOF.\n", __FILE__, __LINE__, (byteCount - growingBuffer.getRemaining()));
+               return false;
+       }
+       return true;
+}
+
+void CollectionReader::readMoreBytes() {
+       char buffer[1024];
+       size_t readBytes;
+
+       readBytes = readerInput->read(buffer, 1024);
+       growingBuffer.write(buffer, readBytes);
+}
+
+void CollectionReader::identifyCollectionType() {
+       size_t readBytes;
+
+       readBytes = readerInput->read(&version, 4);
+       if ((readBytes < 4) || readerInput->isEOF()) {
+               return;
+       }
+       if (version == 0x01) {
+               isTextCollection = false;
+               this->version = 1;
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] identified binary collection, version 1\n", __FILE__, __LINE__);
+       } else if (version == 0x02) {
+               isTextCollection = false;
+               this->version = 2;
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] identified binary collection, version 2\n", __FILE__, __LINE__);
+       } else if (!strncmp("ed2k", (char*) &version, 4)) {
+               isTextCollection = true;
+               growingBuffer.write(&version, 4);
+               version = 0;
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] identified text collection\n", __FILE__, __LINE__);
+       } else  {
+               GlobalSettings::isVerbose() && fprintf(stderr, "[%s:%d] could not identify collection!\n", __FILE__, __LINE__);
+       }
 }