diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp index 76712c31e7..61c3ba9f17 100644 --- a/src/PowderToySDL.cpp +++ b/src/PowderToySDL.cpp @@ -945,8 +945,9 @@ int main(int argc, char * argv[]) std::string ptsaveArg = arguments["ptsave"]; try { - if(!ptsaveArg.find("ptsave:")) - { + if (ptsaveArg.find("ptsave:")) + throw std::runtime_error("Invalid save link"); + std::string saveIdPart = ""; int saveId; size_t hashPos = ptsaveArg.find('#'); @@ -958,33 +959,30 @@ int main(int argc, char * argv[]) { saveIdPart = ptsaveArg.substr(7); } - if (saveIdPart.length()) - { -#ifdef DEBUG - std::cout << "Got Ptsave: id: " << saveIdPart << std::endl; -#endif - saveId = format::StringToNumber(saveIdPart); - if(!saveId) - throw std::runtime_error("Invalid Save ID"); - - SaveInfo * newSave = Client::Ref().GetSave(saveId, 0); - if (!newSave) - throw std::runtime_error("Could not load save"); - GameSave * newGameSave = new GameSave(Client::Ref().GetSaveData(saveId, 0)); - newSave->SetGameSave(newGameSave); - - gameController->LoadSave(newSave); - delete newSave; - } - else - { + if (!saveIdPart.length()) throw std::runtime_error("No Save ID"); - } - } +#ifdef DEBUG + std::cout << "Got Ptsave: id: " << saveIdPart << std::endl; +#endif + saveId = format::StringToNumber(saveIdPart); + if (!saveId) + throw std::runtime_error("Invalid Save ID"); + + SaveInfo * newSave = Client::Ref().GetSave(saveId, 0); + if (!newSave) + throw std::runtime_error("Could not load save info"); + std::vector saveData = Client::Ref().GetSaveData(saveId, 0); + if (!saveData.size()) + throw std::runtime_error("Could not load save\n" + Client::Ref().GetLastError()); + GameSave * newGameSave = new GameSave(saveData); + newSave->SetGameSave(newGameSave); + + gameController->LoadSave(newSave); + delete newSave; } catch (std::exception & e) { - new ErrorMessage("Error", "Invalid save link"); + new ErrorMessage("Error", e.what()); } } diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 39a4830081..985062a198 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -666,20 +666,86 @@ std::vector > Client::GetServerNotifications return serverNotifications; } +RequestStatus Client::ParseServerReturn(char *result, int status, bool json) +{ + // no server response, return "Malformed Response" + if (status == 200 && !result) + { + status = 603; + } + if (status == 302) + return RequestOkay; + if (status != 200) + { + std::stringstream httperror; + httperror << "HTTP Error " << status << ": " << http_ret_text(status); + lastError = httperror.str(); + return RequestFailure; + } + + if (json) + { + std::istringstream datastream(result); + json::Object root; + + try + { + json::Reader::Read(root, datastream); + // assume everything is fine if an empty [] is returned + if (root.Size() == 0) + { + return RequestOkay; + } + json::Number status = root["Status"]; + if (status != 1) + { + json::String error = root["Error"]; + lastError = std::string(error); + if (lastError == "") + lastError = "Unspecified Error"; + return RequestFailure; + } + } + catch (std::exception &e) + { + // sometimes the server returns a 200 with the text "Error: 401" + if (!strncmp((const char *)result, "Error: ", 7)) + { + status = atoi(result+7); + std::stringstream httperror; + httperror << "HTTP Error " << status << ": " << http_ret_text(status); + lastError = httperror.str(); + return RequestFailure; + } + lastError = "Could not read response"; + return RequestFailure; + } + } + else + { + if (strncmp((const char *)result, "OK", 2)) + { + lastError = std::string(result); + return RequestFailure; + } + } + return RequestOkay; +} + void Client::Tick() { //Check thumbnail queue RequestBroker::Ref().FlushThumbQueue(); //Check status on version check request - if(versionCheckRequest && http_async_req_status(versionCheckRequest)) + if (versionCheckRequest && http_async_req_status(versionCheckRequest)) { int status; int dataLength; char * data = http_async_req_stop(versionCheckRequest, &status, &dataLength); versionCheckRequest = NULL; - if(status != 200) + if (status != 200) { free(data); } @@ -725,7 +791,7 @@ void Client::Tick() json::Number stableMinor = stableVersion["Minor"]; json::Number stableBuild = stableVersion["Build"]; json::String stableFile = stableVersion["File"]; - if(stableMajor.Value()>SAVE_VERSION || (stableMinor.Value()>MINOR_VERSION && stableMajor.Value()==SAVE_VERSION) || stableBuild.Value()>BUILD_NUM) + if (stableMajor.Value()>SAVE_VERSION || (stableMinor.Value()>MINOR_VERSION && stableMajor.Value()==SAVE_VERSION) || stableBuild.Value()>BUILD_NUM) { updateAvailable = true; updateInfo = UpdateInfo(stableMajor.Value(), stableMinor.Value(), stableBuild.Value(), stableFile.Value(), UpdateInfo::Stable); @@ -738,7 +804,7 @@ void Client::Tick() json::Number betaMinor = betaVersion["Minor"]; json::Number betaBuild = betaVersion["Build"]; json::String betaFile = betaVersion["File"]; - if(betaMajor.Value()>SAVE_VERSION || (betaMinor.Value()>MINOR_VERSION && betaMajor.Value()==SAVE_VERSION) || betaBuild.Value()>BUILD_NUM) + if (betaMajor.Value()>SAVE_VERSION || (betaMinor.Value()>MINOR_VERSION && betaMajor.Value()==SAVE_VERSION) || betaBuild.Value()>BUILD_NUM) { updateAvailable = true; updateInfo = UpdateInfo(betaMajor.Value(), betaMinor.Value(), betaBuild.Value(), betaFile.Value(), UpdateInfo::Beta); @@ -749,7 +815,7 @@ void Client::Tick() json::Object snapshotVersion = versions["Snapshot"]; json::Number snapshotSnapshot = snapshotVersion["Snapshot"]; json::String snapshotFile = snapshotVersion["File"]; - if(snapshotSnapshot.Value() > SNAPSHOT_ID) + if (snapshotSnapshot.Value() > SNAPSHOT_ID) { updateAvailable = true; updateInfo = UpdateInfo(snapshotSnapshot.Value(), snapshotFile.Value(), UpdateInfo::Snapshot); @@ -835,9 +901,9 @@ void Client::WritePrefs() configFile.open("powder.pref", std::ios::trunc); #endif - if(configFile) + if (configFile) { - if(authUser.ID) + if (authUser.ID) { configDocument["User"]["ID"] = json::Number(authUser.ID); configDocument["User"]["SessionID"] = json::String(authUser.SessionID); @@ -904,9 +970,9 @@ RequestStatus Client::UploadSave(SaveInfo & save) int dataLength = 0; std::stringstream userIDStream; userIDStream << authUser.ID; - if(authUser.ID) + if (authUser.ID) { - if(!save.GetGameSave()) + if (!save.GetGameSave()) { lastError = "Empty game save"; return RequestFailure; @@ -915,7 +981,7 @@ RequestStatus Client::UploadSave(SaveInfo & save) gameData = save.GetGameSave()->Serialise(gameDataLength); - if(!gameData) + if (!gameData) { lastError = "Cannot upload game save"; return RequestFailure; @@ -933,7 +999,6 @@ RequestStatus Client::UploadSave(SaveInfo & save) const char *const postNames[] = { "Name", "Description", "Data:save.bin", "Publish", NULL }; const char *const postDatas[] = { saveName, saveDescription, gameData, (char *)(save.GetPublished()?"Public":"Private") }; size_t postLengths[] = { save.GetName().length(), save.GetDescription().length(), gameDataLength, (size_t)(save.GetPublished()?6:7) }; - //std::cout << postNames[0] << " " << postDatas[0] << " " << postLengths[0] << std::endl; data = http_multipart_post("http://" SERVER "/Save.api", postNames, postDatas, postLengths, userid, NULL, session, &dataStatus, &dataLength); delete[] saveDescription; @@ -946,40 +1011,22 @@ RequestStatus Client::UploadSave(SaveInfo & save) lastError = "Not authenticated"; return RequestFailure; } - if(data && dataStatus == 200) + + RequestStatus ret = ParseServerReturn(data, dataStatus, false); + if (ret == RequestOkay) { - if(strncmp((const char *)data, "OK", 2)!=0) + int saveID = format::StringToNumber(data+3); + if (!saveID) { - delete[] gameData; - lastError = std::string((const char *)data); - free(data); - return RequestFailure; + lastError = "Server did not return Save ID"; + ret = RequestFailure; } else - { - int tempID; - std::stringstream saveIDStream((char *)(data+3)); - saveIDStream >> tempID; - if(!tempID) - { - lastError = "Server did not return Save ID"; - return RequestFailure; - } - else - { - save.SetID(tempID); - } - } - free(data); - delete[] gameData; - return RequestOkay; - } - else if(data) - { - free(data); + save.SetID(saveID); } + free(data); delete[] gameData; - return RequestFailure; + return ret; } void Client::MoveStampToFront(std::string stampID) @@ -1140,26 +1187,23 @@ RequestStatus Client::ExecVote(int saveID, int direction) int dataStatus; char * data; int dataLength = 0; - std::stringstream idStream; - idStream << saveID; - if(authUser.ID) + if (authUser.ID) { char * directionText = (char*)(direction==1?"Up":"Down"); std::string saveIDText = format::NumberToString(saveID); std::string userIDText = format::NumberToString(authUser.ID); char *id = new char[saveIDText.length() + 1]; - std::strcpy (id, saveIDText.c_str()); + std::strcpy(id, saveIDText.c_str()); char *userid = new char[userIDText.length() + 1]; - std::strcpy (userid, userIDText.c_str()); + std::strcpy(userid, userIDText.c_str()); char *session = new char[authUser.SessionID.length() + 1]; - std::strcpy (session, authUser.SessionID.c_str()); + std::strcpy(session, authUser.SessionID.c_str()); const char *const postNames[] = { "ID", "Action", NULL }; const char *const postDatas[] = { id, directionText }; size_t postLengths[] = { saveIDText.length(), strlen(directionText) }; - //std::cout << postNames[0] << " " << postDatas[0] << " " << postLengths[0] << std::endl; data = http_multipart_post("http://" SERVER "/Vote.api", postNames, postDatas, postLengths, userid, NULL, session, &dataStatus, &dataLength); delete[] id; @@ -1171,54 +1215,32 @@ RequestStatus Client::ExecVote(int saveID, int direction) lastError = "Not authenticated"; return RequestFailure; } - if(data && dataStatus == 200) - { - if(strncmp((const char *)data, "OK", 2)!=0) - { - lastError = std::string((const char *)data); - free(data); - return RequestFailure; - } - free(data); - return RequestOkay; - } - else if(data) - { - free(data); - } - lastError = http_ret_text(dataStatus); - return RequestFailure; + RequestStatus ret = ParseServerReturn(data, dataStatus, false); + return ret; } unsigned char * Client::GetSaveData(int saveID, int saveDate, int & dataLength) { lastError = ""; int dataStatus; - unsigned char * data; + char *data; dataLength = 0; std::stringstream urlStream; - if(saveDate) - { + if (saveDate) urlStream << "http://" << STATICSERVER << "/" << saveID << "_" << saveDate << ".cps"; - } else - { urlStream << "http://" << STATICSERVER << "/" << saveID << ".cps"; - } char *url = new char[urlStream.str().length() + 1]; - std::strcpy (url, urlStream.str().c_str()); - data = (unsigned char *)http_simple_get(url, &dataStatus, &dataLength); + std::strcpy(url, urlStream.str().c_str()); + data = http_simple_get(url, &dataStatus, &dataLength); delete[] url; - if(data && dataStatus == 200) - { - return data; - } - else if(data) - { - free(data); - } + // will always return failure + ParseServerReturn(data, dataStatus, false); + if (data && dataStatus == 200) + return (unsigned char *)data; + free(data); return NULL; } @@ -1226,9 +1248,10 @@ std::vector Client::GetSaveData(int saveID, int saveDate) { int dataSize; unsigned char * data = GetSaveData(saveID, saveDate, dataSize); + if (!data) + return std::vector(); std::vector saveData(data, data+dataSize); - delete[] data; return saveData; } @@ -1241,7 +1264,7 @@ RequestBroker::Request * Client::GetSaveDataAsync(int saveID, int saveDate) } else { urlStream << "http://" << STATICSERVER << "/" << saveID << ".cps"; } - return new WebRequest(urlStream.str()); + return new WebRequest(urlStream.str()); } RequestBroker::Request * Client::SaveUserInfoAsync(UserInfo info) @@ -1273,7 +1296,7 @@ RequestBroker::Request * Client::SaveUserInfoAsync(UserInfo info) std::map postData; postData.insert(std::pair("Location", info.location)); postData.insert(std::pair("Biography", info.biography)); - return new APIRequest("http://" SERVER "/Profile.json", postData, new StatusParser()); + return new APIRequest("http://" SERVER "/Profile.json", postData, new StatusParser()); } RequestBroker::Request * Client::GetUserInfoAsync(std::string username) @@ -1340,52 +1363,43 @@ LoginStatus Client::Login(std::string username, std::string password, User & use const char *const postDatas[] = { (char*)username.c_str(), totalHash }; size_t postLengths[] = { username.length(), 32 }; data = http_multipart_post("http://" SERVER "/Login.json", postNames, postDatas, postLengths, NULL, NULL, NULL, &dataStatus, &dataLength); - if(dataStatus == 200 && data) + RequestStatus ret = ParseServerReturn(data, dataStatus, true); + if (ret == RequestOkay) { try { std::istringstream dataStream(data); json::Object objDocument; json::Reader::Read(objDocument, dataStream); - json::Number tempStatus = objDocument["Status"]; - free(data); - if(tempStatus.Value() == 1) - { - json::Number userIDTemp = objDocument["UserID"]; - json::String sessionIDTemp = objDocument["SessionID"]; - json::String sessionKeyTemp = objDocument["SessionKey"]; - json::String userElevationTemp = objDocument["Elevation"]; - - json::Array notificationsArray = objDocument["Notifications"]; - for (size_t j = 0; j < notificationsArray.Size(); j++) - { - json::String notificationLink = notificationsArray[j]["Link"]; - json::String notificationText = notificationsArray[j]["Text"]; - std::pair item = std::pair(notificationText.Value(), notificationLink.Value()); - AddServerNotification(item); - } + json::Number userIDTemp = objDocument["UserID"]; + json::String sessionIDTemp = objDocument["SessionID"]; + json::String sessionKeyTemp = objDocument["SessionKey"]; + json::String userElevationTemp = objDocument["Elevation"]; - user.Username = username; - user.ID = userIDTemp.Value(); - user.SessionID = sessionIDTemp.Value(); - user.SessionKey = sessionKeyTemp.Value(); - std::string userElevation = userElevationTemp.Value(); - if(userElevation == "Admin") - user.UserElevation = User::ElevationAdmin; - else if(userElevation == "Mod") - user.UserElevation = User::ElevationModerator; - else - user.UserElevation= User::ElevationNone; - return LoginOkay; - } - else + json::Array notificationsArray = objDocument["Notifications"]; + for (size_t j = 0; j < notificationsArray.Size(); j++) { - json::String tempError = objDocument["Error"]; - lastError = tempError.Value(); - return LoginError; + json::String notificationLink = notificationsArray[j]["Link"]; + json::String notificationText = notificationsArray[j]["Text"]; + + std::pair item = std::pair(notificationText.Value(), notificationLink.Value()); + AddServerNotification(item); } + + user.Username = username; + user.ID = userIDTemp.Value(); + user.SessionID = sessionIDTemp.Value(); + user.SessionKey = sessionKeyTemp.Value(); + std::string userElevation = userElevationTemp.Value(); + if(userElevation == "Admin") + user.UserElevation = User::ElevationAdmin; + else if(userElevation == "Mod") + user.UserElevation = User::ElevationModerator; + else + user.UserElevation= User::ElevationNone; + return LoginOkay; } catch (json::Exception &e) { @@ -1393,10 +1407,6 @@ LoginStatus Client::Login(std::string username, std::string password, User & use return LoginError; } } - else - { - lastError = http_ret_text(dataStatus); - } free(data); return LoginError; } @@ -1419,35 +1429,9 @@ RequestStatus Client::DeleteSave(int saveID) lastError = "Not authenticated"; return RequestFailure; } - if(dataStatus == 200 && data) - { - try - { - std::istringstream dataStream(data); - json::Object objDocument; - json::Reader::Read(objDocument, dataStream); - - int status = ((json::Number)objDocument["Status"]).Value(); - - if(status!=1) - goto failure; - } - catch (json::Exception &e) - { - lastError = "Could not read response"; - goto failure; - } - } - else - { - lastError = http_ret_text(dataStatus); - goto failure; - } + RequestStatus ret = ParseServerReturn(data, dataStatus, true); free(data); - return RequestOkay; -failure: - free(data); - return RequestFailure; + return ret; } RequestStatus Client::AddComment(int saveID, std::string comment) @@ -1472,40 +1456,9 @@ RequestStatus Client::AddComment(int saveID, std::string comment) lastError = "Not authenticated"; return RequestFailure; } - if(dataStatus == 200 && data) - { - try - { - std::istringstream dataStream(data); - json::Object objDocument; - json::Reader::Read(objDocument, dataStream); - - int status = ((json::Number)objDocument["Status"]).Value(); - - if(status!=1) - { - lastError = ((json::String)objDocument["Error"]).Value(); - } - - if(status!=1) - goto failure; - } - catch (json::Exception &e) - { - lastError = "Could not read response"; - goto failure; - } - } - else - { - lastError = http_ret_text(dataStatus); - goto failure; - } - free(data); - return RequestOkay; -failure: + RequestStatus ret = ParseServerReturn(data, dataStatus, true); free(data); - return RequestFailure; + return ret; } RequestStatus Client::FavouriteSave(int saveID, bool favourite) @@ -1528,38 +1481,9 @@ RequestStatus Client::FavouriteSave(int saveID, bool favourite) lastError = "Not authenticated"; return RequestFailure; } - if(dataStatus == 200 && data) - { - try - { - std::istringstream dataStream(data); - json::Object objDocument; - json::Reader::Read(objDocument, dataStream); - - int status = ((json::Number)objDocument["Status"]).Value(); - - if(status!=1) - { - lastError = ((json::String)objDocument["Error"]).Value(); - goto failure; - } - } - catch (json::Exception &e) - { - lastError = "Could not read response"; - goto failure; - } - } - else - { - lastError = http_ret_text(dataStatus); - goto failure; - } + RequestStatus ret = ParseServerReturn(data, dataStatus, true); free(data); - return RequestOkay; -failure: - free(data); - return RequestFailure; + return ret; } RequestStatus Client::ReportSave(int saveID, std::string message) @@ -1584,38 +1508,9 @@ RequestStatus Client::ReportSave(int saveID, std::string message) lastError = "Not authenticated"; return RequestFailure; } - if(dataStatus == 200 && data) - { - try - { - std::istringstream dataStream(data); - json::Object objDocument; - json::Reader::Read(objDocument, dataStream); - - int status = ((json::Number)objDocument["Status"]).Value(); - - if(status!=1) - { - lastError = ((json::String)objDocument["Error"]).Value(); - goto failure; - } - } - catch (json::Exception &e) - { - lastError = "Could not read response"; - goto failure; - } - } - else - { - lastError = http_ret_text(dataStatus); - goto failure; - } + RequestStatus ret = ParseServerReturn(data, dataStatus, true); free(data); - return RequestOkay; -failure: - free(data); - return RequestFailure; + return ret; } RequestStatus Client::UnpublishSave(int saveID) @@ -1636,44 +1531,16 @@ RequestStatus Client::UnpublishSave(int saveID) lastError = "Not authenticated"; return RequestFailure; } - if(dataStatus == 200 && data) - { - try - { - std::istringstream dataStream(data); - json::Object objDocument; - json::Reader::Read(objDocument, dataStream); - - int status = ((json::Number)objDocument["Status"]).Value(); - - if(status!=1) - { - lastError = ((json::String)objDocument["Error"]).Value(); - goto failure; - } - } - catch (json::Exception &e) - { - lastError = "Could not read response"; - goto failure; - } - } - else - { - lastError = http_ret_text(dataStatus); - goto failure; - } - free(data); - return RequestOkay; -failure: + RequestStatus ret = ParseServerReturn(data, dataStatus, true); free(data); - return RequestFailure; + return ret; } RequestStatus Client::PublishSave(int saveID) { lastError = ""; std::stringstream urlStream; + char *data; int dataStatus; urlStream << "http://" << SERVER << "/Browse/View.html?ID=" << saveID << "&Key=" << authUser.SessionKey; if (authUser.ID) @@ -1683,20 +1550,15 @@ RequestStatus Client::PublishSave(int saveID) const char *const postNames[] = { "ActionPublish", NULL }; const char *const postDatas[] = { "" }; size_t postLengths[] = { 1 }; - char *data = http_multipart_post(urlStream.str().c_str(), postNames, postDatas, postLengths, userIDStream.str().c_str(), NULL, authUser.SessionID.c_str(), &dataStatus, NULL); - free(data); - } + data = http_multipart_post(urlStream.str().c_str(), postNames, postDatas, postLengths, userIDStream.str().c_str(), NULL, authUser.SessionID.c_str(), &dataStatus, NULL); } else { lastError = "Not authenticated"; return RequestFailure; } - if (dataStatus != 302) - { - lastError = http_ret_text(dataStatus); - return RequestFailure; - } - return RequestOkay; + RequestStatus ret = ParseServerReturn(data, dataStatus, false); + free(data); + return ret; } SaveInfo * Client::GetSave(int saveID, int saveDate) @@ -1710,7 +1572,6 @@ SaveInfo * Client::GetSave(int saveID, int saveDate) } char * data; int dataStatus, dataLength; - //Save(int _id, int _votesUp, int _votesDown, string _userName, string _name, string description_, string date_, bool published_): if(authUser.ID) { std::stringstream userIDStream; @@ -1860,48 +1721,6 @@ RequestBroker::Request * Client::GetSaveAsync(int saveID, int saveDate) return new APIRequest(urlStream.str(), new SaveInfoParser()); } -Thumbnail * Client::GetPreview(int saveID, int saveDate) -{ - std::stringstream urlStream; - urlStream << "http://" << STATICSERVER << "/" << saveID; - if(saveDate) - { - urlStream << "_" << saveDate; - } - urlStream << "_large.pti"; - pixel * thumbData; - char * data; - int status, data_size, imgw, imgh; - data = http_simple_get((char *)urlStream.str().c_str(), &status, &data_size); - if (status == 200 && data) - { - thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh); - if(data) - { - free(data); - } - if(thumbData) - { - return new Thumbnail(saveID, saveDate, thumbData, ui::Point(imgw, imgh)); - free(thumbData); - } - else - { - thumbData = (pixel *)malloc((128*128) * PIXELSIZE); - return new Thumbnail(saveID, saveDate, thumbData, ui::Point(128, 128)); - free(thumbData); - } - } - else - { - if(data) - { - free(data); - } - } - return new Thumbnail(saveID, saveDate, (pixel *)malloc((128*128) * PIXELSIZE), ui::Point(128, 128)); -} - RequestBroker::Request * Client::GetCommentsAsync(int saveID, int start, int count) { class CommentsParser: public APIResultParser @@ -1950,53 +1769,6 @@ RequestBroker::Request * Client::GetCommentsAsync(int saveID, int start, int cou return new APIRequest(urlStream.str(), new CommentsParser()); } -std::vector * Client::GetComments(int saveID, int start, int count) -{ - lastError = ""; - std::vector * commentArray = new std::vector(); - - std::stringstream urlStream; - char * data; - int dataStatus, dataLength; - urlStream << "http://" << SERVER << "/Browse/Comments.json?ID=" << saveID << "&Start=" << start << "&Count=" << count; - data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength); - if(dataStatus == 200 && data) - { - try - { - std::istringstream dataStream(data); - json::Array commentsArray; - json::Reader::Read(commentsArray, dataStream); - - for (size_t j = 0; j < commentsArray.Size(); j++) - { - json::Number tempUserID = commentsArray[j]["UserID"]; - json::String tempUsername = commentsArray[j]["Username"]; - json::String tempFormattedUsername = commentsArray[j]["FormattedUsername"]; - json::String tempComment = commentsArray[j]["Text"]; - commentArray->push_back( - new SaveComment( - tempUserID.Value(), - tempUsername.Value(), - tempFormattedUsername.Value(), - tempComment.Value() - ) - ); - } - } - catch (json::Exception &e) - { - lastError = "Could not read response"; - } - } - else - { - lastError = http_ret_text(dataStatus); - } - free(data); - return commentArray; -} - std::vector > * Client::GetTags(int start, int count, std::string query, int & resultCount) { lastError = ""; @@ -2080,7 +1852,8 @@ std::vector * Client::SearchSaves(int start, int count, std::string q { data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength); } - if(dataStatus == 200 && data) + ParseServerReturn(data, dataStatus, true); + if (dataStatus == 200 && data) { try { @@ -2119,10 +1892,6 @@ std::vector * Client::SearchSaves(int start, int count, std::string q lastError = "Could not read response"; } } - else - { - lastError = http_ret_text(dataStatus); - } free(data); return saveArray; } @@ -2141,120 +1910,6 @@ void Client::ClearThumbnailRequests() } } -Thumbnail * Client::GetThumbnail(int saveID, int saveDate) -{ - std::stringstream urlStream; - std::stringstream idStream; - int i = 0, currentTime = time(NULL); - //Check active requests for any "forgotten" requests - for(i = 0; i < IMGCONNS; i++) - { - //If the request is active, and we've received a response - if(activeThumbRequests[i] && http_async_req_status(activeThumbRequests[i])) - { - //If we haven't already, mark the request as completed - if(!activeThumbRequestCompleteTimes[i]) - { - activeThumbRequestCompleteTimes[i] = time(NULL); - } - else if(activeThumbRequestCompleteTimes[i] < (currentTime-2)) //Otherwise, if it completed more than 2 seconds ago, destroy it. - { - http_async_req_close(activeThumbRequests[i]); - activeThumbRequests[i] = NULL; - activeThumbRequestTimes[i] = 0; - activeThumbRequestCompleteTimes[i] = 0; - } - } - } - for(i = 0; i < THUMB_CACHE_SIZE; i++) - { - if(thumbnailCache[i] && thumbnailCache[i]->ID == saveID && thumbnailCache[i]->Datestamp == saveDate) - return thumbnailCache[i]; - } - urlStream << "http://" << STATICSERVER << "/" << saveID; - if(saveDate) - { - urlStream << "_" << saveDate; - } - urlStream << "_small.pti"; - idStream << saveID << ":" << saveDate; - std::string idString = idStream.str(); - bool found = false; - for(i = 0; i < IMGCONNS; i++) - { - if(activeThumbRequests[i] && activeThumbRequestIDs[i] == idString) - { - found = true; - if(http_async_req_status(activeThumbRequests[i])) - { - pixel * thumbData; - char * data; - int status, data_size, imgw, imgh; - data = http_async_req_stop(activeThumbRequests[i], &status, &data_size); - free(activeThumbRequests[i]); - activeThumbRequests[i] = NULL; - if (status == 200 && data) - { - thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh); - if(data) - { - free(data); - } - thumbnailCacheNextID %= THUMB_CACHE_SIZE; - if(thumbnailCache[thumbnailCacheNextID]) - { - delete thumbnailCache[thumbnailCacheNextID]; - } - if(thumbData) - { - thumbnailCache[thumbnailCacheNextID] = new Thumbnail(saveID, saveDate, thumbData, ui::Point(imgw, imgh)); - free(thumbData); - } - else - { - thumbData = (pixel *)malloc((128*128) * PIXELSIZE); - thumbnailCache[thumbnailCacheNextID] = new Thumbnail(saveID, saveDate, thumbData, ui::Point(128, 128)); - free(thumbData); - } - return thumbnailCache[thumbnailCacheNextID++]; - } - else - { - if(data) - { - free(data); - } - thumbnailCacheNextID %= THUMB_CACHE_SIZE; - if(thumbnailCache[thumbnailCacheNextID]) - { - delete thumbnailCache[thumbnailCacheNextID]; - } - thumbData = (pixel *)malloc((128*128) * PIXELSIZE); - thumbnailCache[thumbnailCacheNextID] = new Thumbnail(saveID, saveDate, thumbData, ui::Point(128, 128)); - free(thumbData); - return thumbnailCache[thumbnailCacheNextID++]; - } - } - } - } - if(!found) - { - for(i = 0; i < IMGCONNS; i++) - { - if(!activeThumbRequests[i]) - { - activeThumbRequests[i] = http_async_req_start(NULL, (char *)urlStream.str().c_str(), NULL, 0, 0); - activeThumbRequestTimes[i] = currentTime; - activeThumbRequestCompleteTimes[i] = 0; - activeThumbRequestIDs[i] = idString; - return NULL; - } - } - } - //http_async_req_start(http, urlStream.str().c_str(), NULL, 0, 1); - return NULL; -} - std::list * Client::RemoveTag(int saveID, std::string tag) { lastError = ""; @@ -2262,7 +1917,7 @@ std::list * Client::RemoveTag(int saveID, std::string tag) std::stringstream urlStream; char * data = NULL; int dataStatus, dataLength; - urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=delete&ID=" << saveID << "&Tag=" << tag << "&Key=" << authUser.SessionKey;; + urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=delete&ID=" << saveID << "&Tag=" << tag << "&Key=" << authUser.SessionKey; if(authUser.ID) { std::stringstream userIDStream; @@ -2274,7 +1929,8 @@ std::list * Client::RemoveTag(int saveID, std::string tag) lastError = "Not authenticated"; return NULL; } - if(dataStatus == 200 && data) + RequestStatus ret = ParseServerReturn(data, dataStatus, true); + if (ret == RequestOkay) { try { @@ -2282,24 +1938,13 @@ std::list * Client::RemoveTag(int saveID, std::string tag) json::Object responseObject; json::Reader::Read(responseObject, dataStream); - json::Number status = responseObject["Status"]; + json::Array tagsArray = responseObject["Tags"]; + tags = new std::list(); - if(status.Value()==0) - { - json::String error = responseObject["Error"]; - lastError = error.Value(); - } - else + for (size_t j = 0; j < tagsArray.Size(); j++) { - json::Array tagsArray = responseObject["Tags"]; - - tags = new std::list(); - - for (size_t j = 0; j < tagsArray.Size(); j++) - { - json::String tempTag = tagsArray[j]; - tags->push_back(tempTag.Value()); - } + json::String tempTag = tagsArray[j]; + tags->push_back(tempTag.Value()); } } catch (json::Exception &e) @@ -2307,10 +1952,6 @@ std::list * Client::RemoveTag(int saveID, std::string tag) lastError = "Could not read response"; } } - else - { - lastError = http_ret_text(dataStatus); - } free(data); return tags; } @@ -2334,7 +1975,8 @@ std::list * Client::AddTag(int saveID, std::string tag) lastError = "Not authenticated"; return NULL; } - if(dataStatus == 200 && data) + RequestStatus ret = ParseServerReturn(data, dataStatus, true); + if (ret == RequestOkay) { try { @@ -2342,24 +1984,13 @@ std::list * Client::AddTag(int saveID, std::string tag) json::Object responseObject; json::Reader::Read(responseObject, dataStream); - json::Number status = responseObject["Status"]; + json::Array tagsArray = responseObject["Tags"]; + tags = new std::list(); - if(status.Value()==0) - { - json::String error = responseObject["Error"]; - lastError = error.Value(); - } - else + for (size_t j = 0; j < tagsArray.Size(); j++) { - json::Array tagsArray = responseObject["Tags"]; - - tags = new std::list(); - - for (size_t j = 0; j < tagsArray.Size(); j++) - { - json::String tempTag = tagsArray[j]; - tags->push_back(tempTag.Value()); - } + json::String tempTag = tagsArray[j]; + tags->push_back(tempTag.Value()); } } catch (json::Exception &e) @@ -2367,10 +1998,6 @@ std::list * Client::AddTag(int saveID, std::string tag) lastError = "Could not read response"; } } - else - { - lastError = http_ret_text(dataStatus); - } free(data); return tags; } diff --git a/src/client/Client.h b/src/client/Client.h index ee25017242..3d14a9a9c8 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -146,11 +146,7 @@ class Client: public Singleton { std::vector * SearchSaves(int start, int count, std::string query, std::string sort, std::string category, int & resultCount); std::vector > * GetTags(int start, int count, std::string query, int & resultCount); - std::vector * GetComments(int saveID, int start, int count); RequestBroker::Request * GetCommentsAsync(int saveID, int start, int count); - - Thumbnail * GetPreview(int saveID, int saveDate); - Thumbnail * GetThumbnail(int saveID, int saveDate); SaveInfo * GetSave(int saveID, int saveDate); RequestBroker::Request * GetSaveAsync(int saveID, int saveDate); @@ -167,6 +163,7 @@ class Client: public Singleton { std::string GetLastError() { return lastError; } + RequestStatus ParseServerReturn(char *result, int status, bool json); void Tick(); void Shutdown(); diff --git a/src/client/requestbroker/APIRequest.cpp b/src/client/requestbroker/APIRequest.cpp index fae02528bb..a1a028a4e8 100644 --- a/src/client/requestbroker/APIRequest.cpp +++ b/src/client/requestbroker/APIRequest.cpp @@ -38,6 +38,7 @@ RequestBroker::ProcessResponse APIRequest::Process(RequestBroker & rb) int status, data_size; data = http_async_req_stop(HTTPContext, &status, &data_size); + Client::Ref().ParseServerReturn(data, status, true); if (status == 200 && data) { void * resultObject = Parser->ProcessResponse((unsigned char *)data, data_size); @@ -51,25 +52,28 @@ RequestBroker::ProcessResponse APIRequest::Process(RequestBroker & rb) } else { +#ifdef DEBUG std::cout << typeid(*this).name() << " Request for " << URL << " could not be parsed: " << data << std::endl; +#endif free(data); return RequestBroker::Failed; } } else { -//#ifdef DEBUG +#ifdef DEBUG std::cout << typeid(*this).name() << " Request for " << URL << " failed with status " << status << std::endl; -//#endif +#endif free(data); - return RequestBroker::Failed; } } } else { +#ifdef DEBUG std::cout << typeid(*this).name() << " New Request for " << URL << std::endl; +#endif if(Post) { char ** postNames = new char*[PostData.size() + 1]; diff --git a/src/client/requestbroker/RequestBroker.cpp b/src/client/requestbroker/RequestBroker.cpp index 809dda3ddd..9a4587393e 100644 --- a/src/client/requestbroker/RequestBroker.cpp +++ b/src/client/requestbroker/RequestBroker.cpp @@ -210,6 +210,8 @@ void RequestBroker::thumbnailQueueProcessTH() resultStatus = r->Process(*this); if(resultStatus == Duplicate || resultStatus == Failed || resultStatus == Finished) { + if (resultStatus == Duplicate || resultStatus == Failed) + r->Listener.second->OnResponseFailed(r->Identifier); req = activeRequests.erase(req); } else diff --git a/src/client/requestbroker/RequestListener.h b/src/client/requestbroker/RequestListener.h index 2ded44e8d7..5a52ccc83e 100644 --- a/src/client/requestbroker/RequestListener.h +++ b/src/client/requestbroker/RequestListener.h @@ -9,4 +9,5 @@ class RequestListener virtual ~RequestListener() {} virtual void OnResponseReady(void * response, int identifier) {} + virtual void OnResponseFailed(int identifier) {} }; diff --git a/src/client/requestbroker/WebRequest.cpp b/src/client/requestbroker/WebRequest.cpp index 2198062aed..f24f0e9730 100644 --- a/src/client/requestbroker/WebRequest.cpp +++ b/src/client/requestbroker/WebRequest.cpp @@ -37,6 +37,7 @@ RequestBroker::ProcessResponse WebRequest::Process(RequestBroker & rb) int status, data_size; data = http_async_req_stop(HTTPContext, &status, &data_size); + Client::Ref().ParseServerReturn(data, status, true); if (status == 200 && data) { void * resultObject = new std::vector(data, data+data_size); @@ -50,16 +51,18 @@ RequestBroker::ProcessResponse WebRequest::Process(RequestBroker & rb) } else { +#ifdef DEBUG std::cout << typeid(*this).name() << " Request for " << URL << " could not be parsed: " << data << std::endl; +#endif free(data); return RequestBroker::Failed; } } else { -//#ifdef DEBUG +#ifdef DEBUG std::cout << typeid(*this).name() << " Request for " << URL << " failed with status " << status << std::endl; -//#endif +#endif free(data); return RequestBroker::Failed; @@ -68,7 +71,9 @@ RequestBroker::ProcessResponse WebRequest::Process(RequestBroker & rb) } else { +#ifdef DEBUG std::cout << typeid(*this).name() << " New Request for " << URL << std::endl; +#endif if(Post) { char ** postNames = new char*[PostData.size() + 1]; @@ -95,7 +100,9 @@ RequestBroker::ProcessResponse WebRequest::Process(RequestBroker & rb) if(Client::Ref().GetAuthUser().ID) { +#ifdef DEBUG std::cout << typeid(*this).name() << " Authenticated " << std::endl; +#endif User user = Client::Ref().GetAuthUser(); char userName[12]; char *userSession = new char[user.SessionID.length() + 1]; diff --git a/src/gui/login/LoginModel.cpp b/src/gui/login/LoginModel.cpp index 2580fab612..6cc0c9c10a 100644 --- a/src/gui/login/LoginModel.cpp +++ b/src/gui/login/LoginModel.cpp @@ -19,7 +19,7 @@ void LoginModel::Login(string username, string password) loginStatus = true; break; case LoginError: - statusText = "Error: " + Client::Ref().GetLastError(); + statusText = Client::Ref().GetLastError(); size_t banStart = statusText.find(". Ban expire in"); //TODO: temporary, remove this when the ban message is fixed if (banStart != statusText.npos) statusText.replace(banStart, 15, ". Login at http://powdertoy.co.uk in order to see the full ban reason. Ban expires in"); diff --git a/src/gui/preview/PreviewController.cpp b/src/gui/preview/PreviewController.cpp index 74b766276e..ed80724503 100644 --- a/src/gui/preview/PreviewController.cpp +++ b/src/gui/preview/PreviewController.cpp @@ -125,7 +125,7 @@ void PreviewController::Report(std::string message) new ErrorMessage("Information", "Report submitted"); //TODO: InfoMessage } else - new ErrorMessage("Error", "Unable file report"); + new ErrorMessage("Error", "Unable file report: " + Client::Ref().GetLastError()); } void PreviewController::FavouriteSave() diff --git a/src/gui/preview/PreviewModel.cpp b/src/gui/preview/PreviewModel.cpp index e4c76c775d..39a3324403 100644 --- a/src/gui/preview/PreviewModel.cpp +++ b/src/gui/preview/PreviewModel.cpp @@ -25,9 +25,9 @@ void PreviewModel::SetFavourite(bool favourite) if (Client::Ref().FavouriteSave(save->id, favourite) == RequestOkay) save->Favourite = favourite; else if (favourite) - throw PreviewModelException("Error, could not fav. the save, are you logged in?"); + throw PreviewModelException("Error, could not fav. the save: " + Client::Ref().GetLastError()); else - throw PreviewModelException("Error, could not unfav. the save, are you logged in?"); + throw PreviewModelException("Error, could not unfav. the save: " + Client::Ref().GetLastError()); notifySaveChanged(); } } @@ -158,7 +158,6 @@ void PreviewModel::OnResponseReady(void * object, int identifier) saveComments = NULL; } saveComments = (std::vector*)object; - std::cout << object << std::endl; commentsLoaded = true; notifySaveCommentsChanged(); } @@ -185,6 +184,30 @@ void PreviewModel::OnResponseReady(void * object, int identifier) } } +void PreviewModel::OnResponseFailed(int identifier) +{ + if (identifier == 3) + { + if (saveComments) + { + for (size_t i = 0; i < saveComments->size(); i++) + delete saveComments->at(i); + saveComments->clear(); + delete saveComments; + saveComments = NULL; + } + saveComments = NULL; + commentsLoaded = true; + notifySaveCommentsChanged(); + } + else + { + for (size_t i = 0; i < observers.size(); i++) + { + observers[i]->SaveLoadingError(Client::Ref().GetLastError()); + } + } +} void PreviewModel::Update() { diff --git a/src/gui/preview/PreviewModel.h b/src/gui/preview/PreviewModel.h index 8e8eabf3ab..6be0168802 100644 --- a/src/gui/preview/PreviewModel.h +++ b/src/gui/preview/PreviewModel.h @@ -55,6 +55,7 @@ class PreviewModel: RequestListener { void SetDoOpen(bool doOpen); void Update(); virtual void OnResponseReady(void * object, int identifier); + virtual void OnResponseFailed(int identifier); virtual ~PreviewModel(); }; diff --git a/src/gui/preview/PreviewView.cpp b/src/gui/preview/PreviewView.cpp index 263c674d85..f3cb01b8e5 100644 --- a/src/gui/preview/PreviewView.cpp +++ b/src/gui/preview/PreviewView.cpp @@ -15,6 +15,7 @@ #include "gui/interface/ScrollPanel.h" #include "gui/interface/AvatarButton.h" #include "gui/interface/Keys.h" +#include "gui/dialogues/ErrorMessage.h" class PreviewView::LoginAction: public ui::ButtonAction { @@ -68,6 +69,8 @@ PreviewView::PreviewView(): submitCommentButton(NULL), addCommentBox(NULL), doOpen(false), + doError(false), + doErrorMessage(""), showAvatars(true), prevPage(false), commentBoxHeight(20) @@ -365,6 +368,11 @@ void PreviewView::OnTick(float dt) } c->Update(); + if (doError) + { + ErrorMessage::Blocking("Error loading save", doErrorMessage); + c->Exit(); + } } void PreviewView::OnTryExit(ExitMethod method) @@ -528,6 +536,12 @@ void PreviewView::NotifyCommentBoxEnabledChanged(PreviewModel * sender) } } +void PreviewView::SaveLoadingError(std::string errorMessage) +{ + doError = true; + doErrorMessage = errorMessage; +} + void PreviewView::NotifyCommentsPageChanged(PreviewModel * sender) { std::stringstream pageInfoStream; @@ -609,6 +623,8 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender) commentsPanel->SetScrollPosition(currentY); } } + //else if (sender->GetCommentsLoaded()) + // ErrorMessage::Blocking("Error loading comments", Client::Ref().GetLastError()); } PreviewView::~PreviewView() diff --git a/src/gui/preview/PreviewView.h b/src/gui/preview/PreviewView.h index 9975afc40d..89c21d5cbe 100644 --- a/src/gui/preview/PreviewView.h +++ b/src/gui/preview/PreviewView.h @@ -48,6 +48,8 @@ class PreviewView: public ui::Window { int votesUp; int votesDown; bool doOpen; + bool doError; + std::string doErrorMessage; bool showAvatars; bool prevPage; @@ -67,6 +69,7 @@ class PreviewView: public ui::Window { void NotifyCommentsChanged(PreviewModel * sender); void NotifyCommentsPageChanged(PreviewModel * sender); void NotifyCommentBoxEnabledChanged(PreviewModel * sender); + void SaveLoadingError(std::string errorMessage); virtual void OnDraw(); virtual void DoDraw(); virtual void OnTick(float dt); diff --git a/src/gui/profile/ProfileActivity.cpp b/src/gui/profile/ProfileActivity.cpp index ff822542b0..07c849d2ae 100644 --- a/src/gui/profile/ProfileActivity.cpp +++ b/src/gui/profile/ProfileActivity.cpp @@ -6,6 +6,7 @@ #include "gui/interface/AvatarButton.h" #include "gui/interface/ScrollPanel.h" #include "gui/interface/Keys.h" +#include "gui/dialogues/ErrorMessage.h" #include "gui/Style.h" #include "client/Client.h" #include "client/UserInfo.h" @@ -15,7 +16,9 @@ ProfileActivity::ProfileActivity(std::string username) : WindowActivity(ui::Point(-1, -1), ui::Point(236, 300)), loading(false), - saving(false) + saving(false), + doError(false), + doErrorMessage("") { editable = Client::Ref().GetAuthUser().ID && Client::Ref().GetAuthUser().Username == username; @@ -241,6 +244,24 @@ void ProfileActivity::OnResponseReady(void * userDataPtr, int identifier) } } +void ProfileActivity::OnResponseFailed(int identifier) +{ + doError = true; + if (loading) + doErrorMessage = "Could not load user info: " + Client::Ref().GetLastError(); + else if (saving) + doErrorMessage = "Could not save user info: " + Client::Ref().GetLastError(); +} + +void ProfileActivity::OnTick(float dt) +{ + if (doError) + { + ErrorMessage::Blocking("Error", doErrorMessage); + Exit(); + } +} + void ProfileActivity::OnDraw() { Graphics * g = ui::Engine::Ref().g; diff --git a/src/gui/profile/ProfileActivity.h b/src/gui/profile/ProfileActivity.h index 972c276e41..48e7663245 100644 --- a/src/gui/profile/ProfileActivity.h +++ b/src/gui/profile/ProfileActivity.h @@ -20,11 +20,15 @@ class ProfileActivity: public WindowActivity, public RequestListener { bool editable; bool loading; bool saving; + bool doError; + std::string doErrorMessage; void setUserInfo(UserInfo newInfo); public: ProfileActivity(std::string username); virtual ~ProfileActivity(); virtual void OnResponseReady(void * userDataPtr, int identifier); + virtual void OnResponseFailed(int identifier); + virtual void OnTick(float dt); virtual void OnDraw(); virtual void OnTryExit(ExitMethod method); diff --git a/src/gui/search/SearchController.cpp b/src/gui/search/SearchController.cpp index 05b05a8b72..429955c38e 100644 --- a/src/gui/search/SearchController.cpp +++ b/src/gui/search/SearchController.cpp @@ -268,8 +268,8 @@ void SearchController::removeSelectedC() if (Client::Ref().DeleteSave(saves[i])!=RequestOkay) { std::stringstream saveIDF; - saveIDF << "\boFailed to delete [" << saves[i] << "] ..."; - notifyStatus(saveIDF.str()); + saveIDF << "\boFailed to delete [" << saves[i] << "]: " << Client::Ref().GetLastError(); + notifyError(saveIDF.str()); c->Refresh(); return false; } @@ -350,7 +350,10 @@ void SearchController::unpublishSelectedC(bool publish) if (!ret) { std::stringstream error; - error << "\boFailed to " << (publish ? "Publish" : "Unpublish") << " [" << saves[i] << "], is this save yours?"; + if (publish) // uses html page so error message will be spam + error << "\boFailed to publish [" << saves[i] << "], is this save yours?"; + else + error << "\boFailed to unpublish [" << saves[i] << "]: " + Client::Ref().GetLastError(); notifyError(error.str()); c->Refresh(); return false; @@ -383,7 +386,7 @@ void SearchController::FavouriteSelected() if (Client::Ref().FavouriteSave(saves[i], true)!=RequestOkay) { std::stringstream saveIDF; - saveIDF << "\boFailed to favourite [" << saves[i] << "], are you logged in?"; + saveIDF << "\boFailed to favourite [" << saves[i] << "]: " + Client::Ref().GetLastError(); notifyError(saveIDF.str()); return false; } @@ -408,7 +411,7 @@ void SearchController::FavouriteSelected() if (Client::Ref().FavouriteSave(saves[i], false)!=RequestOkay) { std::stringstream saveIDF; - saveIDF << "\boFailed to unfavourite [" << saves[i] << "], are you logged in?"; + saveIDF << "\boFailed to unfavourite [" << saves[i] << "]: " + Client::Ref().GetLastError(); notifyError(saveIDF.str()); return false; }