1 /***************************************
2   WWWOFFLE - World Wide Web Offline Explorer - Version 2.9j.
3 
4   Tools for use in the cache for version 2.x.
5   ******************/ /******************
6   Written by Andrew M. Bishop
7 
8   This file Copyright 1997-2016 Andrew M. Bishop
9   It may be distributed under the GNU Public License, version 2, or
10   any higher version.  See section COPYING of the GNU Public license
11   for conditions under which this file may be redistributed.
12   ***************************************/
13 
14 
15 #include "autoconfig.h"
16 
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 
23 #include <sys/types.h>
24 #include <unistd.h>
25 
26 #if TIME_WITH_SYS_TIME
27 # include <sys/time.h>
28 # include <time.h>
29 #else
30 # if HAVE_SYS_TIME_H
31 #  include <sys/time.h>
32 # else
33 #  include <time.h>
34 # endif
35 #endif
36 
37 #include <utime.h>
38 #include <sys/stat.h>
39 
40 #if HAVE_DIRENT_H
41 # include <dirent.h>
42 #else
43 # define dirent direct
44 # if HAVE_SYS_NDIR_H
45 #  include <sys/ndir.h>
46 # endif
47 # if HAVE_SYS_DIR_H
48 #  include <sys/dir.h>
49 # endif
50 # if HAVE_NDIR_H
51 #  include <ndir.h>
52 # endif
53 #endif
54 
55 #include <fcntl.h>
56 #include <grp.h>
57 
58 #include "wwwoffle.h"
59 #include "io.h"
60 #include "misc.h"
61 #include "errors.h"
62 #include "version.h"
63 #include "config.h"
64 
65 
66 #ifndef PATH_MAX
67 /*+ The maximum pathname length in characters. +*/
68 #define PATH_MAX 4096
69 #endif
70 
71 #ifndef O_BINARY
72 /*+ A work-around for needing O_BINARY with Win32 to use binary mode. +*/
73 #define O_BINARY 0
74 #endif
75 
76 #define LS         1            /*+ For the 'ls' operation. +*/
77 #define LS_DIR     2            /*+ For the 'ls' operation on directories. +*/
78 #define LS_SPECIAL 3            /*+ For the 'ls' operation on special directories. +*/
79 #define MV         4            /*+ For the 'mv' operation. +*/
80 #define RM         5            /*+ For the 'rm' operation. +*/
81 #define READ       6            /*+ For the 'read' operation. +*/
82 #define WRITE      7            /*+ For the 'write' operation. +*/
83 #define HASH       8            /*+ For the 'hash' operation. +*/
84 #define GZIP       9            /*+ For the 'gzip' operation. +*/
85 #define GUNZIP    10            /*+ For the 'gunzip' operation. +*/
86 #define FSCK      11            /*+ For the 'fsck' operation. +*/
87 
88 /*+ A compile time option to not actually make any changes to files for debugging. +*/
89 #define MAKE_CHANGES 1
90 
91 
92 /* Local functions */
93 
94 static int wwwoffle_ls_url(URL *Url);
95 static int wwwoffle_ls_dir(char *name);
96 static int wwwoffle_ls_special(char *name);
97 static int wwwoffle_mv(URL *Url1,URL *Url2);
98 static int wwwoffle_rm(URL *Url);
99 static int wwwoffle_read(URL *Url);
100 static int wwwoffle_write(URL *Url);
101 static int wwwoffle_hash(URL *Url);
102 static int wwwoffle_gzip(URL *Url,int compress);
103 static int wwwoffle_fsck(void);
104 
105 static int ls(char *file);
106 
107 #if USE_ZLIB
108 static void gzip_file(char *proto,char *hostport,char *file,int compress);
109 #endif
110 
111 static void wwwoffle_fsck_check_proto(char *proto);
112 static void wwwoffle_fsck_check_dir(char *proto,char *host,char *special);
113 static char *FileNameTo_url(char *file);
114 
115 
116 /*++++++++++++++++++++++++++++++++++++++
117   The main program
118   ++++++++++++++++++++++++++++++++++++++*/
119 
main(int argc,char ** argv)120 int main(int argc,char **argv)
121 {
122  struct stat buf;
123  URL **Url=NULL;
124  int mode=0,exitval=0;
125  int i;
126  char *argv0,*config_file=NULL;
127 
128  /* Check which program we are running */
129 
130  argv0=argv[0]+strlen(argv[0])-1;
131  while(argv0>=argv[0] && *argv0!='/')
132     argv0--;
133 
134  argv0++;
135 
136  if(!strcmp(argv0,"wwwoffle-ls"))
137     mode=LS;
138  else if(!strcmp(argv0,"wwwoffle-mv"))
139     mode=MV;
140  else if(!strcmp(argv0,"wwwoffle-rm"))
141     mode=RM;
142  else if(!strcmp(argv0,"wwwoffle-read"))
143     mode=READ;
144  else if(!strcmp(argv0,"wwwoffle-write"))
145     mode=WRITE;
146  else if(!strcmp(argv0,"wwwoffle-hash"))
147     mode=HASH;
148  else if(!strcmp(argv0,"wwwoffle-gzip"))
149     mode=GZIP;
150  else if(!strcmp(argv0,"wwwoffle-gunzip"))
151     mode=GUNZIP;
152  else if(!strcmp(argv0,"wwwoffle-fsck"))
153     mode=FSCK;
154  else if(!strcmp(argv0,"wwwoffle-tools"))
155    {
156     if(argc>1 && !strcmp(argv[1],"-ls"))
157       {mode=LS; argv++; argc--;}
158     else if(argc>1 && !strcmp(argv[1],"-mv"))
159       {mode=MV; argv++; argc--;}
160     else if(argc>1 && !strcmp(argv[1],"-rm"))
161       {mode=RM; argv++; argc--;}
162     else if(argc>1 && !strcmp(argv[1],"-read"))
163       {mode=READ; argv++; argc--;}
164     else if(argc>1 && !strcmp(argv[1],"-write"))
165       {mode=WRITE; argv++; argc--;}
166     else if(argc>1 && !strcmp(argv[1],"-hash"))
167       {mode=HASH; argv++; argc--;}
168     else if(argc>1 && !strcmp(argv[1],"-gzip"))
169       {mode=GZIP; argv++; argc--;}
170     else if(argc>1 && !strcmp(argv[1],"-gunzip"))
171       {mode=GUNZIP; argv++; argc--;}
172     else if(argc>1 && !strcmp(argv[1],"-fsck"))
173       {mode=FSCK; argv++; argc--;}
174    }
175 
176  if(mode==0)
177    {
178     fprintf(stderr,"wwwoffle-tools version %s\n"
179                    "To select the mode of operation choose:\n"
180                    "        wwwoffle-ls     ( = wwwoffle-tools -ls )\n"
181                    "        wwwoffle-mv     ( = wwwoffle-tools -mv )\n"
182                    "        wwwoffle-rm     ( = wwwoffle-tools -rm )\n"
183                    "        wwwoffle-read   ( = wwwoffle-tools -read )\n"
184                    "        wwwoffle-write  ( = wwwoffle-tools -write )\n"
185                    "        wwwoffle-hash   ( = wwwoffle-tools -hash )\n"
186                    "        wwwoffle-gzip   ( = wwwoffle-tools -gzip )\n"
187                    "        wwwoffle-gunzip ( = wwwoffle-tools -gunzip )\n"
188                    "        wwwoffle-fsck   ( = wwwoffle-tools -fsck )\n",
189                    WWWOFFLE_VERSION);
190     exit(1);
191    }
192 
193  if(mode==LS)
194    {
195     for(i=1;i<argc;i++)
196        if(!strcmp(argv[i],"outgoing") || !strcmp(argv[i],"monitor") ||
197           !strcmp(argv[i],"lasttime") || (!strncmp(argv[i],"prevtime",(size_t)8) && isdigit(argv[i][8])) ||
198           !strcmp(argv[i],"lastout")  || (!strncmp(argv[i],"prevout",(size_t)7)  && isdigit(argv[i][7])))
199           mode=LS_SPECIAL;
200        else
201          {
202           int c=0;
203           char *p=argv[i];
204 
205           while(*p)
206              if(*p++=='/')
207                 c++;
208 
209           if(c==1)
210              mode=LS_DIR;
211          }
212    }
213 
214  /* Find the configuration file */
215 
216  for(i=1;i<argc;i++)
217     if(config_file)
218       {
219        argv[i-2]=argv[i];
220       }
221     else if(!strcmp(argv[i],"-c"))
222       {
223        if(++i>=argc)
224          {fprintf(stderr,"%s: The '-c' argument requires a configuration file name.\n",argv0); exit(1);}
225 
226        config_file=argv[i];
227       }
228 
229  if(config_file)
230     argc-=2;
231 
232  if(!config_file)
233    {
234     char *env=getenv("WWWOFFLE_PROXY");
235 
236     if(env && *env=='/')
237        config_file=env;
238    }
239 
240  /* Print the usage instructions */
241 
242  if(argc>1 && !strcmp(argv[1],"--version"))
243    {fprintf(stderr,"%s version %s\n",argv0,WWWOFFLE_VERSION);exit(0);}
244 
245  else if((mode==LS && (argc<2 || (argc>1 && !strcmp(argv[1],"--help")))) ||
246          (mode==LS_DIR && (argc!=2 || (argc>1 && !strcmp(argv[1],"--help")))) ||
247          (mode==LS_SPECIAL && (argc!=2 || (argc>1 && !strcmp(argv[1],"--help")))))
248    {fprintf(stderr,"Usage: wwwoffle-ls [-c <config-file>]\n"
249                    "                   <URL> ...\n"
250                    "       wwwoffle-ls [-c <config-file>]\n"
251                    "                   <dir>/<subdir>\n"
252                    "       wwwoffle-ls [-c <config-file>]\n"
253                    "                   ( outgoing | lastout | prevout[0-9] |\n"
254                    "                     monitor | lasttime | prevtime[0-9] )\n");exit(0);}
255  else if(mode==MV && (argc!=3 || (argc>1 && !strcmp(argv[1],"--help"))))
256    {fprintf(stderr,"Usage: wwwoffle-mv [-c <config-file>]\n"
257                    "                   (<dir1>/<subdir1> | <protocol1>://<host1>)\n"
258                    "                   (<dir2>/<subdir2> | <protocol2>://<host2>)\n");exit(0);}
259  else if(mode==RM && (argc<2 || (argc>1 && !strcmp(argv[1],"--help"))))
260    {fprintf(stderr,"Usage: wwwoffle-rm [-c <config-file>] <URL> ...\n");exit(0);}
261  else if(mode==READ && (argc!=2 || (argc>1 && !strcmp(argv[1],"--help"))))
262    {fprintf(stderr,"Usage: wwwoffle-read [-c <config-file>] <URL>\n");exit(0);}
263  else if(mode==WRITE && (argc!=2 || (argc>1 && !strcmp(argv[1],"--help"))))
264    {fprintf(stderr,"Usage: wwwoffle-write [-c <config-file>] <URL>\n");exit(0);}
265  else if(mode==HASH && (argc!=2 || (argc>1 && !strcmp(argv[1],"--help"))))
266    {fprintf(stderr,"Usage: wwwoffle-hash [-c <config-file>] <URL>\n");exit(0);}
267  else if(mode==GZIP && (argc<2 || (argc>1 && !strcmp(argv[1],"--help"))))
268    {fprintf(stderr,"Usage: wwwoffle-gzip [-c <config-file>] \n"
269                    "                     ( <dir>/<subdir> | <protocol>://<host> ) ...\n");exit(0);}
270  else if(mode==GUNZIP && (argc<2 || (argc>1 && !strcmp(argv[1],"--help"))))
271    {fprintf(stderr,"Usage: wwwoffle-gunzip [-c <config-file>] \n"
272                    "                       ( <dir>/<subdir> | <protocol>://<host> ) ...\n");exit(0);}
273  else if(mode==FSCK && (argc!=1 || (argc>1 && !strcmp(argv[1],"--help"))))
274    {fprintf(stderr,"Usage: wwwoffle-fsck [-c <config-file>]\n");exit(0);}
275 
276  /* Initialise */
277 
278  InitErrorHandler(argv0,0,1);
279 
280  InitConfigurationFile(config_file);
281 
282  if(config_file)
283    {
284     init_io(STDERR_FILENO);
285 
286     if(ReadConfigurationFile(STDERR_FILENO))
287        PrintMessage(Fatal,"Error in configuration file '%s'.",config_file);
288 
289     finish_io(STDERR_FILENO);
290    }
291 
292  umask(0);
293 
294  if(mode==HASH)
295     ;
296  else if((stat("outgoing",&buf) || !S_ISDIR(buf.st_mode)) ||
297          (stat("http",&buf) || !S_ISDIR(buf.st_mode)))
298    {
299     if(ChangeToSpoolDir(ConfigString(SpoolDir)))
300       {fprintf(stderr,"The %s program must be started from the spool directory\n"
301                       "Cannot change to the '%s' directory.\n",argv0,ConfigString(SpoolDir));exit(1);}
302     if((stat("outgoing",&buf) || !S_ISDIR(buf.st_mode)) ||
303        (stat("http",&buf) || !S_ISDIR(buf.st_mode)))
304       {fprintf(stderr,"The %s program must be started from the spool directory\n"
305                       "There is no accessible 'outgoing' directory here so it can't be right.\n",argv0);exit(1);}
306    }
307  else
308    {
309     char cwd[PATH_MAX+1];
310 
311     getcwd(cwd,(size_t)PATH_MAX);
312 
313     ChangeToSpoolDir(cwd);
314    }
315 
316  /* Get the arguments */
317 
318  if(mode!=LS_DIR && mode!=LS_SPECIAL)
319    {
320     Url=(URL**)malloc(argc*sizeof(URL*));
321 
322     for(i=1;i<argc;i++)
323       {
324        char *arg,*colon,*slash;
325 
326        if(!strncmp(ConfigString(SpoolDir),argv[i],strlen(ConfigString(SpoolDir))) &&
327           argv[i][strlen(ConfigString(SpoolDir))]=='/')
328           arg=argv[i]+strlen(ConfigString(SpoolDir))+1;
329        else
330           arg=argv[i];
331 
332        colon=strchr(arg,':');
333        slash=strchr(arg,'/');
334 
335        if((colon && slash && colon<slash) ||
336           !slash)
337          {
338           Url[i]=SplitURL(arg);
339          }
340        else
341          {
342           *slash=0;
343           Url[i]=CreateURL(arg,slash+1,"/",NULL,NULL,NULL);
344          }
345       }
346    }
347 
348  if(mode==LS)
349     for(i=1;i<argc;i++)
350        exitval+=wwwoffle_ls_url(Url[i]);
351  else if(mode==LS_DIR)
352     exitval=wwwoffle_ls_dir(argv[1]);
353  else if(mode==LS_SPECIAL)
354     exitval=wwwoffle_ls_special(argv[1]);
355  else if(mode==MV)
356     exitval=wwwoffle_mv(Url[1],Url[2]);
357  else if(mode==RM)
358     for(i=1;i<argc;i++)
359        exitval+=wwwoffle_rm(Url[i]);
360  else if(mode==READ)
361     exitval=wwwoffle_read(Url[1]);
362  else if(mode==WRITE)
363    {
364     if(config_file)
365       {
366        /* Change the user and group. */
367 
368        int gid=ConfigInteger(WWWOFFLE_Gid);
369        int uid=ConfigInteger(WWWOFFLE_Uid);
370 
371        if(uid!=-1)
372           seteuid(0);
373 
374        if(gid!=-1)
375          {
376 #if HAVE_SETGROUPS
377           if(getuid()==0 || geteuid()==0)
378              if(setgroups(0,NULL)<0)
379                 PrintMessage(Fatal,"Cannot clear supplementary group list [%!s].");
380 #endif
381 
382 #if HAVE_SETRESGID
383           if(setresgid((gid_t)gid,(gid_t)gid,(gid_t)gid)<0)
384              PrintMessage(Fatal,"Cannot set real/effective/saved group id to %d [%!s].",gid);
385 #else
386           if(geteuid()==0)
387             {
388              if(setgid((gid_t)gid)<0)
389                 PrintMessage(Fatal,"Cannot set group id to %d [%!s].",gid);
390             }
391           else
392             {
393 #if HAVE_SETREGID
394              if(setregid(getegid(),(gid_t)gid)<0)
395                 PrintMessage(Fatal,"Cannot set effective group id to %d [%!s].",gid);
396              if(setregid((gid_t)gid,(gid_t)~1)<0)
397                 PrintMessage(Fatal,"Cannot set real group id to %d [%!s].",gid);
398 #else
399              PrintMessage(Fatal,"Must be root to totally change group id.");
400 #endif
401             }
402 #endif
403          }
404 
405        if(uid!=-1)
406          {
407 #if HAVE_SETRESUID
408           if(setresuid((uid_t)uid,(uid_t)uid,(uid_t)uid)<0)
409              PrintMessage(Fatal,"Cannot set real/effective/saved user id to %d [%!s].",uid);
410 #else
411           if(geteuid()==0)
412             {
413              if(setuid((uid_t)uid)<0)
414                 PrintMessage(Fatal,"Cannot set user id to %d [%!s].",uid);
415             }
416           else
417             {
418 #if HAVE_SETREUID
419              if(setreuid(geteuid(),(uid_t)uid)<0)
420                 PrintMessage(Fatal,"Cannot set effective user id to %d [%!s].",uid);
421              if(setreuid((uid_t)uid,(uid_t)~1)<0)
422                 PrintMessage(Fatal,"Cannot set real user id to %d [%!s].",uid);
423 #else
424              PrintMessage(Fatal,"Must be root to totally change user id.");
425 #endif
426             }
427 #endif
428          }
429 
430        if(uid!=-1 || gid!=-1)
431           PrintMessage(Inform,"Running with uid=%d, gid=%d.",geteuid(),getegid());
432       }
433 
434     exitval=wwwoffle_write(Url[1]);
435    }
436  else if(mode==HASH)
437     exitval=wwwoffle_hash(Url[1]);
438  else if(mode==GZIP)
439     for(i=1;i<argc;i++)
440        exitval+=wwwoffle_gzip(Url[i],1);
441  else if(mode==GUNZIP)
442     for(i=1;i<argc;i++)
443        exitval+=wwwoffle_gzip(Url[i],0);
444  else if(mode==FSCK)
445     exitval=wwwoffle_fsck();
446 
447  exit(exitval);
448 }
449 
450 
451 /*++++++++++++++++++++++++++++++++++++++
452   List the single specified URL from the cache.
453 
454   int wwwoffle_ls_url Return 1 in case of error or 0 if OK.
455 
456   URL *Url The URL to list.
457   ++++++++++++++++++++++++++++++++++++++*/
458 
wwwoffle_ls_url(URL * Url)459 static int wwwoffle_ls_url(URL *Url)
460 {
461  int retval=0;
462 
463  if(chdir(Url->proto))
464    {PrintMessage(Warning,"Cannot change to directory '%s' [%!s].",Url->proto);return(1);}
465 
466  if(chdir(URLToDirName(Url)))
467    {PrintMessage(Warning,"Cannot change to directory '%s/%s' [%!s].",Url->proto,URLToDirName(Url));ChangeBackToSpoolDir();return(1);}
468 
469  retval=ls(URLToFileName(Url,'D',0));
470 
471  ChangeBackToSpoolDir();
472 
473  return(retval);
474 }
475 
476 
477 /*++++++++++++++++++++++++++++++++++++++
478   List the URLs within a directory of the cache.
479 
480   int wwwoffle_ls_dir Return 1 in case of error or 0 if OK.
481 
482   char *name The directory name (protocol/hostname) to list.
483   ++++++++++++++++++++++++++++++++++++++*/
484 
wwwoffle_ls_dir(char * name)485 static int wwwoffle_ls_dir(char *name)
486 {
487  int retval=0;
488  struct dirent* ent;
489  DIR *dir;
490  char *slash;
491 
492  slash=strchr(name,'/');
493  *slash++=0;
494 
495  if(chdir(name))
496    {PrintMessage(Warning,"Cannot change to directory '%s' [%!s].",name);return(1);}
497 
498  if(chdir(slash))
499    {PrintMessage(Warning,"Cannot change to directory '%s/%s' [%!s].",name,slash);ChangeBackToSpoolDir();return(1);}
500 
501  dir=opendir(".");
502 
503  if(!dir)
504    {PrintMessage(Warning,"Cannot open current directory '%s/%s' [%!s].",name,slash);ChangeBackToSpoolDir();return(1);}
505 
506  ent=readdir(dir);
507  if(!ent)
508    {PrintMessage(Warning,"Cannot read current directory '%s/%s' [%!s].",name,slash);closedir(dir);ChangeBackToSpoolDir();return(1);}
509 
510  do
511    {
512     if(ent->d_name[0]=='.' && (ent->d_name[1]==0 || (ent->d_name[1]=='.' && ent->d_name[2]==0)))
513        continue; /* skip . & .. */
514 
515     if(*ent->d_name=='D' && ent->d_name[strlen(ent->d_name)-1]!='~')
516        retval+=ls(ent->d_name);
517    }
518  while((ent=readdir(dir)));
519 
520  closedir(dir);
521 
522  ChangeBackToSpoolDir();
523 
524  return(retval);
525 }
526 
527 
528 /*++++++++++++++++++++++++++++++++++++++
529   List the URLs within the outgoing, monitor or lasttime/prevtime special directory of the cache.
530 
531   int wwwoffle_ls_special Return 1 in case of error or 0 if OK.
532 
533   char *name The name of the directory to list.
534   ++++++++++++++++++++++++++++++++++++++*/
535 
wwwoffle_ls_special(char * name)536 static int wwwoffle_ls_special(char *name)
537 {
538  struct dirent* ent;
539  DIR *dir;
540  int retval=0;
541 
542  if(chdir(name))
543    {PrintMessage(Warning,"Cannot change to directory '%s' [%!s].",name);return(1);}
544 
545  dir=opendir(".");
546 
547  if(!dir)
548    {PrintMessage(Warning,"Cannot open current directory '%s' [%!s].",name);ChangeBackToSpoolDir();return(1);}
549 
550  ent=readdir(dir);
551  if(!ent)
552    {PrintMessage(Warning,"Cannot read current directory '%s' [%!s].",name);closedir(dir);ChangeBackToSpoolDir();return(1);}
553 
554  do
555    {
556     if(ent->d_name[0]=='.' && (ent->d_name[1]==0 || (ent->d_name[1]=='.' && ent->d_name[2]==0)))
557        continue; /* skip . & .. */
558 
559     if((*ent->d_name=='D' || *ent->d_name=='O') && ent->d_name[strlen(ent->d_name)-1]!='~')
560        retval+=ls(ent->d_name);
561    }
562  while((ent=readdir(dir)));
563 
564  closedir(dir);
565 
566  ChangeBackToSpoolDir();
567 
568  return(retval);
569 }
570 
571 
572 /*++++++++++++++++++++++++++++++++++++++
573   List one file.
574 
575   int ls Return 1 in case of error or 0 if OK.
576 
577   char *file The name of the file to ls.
578   ++++++++++++++++++++++++++++++++++++++*/
579 
ls(char * file)580 static int ls(char *file)
581 {
582  struct stat buf;
583  time_t now=-1;
584 
585  if(now==-1)
586     now=time(NULL);
587 
588  if(stat(file,&buf))
589    {PrintMessage(Warning,"Cannot stat the file '%s' [%!s].",file);return(1);}
590  else
591    {
592     URL *Url=FileNameToURL(file);
593 
594     if(Url)
595       {
596        char month[4];
597        struct tm *tim=localtime(&buf.st_mtime);
598 
599        strftime(month,(size_t)4,"%b",tim);
600 
601        if(buf.st_mtime<(now-(180*24*3600)))
602           printf("%s %7ld %3s %2d %5d %s\n",file,(long)buf.st_size,month,tim->tm_mday,tim->tm_year+1900,Url->file);
603        else
604           printf("%s %7ld %3s %2d %2d:%02d %s\n",file,(long)buf.st_size,month,tim->tm_mday,tim->tm_hour,tim->tm_min,Url->file);
605 
606        FreeURL(Url);
607       }
608    }
609 
610  return(0);
611 }
612 
613 
614 /*++++++++++++++++++++++++++++++++++++++
615   Move one URL or host to another.
616 
617   int wwwoffle_mv Return 1 in case of error or 0 if OK.
618 
619   URL *Url1 The source URL.
620 
621   URL *Url2 The destination URL.
622   ++++++++++++++++++++++++++++++++++++++*/
623 
wwwoffle_mv(URL * Url1,URL * Url2)624 static int wwwoffle_mv(URL *Url1,URL *Url2)
625 {
626  struct dirent* ent;
627  DIR *dir;
628 
629  if(chdir(Url1->proto))
630    {PrintMessage(Warning,"Cannot change to directory '%s' [%!s].",Url1->proto);return(1);}
631 
632  if(chdir(URLToDirName(Url1)))
633    {PrintMessage(Warning,"Cannot change to directory '%s/%s' [%!s].",Url1->proto,URLToDirName(Url1));ChangeBackToSpoolDir();return(1);}
634 
635  dir=opendir(".");
636 
637  if(!dir)
638    {PrintMessage(Warning,"Cannot open current directory '%s/%s' [%!s].",Url1->proto,URLToDirName(Url1));ChangeBackToSpoolDir();return(1);}
639 
640  ent=readdir(dir);
641  if(!ent)
642    {PrintMessage(Warning,"Cannot read current directory '%s/%s' [%!s].",Url1->proto,URLToDirName(Url1));closedir(dir);ChangeBackToSpoolDir();return(1);}
643 
644  do
645    {
646     if(ent->d_name[0]=='.' && (ent->d_name[1]==0 || (ent->d_name[1]=='.' && ent->d_name[2]==0)))
647        continue; /* skip . & .. */
648 
649     if(*ent->d_name=='D')
650       {
651        URL *old_Url=FileNameToURL(ent->d_name);
652 
653        if(old_Url && !strncmp(Url1->name,old_Url->name,strlen(Url1->name)))
654          {
655           URL *new_Url;
656           char *new_path,*old_name,*new_name;
657           int newlen,oldlen;
658 #if MAKE_CHANGES
659           int fd2;
660 #endif
661 
662           oldlen=strlen(Url1->path);
663           newlen=strlen(Url2->path);
664 
665           new_path=(char*)malloc(newlen-oldlen+strlen(old_Url->path)+2);
666           strcpy(new_path,Url2->path);
667 
668           if(Url2->path[newlen-1]!='/' && Url1->path[oldlen-1]=='/')
669              strcat(new_path,"/");
670           if(Url2->path[newlen-1]=='/' && Url1->path[oldlen-1]!='/')
671              new_path[newlen-1]=0;
672 
673           strcat(new_path,old_Url->path+oldlen);
674 
675           new_Url=CreateURL(Url2->proto,Url2->hostport,new_path,old_Url->args,old_Url->user,old_Url->pass);
676           free(new_path);
677 
678           old_name=URLToFileName(old_Url,'D',0);
679           new_name=URLToFileName(new_Url,'D',0);
680 
681           printf("  - %s %s\n",old_name,old_Url->file);
682           printf("  + %s %s\n",new_name,new_Url->file);
683 
684           new_path=(char*)malloc(strlen(new_Url->proto)+strlen(URLToDirName(new_Url))+strlen(new_name)+16);
685 
686           sprintf(new_path,"../../%s",new_Url->proto);
687           mkdir(new_path,DEF_DIR_PERM);
688 
689           sprintf(new_path,"../../%s/%s",new_Url->proto,URLToDirName(new_Url));
690           mkdir(new_path,DEF_DIR_PERM);
691 
692           *old_name=*new_name='D';
693           sprintf(new_path,"../../%s/%s/%s",new_Url->proto,URLToDirName(new_Url),new_name);
694 
695 #if MAKE_CHANGES
696           rename(old_name,new_path);
697 #endif
698 
699           old_name=URLToFileName(old_Url,'U',0);
700           new_name=URLToFileName(new_Url,'U',0);
701 
702           sprintf(new_path,"../../%s/%s/%s",new_Url->proto,URLToDirName(new_Url),new_name);
703 
704 #if MAKE_CHANGES
705           fd2=open(new_path,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,DEF_FILE_PERM);
706 
707           if(fd2!=-1)
708             {
709              struct stat buf;
710              struct utimbuf utbuf;
711 
712              init_io(fd2);
713              write_string(fd2,new_Url->name);
714              finish_io(fd2);
715              close(fd2);
716 
717              if(!stat(old_name,&buf))
718                {
719                 utbuf.actime=time(NULL);
720                 utbuf.modtime=buf.st_mtime;
721                 utime(new_path,&utbuf);
722                }
723 
724              unlink(old_name);
725             }
726 #endif
727 
728           free(new_path);
729           FreeURL(new_Url);
730          }
731 
732        if(old_Url)
733           FreeURL(old_Url);
734       }
735    }
736  while((ent=readdir(dir)));
737 
738  closedir(dir);
739 
740  ChangeBackToSpoolDir();
741 
742  return(0);
743 }
744 
745 
746 /*++++++++++++++++++++++++++++++++++++++
747   Delete a URL.
748 
749   int wwwoffle_rm Return 1 in case of error or 0 if OK.
750 
751   URL *Url The URL to delete.
752   ++++++++++++++++++++++++++++++++++++++*/
753 
wwwoffle_rm(URL * Url)754 static int wwwoffle_rm(URL *Url)
755 {
756  char *error=NULL;
757 
758  printf("  - %s\n",Url->file);
759 
760 #if MAKE_CHANGES
761  error=DeleteWebpageSpoolFile(Url,0);
762 #endif
763 
764  return(!!error);
765 }
766 
767 
768 /*++++++++++++++++++++++++++++++++++++++
769   Read a URL and output on stdout.
770 
771   int wwwoffle_read Return 1 in case of error or 0 if OK.
772 
773   URL *Url The URL to read.
774   ++++++++++++++++++++++++++++++++++++++*/
775 
wwwoffle_read(URL * Url)776 static int wwwoffle_read(URL *Url)
777 {
778  char *line=NULL,buffer[IO_BUFFER_SIZE];
779  int n,spool=OpenWebpageSpoolFile(1,Url);
780 #if USE_ZLIB
781  int compression=0;
782 #endif
783 
784  if(spool==-1)
785     return(1);
786 
787  init_io(1);
788  init_io(spool);
789 
790  while((line=read_line(spool,line)))
791    {
792 #if USE_ZLIB
793     if(!strncmp(line,"Pragma: wwwoffle-compressed",(size_t)27))
794        compression=2;
795     else
796 #endif
797        write_string(1,line);
798 
799     if(*line=='\r' || *line=='\n')
800        break;
801    }
802 
803 #if USE_ZLIB
804  if(compression)
805     configure_io_zlib(spool,compression,-1);
806 #endif
807 
808  while((n=read_data(spool,buffer,IO_BUFFER_SIZE))>0)
809     write_data(1,buffer,n);
810 
811  finish_io(1);
812  finish_io(spool);
813  close(spool);
814 
815  return(0);
816 }
817 
818 
819 /*++++++++++++++++++++++++++++++++++++++
820   Write a URL from the input on stdin.
821 
822   int wwwoffle_write Return 1 in case of error or 0 if OK.
823 
824   URL *Url The URL to write.
825   ++++++++++++++++++++++++++++++++++++++*/
826 
wwwoffle_write(URL * Url)827 static int wwwoffle_write(URL *Url)
828 {
829  char buffer[IO_BUFFER_SIZE];
830  int n,spool=OpenWebpageSpoolFile(0,Url);
831 
832  if(spool==-1)
833     return(1);
834 
835  init_io(0);
836  init_io(spool);
837 
838  while((n=read_data(0,buffer,IO_BUFFER_SIZE))>0)
839     write_data(spool,buffer,n);
840 
841  finish_io(0);
842  finish_io(spool);
843  close(spool);
844 
845  return(0);
846 }
847 
848 
849 /*++++++++++++++++++++++++++++++++++++++
850   Print the hash pattern for an URL
851 
852   int wwwoffle_hash Return 1 in case of error or 0 if OK.
853 
854   URL *Url The URL to be hashed.
855   ++++++++++++++++++++++++++++++++++++++*/
856 
wwwoffle_hash(URL * Url)857 static int wwwoffle_hash(URL *Url)
858 {
859  char *name=URLToFileName(Url,'X',0);
860 
861  printf("%s\n",name+1);
862 
863  return(0);
864 }
865 
866 
867 /*++++++++++++++++++++++++++++++++++++++
868   Compress or uncompress the cached copy of the specified URLs.
869 
870   int wwwoffle_gzip Return 1 in case of error or 0 if OK.
871 
872   URL *Url The URL to be compressed.
873 
874   int compress A flag to indicate compression (1) or uncompression (0).
875   ++++++++++++++++++++++++++++++++++++++*/
876 
wwwoffle_gzip(URL * Url,int compress)877 static int wwwoffle_gzip(URL *Url,int compress)
878 {
879  char *direction=compress?"compression":"uncompression";
880 
881 #if USE_ZLIB
882  DIR *dir;
883  struct dirent* ent;
884  struct stat buf;
885 
886  /* Change to the spool directory. */
887 
888  if(chdir(Url->proto))
889    {PrintMessage(Warning,"Cannot change to directory '%s' [%!s]; %s failed.",Url->proto,direction);return(1);}
890 
891  if(chdir(URLToDirName(Url)))
892    {PrintMessage(Warning,"Cannot change to directory '%s/%s' [%!s]; %s failed.",Url->proto,URLToDirName(Url),direction);ChangeBackToSpoolDir();return(1);}
893 
894  dir=opendir(".");
895 
896  if(!dir)
897    {PrintMessage(Warning,"Cannot open current directory '%s/%s' [%!s]; %s failed.",Url->proto,URLToDirName(Url),direction);ChangeBackToSpoolDir();return(1);}
898 
899  ent=readdir(dir);
900  if(!ent)
901    {PrintMessage(Warning,"Cannot read current directory '%s/%s' [%!s]; %s failed.",Url->proto,URLToDirName(Url),direction);closedir(dir);ChangeBackToSpoolDir();return(1);}
902 
903  /* Go through each entry. */
904 
905  do
906    {
907     if(ent->d_name[0]=='.' && (ent->d_name[1]==0 || (ent->d_name[1]=='.' && ent->d_name[2]==0)))
908        continue; /* skip . & .. */
909 
910     if(!stat(ent->d_name,&buf) && S_ISREG(buf.st_mode) && *ent->d_name=='D')
911        gzip_file(Url->proto,URLToDirName(Url),ent->d_name,compress);
912    }
913  while((ent=readdir(dir)));
914 
915  closedir(dir);
916 
917  ChangeBackToSpoolDir();
918 
919  return(0);
920 
921 #else
922 
923  fprintf(stderr,"Error: wwwoffle-tools was compiled without zlib, no %s possible.\n",direction);
924 
925  return(1);
926 
927 #endif /* USE_ZLIB */
928 }
929 
930 
931 #if USE_ZLIB
932 /*++++++++++++++++++++++++++++++++++++++
933   Uncompress the named file (if already compressed).
934 
935   char *proto The protocol directory to uncompress.
936 
937   char *hostport The host and port number directory to uncompress.
938 
939   char *file The name of the file in the current directory to uncompress.
940 
941   int compress Set to 1 if the file is to be compressed, 0 for uncompression.
942   ++++++++++++++++++++++++++++++++++++++*/
943 
gzip_file(char * proto,char * hostport,char * file,int compress)944 static void gzip_file(char *proto,char *hostport,char *file,int compress)
945 {
946  int ifd,ofd;
947  char *zfile;
948  Header *spool_head;
949  char *head,buffer[IO_BUFFER_SIZE];
950  int n;
951  struct stat buf;
952  struct utimbuf ubuf;
953  char *direction=compress?"compress":"uncompress";
954 
955  if(!stat(file,&buf))
956    {
957     ubuf.actime=buf.st_atime;
958     ubuf.modtime=buf.st_mtime;
959    }
960  else
961    {
962     PrintMessage(Inform,"Cannot stat file '%s/%s/%s' to %s it [%!s]; race condition?",proto,hostport,file,direction);
963     return;
964    }
965 
966  ifd=open(file,O_RDONLY|O_BINARY);
967 
968  if(ifd==-1)
969    {
970     PrintMessage(Inform,"Cannot open file '%s/%s/%s' to %s it [%!s]; race condition?",proto,hostport,file,direction);
971     return;
972    }
973 
974  init_io(ifd);
975 
976  ParseReply(ifd,&spool_head);
977 
978  if(!spool_head ||
979     (compress && (GetHeader(spool_head,"Content-Encoding") ||
980                   GetHeader2(spool_head,"Pragma","wwwoffle-compressed") ||
981                   NotCompressed(GetHeader(spool_head,"Content-Type"),NULL))) ||
982     (!compress && !GetHeader2(spool_head,"Pragma","wwwoffle-compressed")))
983    {
984     if(spool_head)
985        FreeHeader(spool_head);
986 
987     finish_io(ifd);
988     close(ifd);
989 
990     utime(file,&ubuf);
991     return;
992    }
993 
994  printf("    %s\n",file);
995 
996  if(compress)
997    {
998     AddToHeader(spool_head,"Content-Encoding","x-gzip");
999     AddToHeader(spool_head,"Pragma","wwwoffle-compressed");
1000     RemoveFromHeader(spool_head,"Content-Length");
1001    }
1002  else
1003    {
1004     RemoveFromHeader(spool_head,"Content-Encoding");
1005     RemoveFromHeader2(spool_head,"Pragma","wwwoffle-compressed");
1006    }
1007 
1008  zfile=(char*)malloc(strlen(file)+4);
1009  strcpy(zfile,file);
1010  strcat(zfile,".z");
1011 
1012  ofd=open(zfile,O_WRONLY|O_BINARY|O_CREAT|O_TRUNC,buf.st_mode&07777);
1013 
1014  if(ofd==-1)
1015    {
1016     PrintMessage(Inform,"Cannot open file '%s/%s/%s' to %s to [%!s].",proto,hostport,zfile,direction);
1017 
1018     FreeHeader(spool_head);
1019     free(zfile);
1020 
1021     finish_io(ifd);
1022     close(ifd);
1023 
1024     utime(file,&ubuf);
1025     return;
1026    }
1027 
1028  init_io(ofd);
1029 
1030  head=HeaderString(spool_head);
1031  FreeHeader(spool_head);
1032 
1033  write_string(ofd,head);
1034  free(head);
1035 
1036  if(compress)
1037     configure_io_zlib(ofd,-1,2);
1038  else
1039     configure_io_zlib(ifd,2,-1);
1040 
1041  while((n=read_data(ifd,buffer,IO_BUFFER_SIZE))>0)
1042     write_data(ofd,buffer,n);
1043 
1044  finish_io(ifd);
1045  close(ifd);
1046 
1047  finish_io(ofd);
1048  close(ofd);
1049 
1050 #if MAKE_CHANGES
1051  if(rename(zfile,file))
1052    {
1053     PrintMessage(Inform,"Cannot rename file '%s/%s/%s' to '%s/%s/%s' [%!s].",proto,hostport,zfile,proto,hostport,file);
1054     unlink(zfile);
1055    }
1056 #else
1057  unlink(zfile);
1058 #endif
1059 
1060  utime(file,&ubuf);
1061 
1062  free(zfile);
1063 }
1064 #endif
1065 
1066 
1067 /*++++++++++++++++++++++++++++++++++++++
1068   Check the cache for inconsistent filenames and fix them.
1069 
1070   int wwwoffle_fsck Return 1 in case of any files corrected or 0 if OK.
1071   ++++++++++++++++++++++++++++++++++++++*/
1072 
wwwoffle_fsck(void)1073 static int wwwoffle_fsck(void)
1074 {
1075  int i;
1076  struct stat buf;
1077 
1078  for(i=0;i<3;i++)
1079    {
1080     char *proto;
1081 
1082     if(i==0)
1083        proto="http";
1084     else if(i==1)
1085        proto="ftp";
1086     else
1087        proto="finger";
1088 
1089     if(stat(proto,&buf))
1090        PrintMessage(Inform,"Cannot stat the '%s' directory [%!s]; not checked.",proto);
1091     else
1092       {
1093        printf("Checking %s\n",proto);
1094 
1095        if(chdir(proto))
1096           PrintMessage(Warning,"Cannot change to the '%s' directory [%!s]; not checked.",proto);
1097        else
1098          {
1099           wwwoffle_fsck_check_proto(proto);
1100 
1101           ChangeBackToSpoolDir();
1102          }
1103       }
1104    }
1105 
1106  for(i=0;i<3;i++)
1107    {
1108     char *special;
1109 
1110     if(i==0)
1111        special="outgoing";
1112     else if(i==1)
1113        special="lasttime";
1114     else
1115        special="monitor";
1116 
1117     if(stat(special,&buf))
1118        PrintMessage(Inform,"Cannot stat the '%s' directory [%!s]; not checked.",special);
1119     else
1120       {
1121        printf("Checking %s\n",special);
1122 
1123        if(chdir(special))
1124           PrintMessage(Warning,"Cannot change to the '%s' directory [%!s]; not checked.",special);
1125        else
1126          {
1127           wwwoffle_fsck_check_dir(NULL,NULL,special);
1128 
1129           ChangeBackToSpoolDir();
1130          }
1131       }
1132    }
1133 
1134  return(0);
1135 }
1136 
1137 
1138 /*++++++++++++++++++++++++++++++++++++++
1139   Check a complete protocol directory.
1140 
1141   char *proto The protocol of the spool directory we are in.
1142   ++++++++++++++++++++++++++++++++++++++*/
1143 
wwwoffle_fsck_check_proto(char * proto)1144 static void wwwoffle_fsck_check_proto(char *proto)
1145 {
1146  DIR *dir;
1147  struct dirent* ent;
1148  struct stat buf;
1149 
1150  /* Open the spool directory. */
1151 
1152  dir=opendir(".");
1153  if(!dir)
1154    {PrintMessage(Warning,"Cannot open spool directory '%s' [%!s]; checking failed.",proto);return;}
1155 
1156  ent=readdir(dir);
1157  if(!ent)
1158    {PrintMessage(Warning,"Cannot read spool directory '%s' [%!s]; checking failed.",proto);closedir(dir);return;}
1159 
1160  /* Go through each entry. */
1161 
1162  do
1163    {
1164     if(ent->d_name[0]=='.' && (ent->d_name[1]==0 || (ent->d_name[1]=='.' && ent->d_name[2]==0)))
1165        continue; /* skip . & .. */
1166 
1167     if(stat(ent->d_name,&buf))
1168        PrintMessage(Warning,"Cannot stat file '%s/%s' [%!s] not checked.",proto,ent->d_name);
1169     else if(S_ISDIR(buf.st_mode))
1170       {
1171        if(chdir(ent->d_name))
1172           PrintMessage(Warning,"Cannot change to the '%s/%s' directory [%!s]; not checked.",proto,ent->d_name);
1173        else
1174          {
1175           printf("  Checking %s\n",ent->d_name);
1176 
1177           wwwoffle_fsck_check_dir(proto,ent->d_name,NULL);
1178 
1179           ChangeBackToSpoolDir();
1180           chdir(proto);
1181          }
1182       }
1183    }
1184  while((ent=readdir(dir)));
1185 
1186  closedir(dir);
1187 }
1188 
1189 
1190 /*++++++++++++++++++++++++++++++++++++++
1191   Check a complete directory directory, either one host or a special directory.
1192 
1193   char *proto The protocol of the spool directory we are in.
1194 
1195   char *host The hostname of the spool directory we are in.
1196 
1197   char *special The name of the special directory (either special or proto and host are non-NULL).
1198   ++++++++++++++++++++++++++++++++++++++*/
1199 
wwwoffle_fsck_check_dir(char * proto,char * host,char * special)1200 static void wwwoffle_fsck_check_dir(char *proto,char *host,char *special)
1201 {
1202  DIR *dir;
1203  struct dirent* ent;
1204  struct stat buf;
1205 
1206  /* Open the spool directory. */
1207 
1208  dir=opendir(".");
1209  if(!dir)
1210    {
1211     if(special)
1212        PrintMessage(Warning,"Cannot open spool directory '%s' [%!s]; checking failed.",special);
1213     else
1214        PrintMessage(Warning,"Cannot open spool directory '%s/%s' [%!s]; checking failed.",proto,host);
1215     return;
1216    }
1217 
1218  ent=readdir(dir);
1219  if(!ent)
1220    {
1221     if(special)
1222        PrintMessage(Warning,"Cannot read spool directory '%s' [%!s]; checking failed.",special);
1223     else
1224        PrintMessage(Warning,"Cannot read spool directory '%s/%s' [%!s]; checking failed.",proto,host);
1225     closedir(dir);
1226     return;
1227    }
1228 
1229  /* Go through each entry. */
1230 
1231  do
1232    {
1233     if(ent->d_name[0]=='.' && (ent->d_name[1]==0 || (ent->d_name[1]=='.' && ent->d_name[2]==0)))
1234        continue; /* skip . & .. */
1235 
1236     if(!stat(ent->d_name,&buf) && (*ent->d_name=='O' || *ent->d_name=='D')  && S_ISREG(buf.st_mode))
1237       {
1238        char *url=FileNameTo_url(ent->d_name);
1239 
1240        if(url)
1241          {
1242           URL *Url=SplitURL(url);
1243           char *newname=URLToFileName(Url,'X',0);
1244           char *oldname=ent->d_name;
1245 
1246           if(strncmp(oldname+1,newname+1,strlen(newname+1)) || strcmp(url,Url->file))
1247             {
1248 #if MAKE_CHANGES
1249              int ufd;
1250 #endif
1251 
1252              printf("  - %s %s\n",oldname+1,url);
1253              printf("  + %s %s\n",newname+1,Url->file);
1254 
1255 #if MAKE_CHANGES
1256              *oldname=*newname='D';
1257              rename(oldname,newname);
1258              *oldname=*newname='U';
1259              rename(oldname,newname);
1260              if(special)
1261                {
1262                 *oldname=*newname='O';
1263                 rename(oldname,newname);
1264                 *oldname=*newname='M';
1265                 rename(oldname,newname);
1266                }
1267 
1268              ufd=open(newname,O_WRONLY|O_TRUNC|O_BINARY);
1269 
1270              if(ufd!=-1)
1271                {
1272                 init_io(ufd);
1273 
1274                 write_string(ufd,Url->file);
1275 
1276                 finish_io(ufd);
1277                 close(ufd);
1278                }
1279 #endif
1280             }
1281 
1282           FreeURL(Url);
1283           free(url);
1284          }
1285       }
1286    }
1287  while((ent=readdir(dir)));
1288 
1289  closedir(dir);
1290 }
1291 
1292 
1293 /*++++++++++++++++++++++++++++++++++++++
1294   Convert a filename to a URL.
1295 
1296   char *FileNameTo_url Returns the URL.
1297 
1298   char *file The file name.
1299 
1300   A copy of the function from spool.c but returning the actual string, not a canonicalised version of it.
1301   ++++++++++++++++++++++++++++++++++++++*/
1302 
FileNameTo_url(char * file)1303 static char *FileNameTo_url(char *file)
1304 {
1305  struct stat buf;
1306  char *url,*copy;
1307  int ufd;
1308  ssize_t nr;
1309 
1310  if(!file || !*file)
1311     return(NULL);
1312 
1313  copy=(char*)malloc(strlen(file)+1);
1314  strcpy(copy,file);
1315 
1316  *copy='U';
1317 
1318  ufd=open(copy,O_RDONLY|O_BINARY);
1319 
1320  free(copy);
1321 
1322  if(ufd==-1)
1323     return(NULL);
1324 
1325  if(fstat(ufd,&buf))
1326     return(NULL);
1327 
1328  url=(char*)malloc((size_t)buf.st_size+1);
1329 
1330  init_io(ufd);
1331 
1332  nr=read_data(ufd,url,buf.st_size);
1333 
1334  finish_io(ufd);
1335  close(ufd);
1336 
1337  if(nr!=buf.st_size)
1338    {
1339     free(url);
1340     return(NULL);
1341    }
1342 
1343  url[nr]=0;
1344 
1345  return(url);
1346 }
1347