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