// 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. ifstream fileStream; string receivedStringData = ""; #ifndef WIN32 if (!FileExists(loadFilename)) { return CALENDAROBJECTLOAD_MISSING; } fileStream.open(loadFilename, ifstream::in); #else wstring loadFilename_utf16; int len = MultiByteToWideChar(CP_UTF8, 0, &loadFilename[0], (int)loadFilename.size(), NULL, 0); if (len > 0) { loadFilename_utf16.resize(len); MultiByteToWideChar(CP_UTF8, 0, &loadFilename[0], (int)loadFilename.size(), &loadFilename_utf16[0], len); } if (!FileExists(loadFilename_utf16)) { return CALENDAROBJECTLOAD_MISSING; } fileStream.open(loadFilename_utf16, ifstream::in); #endif 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; } }