2 Domtool
2 (http
://hcoop
.sf
.net
/)
3 Copyright (C
) 2004-2007 Adam Chlipala
5 This program is free software
; you can redistribute it
and/or
6 modify it under the terms
of the GNU General Public License
7 as published by the Free Software Foundation
; either version
2
8 of the License
, or (at your option
) any later version
.
10 This program is distributed
in the hope that it will be useful
,
11 but WITHOUT ANY WARRANTY
; without even the implied warranty
of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
. See the
13 GNU General Public License for more details
.
15 You should have received a copy
of the GNU General Public License
16 along
with this program
; if not
, write to the Free Software
17 Foundation
, Inc
., 59 Temple Place
- Suite
330, Boston
, MA
02111-1307, USA
.
20 (* Generation
of aggregate per
-user
/per
-vhost web bandwidth statistics
*)
22 structure Webbw
= struct
24 val groupsBase
= Config
.Webalizer
.defaultOutput
25 (* Where to look for grouped user statistics
*)
65 val monthInc
= Time
.fromSeconds (LargeInt
.fromInt
2592000)
69 val now
= Date
.fromTimeLocal (Time
.now ())
73 val now
= Date
.fromTimeLocal t
77 val d
= Date
.fromTimeLocal t
79 if Date
.month d
= Date
.month now
then
80 backupMonth
' (Time
.- (t
, monthInc
))
92 backupMonth (backupMulti (n
-1))
95 case CommandLine
.arguments () of
97 (case Int.fromString n
of
98 NONE
=> raise Fail
"Invalid integer parameter"
101 Date
.fromTimeLocal (backupMulti n
)
103 raise Fail
"Negative parameter")
106 val when
= mots (Date
.month now
) ^
" " ^
Int.toString (Date
.year now
)
109 val inf
= TextIO.openIn (groupsBase ^
Int.toString (Date
.year now
) ^
motn (Date
.month now
) ^
".html")
110 val _
= TextIO.inputLine inf
111 val _
= TextIO.inputLine inf
112 val _
= TextIO.inputLine inf
113 val _
= TextIO.inputLine inf
116 case TextIO.inputLine inf
of
119 case String.tokens
Char.isSpace line
of
120 [hits
, perc
, kb
, kbperc
, url
] =>
122 andalso String.sub (url
, 0) = #
"/"
123 andalso String.sub (url
, 1) = #
"~"
124 andalso String.sub (url
, size url
- 2) = #
"/"
125 andalso String.sub (url
, size url
- 1) = #
"*" then
127 val uname
= String.substring (url
, 2, size url
- 4)
129 loop (((uname
, ["www.hcoop.net"]),
130 valOf (Int.fromString kb
)) :: groups
)
136 val groups
: ((string * string list
) * int) list ref
= ref (loop
[])
137 val _
= TextIO.closeIn inf
140 end handle ex
=> ref
[]
143 case rev (String.tokens (fn ch
=> ch
= #
".") s
) of
146 [] => raise Fail ("SSL goofyness: " ^ s
)
147 | first
:: rest
=> first ^
"_ssl." ^
String.concatWith
"." rest
)
150 fun addGroup (group
, n
, d
, d
') =
152 val groups
' = if List.exists (fn ((x
, _
), _
) => x
= group
) (!groups
) then
153 map (fn v
as ((gr
, ds
), n
') => if gr
= group
then ((gr
, d
' ^
"@" ^ d
:: ds
), n
+ n
') else v
) (!groups
)
155 ((group
, [d
' ^
"@" ^ d
]), n
) :: (!groups
)
160 fun dodir
{node
, host
} =
164 val file
= Config
.Webalizer
.outputDir ^
"/" ^ node ^
"/" ^ host ^
"/index.html"
166 if not (Posix
.FileSys
.access (file
, [])) then
170 val inf
= TextIO.openIn file
178 (TextIO.inputLine inf
;
183 val l
= valOf (TextIO.inputLine inf
)
184 val num
= String.extract (l
, 32, NONE
)
187 if Char.isDigit (String.sub (num
, i
)) then
190 valOf (Int.fromString (String.substring (num
, 0, i
)))
196 case TextIO.inputLine inf
of
199 if Substring
.isSubstring
when (Substring
.full l
) then
206 val tokens
= String.tokens (fn ch
=> ch
= #
".") host
209 "ssl" :: tokens
=> (rev tokens
, true)
210 | _
=> (tokens
, false)
213 host
:: tokens
=> (host
, tokens
)
214 | _
=> raise Fail
"Host name too short"
217 if host
<> Config
.Webalizer
.defaultHost
then
219 val file
= Config
.resultRoot ^
"/" ^ node ^
"/" ^
String.concatWith
"/" (rev tokens
)
220 ^
"/" ^ host ^
"." ^
String.concatWith
"." tokens ^
".vhost"
221 val file
= if ssl
then
226 val inf
= TextIO.openIn file
227 val line
= case TextIO.inputLine inf
of
228 NONE
=> raise Fail ("Empty file: " ^ file
)
231 val user
= case String.tokens
Char.isSpace line
of
233 | _
=> raise Fail ("Bad vhost file format in " ^ file
)
241 (case (ret
, group
) of
242 (SOME ret
, SOME group
) => addGroup (group
, ret
, node
, sslTweak fullHost
)
246 end handle IO
.Io
{name
, function
, ...} => NONE
249 val dir
= Posix
.FileSys
.opendir Config
.Webalizer
.outputDir
252 case Posix
.FileSys
.readdir dir
of
256 val dir
= Posix
.FileSys
.opendir (OS
.Path
.joinDirFile
{dir
= Config
.Webalizer
.outputDir
,
260 case Posix
.FileSys
.readdir dir
of
263 case dodir
{node
= d
, host
= d
'} of
265 | SOME n
=> loop
' (((d
, sslTweak d
'), n
) :: L
)
276 fun sort ls
= ListMergeSort
.sort (fn ((_
, n1
), (_
, n2
)) => n1
< n2
) ls
280 val groups
= sort (!groups
)
282 val sum
= List.foldl (fn ((_
, n
), s
) => s
+n
) 0 doms
284 print ("TOTAL: " ^
Int.toString sum ^
"\n\n");
285 List.app (fn ((node
, host
), n
) => print (host ^
"@" ^ node ^
": " ^
Int.toString n ^
"\n")) doms
;
287 List.app (fn ((d
, ds
), n
) => print (d ^
"[" ^
String.concatWith
"," ds ^
"]: " ^
Int.toString n ^
"\n")) groups
;
288 Posix
.FileSys
.closedir dir