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