// CalendarObject.cpp - CalendarObject class functions // // (c) 2016-2017 Xestia Software Development. // // This file is part of Xestia Calendar. // // Xestia Calendar is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by the // Free Software Foundation, version 3 of the license. // // Xestia Calendar is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with Xestia Calendar. If not, see #include "CalendarObject.h" #include "../../common/file.h" using namespace std; CalendarObjectLoadResult CalendarObject::LoadFile(std::string LoadFilename){ // Check if the file exists and return // CALENDAROBJECTLOAD_CANNOTOPEN if not. if (!FileExists(LoadFilename)){ return CALENDAROBJECTLOAD_MISSING; } ifstream FileStream; string ReceivedStringData = ""; FileStream.open(LoadFilename, ifstream::in); if (FileStream.rdstate() & ifstream::failbit){ return CALENDAROBJECTLOAD_CANNOTOPEN; } if (FileStream.rdstate() & ifstream::badbit){ return CALENDAROBJECTLOAD_CANNOTOPEN; } // Read the data into a string. char *BufferRead = new char[256]; while (!FileStream.eof()){ FileStream.getline(BufferRead, 256); ReceivedStringData.append(BufferRead); ReceivedStringData.append("\n"); } delete[] BufferRead; CalendarObjectLoadResult StringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL; StringProcResult = LoadString(&ReceivedStringData); return StringProcResult; } CalendarObjectLoadResult CalendarObject::LoadString(std::string *LoadStringData){ bool NewLine = false; bool SkipMode = false; bool ColonFound = false; bool QuoteMode = false; char BufferChar = 0; int StringDataSize = LoadStringData->size(); int SeekCount = 0; string PropertyName; string PropertyValue; while (SeekCount < StringDataSize){ // Check if character is blank or a tab and is the first character // on a new line. if (((*LoadStringData)[SeekCount] == ' ' || (*LoadStringData)[SeekCount] == '\t') && NewLine == true){ // Character is on a new line and it is a space or // tab. Ignore this character as it is not part // of the value. NewLine = false; } else if ((*LoadStringData)[SeekCount] == '\"'){ if (QuoteMode == false){ QuoteMode = true; } else { QuoteMode = false; } BufferChar = (*LoadStringData)[SeekCount]; if (ColonFound == false){ PropertyName += BufferChar; } else { PropertyValue += BufferChar; } } else if (NewLine == true){ // Character is on a new line but not a space or // tab so check if the colon has been found // and add the property name and value to // the lists. if (ColonFound == true){ ObjectName.push_back(PropertyName); ObjectData.push_back(PropertyValue); } ColonFound = false; NewLine = false; PropertyName.clear(); PropertyValue.clear(); BufferChar = (*LoadStringData)[SeekCount]; PropertyName += BufferChar; } else if ((*LoadStringData)[SeekCount] == '\n'){ // Character is the new line character so mark // the NewLine boolean as true. NewLine = true; } else if ((*LoadStringData)[SeekCount] == ':' && QuoteMode == false){ // Character is the colon. Set the colon // found boolen to true. BufferChar = (*LoadStringData)[SeekCount]; if (ColonFound == true){ PropertyValue += BufferChar; } else { ColonFound = true; } } else { // Character is not part of a new line and is not // the new line character itself. BufferChar = (*LoadStringData)[SeekCount]; if (ColonFound == false){ PropertyName += BufferChar; } else { PropertyValue += BufferChar; } } SeekCount++; } // Finish off processing any data that wasn't processed // when the end of the string was reached. if (ColonFound == true && PropertyName.size() > 0 && PropertyValue.size() > 0){ ObjectName.push_back(PropertyName); ObjectData.push_back(PropertyValue); } // Check that the object contains valid data. CalendarObjectLoadResult StringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL; CalendarObjectValidResult BaseDataResult = ValidBaseObject(); CalendarObjectValidResult EventDataResult = ValidObject(); if (BaseDataResult != CALENDAROBJECTVALID_OK || EventDataResult != CALENDAROBJECTVALID_OK){ StringProcResult = CALENDAROBJECTLOAD_INVALIDFORMAT; } else { StringProcResult = CALENDAROBJECTLOAD_OK; } ProcessBaseData(); ProcessData(); return StringProcResult; } CalendarObjectValidResult CalendarObject::ValidBaseObject(){ bool ValidBegin = false; bool ValidAlarmBegin = false; bool ValidVersion = false; bool ValidEnd = false; int SeekCount = 0; vector DeleteLines; vector AlarmObjectName; vector AlarmObjectData; // Check that the first line contains BEGIN:VCALENDAR // and it only appears once. for (vector::iterator iter = ObjectName.begin(); iter != ObjectName.end(); iter++){ if (ObjectName[SeekCount] == "BEGIN" && ObjectData[SeekCount] == "VCALENDAR"){ if (ValidBegin == false){ ValidBegin = true; } else { return CALENDAROBJECTVALID_INVALIDFORMAT; } } if (ObjectName[SeekCount] == "END" && ObjectData[SeekCount] == "VCALENDAR" && ValidBegin == false){ return CALENDAROBJECTVALID_INVALIDFORMAT; } else if (ObjectName[SeekCount] == "END" && ObjectData[SeekCount] == "VALARM" && ValidAlarmBegin == false){ return CALENDAROBJECTVALID_INVALIDFORMAT; } else if (ObjectName[SeekCount] == "END" && ObjectData[SeekCount] == "VCALENDAR" && ValidAlarmBegin == true){ return CALENDAROBJECTVALID_INVALIDFORMAT; } // Look for any VALARM sections. if (ValidAlarmBegin == true){ AlarmObjectName.push_back(ObjectName[SeekCount]); AlarmObjectData.push_back(ObjectData[SeekCount]); DeleteLines.push_back(SeekCount); } if (ObjectName[SeekCount] == "END" && ObjectData[SeekCount] == "VALARM" && ValidAlarmBegin == true){ EventAlarmName.push_back(AlarmObjectName); EventAlarmData.push_back(AlarmObjectData); AlarmObjectName.clear(); AlarmObjectData.clear(); ValidAlarmBegin = false; } if (ObjectName[SeekCount] == "BEGIN" && ObjectData[SeekCount] == "VALARM" && ValidBegin == true){ if (ValidAlarmBegin == false){ ValidAlarmBegin = true; } else { return CALENDAROBJECTVALID_INVALIDFORMAT; } AlarmObjectName.push_back(ObjectName[SeekCount]); AlarmObjectData.push_back(ObjectData[SeekCount]); DeleteLines.push_back(SeekCount); } SeekCount++; } SeekCount = 0; // Check that the last line contains END:VCALENDAR // and it only appears once. for (vector::iterator iter = ObjectName.begin(); iter != ObjectName.end(); iter++){ if (ObjectName[SeekCount] == "END" && ObjectData[SeekCount] == "VCALENDAR"){ if (ValidEnd == false){ ValidEnd = true; } else { return CALENDAROBJECTVALID_INVALIDFORMAT; } } SeekCount++; } SeekCount = 0; // Check that the VERSION value contains 2.0 and that // it only appears once. for (vector::iterator iter = ObjectName.begin(); iter != ObjectName.end(); iter++){ if (ObjectName[SeekCount] == "VERSION" && ObjectData[SeekCount] == "2.0"){ if (ValidVersion == false){ ValidVersion = true; } else { return CALENDAROBJECTVALID_INVALIDFORMAT; } } SeekCount++; } // Remove lines that aren't needed as they have // been moved to the EventAlarm section. for (vector::reverse_iterator deliter = DeleteLines.rbegin(); deliter != DeleteLines.rend(); deliter++){ ObjectName.erase(ObjectName.begin()+(*deliter)); ObjectData.erase(ObjectData.begin()+(*deliter)); } if (ValidBegin == true && ValidEnd == true && ValidVersion == true && ValidAlarmBegin == false){ return CALENDAROBJECTVALID_OK; } else { return CALENDAROBJECTVALID_INVALIDFORMAT; } } void CalendarObject::ProcessBaseData(){ // Process the base object data. multimap DataReceived; // Get the method (METHOD). DataReceived = ProcessTextVectors(&ObjectName, &ObjectData, false, "METHOD"); if (DataReceived.begin() != DataReceived.end()){ try { MethodTokens = DataReceived.begin()->first.substr(7); } catch(const out_of_range &oor){ // Do nothing as there is no data. } MethodData = DataReceived.begin()->second; } // Get the calendar scale (CALSCALE). DataReceived = ProcessTextVectors(&ObjectName, &ObjectData, false, "CALSCALE"); if (DataReceived.begin() != DataReceived.end()){ try { CalendarScaleTokens = DataReceived.begin()->first.substr(9); } catch(const out_of_range &oor){ // Do nothing as there is no data. } CalendarScaleData = DataReceived.begin()->second; } }