Properly html-escape labels
[novacoin.git] / src / qt / transactiondesc.cpp
1 #include <transactiondesc.h>
2
3 #include "guiutil.h"
4
5 #include "headers.h"
6 #include "qtui.h"
7
8 #include <QString>
9
10 // Taken straight from ui.cpp
11 // TODO: Convert to use QStrings, Qt::Escape and tr()
12 //   or: refactor and put describeAsHTML() into bitcoin core but that is unneccesary with better
13 //       UI<->core API, no need to put display logic in core.
14
15 using namespace std;
16
17 static string HtmlEscape(const char* psz, bool fMultiLine=false)
18 {
19     int len = 0;
20     for (const char* p = psz; *p; p++)
21     {
22              if (*p == '<') len += 4;
23         else if (*p == '>') len += 4;
24         else if (*p == '&') len += 5;
25         else if (*p == '"') len += 6;
26         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
27         else if (*p == '\n' && fMultiLine) len += 5;
28         else
29             len++;
30     }
31     string str;
32     str.reserve(len);
33     for (const char* p = psz; *p; p++)
34     {
35              if (*p == '<') str += "&lt;";
36         else if (*p == '>') str += "&gt;";
37         else if (*p == '&') str += "&amp;";
38         else if (*p == '"') str += "&quot;";
39         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
40         else if (*p == '\n' && fMultiLine) str += "<br>\n";
41         else
42             str += *p;
43     }
44     return str;
45 }
46
47 static string HtmlEscape(const string& str, bool fMultiLine=false)
48 {
49     return HtmlEscape(str.c_str(), fMultiLine);
50 }
51
52 static string FormatTxStatus(const CWalletTx& wtx)
53 {
54     // Status
55     if (!wtx.IsFinal())
56     {
57         if (wtx.nLockTime < 500000000)
58             return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
59         else
60             return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str());
61     }
62     else
63     {
64         int nDepth = wtx.GetDepthInMainChain();
65         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
66             return strprintf(_("%d/offline?"), nDepth);
67         else if (nDepth < 6)
68             return strprintf(_("%d/unconfirmed"), nDepth);
69         else
70             return strprintf(_("%d confirmations"), nDepth);
71     }
72 }
73
74 string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
75 {
76     string strHTML;
77     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
78     {
79         strHTML.reserve(4000);
80         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
81
82         int64 nTime = wtx.GetTxTime();
83         int64 nCredit = wtx.GetCredit();
84         int64 nDebit = wtx.GetDebit();
85         int64 nNet = nCredit - nDebit;
86
87
88
89         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
90         int nRequests = wtx.GetRequestCount();
91         if (nRequests != -1)
92         {
93             if (nRequests == 0)
94                 strHTML += _(", has not been successfully broadcast yet");
95             else if (nRequests == 1)
96                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
97             else
98                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
99         }
100         strHTML += "<br>";
101
102         strHTML += _("<b>Date:</b> ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "<br>";
103
104
105         //
106         // From
107         //
108         if (wtx.IsCoinBase())
109         {
110             strHTML += _("<b>Source:</b> Generated<br>");
111         }
112         else if (!wtx.mapValue["from"].empty())
113         {
114             // Online transaction
115             if (!wtx.mapValue["from"].empty())
116                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
117         }
118         else
119         {
120             // Offline transaction
121             if (nNet > 0)
122             {
123                 // Credit
124                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
125                 {
126                     if (wallet->IsMine(txout))
127                     {
128                         CBitcoinAddress address;
129                         if (ExtractAddress(txout.scriptPubKey, wallet, address))
130                         {
131                             if (wallet->mapAddressBook.count(address))
132                             {
133                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
134                                 strHTML += _("<b>To:</b> ");
135                                 strHTML += HtmlEscape(address.ToString());
136                                 if (!wallet->mapAddressBook[address].empty())
137                                     strHTML += _(" (yours, label: ") + HtmlEscape(wallet->mapAddressBook[address]) + ")";
138                                 else
139                                     strHTML += _(" (yours)");
140                                 strHTML += "<br>";
141                             }
142                         }
143                         break;
144                     }
145                 }
146             }
147         }
148
149
150         //
151         // To
152         //
153         string strAddress;
154         if (!wtx.mapValue["to"].empty())
155         {
156             // Online transaction
157             strAddress = wtx.mapValue["to"];
158             strHTML += _("<b>To:</b> ");
159             if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty())
160                 strHTML += HtmlEscape(wallet->mapAddressBook[strAddress]) + " ";
161             strHTML += HtmlEscape(strAddress) + "<br>";
162         }
163
164
165         //
166         // Amount
167         //
168         if (wtx.IsCoinBase() && nCredit == 0)
169         {
170             //
171             // Coinbase
172             //
173             int64 nUnmatured = 0;
174             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
175                 nUnmatured += wallet->GetCredit(txout);
176             strHTML += _("<b>Credit:</b> ");
177             if (wtx.IsInMainChain())
178                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
179             else
180                 strHTML += _("(not accepted)");
181             strHTML += "<br>";
182         }
183         else if (nNet > 0)
184         {
185             //
186             // Credit
187             //
188             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
189         }
190         else
191         {
192             bool fAllFromMe = true;
193             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
194                 fAllFromMe = fAllFromMe && wallet->IsMine(txin);
195
196             bool fAllToMe = true;
197             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
198                 fAllToMe = fAllToMe && wallet->IsMine(txout);
199
200             if (fAllFromMe)
201             {
202                 //
203                 // Debit
204                 //
205                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
206                 {
207                     if (wallet->IsMine(txout))
208                         continue;
209
210                     if (wtx.mapValue["to"].empty())
211                     {
212                         // Offline transaction
213                         CBitcoinAddress address;
214                         if (ExtractAddress(txout.scriptPubKey, 0, address))
215                         {
216                             strHTML += _("<b>To:</b> ");
217                             if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
218                                 strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " ";
219                             strHTML += HtmlEscape(address.ToString());
220                             strHTML += "<br>";
221                         }
222                     }
223
224                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
225                 }
226
227                 if (fAllToMe)
228                 {
229                     // Payment to self
230                     int64 nChange = wtx.GetChange();
231                     int64 nValue = nCredit - nChange;
232                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
233                     strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
234                 }
235
236                 int64 nTxFee = nDebit - wtx.GetValueOut();
237                 if (nTxFee > 0)
238                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
239             }
240             else
241             {
242                 //
243                 // Mixed debit transaction
244                 //
245                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
246                     if (wallet->IsMine(txin))
247                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-wallet->GetDebit(txin)) + "<br>";
248                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
249                     if (wallet->IsMine(txout))
250                         strHTML += _("<b>Credit:</b> ") + FormatMoney(wallet->GetCredit(txout)) + "<br>";
251             }
252         }
253
254         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
255
256
257         //
258         // Message
259         //
260         if (!wtx.mapValue["message"].empty())
261             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
262         if (!wtx.mapValue["comment"].empty())
263             strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
264
265         if (wtx.IsCoinBase())
266             strHTML += string() + "<br>" + _("Generated coins must wait 120 blocks before they can be spent.  When you generated this block, it was broadcast to the network to be added to the block chain.  If it fails to get into the chain, it will change to \"not accepted\" and not be spendable.  This may occasionally happen if another node generates a block within a few seconds of yours.") + "<br>";
267
268
269         //
270         // Debug view
271         //
272         if (fDebug)
273         {
274             strHTML += "<hr><br>Debug information<br><br>";
275             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
276                 if(wallet->IsMine(txin))
277                     strHTML += "<b>Debit:</b> " + FormatMoney(-wallet->GetDebit(txin)) + "<br>";
278             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
279                 if(wallet->IsMine(txout))
280                     strHTML += "<b>Credit:</b> " + FormatMoney(wallet->GetCredit(txout)) + "<br>";
281
282             strHTML += "<br><b>Transaction:</b><br>";
283             strHTML += HtmlEscape(wtx.ToString(), true);
284
285             CTxDB txdb("r"); // To fetch source txouts
286
287             strHTML += "<br><b>Inputs:</b>";
288             strHTML += "<ul>";
289             CRITICAL_BLOCK(wallet->cs_mapWallet)
290             {
291                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
292                 {
293                     COutPoint prevout = txin.prevout;
294
295                     CTransaction prev;
296                     if(txdb.ReadDiskTx(prevout.hash, prev))
297                     {
298                         if (prevout.n < prev.vout.size())
299                         {
300                             strHTML += "<li>";
301                             const CTxOut &vout = prev.vout[prevout.n];
302                             CBitcoinAddress address;
303                             if (ExtractAddress(vout.scriptPubKey, 0, address))
304                             {
305                                 if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
306                                     strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " ";
307                                 strHTML += address.ToString();
308                             }
309                             strHTML = strHTML + " Amount=" + FormatMoney(vout.nValue);
310                             strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? "true" : "false") + "</li>";
311                         }
312                     }
313                 }
314             }
315             strHTML += "</ul>";
316         }
317
318
319
320         strHTML += "</font></html>";
321     }
322     return strHTML;
323 }