1 /***************************************
2 WWWOFFLE - World Wide Web Offline Explorer - Version 2.9j.
3
4 Configuration file reading and writing functions.
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 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 # include <sys/time.h>
28 # else
29 # include <time.h>
30 # endif
31 #endif
32
33 #include <limits.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <pwd.h>
39 #include <grp.h>
40
41 #include "io.h"
42 #include "misc.h"
43 #include "proto.h"
44 #include "errors.h"
45 #include "configpriv.h"
46 #include "config.h"
47
48
49 #ifndef O_BINARY
50 /*+ A work-around for needing O_BINARY with Win32 to use binary mode. +*/
51 #define O_BINARY 0
52 #endif
53
54 #ifndef PATH_MAX
55 /*+ The maximum pathname length in characters. +*/
56 #define PATH_MAX 4096
57 #endif
58
59
60 /*+ The state of the parser +*/
61
62 typedef enum _ParserState
63 {
64 OutsideSection, /*+ Outside of a section, (a comment or blank). +*/
65
66 StartSection, /*+ After seeing the sectio nname before deciding the type. +*/
67 StartSectionCurly, /*+ After seeing the curly bracket '{'. +*/
68 StartSectionSquare, /*+ After seeing the square bracket '['. +*/
69 StartSectionIncluded, /*+ After opening the included file. +*/
70
71 InsideSectionCurly, /*+ Parsing within a normal section delimited by '{' & '}'. +*/
72 InsideSectionSquare, /*+ Looking for the included filename within a section delimited by '[' & ']'. +*/
73 InsideSectionIncluded, /*+ Parsing within an included file. +*/
74
75 Finished /*+ At end of file. +*/
76 }
77 ParserState;
78
79
80 /* Local functions */
81
82 static char *filename_or_symlink_target(const char *name);
83
84 static /*@null@*/ char *InitParser(void);
85 static /*@null@*/ char *ParseLine(/*@out@*/ char **line);
86 static /*@null@*/ char *ParseItem(char *line,/*@out@*/ char **url_str,/*@out@*/ char **key_str,/*@out@*/ char **val_str);
87 static /*@null@*/ char *ParseEntry(const ConfigItemDef *itemdef,/*@out@*/ ConfigItem *item,/*@null@*/ const char *url_str,const char *key_str,/*@null@*/ const char *val_str);
88
89 static int isanumber(const char *string);
90
91
92 /* Local variables */
93
94 static char *parse_name; /*+ The name of the configuration file. +*/
95 static int parse_file; /*+ The file descriptor of the configuration file. +*/
96 static int parse_line; /*+ The line number in the configuration file. +*/
97
98 static char *parse_name_org; /*+ The name of the original configuration file when parsing an included one. +*/
99 static int parse_file_org; /*+ The file descriptor of the original configuration file when parsing an included one. +*/
100 static int parse_line_org; /*+ The line number in the original configuration file when parsing an included one. +*/
101
102 static int parse_section; /*+ The current section of the configuration file. +*/
103 static int parse_item; /*+ The current item in the configuration file. +*/
104 static ParserState parse_state; /*+ The parser state. +*/
105
106
107 /*++++++++++++++++++++++++++++++++++++++
108 Read the data from the file.
109
110 char *ReadConfigFile Returns the error message or NULL if OK.
111
112 int read_startup If true then only the startup section of the configuration file is read.
113 If false then only the other sections are read.
114 ++++++++++++++++++++++++++++++++++++++*/
115
ReadConfigFile(int read_startup)116 char *ReadConfigFile(int read_startup)
117 {
118 char *errmsg=NULL;
119
120 CreateBackupConfigFile();
121
122 errmsg=InitParser();
123
124 if(!errmsg)
125 {
126 char *line=NULL;
127
128 do
129 {
130 if((errmsg=ParseLine(&line)))
131 break;
132
133 if((parse_section==0 && read_startup) ||
134 (parse_section>0 && !read_startup))
135 {
136 char *url_str,*key_str,*val_str;
137
138 if((errmsg=ParseItem(line,&url_str,&key_str,&val_str)))
139 break;
140
141 if(parse_item!=-1)
142 {
143 if((errmsg=ParseEntry(&CurrentConfig.sections[parse_section]->itemdefs[parse_item],
144 CurrentConfig.sections[parse_section]->itemdefs[parse_item].item,
145 url_str,key_str,val_str)))
146 break;
147 }
148 }
149 }
150 while(parse_state!=Finished);
151
152 if(line)
153 free(line);
154 }
155
156 if(parse_file!=-1)
157 {
158 finish_io(parse_file);
159 close(parse_file);
160 }
161 if(parse_file_org!=-1)
162 {
163 finish_io(parse_file_org);
164 close(parse_file_org);
165 }
166
167 if(errmsg)
168 RestoreBackupConfigFile();
169 else
170 PurgeBackupConfigFile(!read_startup);
171
172 if(errmsg)
173 {
174 char *newerrmsg=(char*)malloc(strlen(errmsg)+64+MAX_INT_STR+strlen(parse_name));
175 sprintf(newerrmsg,"Configuration file syntax error at line %d in '%s'; %s\n",parse_line,parse_name,errmsg); /* Used in wwwoffle.c */
176 free(errmsg);
177 errmsg=newerrmsg;
178 }
179
180 return(errmsg);
181 }
182
183
184 /*++++++++++++++++++++++++++++++++++++++
185 Dump the contents of the configuration file
186
187 int fd The file descriptor to write to.
188 ++++++++++++++++++++++++++++++++++++++*/
189
DumpConfigFile(int fd)190 void DumpConfigFile(int fd)
191 {
192 int s,i,e;
193
194 write_string(fd,"# WWWOFFLE CURRENT CONFIGURATION\n");
195
196 for(s=0;s<CurrentConfig.nsections;s++)
197 {
198 write_formatted(fd,"\n%s\n{\n",CurrentConfig.sections[s]->name);
199
200 for(i=0;i<CurrentConfig.sections[s]->nitemdefs;i++)
201 {
202 if(*CurrentConfig.sections[s]->itemdefs[i].name)
203 write_formatted(fd,"# Item %s\n",CurrentConfig.sections[s]->itemdefs[i].name);
204 else
205 write_string(fd,"# Item [default]\n");
206
207 if(*CurrentConfig.sections[s]->itemdefs[i].item)
208 for(e=0;e<(*CurrentConfig.sections[s]->itemdefs[i].item)->nentries;e++)
209 {
210 char *string=ConfigEntryString(*CurrentConfig.sections[s]->itemdefs[i].item,e);
211 write_formatted(fd," %s\n",string);
212 free(string);
213 }
214 }
215
216 write_string(fd,"}\n");
217 }
218 }
219
220
221 /*++++++++++++++++++++++++++++++++++++++
222 Modify an entry in the configuration file.
223
224 char *ModifyConfigFile Returns a string detailing the error if there is one.
225
226 int section The section of the configuration file to modify.
227
228 int item The item of the configuration to modify.
229
230 char *newentry The new entry to insert or change to (or NULL if deleting one).
231
232 char *preventry The previous entry in the current list (or NULL if not adding after one).
233
234 char *sameentry The same entry in the current list (or NULL if not replacing one).
235
236 char *nextentry The next entry in the current list (or NULL if not adding before one).
237 ++++++++++++++++++++++++++++++++++++++*/
238
ModifyConfigFile(int section,int item,char * newentry,char * preventry,char * sameentry,char * nextentry)239 char *ModifyConfigFile(int section,int item,char *newentry,char *preventry,char *sameentry,char *nextentry)
240 {
241 char *errmsg=NULL;
242 char **names=(char**)calloc((size_t)(1+CurrentConfig.nsections),sizeof(char*));
243 int file=-1,file_org=-1;
244 ConfigItem dummy=NULL;
245 int matched=0;
246 int s,rename_failed=0;
247
248 /* Initialise the parser and open the new file. */
249
250 errmsg=InitParser();
251
252 if(!errmsg)
253 {
254 names[0]=filename_or_symlink_target(parse_name);
255
256 strcat(names[0],".new");
257
258 file=open(names[0],O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,0600);
259
260 if(file==-1)
261 {
262 errmsg=(char*)malloc(64+strlen(names[0]));
263 sprintf(errmsg,"Cannot open the configuration file '%s' for writing.",names[0]);
264 }
265 else
266 init_io(file);
267 }
268
269 /* Parse the file */
270
271 if(!errmsg)
272 {
273 char *line=NULL;
274
275 do
276 {
277 if((errmsg=ParseLine(&line)))
278 break;
279
280 if(parse_section==section && line)
281 {
282 char *url_str,*key_str,*val_str;
283 char *copy;
284
285 /* Insert a new entry for a non-existing item. */
286
287 if(newentry && !preventry && !sameentry && !nextentry && !matched &&
288 (parse_state==InsideSectionCurly || parse_state==InsideSectionIncluded))
289 {
290 char *copyentry=(char*)malloc(strlen(newentry)+1);
291 strcpy(copyentry,newentry);
292
293 if((errmsg=ParseItem(copyentry,&url_str,&key_str,&val_str)))
294 break;
295
296 if((errmsg=ParseEntry(&CurrentConfig.sections[section]->itemdefs[item],
297 &dummy,
298 url_str,key_str,val_str)))
299 break;
300
301 write_formatted(file,"\n# WWWOFFLE Configuration Edit Inserted: %s\n %s\n\n",RFC822Date(time(NULL),0),newentry);
302
303 matched=1;
304
305 free(copyentry);
306 }
307
308 copy=(char*)malloc(strlen(line)+1);
309 strcpy(copy,line);
310
311 if((errmsg=ParseItem(copy,&url_str,&key_str,&val_str)))
312 break;
313
314 if(parse_item==item)
315 {
316 char *thisentry;
317
318 if((errmsg=ParseEntry(&CurrentConfig.sections[section]->itemdefs[item],
319 &dummy,
320 url_str,key_str,val_str)))
321 break;
322
323 thisentry=ConfigEntryString(dummy,dummy->nentries-1);
324
325 /* Insert a new entry before the current one */
326
327 if(newentry && nextentry && !strcmp(thisentry,nextentry))
328 {
329 char *copyentry=(char*)malloc(strlen(newentry)+1);
330 strcpy(copyentry,newentry);
331
332 if((errmsg=ParseItem(copyentry,&url_str,&key_str,&val_str)))
333 break;
334
335 if((errmsg=ParseEntry(&CurrentConfig.sections[section]->itemdefs[item],
336 &dummy,
337 url_str,key_str,val_str)))
338 break;
339
340 write_formatted(file,"\n# WWWOFFLE Configuration Edit Inserted: %s\n %s\n\n",RFC822Date(time(NULL),0),newentry);
341 write_string(file,line);
342
343 matched=1;
344
345 free(copyentry);
346 }
347
348 /* Insert a new entry after the current one */
349
350 else if(newentry && preventry && !strcmp(thisentry,preventry))
351 {
352 char *copyentry=(char*)malloc(strlen(newentry)+1);
353 strcpy(copyentry,newentry);
354
355 if((errmsg=ParseItem(copyentry,&url_str,&key_str,&val_str)))
356 break;
357
358 if((errmsg=ParseEntry(&CurrentConfig.sections[section]->itemdefs[item],
359 &dummy,
360 url_str,key_str,val_str)))
361 break;
362
363 write_string(file,line);
364 write_formatted(file,"\n# WWWOFFLE Configuration Edit Inserted: %s\n %s\n\n",RFC822Date(time(NULL),0),newentry);
365
366 matched=1;
367
368 free(copyentry);
369 }
370
371 /* Delete an entry */
372
373 else if(!newentry && sameentry && !strcmp(thisentry,sameentry))
374 {
375 write_formatted(file,"\n# WWWOFFLE Configuration Edit Deleted: %s\n#%s",RFC822Date(time(NULL),0),line);
376
377 matched=1;
378 }
379
380 /* Change an entry */
381
382 else if(newentry && sameentry && !strcmp(thisentry,sameentry))
383 {
384 char *copyentry=(char*)malloc(strlen(newentry)+1);
385 strcpy(copyentry,newentry);
386
387 if(CurrentConfig.sections[section]->itemdefs[item].same_key==0 &&
388 CurrentConfig.sections[section]->itemdefs[item].url_type==0)
389 {
390 FreeConfigItem(dummy);
391 dummy=NULL;
392 }
393
394 if((errmsg=ParseItem(copyentry,&url_str,&key_str,&val_str)))
395 break;
396
397 if((errmsg=ParseEntry(&CurrentConfig.sections[section]->itemdefs[item],
398 &dummy,
399 url_str,key_str,val_str)))
400 break;
401
402 write_formatted(file,"# WWWOFFLE Configuration Edit Changed: %s\n#%s",RFC822Date(time(NULL),0),line);
403 write_formatted(file," %s\n",newentry);
404
405 matched=1;
406
407 free(copyentry);
408 }
409 else
410 write_string(file,line);
411
412 free(thisentry);
413 }
414 else
415 write_string(file,line);
416
417 free(copy);
418 }
419 else if(line)
420 write_string(file,line);
421
422 if(parse_state==StartSectionIncluded && file_org==-1)
423 {
424 names[parse_section+1]=filename_or_symlink_target(parse_name);
425 strcat(names[parse_section+1],".new");
426
427 file_org=file;
428 file=open(names[parse_section+1],O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,0600);
429
430 if(file==-1)
431 {
432 errmsg=(char*)malloc(48+strlen(names[parse_section+1]));
433 sprintf(errmsg,"Cannot open the included file '%s' for writing.",names[parse_section+1]);
434 break;
435 }
436
437 init_io(file);
438 }
439 else if(parse_state==StartSectionSquare && file_org!=-1)
440 {
441 finish_io(file);
442 close(file);
443 file=file_org;
444 file_org=-1;
445 }
446 }
447 while(parse_state!=Finished);
448
449 if(line)
450 free(line);
451 }
452
453 if(!errmsg && !matched && (preventry || sameentry || nextentry))
454 {
455 char *whichentry=sameentry?sameentry:preventry?preventry:nextentry;
456
457 errmsg=(char*)malloc(64+strlen(whichentry));
458 sprintf(errmsg,"No entry to match '%s' was found to make the change.",whichentry);
459 }
460
461 if(file!=-1)
462 {
463 finish_io(file);
464 close(file);
465 }
466 if(file_org!=-1)
467 {
468 finish_io(file_org);
469 close(file_org);
470 }
471 if(parse_file!=-1)
472 {
473 finish_io(parse_file);
474 close(parse_file);
475 }
476 if(parse_file_org!=-1)
477 {
478 finish_io(parse_file_org);
479 close(parse_file_org);
480 }
481
482 for(s=0;s<=CurrentConfig.nsections;s++)
483 if(names[s])
484 {
485 if(!errmsg)
486 {
487 struct stat buf;
488 char *name=(char*)malloc(strlen(names[s])+1);
489 char *name_bak=(char*)malloc(strlen(names[s])+16);
490
491 strcpy(name,names[s]);
492 name[strlen(name)-4]=0;
493
494 strcpy(name_bak,names[s]);
495 strcpy(name_bak+strlen(name_bak)-3,"bak");
496
497 if(!stat(name,&buf))
498 {
499 chown(names[s],buf.st_uid,buf.st_gid);
500 chmod(names[s],buf.st_mode&(~S_IFMT));
501 }
502
503 if(rename(name,name_bak))
504 {
505 rename_failed++;
506 PrintMessage(Warning,"Cannot rename '%s' to '%s' when modifying configuration entry [%!s].",name,name_bak);
507 }
508 if(rename(names[s],name))
509 {
510 rename_failed++;
511 PrintMessage(Warning,"Cannot rename '%s' to '%s' when modifying configuration entry [%!s].",name[s],name);
512 }
513
514 free(name);
515 free(name_bak);
516 }
517 else
518 unlink(names[s]);
519
520 free(names[s]);
521 }
522
523 if(rename_failed)
524 {
525 errmsg=(char*)malloc((size_t)120);
526 strcpy(errmsg,"There were problems renaming files, check the error log (this might stop the change you tried to make).");
527 }
528
529 FreeConfigItem(dummy);
530
531 free(names);
532
533 return(errmsg);
534 }
535
536
537 /*++++++++++++++++++++++++++++++++++++++
538 Find the real filename of a potential symbolic link.
539
540 char *filename_or_symlink_target Returns the real file name.
541
542 const char *name The file name that may be a symbolic link.
543 ++++++++++++++++++++++++++++++++++++++*/
544
filename_or_symlink_target(const char * name)545 static char *filename_or_symlink_target(const char *name)
546 {
547 struct stat buf;
548 char linkname[PATH_MAX+1];
549 char *result=NULL;
550
551 if(!stat(name,&buf) && buf.st_mode&S_IFLNK)
552 {
553 int linklen=0;
554
555 if((linklen=readlink(name,linkname,(size_t)PATH_MAX))!=-1)
556 {
557 linkname[linklen]=0;
558
559 if(*linkname=='/')
560 {
561 result=(char*)malloc(linklen+8);
562 strcpy(result,linkname);
563 }
564 else
565 {
566 char *p;
567 result=(char*)malloc(strlen(name)+linklen+8);
568 strcpy(result,name);
569 p=result+strlen(result)-1;
570 while(p>=result && *p!='/')
571 p--;
572 strcpy(p+1,linkname);
573 CanonicaliseName(result);
574 }
575 }
576 }
577
578 if(!result)
579 {
580 result=(char*)malloc(strlen(name)+8);
581 strcpy(result,name);
582 }
583
584 return(result);
585 }
586
587
588 /*++++++++++++++++++++++++++++++++++++++
589 Initialise the file parser.
590
591 char *InitParser Return an error string in case of error.
592 ++++++++++++++++++++++++++++++++++++++*/
593
InitParser(void)594 static char *InitParser(void)
595 {
596 parse_name=CurrentConfig.name;
597 parse_file=open(parse_name,O_RDONLY|O_BINARY);
598 parse_line=0;
599
600 parse_name_org=NULL;
601 parse_file_org=-1;
602 parse_line_org=0;
603
604 parse_section=-1;
605 parse_item=-1;
606 parse_state=OutsideSection;
607
608 if(parse_file==-1)
609 {
610 char *errmsg=(char*)malloc(64+strlen(parse_name));
611 sprintf(errmsg,"Cannot open the configuration file '%s' for reading.",parse_name);
612 return(errmsg);
613 }
614
615 init_io(parse_file);
616
617 return(NULL);
618 }
619
620
621 /*++++++++++++++++++++++++++++++++++++++
622 Parse the current line of the configuration file.
623
624 char *ParseLine Returns an error message if there is one.
625
626 char **line The line just read from the file or NULL.
627 ++++++++++++++++++++++++++++++++++++++*/
628
ParseLine(char ** line)629 static char *ParseLine(char **line)
630 {
631 char *errmsg=NULL;
632
633 /* Read from the line and make a copy */
634
635 *line=read_line(parse_file,*line);
636
637 parse_line++;
638
639 parse_item=-1;
640
641 /* At the end of the file, finish, error or close included file. */
642
643 if(!*line)
644 {
645 if(parse_state==OutsideSection)
646 parse_state=Finished;
647 else if(parse_state==StartSectionIncluded || parse_state==InsideSectionIncluded)
648 {
649 finish_io(parse_file);
650 close(parse_file);
651 free(parse_name);
652
653 parse_file=parse_file_org;
654 parse_file_org=-1;
655
656 parse_name=parse_name_org;
657 parse_name_org=NULL;
658
659 parse_line=parse_line_org;
660 parse_line_org=0;
661
662 parse_state=StartSectionSquare;
663 }
664 else
665 {
666 errmsg=(char*)malloc((size_t)32);
667 strcpy(errmsg,"Unexpected end of file.");
668 }
669 }
670 else
671 {
672 char *l,*r;
673
674 /* Trim the line. */
675
676 l=*line;
677 r=*line+strlen(*line)-1;
678
679 while(isspace(*l))
680 l++;
681
682 while(r>l && isspace(*r))
683 r--;
684 r++;
685
686 /* Outside of section, searching for a section. */
687
688 if(parse_state==OutsideSection)
689 {
690 if(*l!='#' && *l!=0)
691 {
692 for(parse_section=0;parse_section<CurrentConfig.nsections;parse_section++)
693 if(!strncmp(CurrentConfig.sections[parse_section]->name,l,r-l))
694 {
695 parse_state=StartSection;
696 break;
697 }
698
699 if(parse_section==CurrentConfig.nsections)
700 {
701 errmsg=(char*)malloc(64+strlen(l));
702 sprintf(errmsg,"Unrecognised text outside of section (not section label) '%s'.",l);
703 parse_section=-1;
704 }
705 }
706 }
707
708 /* The start of a section, undecided which type. */
709
710 else if(parse_state==StartSection)
711 {
712 if(*l=='{' && (l+1)==r)
713 {
714 parse_state=StartSectionCurly;
715 }
716 else if(*l=='[' && (l+1)==r)
717 {
718 parse_state=StartSectionSquare;
719 }
720 else if(*l!='{' && *l!='[')
721 {
722 errmsg=(char*)malloc((size_t)48);
723 strcpy(errmsg,"Start of section must be '{' or '['.");
724 }
725 else
726 {
727 errmsg=(char*)malloc((size_t)48);
728 sprintf(errmsg,"Start of section '%c' has trailing junk.",*l);
729 }
730 }
731
732 /* Inside a normal '{...}' section. */
733
734 else if(parse_state==StartSectionCurly || parse_state==InsideSectionCurly)
735 {
736 parse_state=InsideSectionCurly;
737
738 if(*l=='}' && (l+1)==r)
739 {
740 parse_state=OutsideSection;
741 parse_section=-1;
742 }
743 else if(*l=='}')
744 {
745 errmsg=(char*)malloc((size_t)48);
746 sprintf(errmsg,"End of section '%c' has trailing junk.",*l);
747 }
748 }
749
750 /* Inside a include '[...]' section. */
751
752 else if(parse_state==StartSectionSquare || parse_state==InsideSectionSquare)
753 {
754 parse_state=InsideSectionSquare;
755
756 if(*l==']' && (l+1)==r)
757 {
758 parse_state=OutsideSection;
759 parse_section=-1;
760 }
761 else if(*l==']')
762 {
763 errmsg=(char*)malloc((size_t)48);
764 sprintf(errmsg,"End of section '%c' has trailing junk.",*l);
765 }
766 else if(*l!='#' && *l!=0 && strchr(l,'/'))
767 {
768 errmsg=(char*)malloc((size_t)64);
769 strcpy(errmsg,"Included file must be in same directory (no '/').");
770 }
771 else if(*l!='#' && *l!=0)
772 {
773 char *rr;
774 char *inc_parse_name;
775 int inc_parse_file;
776
777 inc_parse_name=(char*)malloc(strlen(CurrentConfig.name)+(r-l)+1);
778
779 strcpy(inc_parse_name,CurrentConfig.name);
780
781 rr=inc_parse_name+strlen(inc_parse_name)-1;
782 while(rr>inc_parse_name && *rr!='/')
783 rr--;
784
785 strncpy(rr+1,l,(size_t)(r-l));
786 *((rr+1)+(r-l))=0;
787
788 inc_parse_file=open(inc_parse_name,O_RDONLY|O_BINARY);
789
790 if(inc_parse_file==-1)
791 {
792 errmsg=(char*)malloc(48+strlen(inc_parse_name));
793 sprintf(errmsg,"Cannot open the included file '%s' for reading.",inc_parse_name);
794 }
795 else
796 {
797 init_io(inc_parse_file);
798
799 parse_state=StartSectionIncluded;
800
801 parse_name_org=parse_name;
802 parse_file_org=parse_file;
803 parse_line_org=parse_line;
804
805 parse_name=inc_parse_name;
806 parse_file=inc_parse_file;
807 parse_line=0;
808 }
809 }
810 }
811
812 else if(parse_state==StartSectionIncluded || parse_state==InsideSectionIncluded)
813 {
814 parse_state=InsideSectionIncluded;
815 }
816 }
817
818 return(errmsg);
819 }
820
821
822 /*++++++++++++++++++++++++++++++++++++++
823 Parse the item from the current line.
824
825 char *ParseItem Returns an error message string in case of a problem.
826
827 char *line The line to parse (modified by the function).
828
829 char **url_str Returns the URL string or NULL.
830
831 char **key_str Returns the key string or NULL.
832
833 char **val_str Returns the value string or NULL.
834 ++++++++++++++++++++++++++++++++++++++*/
835
ParseItem(char * line,char ** url_str,char ** key_str,char ** val_str)836 static char *ParseItem(char *line,char **url_str,char **key_str,char **val_str)
837 {
838 *url_str=NULL;
839 *key_str=NULL;
840 *val_str=NULL;
841
842 parse_item=-1;
843
844 if(parse_state==InsideSectionCurly || parse_state==InsideSectionIncluded)
845 {
846 char *url=NULL,*key=NULL,*val=NULL;
847 char *l,*r;
848
849 l=line;
850
851 while(isspace(*l))
852 l++;
853
854 if(!*l || *l=='#')
855 return(NULL);
856
857 r=line+strlen(line)-1;
858
859 while(r>l && isspace(*r))
860 *r--=0;
861
862 if(*l=='<')
863 {
864 char *uu;
865
866 uu=url=l+1;
867 while(*uu && *uu!='>')
868 uu++;
869 if(!*uu)
870 {
871 char *errmsg=(char*)malloc((size_t)32);
872 strcpy(errmsg,"No '>' to match the '<'.");
873 return(errmsg);
874 }
875
876 *uu=0;
877 key=uu+1;
878 while(*key && isspace(*key))
879 key++;
880 if(!*key)
881 {
882 char *errmsg=(char*)malloc((size_t)48);
883 strcpy(errmsg,"No configuration entry following the '<...>'.");
884 return(errmsg);
885 }
886 }
887 else
888 key=l;
889
890 for(parse_item=0;parse_item<CurrentConfig.sections[parse_section]->nitemdefs;parse_item++)
891 if(!*CurrentConfig.sections[parse_section]->itemdefs[parse_item].name ||
892 !strncmp(CurrentConfig.sections[parse_section]->itemdefs[parse_item].name,key,strlen(CurrentConfig.sections[parse_section]->itemdefs[parse_item].name)))
893 {
894 char *ll;
895
896 if(*CurrentConfig.sections[parse_section]->itemdefs[parse_item].name)
897 {
898 ll=key+strlen(CurrentConfig.sections[parse_section]->itemdefs[parse_item].name);
899
900 if(*ll && *ll!='=' && !isspace(*ll))
901 continue;
902 }
903 else if(CurrentConfig.sections[parse_section]->itemdefs[parse_item].key_type==UrlSpecification)
904 {
905 char *equal;
906
907 ll=key;
908
909 while((equal=strchr(ll,'=')))
910 {
911 ll=equal;
912 if(--equal>key && isspace(*equal))
913 {
914 while(isspace(*equal))
915 *equal--=0;
916 break;
917 }
918 ll++;
919 }
920
921 while(*ll && *ll!='=' && !isspace(*ll))
922 ll++;
923 }
924 else
925 {
926 ll=key;
927 while(*ll && *ll!='=' && !isspace(*ll))
928 ll++;
929 }
930
931 if(CurrentConfig.sections[parse_section]->itemdefs[parse_item].url_type==0 && url)
932 {
933 char *errmsg=(char*)malloc((size_t)48);
934 strcpy(errmsg,"No URL context '<...>' allowed for this entry.");
935 return(errmsg);
936 }
937
938 if(CurrentConfig.sections[parse_section]->itemdefs[parse_item].val_type==None)
939 {
940 if(strchr(ll,'='))
941 {
942 char *errmsg=(char*)malloc((size_t)40);
943 strcpy(errmsg,"Equal sign seen but not expected.");
944 return(errmsg);
945 }
946
947 *ll=0;
948 val=NULL;
949 }
950 else
951 {
952 val=strchr(ll,'=');
953 if(!val)
954 {
955 char *errmsg=(char*)malloc((size_t)40);
956 strcpy(errmsg,"No equal sign seen but expected.");
957 return(errmsg);
958 }
959
960 *ll=0;
961 if(!*key)
962 {
963 char *errmsg=(char*)malloc((size_t)48);
964 strcpy(errmsg,"Nothing to the left of the equal sign.");
965 return(errmsg);
966 }
967
968 val++;
969 while(isspace(*val))
970 val++;
971 }
972
973 *url_str=url;
974 *key_str=key;
975 *val_str=val;
976
977 break;
978 }
979
980 if(parse_item==CurrentConfig.sections[parse_section]->nitemdefs)
981 {
982 char *errmsg=(char*)malloc(32+strlen(l));
983 sprintf(errmsg,"Unrecognised entry '%s'.",l);
984 return(errmsg);
985 }
986 }
987
988 return(NULL);
989 }
990
991
992 /*++++++++++++++++++++++++++++++++++++++
993 Parse an entry from the file.
994
995 char *ParseEntry Return a string containing an error message in case of error.
996
997 const ConfigItemDef *itemdef The item definition for the item in the section.
998
999 ConfigItem *item The item to add the entry to.
1000
1001 const char *url_str The string for the URL specification.
1002
1003 const char *key_str The string for the key.
1004
1005 const char *val_str The string to the value.
1006 ++++++++++++++++++++++++++++++++++++++*/
1007
ParseEntry(const ConfigItemDef * itemdef,ConfigItem * item,const char * url_str,const char * key_str,const char * val_str)1008 static char *ParseEntry(const ConfigItemDef *itemdef,ConfigItem *item,const char *url_str,const char *key_str,const char *val_str)
1009 {
1010 UrlSpec *url=NULL;
1011 KeyOrValue key,val;
1012 char *errmsg=NULL;
1013
1014 key.string=NULL;
1015 val.string=NULL;
1016
1017 if(itemdef->same_key==0 && itemdef->url_type==0 && (*item) && (*item)->nentries)
1018 {
1019 errmsg=(char*)malloc(32+strlen(key_str));
1020 sprintf(errmsg,"Duplicated entry: '%s'.",key_str);
1021 return(errmsg);
1022 }
1023
1024 if(!itemdef->url_type || !url_str)
1025 url=NULL;
1026 else
1027 if((errmsg=ParseKeyOrValue(url_str,UrlSpecification,(KeyOrValue*)&url)))
1028 return(errmsg);
1029
1030 if(itemdef->key_type==Fixed)
1031 {
1032 if(strcmp(key_str,itemdef->name))
1033 {
1034 errmsg=(char*)malloc(32+strlen(key_str));
1035 sprintf(errmsg,"Unexpected key string: '%s'.",key_str);
1036 if(url) free(url);
1037 return(errmsg);
1038 }
1039 key.string=itemdef->name;
1040 }
1041 else
1042 if((errmsg=ParseKeyOrValue(key_str,itemdef->key_type,&key)))
1043 {
1044 if(url) free(url);
1045 return(errmsg);
1046 }
1047
1048 if(!val_str)
1049 val.string=NULL;
1050 else if(itemdef->val_type==None)
1051 val.string=NULL;
1052 else
1053 if((errmsg=ParseKeyOrValue(val_str,itemdef->val_type,&val)))
1054 {
1055 if(url) free(url);
1056 FreeKeyOrValue(&key,itemdef->key_type);
1057 return(errmsg);
1058 }
1059
1060 if(!item)
1061 {
1062 if(url) free(url);
1063 FreeKeyOrValue(&key,itemdef->key_type);
1064 FreeKeyOrValue(&val,itemdef->val_type);
1065 return(NULL);
1066 }
1067
1068 if(!(*item))
1069 {
1070 *item=(ConfigItem)malloc(sizeof(struct _ConfigItem));
1071 (*item)->itemdef=itemdef;
1072 (*item)->nentries=0;
1073 (*item)->url=NULL;
1074 (*item)->key=NULL;
1075 (*item)->val=NULL;
1076 (*item)->def_val=NULL;
1077 }
1078 if(!(*item)->nentries)
1079 {
1080 (*item)->nentries=1;
1081 if(itemdef->url_type!=0)
1082 (*item)->url=(UrlSpec**)malloc(sizeof(UrlSpec*));
1083 (*item)->key=(KeyOrValue*)malloc(sizeof(KeyOrValue));
1084 if(itemdef->val_type!=None)
1085 (*item)->val=(KeyOrValue*)malloc(sizeof(KeyOrValue));
1086 }
1087 else
1088 {
1089 (*item)->nentries++;
1090 if(itemdef->url_type!=0)
1091 (*item)->url=(UrlSpec**)realloc((void*)(*item)->url,(*item)->nentries*sizeof(UrlSpec*));
1092 (*item)->key=(KeyOrValue*)realloc((void*)(*item)->key,(*item)->nentries*sizeof(KeyOrValue));
1093 if(itemdef->val_type!=None)
1094 (*item)->val=(KeyOrValue*)realloc((void*)(*item)->val,(*item)->nentries*sizeof(KeyOrValue));
1095 }
1096
1097 if(itemdef->url_type!=0)
1098 (*item)->url[(*item)->nentries-1]=url;
1099 (*item)->key[(*item)->nentries-1]=key;
1100 if(itemdef->val_type!=None)
1101 (*item)->val[(*item)->nentries-1]=val;
1102
1103 return(NULL);
1104 }
1105
1106
1107 /*++++++++++++++++++++++++++++++++++++++
1108 Parse the text and put a value into the location.
1109
1110 char *ParseKeyOrValue Returns a string containing the error message.
1111
1112 const char *text The text string to parse.
1113
1114 ConfigType type The type we are looking for.
1115
1116 KeyOrValue *pointer The location to store the key or value.
1117 ++++++++++++++++++++++++++++++++++++++*/
1118
ParseKeyOrValue(const char * text,ConfigType type,KeyOrValue * pointer)1119 char *ParseKeyOrValue(const char *text,ConfigType type,KeyOrValue *pointer)
1120 {
1121 char *errmsg=NULL;
1122
1123 switch(type)
1124 {
1125 case Fixed:
1126 case None:
1127 break;
1128
1129 case CfgMaxServers:
1130 if(!*text)
1131 {errmsg=(char*)malloc((size_t)56);strcpy(errmsg,"Expecting a maximum server count, got nothing.");}
1132 else if(!isanumber(text))
1133 {errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a maximum server count, got '%s'.",text);}
1134 else
1135 {
1136 pointer->integer=atoi(text);
1137 if(pointer->integer<=0 || pointer->integer>MAX_SERVERS)
1138 {errmsg=(char*)malloc((size_t)(36+MAX_INT_STR));sprintf(errmsg,"Invalid maximum server count: %d.",pointer->integer);}
1139 }
1140 break;
1141
1142 case CfgMaxFetchServers:
1143 if(!*text)
1144 {errmsg=(char*)malloc((size_t)56);strcpy(errmsg,"Expecting a maximum fetch server count, got nothing.");}
1145 else if(!isanumber(text))
1146 {errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a maximum fetch server count, got '%s'.",text);}
1147 else
1148 {
1149 pointer->integer=atoi(text);
1150 if(pointer->integer<=0 || pointer->integer>MAX_FETCH_SERVERS)
1151 {errmsg=(char*)malloc((size_t)(40+MAX_INT_STR));sprintf(errmsg,"Invalid maximum fetch server count: %d.",pointer->integer);}
1152 }
1153 break;
1154
1155 case CfgLogLevel:
1156 if(!*text)
1157 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a log level, got nothing.");}
1158 else if(strcasecmp(text,"debug")==0)
1159 pointer->integer=Debug;
1160 else if(strcasecmp(text,"info")==0)
1161 pointer->integer=Inform;
1162 else if(strcasecmp(text,"important")==0)
1163 pointer->integer=Important;
1164 else if(strcasecmp(text,"warning")==0)
1165 pointer->integer=Warning;
1166 else if(strcasecmp(text,"fatal")==0)
1167 pointer->integer=Fatal;
1168 else
1169 {errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a log level, got '%s'.",text);}
1170 break;
1171
1172 case Boolean:
1173 if(!*text)
1174 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a Boolean, got nothing.");}
1175 else if(!strcasecmp(text,"yes") || !strcasecmp(text,"true"))
1176 pointer->integer=1;
1177 else if(!strcasecmp(text,"no") || !strcasecmp(text,"false"))
1178 pointer->integer=0;
1179 else
1180 {errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a Boolean, got '%s'.",text);}
1181 break;
1182
1183 case PortNumber:
1184 if(!*text)
1185 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a port number, got nothing.");}
1186 else if(!isanumber(text))
1187 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a port number, got '%s'.",text);}
1188 else
1189 {
1190 pointer->integer=atoi(text);
1191 if(pointer->integer<=0 || pointer->integer>65535)
1192 {errmsg=(char*)malloc((size_t)(24+MAX_INT_STR));sprintf(errmsg,"Invalid port number %d.",pointer->integer);}
1193 }
1194 break;
1195
1196 case AgeDays:
1197 if(!*text)
1198 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting an age in days, got nothing.");}
1199 else if(isanumber(text))
1200 pointer->integer=atoi(text);
1201 else
1202 {
1203 int val,len;
1204 char suffix;
1205
1206 if(sscanf(text,"%d%1c%n",&val,&suffix,&len)==2 && len==strlen(text) &&
1207 (suffix=='d' || suffix=='w' || suffix=='m' || suffix=='y'))
1208 {
1209 if(suffix=='y')
1210 pointer->integer=val*365;
1211 else if(suffix=='m')
1212 pointer->integer=val*30;
1213 else if(suffix=='w')
1214 pointer->integer=val*7;
1215 else /* if(suffix=='d') */
1216 pointer->integer=val;
1217 }
1218 else
1219 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting an age in days, got '%s'.",text);}
1220 }
1221 break;
1222
1223 case TimeSecs:
1224 if(!*text)
1225 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a time in seconds, got nothing.");}
1226 else if(isanumber(text))
1227 pointer->integer=atoi(text);
1228 else
1229 {
1230 int val,len;
1231 char suffix;
1232
1233 if(sscanf(text,"%d%1c%n",&val,&suffix,&len)==2 && len==strlen(text) &&
1234 (suffix=='s' || suffix=='m' || suffix=='h' || suffix=='d' || suffix=='w'))
1235 {
1236 if(suffix=='w')
1237 pointer->integer=val*3600*24*7;
1238 else if(suffix=='d')
1239 pointer->integer=val*3600*24;
1240 else if(suffix=='h')
1241 pointer->integer=val*3600;
1242 else if(suffix=='m')
1243 pointer->integer=val*60;
1244 else /* if(suffix=='s') */
1245 pointer->integer=val;
1246 }
1247 else
1248 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a time in seconds, got '%s'.",text);}
1249 }
1250 break;
1251
1252 case CacheSize:
1253 if(!*text)
1254 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a cache size in MB, got nothing.");}
1255 else if(!isanumber(text))
1256 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a cache size in MB, got '%s'.",text);}
1257 else
1258 {
1259 pointer->integer=atoi(text);
1260 if(pointer->integer<-1)
1261 {errmsg=(char*)malloc((size_t)(24+MAX_INT_STR));sprintf(errmsg,"Invalid cache size %d.",pointer->integer);}
1262 }
1263 break;
1264
1265 case FileSize:
1266 if(!*text)
1267 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a file size in kB, got nothing.");}
1268 else if(!isanumber(text))
1269 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a file size in kB, got '%s'.",text);}
1270 else
1271 {
1272 pointer->integer=atoi(text);
1273 if(pointer->integer<0)
1274 {errmsg=(char*)malloc((size_t)(24+MAX_INT_STR));sprintf(errmsg,"Invalid file size %d.",pointer->integer);}
1275 }
1276 break;
1277
1278 case Percentage:
1279 if(!*text)
1280 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a percentage, got nothing.");}
1281 else if(!isanumber(text))
1282 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a percentage, got '%s'.",text);}
1283 else
1284 {
1285 pointer->integer=atoi(text);
1286 if(pointer->integer<0 || pointer->integer>100)
1287 {errmsg=(char*)malloc((size_t)(24+MAX_INT_STR));sprintf(errmsg,"Invalid percentage %d.",pointer->integer);}
1288 }
1289 break;
1290
1291 case UserId:
1292 if(!*text)
1293 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a username or uid, got nothing.");}
1294 else
1295 {
1296 uid_t uid;
1297 struct passwd *userInfo=getpwnam(text);
1298 if(userInfo)
1299 uid=userInfo->pw_uid;
1300 else
1301 {
1302 if(sscanf(text,"%d",&uid)!=1)
1303 {errmsg=(char*)malloc(24+strlen(text));sprintf(errmsg,"Invalid user %s.",text);}
1304 else if(uid!=-1 && !getpwuid(uid))
1305 {errmsg=(char*)malloc((size_t)(24+MAX_INT_STR));sprintf(errmsg,"Unknown user id %d.",uid);}
1306 }
1307 pointer->integer=uid;
1308 }
1309 break;
1310
1311 case GroupId:
1312 if(!*text)
1313 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a group name or gid, got nothing.");}
1314 else
1315 {
1316 gid_t gid;
1317 struct group *groupInfo=getgrnam(text);
1318 if(groupInfo)
1319 gid=groupInfo->gr_gid;
1320 else
1321 {
1322 if(sscanf(text,"%d",&gid)!=1)
1323 {errmsg=(char*)malloc(24+strlen(text));sprintf(errmsg,"Invalid group %s.",text);}
1324 else if(gid!=-1 && !getgrgid(gid))
1325 {errmsg=(char*)malloc((size_t)(24+MAX_INT_STR));sprintf(errmsg,"Unknown group id %d.",gid);}
1326 }
1327 pointer->integer=gid;
1328 }
1329 break;
1330
1331 case String:
1332 if(!*text || !strcasecmp(text,"none"))
1333 pointer->string=NULL;
1334 else
1335 {
1336 pointer->string=(char*)malloc(strlen(text)+1);
1337 strcpy(pointer->string,text);
1338 }
1339 break;
1340
1341 case PathName:
1342 if(!*text)
1343 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a pathname, got nothing.");}
1344 else if(*text!='/')
1345 {errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting an absolute pathname, got '%s'.",text);}
1346 else
1347 {
1348 pointer->string=(char*)malloc(strlen(text)+1);
1349 strcpy(pointer->string,text);
1350 }
1351 break;
1352
1353 case FileExt:
1354 if(!*text)
1355 {errmsg=malloc(48);strcpy(errmsg,"Expecting a file extension, got nothing.");}
1356 else if(*text!='.')
1357 {errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a file extension, got '%s'.",text);}
1358 else
1359 {
1360 pointer->string=(char*)malloc(strlen(text)+1);
1361 strcpy(pointer->string,text);
1362 }
1363 break;
1364
1365 case FileMode:
1366 if(!*text)
1367 {errmsg=malloc(48);strcpy(errmsg,"Expecting a file permissions mode, got nothing.");}
1368 else if(!isanumber(text) || *text!='0')
1369 {errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting an octal file permissions mode, got '%s'.",text);}
1370 else
1371 {
1372 sscanf(text,"%o",(unsigned *)&pointer->integer);
1373 pointer->integer&=07777;
1374 }
1375 break;
1376
1377 case MIMEType:
1378 if(!*text)
1379 {errmsg=(char*)malloc((size_t)40);strcpy(errmsg,"Expecting a MIME Type, got nothing.");}
1380 else
1381 {
1382 char *slash=strchr(text,'/');
1383 if(!slash)
1384 {errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a MIME Type/Subtype, got '%s'.",text);}
1385 pointer->string=(char*)malloc(strlen(text)+1);
1386 strcpy(pointer->string,text);
1387 }
1388 break;
1389
1390 case HostOrNone:
1391 if(!*text || !strcasecmp(text,"none"))
1392 {
1393 pointer->string=NULL;
1394 break;
1395 }
1396
1397 /*@fallthrough@*/
1398
1399 case Host:
1400 if(!*text)
1401 {errmsg=(char*)malloc((size_t)40);strcpy(errmsg,"Expecting a hostname, got nothing.");}
1402 else
1403 {
1404 char *host,*colon;
1405
1406 if(strchr(text,'*'))
1407 {errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a hostname without a wildcard, got '%s'.",text); break;}
1408
1409 host=CanonicaliseHost(text);
1410
1411 if(*host=='[')
1412 {
1413 char *square=strchr(host,']');
1414 colon=strchr(square,':');
1415 }
1416 else
1417 colon=strchr(host,':');
1418
1419 if(colon)
1420 {errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a hostname without a port number, got '%s'.",text); free(host); break;}
1421
1422 pointer->string=host;
1423 }
1424 break;
1425
1426 case HostAndPortOrNone:
1427 if(!*text || !strcasecmp(text,"none"))
1428 {
1429 pointer->string=NULL;
1430 break;
1431 }
1432
1433 /*@fallthrough@*/
1434
1435 case HostAndPort:
1436 if(!*text)
1437 {errmsg=(char*)malloc((size_t)56);strcpy(errmsg,"Expecting a hostname and port number, got nothing.");}
1438 else
1439 {
1440 char *host,*colon;
1441
1442 if(strchr(text,'*'))
1443 {errmsg=(char*)malloc(72+strlen(text));sprintf(errmsg,"Expecting a hostname and port number, without a wildcard, got '%s'.",text); break;}
1444
1445 host=CanonicaliseHost(text);
1446
1447 if(*host=='[')
1448 {
1449 char *square=strchr(host,']');
1450 colon=strchr(square,':');
1451 }
1452 else
1453 colon=strchr(host,':');
1454
1455 if(!colon)
1456 {errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a hostname and port number, got '%s'.",text); free(host); break;}
1457
1458 if(colon && (!isanumber(colon+1) || atoi(colon+1)<=0 || atoi(colon+1)>65535))
1459 {errmsg=(char*)malloc(32+strlen(colon+1));sprintf(errmsg,"Invalid port number %s.",colon+1); free(host); break;}
1460
1461 pointer->string=host;
1462 }
1463 break;
1464
1465 case HostWild:
1466 if(!*text)
1467 {errmsg=(char*)malloc((size_t)64);strcpy(errmsg,"Expecting a hostname (perhaps with wildcard), got nothing.");}
1468 else
1469 {
1470 const char *p;
1471 char *host;
1472 int wildcard=0,colons=0;
1473
1474 p=text;
1475 while(*p)
1476 {
1477 if(*p=='*')
1478 wildcard++;
1479 else if(*p==':')
1480 colons++;
1481 p++;
1482 }
1483
1484 /*
1485 This is tricky, we should check for a host:port combination and disallow it.
1486 But the problem is that a single colon could be an IPv6 wildcard or an IPv4 host and port.
1487 If there are 2 or more ':' then it is IPv6, if it starts with '[' it is IPv6.
1488 */
1489
1490 if(wildcard)
1491 {
1492 char *p;
1493 host=(char*)malloc(strlen(text)+1);
1494 strcpy(host,text);
1495 for(p=host;*p;p++)
1496 *p=tolower(*p);
1497 }
1498 else
1499 host=CanonicaliseHost(text);
1500
1501 if(colons==1 && *host!='[')
1502 {errmsg=(char*)malloc(80+strlen(text));sprintf(errmsg,"Expecting a hostname without a port number (perhaps with wildcard), got '%s'.",text); free(host); break;}
1503
1504 if(*host=='[')
1505 {
1506 char *square=strchr(host,']');
1507 if(!square || *(square+1))
1508 {errmsg=(char*)malloc(80+strlen(text));sprintf(errmsg,"Expecting a hostname without a port number (perhaps with wildcard), got '%s'.",text); free(host); break;}
1509 }
1510
1511 pointer->string=host;
1512 }
1513 break;
1514
1515 case HostAndPortWild:
1516 if(!*text)
1517 {errmsg=(char*)malloc((size_t)80);strcpy(errmsg,"Expecting a hostname and port number (perhaps with wildcard), got nothing.");}
1518 else
1519 {
1520 const char *p;
1521 char *host;
1522 int wildcard=0,colons=0;
1523
1524 p=text;
1525 while(*p)
1526 {
1527 if(*p=='*')
1528 wildcard++;
1529 else if(*p==':')
1530 colons++;
1531 p++;
1532 }
1533
1534 /*
1535 This is tricky, we should check for a host:port combination and allow it.
1536 But the problem is that a single colon could be an IPv6 wildcard or an IPv4 host and port.
1537 If there are 2 or more ':' then it is IPv6, if it starts with '[' it is IPv6.
1538 */
1539
1540 if(wildcard)
1541 {
1542 char *p;
1543 host=(char*)malloc(strlen(text)+1);
1544 strcpy(host,text);
1545 for(p=host;*p;p++)
1546 *p=tolower(*p);
1547 }
1548 else
1549 host=CanonicaliseHost(text);
1550
1551 if(colons==0 || (colons>1 && *host!='['))
1552 {errmsg=(char*)malloc(80+strlen(text));sprintf(errmsg,"Expecting a hostname and a port number (perhaps with wildcard), got '%s'.",text); free(host); break;}
1553
1554 if(*host=='[')
1555 {
1556 char *square=strchr(host,']');
1557 if(!square || *(square+1)!=':')
1558 {errmsg=(char*)malloc(80+strlen(text));sprintf(errmsg,"Expecting a hostname and a port number (perhaps with wildcard), got '%s'.",text); free(host); break;}
1559 }
1560
1561 pointer->string=host;
1562 }
1563 break;
1564
1565 case UserPass:
1566 if(!*text)
1567 {errmsg=(char*)malloc((size_t)48);strcpy(errmsg,"Expecting a username and password, got nothing.");}
1568 else if(!strchr(text,':'))
1569 {errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a username and password, got '%s'.",text);}
1570 else
1571 pointer->string=Base64Encode(text,strlen(text));
1572 break;
1573
1574 case Url:
1575 if(!*text || !strcasecmp(text,"none"))
1576 {errmsg=(char*)malloc((size_t)32);strcpy(errmsg,"Expecting a URL, got nothing.");}
1577 else
1578 {
1579 URL *tempUrl;
1580
1581 if(strchr(text,'*'))
1582 {errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a URL without a wildcard, got '%s'.",text); break;}
1583
1584 tempUrl=SplitURL(text);
1585 if(!IsProtocolHandled(tempUrl))
1586 {errmsg=(char*)malloc(32+strlen(text));sprintf(errmsg,"Expecting a URL, got '%s'.",text);}
1587 else
1588 {
1589 pointer->string=(char*)malloc(strlen(tempUrl->file)+1);
1590 strcpy(pointer->string,tempUrl->file);
1591 }
1592 FreeURL(tempUrl);
1593 }
1594 break;
1595
1596 case UrlWild:
1597 if(!*text || !strcasecmp(text,"none"))
1598 {errmsg=(char*)malloc((size_t)64);strcpy(errmsg,"Expecting a URL (perhaps with wildcard), got nothing.");}
1599 else
1600 {
1601 URL *tempUrl=SplitURL(text);
1602 if(!IsProtocolHandled(tempUrl))
1603 {errmsg=(char*)malloc(32+strlen(text));sprintf(errmsg,"Expecting a URL, got '%s'.",text);}
1604 else
1605 {
1606 pointer->string=(char*)malloc(strlen(text)+1);
1607 strcpy(pointer->string,text);
1608 }
1609 FreeURL(tempUrl);
1610 }
1611 break;
1612
1613 case UrlSpecification:
1614 if(!*text)
1615 {errmsg=(char*)malloc((size_t)64);strcpy(errmsg,"Expecting a URL-SPECIFICATION, got nothing.");}
1616 else
1617 {
1618 const char *p,*orgtext=text;
1619
1620 pointer->urlspec=(UrlSpec*)malloc(sizeof(UrlSpec));
1621 pointer->urlspec->null=0;
1622 pointer->urlspec->negated=0;
1623 pointer->urlspec->nocase=0;
1624 pointer->urlspec->proto=0;
1625 pointer->urlspec->host=0;
1626 pointer->urlspec->port=-1;
1627 pointer->urlspec->path=0;
1628 pointer->urlspec->args=0;
1629
1630 /* !~ */
1631
1632 while(*text)
1633 {
1634 if(*text=='!')
1635 pointer->urlspec->negated=1;
1636 else if(*text=='~')
1637 pointer->urlspec->nocase=1;
1638 else
1639 break;
1640
1641 text++;
1642 }
1643
1644 /* protocol */
1645
1646 if(!strncmp(text,"*://",(size_t)4))
1647 p=text+4;
1648 else if(!strncmp(text,"://",(size_t)3))
1649 p=text+3;
1650 else if((p=strstr(text,"://")))
1651 {
1652 pointer->urlspec->proto=sizeof(UrlSpec);
1653
1654 pointer->urlspec=(UrlSpec*)realloc((void*)pointer->urlspec,
1655 pointer->urlspec->proto+(p-text)+1);
1656
1657 strncpy(UrlSpecProto(pointer->urlspec),text,(size_t)(p-text));
1658 *(UrlSpecProto(pointer->urlspec)+(p-text))=0;
1659 p+=3;
1660 }
1661 else
1662 {errmsg=(char*)malloc(64+strlen(orgtext));sprintf(errmsg,"Expecting a URL-SPECIFICATION, got this '%s'.",orgtext);
1663 free(pointer->urlspec); break;}
1664
1665 if(pointer->urlspec->proto)
1666 {
1667 char *q;
1668
1669 for(q=UrlSpecProto(pointer->urlspec);*q;q++)
1670 *q=tolower(*q);
1671 }
1672
1673 text=p;
1674
1675 /* host */
1676
1677 if(*text=='*' && (*(text+1)=='/' || !*(text+1)))
1678 p=text+1;
1679 else if(*text==':')
1680 p=text;
1681 else if(*text=='[' && (p=strchr(text,']')))
1682 {
1683 p++;
1684 pointer->urlspec->host=1;
1685 }
1686 else if((p=strchr(text,':')) && p<strchr(text,'/'))
1687 {
1688 pointer->urlspec->host=1;
1689 }
1690 else if((p=strchr(text,'/')))
1691 {
1692 pointer->urlspec->host=1;
1693 }
1694 else if(*text)
1695 {
1696 p=text+strlen(text);
1697 pointer->urlspec->host=1;
1698 }
1699 else
1700 p=text;
1701
1702 if(pointer->urlspec->host)
1703 {
1704 char *q;
1705
1706 pointer->urlspec->host=(unsigned short)(sizeof(UrlSpec)+
1707 (pointer->urlspec->proto?1+strlen(UrlSpecProto(pointer->urlspec)):0));
1708
1709 pointer->urlspec=(UrlSpec*)realloc((void*)pointer->urlspec,
1710 pointer->urlspec->host+(p-text)+1);
1711
1712 strncpy(UrlSpecHost(pointer->urlspec),text,(size_t)(p-text));
1713 *(UrlSpecHost(pointer->urlspec)+(p-text))=0;
1714
1715 for(q=UrlSpecHost(pointer->urlspec);*q;q++)
1716 *q=tolower(*q);
1717 }
1718
1719 text=p;
1720
1721 /* port */
1722
1723 if(*text==':' && isdigit(*(text+1)))
1724 {
1725 pointer->urlspec->port=atoi(text+1);
1726 p=text+1;
1727 while(isdigit(*p))
1728 p++;
1729 }
1730 else if(*text==':' && (*(text+1)=='/' || *(text+1)==0))
1731 {
1732 pointer->urlspec->port=0;
1733 p=text+1;
1734 }
1735 else if(*text==':' && *(text+1)=='*')
1736 {
1737 p=text+2;
1738 }
1739 else if(*text==':')
1740 {errmsg=(char*)malloc(64+strlen(orgtext));sprintf(errmsg,"Expecting a URL-SPECIFICATION, got this '%s'.",orgtext);
1741 free(pointer->urlspec); break;}
1742
1743 text=p;
1744
1745 /* path */
1746
1747 if(!*text)
1748 ;
1749 else if(*text=='?')
1750 ;
1751 else if(*text=='/' && (p=strchr(text,'?')))
1752 {
1753 if(strncmp(text,"/*?",(size_t)3))
1754 pointer->urlspec->path=1;
1755 }
1756 else if(*text=='/')
1757 {
1758 p=text+strlen(text);
1759 if(strcmp(text,"/*"))
1760 pointer->urlspec->path=1;
1761 }
1762 else
1763 {errmsg=(char*)malloc(64+strlen(orgtext));sprintf(errmsg,"Expecting a URL-SPECIFICATION, got this '%s'.",orgtext);
1764 free(pointer->urlspec); break;}
1765
1766 if(pointer->urlspec->path)
1767 {
1768 char *temppath,*path;
1769
1770 if(*p)
1771 {
1772 char *temptemppath=(char*)malloc(1+(p-text));
1773
1774 strncpy(temptemppath,text,p-text);
1775 temptemppath[p-text]=0;
1776
1777 temppath=URLDecodeGeneric(temptemppath);
1778
1779 free(temptemppath);
1780 }
1781 else
1782 temppath=URLDecodeGeneric(text);
1783
1784 path=URLEncodePath(temppath);
1785 free(temppath);
1786
1787 pointer->urlspec->path=(unsigned short)(sizeof(UrlSpec)+
1788 (pointer->urlspec->proto?1+strlen(UrlSpecProto(pointer->urlspec)):0)+
1789 (pointer->urlspec->host ?1+strlen(UrlSpecHost (pointer->urlspec)):0));
1790
1791 pointer->urlspec=(UrlSpec*)realloc((void*)pointer->urlspec,
1792 pointer->urlspec->path+strlen(path)+1);
1793
1794 strcpy(UrlSpecPath(pointer->urlspec),path);
1795
1796 free(path);
1797 }
1798
1799 text=p;
1800
1801 /* args */
1802
1803 if(!*text)
1804 ;
1805 else if(*text=='?')
1806 {
1807 p=text+1;
1808
1809 pointer->urlspec->args=1;
1810 }
1811 else
1812 {errmsg=(char*)malloc(64+strlen(orgtext));sprintf(errmsg,"Expecting a URL-SPECIFICATION, got this '%s'.",orgtext);
1813 free(pointer->urlspec); break;}
1814
1815 if(pointer->urlspec->args)
1816 {
1817 char *args=NULL;
1818
1819 if(*p)
1820 args=URLRecodeFormArgs(p);
1821 else
1822 args="";
1823
1824 pointer->urlspec->args=(unsigned short)(sizeof(UrlSpec)+
1825 (pointer->urlspec->proto?1+strlen(UrlSpecProto(pointer->urlspec)):0)+
1826 (pointer->urlspec->host ?1+strlen(UrlSpecHost (pointer->urlspec)):0)+
1827 (pointer->urlspec->path ?1+strlen(UrlSpecPath (pointer->urlspec)):0));
1828
1829 pointer->urlspec=(UrlSpec*)realloc((void*)pointer->urlspec,
1830 pointer->urlspec->args+strlen(args)+1);
1831
1832 strcpy(UrlSpecArgs(pointer->urlspec),args);
1833
1834 if(*args)
1835 free(args);
1836 }
1837 }
1838 break;
1839 }
1840
1841 return(errmsg);
1842 }
1843
1844
1845 /*++++++++++++++++++++++++++++++++++++++
1846 Decide if a string is an integer.
1847
1848 int isanumber Returns 1 if it is, 0 if not.
1849
1850 const char *string The string that may be an integer.
1851 ++++++++++++++++++++++++++++++++++++++*/
1852
isanumber(const char * string)1853 static int isanumber(const char *string)
1854 {
1855 int i=0;
1856
1857 if(string[i]=='-' || string[i]=='+')
1858 i++;
1859
1860 for(;string[i];i++)
1861 if(!isdigit(string[i]))
1862 return(0);
1863
1864 return(1);
1865 }
1866