1 /***************************************
2   $Header: /home/amb/CVS/wwwoffle/src/controledit.c,v 2.36 2007-12-05 18:50:34 amb Exp $
3 
4   WWWOFFLE - World Wide Web Offline Explorer - Version 2.9d.
5   Configuration file management via a web-page.
6   ******************/ /******************
7   Written by Andrew M. Bishop
8 
9   This file Copyright 1997,98,99,2000,01,02,03,04,05 Andrew M. Bishop
10   It may be distributed under the GNU Public License, version 2, or
11   any higher version.  See section COPYING of the GNU Public license
12   for conditions under which this file may be redistributed.
13   ***************************************/
14 
15 
16 #include "autoconfig.h"
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 
27 #include "wwwoffle.h"
28 #include "io.h"
29 #include "misc.h"
30 #include "errors.h"
31 #include "config.h"
32 
33 
34 #ifndef O_BINARY
35 /*+ A work-around for needing O_BINARY with Win32 to use binary mode. +*/
36 #define O_BINARY 0
37 #endif
38 
39 
40 /*+ A type definition to contain the contents of a configuration file section. +*/
41 
42 typedef struct _ControlEditSection
43 {
44  /*@only@*/ char *comment;                 /*+ The comment outside the section. +*/
45  /*@only@*/ char *name;                    /*+ The name of the section. +*/
46  /*@only@*/ char *file;                    /*+ The filename of an included section. +*/
47  /*@only@*/ char *content;                 /*+ The content of the section. +*/
48 }
49 *ControlEditSection;
50 
51 
52 /* Local functions */
53 
54 static void ControlEditForms(int fd,ControlEditSection *sections);
55 static void ControlEditUpdate(int fd,char *section,ControlEditSection *sections);
56 
57 static /*@null@*/ /*@only@*/ ControlEditSection *read_config_file(void);
58 static int write_config_file(ControlEditSection *sections);
59 static void free_sections(/*@only@*/ ControlEditSection *sections);
60 
61 
62 /*++++++++++++++++++++++++++++++++++++++
63   The control page that allows editing of the configuration file.
64 
65   int fd The file descriptor to write the file to.
66 
67   char *request_args The arguments to the page.
68 
69   Body *request_body The body of the HTTP request for the page.
70   ++++++++++++++++++++++++++++++++++++++*/
71 
ControlEditPage(int fd,char * request_args,Body * request_body)72 void ControlEditPage(int fd,char *request_args,Body *request_body)
73 {
74  char *newargs=NULL;
75  ControlEditSection *sections;
76 
77  if(request_args)
78    {
79     if(*request_args=='!' && strchr(request_args+1,'!'))
80       {
81        char *pling;
82        newargs=(char*)malloc(strlen(request_args)+1);
83        strcpy(newargs,request_args+1);
84        pling=strchr(newargs,'!');
85        *pling=0;
86       }
87     else if(*request_args!='!')
88       {
89        newargs=(char*)malloc(strlen(request_args)+1);
90        strcpy(newargs,request_args);
91       }
92    }
93 
94  sections=read_config_file();
95 
96  if(!sections)
97     HTMLMessage(fd,404,"WWWOFFLE Configuration Error",NULL,"ControlEditError",
98                 "section",NULL,
99                 "reason","ReadError",
100                 NULL);
101  else if(newargs && *newargs)
102    {
103     int i=0;
104     char *section=NULL;
105 
106     while(sections[i])
107       {
108        if(sections[i]->name && !strcmp(sections[i]->name,newargs))
109          {section=newargs;break;}
110        i++;
111       }
112 
113     if(!section)
114        HTMLMessage(fd,404,"WWWOFFLE Configuration Error",NULL,"ControlEditError",
115                    "section",newargs,
116                    "reason","BadSection",
117                    NULL);
118     else if(!request_body || strncmp(request_body->content,"value=",(size_t)6))
119        HTMLMessage(fd,404,"WWWOFFLE Configuration Error",NULL,"ControlEditError",
120                    "section",newargs,
121                    "reason","BadBody",
122                    NULL);
123     else
124       {
125        char *old=sections[i]->content;
126        char *new=URLDecodeFormArgs(request_body->content+6);
127 
128 #if !defined(__CYGWIN__)
129        char *p,*q;
130 
131        for(p=q=new;*p;p++)
132           if(*p!='\r')
133              *q++=*p;
134        *q=0;
135 #endif
136 
137        sections[i]->content=new;
138 
139        ControlEditUpdate(fd,section,sections);
140 
141        free(old);
142       }
143    }
144  else
145     ControlEditForms(fd,sections);
146 
147  if(sections)
148     free_sections(sections);
149 
150  if(newargs)
151     free(newargs);
152 }
153 
154 
155 /*++++++++++++++++++++++++++++++++++++++
156   The page that contains the forms making up the config file.
157 
158   int fd The file descriptor to write to.
159 
160   ControlEditSection *sections The sections of the file.
161   ++++++++++++++++++++++++++++++++++++++*/
162 
ControlEditForms(int fd,ControlEditSection * sections)163 static void ControlEditForms(int fd,ControlEditSection *sections)
164 {
165  int i=0;
166 
167  HTMLMessageHead(fd,200,"WWWOFFLE Configuration Edit Page",
168                  NULL);
169 
170  HTMLMessageBody(fd,"ControlEditPage-Head",
171                  NULL);
172 
173  while(sections[i])
174    {
175     char *htmlcomment=NULL,*htmlcontent=NULL;
176 
177     if(sections[i]->comment)
178        htmlcomment=HTMLString(sections[i]->comment,0);
179     if(sections[i]->content)
180        htmlcontent=HTMLString(sections[i]->content,0);
181 
182     HTMLMessageBody(fd,"ControlEditPage-Body",
183                     "section",sections[i]->name,
184                     "comment",htmlcomment,
185                     "content",htmlcontent,
186                     NULL);
187 
188     if(htmlcomment)
189        free(htmlcomment);
190     if(htmlcontent)
191        free(htmlcontent);
192 
193     i++;
194    }
195 
196  HTMLMessageBody(fd,"ControlEditPage-Tail",
197                  NULL);
198 }
199 
200 
201 /*++++++++++++++++++++++++++++++++++++++
202   Update the configuration file.
203 
204   int fd The file descriptor to write the message to.
205 
206   char *section The section that was updated.
207 
208   ControlEditSection *sections The sections including the updated one.
209   ++++++++++++++++++++++++++++++++++++++*/
210 
ControlEditUpdate(int fd,char * section,ControlEditSection * sections)211 static void ControlEditUpdate(int fd,char *section,ControlEditSection *sections)
212 {
213  if(write_config_file(sections))
214     HTMLMessage(fd,404,"WWWOFFLE Configuration Error",NULL,"ControlEditError",
215                 "section",section,
216                 "reason","WriteError",
217                 NULL);
218  else
219     HTMLMessage(fd,200,"WWWOFFLE Configuration Update",NULL,"ControlEditUpdate",
220                 "section",section,
221                 NULL);
222 }
223 
224 
225 /*++++++++++++++++++++++++++++++++++++++
226   Read in the config file into a set of sections.
227 
228   ControlEditSection *read_config_file Returns the sections of the file as a NULL terminated list.
229   ++++++++++++++++++++++++++++++++++++++*/
230 
read_config_file(void)231 static ControlEditSection *read_config_file(void)
232 {
233  int sec_num=0,state=0;
234  int conf;
235  ControlEditSection *sections;
236  char *line=NULL;
237  int line_num=0;
238 
239  conf=open(ConfigurationFileName(),O_RDONLY|O_BINARY);
240 
241  if(conf==-1)
242    {PrintMessage(Warning,"Cannot open the config file '%s' for reading; [%!s].",ConfigurationFileName()); return(NULL);}
243 
244  init_io(conf);
245 
246  sections=(ControlEditSection*)calloc((size_t)1,sizeof(ControlEditSection));
247 
248  while((line=read_line(conf,line)))
249    {
250     char *l=line;
251     char *r=line+strlen(line)-1;
252 
253     line_num++;
254 
255     while(isspace(*l))
256        l++;
257 
258     if(state==0 && *l=='#')
259       {
260        state=1;
261        sections=(ControlEditSection*)realloc((void*)sections,sizeof(ControlEditSection)*(sec_num+2));
262        sections[sec_num]=(ControlEditSection)calloc((size_t)1,sizeof(struct _ControlEditSection));
263        sections[++sec_num]=NULL;
264        sections[sec_num-1]->comment=(char*)malloc(strlen(l)+1);
265        strcpy(sections[sec_num-1]->comment,l);
266       }
267     else if((state==1 || state==2) && *l=='#')
268       {
269        sections[sec_num-1]->comment=(char*)realloc((void*)sections[sec_num-1]->comment,strlen(sections[sec_num-1]->comment)+strlen(l)+1);
270        strcat(sections[sec_num-1]->comment,l);
271       }
272     else if(state==0 && !*l)
273       ;
274     else if(state==1 && !*l)
275        state=0;
276     else if((state==0 || state==1) && *l)
277       {
278        state=2;
279        while(r>l && isspace(*r))
280           *r--=0;
281        if(sec_num==0 || sections[sec_num-1]->name)
282          {
283           sections=(ControlEditSection*)realloc((void*)sections,sizeof(ControlEditSection)*(sec_num+2));
284           sections[sec_num]=(ControlEditSection)calloc((size_t)1,sizeof(struct _ControlEditSection));
285           sections[++sec_num]=NULL;
286          }
287        sections[sec_num-1]->name=(char*)malloc(strlen(l)+1);
288        strcpy(sections[sec_num-1]->name,l);
289       }
290     else if(state==2 && !*l)
291       ;
292     else if(state==2 && *l=='{')
293       {
294        state=3;
295        sections[sec_num-1]->content=(char*)malloc((size_t)1);
296        strcpy(sections[sec_num-1]->content,"");
297       }
298     else if(state==2 && *l=='[')
299       {
300        state=4;
301       }
302     else if(state==3 && *l=='}')
303        state=0;
304     else if(state==3)
305       {
306        sections[sec_num-1]->content=(char*)realloc((void*)sections[sec_num-1]->content,strlen(sections[sec_num-1]->content)+strlen(line)+1);
307        strcat(sections[sec_num-1]->content,line);
308       }
309     else if(state==4 && *l)
310       {
311        state=5;
312        if(strchr(l,'/'))
313          {
314           PrintMessage(Warning,"Error parsing config file, line %d, included file is not in same directory",line_num);
315           free_sections(sections);
316           free(line);
317           return(NULL);
318          }
319        while(r>l && isspace(*r))
320           *r--=0;
321        sections[sec_num-1]->file=(char*)malloc(strlen(l)+1);
322        strcpy(sections[sec_num-1]->file,l);
323       }
324     else if(state==5 && *l==']')
325        state=0;
326     else
327       {
328        line[strlen(line)-1]=0;
329        PrintMessage(Warning,"Error parsing config file, line %d = '%s' [state=%d]",line_num,line,state);
330        free_sections(sections);
331        free(line);
332        return(NULL);
333       }
334    }
335 
336  finish_io(conf);
337  close(conf);
338 
339  for(sec_num=0;sections[sec_num];sec_num++)
340     if(sections[sec_num]->name && sections[sec_num]->file)
341       {
342        char *name,*r,*old;
343 
344        sections[sec_num]->content=(char*)malloc((size_t)1);
345        strcpy(sections[sec_num]->content,"");
346 
347        name=(char*)malloc(strlen(ConfigurationFileName())+strlen(sections[sec_num]->file)+1);
348 
349        strcpy(name,ConfigurationFileName());
350 
351        r=name+strlen(name)-1;
352        while(r>name && *r!='/')
353           r--;
354 
355        strcpy(r+1,sections[sec_num]->file);
356 
357        conf=open(name,O_RDONLY|O_BINARY);
358 
359        if(conf==-1)
360          {PrintMessage(Warning,"Cannot open the config file '%s' for reading; [%!s].",name); free_sections(sections); free(name); return(NULL);}
361 
362        init_io(conf);
363 
364        old=sections[sec_num]->file;
365        sections[sec_num]->file=name;
366        free(old);
367 
368        while((line=read_line(conf,line)))
369          {
370           sections[sec_num]->content=(char*)realloc((void*)sections[sec_num]->content,strlen(sections[sec_num]->content)+strlen(line)+1);
371           strcat(sections[sec_num]->content,line);
372          }
373 
374        finish_io(conf);
375        close(conf);
376       }
377 
378  return(sections);
379 }
380 
381 
382 /*++++++++++++++++++++++++++++++++++++++
383   Write out a set of sections to the config file.
384 
385   int write_config_file Returns 1 if in error.
386 
387   ControlEditSection *sections The sections to write out.
388   ++++++++++++++++++++++++++++++++++++++*/
389 
write_config_file(ControlEditSection * sections)390 static int write_config_file(ControlEditSection *sections)
391 {
392  char *conf_file_backup;
393  char *conf_file=ConfigurationFileName();
394  int renamed=0,i;
395  struct stat buf;
396  int conf;
397 
398  /* Rename the old file as a backup. */
399 
400  conf_file_backup=(char*)malloc(strlen(conf_file)+5);
401  strcpy(conf_file_backup,conf_file);
402  strcat(conf_file_backup,".bak");
403 
404  if(rename(conf_file,conf_file_backup))
405     PrintMessage(Warning,"Cannot rename the config file '%s' to '%s'; [%!s].",conf_file,conf_file_backup);
406  else if(stat(conf_file_backup,&buf))
407     PrintMessage(Warning,"Cannot stat the config file '%s'; [%!s].",conf_file);
408  else
409     renamed=1;
410 
411  free(conf_file_backup);
412 
413  conf=open(conf_file,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,0600);
414 
415  if(conf==-1)
416    {PrintMessage(Warning,"Cannot open the config file '%s' for writing; [%!s].",conf_file); return(1);}
417 
418  init_io(conf);
419 
420  if(renamed)
421    {
422     chown(conf_file,buf.st_uid,buf.st_gid);
423     chmod(conf_file,buf.st_mode&(~S_IFMT));
424    }
425 
426  for(i=0;sections[i];i++)
427    {
428     if(sections[i]->comment)
429       {
430        write_formatted(conf,"%s\n",sections[i]->comment);
431       }
432     if(sections[i]->name)
433       {
434        write_formatted(conf,"%s\n",sections[i]->name);
435 
436        if(sections[i]->file)
437          {
438           char *p=sections[i]->file+strlen(sections[i]->file)-1;
439           while(p>sections[i]->file && *p!='/')
440              p--;
441           write_formatted(conf,"[\n%s\n]\n\n\n",p+1);
442          }
443        else
444          {
445           write_formatted(conf,"{\n");
446           if(sections[i]->content)
447              write_string(conf,sections[i]->content);
448           if(sections[i]->content[strlen(sections[i]->content)-1]!='\n')
449              write_string(conf,"\n");
450           write_string(conf,"}\n\n\n");
451          }
452       }
453    }
454 
455  finish_io(conf);
456  close(conf);
457 
458  for(i=0;sections[i];i++)
459     if(sections[i]->name && sections[i]->file)
460       {
461        conf_file_backup=(char*)malloc(strlen(sections[i]->file)+5);
462        strcpy(conf_file_backup,sections[i]->file);
463        strcat(conf_file_backup,".bak");
464 
465        if(rename(sections[i]->file,conf_file_backup))
466           PrintMessage(Warning,"Cannot rename the config file '%s' to '%s'; [%!s].",sections[i]->file,conf_file_backup);
467        else if(stat(conf_file_backup,&buf))
468           PrintMessage(Warning,"Cannot stat the config file '%s'; [%!s].",sections[i]->file);
469        else
470           renamed=1;
471 
472        free(conf_file_backup);
473 
474        conf=open(sections[i]->file,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,0600);
475 
476        if(conf==-1)
477          {PrintMessage(Warning,"Cannot open the config file '%s' for writing; [%!s].",sections[i]->file); return(1);}
478 
479        init_io(conf);
480 
481        if(renamed)
482          {
483           chown(sections[i]->file,buf.st_uid,buf.st_gid);
484           chmod(sections[i]->file,buf.st_mode&(~S_IFMT));
485          }
486 
487        write_string(conf,sections[i]->content);
488 
489        finish_io(conf);
490        close(conf);
491       }
492 
493  return(0);
494 }
495 
496 
497 /*++++++++++++++++++++++++++++++++++++++
498   Free up a set of sections.
499 
500   ControlEditSection *sections The sections that are to be freed up.
501   ++++++++++++++++++++++++++++++++++++++*/
502 
free_sections(ControlEditSection * sections)503 static void free_sections(ControlEditSection *sections)
504 {
505  int i=0;
506 
507  while(sections[i])
508    {
509     if(sections[i]->comment)
510        free(sections[i]->comment);
511     if(sections[i]->name)
512        free(sections[i]->name);
513     if(sections[i]->file)
514        free(sections[i]->file);
515     if(sections[i]->content)
516        free(sections[i]->content);
517     free(sections[i]);
518     i++;
519    }
520 
521  free(sections);
522 }
523