Updated init script.
[dtbartle/bnbt.git] / tracker_stats.cpp
1 /***\r
2 *\r
3 * BNBT Beta 8.0 - A C++ BitTorrent Tracker\r
4 * Copyright (C) 2003-2004 Trevor Hogan\r
5 *\r
6 * CBTT variations (C) 2003-2005 Harold Feit\r
7 *\r
8 * This library is free software; you can redistribute it and/or\r
9 * modify it under the terms of the GNU Lesser General Public\r
10 * License as published by the Free Software Foundation; either\r
11 * version 2.1 of the License, or (at your option) any later version.\r
12 *\r
13 * This library is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
16 * Lesser General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU Lesser General Public\r
19 * License along with this library; if not, write to the Free Software\r
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
21 *\r
22 ***/\r
23 \r
24 #include "bnbt.h"\r
25 #include "atom.h"\r
26 #include "bencode.h"\r
27 #include "sort.h"\r
28 #include "tracker.h"\r
29 #include "util.h"\r
30 \r
31 void CTracker :: serverResponseStats( struct request_t *pRequest, struct response_t *pResponse, user_t user )\r
32 {\r
33         struct bnbttv btv = UTIL_CurrentTime( );\r
34 \r
35         if( !m_bShowStats )\r
36         {\r
37                 pResponse->strCode = "403 Forbidden";\r
38 \r
39                 return;\r
40         }\r
41 \r
42         pResponse->strCode = "200 OK";\r
43 \r
44         pResponse->mapHeaders.insert( pair<string, string>( "Content-Type", string( "text/html; charset=" ) + gstrCharSet ) );\r
45 \r
46         pResponse->strContent += "<html>\n";\r
47         pResponse->strContent += "<head>\n";\r
48         if ( !gstrTrackerTitle.empty( ) )\r
49                 pResponse->strContent += "<title>" + gstrTrackerTitle + " - File Statistics</title>\n";\r
50         else\r
51                 pResponse->strContent += "<title>BNBT File Statistics</title>\n";\r
52 \r
53         if( !gstrStyle.empty( ) )\r
54                 pResponse->strContent += "<link rel=stylesheet type=\"text/css\" href=\"" + gstrStyle + "\">\n";\r
55 \r
56         pResponse->strContent += "</head>\n";\r
57         pResponse->strContent += "<body>\n";\r
58 \r
59         if( !m_bDisableLogon )\r
60         {\r
61                 if( user.strLogin.empty( ) )\r
62                         pResponse->strContent += "<p class=\"login1_index\">You are not logged in. Click <a href=\"/login.html\">here</a> to login.</p>\n";\r
63                 else\r
64                         pResponse->strContent += "<p class=\"login2_index\">You are logged in as <span class=\"username\">" + UTIL_RemoveHTML( user.strLogin ) + "</span>. Click <a href=\"/login.html?logout=1\">here</a> to logout.</p>\n";\r
65         }\r
66 \r
67         if ( !gstrTrackerTitle.empty( ) )\r
68                 pResponse->strContent += "<h3>" + gstrTrackerTitle + " - File Statistics</h3>\n";\r
69         else\r
70                 pResponse->strContent += "<h3>BNBT File Statistics</h3>\n";\r
71 \r
72         // The Trinity Edition - Addition Begins\r
73         // The following adds an RTT link to the TORRENT STATS page\r
74 \r
75         pResponse->strContent += "<p><a href=\"/index.html\">Return to Tracker</a></p>\n";\r
76 \r
77         // ------------------------------------------------- End of Addition\r
78 \r
79         if( user.iAccess & ACCESS_VIEW )\r
80         {\r
81                 string strHashString = pRequest->mapParams["info_hash"];\r
82                 string strHash = UTIL_StringToHash( strHashString );\r
83 \r
84                 if( !strHash.empty( ) )\r
85                 {\r
86                         //\r
87                         // admin\r
88                         //\r
89 \r
90                         if( user.iAccess & ACCESS_EDIT )\r
91                         {\r
92                                 if( m_pTags )\r
93                                 {\r
94                                         string strName = pRequest->mapParams["name"];\r
95                                         string strTag = pRequest->mapParams["tag"];\r
96                                         string strUploader = pRequest->mapParams["uploader"];\r
97                                         string strInfoLink = pRequest->mapParams["infolink"];\r
98                                         string strOK = pRequest->mapParams["ok"];\r
99 \r
100                                         if( !strName.empty( ) || !strTag.empty( ) || !strUploader.empty( ) || !strInfoLink.empty( ) )\r
101                                         {\r
102                                                 if( strOK == "1" )\r
103                                                 {\r
104                                                         addTag( strHash, strTag, strName, strUploader, strInfoLink );\r
105 \r
106                                                         pResponse->strContent += "<ul>\n";\r
107                                                         pResponse->strContent += "<li>Changed name to \"" + UTIL_RemoveHTML( strName ) + "\" (blank values mean no change).</li>\n";\r
108                                                         pResponse->strContent += "<li>Changed tag to \"" + UTIL_RemoveHTML( strTag ) + "\" (blank values mean no change).</li>\n";\r
109                                                         pResponse->strContent += "<li>Changed uploader to \"" + UTIL_RemoveHTML( strUploader ) + "\" (blank values mean no change).</li>\n";\r
110                                                         pResponse->strContent += "<li>Changed info link to \"" + UTIL_RemoveHTML( strInfoLink ) + "\" (blank values mean no change).</li>\n";\r
111                                                         pResponse->strContent += "</ul>\n";\r
112                                                         pResponse->strContent += "<p>Click <a href=\"/stats.html?info_hash=" + strHashString + "\">here</a> to return to the stats page.</p>\n";\r
113 \r
114                                                         if( m_bGen )\r
115                                                                 pResponse->strContent += "<p class=\"gen\">Generated in " + UTIL_ElapsedTimeStr( btv, UTIL_CurrentTime( ) ) + " seconds.</p>\n";\r
116 \r
117                                                         pResponse->strContent += "</body>\n";\r
118                                                         pResponse->strContent += "</html>\n";\r
119 \r
120                                                         return;\r
121                                                 }\r
122                                                 else\r
123                                                 {\r
124                                                         pResponse->strContent += "<p>Are you sure you want to change the torrent's info?</p>\n";\r
125                                                 // The Trinity Edition - Modification Begins\r
126                                                 // The following replaces the OK response with a YES | NO option\r
127                                                 // when CHANGING A TORRENT'S INFORMATION - name/uploader/infolink\r
128 \r
129                                                 pResponse->strContent += "<p><a href=\"/stats.html?info_hash=" + strHashString + "&name=" + UTIL_StringToEscaped( strName ) + "&uploader=" + UTIL_StringToEscaped( strUploader ) + "&infolink=" + UTIL_StringToEscaped( strInfoLink ) + "&tag=" + UTIL_StringToEscaped( strTag ) + "&ok=1\">";\r
130                         pResponse->strContent += "YES</a> | <a href=\"javascript:history.back()\">NO</a></p>\n";\r
131 \r
132                                                 /* Original Source Code:\r
133                                                 pResponse->strContent += "<p><a href=\"/stats.html?info_hash=" + strHashString + "&name=" + UTIL_StringToEscaped( strName ) + "&uploader=" + UTIL_StringToEscaped( strUploader ) + "&infolink=" + UTIL_StringToEscaped( strInfoLink ) + "&tag=" + UTIL_StringToEscaped( strTag ) + "&ok=1\">OK</a></p>\n";\r
134                                                 */\r
135 \r
136                                                 // ------------------------------------------------- End of Modification\r
137 \r
138                                                         if( m_bGen )\r
139                                                                 pResponse->strContent += "<p class=\"gen\">Generated in " + UTIL_ElapsedTimeStr( btv, UTIL_CurrentTime( ) ) + " seconds.</p>\n";\r
140 \r
141                                                         pResponse->strContent += "</body>\n";\r
142                                                         pResponse->strContent += "</html>\n";\r
143 \r
144                                                         return;\r
145                                                 }\r
146                                         }\r
147 \r
148                                 // The Trinity Edition - Modification Begins\r
149                                 // Horizontal Ruled Line has a 100% width\r
150                                 // Removed "(blank values mean no change)" from each field; This information now appears once after the form\r
151 \r
152                                 pResponse->strContent += "<hr>\n";\r
153                                 pResponse->strContent += "<p>Change Info</p>\n";\r
154                                 pResponse->strContent += "<form method=\"get\" action=\"/stats.html\">\n";\r
155                                 pResponse->strContent += "<input name=\"info_hash\" type=hidden value=\"" + strHashString + "\">\n";\r
156                                 pResponse->strContent += "<input name=\"name\" type=text size=64 maxlength=" + CAtomInt( MAX_FILENAME_LEN ).toString( ) + "> New Name (blank values mean no change)<br><br>\n";\r
157                                 pResponse->strContent += "<input name=\"uploader\" type=text size=64 maxlength=" + CAtomInt( m_iNameLength ).toString( ) + "> New Uploader (blank values mean no change)<br><br>\n";\r
158                                 pResponse->strContent += "<p><input name=\"infolink\" type=text size=64 maxlength=" + CAtomInt( MAX_INFOLINK_LEN ).toString( ) + "> New Info Link (&dagger;&Dagger;)</p>\n";\r
159 \r
160                                 /* Original Source Code:\r
161                                 pResponse->strContent += "<hr width=\"80%\">\n";\r
162                                 pResponse->strContent += "<p>Change Info</p>\n";\r
163                                 pResponse->strContent += "<form method=\"get\" action=\"/stats.html\">\n";\r
164                                 pResponse->strContent += "<input name=\"info_hash\" type=hidden value=\"" + strHashString + "\">\n";\r
165                                 pResponse->strContent += "<input name=\"name\" type=text size=64 maxlength=" + CAtomInt( MAX_FILENAME_LEN ).toString( ) + "> New Name (blank values mean no change)<br><br>\n";\r
166                                 pResponse->strContent += "<input name=\"uploader\" type=text size=64 maxlength=" + CAtomInt( MAX_NAME_LEN ).toString( ) + "> New Uploader (blank values mean no change)<br><br>\n";\r
167                                 pResponse->strContent += "<input name=\"infolink\" type=text size=64 maxlength=" + CAtomInt( MAX_INFOLINK_LEN ).toString( ) + "> New Info Link (blank values mean no change)<br><br>\n";\r
168                                 */\r
169 \r
170                                 // ------------------------------------------------- End of Modification\r
171 \r
172                                         string strCur;\r
173 \r
174                                         CAtom *pTagInfo = m_pTags->getItem( strHash );\r
175 \r
176                                         if( pTagInfo && pTagInfo->isDicti( ) )\r
177                                         {\r
178                                                 CAtom *pCur = ( (CAtomDicti *)pTagInfo )->getItem( "tag" );\r
179 \r
180                                                 if( pCur )\r
181                                                         strCur = pCur->toString( );\r
182                                         }\r
183 \r
184                                         if( !m_vecTags.empty( ) )\r
185                                                 pResponse->strContent += "<select name=\"tag\">\n";\r
186 \r
187                                         for( vector< pair<string, string> > :: iterator i = m_vecTags.begin( ); i != m_vecTags.end( ); i++ )\r
188                                         {\r
189                                                 pResponse->strContent += "<option";\r
190 \r
191                                                 if( (*i).first == strCur )\r
192                                                         pResponse->strContent += " selected";\r
193 \r
194                                                 pResponse->strContent += ">" + (*i).first + "\n";\r
195                                         }\r
196 \r
197                                         if( !m_vecTags.empty( ) )\r
198                                                 pResponse->strContent += "</select> New Tag<br><br>\n";\r
199 \r
200                                         pResponse->strContent += "<input type=submit value=\"Change Info\">\n";\r
201                                 // The Trinity Edition - Addition Begins\r
202                                 // The following explains how to leave values unchanged\r
203                                 // and how to remove values from the database\r
204 \r
205                                 pResponse->strContent += "<p>&dagger; If this field is left blank, this value will remain unchanged.</p>\n";\r
206                                 pResponse->strContent += "<p>&Dagger; Type <u>REMOVE</u> to clear this value from the database.</p>\n";\r
207 \r
208                                 // ------------------------------------------------- End of Addition\r
209                                         pResponse->strContent += "</form>\n";\r
210                                 // The Trinity Edition - Modification Begins\r
211                                 // Changes the width of the Horizontal Ruled Line to 100%\r
212                                                                                                                                                                 \r
213                                 pResponse->strContent += "<hr>\n";                                                                                                                                                                                                              \r
214 \r
215                                 /* Original Source Code:\r
216                                 pResponse->strContent += "<hr width=\"80%\">\n";\r
217                                 */\r
218                                                                                                                                                                 \r
219                                 // ------------------------------------------------- End of Modification\r
220                                 }\r
221                         }\r
222 \r
223                         // display torrent information list\r
224 \r
225                         string strFileName;\r
226 \r
227                         int64 iSize = 0;\r
228                         int iFiles = 1;\r
229 \r
230                         if( m_pAllowed )\r
231                         {\r
232                                 CAtom *pTorrent = m_pAllowed->getItem( strHash );\r
233 \r
234                                 if( pTorrent && pTorrent->isList( ) )\r
235                                 {\r
236                                         vector<CAtom *> vecTorrent = ( (CAtomList *)pTorrent )->getValue( );\r
237 \r
238                                         if( vecTorrent.size( ) == 6 )\r
239                                         {\r
240                                                 CAtom *pFileName = vecTorrent[0];\r
241                                                 CAtom *pName = vecTorrent[1];\r
242                                                 CAtom *pAdded = vecTorrent[2];\r
243                                                 CAtom *pSize = vecTorrent[3];\r
244                                                 CAtom *pFiles = vecTorrent[4];\r
245                                                 CAtom *pComment = vecTorrent[5];\r
246 \r
247                                                 pResponse->strContent += "<p>File Information</p>\n";\r
248                                                 pResponse->strContent += "<ul>\n";\r
249 \r
250                                                 if( pFileName )\r
251                                                         strFileName = pFileName->toString( );\r
252 \r
253                                                 if( pName )\r
254                                                         pResponse->strContent += "<li><strong>Name:</strong> " + UTIL_RemoveHTML( pName->toString( ) ) + "</li>\n";\r
255 \r
256                                                 pResponse->strContent += "<li><strong>Info Hash:</strong> " + strHashString + "</li>\n";\r
257 \r
258                                                 if( pAdded )\r
259                                                         pResponse->strContent += "<li><strong>Added:</strong> " + pAdded->toString( ) + "</li>\n";\r
260 \r
261                                                 if( pSize && dynamic_cast<CAtomLong *>( pSize ) )\r
262                                                 {\r
263                                                         // cache iSize\r
264 \r
265                                                         iSize = dynamic_cast<CAtomLong *>( pSize )->getValue( );\r
266 \r
267                                                         pResponse->strContent += "<li><strong>Size:</strong> " + UTIL_BytesToString( iSize ) + "</li>\n";\r
268                                                 }\r
269 \r
270                                                 if( pFiles && dynamic_cast<CAtomInt *>( pFiles ) )\r
271                                                 {\r
272                                                         // cache iFiles\r
273 \r
274                                                         iFiles = dynamic_cast<CAtomInt *>( pFiles )->getValue( );\r
275 \r
276                                                         pResponse->strContent += "<li><strong>Files:</strong> " + pFiles->toString( ) + "</li>\n";\r
277                                                 }\r
278 \r
279                                                 pResponse->strContent += "</ul>\n";\r
280 \r
281                                                 if( pComment )\r
282                                                 {\r
283                                                         if( m_bShowFileComment )\r
284                                                         {\r
285                                                                 pResponse->strContent += "<p>File Comment</p>\n";\r
286                                                                 pResponse->strContent += "<table summary=\"file comment\">\n";\r
287                                                                 pResponse->strContent += "<tr><td class=\"com_body\"><code>" + UTIL_RemoveHTML( pComment->toString( ) ) + "</code></td></tr>\n";\r
288                                                                 pResponse->strContent += "</table>\n";\r
289                                                         }\r
290                                                 }\r
291                                         }\r
292                                 }\r
293                         }\r
294 \r
295                         if( m_pAllowed && m_bAllowTorrentDownloads && ( user.iAccess & ACCESS_DL ) )\r
296                         {\r
297                                 pResponse->strContent += "<p><a class=\"download\" href=\"";\r
298 \r
299                                 if( m_strExternalTorrentDir.empty( ) )\r
300                                         pResponse->strContent += "/torrents/" + UTIL_StringToEscapedStrict( strFileName ) + "?info_hash=" + strHashString;\r
301                                         // pResponse->strContent += "/torrent.html?info_hash=" + strHashString;\r
302                                 else\r
303                                         pResponse->strContent += m_strExternalTorrentDir + UTIL_StringToEscapedStrict( strFileName );\r
304 \r
305                                 pResponse->strContent += "\">DOWNLOAD TORRENT</a></p>";\r
306                         }\r
307                         // The Trinity Edition - Addition Begins\r
308                         // The following adds a link to DELETE TORRENT\r
309                         // when viewing the TORRENT'S STATS page\r
310                         // This link is ONLY DISPLAYED when the user had EDIT ACCESS - Moderators and Admins\r
311 \r
312                         if( user.iAccess & ACCESS_EDIT ) {\r
313                                 pResponse->strContent += "<p class=\"deletelink\"><a href=\"/index.html?del=" + strHashString + "\">DELETE THIS TORRENT</a></p>\n";\r
314             }\r
315 \r
316                         // ------------------------------------------------- End of Addition\r
317 \r
318                         if( m_bShowFileContents && iFiles > 1 )\r
319                         {\r
320                                 CAtom *pDecoded = DecodeFile( ( m_strAllowedDir + strFileName ).c_str( ) );\r
321 \r
322                                 if( pDecoded && pDecoded->isDicti( ) )\r
323                                 {\r
324                                         CAtom *pInfo = ( (CAtomDicti *)pDecoded )->getItem( "info" );\r
325 \r
326                                         if( pInfo && pInfo->isDicti( ) )\r
327                                         {\r
328                                                 CAtom *pFiles = ( (CAtomDicti *)pInfo )->getItem( "files" );\r
329 \r
330                                                 if( pFiles && pFiles->isList( ) )\r
331                                                 {\r
332                                                         bool bFound = false;\r
333 \r
334                                                         int iAdded = 0;\r
335 \r
336                                                         vector<CAtom *> *pvecFiles = ( (CAtomList *)pFiles )->getValuePtr( );\r
337 \r
338                                                         for( vector<CAtom *> :: iterator i = pvecFiles->begin( ); i != pvecFiles->end( ); i++ )\r
339                                                         {\r
340                                                                 if( (*i)->isDicti( ) )\r
341                                                                 {\r
342                                                                         CAtom *pPath = ( (CAtomDicti *)(*i) )->getItem( "path" );\r
343                                                                         CAtom *pLen = ( (CAtomDicti *)(*i) )->getItem( "length" );\r
344 \r
345                                                                         if( pPath && pPath->isList( ) )\r
346                                                                         {\r
347                                                                                 if( !bFound )\r
348                                                                                 {\r
349                                                                                         pResponse->strContent += "<p>Contents</p>\n";\r
350                                                                                         pResponse->strContent += "<table summary=\"contents\">\n";\r
351 \r
352                                                                                         bFound = true;\r
353                                                                                 }\r
354 \r
355                                                                                 string strPath;\r
356 \r
357                                                                                 vector<CAtom *> *pvecPath = ( (CAtomList *)pPath )->getValuePtr( );\r
358 \r
359                                                                                 for( vector<CAtom *> :: iterator j = pvecPath->begin( ); j != pvecPath->end( ); j++ )\r
360                                                                                 {\r
361                                                                                         if( j != pvecPath->begin( ) )\r
362                                                                                                 strPath += '/';\r
363 \r
364                                                                                         strPath += (*j)->toString( );\r
365                                                                                 }\r
366 \r
367                                                                                 if( !strPath.empty( ) )\r
368                                                                                 {\r
369                                                                                         if( iAdded % 2 )\r
370                                                                                                 pResponse->strContent += "<tr class=\"even\">";\r
371                                                                                         else\r
372                                                                                                 pResponse->strContent += "<tr class=\"odd\">";\r
373 \r
374                                                                                         pResponse->strContent += "<td class=\"path\">" + UTIL_RemoveHTML( strPath ) + "</td>";\r
375 \r
376                                                                                         if( pLen && pLen->isLong( ) )\r
377                                                                                                 pResponse->strContent += "<td class=\"bytes\">" + UTIL_BytesToString( ( (CAtomLong *)pLen )->getValue( ) ) + "</td>";\r
378 \r
379                                                                                         pResponse->strContent += "</tr>\n";\r
380 \r
381                                                                                         iAdded++;\r
382                                                                                 }\r
383                                                                         }\r
384                                                                 }\r
385                                                         }\r
386 \r
387                                                         if( bFound )\r
388                                                                 pResponse->strContent += "</table>\n";\r
389                                                 }\r
390                                         }\r
391                                 }\r
392 \r
393                                 if( pDecoded )\r
394                                         delete pDecoded;\r
395                         }\r
396 \r
397                         if( m_pDFile )\r
398                         {\r
399                                 CAtom *pPeers = m_pDFile->getItem( strHash );\r
400 \r
401                                 if( pPeers && pPeers->isDicti( ) )\r
402                                 {\r
403                                         map<string, CAtom *> *pmapPeersDicti = ( (CAtomDicti *)pPeers )->getValuePtr( );\r
404 \r
405                                         // add the peers into this structure one by one and sort it afterwards\r
406 \r
407                                         struct peer_t *pPeersT = new struct peer_t[pmapPeersDicti->size( )];\r
408 \r
409                                         unsigned long i = 0;\r
410 \r
411                                         for( map<string, CAtom *> :: iterator it = pmapPeersDicti->begin( ); it != pmapPeersDicti->end( ); it++ )\r
412                                         {\r
413                                                 pPeersT[i].iUpped = 0;\r
414                                                 pPeersT[i].iDowned = 0;\r
415                                                 pPeersT[i].iLeft = 0;\r
416                                                 pPeersT[i].iConnected = 0;\r
417                                                 pPeersT[i].flShareRatio = 0.0;\r
418 \r
419                                                 if( (*it).second->isDicti( ) )\r
420                                                 {\r
421                                                         CAtomDicti *pPeerDicti = (CAtomDicti *)(*it).second;\r
422 \r
423                                                         CAtom *pIP = pPeerDicti->getItem( "ip" );\r
424                                                         CAtom *pUpped = pPeerDicti->getItem( "uploaded" );\r
425                                                         CAtom *pDowned = pPeerDicti->getItem( "downloaded" );\r
426                                                         CAtom *pLef = pPeerDicti->getItem( "left" );\r
427                                                         CAtom *pConn = pPeerDicti->getItem( "connected" );\r
428 \r
429                                                         if( pIP )\r
430                                                         {\r
431                                                                 pPeersT[i].strIP = pIP->toString( );\r
432 \r
433                                                                 // strip ip\r
434 \r
435                                                                 string :: size_type iStart = pPeersT[i].strIP.rfind( "." );\r
436 \r
437                                                                 if( iStart != string :: npos )\r
438                                                                 {\r
439                                                                         // don't strip ip for mods\r
440 \r
441                                                                         if( !( user.iAccess & ACCESS_EDIT ) )\r
442                                                                                 pPeersT[i].strIP = pPeersT[i].strIP.substr( 0, iStart + 1 ) + "xxx";\r
443                                                                 }\r
444                                                         }\r
445 \r
446                                                         if( pUpped && pUpped->isLong( ) )\r
447                                                                 pPeersT[i].iUpped = ( (CAtomLong *)pUpped )->getValue( );\r
448 \r
449                                                         if( pDowned && pDowned->isLong( ) )\r
450                                                         {\r
451                                                                 pPeersT[i].iDowned = ( (CAtomLong *)pDowned )->getValue( );\r
452 \r
453                                                                 if( m_bShowShareRatios )\r
454                                                                 {\r
455                                                                         if( pPeersT[i].iDowned > 0 )\r
456                                                                                 pPeersT[i].flShareRatio = (float)pPeersT[i].iUpped / (float)pPeersT[i].iDowned;\r
457                                                                         else if( pPeersT[i].iUpped == 0 )\r
458                                                                                 pPeersT[i].flShareRatio = 0.0;\r
459                                                                         else\r
460                                                                                 pPeersT[i].flShareRatio = -1.0;\r
461                                                                 }\r
462                                                         }\r
463 \r
464                                                         if( pLef && pLef->isLong( ) )\r
465                                                                 pPeersT[i].iLeft = ( (CAtomLong *)pLef )->getValue( );\r
466 \r
467                                                         if( pConn && pConn->isLong( ) )\r
468                                                                 pPeersT[i].iConnected = GetTime( ) - (unsigned long)( (CAtomLong *)pConn )->getValue( );\r
469                                                 }\r
470 \r
471                                                 i++;\r
472                                         }\r
473 \r
474                                         string strSort = pRequest->mapParams["sort"];\r
475 \r
476                                         if( m_bSort )\r
477                                         {\r
478                                                 if( !strSort.empty( ) )\r
479                                                 {\r
480                                                         int iSort = atoi( strSort.c_str( ) );\r
481 \r
482                                                         if( iSort == SORTP_AUPPED )\r
483                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByUpped );\r
484                                                         else if( iSort == SORTP_ADOWNED )\r
485                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByDowned );\r
486                                                         else if( iSort == SORTP_ALEFT )\r
487                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByLeft );\r
488                                                         else if( iSort == SORTP_ACONNECTED )\r
489                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByConnected );\r
490                                                         else if( iSort == SORTP_ASHARERATIO )\r
491                                                         {\r
492                                                                 if( m_bShowShareRatios )\r
493                                                                         qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByShareRatio );\r
494                                                         }\r
495                                                         else if( iSort == SORTP_DUPPED )\r
496                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), dsortpByUpped );\r
497                                                         else if( iSort == SORTP_DDOWNED )\r
498                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), dsortpByDowned );\r
499                                                         else if( iSort == SORTP_DLEFT )\r
500                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), dsortpByLeft );\r
501                                                         else if( iSort == SORTP_DCONNECTED )\r
502                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), dsortpByConnected );\r
503                                                         else if( iSort == SORTP_DSHARERATIO )\r
504                                                         {\r
505                                                                 if( m_bShowShareRatios )\r
506                                                                         qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), dsortpByShareRatio );\r
507                                                         }\r
508                                                         else\r
509                                                         {\r
510                                                                 // default action is to sort by left\r
511 \r
512                                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByLeft );\r
513                                                         }\r
514                                                 }\r
515                                                 else\r
516                                                 {\r
517                                                         // default action is to sort by left\r
518 \r
519                                                         qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByLeft );\r
520                                                 }\r
521                                         }\r
522                                         else\r
523                                         {\r
524                                                 // sort is disabled, but default action is to sort by left\r
525 \r
526                                                 qsort( pPeersT, pmapPeersDicti->size( ), sizeof( struct peer_t ), asortpByLeft );\r
527                                         }\r
528 \r
529                                         bool bFound = false;\r
530 \r
531                                         int iAdded = 0;\r
532 \r
533                                         //\r
534                                         // seeders\r
535                                         //\r
536 \r
537                                         string :: size_type iCountGoesHere = string :: npos;\r
538 \r
539                                         for( unsigned long i = 0; i < pmapPeersDicti->size( ); i++ )\r
540                                         {\r
541                                                 if( iAdded >= m_iMaxPeersDisplay )\r
542                                                         break;\r
543 \r
544                                                 if( pPeersT[i].iLeft == 0 )\r
545                                                 {\r
546                                                         if( !bFound )\r
547                                                         {\r
548                                                                 // output table headers\r
549 \r
550                                                                 pResponse->strContent += "<p>Seeders</p>\n";\r
551 \r
552                                                                 // to save on calculations, we're going to insert the number of seeders later, keep track of where\r
553 \r
554                                                                 iCountGoesHere = pResponse->strContent.size( ) - strlen( "</p>\n" );\r
555 \r
556                                                                 pResponse->strContent += "<table summary=\"seeders\">\n";\r
557                                                                 pResponse->strContent += "<tr><th class=\"ip\">Peer IP</th><th class=\"bytes\">Uploaded";\r
558 \r
559                                                                 if( m_bSort )\r
560                                                                 {\r
561                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
562                                                                         pResponse->strContent += strHashString;\r
563                                                                         pResponse->strContent += "&sort=";\r
564                                                                         pResponse->strContent += SORTPSTR_AUPPED;\r
565                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
566                                                                         pResponse->strContent += strHashString;\r
567                                                                         pResponse->strContent += "&sort=";\r
568                                                                         pResponse->strContent += SORTPSTR_DUPPED;\r
569                                                                         pResponse->strContent += "\">Z</a>";\r
570                                                                 }\r
571 \r
572                                                                 pResponse->strContent += "</th><th class=\"bytes\">Downloaded";\r
573 \r
574                                                                 if( m_bSort )\r
575                                                                 {\r
576                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
577                                                                         pResponse->strContent += strHashString;\r
578                                                                         pResponse->strContent += "&sort=";\r
579                                                                         pResponse->strContent += SORTPSTR_ADOWNED;\r
580                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
581                                                                         pResponse->strContent += strHashString;\r
582                                                                         pResponse->strContent += "&sort=";\r
583                                                                         pResponse->strContent += SORTPSTR_DDOWNED;\r
584                                                                         pResponse->strContent += "\">Z</a>";\r
585                                                                 }\r
586 \r
587                                                                 pResponse->strContent += "</th><th class=\"connected\">Connected";\r
588 \r
589                                                                 if( m_bSort )\r
590                                                                 {\r
591                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
592                                                                         pResponse->strContent += strHashString;\r
593                                                                         pResponse->strContent += "&sort=";\r
594                                                                         pResponse->strContent += SORTPSTR_ACONNECTED;\r
595                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
596                                                                         pResponse->strContent += strHashString;\r
597                                                                         pResponse->strContent += "&sort=";\r
598                                                                         pResponse->strContent += SORTPSTR_DCONNECTED;\r
599                                                                         pResponse->strContent += "\">Z</a>";\r
600                                                                 }\r
601 \r
602                                                                 pResponse->strContent += "</th>";\r
603 \r
604                                                                 if( m_bShowShareRatios )\r
605                                                                 {\r
606                                                                         pResponse->strContent += "<th class=\"number\">Share Ratio";\r
607 \r
608                                                                         if( m_bSort )\r
609                                                                         {\r
610                                                                                 pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
611                                                                                 pResponse->strContent += strHashString;\r
612                                                                                 pResponse->strContent += "&sort=";\r
613                                                                                 pResponse->strContent += SORTPSTR_ASHARERATIO;\r
614                                                                                 pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
615                                                                                 pResponse->strContent += strHashString;\r
616                                                                                 pResponse->strContent += "&sort=";\r
617                                                                                 pResponse->strContent += SORTPSTR_DSHARERATIO;\r
618                                                                                 pResponse->strContent += "\">Z</a>";\r
619                                                                         }\r
620 \r
621                                                                         pResponse->strContent += "</th>";\r
622                                                                 }\r
623 \r
624                                                                 if( m_bShowAvgULRate )\r
625                                                                         pResponse->strContent += "<th class=\"number\">Average Upload Rate</th>";\r
626 \r
627                                                                 pResponse->strContent += "</tr>\n";\r
628 \r
629                                                                 bFound = true;\r
630                                                         }\r
631 \r
632                                                         // output table rows\r
633 \r
634                                                         if( iAdded % 2 )\r
635                                                                 pResponse->strContent += "<tr class=\"even\">";\r
636                                                         else\r
637                                                                 pResponse->strContent += "<tr class=\"odd\">";\r
638 \r
639                                                         pResponse->strContent += "<td class=\"ip\">" + pPeersT[i].strIP + "</td>";\r
640                                                         pResponse->strContent += "<td class=\"bytes\">" + UTIL_BytesToString( pPeersT[i].iUpped ) + "</td>";\r
641                                                         pResponse->strContent += "<td class=\"bytes\">" + UTIL_BytesToString( pPeersT[i].iDowned ) + "</td>";\r
642                                                         pResponse->strContent += "<td class=\"connected\">" + UTIL_SecondsToString( pPeersT[i].iConnected ) + "</td>";\r
643 \r
644                                                         if( m_bShowShareRatios )\r
645                                                         {\r
646                                                                 pResponse->strContent += "<td class=\"number_";\r
647 \r
648                                                                 if( pPeersT[i].flShareRatio == -1.0 )\r
649                                                                         pResponse->strContent += "green\">";\r
650                                                                 else if( pPeersT[i].flShareRatio < 0.8 )\r
651                                                                         pResponse->strContent += "red\">";\r
652                                                                 else if( pPeersT[i].flShareRatio < 1.2 )\r
653                                                                         pResponse->strContent += "yellow\">";\r
654                                                                 else\r
655                                                                         pResponse->strContent += "green\">";\r
656 \r
657                                                                 // turn the share ratio into a string\r
658 \r
659                                                                 if( pPeersT[i].flShareRatio == -1.0 )\r
660                                                                         pResponse->strContent += "Perfect";\r
661                                                                 else\r
662                                                                 {\r
663                                                                         char szFloat[16];\r
664                                                                         memset( szFloat, 0, sizeof( char ) * 16 );\r
665                                                                         sprintf( szFloat, "%0.3f", pPeersT[i].flShareRatio );\r
666 \r
667                                                                         pResponse->strContent += szFloat;\r
668                                                                 }\r
669 \r
670                                                                 pResponse->strContent += "</td>";\r
671                                                         }\r
672 \r
673                                                         if( m_bShowAvgULRate )\r
674                                                         {\r
675                                                                 pResponse->strContent += "<td class=\"number\">";\r
676 \r
677                                                                 if( pPeersT[i].iConnected > 0 )\r
678                                                                         pResponse->strContent += UTIL_BytesToString( pPeersT[i].iUpped / pPeersT[i].iConnected ) + "/sec";\r
679                                                                 else\r
680                                                                         pResponse->strContent += "N/A";\r
681 \r
682                                                                 pResponse->strContent += "</td>";\r
683                                                         }\r
684 \r
685                                                         pResponse->strContent += "</tr>\n";\r
686 \r
687                                                         iAdded++;\r
688                                                 }\r
689                                         }\r
690 \r
691                                         // insert the number of seeders\r
692 \r
693                                         string strTemp = " (" + CAtomInt( iAdded ).toString( ) + ")";\r
694 \r
695                                         if( iCountGoesHere != string :: npos )\r
696                                                 pResponse->strContent.insert( iCountGoesHere, strTemp );\r
697 \r
698                                         iCountGoesHere = string :: npos;\r
699 \r
700                                         if( bFound )\r
701                                                 pResponse->strContent += "</table>\n";\r
702 \r
703                                         bFound = false;\r
704 \r
705                                         iAdded = 0;\r
706 \r
707                                         //\r
708                                         // leechers\r
709                                         //\r
710 \r
711                                         for( unsigned long i = 0; i < pmapPeersDicti->size( ); i++ )\r
712                                         {\r
713                                                 if( iAdded >= m_iMaxPeersDisplay )\r
714                                                         break;\r
715 \r
716                                                 if( pPeersT[i].iLeft != 0 )\r
717                                                 {\r
718                                                         if( !bFound )\r
719                                                         {\r
720                                                                 // output table headers\r
721 \r
722                                                                 pResponse->strContent += "<p>Leechers</p>\n";\r
723 \r
724                                                                 // to save on calculations, we're going to insert the number of leechers later, keep track of where\r
725 \r
726                                                                 iCountGoesHere = pResponse->strContent.size( ) - strlen( "</p>\n" );\r
727 \r
728                                                                 pResponse->strContent += "<table summary=\"leechers\">\n";\r
729                                                                 pResponse->strContent += "<tr><th class=\"ip\">Peer IP</th><th class=\"bytes\">Uploaded";\r
730 \r
731                                                                 if( m_bSort )\r
732                                                                 {\r
733                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
734                                                                         pResponse->strContent += strHashString;\r
735                                                                         pResponse->strContent += "&sort=";\r
736                                                                         pResponse->strContent += SORTPSTR_AUPPED;\r
737                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
738                                                                         pResponse->strContent += strHashString;\r
739                                                                         pResponse->strContent += "&sort=";\r
740                                                                         pResponse->strContent += SORTPSTR_DUPPED;\r
741                                                                         pResponse->strContent += "\">Z</a>";\r
742                                                                 }\r
743 \r
744                                                                 pResponse->strContent += "</th><th class=\"bytes\">Downloaded";\r
745 \r
746                                                                 if( m_bSort )\r
747                                                                 {\r
748                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
749                                                                         pResponse->strContent += strHashString;\r
750                                                                         pResponse->strContent += "&sort=";\r
751                                                                         pResponse->strContent += SORTPSTR_ADOWNED;\r
752                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
753                                                                         pResponse->strContent += strHashString;\r
754                                                                         pResponse->strContent += "&sort=";\r
755                                                                         pResponse->strContent += SORTPSTR_DDOWNED;\r
756                                                                         pResponse->strContent += "\">Z</a>";\r
757                                                                 }\r
758 \r
759                                                                 if( m_pAllowed && m_bShowLeftAsProgress )\r
760                                                                         pResponse->strContent += "</th><th class=\"bytes\">Progress";\r
761                                                                 else\r
762                                                                         pResponse->strContent += "</th><th class=\"bytes\">Left";\r
763 \r
764                                                                 if( m_bSort )\r
765                                                                 {\r
766                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
767                                                                         pResponse->strContent += strHashString;\r
768                                                                         pResponse->strContent += "&sort=";\r
769                                                                         pResponse->strContent += SORTPSTR_ALEFT;\r
770                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
771                                                                         pResponse->strContent += strHashString;\r
772                                                                         pResponse->strContent += "&sort=";\r
773                                                                         pResponse->strContent += SORTPSTR_DLEFT;\r
774                                                                         pResponse->strContent += "\">Z</a>";\r
775                                                                 }\r
776 \r
777                                                                 pResponse->strContent += "</th><th class=\"connected\">Connected";\r
778 \r
779                                                                 if( m_bSort )\r
780                                                                 {\r
781                                                                         pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
782                                                                         pResponse->strContent += strHashString;\r
783                                                                         pResponse->strContent += "&sort=";\r
784                                                                         pResponse->strContent += SORTPSTR_ACONNECTED;\r
785                                                                         pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
786                                                                         pResponse->strContent += strHashString;\r
787                                                                         pResponse->strContent += "&sort=";\r
788                                                                         pResponse->strContent += SORTPSTR_DCONNECTED;\r
789                                                                         pResponse->strContent += "\">Z</a>";\r
790                                                                 }\r
791 \r
792                                                                 pResponse->strContent += "</th>";\r
793 \r
794                                                                 if( m_bShowShareRatios )\r
795                                                                 {\r
796                                                                         pResponse->strContent += "<th class=\"number\">Share Ratio";\r
797 \r
798                                                                         if( m_bSort )\r
799                                                                         {\r
800                                                                                 pResponse->strContent += "<br><a class=\"sort\" href=\"/stats.html?info_hash=";\r
801                                                                                 pResponse->strContent += strHashString;\r
802                                                                                 pResponse->strContent += "&sort=";\r
803                                                                                 pResponse->strContent += SORTPSTR_ASHARERATIO;\r
804                                                                                 pResponse->strContent += "\">A</a> <a class=\"sort\" href=\"/stats.html?info_hash=";\r
805                                                                                 pResponse->strContent += strHashString;\r
806                                                                                 pResponse->strContent += "&sort=";\r
807                                                                                 pResponse->strContent += SORTPSTR_DSHARERATIO;\r
808                                                                                 pResponse->strContent += "\">Z</a>";\r
809                                                                         }\r
810 \r
811                                                                         pResponse->strContent += "</th>";\r
812                                                                 }\r
813 \r
814                                                                 if( m_bShowAvgULRate )\r
815                                                                         pResponse->strContent += "<th class=\"number\">Average Upload Rate</th>";\r
816 \r
817                                                                 if( m_bShowAvgDLRate )\r
818                                                                         pResponse->strContent += "<th class=\"number\">Average Download Rate</th>";\r
819 \r
820                                                                 pResponse->strContent += "</tr>\n";\r
821 \r
822                                                                 bFound = true;\r
823                                                         }\r
824 \r
825                                                         // output table rows\r
826 \r
827                                                         if( iAdded % 2 )\r
828                                                                 pResponse->strContent += "<tr class=\"even\">";\r
829                                                         else\r
830                                                                 pResponse->strContent += "<tr class=\"odd\">";\r
831 \r
832                                                         pResponse->strContent += "<td class=\"ip\">" + pPeersT[i].strIP + "</td>";\r
833                                                         pResponse->strContent += "<td class=\"bytes\">" + UTIL_BytesToString( pPeersT[i].iUpped ) + "</td>";\r
834                                                         pResponse->strContent += "<td class=\"bytes\">" + UTIL_BytesToString( pPeersT[i].iDowned ) + "</td>";\r
835                                                         pResponse->strContent += "<td class=\"percent\">";\r
836 \r
837                                                         if( m_pAllowed && m_bShowLeftAsProgress )\r
838                                                                 pResponse->strContent += UTIL_BytesToString( iSize - pPeersT[i].iLeft );\r
839                                                         else\r
840                                                                 pResponse->strContent += UTIL_BytesToString( pPeersT[i].iLeft );\r
841 \r
842                                                         if( m_pAllowed )\r
843                                                         {\r
844                                                                 pResponse->strContent += " (";\r
845 \r
846                                                                 int iPercent = 0;\r
847 \r
848                                                                 if( iSize > 0 )\r
849                                                                 {\r
850                                                                         if( m_bShowLeftAsProgress )\r
851                                                                                 iPercent = 100 - (int)( ( (float)pPeersT[i].iLeft / iSize ) * 100 );\r
852                                                                         else\r
853                                                                                 iPercent = (int)( ( (float)pPeersT[i].iLeft / iSize ) * 100 );\r
854                                                                 }\r
855 \r
856                                                                 pResponse->strContent += CAtomInt( iPercent ).toString( ) + "%)";\r
857 \r
858                                                                 if( !m_strImageBarFill.empty( ) && !m_strImageBarTrans.empty( ) )\r
859                                                                 {\r
860                                                                         pResponse->strContent += "<br>";\r
861 \r
862                                                                         if( iPercent < 0 )\r
863                                                                                 iPercent = 0;\r
864                                                                         if( iPercent > 100 )\r
865                                                                                 iPercent = 100;\r
866 \r
867                                                                         if( iPercent > 0 )\r
868                                                                                 pResponse->strContent += "<img src=\"" + m_strImageBarFill + "\" width=" + CAtomInt( iPercent ).toString( ) + " height=8>";\r
869 \r
870                                                                         if( iPercent < 100 )\r
871                                                                                 pResponse->strContent += "<img src=\"" + m_strImageBarTrans + "\" width=" + CAtomInt( 100 - iPercent ).toString( ) + " height=8>";\r
872                                                                 }\r
873                                                         }\r
874 \r
875                                                         pResponse->strContent += "</td><td class=\"connected\">" + UTIL_SecondsToString( pPeersT[i].iConnected ) + "</td>";\r
876 \r
877                                                         if( m_bShowShareRatios )\r
878                                                         {\r
879                                                                 pResponse->strContent += "<td class=\"number_";\r
880 \r
881                                                                 if( pPeersT[i].flShareRatio == -1.0 )\r
882                                                                         pResponse->strContent += "green\">";\r
883                                                                 else if( pPeersT[i].flShareRatio < 0.8 )\r
884                                                                         pResponse->strContent += "red\">";\r
885                                                                 else if( pPeersT[i].flShareRatio < 1.2 )\r
886                                                                         pResponse->strContent += "yellow\">";\r
887                                                                 else\r
888                                                                         pResponse->strContent += "green\">";\r
889 \r
890                                                                 // turn the share ratio into a string\r
891 \r
892                                                                 if( pPeersT[i].flShareRatio == -1.0 )\r
893                                                                         pResponse->strContent += "Perfect";\r
894                                                                 else\r
895                                                                 {\r
896                                                                         char szFloat[16];\r
897                                                                         memset( szFloat, 0, sizeof( char ) * 16 );\r
898                                                                         sprintf( szFloat, "%0.3f", pPeersT[i].flShareRatio );\r
899 \r
900                                                                         pResponse->strContent += szFloat;\r
901                                                                 }\r
902 \r
903                                                                 pResponse->strContent += "</td>";\r
904                                                         }\r
905 \r
906                                                         if( m_bShowAvgULRate )\r
907                                                         {\r
908                                                                 pResponse->strContent += "<td class=\"number\">";\r
909 \r
910                                                                 if( pPeersT[i].iConnected > 0 )\r
911                                                                         pResponse->strContent += UTIL_BytesToString( pPeersT[i].iUpped / pPeersT[i].iConnected ) + "/sec";\r
912                                                                 else\r
913                                                                         pResponse->strContent += "N/A";\r
914 \r
915                                                                 pResponse->strContent += "</td>";\r
916                                                         }\r
917 \r
918                                                         if( m_bShowAvgDLRate )\r
919                                                         {\r
920                                                                 pResponse->strContent += "<td class=\"number\">";\r
921 \r
922                                                                 if( pPeersT[i].iConnected > 0 )\r
923                                                                         pResponse->strContent += UTIL_BytesToString( pPeersT[i].iDowned / pPeersT[i].iConnected ) + "/sec";\r
924                                                                 else\r
925                                                                         pResponse->strContent += "N/A";\r
926 \r
927                                                                 pResponse->strContent += "</td>";\r
928                                                         }\r
929 \r
930                                                         pResponse->strContent += "</tr>\n";\r
931 \r
932                                                         iAdded++;\r
933                                                 }\r
934                                         }\r
935 \r
936                                         // insert the number of leechers\r
937 \r
938                                         strTemp = " (" + CAtomInt( iAdded ).toString( ) + ")";\r
939 \r
940                                         if( iCountGoesHere != string :: npos )\r
941                                                 pResponse->strContent.insert( iCountGoesHere, strTemp );\r
942 \r
943                                         delete [] pPeersT;\r
944 \r
945                                         if( bFound )\r
946                                                 pResponse->strContent += "</table>\n";\r
947                                 }\r
948                         }\r
949                 }\r
950         }\r
951         else\r
952                 pResponse->strContent += "<p class=\"denied\">You are not authorized to view this page.</p>\n";\r
953 \r
954         if( m_bGen )\r
955                 pResponse->strContent += "<p class=\"gen\">Generated in " + UTIL_ElapsedTimeStr( btv, UTIL_CurrentTime( ) ) + " seconds.</p>\n";\r
956 \r
957         pResponse->strContent += "</body>\n";\r
958         pResponse->strContent += "</html>\n";\r
959 }\r