llexperiencelog.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /**
  2. * @file llexperiencelog.cpp
  3. * @brief llexperiencelog implementation
  4. *
  5. * $LicenseInfo:firstyear=2014&license=viewergpl$
  6. *
  7. * Copyright (c) 2014, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llexperiencelog.h"
  34. #include "lldate.h"
  35. #include "lldir.h"
  36. #include "lldispatcher.h"
  37. #include "llexperiencecache.h"
  38. #include "llnotifications.h"
  39. #include "llsdserialize.h"
  40. #include "lltrans.h"
  41. #include "llslurl.h"
  42. #include "llviewermessage.h" // For gGenericDispatcher
  43. // Global
  44. const std::string PUMP_EXPERIENCE = "experience_permission";
  45. class LLExperienceLogDispatchHandler final : public LLDispatchHandler
  46. {
  47. protected:
  48. LOG_CLASS(LLExperienceLogDispatchHandler);
  49. public:
  50. bool operator()(const LLDispatcher* dispatcher, const std::string& key,
  51. const LLUUID& invoice, const sparam_t& strings) override
  52. {
  53. LLSD message;
  54. sparam_t::const_iterator it = strings.begin();
  55. if (it != strings.end())
  56. {
  57. const std::string& llsd_raw = *it++;
  58. std::istringstream llsd_data(llsd_raw);
  59. if (!LLSDSerialize::deserialize(message, llsd_data,
  60. llsd_raw.length()))
  61. {
  62. llwarns << "Attempted to read parameter data into LLSD but failed: "
  63. << llsd_raw << llendl;
  64. }
  65. }
  66. message["public_id"] = invoice;
  67. // Object Name
  68. if (it != strings.end())
  69. {
  70. message["ObjectName"] = *it++;
  71. }
  72. // Parcel Name
  73. if (it != strings.end())
  74. {
  75. message["ParcelName"] = *it++;
  76. }
  77. message["Count"] = 1;
  78. LLExperienceLog::getInstance()->handleExperienceMessage(message);
  79. return true;
  80. }
  81. };
  82. static LLExperienceLogDispatchHandler experience_log_dispatch_handler;
  83. void LLExperienceLog::handleExperienceMessage(LLSD& message)
  84. {
  85. time_t now;
  86. time(&now);
  87. char daybuf[16];
  88. char time_of_day[16];
  89. strftime(daybuf, 16, "%Y-%m-%d", localtime(&now));
  90. strftime(time_of_day, 16, " %H:%M:%S", localtime(&now));
  91. message["Time"] = time_of_day;
  92. std::string day = daybuf;
  93. if (!mEvents.has(day))
  94. {
  95. mEvents[day] = LLSD::emptyArray();
  96. }
  97. LLSD& dayEvents = mEvents[day];
  98. if (dayEvents.size() > 0)
  99. {
  100. LLSD& last = *(dayEvents.rbeginArray());
  101. if (last["public_id"].asUUID() == message["public_id"].asUUID() &&
  102. last["ObjectName"].asString() == message["ObjectName"].asString() &&
  103. last["OwnerID"].asUUID() == message["OwnerID"].asUUID() &&
  104. last["ParcelName"].asString() == message["ParcelName"].asString() &&
  105. last["Permission"].asInteger() == message["Permission"].asInteger())
  106. {
  107. last["Count"] = last["Count"].asInteger() + 1;
  108. last["Time"] = time_of_day;
  109. mSignals(last);
  110. return;
  111. }
  112. }
  113. message["Time"] = time_of_day;
  114. mEvents[day].append(message);
  115. mEventsToSave[day].append(message);
  116. mSignals(message);
  117. }
  118. LLExperienceLog::LLExperienceLog()
  119. : mMaxDays(7),
  120. mPageSize(25),
  121. mNotifyNewEvent(false)
  122. {
  123. }
  124. void LLExperienceLog::initialize()
  125. {
  126. loadEvents();
  127. if (!gGenericDispatcher.isHandlerPresent("ExperienceEvent"))
  128. {
  129. gGenericDispatcher.addHandler("ExperienceEvent",
  130. &experience_log_dispatch_handler);
  131. }
  132. }
  133. std::string LLExperienceLog::getFilename()
  134. {
  135. return gDirUtil.getFullPath(LL_PATH_PER_ACCOUNT, "experience_events.xml");
  136. }
  137. std::string LLExperienceLog::getPermissionString(const LLSD& message,
  138. const std::string& base)
  139. {
  140. std::string name;
  141. if (message.has("Permission"))
  142. {
  143. name = base + llformat("%d", message["Permission"].asInteger());
  144. if (!LLTrans::hasString(name))
  145. {
  146. name.clear();
  147. }
  148. }
  149. else
  150. {
  151. std::stringstream str;
  152. LLSDSerialize::toPrettyXML(message, str);
  153. llwarns << "Missing \"Permission\" field in LLSD for message type: "
  154. << base << " - LLSD = " << str.str() << llendl;
  155. }
  156. if (name.empty())
  157. {
  158. name = base + "Unknown";
  159. }
  160. return LLTrans::getString(name, message);
  161. }
  162. void LLExperienceLog::notify(LLSD& message)
  163. {
  164. LL_DEBUGS("ExperienceLog") << "Notifying about event:";
  165. std::stringstream str;
  166. LLSDSerialize::toPrettyXML(message, str);
  167. LL_CONT << "\n" << str.str() << LL_ENDL;
  168. LLUUID id;
  169. std::string experience;
  170. if (message.has("public_id"))
  171. {
  172. id = message["public_id"].asUUID();
  173. }
  174. if (id.isNull())
  175. {
  176. llwarns << "Absent or invalid public experience Id !" << llendl;
  177. if (message.has("ExpName"))
  178. {
  179. experience = "'" + message["ExpName"].asString() + "'";
  180. }
  181. else
  182. {
  183. experience = "<Unknown>";
  184. }
  185. }
  186. else
  187. {
  188. experience = LLSLURL("experience", id, "profile").getSLURLString();
  189. }
  190. LLSD args;
  191. args["EXPERIENCE"] = experience;
  192. args["EVENTTYPE"] = getPermissionString(message, "ExperiencePermission");
  193. if (message.has("ObjectName"))
  194. {
  195. args["OBJECTNAME"] = message["ObjectName"].asString();
  196. }
  197. else
  198. {
  199. args["OBJECTNAME"] = LLStringUtil::null;
  200. }
  201. bool from_attachment = message.has("IsAttachment") &&
  202. message["IsAttachment"].asBoolean();
  203. if (!from_attachment)
  204. {
  205. id.setNull();
  206. if (message.has("OwnerID"))
  207. {
  208. id = message["OwnerID"].asUUID();
  209. }
  210. std::string owner;
  211. if (id.notNull())
  212. {
  213. owner = LLSLURL("agent", id, "about").getSLURLString();
  214. }
  215. else
  216. {
  217. llwarns << "Absent or invalid experience owner Id !" << llendl;
  218. }
  219. args["OWNER"] = owner;
  220. if (message.has("ParcelName"))
  221. {
  222. args["PARCELNAME"] = message["ParcelName"].asString();
  223. }
  224. else
  225. {
  226. args["PARCELNAME"] = LLStringUtil::null;
  227. }
  228. }
  229. LL_DEBUGS("ExperienceLog") << "... translated into notification arguments:";
  230. std::stringstream str;
  231. LLSDSerialize::toPrettyXML(args, str);
  232. LL_CONT << "\n" << str.str() << LL_ENDL;
  233. if (from_attachment)
  234. {
  235. gNotifications.add("ExperienceEventAttachment", args);
  236. }
  237. else
  238. {
  239. gNotifications.add("ExperienceEvent", args);
  240. }
  241. }
  242. void LLExperienceLog::saveEvents()
  243. {
  244. std::string filename = getFilename();
  245. LLSD settings = LLSD::emptyMap().with("Events", mEventsToSave);
  246. settings["MaxDays"] = (LLSD::Integer)mMaxDays;
  247. settings["Notify"] = mNotifyNewEvent;
  248. settings["PageSize"] = (LLSD::Integer)mPageSize;
  249. llofstream stream(filename.c_str());
  250. if (stream.is_open())
  251. {
  252. LLSDSerialize::toPrettyXML(settings, stream);
  253. stream.close();
  254. }
  255. }
  256. void LLExperienceLog::loadEvents()
  257. {
  258. LLSD settings = LLSD::emptyMap();
  259. std::string filename = getFilename();
  260. llifstream stream(filename.c_str());
  261. if (stream.is_open())
  262. {
  263. LLSDSerialize::fromXMLDocument(settings, stream);
  264. stream.close();
  265. }
  266. if (settings.has("MaxDays"))
  267. {
  268. setMaxDays((U32)settings["MaxDays"].asInteger());
  269. }
  270. if (settings.has("Notify"))
  271. {
  272. setNotifyNewEvent(settings["Notify"].asBoolean());
  273. }
  274. if (settings.has("PageSize"))
  275. {
  276. setPageSize((U32)settings["PageSize"].asInteger());
  277. }
  278. mEvents.clear();
  279. if (mMaxDays > 0 && settings.has("Events"))
  280. {
  281. mEvents = settings["Events"];
  282. mEventsToSave = mEvents;
  283. }
  284. }
  285. LLExperienceLog::~LLExperienceLog()
  286. {
  287. saveEvents();
  288. }
  289. void LLExperienceLog::eraseExpired()
  290. {
  291. while ((U32)mEvents.size() > mMaxDays && mMaxDays > 0)
  292. {
  293. mEvents.erase(mEvents.beginMap()->first);
  294. }
  295. }
  296. bool LLExperienceLog::isNotExpired(std::string& date)
  297. {
  298. LLDate event_date;
  299. S32 month, day, year;
  300. S32 matched = sscanf(date.c_str(), "%d-%d-%d", &year, &month, &day);
  301. if (matched != 3) return false;
  302. event_date.fromYMDHMS(year, month, day);
  303. S32 curr_year, curr_month, curr_day;
  304. LLDate curr_date = LLDate::now();
  305. curr_date.split(&curr_year, &curr_month, &curr_day);
  306. // Sets hour, min, and sec to 0
  307. curr_date.fromYMDHMS(curr_year, curr_month, curr_day);
  308. LLDate boundary_date = LLDate(curr_date.secondsSinceEpoch() -
  309. 86400 * getMaxDays());
  310. return event_date >= boundary_date;
  311. }
  312. LLExperienceLog::callback_connection_t LLExperienceLog::addUpdateSignal(const callback_slot_t& cb)
  313. {
  314. return mSignals.connect(cb);
  315. }
  316. void LLExperienceLog::setNotifyNewEvent(bool val)
  317. {
  318. mNotifyNewEvent = val;
  319. if (!val && mNotifyConnection.connected())
  320. {
  321. mNotifyConnection.disconnect();
  322. }
  323. else if (val && !mNotifyConnection.connected())
  324. {
  325. mNotifyConnection = addUpdateSignal(boost::function<void(LLSD&)>(LLExperienceLog::notify));
  326. }
  327. }