diff --git a/src/client/Client.cpp b/src/client/Client.cpp index dcd490340..ef457b92f 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -1181,6 +1182,40 @@ std::vector Client::GetSaveData(int saveID, int saveDate) return saveData; } +RequestBroker::Request * Client::SaveUserInfoAsync(UserInfo info) +{ + class StatusParser: public APIResultParser + { + virtual void * ProcessResponse(unsigned char * data, int dataLength) + { + try + { + std::istringstream dataStream((char*)data); + json::Object objDocument; + json::Reader::Read(objDocument, dataStream); + json::Number tempStatus = objDocument["Status"]; + + bool returnValue = tempStatus.Value() == 1; + + return (void*)(returnValue ? 1 : 0); + } + catch (json::Exception &e) + { + return 0; + } + } + virtual void Cleanup(void * objectPtr) + { + //delete (UserInfo*)objectPtr; + } + virtual ~StatusParser() { } + }; + 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()); +} + RequestBroker::Request * Client::GetUserInfoAsync(std::string username) { class UserInfoParser: public APIResultParser @@ -1197,13 +1232,15 @@ RequestBroker::Request * Client::GetUserInfoAsync(std::string username) json::Number userIDTemp = tempUser["ID"]; json::String usernameTemp = tempUser["Username"]; json::String bioTemp = tempUser["Biography"]; - //json::Number ageTemp = tempUser["Age"]; + json::String locationTemp = tempUser["Location"]; + json::Number ageTemp = tempUser["Age"]; return new UserInfo( userIDTemp.Value(), - 0,//ageTemp.Value(), + ageTemp.Value(), usernameTemp.Value(), - bioTemp.Value()); + bioTemp.Value(), + locationTemp.Value()); } catch (json::Exception &e) { diff --git a/src/client/Client.h b/src/client/Client.h index f00083d8a..085c60a91 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -9,6 +9,7 @@ #include "Singleton.h" #include "User.h" +#include "UserInfo.h" #include "cajun/elements.h" @@ -93,6 +94,9 @@ public: std::vector DirectorySearch(std::string directory, std::string search, std::vector extensions); std::vector DirectorySearch(std::string directory, std::string search, std::string extension); + std::string FileOpenDialogue(); + //std::string FileSaveDialogue(); + bool DoInstallation(); std::vector ReadFile(std::string filename); @@ -130,6 +134,7 @@ public: //Retrieves a "UserInfo" object RequestBroker::Request * GetUserInfoAsync(std::string username); + RequestBroker::Request * SaveUserInfoAsync(UserInfo info); unsigned char * GetSaveData(int saveID, int saveDate, int & dataLength); std::vector GetSaveData(int saveID, int saveDate); diff --git a/src/client/HTTP.cpp b/src/client/HTTP.cpp index 5fc4d0862..c3110f528 100644 --- a/src/client/HTTP.cpp +++ b/src/client/HTTP.cpp @@ -1102,3 +1102,194 @@ fail: *len = 0; return NULL; } + + +void *http_multipart_post_async(char *uri, char **names, char **parts, int *plens, char *user, char *pass, char *session_id) +{ + void *ctx; + char *data = NULL, *tmp, *p; + int dlen = 0, i, j; + unsigned char hash[16]; + unsigned char boundary[32], ch; + int blen = 0; + unsigned int map[62], m; + struct md5_context md5; + //struct md5_context md52; + int own_plen = 0; + + if (names) + { + if (!plens) + { + own_plen = 1; + for (i=0; names[i]; i++) ; + plens = (int *)calloc(i, sizeof(int)); + for (i=0; names[i]; i++) + plens[i] = strlen(parts[i]); + } + +retry: + if (blen >= 31) + goto fail; + memset(map, 0, 62*sizeof(int)); + for (i=0; names[i]; i++) + { + for (j=0; j='0' && ch<='9') + map[ch-'0']++; + else if (ch>='A' && ch<='Z') + map[ch-'A'+10]++; + else if (ch>='a' && ch<='z') + map[ch-'a'+36]++; + } + } + m = ~0; + j = 61; + for (i=0; i<62; i++) + if (map[i]>4]; + tmp[i*2+1] = hexChars[hash[i]&15]; + } + tmp[32] = 0; + http_async_add_header(ctx, "X-Auth-Hash", tmp); + free(tmp); + } + if (session_id) + { + http_async_add_header(ctx, "X-Auth-User-Id", user); + http_async_add_header(ctx, "X-Auth-Session-Key", session_id); + } + else + { + http_async_add_header(ctx, "X-Auth-User", user); + } + } + + if (data) + { + tmp = (char *)malloc(32+strlen((char *)boundary)); + sprintf(tmp, "multipart/form-data, boundary=%s", boundary); + http_async_add_header(ctx, "Content-type", tmp); + free(tmp); + free(data); + } + + if (own_plen) + free(plens); + + return ctx; + +fail: + if (data) + free(data); + if (own_plen) + free(plens); + //if (ret) + // *ret = 600; + //if (len) + // *len = 0; + return NULL; +} diff --git a/src/client/HTTP.h b/src/client/HTTP.h index 51b943809..c83284a41 100644 --- a/src/client/HTTP.h +++ b/src/client/HTTP.h @@ -39,6 +39,7 @@ char *http_async_req_stop(void *ctx, int *ret, int *len); void http_async_req_close(void *ctx); char *http_multipart_post(char *uri, char **names, char **parts, int *plens, char *user, char *pass, char * session_id, int *ret, int *len); +void *http_multipart_post_async(char *uri, char **names, char **parts, int *plens, char *user, char *pass, char * session_id); char *http_ret_text(int ret); diff --git a/src/client/UserInfo.h b/src/client/UserInfo.h index edd6659cf..2df00159f 100644 --- a/src/client/UserInfo.h +++ b/src/client/UserInfo.h @@ -10,12 +10,15 @@ public: int Age; std::string Username; std::string Biography; - UserInfo(int id, int age, std::string username, std::string biography): + std::string Location; + UserInfo(int id, int age, std::string username, std::string biography, std::string location): ID(id), Age(age), Username(username), - Biography(biography) + Biography(biography), + Location(location) { } + UserInfo() {} }; diff --git a/src/client/requestbroker/APIRequest.cpp b/src/client/requestbroker/APIRequest.cpp index 71d683f6d..811096d81 100644 --- a/src/client/requestbroker/APIRequest.cpp +++ b/src/client/requestbroker/APIRequest.cpp @@ -1,6 +1,10 @@ #include #include #include +#include +#include "Config.h" +#include "Format.h" +#include "client/Client.h" #include "APIRequest.h" #include "client/HTTP.h" #include "APIResultParser.h" @@ -8,6 +12,17 @@ APIRequest::APIRequest(std::string url, APIResultParser * parser, ListenerHandle listener): RequestBroker::Request(API, listener) { + Post = false; + HTTPContext = NULL; + Parser = parser; + URL = url; +} + +APIRequest::APIRequest(std::string url, std::map postData, APIResultParser * parser, ListenerHandle listener): + RequestBroker::Request(API, listener) +{ + Post = true; + PostData = postData; HTTPContext = NULL; Parser = parser; URL = url; @@ -26,17 +41,18 @@ RequestBroker::ProcessResponse APIRequest::Process(RequestBroker & rb) if (status == 200 && data) { void * resultObject = Parser->ProcessResponse((unsigned char *)data, data_size); - free(data); if(resultObject) { this->ResultObject = resultObject; rb.requestComplete(this); + free(data); return RequestBroker::Finished; } else { - std::cout << typeid(*this).name() << " Request for " << URL << " could not be parsed" << status << std::endl; + std::cout << typeid(*this).name() << " Request for " << URL << " could not be parsed: " << data << std::endl; + free(data); return RequestBroker::Failed; } } @@ -55,7 +71,50 @@ RequestBroker::ProcessResponse APIRequest::Process(RequestBroker & rb) else { std::cout << typeid(*this).name() << " New Request for " << URL << std::endl; - HTTPContext = http_async_req_start(NULL, (char *)URL.c_str(), NULL, 0, 0); + if(Post) + { + char ** postNames = new char*[PostData.size() + 1]; + char ** postData = new char*[PostData.size()]; + int * postLength = new int[PostData.size()]; + + int i = 0; + std::map::iterator iter = PostData.begin(); + while(iter != PostData.end()) + { + std::string name = iter->first; + std::string data = iter->second; + char * cName = new char[name.length() + 1]; + char * cData = new char[data.length() + 1]; + std::strcpy(cName, name.c_str()); + std::strcpy(cData, data.c_str()); + postNames[i] = cName; + postData[i] = cData; + postLength[i] = data.length(); + i++; + iter++; + } + postNames[i] = NULL; + + if(Client::Ref().GetAuthUser().ID) + { + std::cout << typeid(*this).name() << " Authenticated " << std::endl; + User user = Client::Ref().GetAuthUser(); + char userName[12]; + char userSession[user.SessionID.length() + 1]; + std::strcpy(userName, format::NumberToString(user.ID).c_str()); + std::strcpy(userSession, user.SessionID.c_str()); + HTTPContext = http_multipart_post_async((char*)URL.c_str(), postNames, postData, postLength, userName, NULL, userSession); + } + else + { + HTTPContext = http_multipart_post_async((char*)URL.c_str(), postNames, postData, postLength, NULL, NULL, NULL); + } + + } + else + { + HTTPContext = http_async_req_start(NULL, (char *)URL.c_str(), NULL, 0, 0); + } //RequestTime = time(NULL); } return RequestBroker::OK; diff --git a/src/client/requestbroker/APIRequest.h b/src/client/requestbroker/APIRequest.h index 2d41eb6e0..a20fc99f7 100644 --- a/src/client/requestbroker/APIRequest.h +++ b/src/client/requestbroker/APIRequest.h @@ -1,13 +1,17 @@ +#include #include "RequestBroker.h" class APIResultParser; class APIRequest: public RequestBroker::Request { public: + bool Post; APIResultParser * Parser; std::string URL; + std::map PostData; void * HTTPContext; APIRequest(std::string url, APIResultParser * parser, ListenerHandle listener = ListenerHandle(0, 0)); + APIRequest(std::string url, std::map, APIResultParser * parser, ListenerHandle listener = ListenerHandle(0, 0)); virtual RequestBroker::ProcessResponse Process(RequestBroker & rb); virtual ~APIRequest(); virtual void Cleanup(); diff --git a/src/profile/ProfileActivity.cpp b/src/profile/ProfileActivity.cpp index 1b3a1f87e..ed10df9c5 100644 --- a/src/profile/ProfileActivity.cpp +++ b/src/profile/ProfileActivity.cpp @@ -3,15 +3,20 @@ #include "interface/Button.h" #include "interface/Textbox.h" #include "interface/Label.h" +#include "interface/AvatarButton.h" +#include "interface/ScrollPanel.h" #include "interface/Keys.h" #include "Style.h" #include "client/Client.h" +#include "client/UserInfo.h" #include "client/requestbroker/RequestListener.h" ProfileActivity::ProfileActivity(std::string username) : - WindowActivity(ui::Point(-1, -1), ui::Point(236, 302)) + WindowActivity(ui::Point(-1, -1), ui::Point(236, 200)), + loading(false), + saving(false) { - bool editable = Client::Ref().GetAuthUser().ID && Client::Ref().GetAuthUser().Username == username; + editable = Client::Ref().GetAuthUser().ID && Client::Ref().GetAuthUser().Username == username; class CloseAction: public ui::ButtonAction @@ -32,6 +37,15 @@ ProfileActivity::ProfileActivity(std::string username) : SaveAction(ProfileActivity * a) : a(a) { } void ActionCallback(ui::Button * sender_) { + if(!a->loading && !a->saving && a->editable) + { + sender_->Enabled = false; + sender_->SetText("Saving..."); + a->saving = true; + a->info.Location = ((ui::Textbox*)a->location)->GetText(); + a->info.Biography = ((ui::Textbox*)a->bio)->GetText(); + RequestBroker::Ref().Start(Client::Ref().SaveUserInfoAsync(a->info), a); + } } }; @@ -47,12 +61,104 @@ ProfileActivity::ProfileActivity(std::string username) : AddComponent(closeButton); + loading = true; RequestBroker::Ref().Start(Client::Ref().GetUserInfoAsync(username), this); } +void ProfileActivity::setUserInfo(UserInfo newInfo) +{ + info = newInfo; + + if(!info.Biography.length() && !editable) + info.Biography = "\bg(no bio)"; + + if(!info.Location.length() && !editable) + info.Location = "\bg(no location)"; + + ui::AvatarButton * avatar = new ui::AvatarButton(ui::Point((Size.X-40)-8, 8), ui::Point(40, 40), info.Username); + AddComponent(avatar); + + int currentY = 5; + ui::Label * title = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8), 15), info.Username); + title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(title); + currentY += 20; + + ui::Label * locationTitle = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8), 15), "Location"); + locationTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(locationTitle); + currentY += 17; + + if(editable) + { + ui::Textbox * location = new ui::Textbox(ui::Point(8, currentY), ui::Point(Size.X-16-(40+8), 17), info.Location); + location->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(location); + this->location = location; + currentY += 10+location->Size.Y; + } + else + { + ui::Label * location = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8), 12), info.Location); + location->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + location->SetTextColour(ui::Colour(180, 180, 180)); + AddComponent(location); + this->location = location; + currentY += 10+location->Size.Y; + } + + ui::Label * bioTitle = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8, 15), "Biography"); + bioTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(bioTitle); + currentY += 17; + + if(editable) + { + ui::Textbox * bio = new ui::Textbox(ui::Point(8, currentY), ui::Point(Size.X-16, Size.Y-30-currentY), info.Biography); + bio->SetMultiline(true); + bio->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + bio->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(bio); + currentY += 10+bio->Size.Y; + this->bio = bio; + } + else + { + ui::Label * bio = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8, -1), info.Biography); + bio->SetMultiline(true); + bio->SetTextColour(ui::Colour(180, 180, 180)); + bio->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + currentY += 10+bio->Size.Y; + if(currentY > Size.Y - 20) + { + ui::ScrollPanel * scrollPanel = new ui::ScrollPanel(bio->Position, ui::Point(Size.X, Size.Y-30-bio->Position.Y)); + AddComponent(scrollPanel); + bio->Position = ui::Point(4, 4); + scrollPanel->AddChild(bio); + scrollPanel->InnerSize = ui::Point(Size.X, bio->Size.Y+8); + } + else + { + AddComponent(bio); + } + this->bio = bio; + } + + //exit(0); +} + void ProfileActivity::OnResponseReady(void * userDataPtr) { - exit(0); + if(loading) + { + loading = false; + setUserInfo(*(UserInfo*)userDataPtr); + delete (UserInfo*)userDataPtr; + } + else if(saving) + { + Exit(); + } } void ProfileActivity::OnDraw() diff --git a/src/profile/ProfileActivity.h b/src/profile/ProfileActivity.h index a4648e918..9691dff9e 100644 --- a/src/profile/ProfileActivity.h +++ b/src/profile/ProfileActivity.h @@ -4,9 +4,17 @@ #include #include "Activity.h" #include "client/requestbroker/RequestListener.h" +#include "client/UserInfo.h" #include "interface/Window.h" class ProfileActivity: public WindowActivity, public RequestListener { + ui::Component * location; + ui::Component * bio; + UserInfo info; + bool editable; + bool loading; + bool saving; + void setUserInfo(UserInfo newInfo); public: ProfileActivity(std::string username); virtual ~ProfileActivity();