"Fossies" - the Fresh Open Source Software Archive

Member "sarg-2.4.0/util.c" (24 Dec 2019, 70350 Bytes) of package /linux/privat/sarg-2.4.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "util.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.3.11_vs_2.4.0.

    1 /*
    2  * SARG Squid Analysis Report Generator      http://sarg.sourceforge.net
    3  *                                                            1998, 2015
    4  *
    5  * SARG donations:
    6  *      please look at http://sarg.sourceforge.net/donations.php
    7  * Support:
    8  *     http://sourceforge.net/projects/sarg/forums/forum/363374
    9  * ---------------------------------------------------------------------
   10  *
   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.
   15  *
   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.
   20  *
   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.
   24  *
   25  */
   26 
   27 // #define LEGACY_MY_ATOLL
   28 // #define LEGACY_TESTVALIDUSERCHAR
   29 
   30 #include "include/conf.h"
   31 #include "include/defs.h"
   32 
   33 #if defined(__MINGW32__) && defined(HAVE_DIRECT_H)
   34 #define NO_OLDNAMES 1
   35 #include <direct.h>
   36 #endif
   37 
   38 #if defined(HAVE_BACKTRACE)
   39 #define USE_GETWORD_BACKTRACE 1
   40 #else
   41 #define USE_GETWORD_BACKTRACE 0
   42 #endif
   43 
   44 static char mtab1[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
   45 
   46 //! The list of the HTTP codes to exclude from the report.
   47 static char *excludecode=NULL;
   48 
   49 //! Directory where the images are stored.
   50 char ImageDir[MAXLEN]=IMAGEDIR;
   51 
   52 extern char *CurrentLocale;
   53 
   54 #if USE_GETWORD_BACKTRACE
   55 static void getword_backtrace(void)
   56 {
   57     void *buffer[5];
   58     int i, n;
   59     char **calls;
   60 
   61     n=backtrace(buffer,sizeof(buffer)/sizeof(buffer[0]));
   62     if (n<=0) return;
   63     calls=backtrace_symbols(buffer,n);
   64     if (calls) {
   65         debuga(__FILE__,__LINE__,_("getword backtrace:\n"));
   66         for (i=0 ; i<n ; i++) {
   67             fprintf(stderr,"SARG: %d:%s\n",i+1,calls[i]);
   68         }
   69         free(calls);
   70     }
   71 }
   72 #endif //USE_GETWORD_BACKTRACE
   73 
   74 void getword_start(struct getwordstruct *gwarea, const char *line)
   75 {
   76     gwarea->beginning=line;
   77     gwarea->current=line;
   78     gwarea->modified=0;
   79 }
   80 
   81 void getword_restart(struct getwordstruct *gwarea)
   82 {
   83     if (gwarea->modified) {
   84         debuga(__FILE__,__LINE__,_("Cannot parse again the line as it was modified\n"));
   85         exit(EXIT_FAILURE);
   86     }
   87     gwarea->current=gwarea->beginning;
   88 }
   89 
   90 int getword(char *word, int limit, struct getwordstruct *gwarea, char stop)
   91 {
   92     int x;
   93 
   94     for (x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
   95         if (x>=limit) {
   96             /*
   97              TRANSLATORS: The %s is the name of the function reporting the
   98              error message.
   99              */
  100             debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
  101             debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  102             debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  103             debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  104             word[(limit>0) ? limit-1 : 0]='\0';
  105 #if USE_GETWORD_BACKTRACE
  106             getword_backtrace();
  107 #endif
  108             return(-1);
  109         }
  110         word[x] = gwarea->current[x];
  111     }
  112 
  113     word[x] = '\0';
  114     if (gwarea->current[x]) ++x;
  115     gwarea->current+=x;
  116     return(0);
  117 }
  118 
  119 int getword_limit(char *word, int limit, struct getwordstruct *gwarea, char stop)
  120 {
  121     int x;
  122 
  123     limit--;
  124     for (x=0; x<limit && gwarea->current[x] && gwarea->current[x] != stop ;x++) {
  125         word[x] = gwarea->current[x];
  126     }
  127     word[x] = '\0';
  128     gwarea->current+=x;
  129     while (*gwarea->current && *gwarea->current != stop)  gwarea->current++;
  130     if (*gwarea->current) ++gwarea->current;
  131     return(0);
  132 }
  133 
  134 int getword_multisep(char *word, int limit, struct getwordstruct *gwarea, char stop)
  135 {
  136     int x;
  137 
  138     for (x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
  139         if (x>=limit) {
  140             debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
  141             debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  142             debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  143             debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  144             if (limit>0) word[limit-1]='\0';
  145 #if USE_GETWORD_BACKTRACE
  146             getword_backtrace();
  147 #endif
  148             //exit(EXIT_FAILURE);
  149             return(-1);
  150         }
  151         word[x] = gwarea->current[x];
  152     }
  153 
  154     word[x] = '\0';
  155     while (gwarea->current[x] && gwarea->current[x]==stop) ++x;
  156     gwarea->current+=x;
  157     return(0);
  158 }
  159 
  160 int getword_skip(int limit, struct getwordstruct *gwarea, char stop)
  161 {
  162     int x;
  163 
  164     for (x=0;(gwarea->current[x] && (gwarea->current[x] != stop ));x++) {
  165         if (x>=limit) {
  166             debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
  167             debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  168             debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  169             debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  170 #if USE_GETWORD_BACKTRACE
  171             getword_backtrace();
  172 #endif
  173             return(-1);
  174         }
  175     }
  176 
  177     if (gwarea->current[x]) ++x;
  178     gwarea->current+=x;
  179     return(0);
  180 }
  181 
  182 int getword_atoll(long long int *number, struct getwordstruct *gwarea, char stop)
  183 {
  184     int x;
  185     int sign=+1;
  186     int digit;
  187 
  188     if (gwarea->current[0] == '-') {
  189         gwarea->current++;
  190         sign=-1;
  191     } else if (gwarea->current[0] == '+') {
  192         gwarea->current++;
  193     }
  194     *number=0LL;
  195     for (x=0;isdigit(gwarea->current[x]);x++) {
  196         digit=gwarea->current[x]-'0';
  197         if (*number >= (LLONG_MAX-digit)/10) {
  198             /*
  199              TRANSLATORS: The first %s is the function name (in the source code) where the
  200              overflow is detected.
  201             */
  202             debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
  203             return(-1);
  204         }
  205         *number=(*number * 10) + digit;
  206     }
  207     if (gwarea->current[x] && gwarea->current[x]!=stop) {
  208         /*
  209          TRANSLATORS: The %s is the function name, in the source code, where the problem occured.
  210         */
  211         debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
  212         debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  213         debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  214         debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  215 #if USE_GETWORD_BACKTRACE
  216         getword_backtrace();
  217 #endif
  218         return(-1);
  219     }
  220     *number*=sign;
  221 
  222     if (gwarea->current[x]) ++x;
  223     gwarea->current+=x;
  224     return(0);
  225 }
  226 
  227 int getword_atoi(int *number, struct getwordstruct *gwarea, char stop)
  228 {
  229     int x;
  230     int sign=+1;
  231     int digit;
  232 
  233     if (gwarea->current[0] == '-') {
  234         gwarea->current++;
  235         sign=-1;
  236     } else if (gwarea->current[0] == '+') {
  237         gwarea->current++;
  238     }
  239     *number=0;
  240     for (x=0;isdigit(gwarea->current[x]);x++) {
  241         digit=gwarea->current[x]-'0';
  242         if (*number > (INT_MAX-digit)/10) {
  243             debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
  244             return(-1);
  245         }
  246         *number=(*number * 10) + digit;
  247     }
  248     if (gwarea->current[x] && gwarea->current[x]!=stop) {
  249         debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
  250         debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  251         debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  252         debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  253 #if USE_GETWORD_BACKTRACE
  254         getword_backtrace();
  255 #endif
  256         return(-1);
  257     }
  258     *number*=sign;
  259 
  260     if (gwarea->current[x]) ++x;
  261     gwarea->current+=x;
  262     return(0);
  263 }
  264 
  265 int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
  266 {
  267     int x;
  268     long int sign=+1;
  269     int digit;
  270 
  271     if (gwarea->current[0] == '-') {
  272         gwarea->current++;
  273         sign=-1;
  274     } else if (gwarea->current[0] == '+') {
  275         gwarea->current++;
  276     }
  277     *number=0;
  278     for (x=0;isdigit(gwarea->current[x]);x++) {
  279         digit=gwarea->current[x]-'0';
  280         if (*number > (LONG_MAX-digit)/10) {
  281             debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
  282             return(-1);
  283         }
  284         *number=(*number * 10) + digit;
  285     }
  286     if (gwarea->current[x] && gwarea->current[x]!=stop) {
  287         debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
  288         debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  289         debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  290         debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  291 #if USE_GETWORD_BACKTRACE
  292         getword_backtrace();
  293 #endif
  294         return(-1);
  295     }
  296     *number*=sign;
  297 
  298     if (gwarea->current[x]) ++x;
  299     gwarea->current+=x;
  300     return(0);
  301 }
  302 
  303 int getword_atolu(unsigned long int *number, struct getwordstruct *gwarea, char stop)
  304 {
  305     int x;
  306     int digit;
  307 
  308     if (gwarea->current[0] == '-') {
  309         debuga(__FILE__,__LINE__,_("getword_atolu got a negative number.\n"));
  310         debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  311         debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  312         return(-1);
  313     }
  314     if (gwarea->current[0] == '+') {
  315         gwarea->current++;
  316     }
  317     *number=0;
  318     for (x=0;isdigit(gwarea->current[x]);x++) {
  319         digit=gwarea->current[x]-'0';
  320         if (*number > (ULONG_MAX-digit)/10) {
  321             debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
  322             return(-1);
  323         }
  324         *number=(*number * 10) + digit;
  325     }
  326     if (gwarea->current[x] && gwarea->current[x]!=stop) {
  327         debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
  328         debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
  329         debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
  330         debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
  331 #if USE_GETWORD_BACKTRACE
  332         getword_backtrace();
  333 #endif
  334         return(-1);
  335     }
  336 
  337     if (gwarea->current[x]) ++x;
  338     gwarea->current+=x;
  339     return(0);
  340 }
  341 
  342 
  343 int getword_ptr(char *orig_line,char **word, struct getwordstruct *gwarea, char stop)
  344 {
  345     /*!
  346     \note Why pass the original buffer to the function ? Because we must modify it to
  347     insert the terminating ASCII zero for the word we return and that's not compatible
  348     with getword_restart(). Moreover, getword_start() sometime works on constant strings
  349     so this function require the original buffer to detect any missuse.
  350     */
  351     int x;
  352     int sep;
  353     int start;
  354 
  355     if (orig_line && orig_line!=gwarea->beginning) {
  356         debuga(__FILE__,__LINE__,_("Invalid buffer passed to getword_ptr\n"));
  357         return(-1);
  358     }
  359 
  360     start=(gwarea->current-gwarea->beginning);
  361     if (word && orig_line) *word=orig_line+start;
  362     for (x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++);
  363     sep=(gwarea->current[x]!='\0');
  364     if (word && orig_line) orig_line[start+x] = '\0';
  365     if (sep) ++x;
  366     gwarea->current+=x;
  367     gwarea->modified=1;
  368     return(0);
  369 }
  370 
  371 #define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
  372 long long int my_atoll (const char *nptr)
  373 {
  374     long long int returnval=0LL;
  375     int max_digits = MAXLLL ;
  376 
  377     // Soak up all the white space
  378     while (isspace( *nptr )) {
  379         nptr++;
  380     }
  381 
  382     //For each character left to right
  383     //change the character to a single digit
  384     //multiply what we had before by 10 and add the new digit
  385 
  386     while (--max_digits && isdigit( *nptr ))
  387     {
  388         returnval = ( returnval * 10 ) + ( *nptr++ - '0' ) ;
  389     }
  390 
  391     return returnval;
  392 }
  393 
  394 int is_absolute(const char *path)
  395 {
  396     if (*path=='/') return(1);
  397 #ifdef _WIN32
  398     if (isalpha(path[0]) && path[1]==':') return(1);
  399 #endif
  400     return(0);
  401 }
  402 
  403 int PortableMkDir(const char *path,int mode)
  404 {
  405 #if defined(__linux__)
  406     int mkerror=mkdir(path,mode);
  407 #else //mingw
  408     (void)mode;
  409     int mkerror=_mkdir(path);
  410 #endif
  411     return(mkerror);
  412 }
  413 
  414 /*!
  415  * Recursively create a path by adding missing directory until the whole path is created.
  416  * \param name The path to create.
  417  * \return True if the directory was created or false if it already existed
  418  */
  419 bool my_mkdir(const char *name)
  420 {
  421     char w0[MAXLEN];
  422     int i;
  423     int chars;
  424     bool created = false;
  425     struct stat st;
  426 
  427     if (!is_absolute(name)) {
  428         debuga(__FILE__,__LINE__,_("Invalid path (%s). Please, use absolute paths only.\n"),name);
  429         exit(EXIT_FAILURE);
  430     }
  431 
  432     chars=0;
  433     for (i=0 ; name[i] ; i++) {
  434         if (i>=sizeof(w0)) {
  435             debuga(__FILE__,__LINE__,_("Path too long: "));
  436             debuga_more("%s\n",name);
  437             exit(EXIT_FAILURE);
  438         }
  439         if (chars>0 && name[i] == '/') {
  440             w0[i] = '\0';
  441             if (access(w0, R_OK) != 0) {
  442                 if (PortableMkDir(w0,0755)) {
  443                     debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),w0,strerror(errno));
  444                     exit(EXIT_FAILURE);
  445                 }
  446             }
  447         }
  448         if (name[i] != '/') chars++;
  449         w0[i] = name[i];
  450     }
  451 
  452     if (access(name, R_OK) != 0) {
  453         if (PortableMkDir(name,0755)) {
  454             debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),name,strerror(errno));
  455             exit(EXIT_FAILURE);
  456         }
  457         created = true;
  458     }
  459     if (!created) {
  460         /*
  461          * Check the final path is a directory (symlink to a directory is ok).
  462          */
  463         if (stat(name, &st)) {
  464             debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"), name, strerror(errno));
  465             exit(EXIT_FAILURE);
  466         }
  467         if (!S_ISDIR(st.st_mode)) {
  468             debuga(__FILE__,__LINE__,_("Directory \"%s\" can't be created because the path already exists and is not a directory\n"), name);
  469             exit(EXIT_FAILURE);
  470         }
  471     }
  472     return created;
  473 }
  474 
  475 void makeTmpDir(const char *tmp)
  476 {
  477     /*
  478      * We must ensure the temporary directory is ours. In particular, we must make sure no malicious
  479      * users managed to create or replace the temporary directory with a symlink to a system directory.
  480      * As sarg purges the content of the temporary directory upon exit, should the temporary directory
  481      * be hijacked, sarg could be tricked in deleting system files such as /bin or users files in /home
  482      * or logs in /var/log.
  483      *
  484      * The code first create the temporary directory. If it wasn't created, the content is checked and
  485      * purged if it looks safe to delete every file and directory it contains.
  486      */
  487     if (!my_mkdir(tmp)) {
  488         if (debug) debuga(__FILE__, __LINE__, _("Purging temporary directory \"%s\"\n"), tmp);
  489         emptytmpdir(tmp);
  490     }
  491 }
  492 
  493 void my_lltoa(unsigned long long int n, char *s, int ssize, int len)
  494 {
  495     int i;
  496     int slen = 0;
  497     int j;
  498     char c;
  499 
  500     ssize--;
  501     if (len>ssize) {
  502         debuga(__FILE__,__LINE__,_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len,ssize);
  503         abort();
  504     }
  505 
  506     do {
  507         s[slen++] = (n % 10) + '0';
  508     } while ((n /= 10) > 0 && slen<ssize);
  509     s[slen] = '\0';
  510 
  511     for (i = 0, j = slen-1; i<j; i++, j--) {
  512         c = s[i];
  513         s[i] = s[j];
  514         s[j] = c;
  515     }
  516 
  517     if (len>slen) {
  518         i=len-slen;
  519         for (j=slen; j>=0; j--)
  520             s[j+i]=s[j];
  521         for (j=0 ; j<i ; j++)
  522             s[j]='0';
  523     }
  524 }
  525 
  526 int month2num(const char *month)
  527 {
  528     int m;
  529 
  530     for (m=0 ; m<12 && strcmp(mtab1[m],month) != 0; m++);
  531     return(m);
  532 }
  533 
  534 int builddia(int day, int month, int year)
  535 {
  536     return(year*10000+month*100+day);
  537 }
  538 
  539 /*!
  540 Compare two dates.
  541 
  542 \param date1 The first date to compare.
  543 \param date2 The second date to compare.
  544 
  545 \retval -1 If date1<date2.
  546 \retval 0 If date1==date2.
  547 \retval 1 if date1>date2.
  548 */
  549 int compare_date(const struct tm *date1,const struct tm *date2)
  550 {
  551     if (date1->tm_year<date2->tm_year) return(-1);
  552     if (date1->tm_year>date2->tm_year) return(1);
  553     if (date1->tm_mon<date2->tm_mon) return(-1);
  554     if (date1->tm_mon>date2->tm_mon) return(1);
  555     if (date1->tm_mday<date2->tm_mday) return(-1);
  556     if (date1->tm_mday>date2->tm_mday) return(1);
  557     if (date1->tm_hour<date2->tm_hour) return(-1);
  558     if (date1->tm_hour>date2->tm_hour) return(1);
  559     if (date1->tm_min<date2->tm_min) return(-1);
  560     if (date1->tm_min>date2->tm_min) return(1);
  561     if (date1->tm_sec<date2->tm_sec) return(-1);
  562     if (date1->tm_sec>date2->tm_sec) return(1);
  563     return(0);
  564 }
  565 
  566 void buildymd(const char *dia, const char *mes, const char *ano, char *wdata,int wdata_size)
  567 {
  568     int nmes;
  569 
  570     nmes=month2num(mes);
  571     snprintf(wdata,wdata_size,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
  572 }
  573 
  574 
  575 int conv_month(const char *month)
  576 {
  577     int  x;
  578 
  579     for (x=0; x<12 && strncmp(mtab1[x],month,3)!=0; x++);
  580     return(x+1);
  581 }
  582 
  583 
  584 const char *conv_month_name(int month)
  585 {
  586     static char str[4];
  587 
  588     if (month<1 || month>12) {
  589         snprintf(str,sizeof(str),"%03d",month);
  590         return(str);
  591     }
  592     return(mtab1[month-1]);
  593 }
  594 
  595 /*!
  596 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
  597 
  598 \param msg The printf like message to format.
  599 \param ... The arguments to format in the message.
  600 */
  601 void debuga(const char *File,int Line,const char *msg,...)
  602 {
  603     va_list ap;
  604 
  605     if (debugz>=LogLevel_Source) {
  606         /* The path is removed because every source file is in the same directory.
  607          * There is no point in reporting the full path from the build directory.
  608          */
  609         const char *ptr=strrchr(File,'/');
  610         if (!ptr) ptr=File;
  611         /* TRANSLATORS: This is the prefix to stderr messages when the debug level is
  612          set to display the source file (%s) and the line number (%d). */
  613         fprintf(stderr,_("SARG(%s:%d): "),ptr,Line);
  614     } else {
  615         /* TRANSLATORS: This is the prefix to stderr messages when the debug level
  616          is low. */
  617         fputs(_("SARG: "),stderr);
  618     }
  619     va_start(ap,msg);
  620     vfprintf(stderr,msg,ap);
  621     va_end(ap);
  622 }
  623 
  624 /*!
  625 Write a debug message to stderr. The message is supposed
  626 to be displayed after a message from debuga().
  627 
  628 \param msg The printf like message to format.
  629 \param ... The arguments to format in the message.
  630 */
  631 void debuga_more(const char *msg,...)
  632 {
  633     va_list ap;
  634 
  635     va_start(ap,msg);
  636     vfprintf(stderr,msg,ap);
  637     va_end(ap);
  638 }
  639 
  640 /*!
  641 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
  642 
  643 \param msg The printf like message to format.
  644 \param ... The arguments to format in the message.
  645 */
  646 void debugaz(const char *File,int Line,const char *msg,...)
  647 {
  648     va_list ap;
  649 
  650     if (debugz>=LogLevel_Source) {
  651         /* The path is removed because every source file is in the same directory.
  652          * There is no point in reporting the full path from the build directory.
  653          */
  654         const char *ptr=strrchr(File,'/');
  655         if (!ptr) ptr=File;
  656         /* TRANSLATORS: This is the prefix to information messages when the debug level is
  657          set to display the source file (%s) and the line number (%d). */
  658         fprintf(stderr,_("SARG(%s:%d): (info) "),ptr,Line);
  659     } else {
  660         /* TRANSLATORS: This is the prefix to information messages when the debug level
  661          is low. */
  662         fputs(_("SARG: (info) "),stderr);
  663     }
  664     va_start(ap,msg);
  665     vfprintf(stderr,msg,ap);
  666     va_end(ap);
  667 }
  668 
  669 
  670 char *fixnum(long long int value, int n)
  671 {
  672 #define MAXLEN_FIXNUM 256
  673     char num[MAXLEN_FIXNUM]="";
  674     char buf[MAXLEN_FIXNUM * 2];
  675     char *pbuf;
  676     static char ret[MAXLEN_FIXNUM * 2];
  677     char *pret;
  678     register int i, j, k;
  679     int numlen;
  680     static char abbrev[30]="";
  681 
  682     my_lltoa(value, num, sizeof(num), 0);
  683 
  684     if (DisplayedValues==DISPLAY_ABBREV) {
  685         numlen = strlen(num);
  686         if (numlen <= 3)
  687             strcpy(abbrev,num);
  688         else if (numlen%3 == 1) {
  689             abbrev[0]=num[0];
  690             abbrev[1]=(UseComma) ? ',' : '.';
  691             abbrev[2]=num[1];
  692             abbrev[3]=num[2];
  693             abbrev[4]='\0';
  694         }
  695         else if (numlen%3 == 2) {
  696             abbrev[0]=num[0];
  697             abbrev[1]=num[1];
  698             abbrev[2]=(UseComma) ? ',' : '.';
  699             abbrev[3]=num[2];
  700             abbrev[4]=num[3];
  701             abbrev[5]='\0';
  702         }
  703         else if (numlen%3 == 0) {
  704             abbrev[0]=num[0];
  705             abbrev[1]=num[1];
  706             abbrev[2]=num[2];
  707             abbrev[3]=(UseComma) ? ',' : '.';
  708             abbrev[4]=num[3];
  709             abbrev[5]=num[4];
  710             abbrev[6]='\0';
  711         }
  712         if (n) {
  713             if (numlen <= 3) {
  714                 //no prefix
  715             }
  716             else if (numlen <= 6)
  717                 strcat(abbrev,"K");
  718             else if (numlen <= 9)
  719                 strcat(abbrev,"M");
  720             else if (numlen <= 12)
  721                 strcat(abbrev,"G");
  722             else if (numlen <= 15)
  723                 strcat(abbrev,"T");
  724             else if (numlen >= 18)
  725                 strcat(abbrev,"P");
  726             else if (numlen <= 21)
  727                 strcat(abbrev,"E");
  728             else if (numlen <= 24)
  729                 strcat(abbrev,"Z");
  730             else if (numlen <= 27)
  731                 strcat(abbrev,"Y");
  732             else
  733                 strcat(abbrev,"???");
  734         }
  735         return(abbrev);
  736     }
  737 
  738     memset(buf,0,MAXLEN_FIXNUM*2);
  739 
  740     pbuf = buf;
  741     pret = ret;
  742     k = 0;
  743 
  744     for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
  745         if ( k == 2 && i != 0 )  {
  746             k = 0;
  747             pbuf[j++] = num[i];
  748             pbuf[j++] = (UseComma) ? ',' : '.';
  749             continue;
  750         }
  751         pbuf[j] = num[i];
  752         j++;
  753         k++;
  754     }
  755 
  756     pret[0]='\0';
  757 
  758     for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
  759         pret[j] = pbuf[i];
  760 
  761     pret[j] = '\0';
  762 
  763     return pret;
  764 }
  765 
  766 
  767 char *fixnum2(long long int value, int n)
  768 {
  769 #define MAXLEN_FIXNUM2 1024
  770     char num[MAXLEN_FIXNUM2];
  771     char buf[MAXLEN_FIXNUM2 * 2];
  772     char *pbuf;
  773     static char ret[MAXLEN_FIXNUM2 * 2];
  774     char *pret;
  775     register int i, j, k;
  776 
  777     my_lltoa(value, num, sizeof(num), 0);
  778     memset(buf,0,MAXLEN_FIXNUM2*2);
  779 
  780     pbuf = buf;
  781     pret = ret;
  782     k = 0;
  783 
  784     for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
  785         if ( k == 2 && i != 0 )  {
  786             k = 0;
  787             pbuf[j++] = num[i];
  788             pbuf[j++] = (UseComma) ? ',' : '.';
  789             continue;
  790         }
  791         pbuf[j] = num[i];
  792         j++;
  793         k++;
  794     }
  795 
  796     pret[0]='\0';
  797 
  798     for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
  799         pret[j] = pbuf[i];
  800 
  801     pret[j] = '\0';
  802 
  803     return pret;
  804 }
  805 
  806 
  807 char *buildtime(long long int elap)
  808 {
  809     long int num = elap / 1000LL;
  810     int hor = 0;
  811     int min = 0;
  812     int sec = 0;
  813     static char buf[20];
  814 
  815     hor=num / 3600L;
  816     min=(num % 3600L) / 60L;
  817     sec=num % 60L;
  818     snprintf(buf,sizeof(buf),"%02d:%02d:%02d",hor,min,sec);
  819 
  820     return(buf);
  821 }
  822 
  823 
  824 /*!
  825 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
  826 
  827 \param dirname The directory to look for the connection directory.
  828 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
  829 \param data The buffer to store the content of the file. It must be more than 80
  830 bytes long.
  831 
  832 \retval 0 No error.
  833 \retval -1 File not found.
  834 */
  835 int obtdate(const char *dirname, const char *name, char *data)
  836 {
  837     FILE *fp_in;
  838     char wdir[MAXLEN];
  839 
  840     if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-date",dirname,name)>=sizeof(wdir)) {
  841         debuga(__FILE__,__LINE__,_("Buffer too small to store "));
  842         debuga_more("%s%s/sarg-date",dirname,name);
  843         exit(EXIT_FAILURE);
  844     }
  845     if ((fp_in = fopen(wdir, "rt")) == 0) {
  846         if (snprintf(wdir,sizeof(wdir),"%s%s/date",dirname,name)>=sizeof(wdir)) {
  847             debuga(__FILE__,__LINE__,_("Buffer too small to store "));
  848             debuga_more("%s%s/date",dirname,name);
  849             exit(EXIT_FAILURE);
  850         }
  851         if ((fp_in = fopen(wdir, "rt")) == 0) {
  852             data[0]='\0';
  853             return(-1);
  854         }
  855     }
  856 
  857     if (!fgets(data,80,fp_in)) {
  858         debuga(__FILE__,__LINE__,_("Failed to read the date in file \"%s\"\n"),wdir);
  859         exit(EXIT_FAILURE);
  860     }
  861     if (fclose(fp_in)==EOF) {
  862         debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,strerror(errno));
  863         exit(EXIT_FAILURE);
  864     }
  865     fixendofline(data);
  866 
  867     return(0);
  868 }
  869 
  870 
  871 void formatdate(char *date,int date_size,int year,int month,int day,int hour,int minute,int second,int dst)
  872 {
  873     struct tm ltm;
  874     time_t unixtime;
  875     struct tm *fulltm;
  876 
  877     memset(&ltm,0,sizeof(ltm));
  878     if (year>=1900) ltm.tm_year=year-1900;
  879     if (month>=1 && month<=12) ltm.tm_mon=month-1;
  880     if (day>=1 && day<=31) ltm.tm_mday=day;
  881     if (hour>=0 && hour<24) ltm.tm_hour=hour;
  882     if (minute>=0 && minute<60) ltm.tm_min=minute;
  883     if (second>=0 && second<60) ltm.tm_sec=second;
  884     ltm.tm_isdst=dst;
  885     unixtime=mktime(&ltm); //fill the missing entries
  886     fulltm=localtime(&unixtime);
  887     //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
  888     strftime(date,date_size,"%c",fulltm);
  889 }
  890 
  891 
  892 void computedate(int year,int month,int day,struct tm *t)
  893 {
  894     memset(t,0,sizeof(*t));
  895     t->tm_year=year-1900;
  896     t->tm_mon=month-1;
  897     t->tm_mday=day;
  898 }
  899 
  900 
  901 int obtuser(const char *dirname, const char *name)
  902 {
  903     FILE *fp_in;
  904     char wdir[MAXLEN];
  905     char tuser[20];
  906     int nuser;
  907 
  908     if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-users",dirname,name)>=sizeof(wdir)) {
  909         debuga(__FILE__,__LINE__,_("Buffer too small to store "));
  910         debuga_more("%s%s/sarg-users",dirname,name);
  911         exit(EXIT_FAILURE);
  912     }
  913     if ((fp_in=fopen(wdir,"r"))==NULL) {
  914         if (snprintf(wdir,sizeof(wdir),"%s%s/users",dirname,name)>=sizeof(wdir)) {
  915             debuga(__FILE__,__LINE__,_("Buffer too small to store "));
  916             debuga_more("%s%s/users",dirname,name);
  917             exit(EXIT_FAILURE);
  918         }
  919         if ((fp_in=fopen(wdir,"r"))==NULL) {
  920             return(0);
  921         }
  922     }
  923 
  924     if (!fgets(tuser,sizeof(tuser),fp_in)) {
  925         debuga(__FILE__,__LINE__,_("Failed to read the number of users in file \"%s\"\n"),wdir);
  926         exit(EXIT_FAILURE);
  927     }
  928     if (fclose(fp_in)==EOF) {
  929         debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,strerror(errno));
  930         exit(EXIT_FAILURE);
  931     }
  932     nuser=atoi(tuser);
  933 
  934     return(nuser);
  935 }
  936 
  937 
  938 void obttotal(const char *dirname, const char *name, int nuser, long long int *tbytes, long long int *media)
  939 {
  940     FileObject *fp_in;
  941     char *buf;
  942     char wdir[MAXLEN];
  943     char user[MAX_USER_LEN];
  944     char sep;
  945     struct getwordstruct gwarea;
  946     longline line;
  947 
  948     *tbytes=0;
  949     *media=0;
  950 
  951     if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-general",dirname,name)>=sizeof(wdir)) {
  952         debuga(__FILE__,__LINE__,_("Buffer too small to store "));
  953         debuga_more("%s%s/sarg-general",dirname,name);
  954         exit(EXIT_FAILURE);
  955     }
  956     if ((fp_in = FileObject_Open(wdir)) == NULL) {
  957         if (snprintf(wdir,sizeof(wdir),"%s%s/general",dirname,name)>=sizeof(wdir)) {
  958             debuga(__FILE__,__LINE__,_("Buffer too small to store "));
  959             debuga_more("%s%s/general",dirname,name);
  960             exit(EXIT_FAILURE);
  961         }
  962         if ((fp_in = FileObject_Open(wdir)) == NULL) {
  963             return;
  964         }
  965     }
  966 
  967     if ((line=longline_create())==NULL) {
  968         debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wdir);
  969         exit(EXIT_FAILURE);
  970     }
  971 
  972     while((buf=longline_read(fp_in,line))!=NULL) {
  973         if (strncmp(buf,"TOTAL\t",6) == 0)
  974             sep='\t'; //new file
  975         else if (strncmp(buf,"TOTAL ",6) == 0)
  976             sep=' '; //old file
  977         else
  978             continue;
  979         getword_start(&gwarea,buf);
  980         if (getword(user,sizeof(user),&gwarea,sep)<0) {
  981             debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),wdir);
  982             exit(EXIT_FAILURE);
  983         }
  984         if (strcmp(user,"TOTAL") != 0)
  985             continue;
  986         if (getword_skip(MAXLEN,&gwarea,sep)<0) {
  987             debuga(__FILE__,__LINE__,_("Invalid total number of accesses in file \"%s\"\n"),wdir);
  988             exit(EXIT_FAILURE);
  989         }
  990         if (getword_atoll(tbytes,&gwarea,sep)<0) {
  991             debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),wdir);
  992             exit(EXIT_FAILURE);
  993         }
  994         break;
  995     }
  996     if (FileObject_Close(fp_in)) {
  997         debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,FileObject_GetLastCloseError());
  998         exit(EXIT_FAILURE);
  999     }
 1000     longline_destroy(&line);
 1001 
 1002     if (nuser <= 0)
 1003         return;
 1004 
 1005     *media=*tbytes / nuser;
 1006     return;
 1007 }
 1008 
 1009 int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
 1010 {
 1011     const char *str;
 1012     int day0, month0, year0, hour0, minute0;
 1013     int day1, month1, year1, hour1, minute1;
 1014     int i;
 1015 
 1016     memset(period,0,sizeof(*period));
 1017 
 1018     str=arqtt;
 1019     while((str=strstr(str,"sarg-"))!=NULL) {
 1020         str+=5;
 1021         if (!isdigit(str[0]) || !isdigit(str[1])) continue;
 1022         day0=(str[0]-'0')*10+(str[1]-'0');
 1023         if (day0<1 || day0>31) continue;
 1024         str+=2;
 1025         month0=(str[0]-'0')*10+(str[1]-'0')-1;
 1026         if (month0<0 || month0>11) continue;
 1027         str+=2;
 1028         year0=0;
 1029         for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
 1030         if (i!=4 || year0<1900) continue;
 1031         str+=4;
 1032         if (str[0]!='_') continue;
 1033         str++;
 1034 
 1035         if (!isdigit(str[0]) || !isdigit(str[1])) continue;
 1036         hour0=(str[0]-'0')*10+(str[1]-'0');
 1037         str+=2;
 1038         if (!isdigit(str[0]) || !isdigit(str[1])) continue;
 1039         minute0=(str[0]-'0')*10+(str[1]-'0');
 1040         str+=2;
 1041 
 1042         if (*str != '-') continue;
 1043         str++;
 1044 
 1045         if (!isdigit(str[0]) || !isdigit(str[1])) continue;
 1046         day1=(str[0]-'0')*10+(str[1]-'0');
 1047         if (day1<1 || day1>31) continue;
 1048         str+=2;
 1049         month1=(str[0]-'0')*10+(str[1]-'0')-1;
 1050         if (month1<0 || month1>11) continue;
 1051         str+=2;
 1052         year1=0;
 1053         for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
 1054         if (i!=4 || year1<1900) continue;
 1055         str+=4;
 1056 
 1057         if (str[0]!='_') continue;
 1058         str++;
 1059 
 1060         if (!isdigit(str[0]) || !isdigit(str[1])) continue;
 1061         hour1=(str[0]-'0')*10+(str[1]-'0');
 1062         str+=2;
 1063         if (!isdigit(str[0]) || !isdigit(str[1])) continue;
 1064         minute1=(str[0]-'0')*10+(str[1]-'0');
 1065         str+=2;
 1066 
 1067         period->start.tm_mday=day0;
 1068         period->start.tm_mon=month0;
 1069         period->start.tm_year=year0-1900;
 1070         period->start.tm_hour=hour0;
 1071         period->start.tm_min=minute0;
 1072         period->end.tm_mday=day1;
 1073         period->end.tm_mon=month1;
 1074         period->end.tm_year=year1-1900;
 1075         period->end.tm_hour=hour1;
 1076         period->end.tm_min=minute1;
 1077         return(0);
 1078     }
 1079     return(-1);
 1080 }
 1081 
 1082 /*!
 1083 Fill the period with the specified range.
 1084 
 1085 \param period The period to change.
 1086 \param ReadFilter Filter containing the date range to write into the period.
 1087 */
 1088 void getperiod_fromrange(struct periodstruct *period,const struct ReadLogDataStruct *ReadFilter)
 1089 {
 1090     int dfrom=ReadFilter->StartDate;
 1091     int duntil=ReadFilter->EndDate;
 1092 
 1093     memset(&period->start,0,sizeof(period->start));
 1094     period->start.tm_mday=dfrom%100;
 1095     period->start.tm_mon=(dfrom/100)%100-1;
 1096     period->start.tm_year=(dfrom/10000)-1900;
 1097 
 1098     memset(&period->end,0,sizeof(period->end));
 1099     period->end.tm_mday=duntil%100;
 1100     period->end.tm_mon=(duntil/100)%100-1;
 1101     period->end.tm_year=(duntil/10000)-1900;
 1102 }
 1103 
 1104 /*!
 1105 Get the range from a period.
 1106 
 1107 \param period The period to convert to a range.
 1108 \param dfrom The variable to store the range beginning. It can be NULL.
 1109 \param duntil The variable to store the range end. It can be NULL.
 1110 */
 1111 void getperiod_torange(const struct periodstruct *period,int *dfrom,int *duntil)
 1112 {
 1113     if (dfrom)
 1114         *dfrom=(period->start.tm_year+1900)*10000+(period->start.tm_mon+1)*100+period->start.tm_mday;
 1115     if (duntil)
 1116         *duntil=(period->end.tm_year+1900)*10000+(period->end.tm_mon+1)*100+period->end.tm_mday;
 1117 }
 1118 
 1119 /*!
 1120 Update the \a main period to encompass the period in \a candidate.
 1121 */
 1122 void getperiod_merge(struct periodstruct *main,struct periodstruct *candidate)
 1123 {
 1124     int cdate;
 1125     int mdate;
 1126 
 1127     mdate=(main->start.tm_year)*10000+(main->start.tm_mon)*100+main->start.tm_mday;
 1128     cdate=(candidate->start.tm_year)*10000+(candidate->start.tm_mon)*100+candidate->start.tm_mday;
 1129     if (mdate==0 || cdate<mdate) memcpy(&main->start,&candidate->start,sizeof(struct tm));
 1130 
 1131     mdate=(main->end.tm_year)*10000+(main->end.tm_mon)*100+main->end.tm_mday;
 1132     cdate=(candidate->end.tm_year)*10000+(candidate->end.tm_mon)*100+candidate->end.tm_mday;
 1133     if (cdate>mdate) memcpy(&main->end,&candidate->end,sizeof(struct tm));
 1134 }
 1135 
 1136 int getperiod_buildtext(struct periodstruct *period)
 1137 {
 1138     int i;
 1139     int range;
 1140     char text1[40], text2[40];
 1141 
 1142     if (df=='u') {
 1143         i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
 1144     } else if (df=='e') {
 1145         i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
 1146     } else /*if (df=='w')*/ {
 1147         IndexTree=INDEX_TREE_FILE;
 1148         i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
 1149     }
 1150     if (i == 0) return(-1);
 1151 
 1152     range=(period->start.tm_year!=period->end.tm_year ||
 1153            period->start.tm_mon!=period->end.tm_mon ||
 1154            period->start.tm_mday!=period->end.tm_mday);
 1155     if (range) {
 1156         if (df=='u') {
 1157             i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
 1158         } else if (df=='e') {
 1159             i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
 1160         } else {
 1161             i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
 1162         }
 1163         if (i == 0) return(-1);
 1164     }
 1165 
 1166     if (range) {
 1167         snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
 1168         snprintf(period->html,sizeof(period->html),"%s&mdash;%s",text1,text2);
 1169     } else {
 1170         safe_strcpy(period->text,text1,sizeof(period->text));
 1171         safe_strcpy(period->html,text1,sizeof(period->html));
 1172     }
 1173     return(0);
 1174 }
 1175 
 1176 static void copy_images(void)
 1177 {
 1178     FILE *img_in, *img_ou;
 1179     char images[512];
 1180     char srcfile[MAXLEN];
 1181     char dstfile[MAXLEN];
 1182     DIR *dirp;
 1183     struct dirent *direntp;
 1184     char buffer[MAXLEN];
 1185     size_t nread;
 1186     struct stat info;
 1187 
 1188     if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
 1189         debuga(__FILE__,__LINE__,_("Cannot copy images to target directory %simages\n"),outdir);
 1190         exit(EXIT_FAILURE);
 1191     }
 1192     if (access(images,R_OK)!=0) {
 1193         if (PortableMkDir(images,0755)) {
 1194             debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),images,strerror(errno));
 1195             exit(EXIT_FAILURE);
 1196         }
 1197     }
 1198 
 1199     dirp = opendir(ImageDir);
 1200     if (dirp==NULL) {
 1201         debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),ImageDir,strerror(errno));
 1202         return;
 1203     }
 1204     while ((direntp = readdir( dirp )) != NULL ){
 1205         if (direntp->d_name[0]=='.')
 1206             continue;
 1207         if (snprintf(srcfile,sizeof(srcfile),"%s/%s",ImageDir,direntp->d_name)>=sizeof(srcfile)) {
 1208             debuga(__FILE__,__LINE__,_("Buffer too small to store "));
 1209             debuga_more("%s/%s",ImageDir,direntp->d_name);
 1210             exit(EXIT_FAILURE);
 1211         }
 1212         if (stat(srcfile,&info)) {
 1213             debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),srcfile,strerror(errno));
 1214             continue;
 1215         }
 1216         if (S_ISREG(info.st_mode)) {
 1217             if (snprintf(dstfile,sizeof(dstfile),"%s/%s",images,direntp->d_name)>=sizeof(dstfile)) {
 1218                 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
 1219                 debuga_more("%s/%s",images,direntp->d_name);
 1220                 exit(EXIT_FAILURE);
 1221             }
 1222             img_in = fopen(srcfile, "rb");
 1223             if (img_in!=NULL) {
 1224                 img_ou = fopen(dstfile, "wb");
 1225                 if (img_ou!=NULL) {
 1226                     while ((nread = fread(buffer,1,sizeof(buffer),img_in))>0) {
 1227                         if (fwrite(buffer,1,nread,img_ou)!=nread) {
 1228                             debuga(__FILE__,__LINE__,_("Failed to copy image \"%s\" to \"%s\"\n"),srcfile,dstfile);
 1229                             break;
 1230                         }
 1231                     }
 1232                     if (fclose(img_ou)==EOF) {
 1233                         debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),dstfile,strerror(errno));
 1234                         exit(EXIT_FAILURE);
 1235                     }
 1236                 } else
 1237                     debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), dstfile, strerror(errno));
 1238                 if (fclose(img_in)==EOF) {
 1239                     debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),srcfile,strerror(errno));
 1240                     exit(EXIT_FAILURE);
 1241                 }
 1242             } else
 1243                 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), srcfile, strerror(errno));
 1244         }
 1245     }
 1246     (void) closedir(dirp);
 1247 
 1248     return;
 1249 }
 1250 
 1251 /*!
 1252  * Check if the proposed file name conforms to the directory structure layed out
 1253  * as a file tree. It is used to check if the file name enumerated while scanning
 1254  * a directory content may have been created by sarg running with IndexTree set to
 1255  * INDEX_TREE_FILE.
 1256  */
 1257 bool IsTreeFileDirName(const char *Name)
 1258 {
 1259     char DateFormat;
 1260     int i;
 1261 
 1262     // start year (date format u) or start day (date format e)
 1263     if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1264 
 1265     if (isdigit(Name[2]) && isdigit(Name[3]))
 1266     {
 1267         // date format is either u or w
 1268         if (Name[4]=='.')
 1269         {
 1270             // date format is w
 1271             if (!isdigit(Name[5]) || !isdigit(Name[6])) return(false);
 1272             return(true);//date format w is confirmed
 1273         }
 1274 
 1275         // date format is u
 1276         Name+=4;
 1277 
 1278         // start month
 1279         if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
 1280         for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
 1281         if (i<0) return(false);
 1282         Name+=3;
 1283 
 1284         // start day
 1285         if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1286         Name+=2;
 1287 
 1288         DateFormat='u';
 1289     }
 1290     else if (isalpha(Name[2]) && isalpha(Name[3]) && isalpha(Name[4]))
 1291     {
 1292         // date format is e
 1293         Name+=2;
 1294 
 1295         // start month
 1296         if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
 1297         for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
 1298         if (i<0) return(false);
 1299         Name+=3;
 1300 
 1301         // start day
 1302         if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
 1303         Name+=4;
 1304 
 1305         DateFormat='e';
 1306     }
 1307     else
 1308         return(false);
 1309 
 1310     if (Name[0]!='-') return(false);
 1311     Name++;
 1312 
 1313     if (DateFormat=='u')
 1314     {
 1315         if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
 1316         Name+=4;
 1317 
 1318         if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
 1319         for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
 1320         if (i<0) return(false);
 1321         Name+=3;
 1322 
 1323         if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1324         Name+=2;
 1325     }
 1326     else //DateFormat=='e'
 1327     {
 1328         if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1329         Name+=2;
 1330 
 1331         if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
 1332         for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
 1333         if (i<0) return(false);
 1334         Name+=3;
 1335 
 1336         if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
 1337         Name+=4;
 1338     }
 1339     /*
 1340      * The directory name may contains additional characters such as a counter if
 1341      * a previous report is never overwritten.
 1342      */
 1343     return(true);
 1344 }
 1345 
 1346 /*!
 1347  * Check if the proposed file name can be the year part of a report tree build with
 1348  * IndexTree set to INDEX_TREE_DATE.
 1349  */
 1350 bool IsTreeYearFileName(const char *Name)
 1351 {
 1352     if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
 1353     Name+=4;
 1354     if (Name[0]=='-')
 1355     {
 1356         Name++;
 1357         if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
 1358         Name+=4;
 1359     }
 1360     if (Name[0]) return(false);
 1361     return(true);
 1362 }
 1363 
 1364 /*!
 1365  * Check if the proposed file name can be the month part of a report tree build with
 1366  * IndexTree set to INDEX_TREE_DATE.
 1367  */
 1368 bool IsTreeMonthFileName(const char *Name)
 1369 {
 1370     int m;
 1371 
 1372     if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1373     m=(Name[0]-'0')*10+(Name[1]-'0');
 1374     if (m<1 || m>12) return(false);
 1375     Name+=2;
 1376     if (Name[0]=='-')
 1377     {
 1378         Name++;
 1379         if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1380         m=(Name[0]-'0')*10+(Name[1]-'0');
 1381         if (m<1 || m>12) return(false);
 1382         Name+=2;
 1383     }
 1384     if (Name[0]) return(false);
 1385     return(true);
 1386 }
 1387 
 1388 /*!
 1389  * Check if the proposed file name can be the day part of a report tree build with
 1390  * IndexTree set to INDEX_TREE_DATE.
 1391  */
 1392 bool IsTreeDayFileName(const char *Name)
 1393 {
 1394     int d;
 1395 
 1396     if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1397     d=(Name[0]-'0')*10+(Name[1]-'0');
 1398     if (d<1 || d>31) return(false);
 1399     if (Name[2]=='-')
 1400     {
 1401         Name+=3;
 1402         if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
 1403         d=(Name[0]-'0')*10+(Name[1]-'0');
 1404         if (d<1 || d>31) return(false);
 1405     }
 1406     /*
 1407      * The directory name may contains additional characters such as a counter if
 1408      * a previous report is never overwritten.
 1409      */
 1410     return(true);
 1411 }
 1412 
 1413 /*!
 1414  * Create a directory to generate a report for the specified connection data
 1415  * and populate it with the a <tt>sarg-date</tt> file containing the current
 1416  * date.
 1417  *
 1418  * The function also create an <tt>images</tt> directory in \a dir and copy all
 1419  * the files from the <tt>SYSCONFDIR/images</tt> into that directory.
 1420  *
 1421  * \param per1 The date range in the form: YYYYMMMDD-YYYYMMMDD or DDMMMYYYY-DDMMMYYYY depending on the value of
 1422  * ::DateFormat.
 1423  * \param addr The ip address or host name to which the report is limited. If the string is empty, all the addresses are accepted.
 1424  * \param site The destination site to which the report is limited. If the string is empty, all the sites are accepted.
 1425  * \param us The user to whom the report is limited. It is an empty string if all the users are accepted.
 1426  */
 1427 int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us)
 1428 {
 1429     FILE *fp_ou;
 1430     char wdir[MAXLEN];
 1431     int y1, y2;
 1432     int m1, m2;
 1433     int d1, d2;
 1434     int wlen, wlen2;
 1435     time_t curtime;
 1436     struct tm *loctm;
 1437 
 1438     strcpy(wdir,outdir);
 1439     wlen=strlen(wdir);
 1440     y1=per1->start.tm_year+1900;
 1441     y2=per1->end.tm_year+1900;
 1442     m1=per1->start.tm_mon+1;
 1443     m2=per1->end.tm_mon+1;
 1444     d1=per1->start.tm_mday;
 1445     d2=per1->end.tm_mday;
 1446     if (IndexTree == INDEX_TREE_DATE) {
 1447         wlen+=sprintf(wdir+wlen,"%04d",y1);
 1448         if (y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
 1449         if (access(wdir, R_OK) != 0)
 1450             my_mkdir(wdir);
 1451 
 1452         wlen+=sprintf(wdir+wlen,"/%02d",m1);
 1453         if (m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
 1454         if (access(wdir, R_OK) != 0)
 1455             my_mkdir(wdir);
 1456 
 1457         wlen+=sprintf(wdir+wlen,"/%02d",d1);
 1458         if (d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
 1459     } else {
 1460         if (df == 'u') {
 1461             wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%04d%s%02d-%04d%s%02d",y1,
 1462                     conv_month_name(m1),d1,y2,conv_month_name(m2),d2);
 1463         } else if (df == 'e') {
 1464             wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%02d%s%04d-%02d%s%04d",d1,
 1465                     conv_month_name(m1),y1,d2,conv_month_name(m2),y2);
 1466         } else if (df == 'w') {
 1467             wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
 1468             if (wlen2==0) return(-1);
 1469             wlen+=wlen2;
 1470         }
 1471     }
 1472 
 1473     if (us[0] != '\0') {
 1474         struct userinfostruct *uinfo=userinfo_find_from_id(us);
 1475         if (uinfo) {
 1476             strcat(wdir,"-");
 1477             strcat(wdir,uinfo->filename);
 1478         }
 1479     }
 1480     if (addr[0] != '\0') {
 1481         strcat(wdir,"-");
 1482         strcat(wdir,addr);
 1483     }
 1484     if (site[0] != '\0') {
 1485         strcat(wdir,"-");
 1486         strcat(wdir,site);
 1487     }
 1488 
 1489     strcpy(outdirname,wdir);
 1490 
 1491     // manufacture a new unique name if configured to keep old reports or overwrite old report if configured to do so
 1492     if (!OverwriteReport) {
 1493         int num=1;
 1494 
 1495         while (access(wdir,R_OK)==0 || errno==EACCES) //file exist or can't be read
 1496         {
 1497             format_path(__FILE__, __LINE__, wdir, sizeof(wdir), "%s.%d", outdirname, num);
 1498             num++;
 1499         }
 1500         if (num>1) {
 1501             if (debug)
 1502                 debuga(__FILE__,__LINE__,_("File \"%s\" already exists, moved to \"%s\"\n"),outdirname,wdir);
 1503             rename(outdirname,wdir);
 1504         }
 1505     } else {
 1506         if (access(outdirname,R_OK) == 0) {
 1507             unlinkdir(outdirname,1);
 1508         }
 1509     }
 1510     my_mkdir(outdirname);
 1511 
 1512     // create sarg-date to keep track of the report creation date
 1513     if (snprintf(wdir,sizeof(wdir),"%s/sarg-date",outdirname)>=sizeof(wdir)) {
 1514         debuga(__FILE__,__LINE__,_("Buffer too small to store "));
 1515         debuga_more("%s/sarg-date",outdirname);
 1516         exit(EXIT_FAILURE);
 1517     }
 1518     if ((fp_ou = fopen(wdir, "wt")) == 0) {
 1519         debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
 1520         perror("SARG:");
 1521         exit(EXIT_FAILURE);
 1522     }
 1523     time(&curtime);
 1524     //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
 1525     loctm=localtime(&curtime);
 1526     strftime(wdir,sizeof(wdir),"%Y-%m-%d %H:%M:%S",loctm);
 1527     if (fprintf(fp_ou,"%s %d\n",wdir,loctm->tm_isdst)<0) {
 1528         debuga(__FILE__,__LINE__,_("Failed to write the date in \"%s\"\n"),wdir);
 1529         perror("SARG:");
 1530         exit(EXIT_FAILURE);
 1531     }
 1532     if (fclose(fp_ou)==EOF) {
 1533         debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdir,strerror(errno));
 1534         exit(EXIT_FAILURE);
 1535     }
 1536 
 1537     copy_images();
 1538     return(0);
 1539 }
 1540 
 1541 /*!
 1542   Copy a string without overflowing the buffer. The copied string
 1543   is properly terminated by an ASCII zero.
 1544 
 1545   \param dest The destination buffer.
 1546   \param src The source buffer.
 1547   \param length The size of the destination buffer. The program is aborted
 1548   if the length is negative or zero.
 1549 */
 1550 void safe_strcpy(char *dest,const char *src,int length)
 1551 {
 1552     if (length<=0) {
 1553         debuga(__FILE__,__LINE__,_("Invalid buffer length passed to the function to safely copy a string\n"));
 1554         exit(EXIT_FAILURE);
 1555     }
 1556     strncpy(dest,src,length-1);
 1557     dest[length-1]='\0';
 1558 }
 1559 
 1560 void strip_latin(char *line)
 1561 {
 1562     int i,j;
 1563     int skip;
 1564 
 1565     j=0;
 1566     skip=0;
 1567     for (i=0;line[i];i++){
 1568         if (skip){
 1569             if (line[i]==';') skip=0;
 1570         } else {
 1571             if (line[i]=='&')
 1572                 skip=1;
 1573             else
 1574                 line[j++]=line[i];
 1575         }
 1576     }
 1577     line[j]='\0';
 1578     return;
 1579 }
 1580 
 1581 void zdate(char *ftime,int ftimesize, char DateFormat)
 1582 {
 1583     time_t t;
 1584     struct tm *local;
 1585 
 1586     t = time(NULL);
 1587     local = localtime(&t);
 1588     if (DateFormat=='u')
 1589         strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
 1590     else if (DateFormat=='e')
 1591         strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
 1592     else if (DateFormat=='w')
 1593         strftime(ftime, ftimesize, "%W-%H-%M", local);
 1594     return;
 1595 }
 1596 
 1597 
 1598 char *fixtime(long long int elap)
 1599 {
 1600     long int num = elap / 1000LL;
 1601     int hor = 0;
 1602     int min = 0;
 1603     int sec = 0;
 1604     static char buf[20];
 1605 
 1606     hor=num / 3600L;
 1607     min=(num % 3600L) / 60L;
 1608     sec=num % 60L;
 1609 
 1610     if (hor==0 && min==0 && sec==0)
 1611         strcpy(buf,"0");
 1612     else
 1613         snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
 1614 
 1615     return buf;
 1616 }
 1617 
 1618 
 1619 void date_from(struct ReadLogDataStruct *ReadFilter)
 1620 {
 1621     int d0=0;
 1622     int m0=0;
 1623     int y0=0;
 1624     int d1=0;
 1625     int m1=0;
 1626     int y1=0;
 1627 
 1628     if (isdigit(ReadFilter->DateRange[0])) {
 1629         int next=-1;
 1630 
 1631         if (sscanf(ReadFilter->DateRange,"%d/%d/%d%n",&d0,&m0,&y0,&next)!=3 || y0<100 || m0<1 || m0>12 || d0<1 || d0>31 || next<0) {
 1632             debuga(__FILE__,__LINE__,_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
 1633             exit(EXIT_FAILURE);
 1634         }
 1635         if (ReadFilter->DateRange[next]=='-') {
 1636             if (sscanf(ReadFilter->DateRange+next+1,"%d/%d/%d",&d1,&m1,&y1)!=3 || y1<100 || m1<1 || m1>12 || d1<1 || d1>31) {
 1637                 debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
 1638                 exit(EXIT_FAILURE);
 1639             }
 1640         } else if (ReadFilter->DateRange[next]!='\0') {
 1641             debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
 1642             exit(EXIT_FAILURE);
 1643         } else {
 1644             d1=d0;
 1645             m1=m0;
 1646             y1=y0;
 1647         }
 1648     } else {
 1649         int i;
 1650         time_t Today,t1;
 1651         struct tm *Date0,Date1;
 1652 
 1653         if (time(&Today)==(time_t)-1) {
 1654             debuga(__FILE__,__LINE__,_("Failed to get the current time\n"));
 1655             exit(EXIT_FAILURE);
 1656         }
 1657         if (sscanf(ReadFilter->DateRange,"day-%d",&i)==1) {
 1658             if (i<0) {
 1659                 debuga(__FILE__,__LINE__,_("Invalid number of days in -d parameter\n"));
 1660                 exit(EXIT_FAILURE);
 1661             }
 1662             Today-=i*24*60*60;
 1663             Date0=localtime(&Today);
 1664             if (Date0==NULL) {
 1665                 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
 1666                 exit(EXIT_FAILURE);
 1667             }
 1668             y0=y1=Date0->tm_year+1900;
 1669             m0=m1=Date0->tm_mon+1;
 1670             d0=d1=Date0->tm_mday;
 1671         } else if (sscanf(ReadFilter->DateRange,"week-%d",&i)==1) {
 1672             /*
 1673             There is no portable way to find the first day of the week even though the
 1674             information is available in the locale. nl_langinfo has the unofficial
 1675             parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
 1676             undocumented as is their return value and it is discouraged to use them.
 1677             Beside, nl_langinfo isn't available on windows and the first day of the
 1678             week isn't available at all on that system.
 1679             */
 1680             const int FirstWeekDay=1;
 1681             time_t WeekBegin;
 1682 
 1683             if (i<0) {
 1684                 debuga(__FILE__,__LINE__,_("Invalid number of weeks in -d parameter\n"));
 1685                 exit(EXIT_FAILURE);
 1686             }
 1687             Date0=localtime(&Today);
 1688             if (Date0==NULL) {
 1689                 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
 1690                 exit(EXIT_FAILURE);
 1691             }
 1692             WeekBegin=Today-((Date0->tm_wday-FirstWeekDay+7)%7)*24*60*60;
 1693             WeekBegin-=i*7*24*60*60;
 1694             Date0=localtime(&WeekBegin);
 1695             if (Date0==NULL) {
 1696                 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
 1697                 exit(EXIT_FAILURE);
 1698             }
 1699             y0=Date0->tm_year+1900;
 1700             m0=Date0->tm_mon+1;
 1701             d0=Date0->tm_mday;
 1702             WeekBegin+=6*24*60*60;
 1703             Date0=localtime(&WeekBegin);
 1704             if (Date0==NULL) {
 1705                 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
 1706                 exit(EXIT_FAILURE);
 1707             }
 1708             y1=Date0->tm_year+1900;
 1709             m1=Date0->tm_mon+1;
 1710             d1=Date0->tm_mday;
 1711         } else if (sscanf(ReadFilter->DateRange,"month-%d",&i)==1) {
 1712             if (i<0) {
 1713                 debuga(__FILE__,__LINE__,_("Invalid number of months in -d parameter\n"));
 1714                 exit(EXIT_FAILURE);
 1715             }
 1716             Date0=localtime(&Today);
 1717             if (Date0==NULL) {
 1718                 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
 1719                 exit(EXIT_FAILURE);
 1720             }
 1721             if (Date0->tm_mon<i%12) {
 1722                 y0=Date0->tm_year+1900-i/12-1;
 1723                 m0=(Date0->tm_mon+12-i%12)%12+1;
 1724                 d0=1;
 1725             } else {
 1726                 y0=Date0->tm_year+1900-i/12;
 1727                 m0=Date0->tm_mon-i%12+1;
 1728                 d0=1;
 1729             }
 1730             memcpy(&Date1,Date0,sizeof(struct tm));
 1731             Date1.tm_isdst=-1;
 1732             Date1.tm_mday=1;
 1733             if (m0<12) {
 1734                 Date1.tm_mon=m0;
 1735                 Date1.tm_year=y0-1900;
 1736             } else {
 1737                 Date1.tm_mon=0;
 1738                 Date1.tm_year=y0-1900+1;
 1739             }
 1740             t1=mktime(&Date1);
 1741             t1-=24*60*60;
 1742             Date0=localtime(&t1);
 1743             y1=Date0->tm_year+1900;
 1744             m1=Date0->tm_mon+1;
 1745             d1=Date0->tm_mday;
 1746         } else {
 1747             debuga(__FILE__,__LINE__,_("Invalid date range passed on command line\n"));
 1748             exit(EXIT_FAILURE);
 1749         }
 1750     }
 1751 
 1752     ReadFilter->StartDate=y0*10000+m0*100+d0;
 1753     ReadFilter->EndDate=y1*10000+m1*100+d1;
 1754     snprintf(ReadFilter->DateRange,sizeof(ReadFilter->DateRange),"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
 1755     return;
 1756 }
 1757 
 1758 
 1759 char *strlow(char *string)
 1760 {
 1761     char *s;
 1762 
 1763     if (string)
 1764     {
 1765         for (s = string; *s; ++s)
 1766             *s = tolower(*s);
 1767     }
 1768 
 1769     return string;
 1770 }
 1771 
 1772 
 1773 
 1774 
 1775 char *strup(char *string)
 1776 {
 1777     char *s;
 1778 
 1779     if (string)
 1780     {
 1781         for (s = string; *s; ++s)
 1782             *s = toupper(*s);
 1783     }
 1784 
 1785     return string;
 1786 }
 1787 
 1788 
 1789 void removetmp(const char *outdir)
 1790 {
 1791     FILE *fp_gen;
 1792     char filename[256];
 1793 
 1794     if (!RemoveTempFiles)
 1795         return;
 1796 
 1797     if (debug) {
 1798         debuga(__FILE__,__LINE__,_("Purging temporary file sarg-general\n"));
 1799     }
 1800     if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
 1801         debuga(__FILE__,__LINE__,_("Path too long: "));
 1802         debuga_more("%s/sarg-period\n",outdir);
 1803         exit(EXIT_FAILURE);
 1804     }
 1805     if ((fp_gen=fopen(filename,"w"))==NULL){
 1806         debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),filename,strerror(errno));
 1807         exit(EXIT_FAILURE);
 1808     }
 1809     totalger(fp_gen,filename);
 1810     if (fclose(fp_gen)==EOF) {
 1811         debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),filename,strerror(errno));
 1812         exit(EXIT_FAILURE);
 1813     }
 1814 }
 1815 
 1816 void load_excludecodes(const char *ExcludeCodes)
 1817 {
 1818     FILE *fp_in;
 1819     char data[80];
 1820     int i;
 1821     int Stored;
 1822     long int MemSize;
 1823 
 1824     if (ExcludeCodes[0] == '\0')
 1825         return;
 1826 
 1827     if ((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
 1828         debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
 1829         exit(EXIT_FAILURE);
 1830     }
 1831 
 1832     if (fseek(fp_in, 0, SEEK_END)==-1) {
 1833         debuga(__FILE__,__LINE__,_("Failed to move till the end of file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
 1834         exit(EXIT_FAILURE);
 1835     }
 1836     MemSize = ftell(fp_in);
 1837     if (MemSize<0) {
 1838         debuga(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),ExcludeCodes);
 1839         exit(EXIT_FAILURE);
 1840     }
 1841     if (fseek(fp_in, 0, SEEK_SET)==-1) {
 1842         debuga(__FILE__,__LINE__,_("Failed to rewind file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
 1843         exit(EXIT_FAILURE);
 1844     }
 1845 
 1846     MemSize+=1;
 1847     if ((excludecode=(char *) malloc(MemSize))==NULL) {
 1848         debuga(__FILE__,__LINE__,_("malloc error (%ld bytes required)\n"),MemSize);
 1849         exit(EXIT_FAILURE);
 1850     }
 1851     memset(excludecode,0,MemSize);
 1852 
 1853     Stored=0;
 1854     while(fgets(data,sizeof(data),fp_in)!=NULL) {
 1855         if (data[0]=='#') continue;
 1856         for (i=strlen(data)-1 ; i>=0 && (unsigned char)data[i]<=' ' ; i--) data[i]='\0';
 1857         if (i<0) continue;
 1858         if (Stored+i+2>=MemSize) {
 1859             debuga(__FILE__,__LINE__,_("Too many codes to exclude in file \"%s\"\n"),ExcludeCodes);
 1860             break;
 1861         }
 1862         strcat(excludecode,data);
 1863         strcat(excludecode,";");
 1864         Stored+=i+1;
 1865     }
 1866 
 1867     if (fclose(fp_in)==EOF) {
 1868         debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),ExcludeCodes,strerror(errno));
 1869         exit(EXIT_FAILURE);
 1870     }
 1871     return;
 1872 }
 1873 
 1874 void free_excludecodes(void)
 1875 {
 1876     if (excludecode) {
 1877         free(excludecode);
 1878         excludecode=NULL;
 1879     }
 1880 }
 1881 
 1882 int vercode(const char *code)
 1883 {
 1884     char *cod;
 1885     int clen;
 1886 
 1887     if (excludecode && excludecode[0]!='\0') {
 1888         clen=strlen(code);
 1889         cod=excludecode;
 1890         while (cod) {
 1891             if (strncmp(code,cod,clen)==0 && cod[clen]==';')
 1892                 return 1;
 1893             cod=strchr(cod,';');
 1894             if (cod) cod++;
 1895         }
 1896     }
 1897     return 0;
 1898 }
 1899 
 1900 void fixnone(char *str)
 1901 {
 1902     int i;
 1903 
 1904     for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
 1905     if (i==3 && strncmp(str,"none",4) == 0)
 1906         str[0]='\0';
 1907 
 1908     return;
 1909 }
 1910 
 1911 void fixendofline(char *str)
 1912 {
 1913     int i;
 1914 
 1915     for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
 1916 }
 1917 
 1918 #ifdef LEGACY_TESTVALIDUSERCHAR
 1919 int testvaliduserchar(const char *user)
 1920 {
 1921     int x=0;
 1922     int y=0;
 1923 
 1924     for (y=0; y<strlen(UserInvalidChar); y++) {
 1925         for (x=0; x<strlen(user); x++) {
 1926             if (user[x] == UserInvalidChar[y])
 1927                 return 1;
 1928         }
 1929     }
 1930     return 0;
 1931 }
 1932 #else
 1933 int testvaliduserchar(const char *user)
 1934 {
 1935     char * p_UserInvalidChar = UserInvalidChar ;
 1936     const char * p_user ;
 1937 
 1938     while( *p_UserInvalidChar ) {
 1939         p_user = user ;
 1940         while ( *p_user ) {
 1941             if ( *p_UserInvalidChar == *p_user )
 1942                 return 1;
 1943             p_user++ ;
 1944         }
 1945         p_UserInvalidChar++ ;
 1946     }
 1947     return 0;
 1948 }
 1949 #endif
 1950 
 1951 int compar( const void *a, const void *b )
 1952 {
 1953     if ( *(int *)a > *(int *)b ) return 1;
 1954     if ( *(int *)a < *(int *)b ) return -1;
 1955     return 0;
 1956 }
 1957 
 1958 /*!
 1959  * Store a range in a list.
 1960  *
 1961  * \param paramname Name of the configuration parameter providing the list.
 1962  * \param list List where to store the numbers.
 1963  * \param d0 Start range or -1 to store only one value.
 1964  * \param d End range if d0>=0 or the single value to store.
 1965  */
 1966 static void storenumlist(const char *paramname, int *list, int d0, int d)
 1967 {
 1968     if (d0<0)
 1969     {
 1970         list[d]=1;
 1971     }
 1972     else
 1973     {
 1974         int i;
 1975 
 1976         if (d<d0)
 1977         {
 1978             debuga(__FILE__,__LINE__,_("Ending value %d is less than or equal to starting value %d in parameter \"%s\"\n"),d,d0,paramname);
 1979             exit(EXIT_FAILURE);
 1980         }
 1981         for (i=d0 ; i<=d ; i++) list[i]=1;
 1982     }
 1983 }
 1984 
 1985 /*!
 1986 Get a comma separated list of numbers and split them into separate values taking into account
 1987 that no value may be greater than a maximum. If a value is a range, it is expended.
 1988 
 1989 Any duplicate value is removed.
 1990 
 1991 \param paramname Name of the configuration parameter providing the list.
 1992 \param buffer The string with the list of numbers.
 1993 \param list List where to store the numbers.
 1994 \param maxvalue The maximum value allowed in the list.
 1995 
 1996 The function terminate the application with an error message if the list is invalid.
 1997 */
 1998 void getnumlist(const char *paramname, const char *buffer, int *list, int maxvalue)
 1999 {
 2000     int i, d, d0;
 2001     int digitcount;
 2002     int nvalues=0;
 2003 
 2004     // skip parameter name
 2005     while (*buffer && *buffer!=' ' && *buffer!='\t') buffer++;
 2006     if (!*buffer)
 2007     {
 2008         debuga(__FILE__,__LINE__,_("Missing values for parameter \"%s\"\n"),paramname);
 2009         exit(EXIT_FAILURE);
 2010     }
 2011 
 2012     // clear list
 2013     for (i=0 ; i<maxvalue ; i++) list[i]=0;
 2014 
 2015     // get values
 2016     d=0;
 2017     d0=-1;
 2018     digitcount=0;
 2019     for ( ; *buffer ; buffer++)
 2020     {
 2021         if (isdigit(*buffer))
 2022         {
 2023             d=d*10+(*buffer-'0');
 2024             if (d>=maxvalue)
 2025             {
 2026                 debuga(__FILE__,__LINE__,_("Value too big found in parameter \"%s\" (max value is %d)\n"),paramname,maxvalue-1);
 2027                 exit(EXIT_FAILURE);
 2028             }
 2029             digitcount++;
 2030         }
 2031         else if (*buffer=='-')
 2032         {
 2033             if (!digitcount)
 2034             {
 2035                 debuga(__FILE__,__LINE__,_("Missing start value before \"-\" in parameter \"%s\"\n"),paramname);
 2036                 exit(EXIT_FAILURE);
 2037             }
 2038             d0=d;
 2039             d=0;
 2040             digitcount=0;
 2041         }
 2042         else if (*buffer==',')
 2043         {
 2044             if (!digitcount)
 2045             {
 2046                 debuga(__FILE__,__LINE__,_("Missing value before \",\" in parameter \"%s\"\n"),paramname);
 2047                 exit(EXIT_FAILURE);
 2048             }
 2049             storenumlist(paramname,list,d0,d);
 2050             nvalues++;
 2051             d0=-1;
 2052             d=0;
 2053             digitcount=0;
 2054         }
 2055         else if (*buffer=='\r' || *buffer=='\n')
 2056         {
 2057             break;
 2058         }
 2059         else if (*buffer!=' ' && *buffer!='\t')
 2060         {
 2061             debuga(__FILE__,__LINE__,_("Invalid character \"%c\" found in parameter \"%s\"\n"),*buffer,paramname);
 2062             exit(EXIT_FAILURE);
 2063         }
 2064     }
 2065     if (digitcount>0)
 2066     {
 2067         storenumlist(paramname,list,d0,d);
 2068         nvalues++;
 2069     }
 2070     else if (d0>=0)
 2071     {
 2072         debuga(__FILE__,__LINE__,_("Missing ending value in range for parameter \"%s\"\n"),paramname);
 2073         exit(EXIT_FAILURE);
 2074     }
 2075     if (!nvalues)
 2076     {
 2077         debuga(__FILE__,__LINE__,_("Parameter \"%s\" is empty\n"),paramname);
 2078         exit(EXIT_FAILURE);
 2079     }
 2080 }
 2081 
 2082 /*!
 2083  * Search if the \a list contains the \a value.
 2084  *
 2085  * \param list The list to search for a value.
 2086  * \param maxvalue The maximum value of the list.
 2087  * \param value The value to search for.
 2088  *
 2089  * \return \c True if the value is enabled in the list.
 2090  */
 2091 bool numlistcontains(const int *list, int maxvalue, int value)
 2092 {
 2093     if (value<0 || value>=maxvalue) return(false);
 2094     return(list[value]!=0);
 2095 }
 2096 
 2097 void show_info(FILE *fp_ou)
 2098 {
 2099     char ftime[127];
 2100 
 2101     if (!ShowSargInfo) return;
 2102     zdate(ftime, sizeof(ftime), df);
 2103     fputs("<div class=\"info\">",fp_ou);
 2104     fprintf(fp_ou,_("Generated by <a href=\"%s\">%s-%s</a> on %s"),URL,PGM,VERSION,ftime);
 2105     fputs("</div>\n",fp_ou);
 2106 }
 2107 
 2108 void show_sarg(FILE *fp_ou, int depth)
 2109 {
 2110     int i;
 2111 
 2112     if (!ShowSargLogo) return;
 2113     fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
 2114     for (i=0 ; i<depth ; i++)
 2115         fputs("../",fp_ou);
 2116     fputs("images/sarg.png\" title=\"SARG, Squid Analysis Report Generator. Logo by Osamu Matsuzaki\" alt=\"Sarg\"></a>&nbsp;Squid Analysis Report Generator</div>\n",fp_ou);
 2117 }
 2118 
 2119 void write_logo_image(FILE *fp_ou)
 2120 {
 2121     if (LogoImage[0]!='\0')
 2122         fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\">&nbsp;%s</div>\n",LogoImage,Width,Height,LogoText);
 2123 }
 2124 
 2125 void write_html_head(FILE *fp_ou, int depth, const char *page_title,int javascript)
 2126 {
 2127     int i;
 2128 
 2129     fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
 2130     fprintf(fp_ou, "<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
 2131     if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
 2132     css(fp_ou);
 2133     if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
 2134         fputs("<script type=\"text/javascript\" src=\"",fp_ou);
 2135         if (strncmp(SortTableJs,"../",3)==0) {
 2136             for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
 2137         }
 2138         fputs(SortTableJs,fp_ou);
 2139         fputs("\"></script>\n",fp_ou);
 2140     }
 2141     fputs("</head>\n<body>\n",fp_ou);
 2142 }
 2143 
 2144 void write_html_header(FILE *fp_ou, int depth, const char *page_title,int javascript)
 2145 {
 2146     write_html_head(fp_ou,depth,page_title,javascript);
 2147     write_logo_image(fp_ou);
 2148     show_sarg(fp_ou, depth);
 2149     fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
 2150 }
 2151 
 2152 void close_html_header(FILE *fp_ou)
 2153 {
 2154     fputs("</table></div>\n",fp_ou);
 2155 }
 2156 
 2157 void write_html_trailer(FILE *fp_ou)
 2158 {
 2159     show_info(fp_ou);
 2160     fputs("</body>\n</html>\n",fp_ou);
 2161 }
 2162 
 2163 void output_html_string(FILE *fp_ou,const char *str,int maxlen)
 2164 {
 2165     int i=0;
 2166 
 2167     while (*str && (maxlen<=0 || i<maxlen)) {
 2168         switch (*str) {
 2169             case '&':
 2170                 fputs("&amp;",fp_ou);
 2171                 break;
 2172             case '<':
 2173                 fputs("&lt;",fp_ou);
 2174                 break;
 2175             case '>':
 2176                 fputs("&gt;",fp_ou);
 2177                 break;
 2178             case '"':
 2179                 fputs("&quot;",fp_ou);
 2180                 break;
 2181             case '\'':
 2182                 fputs("&#39;",fp_ou);
 2183                 break;
 2184             default:
 2185                 fputc(*str,fp_ou);
 2186         }
 2187         str++;
 2188         i++;
 2189     }
 2190     if (maxlen>0 && i>=maxlen)
 2191         fputs("&hellip;",fp_ou);
 2192 }
 2193 
 2194 void output_html_url(FILE *fp_ou,const char *url)
 2195 {
 2196     while (*url) {
 2197         if (*url=='&')
 2198             fputs("&amp;",fp_ou);
 2199         else
 2200             fputc(*url,fp_ou);
 2201         url++;
 2202     }
 2203 }
 2204 
 2205 /*!
 2206   Write a host name inside an A tag of a HTML file. If the host name starts
 2207   with a star, it is assumed to be an alias that cannot be put inside a link
 2208   so the A tag is not written around the host name.
 2209 
 2210   \param fp_ou The handle of the HTML file.
 2211   \param url The host to display in the HTML file.
 2212   \param maxlen The maximum number of characters to print into the host name.
 2213  */
 2214 void output_html_link(FILE *fp_ou,const char *url,int maxlen)
 2215 {
 2216     if (url[0]==ALIAS_PREFIX) {
 2217         // this is an alias, no need for a A tag
 2218         output_html_string(fp_ou,url+1,100);
 2219     } else {
 2220         if (skip_scheme(url)==url)
 2221             fputs("<a href=\"http://",fp_ou);//no scheme in the url, assume http:// to make the link clickable
 2222         else
 2223             fputs("<a href=\"",fp_ou);//the scheme is in the url, no need to add one
 2224         output_html_url(fp_ou,url);
 2225         fputs("\">",fp_ou);
 2226         output_html_string(fp_ou,url,100);
 2227         fputs("</a>",fp_ou);
 2228     }
 2229 }
 2230 
 2231 void url_module(const char *url, char *w2)
 2232 {
 2233     int x, y;
 2234     char w[255];
 2235 
 2236     y=0;
 2237     for (x=strlen(url)-1; x>=0; x--) {
 2238         if (url[x] == '/' || y>=sizeof(w)-1) break;
 2239         w[y++]=url[x];
 2240     }
 2241     if (x<0) {
 2242         w2[0]='\0';
 2243         return;
 2244     }
 2245 
 2246     x=0;
 2247     for (y=y-1; y>=0; y--) {
 2248         w2[x++]=w[y];
 2249     }
 2250     w2[x]='\0';
 2251 }
 2252 
 2253 /*!
 2254 Mangle an URL to produce a part that can be used as an anchor in
 2255 a html <a name=""> tag.
 2256 
 2257 \param url The URL to mangle.
 2258 \param anchor The buffer to write the mangled URL.
 2259 \param size The size of the buffer.
 2260 */
 2261 void url_to_anchor(const char *url,char *anchor,int size)
 2262 {
 2263     int i,j;
 2264     bool skip;
 2265 
 2266     // find url end
 2267     for (i=0 ; url[i] && url[i]!='/' && url[i]!='?' ; i++);
 2268     i--;
 2269     if (i<=0) {
 2270         anchor[0]='\0';
 2271         return;
 2272     }
 2273 
 2274     // only keep really safe characters
 2275     skip=false;
 2276     j=size-1;
 2277     anchor[j]='\0';
 2278     while (j>0 && i>=0)
 2279     {
 2280         if (isalnum(url[i]) || url[i]=='-' || url[i]=='_' || url[i]=='.') {
 2281             anchor[--j]=url[i];
 2282             skip=false;
 2283         } else {
 2284             if (!skip) anchor[--j]='_';
 2285             skip=true;
 2286         }
 2287         i--;
 2288     }
 2289     if (j>0)
 2290     {
 2291         while ( anchor[j])
 2292         {
 2293             *anchor=anchor[j];
 2294             anchor++;
 2295         }
 2296         *anchor='\0';
 2297     }
 2298 }
 2299 
 2300 void version(void)
 2301 {
 2302     printf(_("SARG Version: %s\n"),VERSION);
 2303 #if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
 2304     if (debug) {
 2305         printf(_("\nFor the translation to work, a valid message file should be copied to "
 2306                  "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR,PACKAGE_NAME);
 2307         if (CurrentLocale) {
 2308             printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale);
 2309         } else {
 2310             printf(_("Locale is not set in the environment variable.\n"));
 2311         }
 2312         // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
 2313         printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
 2314     }
 2315 #endif
 2316     if (debug) {
 2317 #ifdef HAVE_GLOB_H
 2318         printf(_("File globbing compiled in.\n"));
 2319 #else
 2320         printf(_("File globbing NOT compiled in.\n"));
 2321 #endif
 2322     }
 2323     exit(EXIT_SUCCESS);
 2324 }
 2325 
 2326 char *get_param_value(const char *param,char *line)
 2327 {
 2328     int plen;
 2329 
 2330     while (*line==' ' || *line=='\t') line++;
 2331     plen=strlen(param);
 2332     if (strncasecmp(line,param,plen)) return(NULL);
 2333     if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
 2334     line+=plen;
 2335     while (*line==' ' || *line=='\t') line++;
 2336     return(line);
 2337 }
 2338 
 2339 void unlinkdir(const char *dir,bool contentonly)
 2340 {
 2341     struct stat st;
 2342     DIR *dirp;
 2343     struct dirent *direntp;
 2344     char dname[MAXLEN];
 2345     int err;
 2346 
 2347     dirp=opendir(dir);
 2348     if (!dirp) return;
 2349     while ((direntp = readdir(dirp)) != NULL) {
 2350         if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
 2351             (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
 2352             continue;
 2353         if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
 2354             debuga(__FILE__,__LINE__,_("Path too long: "));
 2355             debuga_more("%s/%s\n",dir,direntp->d_name);
 2356             exit(EXIT_FAILURE);
 2357         }
 2358 #ifdef HAVE_LSTAT
 2359         err=lstat(dname,&st);
 2360 #else
 2361         err=stat(dname,&st);
 2362 #endif
 2363         if (err) {
 2364             debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
 2365             exit(EXIT_FAILURE);
 2366         }
 2367         if (S_ISREG(st.st_mode)) {
 2368             if (unlink(dname)) {
 2369                 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
 2370                 exit(EXIT_FAILURE);
 2371             }
 2372         } else if (S_ISDIR(st.st_mode)) {
 2373             unlinkdir(dname,0);
 2374         } else {
 2375             debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file nor a directory)\n"),dname);
 2376         }
 2377     }
 2378     closedir(dirp);
 2379 
 2380     if (!contentonly) {
 2381         if (rmdir(dir)) {
 2382             debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dir,strerror(errno));
 2383             exit(EXIT_FAILURE);
 2384         }
 2385     }
 2386 }
 2387 
 2388 /*!
 2389 Delete every file from the temporary directory where sarg is told to store its
 2390 temporary files.
 2391 
 2392 As any stray file left over by a previous run would be included in the report, we
 2393 must delete every file from the temporary directory before we start processing the logs.
 2394 
 2395 But the temporary directory is given by the user either in the configuration file or
 2396 on the command line. We check that the user didn't give a wrong directory by looking
 2397 at the files stored in the directory. If a single file is not one of ours, we abort.
 2398 
 2399 \param dir The temporary directory to purge.
 2400 */
 2401 void emptytmpdir(const char *dir)
 2402 {
 2403     struct stat st;
 2404     DIR *dirp;
 2405     struct dirent *direntp;
 2406     int dlen;
 2407     int elen;
 2408     char dname[MAXLEN];
 2409     int err;
 2410     int i;
 2411     static const char *TmpExt[]=
 2412     {
 2413         ".int_unsort",
 2414         ".int_log",
 2415         ".day",
 2416         "htmlrel.txt",
 2417         ".user_unsort",
 2418         ".user_log",
 2419         ".utmp",
 2420         ".ip",
 2421         "lastlog1",
 2422         "lastlog",
 2423         "emailrep"
 2424     };
 2425 
 2426     dirp=opendir(dir);
 2427     if (!dirp) return;
 2428 
 2429     // make sure the temporary directory contains only our files
 2430     while ((direntp = readdir(dirp)) != NULL) {
 2431         if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
 2432             (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
 2433             continue;
 2434 
 2435         // is it one of our files
 2436         dlen=strlen(direntp->d_name);
 2437         for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
 2438             elen=strlen(TmpExt[i]);
 2439             if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
 2440         }
 2441         if (i<0) {
 2442             debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
 2443             "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
 2444             "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
 2445             exit(EXIT_FAILURE);
 2446         }
 2447 
 2448         if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
 2449             debuga(__FILE__,__LINE__,_("Path too long: "));
 2450             debuga_more("%s/%s\n",dir,direntp->d_name);
 2451             exit(EXIT_FAILURE);
 2452         }
 2453 
 2454 #ifdef HAVE_LSTAT
 2455         err=lstat(dname,&st);
 2456 #else
 2457         err=stat(dname,&st);
 2458 #endif
 2459         if (err) {
 2460             debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
 2461             exit(EXIT_FAILURE);
 2462         }
 2463         if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) {
 2464             debuga(__FILE__,__LINE__,_("Unknown path type for \"%s\". Check temporary directory\n"),dname);
 2465             exit(EXIT_FAILURE);
 2466         }
 2467     }
 2468     rewinddir(dirp);
 2469 
 2470     // now delete our files
 2471     while ((direntp = readdir(dirp)) != NULL) {
 2472         if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
 2473             (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
 2474             continue;
 2475 
 2476         // is it one of our files
 2477         dlen=strlen(direntp->d_name);
 2478         for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
 2479             elen=strlen(TmpExt[i]);
 2480             if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
 2481         }
 2482         if (i<0) {
 2483             debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
 2484             "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
 2485             "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
 2486             exit(EXIT_FAILURE);
 2487         }
 2488 
 2489         if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
 2490             debuga(__FILE__,__LINE__,_("Path too long: "));
 2491             debuga_more("%s/%s\n",dir,direntp->d_name);
 2492             exit(EXIT_FAILURE);
 2493         }
 2494 #ifdef HAVE_LSTAT
 2495         err=lstat(dname,&st);
 2496 #else
 2497         err=stat(dname,&st);
 2498 #endif
 2499         if (err) {
 2500             debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
 2501             exit(EXIT_FAILURE);
 2502         }
 2503         if (S_ISDIR(st.st_mode)) {
 2504             unlinkdir(dname,0);
 2505         } else if (S_ISREG(st.st_mode)) {
 2506             if (unlink(dname)) {
 2507                 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
 2508                 exit(EXIT_FAILURE);
 2509             }
 2510         } else {
 2511             debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file)\n"),dname);
 2512         }
 2513     }
 2514     closedir(dirp);
 2515 }
 2516 
 2517 /*!
 2518   Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
 2519   prefix size.
 2520 
 2521   \param buf The buffer to parse.
 2522   \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
 2523               The pointer may be NULL.
 2524   \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
 2525   \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
 2526   \param nbits The number of prefix bits for an IP address.
 2527   \param next The content of the line after the extracted address.
 2528 
 2529   \retval 3 The pattern is a IPv6 address.
 2530   \retval 2 The pattern is a IPv4 address.
 2531   \retval 1 The patter is a string.
 2532   \retval 0 Empty pattern.
 2533  */
 2534 int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,unsigned short int *ipv6,int *nbits,const char **next)
 2535 {
 2536     int i;
 2537     int j;
 2538     int ip_size;
 2539     unsigned int value4, value6;
 2540     unsigned short int addr[8];
 2541     int addr_len;
 2542     int nibble6_len;
 2543     int mask, max_mask;
 2544     int pad_pos;
 2545     int pad_len;
 2546     bool bracket=false;
 2547     bool port=false;
 2548     int port_num=0;
 2549 
 2550     // skip leading spaces and tabs
 2551     while (*buf && (*buf==' ' || *buf=='\t')) buf++;
 2552 
 2553     // find out the nature of the pattern
 2554     ip_size=0x60  | 0x04;
 2555     if (*buf=='[') {
 2556         bracket=true;
 2557         ip_size=0x60;
 2558         buf++;
 2559     }
 2560     value4=0U;
 2561     value6=0U;
 2562     addr_len=0;
 2563     nibble6_len=0;
 2564     pad_pos=-1;
 2565     for (i=0 ; (unsigned char)buf[i]>' ' && buf[i]!='/' && buf[i]!='?' && (!bracket || buf[i]!=']') && ip_size ; i++) {
 2566         if (ip_size & 0x04) {
 2567             if (isdigit(buf[i])) {
 2568                 if (port) {
 2569                     port_num=port_num*10+(buf[i]-'0');
 2570                     if (port_num>65535) ip_size&=~0x04;
 2571                 } else {
 2572                     value4=value4*10+(buf[i]-'0');
 2573                     if (value4>0xFFU) ip_size&=~0x04;
 2574                 }
 2575             } else if (buf[i]=='.' && addr_len<4) {
 2576                 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
 2577                 value4=0U;
 2578             } else if (!port && buf[i]==':') {
 2579                 port=true;
 2580             } else {
 2581                 ip_size&=~0x04;
 2582             }
 2583         }
 2584         if (ip_size & 0x60) {
 2585             if (isdigit(buf[i])) {
 2586                 value6=(value6<<4)+(buf[i]-'0');
 2587                 nibble6_len++;
 2588                 if (value6>0xFFFFU) ip_size&=~0x60;
 2589             } else if (toupper(buf[i])>='A' && toupper(buf[i])<='F') {
 2590                 value6=(value6<<4)+(toupper(buf[i])-'A'+10);
 2591                 nibble6_len++;
 2592                 if (value6>0xFFFFU) ip_size&=~0x60;
 2593             } else if (buf[i]==':' && addr_len<8) {
 2594                 if (nibble6_len>0) {
 2595                     addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
 2596                     nibble6_len=0;
 2597                 }
 2598                 value6=0U;
 2599                 if (buf[i+1]==':') {
 2600                     pad_pos=addr_len;
 2601                     i++;
 2602                 }
 2603             } else {
 2604                 ip_size&=~0x60;
 2605             }
 2606         }
 2607     }
 2608     if (i==0) return(0);
 2609     if (ip_size & 0x04) {
 2610         if (addr_len!=3)
 2611             ip_size&=~0x04;
 2612         else
 2613             addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
 2614     }
 2615     if (ip_size & 0x60) {
 2616         if (pad_pos<0 && addr_len!=7) {
 2617             ip_size&=~0x60;
 2618         } else if (pad_pos>=0 && addr_len>=7)
 2619             ip_size&=~0x60;
 2620         else if (nibble6_len>0)
 2621             addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
 2622     }
 2623     if (!ip_size) {
 2624         if (text) {
 2625             *text=buf;
 2626             if (bracket) (*text)--;
 2627         }
 2628         while ((unsigned char)buf[i]>' ') i++;
 2629         if (next) *next=buf+i;
 2630         return(1);
 2631     }
 2632     max_mask=(ip_size & 0x04) ? 4*8 : 8*16;
 2633     if (buf[i]=='/') {
 2634         i++;
 2635         mask=atoi(buf+i);
 2636         while (isdigit(buf[i])) i++;
 2637         if (mask<0 || mask>max_mask) mask=max_mask;
 2638     } else
 2639         mask=max_mask;
 2640     if (ip_size & 0x60 && bracket && buf[i]==']') i++;
 2641     if (next) *next=buf+i;
 2642     if (ip_size & 0x04) {
 2643         if (nbits) *nbits=mask;
 2644         for (i=0 ; i<addr_len ; i++)
 2645             ipv4[i]=(unsigned char)addr[i];
 2646         return(2);
 2647     }
 2648 
 2649     // IPv6 address
 2650     if (nbits) *nbits=mask;
 2651     i=0;
 2652     j=0;
 2653     if (pad_pos>=0) {
 2654         while (i<pad_pos)
 2655             ipv6[j++]=(unsigned short int)addr[i++];
 2656         pad_len=8-addr_len;
 2657         while (j<pad_pos+pad_len)
 2658             ipv6[j++]=0;
 2659     }
 2660     while (i<addr_len)
 2661         ipv6[j++]=(unsigned short int)addr[i++];
 2662     return(3);
 2663 }
 2664 
 2665 int format_path(const char *file, int line, char *output_buffer, int buffer_size, const char *format,...)
 2666 {
 2667     va_list ap;
 2668     int output_length;
 2669 
 2670     va_start(ap, format);
 2671     output_length = vsnprintf(output_buffer, buffer_size, format, ap);
 2672     if (output_length >= buffer_size) {
 2673         debuga(file, line, _("Path too long: "));
 2674         vfprintf(stderr, format, ap);
 2675         exit(EXIT_FAILURE);
 2676     }
 2677     va_end(ap);
 2678     return output_length;
 2679 }
 2680 
 2681 void append_to_path(char *base_path, int base_path_size, const char *append)
 2682 {
 2683     int length = strlen(base_path);
 2684     int append_length;
 2685 
 2686     if (append[0] == '/') append++;
 2687     if (length > 0 && base_path[length-1] != '/') {
 2688         if (length+1 >= base_path_size) {
 2689             debuga(__FILE__, __LINE__, _("Path too long: "));
 2690             fprintf(stderr, "%s/%s", base_path, append);
 2691             exit(EXIT_FAILURE);
 2692         }
 2693         base_path[length++] = '/';
 2694     }
 2695     append_length = strlen(append);
 2696     if (length+append_length >= base_path_size) {
 2697         debuga(__FILE__, __LINE__, _("Path too long: "));
 2698         base_path[length] = '\0';
 2699         fprintf(stderr, "%s%s", base_path, append);
 2700         exit(EXIT_FAILURE);
 2701     }
 2702     strcpy(base_path + length, append);
 2703 }