MessageRecord Reader::readNext() { MessageRecord msg; if(!readNextToBuffer(_msgbuf)) return msg; protocol::Message *message; switch(_formatversion) { // see protocol changelog in doc/protocol.md case version32(11, 3): case version32(11, 2): message = compat::deserializeV11((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; default: message = protocol::Message::deserialize((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; } if(message) { msg.status = MessageRecord::OK; msg.message = message; } else { msg.status = MessageRecord::INVALID; msg.error.len = protocol::Message::sniffLength(_msgbuf.constData()); msg.error.type = protocol::MessageType(_msgbuf.at(2)); } return msg; }
bool Writer::writeHibernationHeader(const HibernationHeader &header) { Q_ASSERT(_file->isOpen()); // Format identification (note the 'H' ending) const char *MAGIC = "DPRECH"; _file->write(MAGIC, 7); // Protocol version // Major version is always the same as the server version, but minor version // is taken from the session uchar version[4]; qToBigEndian(version32(DRAWPILE_PROTO_MAJOR_VERSION, header.minorVersion), version); _file->write((const char*)version, 4); // Program version (this is always the server version) const char *VERSION = DRAWPILE_VERSION; _file->write(VERSION, qstrlen(VERSION)+1); // Hibernation format version char hver = 1; _file->write(&hver, 1); // Session title QByteArray title = header.title.toUtf8(); uchar titlelen[2]; qToBigEndian<quint16>(title.length(), titlelen); _file->write((const char*)titlelen, 2); _file->write(title); // Session founder name QByteArray founder = header.founder.toUtf8(); Q_ASSERT(founder.length() < 256); // usernames should be limited to something way shorter than this uchar founderlen = qMin(founder.length(), 255); _file->putChar(founderlen); _file->write(founder); // Session flags char flags = header.flags; _file->write(&flags, 1); // Session password QByteArray password = header.password; uchar passwdlen[2]; qToBigEndian<quint16>(password.length(), passwdlen); _file->write((const char*)passwdlen, 2); _file->write(password); return true; }
bool Writer::writeHeader() { Q_ASSERT(_file->isOpen()); // Format identification const char *MAGIC = "DPRECR"; _file->write(MAGIC, 7); // Protocol version uchar version[4]; qToBigEndian(version32(DRAWPILE_PROTO_MAJOR_VERSION, DRAWPILE_PROTO_MINOR_VERSION), version); _file->write((const char*)version, 4); // Program version const char *VERSION = DRAWPILE_VERSION; _file->write(VERSION, qstrlen(VERSION)+1); return true; }
Compatibility Reader::open() { if(!_file->isOpen()) { if(!_file->open(QFile::ReadOnly)) { return CANNOT_READ; } } // Read magic bytes "DPRECR\0" or "DPRECH\0" char buf[7]; if(_file->read(buf, 7) != 7) return CANNOT_READ; if(memcmp(buf, "DPRECR", 7)) { if(memcmp(buf, "DPRECH", 7)) return NOT_DPREC; _isHibernation = true; } // Read protocol version if(_file->read(buf, 4) != 4) return NOT_DPREC; _formatversion = qFromBigEndian<quint32>((const uchar*)buf); // Read program version QByteArray progver; do { if(_file->read(buf, 1) != 1) return NOT_DPREC; progver.append(buf[0]); } while(buf[0] != '\0'); _writerversion = QString::fromUtf8(progver); // If this is a hibernation file, read the rest of the header if(_isHibernation) { // We already read the protocol minor version _hibheader.minorVersion = minorVersion(_formatversion); // Check hibernation file format version char fmtver; if(!_file->getChar(&fmtver)) return NOT_DPREC; // Currently there is only one format and no forward compatibility if((uchar)fmtver != 1) return INCOMPATIBLE; // Read session title if(_file->read(buf, 2) != 2) return NOT_DPREC; quint16 titleLen = qFromBigEndian<quint16>((const uchar*)buf); QByteArray title = _file->read(titleLen); if(title.length() != titleLen) return NOT_DPREC; _hibheader.title = QString::fromUtf8(title); // Read session founder name if(_file->read(buf, 1) != 1) return NOT_DPREC; quint8 founderLen = *buf; QByteArray founder = _file->read(founderLen); if(founder.length() != founderLen) return NOT_DPREC; _hibheader.founder = QString::fromUtf8(founder); // Read session flags char flags; if(!_file->getChar(&flags)) return NOT_DPREC; _hibheader.flags = HibernationHeader::Flags((uchar)flags); // Read session password if(_file->read(buf, 2) != 2) return NOT_DPREC; quint16 passwdLen = qFromBigEndian<quint16>((const uchar*)buf); _hibheader.password = _file->read(passwdLen); if(_hibheader.password.length() != passwdLen) return NOT_DPREC; } _beginning = _file->pos(); if(_isHibernation) { // Compatibility check is simple for hibernation files: we only need to look at the major version if(DRAWPILE_PROTO_MAJOR_VERSION == majorVersion(_formatversion)) return COMPATIBLE; else return INCOMPATIBLE; } else { // Compatability check for normal recordings const quint32 myversion = version32(DRAWPILE_PROTO_MAJOR_VERSION, DRAWPILE_PROTO_MINOR_VERSION); // Best case: exact version match if(myversion == _formatversion) return COMPATIBLE; // A recording made with a newer (major) version may contain unsupported commands. if(majorVersion(myversion) < majorVersion(_formatversion)) return UNKNOWN_COMPATIBILITY; // Newer minor version: expect rendering differences if(myversion < _formatversion) return MINOR_INCOMPATIBILITY; // Old versions known to be compatible switch(_formatversion) { case version32(11, 3): // we have compatibility code for these versions case version32(11, 2): return COMPATIBLE; } // Other versions are not supported return INCOMPATIBLE; } }
Compatibility Reader::open() { if(!m_file->isOpen()) { if(!m_file->open(QFile::ReadOnly)) { return CANNOT_READ; } } // Read magic bytes "DPREC\0" char buf[6]; if(m_file->read(buf, 6) != 6) return CANNOT_READ; if(memcmp(buf, "DPREC", 6) != 0) { // This may be a pre 2.0 recording if(memcmp(buf, "DPRECR", 6)==0) return INCOMPATIBLE; return NOT_DPREC; } // Read metadata block if(m_file->read(buf, 2) != 2) return NOT_DPREC; const quint16 metadatalen = qFromBigEndian<quint16>((const uchar*)buf); QByteArray metadatabuf = m_file->read(metadatalen); if(metadatabuf.length() != metadatalen) return NOT_DPREC; QJsonParseError jsonError; QJsonDocument metadatadoc = QJsonDocument::fromJson(metadatabuf, &jsonError); if(jsonError.error != QJsonParseError::NoError) { qWarning() << jsonError.errorString(); return NOT_DPREC; } m_metadata = metadatadoc.object(); // Header completed! m_beginning = m_file->pos(); // Check version numbers m_version = protocol::ProtocolVersion::fromString(m_metadata["version"].toString()); // Best case is exact match. const protocol::ProtocolVersion current = protocol::ProtocolVersion::current(); if(m_version == current) return COMPATIBLE; // Different namespace means this recording is meant for some other program if(m_version.ns() != current.ns()) return NOT_DPREC; // A recording made with a newer (major) version may contain unsupported commands. if(current.major() < m_version.major()) return UNKNOWN_COMPATIBILITY; // Newer minor version: expect rendering differences if(current.minor() < m_version.minor()) return MINOR_INCOMPATIBILITY; #if 0 #if DRAWPILE_PROTO_MAJOR_VERSION != 16 || DRAWPILE_PROTO_MINOR_VERSION != 1 #error Update recording compatability check! #endif // Old versions known to be compatible switch(m_formatversion) { case version32(15, 5): // fully compatible (with support code) case version32(14, 5): case version32(13, 5): case version32(13, 4): case version32(12, 4): return COMPATIBLE; case version32(11, 3): // supported, but expect minor rendering differences case version32(11, 2): case version32(10, 2): case version32(9, 2): case version32(8, 1): case version32(7, 1): return MINOR_INCOMPATIBILITY; } #endif // Other versions are not supported return INCOMPATIBLE; }
MessageRecord Reader::readNext() { MessageRecord msg; if(!readNextToBuffer(m_msgbuf)) return msg; protocol::Message *message; #if 0 // TODO if(m_formatversion != version32(DRAWPILE_PROTO_MAJOR_VERSION, DRAWPILE_PROTO_MINOR_VERSION)) { // see protocol changelog in doc/protocol.md switch(_formatversion) { case version32(15, 5): message = compat::deserializeV15_5((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(14, 5): case version32(13, 5): message = compat::deserializeV14((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(12, 4): message = compat::deserializeV12((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(11, 3): case version32(11, 2): message = compat::deserializeV11((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(10, 2): case version32(9, 2): case version32(8, 1): case version32(7, 1): message = compat::deserializeV10((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; default: message = protocol::Message::deserialize((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; } qWarning("TODO: recording compatability mode not yet implemented!"); message = 0; } else { message = protocol::Message::deserialize((const uchar*)m_msgbuf.constData(), m_msgbuf.length(), true); } #endif message = protocol::Message::deserialize((const uchar*)m_msgbuf.constData(), m_msgbuf.length(), true); if(message) { msg.status = MessageRecord::OK; msg.message = message; } else { msg.status = MessageRecord::INVALID; msg.error.len = protocol::Message::sniffLength(m_msgbuf.constData()); msg.error.type = protocol::MessageType(m_msgbuf.at(2)); } return msg; }
MessageRecord Reader::readNext() { MessageRecord msg; if(!readNextToBuffer(_msgbuf)) return msg; protocol::Message *message; if(_compat && _formatversion != version32(DRAWPILE_PROTO_MAJOR_VERSION, DRAWPILE_PROTO_MINOR_VERSION)) { // see protocol changelog in doc/protocol.md switch(_formatversion) { case version32(15, 5): message = compat::deserializeV15_5((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(14, 5): case version32(13, 5): message = compat::deserializeV14((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(12, 4): message = compat::deserializeV12((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(11, 3): case version32(11, 2): message = compat::deserializeV11((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; case version32(10, 2): case version32(9, 2): case version32(8, 1): case version32(7, 1): message = compat::deserializeV10((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; default: message = protocol::Message::deserialize((const uchar*)_msgbuf.constData(), _msgbuf.length()); break; } } else { message = protocol::Message::deserialize((const uchar*)_msgbuf.constData(), _msgbuf.length()); } if(message) { msg.status = MessageRecord::OK; msg.message = message; } else { msg.status = MessageRecord::INVALID; msg.error.len = protocol::Message::sniffLength(_msgbuf.constData()); msg.error.type = protocol::MessageType(_msgbuf.at(2)); } return msg; }