2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
27 #include "include/conf.h"
28 #include "include/defs.h"
29 #include "include/stringbuffer.h"
30 #include "include/alias.h"
32 //! The number of users to group in one unit.
33 #define USERS_PER_GROUP 50
35 /*! \brief Group the users in one allocation unit.
36 Structure to store a group of users and reduce the number of memory
39 struct usergroupstruct
41 //! The next group of users.
42 struct usergroupstruct
*next
;
44 struct userinfostruct list
[USERS_PER_GROUP
];
45 //! The number of users stored in the list.
49 /*! \brief Hold pointer to scan through the user list.
53 //! The group containing the user.
54 struct usergroupstruct
*group
;
55 //! The index of the user in the group.
59 //! The first group of users.
60 static struct usergroupstruct
*first_user_group
=NULL
;
61 //! The counter to generate unique user number when ::AnonymousOutputFiles is set.
62 static int AnonymousCounter
=0;
63 //! String buffer to store the user's related constants.
64 static StringBufferObject UserStrings
=NULL
;
66 static AliasObject UserAliases
=NULL
;
68 extern struct ReadLogDataStruct ReadFilter
;
69 extern char StripUserSuffix
[MAX_USER_LEN
];
70 extern int StripSuffixLen
;
71 extern char *userfile
;
73 struct userinfostruct
*userinfo_create(const char *userid
,const char *ip
)
75 struct usergroupstruct
*group
, *last
;
76 struct userinfostruct
*user
;
82 char filename
[MAX_USER_FNAME_LEN
];
85 UserStrings
=StringBuffer_Create();
87 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the user's strings\n"));
93 for (group
=first_user_group
; group
; group
=group
->next
) {
94 if (group
->nusers
<USERS_PER_GROUP
) break;
99 group
=malloc(sizeof(*group
));
101 debuga(__FILE__
,__LINE__
,_("Not enough memory to store user \"%s\"\n"),userid
);
104 memset(group
,0,sizeof(*group
));
108 first_user_group
=group
;
110 user
=group
->list
+group
->nusers
++;
112 user
->id
=StringBuffer_Store(UserStrings
,userid
);
114 debuga(__FILE__
,__LINE__
,_("Not enough memory to store user ID \"%s\"\n"),userid
);
117 user
->label
=user
->id
; //assign a label to avoid a NULL pointer in case none is provided
120 * IP address is not the same as the user's ID. A separate buffer
123 user
->id_is_ip
=false;
124 user
->ip
=StringBuffer_Store(UserStrings
,ip
);
127 * User's IP address share the same buffer as the user's ID.
133 if (AnonymousOutputFiles
) {
134 snprintf(filename
,sizeof(filename
),"%d",AnonymousCounter
++);
138 for (i
=0 ; userid
[i
] && j
<MAX_USER_FNAME_LEN
-1 ; i
++) {
139 if (isalnum(userid
[i
]) || userid
[i
]=='-' || userid
[i
]=='_') {
140 filename
[j
++]=userid
[i
];
149 if (j
==0) filename
[j
++]='_'; //don't leave a file name empty
154 for (group
=first_user_group
; group
; group
=group
->next
) {
155 lastuser
=(group
->next
) ? group
->nusers
: group
->nusers
-1;
156 for (i
=0 ; i
<lastuser
; i
++) {
157 if (strcasecmp(filename
,group
->list
[i
].filename
)==0) {
158 clen
=sprintf(cstr
,"+%X",count
++);
159 if (flen
+clen
<MAX_USER_FNAME_LEN
)
160 strcpy(filename
+flen
,cstr
);
162 strcpy(filename
+MAX_USER_FNAME_LEN
-clen
,cstr
);
167 user
->filename
=StringBuffer_Store(UserStrings
,filename
);
170 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the file name for user \"%s\"\n"),user
->id
);
177 void userinfo_free(void)
179 struct usergroupstruct
*group
, *next
;
181 for (group
=first_user_group
; group
; group
=next
) {
185 first_user_group
=NULL
;
186 StringBuffer_Destroy(&UserStrings
);
190 * Store the user's label.
191 * \param uinfo The user info structure created by userinfo_create().
192 * \param label The string label to store.
194 void userinfo_label(struct userinfostruct
*uinfo
,const char *label
)
197 if (!UserStrings
) return;
198 uinfo
->label
=StringBuffer_Store(UserStrings
,label
);
200 debuga(__FILE__
,__LINE__
,_("Not enough memory to store label \"%s\" of user \"%s\"\n"),label
,uinfo
->id
);
205 struct userinfostruct
*userinfo_find_from_file(const char *filename
)
207 struct usergroupstruct
*group
;
210 for (group
=first_user_group
; group
; group
=group
->next
) {
211 for (i
=0 ; i
<group
->nusers
; i
++)
212 if (strcmp(filename
,group
->list
[i
].filename
)==0)
213 return(group
->list
+i
);
218 struct userinfostruct
*userinfo_find_from_id(const char *id
)
220 struct usergroupstruct
*group
;
223 for (group
=first_user_group
; group
; group
=group
->next
) {
224 for (i
=0 ; i
<group
->nusers
; i
++)
225 if (strcmp(id
,group
->list
[i
].id
)==0)
226 return(group
->list
+i
);
231 struct userinfostruct
*userinfo_find_from_ip(const char *ip
)
233 struct usergroupstruct
*group
;
236 for (group
=first_user_group
; group
; group
=group
->next
) {
237 for (i
=0 ; i
<group
->nusers
; i
++)
238 if (strcmp(ip
,group
->list
[i
].ip
)==0)
239 return(group
->list
+i
);
245 Start the scanning of the user list.
247 \return The object to pass to subsequent scanning functions or NULL
248 if it failed. The object must be freed with a call to userinfo_stop().
250 userscan
userinfo_startscan(void)
254 uscan
=malloc(sizeof(*uscan
));
255 if (!uscan
) return(NULL
);
256 uscan
->group
=first_user_group
;
262 Free the memory allocated by userinfo_start().
264 \param uscan The object created by userinfo_start().
266 void userinfo_stopscan(userscan uscan
)
272 Get the user pointed to by the object and advance the object
275 \param uscan The object created by userinfo_start().
277 \return The user in the list or NULL if the end of the list
280 struct userinfostruct
*userinfo_advancescan(userscan uscan
)
282 struct userinfostruct
*uinfo
;
284 if (!uscan
) return(NULL
);
285 if (!uscan
->group
) return(NULL
);
286 if (uscan
->index
<0 || uscan
->index
>=uscan
->group
->nusers
) return(NULL
);
288 uinfo
=uscan
->group
->list
+uscan
->index
;
291 if (uscan
->index
>=uscan
->group
->nusers
) {
292 uscan
->group
=uscan
->group
->next
;
299 Clear the general purpose flag from all the user's info.
301 void userinfo_clearflag(void)
303 struct usergroupstruct
*group
;
306 for (group
=first_user_group
; group
; group
=group
->next
) {
307 for (i
=0 ; i
<group
->nusers
; i
++)
308 group
->list
[i
].flag
=0;
313 Read the file containing the user names to alias in the report.
315 \param Filename The name of the file.
317 void read_useralias(const char *Filename
)
323 if (debug
) debuga(__FILE__
,__LINE__
,_("Reading user alias file \"%s\"\n"),Filename
);
325 UserAliases
=Alias_Create();
327 debuga(__FILE__
,__LINE__
,_("Cannot store user's aliases\n"));
331 fi
=FileObject_Open(Filename
);
333 debuga(__FILE__
,__LINE__
,_("Cannot read user name alias file \"%s\": %s\n"),Filename
,FileObject_GetLastOpenError());
337 if ((line
=longline_create())==NULL
) {
338 debuga(__FILE__
,__LINE__
,_("Not enough memory to read file \"%s\"\n"),Filename
);
342 while ((buf
=longline_read(fi
,line
)) != NULL
) {
343 if (Alias_Store(UserAliases
,buf
)<0) {
344 debuga(__FILE__
,__LINE__
,_("While reading \"%s\"\n"),Filename
);
349 longline_destroy(&line
);
350 if (FileObject_Close(fi
)) {
351 debuga(__FILE__
,__LINE__
,_("Read error in \"%s\": %s\n"),Filename
,FileObject_GetLastCloseError());
356 debuga(__FILE__
,__LINE__
,_("List of user names to alias:\n"));
357 Alias_PrintList(UserAliases
);
362 Free the memory allocated by read_useralias().
364 void free_useralias(void)
366 Alias_Destroy(&UserAliases
);
370 Replace the user's name or ID by an alias if one is defined.
372 \param user The user's name or ID as extracted from the report.
374 \retval USERERR_NoError No error.
375 \retval USERERR_NameTooLong User name too long.
377 enum UserProcessError
process_user(const char **UserPtr
,const char *IpAddress
,bool *IsIp
)
379 const char *user
=*UserPtr
;
380 static char UserBuffer
[MAX_USER_LEN
];
389 if (StripSuffixLen
>0)
392 if (x
>StripSuffixLen
&& strcasecmp(user
+(x
-StripSuffixLen
),StripUserSuffix
)==0)
394 if (x
-StripSuffixLen
>=sizeof(UserBuffer
))
395 return(USERERR_NameTooLong
);
396 safe_strcpy(UserBuffer
,user
,x
-StripSuffixLen
+1);
400 if (strlen(user
)>MAX_USER_LEN
)
401 return(USERERR_NameTooLong
);
403 if (testvaliduserchar(user
))
404 return(USERERR_InvalidChar
);
406 if ((user
[0]=='\0') || (user
[1]=='\0' && (user
[0]=='-' || user
[0]==' '))) {
407 if(RecordsWithoutUser
== RECORDWITHOUTUSER_IP
) {
411 if (RecordsWithoutUser
== RECORDWITHOUTUSER_IGNORE
)
412 return(USERERR_EmptyUser
);
413 if (RecordsWithoutUser
== RECORDWITHOUTUSER_EVERYBODY
)
416 if (NtlmUserFormat
== NTLMUSERFORMAT_USER
) {
418 if ((str
=strchr(user
,'+'))!=NULL
|| (str
=strchr(user
,'\\'))!=NULL
|| (str
=strchr(user
,'_'))!=NULL
) {
425 if (us
[0]!='\0' && strcmp(user
,us
)!=0)
426 return(USERERR_Untracked
);
428 if (ReadFilter
.SysUsers
) {
429 char wuser
[MAX_USER_LEN
+2]=":";
433 if (strstr(userfile
, wuser
) == 0)
434 return(USERERR_SysUser
);
437 if (ReadFilter
.UserFilter
) {
438 if (!vuexclude(user
)) {
439 if (debugz
>=LogLevel_Process
) debuga(__FILE__
,__LINE__
,_("Excluded user: %s\n"),user
);
440 return(USERERR_Ignored
);
444 auser
=Alias_Replace(UserAliases
,user
);
446 if (*auser
==ALIAS_PREFIX
) auser
++;//no need for that indicator for a user name
452 if (IncludeUsers
[0] != '\0') {
453 char wuser
[MAX_USER_LEN
+2]=":";
458 str
=strstr(IncludeUsers
,wuser
);
460 return(USERERR_Excluded
);
463 if (user
[0]=='\0' || (user
[1]=='\0' && (user
[0]=='-' || user
[0]==' ' || user
[0]==':')))
464 return(USERERR_EmptyUser
);
467 return(USERERR_NoError
);