Open-Transactions  0.93.0-ge03d287
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
OTMessageOutbuffer.cpp
Go to the documentation of this file.
1 /************************************************************
2  *
3  * OTMessageOutbuffer.cpp
4  *
5  */
6 
7 /************************************************************
8  -----BEGIN PGP SIGNED MESSAGE-----
9  Hash: SHA1
10 
11  * OPEN TRANSACTIONS
12  *
13  * Financial Cryptography and Digital Cash
14  * Library, Protocol, API, Server, CLI, GUI
15  *
16  * -- Anonymous Numbered Accounts.
17  * -- Untraceable Digital Cash.
18  * -- Triple-Signed Receipts.
19  * -- Cheques, Vouchers, Transfers, Inboxes.
20  * -- Basket Currencies, Markets, Payment Plans.
21  * -- Signed, XML, Ricardian-style Contracts.
22  * -- Scripted smart contracts.
23  *
24  * Copyright (C) 2010-2013 by "Fellow Traveler" (A pseudonym)
25  *
26  * EMAIL:
28  *
29  * BITCOIN: 1NtTPVVjDsUfDWybS4BwvHpG2pdS9RnYyQ
30  *
31  * KEY FINGERPRINT (PGP Key in license file):
32  * 9DD5 90EB 9292 4B48 0484 7910 0308 00ED F951 BB8E
33  *
34  * OFFICIAL PROJECT WIKI(s):
35  * https://github.com/FellowTraveler/Moneychanger
36  * https://github.com/FellowTraveler/Open-Transactions/wiki
37  *
38  * WEBSITE:
39  * http://www.OpenTransactions.org/
40  *
41  * Components and licensing:
42  * -- Moneychanger..A Java client GUI.....LICENSE:.....GPLv3
43  * -- otlib.........A class library.......LICENSE:...LAGPLv3
44  * -- otapi.........A client API..........LICENSE:...LAGPLv3
45  * -- opentxs/ot....Command-line client...LICENSE:...LAGPLv3
46  * -- otserver......Server Application....LICENSE:....AGPLv3
47  * Github.com/FellowTraveler/Open-Transactions/wiki/Components
48  *
49  * All of the above OT components were designed and written by
50  * Fellow Traveler, with the exception of Moneychanger, which
51  * was contracted out to Vicky C ([email protected]).
52  * The open-source community has since actively contributed.
53  *
54  * -----------------------------------------------------
55  *
56  * LICENSE:
57  * This program is free software: you can redistribute it
58  * and/or modify it under the terms of the GNU Affero
59  * General Public License as published by the Free Software
60  * Foundation, either version 3 of the License, or (at your
61  * option) any later version.
62  *
63  * ADDITIONAL PERMISSION under the GNU Affero GPL version 3
64  * section 7: (This paragraph applies only to the LAGPLv3
65  * components listed above.) If you modify this Program, or
66  * any covered work, by linking or combining it with other
67  * code, such other code is not for that reason alone subject
68  * to any of the requirements of the GNU Affero GPL version 3.
69  * (==> This means if you are only using the OT API, then you
70  * don't have to open-source your code--only your changes to
71  * Open-Transactions itself must be open source. Similar to
72  * LGPLv3, except it applies to software-as-a-service, not
73  * just to distributing binaries.)
74  *
75  * Extra WAIVER for OpenSSL, Lucre, and all other libraries
76  * used by Open Transactions: This program is released under
77  * the AGPL with the additional exemption that compiling,
78  * linking, and/or using OpenSSL is allowed. The same is true
79  * for any other open source libraries included in this
80  * project: complete waiver from the AGPL is hereby granted to
81  * compile, link, and/or use them with Open-Transactions,
82  * according to their own terms, as long as the rest of the
83  * Open-Transactions terms remain respected, with regard to
84  * the Open-Transactions code itself.
85  *
86  * Lucre License:
87  * This code is also "dual-license", meaning that Ben Lau-
88  * rie's license must also be included and respected, since
89  * the code for Lucre is also included with Open Transactions.
90  * See Open-Transactions/src/otlib/lucre/LUCRE_LICENSE.txt
91  * The Laurie requirements are light, but if there is any
92  * problem with his license, simply remove the Lucre code.
93  * Although there are no other blind token algorithms in Open
94  * Transactions (yet. credlib is coming), the other functions
95  * will continue to operate.
96  * See Lucre on Github: https://github.com/benlaurie/lucre
97  * -----------------------------------------------------
98  * You should have received a copy of the GNU Affero General
99  * Public License along with this program. If not, see:
100  * http://www.gnu.org/licenses/
101  *
102  * If you would like to use this software outside of the free
103  * software license, please contact FellowTraveler.
104  * (Unfortunately many will run anonymously and untraceably,
105  * so who could really stop them?)
106  *
107  * DISCLAIMER:
108  * This program is distributed in the hope that it will be
109  * useful, but WITHOUT ANY WARRANTY; without even the implied
110  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
111  * PURPOSE. See the GNU Affero General Public License for
112  * more details.
113 
114  -----BEGIN PGP SIGNATURE-----
115  Version: GnuPG v1.4.9 (Darwin)
116 
117  iQIcBAEBAgAGBQJRSsfJAAoJEAMIAO35UbuOQT8P/RJbka8etf7wbxdHQNAY+2cC
118  vDf8J3X8VI+pwMqv6wgTVy17venMZJa4I4ikXD/MRyWV1XbTG0mBXk/7AZk7Rexk
119  KTvL/U1kWiez6+8XXLye+k2JNM6v7eej8xMrqEcO0ZArh/DsLoIn1y8p8qjBI7+m
120  aE7lhstDiD0z8mwRRLKFLN2IH5rAFaZZUvj5ERJaoYUKdn4c+RcQVei2YOl4T0FU
121  LWND3YLoH8naqJXkaOKEN4UfJINCwxhe5Ke9wyfLWLUO7NamRkWD2T7CJ0xocnD1
122  sjAzlVGNgaFDRflfIF4QhBx1Ddl6wwhJfw+d08bjqblSq8aXDkmFA7HeunSFKkdn
123  oIEOEgyj+veuOMRJC5pnBJ9vV+7qRdDKQWaCKotynt4sWJDGQ9kWGWm74SsNaduN
124  TPMyr9kNmGsfR69Q2Zq/FLcLX/j8ESxU+HYUB4vaARw2xEOu2xwDDv6jt0j3Vqsg
125  x7rWv4S/Eh18FDNDkVRChiNoOIilLYLL6c38uMf1pnItBuxP3uhgY6COm59kVaRh
126  nyGTYCDYD2TK+fI9o89F1297uDCwEJ62U0Q7iTDp5QuXCoxkPfv8/kX6lS6T3y9G
127  M9mqIoLbIQ1EDntFv7/t6fUTS2+46uCrdZWbQ5RjYXdrzjij02nDmJAm2BngnZvd
128  kamH0Y/n11lCvo1oQxM+
129  =uSzz
130  -----END PGP SIGNATURE-----
131  **************************************************************/
132 
133 #include "OTMessageOutbuffer.hpp"
134 #include "../core/util/OTDataFolder.hpp"
135 #include "../core/util/OTPaths.hpp"
136 #include "../core/OTStorage.hpp"
137 #include "../core/OTMessage.hpp"
138 #include "../core/OTTransaction.hpp"
139 #include "../core/OTPseudonym.hpp"
140 #include "../core/OTLog.hpp"
141 #include "../core/util/OTFolders.hpp"
142 
143 #include <memory>
144 
145 namespace opentxs
146 {
147 
148 // The purpose of this class is to cache client requests (being sent to the
149 // server)
150 // so that they can later be queried (using the request number) by the developer
151 // using the OTAPI, so that if transaction numbers need to be clawed back from
152 // failed
153 // messages, etc, they are available.
154 //
155 // The OT client side also can use this as a mechanism to help separate
156 // old-and-dealt-with
157 // messages, by explicitly removing messages from this queue once they are dealt
158 // with.
159 // This way the developer can automatically assume that any reply is old if it
160 // carries
161 // a request number that cannot be found in this queue.
162 
164  : dataFolder_(OTDataFolder::Get())
165 {
166  OT_ASSERT(dataFolder_.Exists());
167 }
168 
169 void OTMessageOutbuffer::AddSentMessage(OTMessage& theMessage) // must be heap
170  // allocated.
171 {
172  int64_t lRequestNum = 0;
173 
174  if (theMessage.m_strRequestNum.Exists())
175  lRequestNum = atol(theMessage.m_strRequestNum.Get()); // The map index
176  // is the request
177  // number on the
178  // message itself.
179 
180  // It's technically possible to have TWO messages (from two different
181  // servers) that happen to have the same request number. So we verify
182  // that here, before removing any old ones with the same number and IDs.
183  //
184  auto it = messagesMap_.begin();
185 
186  for (; it != messagesMap_.end(); ++it) {
187 
188  const int64_t& lTempReqNum = it->first;
189 
190  if (lTempReqNum != lRequestNum) {
191  continue;
192  }
193 
194  OTMessage* pMsg = it->second;
195  OT_ASSERT(nullptr != pMsg);
196 
197  //
198  // If a server ID was passed in, but doesn't match the server ID on this
199  // message,
200  // Then skip this one. (Same with the NymID.)
201  //
202  if (!theMessage.m_strServerID.Compare(pMsg->m_strServerID) ||
203  !theMessage.m_strNymID.Compare(pMsg->m_strNymID)) {
204  continue;
205  }
206  else {
207  delete pMsg;
208  pMsg = nullptr;
209  messagesMap_.erase(it);
210  break;
211  }
212  }
213  // Whatever it was, it's gone now!
214 
215  // Now that we KNOW there's nothing already there with that request number
216  // (for that
217  // server ID and Nym ID), we go ahead and add the new message to the map.
218  // (And take ownership.)
219  //
220  messagesMap_.insert(
221  std::pair<int64_t, OTMessage*>(lRequestNum, &theMessage));
222 
223  //
224  // Save it to local storage, in case we don't see the reply until the next
225  // run.
226  //
227  bool bAlreadyExists = false, bIsNewFolder = false;
228  OTString strFolder, strFolder1, strFolder2;
229  strFolder1.Format("%s%s%s", OTFolders::Nym().Get(), OTLog::PathSeparator(),
230  theMessage.m_strServerID.Get());
231  strFolder2.Format("%s%s%s", strFolder1.Get(), OTLog::PathSeparator(),
232  "sent" /*todo hardcoding*/);
233 
234  strFolder.Format("%s%s%s", strFolder2.Get(), OTLog::PathSeparator(),
235  theMessage.m_strNymID.Get());
236 
237  OTString strFolderPath = "", strFolder1Path = "", strFolder2Path = "";
238 
239  OTPaths::AppendFolder(strFolderPath, dataFolder_, strFolder);
240  OTPaths::AppendFolder(strFolder1Path, dataFolder_, strFolder1);
241  OTPaths::AppendFolder(strFolder2Path, dataFolder_, strFolder2);
242 
243  OTPaths::ConfirmCreateFolder(strFolderPath, bAlreadyExists, bIsNewFolder);
244  OTPaths::ConfirmCreateFolder(strFolder1Path, bAlreadyExists, bIsNewFolder);
245  OTPaths::ConfirmCreateFolder(strFolder2Path, bAlreadyExists, bIsNewFolder);
246 
247  OTString strFile;
248  strFile.Format("%s.msg", theMessage.m_strRequestNum.Get());
249 
250  theMessage.SaveContract(strFolder.Get(), strFile.Get());
251 
252  // We also keep a list of the request numbers, so let's load it up, add the
253  // number
254  // to that list, and then save it again.
255  //
256  OTNumList theNumList;
257  std::string str_data_filename("sent.dat"); // todo hardcoding.
258  if (OTDB::Exists(strFolder.Get(), str_data_filename)) {
259  OTString strNumList(
260  OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
261  if (strNumList.Exists()) theNumList.Add(strNumList);
262  theNumList.Add(lRequestNum); // Add the new request number to it.
263  }
264  else // it doesn't exist on disk, so let's just create it from the list we
265  // have in RAM so we can store it to disk.
266  {
267  it = messagesMap_.begin();
268  while (it != messagesMap_.end()) {
269 
270  const int64_t& lTempReqNum = it->first;
271 
272  OTMessage* pMsg = it->second;
273  OT_ASSERT(nullptr != pMsg);
274 
275  //
276  // If a server ID was passed in, but doesn't match the server ID on
277  // this message,
278  // Then skip this one. (Same with the NymID.)
279  //
280  if (!theMessage.m_strServerID.Compare(pMsg->m_strServerID) ||
281  !theMessage.m_strNymID.Compare(pMsg->m_strNymID)) {
282  ++it;
283  continue;
284  }
285  else {
286  theNumList.Add(lTempReqNum);
287  }
288  ++it;
289  }
290  } // else
291 
292  // By this point, theNumList has either been loaded from local storage and
293  // had the new number added,
294  // or it wasn't in local storage and thus we created it and added all the
295  // numnbers to it (including new one.)
296  // Therefore nothing left to do here, but save it back again!
297  //
298  OTString strOutput;
299  theNumList.Output(strOutput);
300 
301  if (!OTDB::StorePlainString(strOutput.Get(), strFolder.Get(),
302  str_data_filename)) // todo hardcoding.
303  {
304  otErr << "OTMessageOutbuffer::AddSentMessage: Error: failed writing "
305  "list of request numbers to storage.\n";
306  }
307 }
308 
309 // You are NOT responsible to delete the OTMessage object
310 // that comes back from this function. The buffer maintains
311 // ownership until you call RemoveSentMessage().
312 
313 OTMessage* OTMessageOutbuffer::GetSentMessage(const int64_t& lRequestNum,
314  const OTString& strServerID,
315  const OTString& strNymID)
316 {
317  auto it = messagesMap_.begin();
318 
319  for (; it != messagesMap_.end(); ++it) {
320 
321  const int64_t& lTempReqNum = it->first;
322 
323  if (lTempReqNum != lRequestNum) {
324  continue;
325  }
326 
327  OTMessage* pMsg = it->second;
328  OT_ASSERT(nullptr != pMsg);
329 
330  //
331  // If a server ID was passed in, but doesn't match the server ID on this
332  // message,
333  // Then skip this one. (Same with the NymID.)
334  if (!strServerID.Compare(pMsg->m_strServerID) ||
335  !strNymID.Compare(pMsg->m_strNymID)) {
336  continue;
337  }
338  else {
339  return pMsg;
340  }
341  }
342 
343  // Didn't find it? Okay let's load it from local storage, if it's there...
344  //
345  OTString strFolder, strFile;
346  strFolder.Format(
347  "%s%s%s%s%s%s%s", OTFolders::Nym().Get(), OTLog::PathSeparator(),
348  strServerID.Get(), OTLog::PathSeparator(), "sent",
349  /*todo hardcoding*/ OTLog::PathSeparator(), strNymID.Get());
350  strFile.Format("%lld.msg", lRequestNum);
351 
352  // Check the existing list, if it exists.
353  //
354  OTNumList theNumList;
355  std::string str_data_filename("sent.dat");
356  if (OTDB::Exists(strFolder.Get(), str_data_filename)) // todo hardcoding.
357  {
358  OTString strNumList(
359  OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
360 
361  if (strNumList.Exists()) theNumList.Add(strNumList);
362 
363  if (theNumList.Verify(lRequestNum)) {
364  // Even if the outgoing message was stored, we still act like it
365  // "doesn't exist" if it doesn't appear on the official list.
366  // The list is what matters -- the message is just the contents
367  // referenced
368  // by that list.
369 
370  OTMessage* pMsg = new OTMessage;
371  OT_ASSERT(nullptr != pMsg);
372  std::unique_ptr<OTMessage> theMsgAngel(pMsg);
373 
374  if (OTDB::Exists(strFolder.Get(), strFile.Get()) &&
375  pMsg->LoadContract(strFolder.Get(), strFile.Get())) {
376  // Since we had to load it from local storage, let's add it to
377  // the list in RAM.
378  //
379  messagesMap_.insert(std::pair<int64_t, OTMessage*>(
380  lRequestNum, theMsgAngel.release()));
381  return pMsg;
382  }
383  }
384  }
385 
386  // STILL didn't find it? (Failure.)
387  //
388  return nullptr;
389 }
390 
391 // WARNING: ONLY call this (with arguments) directly after a successful
392 // @getNymbox has been received!
393 // See comments below for more details.
394 //
395 void OTMessageOutbuffer::Clear(const OTString* pstrServerID,
396  const OTString* pstrNymID, OTPseudonym* pNym,
397  const bool* pbHarvestingForRetry)
398 {
399  // const char * szFuncName = "OTMessageOutbuffer::Clear";
400 
401  auto it = messagesMap_.begin();
402 
403  while (it != messagesMap_.end()) {
404 
405  const int64_t& lRequestNum = it->first;
406  OTMessage* pThisMsg = it->second;
407  OT_ASSERT(nullptr != pThisMsg);
408 
409  //
410  // If a server ID was passed in, but doesn't match the server ID on this
411  // message,
412  // Then skip this one. (Same with the NymID.)
413  if (((nullptr != pstrServerID) &&
414  !pstrServerID->Compare(pThisMsg->m_strServerID)) ||
415  ((nullptr != pstrNymID) &&
416  !pstrNymID->Compare(pThisMsg->m_strNymID))) {
417  ++it;
418  continue;
419  }
420  else {
421  /*
422  Sent messages are cached because some of them are so important,
423  that
424  the server drops a reply notice into the Nymbox to make sure they
425  were
426  received. This way, when we download the Nymbox we can SEE which
427  messages
428  were ACTUALLY replied to, and at that time, we removed those
429  messages
430  already from *this "sent buffer." After that loop was done, we
431  called
432  CLEAR (this function) and cleared ALL the sent messages from the
433  buffer
434  (for the appropriate server and nym IDs...clear without those IDs
435  is
436  only for the destructor.)
437 
438  This Clear, where we are now, HARVESTS the transaction numbers back
439  from any messages left in the sent buffer. We are able to do this
440  with
441  confidence because we know that this function is only called in
442  @getNymbox
443  on client side, and only after the ones with actual replies (as
444  evidenced
445  by the Nymbox) have already been removed from *this "sent buffer."
446 
447  Why were they removed in advance? Because clearly: if the server
448  HAS replied
449  to them already, then there's no need to harvest anything: just let
450  it
451  process as normal, whether the transaction inside is a success or
452  fail.
453  (We KNOW the message didn't fail because otherwise there wouldn't
454  even be
455  a notice in the Nymbox. So this is about the transaction inside.)
456 
457  So we remove the ones that we DEFINITELY know the server HAS
458  replied to.
459 
460  And the ones remaining? We know for those, the server definitely
461  has NOT
462  replied to them (the message must have been dropped by the network
463  or
464  something.) How do we know this? Because there would be a notice in
465  the
466  Nymbox! So at the moment of successful @getNymbox, we are able to
467  loop through
468  those receipts and know FOR SURE, WHICH ones definitely have a
469  reply, and
470  which ones definitely DO NOT.
471 
472  The ones where we definitely do NOT have a reply--that is, the ones
473  that are in
474  the "sent messages" buffer, but are not in the Nymbox with the same
475  request
476  number--we harvest those numbers, since the server clearly never
477  saw them, or
478  rejected the message before the transaction itself even had a
479  chance to run.
480 
481  */
482  if (nullptr != pNym) {
483  OT_ASSERT(nullptr != pstrNymID && pstrNymID->Exists());
484  const OTIdentifier MSG_NYM_ID(*pstrNymID);
485  OT_ASSERT(pNym->CompareID(MSG_NYM_ID));
486 
487  OT_ASSERT(nullptr != pstrServerID && pstrServerID->Exists());
488 
489  OT_ASSERT(nullptr != pbHarvestingForRetry);
490 
491  /*
492  getNymbox -- client is NOT sending hash, server is
493  NOT rejecting bad hashes, server IS SENDING HASH in the
494  @getNymbox reply
495  getRequest -- client is NOT sending hash, server is
496  NOT rejecting bad hashes, server IS SENDING HASH in the
497  @getRequest reply
498 
499  processNymbox -- client is SENDING HASH, server is
500  REJECTING BAD HASHES, server is SENDING HASH in the
501  @processNymbox reply
502  notarizeTransactions -- client is SENDING HASH, server is
503  REJECTING BAD HASHES, server is SENDING HASH in the
504  @notarizeTransactions reply
505  processInbox -- client is SENDING HASH, server is
506  REJECTING BAD HASHES, server is SENDING HASH in the
507  @processInbox reply
508  triggerClause -- client is SENDING HASH, server is
509  REJECTING BAD HASHES, server is SENDING HASH in the
510  @triggerClause reply
511 
512  getTransactionNum -- client is SENDING HASH, server is
513  REJECTING BAD HASHES, server is SENDING HASH in the
514  @getTransactionNum reply
515 
516  Already covered in NotarizeTransaction:
517  transfer, withdrawal, deposit, marketOffer, paymentPlan,
518  smartContract, cancelCronItem, exchangeBasket
519  */
520 
521  if (pThisMsg->m_ascPayload.Exists() &&
522  (pThisMsg->m_strCommand.Compare("processNymbox") ||
523  pThisMsg->m_strCommand.Compare("processInbox") ||
524  pThisMsg->m_strCommand.Compare("notarizeTransactions") ||
525  pThisMsg->m_strCommand.Compare("triggerClause"))) {
526  //
527  // If we are here in the first place (i.e. after @getNymbox
528  // just removed
529  // all the messages in this sent buffer that already had a
530  // reply sitting
531  // in the nymbox) therefore we KNOW any messages in here
532  // never got a reply
533  // from the server
534 
535  const bool bReplyWasSuccess =
536  false; // If the msg had been a success, the reply
537  // (whether transaction within succeeded or
538  // failed) would have been dropped into my
539  // Nymbox, and thus removed from this "sent
540  // buffer" in @getNymbox.
541  const bool bReplyWasFailure =
542  true; // If the msg had been an explicit failure, the
543  // reply (without the transaction inside of it
544  // even having a chance to succeed or fail) would
545  // definitely NOT have been dropped into my
546  // Nymbox, and thus removed from this "sent
547  // buffer" in @getNymbox. However, IN THIS ONE
548  // CASE, since we DID just download the Nymbox and
549  // verify there ARE NO REPLIES for this request
550  // number (before calling this function), and
551  // since a dropped message is basically identical
552  // to a rejected message, since in either case,
553  // the transaction itself never even had a chance
554  // to run, we are able to now harvest the message
555  // AS IF the server HAD explicitly rejected the
556  // message. This is why I pass true here, where
557  // anywhere else in the code I would always pass
558  // false unless I had explicitly received a
559  // failure from the server. This place in the
560  // code, where we are now, is the failsafe
561  // endpoint for missed/dropped messages! IF they
562  // STILL haven't been found by this point, they
563  // are cleaned up as if the message was explicitly
564  // rejected by the server before the transaction
565  // even had a chance to run.
566 
567  const bool bTransactionWasSuccess =
568  false; // Per above, since "the transaction never had a
569  // chance to run" then it could NOT have been an
570  // explicit success.
571  const bool bTransactionWasFailure =
572  false; // Per above, since "the transaction never had a
573  // chance to run" then it could NOT have been an
574  // explicit failure.
575 
576  pThisMsg->HarvestTransactionNumbers(
577  *pNym, // Actually it's pNym who is "harvesting" the
578  // numbers in this call. <========= HARVEST
579  *pbHarvestingForRetry, bReplyWasSuccess,
580  bReplyWasFailure, bTransactionWasSuccess,
581  bTransactionWasFailure);
582  } // if there's a transaction to be harvested inside this
583  // message.
584  } // if pNym !nullptr
585 
586  auto temp_it = it;
587  ++temp_it;
588  messagesMap_.erase(it);
589  it = temp_it; // here's where the iterator gets incremented (during
590  // the erase, basically.)
591 
592  delete pThisMsg; // <============ DELETE
593  pThisMsg = nullptr;
594 
595  if (nullptr != pstrNymID && nullptr != pstrServerID) {
596  OTString strFolder, strFile;
597  strFolder.Format("%s%s%s%s%s%s%s", OTFolders::Nym().Get(),
598  OTLog::PathSeparator(), pstrServerID->Get(),
599  OTLog::PathSeparator(), "sent",
600  /*todo hardcoding*/ OTLog::PathSeparator(),
601  pstrNymID->Get());
602  strFile.Format("%lld.msg", lRequestNum);
603 
604  OTNumList theNumList;
605  std::string str_data_filename("sent.dat"); // todo hardcoding.
606  if (OTDB::Exists(strFolder.Get(), str_data_filename)) {
607  OTString strNumList(OTDB::QueryPlainString(
608  strFolder.Get(), str_data_filename));
609  if (strNumList.Exists()) theNumList.Add(strNumList);
610  theNumList.Remove(lRequestNum); // Clear (this function)
611  // loops and removes them.
612  // (Here's the one being
613  // removed this iteration.)
614  }
615  else // it doesn't exist on disk, so let's just create it from
616  // the list we have in RAM so we can store it to disk.
617  { // NOTE: this may be unnecessary since we are "clear"ing them
618  // all anyway. But that just means we can remove this
619  // block during optimization. Todo optimize.
620  // Since we create the NumList based on messagesMap_, and
621  // since the message for this iteration was already removed
622  // above, we don't need to remove anything at this point, we
623  // just create the NumList to contain the same numbers as
624  // are
625  // in messagesMap_.
626  //
627  it = messagesMap_.begin();
628  while (it != messagesMap_.end()) {
629 
630  const int64_t& lTempReqNum = it->first;
631 
632  OTMessage* pMsg = it->second;
633  OT_ASSERT(nullptr != pMsg);
634 
635  //
636  // If a server ID was passed in, but doesn't match the
637  // server ID on this message,
638  // Then skip this one. (Same with the NymID.)
639  //
640  if (!pstrServerID->Compare(pMsg->m_strServerID) ||
641  !pstrNymID->Compare(pMsg->m_strNymID)) {
642  ++it;
643  continue;
644  }
645  else {
646  theNumList.Add(lTempReqNum);
647  }
648  ++it;
649  }
650  } // else
651 
652  // By this point, theNumList has either been loaded from local
653  // storage and had the number removed,
654  // or it wasn't in local storage and thus we created it and
655  // added all the numbers to it from RAM (not
656  // including the one being erased, since it was already removed
657  // from the RAM list, above.) So either
658  // way, the number being removed is now ABSENT from theNumList.
659  //
660  // Therefore nothing left to do here, but save it back again!
661  //
662  OTString strOutput;
663  theNumList.Output(strOutput);
664  if (!OTDB::StorePlainString(strOutput.Get(), strFolder.Get(),
665  str_data_filename)) // todo
666  // hardcoding.
667  {
668  otErr << "OTMessageOutbuffer::Clear: Error: failed writing "
669  "list of request numbers to storage.\n";
670  }
671 
672  // Make sure any messages being erased here, are also erased
673  // from local storage.
674  // Now that we've updated the numlist in local storage, let's
675  // erase the sent message itself...
676  //
677  OTMessage* pMsg = new OTMessage;
678  OT_ASSERT(nullptr != pMsg);
679  std::unique_ptr<OTMessage> theMsgAngel(pMsg);
680 
681  if (OTDB::Exists(strFolder.Get(), strFile.Get()) &&
682  pMsg->LoadContract(strFolder.Get(), strFile.Get())) {
683  OTDB::EraseValueByKey(strFolder.Get(), strFile.Get());
684  }
685  }
686  }
687  }
688 }
689 
690 // OTMessageOutbuffer deletes the OTMessage when you call this.
691 //
692 bool OTMessageOutbuffer::RemoveSentMessage(const int64_t& lRequestNum,
693  const OTString& strServerID,
694  const OTString& strNymID)
695 {
696  OTString strFolder, strFile;
697  strFolder.Format(
698  "%s%s%s%s%s%s%s", OTFolders::Nym().Get(), OTLog::PathSeparator(),
699  strServerID.Get(), OTLog::PathSeparator(), "sent",
700  /*todo hardcoding*/ OTLog::PathSeparator(), strNymID.Get());
701  strFile.Format("%lld.msg", lRequestNum);
702 
703  auto it = messagesMap_.begin();
704 
705  bool bReturnValue = false;
706 
707  while (it != messagesMap_.end()) {
708 
709  const int64_t& lTempReqNum = it->first;
710 
711  if (lTempReqNum != lRequestNum) {
712  ++it;
713  continue;
714  }
715 
716  OTMessage* pMsg = it->second;
717  OT_ASSERT(nullptr != pMsg);
718 
719  //
720  // If a server ID was passed in, but doesn't match the server ID on this
721  // message,
722  // Then skip this one. (Same with the NymID.)
723  if (!strServerID.Compare(pMsg->m_strServerID) ||
724  !strNymID.Compare(pMsg->m_strNymID)) {
725  ++it;
726  continue;
727  }
728  else {
729  delete pMsg;
730  pMsg = nullptr;
731 
732  auto temp_it = it;
733  ++temp_it;
734  messagesMap_.erase(it);
735  it = temp_it; // here's where it gets incremented. (During the
736  // erase, basically.)
737 
738  bReturnValue = true;
739  break;
740  }
741  }
742 
743  // Whether we found it in RAM or not, let's make sure to delete it from
744  // local storage, if it's there... (Since there's a list there we have to
745  // update,
746  // anyway.)
747  // We keep a list of the request numbers, so let's load it up, remove the
748  // number
749  // from that list, and then save it again.
750 
751  OTNumList theNumList;
752  std::string str_data_filename("sent.dat"); // todo hardcoding.
753  if (OTDB::Exists(strFolder.Get(), str_data_filename)) {
754  OTString strNumList(
755  OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
756  if (strNumList.Exists()) theNumList.Add(strNumList);
757  theNumList.Remove(lRequestNum);
758  }
759  else // it doesn't exist on disk, so let's just create it from the list we
760  // have in RAM so we can store it to disk.
761  {
762  it = messagesMap_.begin();
763  while (it != messagesMap_.end()) {
764 
765  const int64_t& lTempReqNum = it->first;
766 
767  OTMessage* pMsg = it->second;
768  OT_ASSERT(nullptr != pMsg);
769 
770  //
771  // If a server ID was passed in, but doesn't match the server ID on
772  // this message,
773  // Then skip this one. (Same with the NymID.)
774  //
775  if (!strServerID.Compare(pMsg->m_strServerID) ||
776  !strNymID.Compare(pMsg->m_strNymID)) {
777  ++it;
778  continue;
779  }
780  else {
781  theNumList.Add(lTempReqNum);
782  }
783  ++it;
784  }
785  } // else
786 
787  // By this point, theNumList has either been loaded from local storage and
788  // had the number removed,
789  // or it wasn't in local storage and thus we created it and added all the
790  // numbers to it from RAM (not
791  // including the one being erased, since it was already removed from the RAM
792  // list, above.) So either
793  // way, the number being removed is now ABSENT from theNumList.
794  //
795  // Therefore nothing left to do here, but save it back again!
796  //
797  OTString strOutput;
798  theNumList.Output(strOutput);
799  if (!OTDB::StorePlainString(strOutput.Get(), strFolder.Get(),
800  str_data_filename)) {
801  otErr << "OTMessageOutbuffer::RemoveSentMessage: Error: failed writing "
802  "list of request numbers to storage.\n";
803  }
804 
805  // Now that we've updated the numlist in local storage, let's
806  // erase the sent message itself...
807  //
808  OTMessage* pMsg = new OTMessage;
809  OT_ASSERT(nullptr != pMsg);
810  std::unique_ptr<OTMessage> theMsgAngel(pMsg);
811 
812  if (OTDB::Exists(strFolder.Get(), strFile.Get()) &&
813  pMsg->LoadContract(strFolder.Get(), strFile.Get())) {
814  OTDB::EraseValueByKey(strFolder.Get(), strFile.Get());
815  return true;
816  }
817 
818  return bReturnValue;
819 }
820 
822  const OTTransaction& theTransaction)
823 {
824  const int64_t& lRequestNum = theTransaction.GetRequestNum();
825  const OTString strServerID(theTransaction.GetPurportedServerID());
826  const OTString strNymID(theTransaction.GetUserID());
827 
828  return GetSentMessage(lRequestNum, strServerID, strNymID);
829 }
830 
831 // OTMessageOutbuffer deletes the OTMessage when you call this.
832 //
834 {
835  const int64_t& lRequestNum = theTransaction.GetRequestNum();
836  const OTString strServerID(theTransaction.GetPurportedServerID());
837  const OTString strNymID(theTransaction.GetUserID());
838 
839  return RemoveSentMessage(lRequestNum, strServerID, strNymID);
840 }
841 
843 {
844  Clear();
845 }
846 
847 } // namespace opentxs
EXPORT bool StorePlainString(std::string strContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:698
EXPORT OTMessage * GetSentMessage(const int64_t &requestNum, const OTString &serverId, const OTString &nymId)
EXPORT bool RemoveSentMessage(const int64_t &requestNum, const OTString &serverId, const OTString &nymId)
EXPORT bool SaveContract()
EXPORT bool Remove(const int64_t &theValue)
Definition: OTNumList.cpp:309
static EXPORT const char * PathSeparator()
Definition: OTLog.cpp:408
static EXPORT const OTString & Nym()
Definition: OTFolders.cpp:327
const int64_t & GetRequestNum() const
EXPORT void AddSentMessage(OTMessage &message)
EXPORT bool Exists() const
Definition: OTString.cpp:1035
EXPORT void Format(const char *fmt,...)
Definition: OTString.cpp:1319
static EXPORT bool AppendFolder(OTString &out_strPath, const OTString &strBasePath, const OTString &strFolderName)
Definition: OTPaths.cpp:1212
EXPORT bool Compare(const char *compare) const
Definition: OTString.cpp:1102
const OTIdentifier & GetPurportedServerID() const
EXPORT void Clear(const OTString *serverId=nullptr, const OTString *nymId=nullptr, OTPseudonym *nym=nullptr, const bool *harvestingForRetry=nullptr)
EXPORT std::string QueryPlainString(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:728
EXPORT bool Output(std::set< int64_t > &theOutput) const
Definition: OTNumList.cpp:430
#define OT_ASSERT(x)
Definition: Assert.hpp:150
EXPORT bool EraseValueByKey(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:843
virtual EXPORT bool LoadContract()
EXPORT bool CompareID(const OTIdentifier &theIdentifier) const
OTString m_strServerID
Definition: OTMessage.hpp:191
static EXPORT bool ConfirmCreateFolder(const OTString &strExactPath, bool &out_Exists, bool &out_IsNew)
Definition: OTPaths.cpp:921
EXPORT const char * Get() const
Definition: OTString.cpp:1045
OTLOG_IMPORT OTLogStream otErr
OTString m_strRequestNum
Definition: OTMessage.hpp:207
OTASCIIArmor m_ascPayload
Definition: OTMessage.hpp:214
EXPORT bool HarvestTransactionNumbers(OTPseudonym &theNym, bool bHarvestingForRetry, bool bReplyWasSuccess, bool bReplyWasFailure, bool bTransactionWasSuccess, bool bTransactionWasFailure) const
Definition: OTMessage.cpp:158
EXPORT bool Verify(const int64_t &theValue) const
Definition: OTNumList.cpp:322
OTString m_strCommand
Definition: OTMessage.hpp:189
EXPORT bool Exists(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:584
const OTIdentifier & GetUserID() const