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