This adds mStatus, mReason and mContent to ResponderBase and fills those in instead of passing it to member functions. The added danger here is that now code can accidently try to access these variables while they didn't already get a correct value. Affected members of ResponderBase (that now have less arguments): decode_llsd_body, decode_raw_body, completedHeaders, completed -> httpCompleted, result -> httpSuccess, errorWithContent and error -> httpFailure. New API: ResponderBase::setResult ResponderBase::getStatus() ResponderBase::getReason() ResponderBase::getContent() ResponderBase::getResponseHeaders() (returns AIHTTPReceivedHeaders though, not LLSD) ResponderBase::dumpResponse() ResponderWithCompleted::completeResult ResponderWithResult::failureResult (previously pubErrorWithContent) ResponderWithResult::successResult (previously pubResult) Not implemented: getHTTPMethod() - use getName() instead which returns the class name of the responder. completedHeaders() is still called as usual, although you can ignore it (not implement in a derived responder) and call getResponseHeaders() instead, provided you implement needsHeaders() and have it return true. However, classes derived from ResponderHeadersOnly do not have completedHeaders(), so they still must implement completedHeaders(), and then call getResponseHeaders() or just access mReceivedHeaders directly, as usual.
438 lines
13 KiB
C++
438 lines
13 KiB
C++
/**
|
|
* @file llfloaterpostcard.cpp
|
|
* @brief Postcard send floater, allows setting name, e-mail address, etc.
|
|
*
|
|
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2004-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llfloaterpostcard.h"
|
|
|
|
#include "llfontgl.h"
|
|
#include "llsys.h"
|
|
#include "llgl.h"
|
|
#include "v3dmath.h"
|
|
#include "lldir.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llui.h"
|
|
#include "lllineeditor.h"
|
|
#include "llviewertexteditor.h"
|
|
#include "llbutton.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewernetwork.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "lluploaddialog.h"
|
|
#include "llviewerstats.h"
|
|
#include "llviewerwindow.h"
|
|
#include "llstatusbar.h"
|
|
#include "llviewerregion.h"
|
|
#include "lleconomy.h"
|
|
#include "lltrans.h"
|
|
|
|
#include "llgl.h"
|
|
#include "llglheaders.h"
|
|
#include "llimagejpeg.h"
|
|
#include "llimagej2c.h"
|
|
#include "llvfile.h"
|
|
#include "llvfs.h"
|
|
|
|
#include "llassetuploadresponders.h"
|
|
|
|
#include "hippogridmanager.h"
|
|
#include "llfloatersnapshot.h"
|
|
|
|
#if LL_MSVC
|
|
#pragma warning( disable : 4265 ) // "class has virtual functions, but destructor is not virtual"
|
|
#endif
|
|
#include <boost/regex.hpp> //boost.regex lib
|
|
|
|
///----------------------------------------------------------------------------
|
|
/// Local function declarations, constants, enums, and typedefs
|
|
///----------------------------------------------------------------------------
|
|
|
|
//static
|
|
LLFloaterPostcard::instance_list_t LLFloaterPostcard::sInstances;
|
|
|
|
///----------------------------------------------------------------------------
|
|
/// Class LLFloaterPostcard
|
|
///----------------------------------------------------------------------------
|
|
|
|
LLFloaterPostcard::LLFloaterPostcard(LLImageJPEG* jpeg, LLViewerTexture *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global, int index)
|
|
: LLFloater(std::string("Postcard Floater")),
|
|
mJPEGImage(jpeg),
|
|
mViewerImage(img),
|
|
mImageScale(img_scale),
|
|
mPosTakenGlobal(pos_taken_global),
|
|
mHasFirstMsgFocus(false),
|
|
mSnapshotIndex(index)
|
|
{
|
|
init();
|
|
}
|
|
|
|
void LLFloaterPostcard::init()
|
|
{
|
|
// pick up the user's up-to-date email address
|
|
if(!gAgent.getID().isNull())
|
|
{
|
|
// we're logged in, so we can get this info.
|
|
gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
sInstances.insert(this);
|
|
}
|
|
|
|
// Destroys the object
|
|
LLFloaterPostcard::~LLFloaterPostcard()
|
|
{
|
|
sInstances.erase(this);
|
|
mJPEGImage = NULL; // deletes image
|
|
}
|
|
|
|
BOOL LLFloaterPostcard::postBuild()
|
|
{
|
|
childSetAction("cancel_btn", onClickCancel, this);
|
|
childSetAction("send_btn", onClickSend, this);
|
|
|
|
childDisable("from_form");
|
|
|
|
std::string name_string;
|
|
gAgent.buildFullname(name_string);
|
|
childSetValue("name_form", LLSD(name_string));
|
|
|
|
LLTextEditor* MsgField = getChild<LLTextEditor>("msg_form");
|
|
if (MsgField)
|
|
{
|
|
MsgField->setWordWrap(TRUE);
|
|
|
|
// For the first time a user focusess to .the msg box, all text will be selected.
|
|
MsgField->setFocusChangedCallback(boost::bind(&LLFloaterPostcard::onMsgFormFocusRecieved, this, _1, MsgField));
|
|
}
|
|
|
|
childSetFocus("to_form", TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLViewerTexture *img, const LLVector2 &image_scale, const LLVector3d& pos_taken_global, int index)
|
|
{
|
|
// Take the images from the caller
|
|
// It's now our job to clean them up
|
|
LLFloaterPostcard *instance = new LLFloaterPostcard(jpeg, img, image_scale, pos_taken_global, index);
|
|
|
|
LLUICtrlFactory::getInstance()->buildFloater(instance, "floater_postcard.xml");
|
|
|
|
S32 left, top;
|
|
gFloaterView->getNewFloaterPosition(&left, &top);
|
|
instance->setOrigin(left, top - instance->getRect().getHeight());
|
|
|
|
instance->open(); /*Flawfinder: ignore*/
|
|
|
|
return instance;
|
|
}
|
|
|
|
void LLFloaterPostcard::draw()
|
|
{
|
|
LLGLSUIDefault gls_ui;
|
|
LLFloater::draw();
|
|
|
|
if(!isMinimized() && mViewerImage.notNull() && mJPEGImage.notNull())
|
|
{
|
|
LLRect rect(getRect());
|
|
|
|
// first set the max extents of our preview
|
|
rect.translate(-rect.mLeft, -rect.mBottom);
|
|
rect.mLeft += 280;
|
|
rect.mRight -= 10;
|
|
rect.mTop -= 20;
|
|
rect.mBottom = rect.mTop - 130;
|
|
|
|
// then fix the aspect ratio
|
|
F32 ratio = (F32)mJPEGImage->getWidth() / (F32)mJPEGImage->getHeight();
|
|
if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
|
|
{
|
|
rect.mRight = (S32)((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
|
|
}
|
|
else
|
|
{
|
|
rect.mBottom = (S32)((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
|
|
}
|
|
{
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
|
|
rect.stretch(-1);
|
|
}
|
|
{
|
|
|
|
gGL.matrixMode(LLRender::MM_TEXTURE);
|
|
gGL.pushMatrix();
|
|
{
|
|
gGL.scalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
gl_draw_scaled_image(rect.mLeft,
|
|
rect.mBottom,
|
|
rect.getWidth(),
|
|
rect.getHeight(),
|
|
mViewerImage,
|
|
LLColor4::white);
|
|
}
|
|
gGL.matrixMode(LLRender::MM_TEXTURE);
|
|
gGL.popMatrix();
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLFloaterPostcard::onClickCancel(void* data)
|
|
{
|
|
if (data)
|
|
{
|
|
LLFloaterPostcard *self = (LLFloaterPostcard *)data;
|
|
self->close(false);
|
|
}
|
|
}
|
|
|
|
void LLFloaterPostcard::onClose(bool app_quitting)
|
|
{
|
|
LLFloaterSnapshot::savePostcardDone(false, mSnapshotIndex);
|
|
destroy();
|
|
}
|
|
|
|
class LLSendPostcardResponder : public LLAssetUploadResponder
|
|
{
|
|
private:
|
|
int mSnapshotIndex;
|
|
|
|
public:
|
|
LLSendPostcardResponder(const LLSD &post_data,
|
|
const LLUUID& vfile_id,
|
|
LLAssetType::EType asset_type,
|
|
int index) :
|
|
LLAssetUploadResponder(post_data, vfile_id, asset_type),
|
|
mSnapshotIndex(index)
|
|
{
|
|
}
|
|
// *TODO define custom uploadFailed here so it's not such a generic message
|
|
/*virtual*/ void uploadComplete(const LLSD& content)
|
|
{
|
|
// we don't care about what the server returns from this post, just clean up the UI
|
|
LLFloaterSnapshot::savePostcardDone(true, mSnapshotIndex);
|
|
}
|
|
/*virtual*/ void uploadFailure(const LLSD& content)
|
|
{
|
|
LLAssetUploadResponder::uploadFailure(content);
|
|
LLFloaterSnapshot::savePostcardDone(false, mSnapshotIndex);
|
|
}
|
|
/*virtual*/ void httpFailure(void)
|
|
{
|
|
LLAssetUploadResponder::httpFailure();
|
|
LLFloaterSnapshot::savePostcardDone(false, mSnapshotIndex);
|
|
}
|
|
/*virtual*/ char const* getName(void) const { return "LLSendPostcardResponder"; }
|
|
};
|
|
|
|
// static
|
|
void LLFloaterPostcard::onClickSend(void* data)
|
|
{
|
|
if (data)
|
|
{
|
|
LLFloaterPostcard *self = (LLFloaterPostcard *)data;
|
|
|
|
std::string from(self->childGetValue("from_form").asString());
|
|
std::string to(self->childGetValue("to_form").asString());
|
|
|
|
boost::regex emailFormat("[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(,[ \t]*[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})*");
|
|
|
|
if (to.empty() || !boost::regex_match(to, emailFormat))
|
|
{
|
|
LLNotificationsUtil::add("PromptRecipientEmail");
|
|
return;
|
|
}
|
|
|
|
if (from.empty() || !boost::regex_match(from, emailFormat))
|
|
{
|
|
LLNotificationsUtil::add("PromptSelfEmail");
|
|
return;
|
|
}
|
|
|
|
std::string subject(self->childGetValue("subject_form").asString());
|
|
if(subject.empty() || !self->mHasFirstMsgFocus)
|
|
{
|
|
LLNotificationsUtil::add("PromptMissingSubjMsg", LLSD(), LLSD(), boost::bind(&LLFloaterPostcard::missingSubjMsgAlertCallback, self, _1, _2));
|
|
return;
|
|
}
|
|
|
|
if (self->mJPEGImage.notNull())
|
|
{
|
|
self->sendPostcard();
|
|
}
|
|
else
|
|
{
|
|
LLNotificationsUtil::add("ErrorProcessingSnapshot");
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLFloaterPostcard::uploadCallback(const LLUUID& asset_id, void *user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
|
|
{
|
|
LLFloaterPostcard *self = (LLFloaterPostcard *)user_data;
|
|
|
|
LLFloaterSnapshot::savePostcardDone(!result, self->mSnapshotIndex);
|
|
|
|
if (result)
|
|
{
|
|
LLSD args;
|
|
args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
|
|
LLNotificationsUtil::add("ErrorUploadingPostcard", args);
|
|
}
|
|
else
|
|
{
|
|
// only create the postcard once the upload succeeds
|
|
|
|
// request the postcard
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessage("SendPostcard");
|
|
msg->nextBlock("AgentData");
|
|
msg->addUUID("AgentID", gAgent.getID());
|
|
msg->addUUID("SessionID", gAgent.getSessionID());
|
|
msg->addUUID("AssetID", self->mAssetID);
|
|
msg->addVector3d("PosGlobal", self->mPosTakenGlobal);
|
|
msg->addString("To", self->childGetValue("to_form").asString());
|
|
msg->addString("From", self->childGetValue("from_form").asString());
|
|
msg->addString("Name", self->childGetValue("name_form").asString());
|
|
msg->addString("Subject", self->childGetValue("subject_form").asString());
|
|
msg->addString("Msg", self->childGetValue("msg_form").asString());
|
|
msg->addBOOL("AllowPublish", FALSE);
|
|
msg->addBOOL("MaturePublish", FALSE);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
self->destroy();
|
|
}
|
|
|
|
// static
|
|
void LLFloaterPostcard::updateUserInfo(const std::string& email)
|
|
{
|
|
for (instance_list_t::iterator iter = sInstances.begin();
|
|
iter != sInstances.end(); ++iter)
|
|
{
|
|
LLFloaterPostcard *instance = *iter;
|
|
const std::string& text = instance->childGetValue("from_form").asString();
|
|
if (text.empty())
|
|
{
|
|
// there's no text in this field yet, pre-populate
|
|
instance->childSetValue("from_form", LLSD(email));
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, LLTextEditor* msg_form)
|
|
{
|
|
if(msg_form && msg_form == receiver && msg_form->hasFocus() && !(mHasFirstMsgFocus))
|
|
{
|
|
mHasFirstMsgFocus = true;
|
|
msg_form->setText(LLStringUtil::null);
|
|
}
|
|
}
|
|
|
|
bool LLFloaterPostcard::missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response)
|
|
{
|
|
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
|
|
if(0 == option)
|
|
{
|
|
// User clicked OK
|
|
if((childGetValue("subject_form").asString()).empty())
|
|
{
|
|
// Stuff the subject back into the form.
|
|
LLStringUtil::format_map_t targs;
|
|
targs["[SECOND_LIFE]"] = LLTrans::getString("SECOND_LIFE");
|
|
std::string subj = getString("default_subject");
|
|
LLStringUtil::format(subj, targs);
|
|
|
|
childSetValue("subject_form", subj);
|
|
}
|
|
|
|
if(!mHasFirstMsgFocus)
|
|
{
|
|
// The user never switched focus to the messagee window.
|
|
// Using the default string.
|
|
childSetValue("msg_form", getString("default_message"));
|
|
}
|
|
|
|
sendPostcard();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LLFloaterPostcard::sendPostcard()
|
|
{
|
|
mTransactionID.generate();
|
|
mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
|
|
LLVFile::writeFile(mJPEGImage->getData(), mJPEGImage->getDataSize(), gVFS, mAssetID, LLAssetType::AT_IMAGE_JPEG);
|
|
|
|
// upload the image
|
|
LLFloaterSnapshot::saveStart(mSnapshotIndex);
|
|
std::string url = gAgent.getRegion()->getCapability("SendPostcard");
|
|
if(!url.empty())
|
|
{
|
|
llinfos << "Send Postcard via capability" << llendl;
|
|
LLSD body = LLSD::emptyMap();
|
|
// the capability already encodes: agent ID, region ID
|
|
body["pos-global"] = mPosTakenGlobal.getValue();
|
|
body["to"] = childGetValue("to_form").asString();
|
|
body["from"] = childGetValue("from_form").asString();
|
|
body["name"] = childGetValue("name_form").asString();
|
|
body["subject"] = childGetValue("subject_form").asString();
|
|
body["msg"] = childGetValue("msg_form").asString();
|
|
LLHTTPClient::post(url, body, new LLSendPostcardResponder(body, mAssetID, LLAssetType::AT_IMAGE_JPEG, mSnapshotIndex));
|
|
}
|
|
else
|
|
{
|
|
gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE);
|
|
}
|
|
|
|
// give user feedback of the event
|
|
gViewerWindow->playSnapshotAnimAndSound();
|
|
LLUploadDialog::modalUploadDialog(getString("upload_message"));
|
|
|
|
// don't destroy the window until the upload is done
|
|
// this way we keep the information in the form
|
|
setVisible(FALSE);
|
|
}
|