summaryrefslogtreecommitdiff
path: root/lib/Asterisk/config.pm
blob: 14a386be078ef708f277a24382f100fe4a1f6e0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
package Asterisk::config;
#--------------------------------------------------------------
#
#	Asterisk::config - asterisk config files read and write
#
#	Copyright (C) 2005 - 2008, Sun bing.
#
#	Sun bing <hoowa.sun@gmail.com>
#
#
#   LICENSE
#   The Asterisk::config is licensed under the GNU 2.0 GPL. 
#   Asterisk::config carries no restrictions on re-branding
#   and people are free to commercially re-distribute it.
#
#
#--------------------------------------------------------------
$Asterisk::config::VERSION='0.96';

use strict;
use Fcntl ':flock';

##############################
#  CLASS METHOD
sub new {
my	$class = shift;
my	%args = @_;
my	(@resource_list,$resource_list,$parsed_conf,$parsed_section_chunk,$comment_flag);

	#try read
	return(0) if (!defined $args{file});
	return(0) if (!-e $args{file});
	if (defined $args{'stream_data'}) {
		@resource_list = split(/\n/,$args{'stream_data'});
	} else {
		open(DATA,"<$args{'file'}") or die "Asterisk-config Can't Open file : $!";
		@resource_list = <DATA>;
		close(DATA);
	}
	chomp(@resource_list);
	#try parse
	$comment_flag = '\;|\#';
	($parsed_conf,$parsed_section_chunk) = &_parse(\@resource_list,$comment_flag,$args{'section_chunk'});

	#try define default variable
	$args{'keep_resource_array'} = 1 if (!defined $args{'keep_resource_array'});
	if (defined $args{'keep_resource_array'} && $args{'keep_resource_array'}) {
		$resource_list = \@resource_list;
	}
	if (!defined $args{'clean_when_reload'}) {
		$args{'clean_when_reload'} = 1;
	}
	if (!defined $args{'reload_when_save'}) {
		$args{'reload_when_save'} = 1;
	}

my	$self = {
		#user input
		file=> $args{'file'},
		keep_resource_array=> $args{'keep_resource_array'},
		clean_when_reload=> $args{'clean_when_reload'},
		reload_when_save=> $args{'reload_when_save'},

		#internal
		commit_list => [],
		parsed_conf=> $parsed_conf,
		parsed_section_chunk=> $parsed_section_chunk,
		resource_list=> $resource_list,
		comment_flag=> $comment_flag,
	};
	bless $self,$class;
	return $self;
}

##############################
#  INTERNAL SUBROUTE _parse
# parse conf
sub _parse {
my	$resource_list = $_[0];
my	$comment_flag = $_[1];
my	$section_chunk = $_[2];

my (%DATA,$last_section_name,%DATA_CHUNK);
	$DATA{'[unsection]'}={};	$DATA_CHUNK{'[unsection]'}={} if ($section_chunk);
	foreach my $one_line (@$resource_list) {
	my	$line_sp=&_clean_string($one_line,$comment_flag);

		#format : Find New Section ???
		if ($line_sp =~ /^\[(.+)\]/) {
			$DATA{$1}={};			$last_section_name = $1;
			$DATA_CHUNK{$1}=[] if ($section_chunk);
			next;

		#save source chunk to data_chunk
		} elsif ($section_chunk) {
			next if ($one_line eq '');
		my	$section_name = $last_section_name;
			$section_name = '[unsection]' if (!$section_name);
			#copying source chunk to data_chunk
			push(@{$DATA_CHUNK{$section_name}},$one_line);
		}

		next if ($line_sp eq '');#next if just comment

		#fromat : Include "#" ???
		if ($line_sp =~ /^\#/) {
		my	$section_name = $last_section_name;
			$section_name = '[unsection]' if (!$section_name);
			$DATA{$section_name}{$line_sp}=[] if (!$DATA{$section_name}{$line_sp});
			push(@{$DATA{$section_name}{$line_sp}},$line_sp);
			next;
		}

		#format : Key=Value ???
		if ($line_sp =~ /\=/) {
			#split data and key
		my	($key,$value)=&_clean_keyvalue($line_sp);

		my	$section_name = $last_section_name;
			$section_name = '[unsection]' if (!$section_name);
			$DATA{$section_name}{$key}=[] if (!$DATA{$section_name}{$key});
			push(@{$DATA{$section_name}{$key}},$value);
			next;
		}
	}

return(\%DATA,\%DATA_CHUNK);
}

##############################
#  INTERNAL SUBROUTE _clean_string
# clean strings
sub _clean_string {
my	$string = shift;
my	$comment_flag = shift;
	return '' unless $string;
	if ($string !~ /^\#/) {
		($string,undef)=split(/$comment_flag/,$string);
	}
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
return($string);
}

##############################
#  INTERNAL SUBROUTE _clean_string
# split key value of data
sub _clean_keyvalue {
my	$string = shift;
my	($key,$value)=split(/\=(.*)/,$string);
	$key =~ s/^(\s+)//;		$key =~ s/(\s+)$//;
	if ($value) {
		$value=~ s/^\>//g;		$value =~ s/^(\s+)//;	$value =~ s/(\s+)$//;
	}

return($key,$value);
}

##############################
#  READ METHOD
sub get_objvar
{
my	$self = shift;
my	$varname = shift;
	if (defined $self->{$varname}) {
		return($self->{$varname});
	} else {
		return(0);
	}
}

sub fetch_sections_list
{
my	$self = shift;
my	@sections_list = grep(!/^\[unsection\]/, keys %{$self->{parsed_conf}});
return(\@sections_list);
}

sub fetch_sections_hashref
{
my	$self = shift;
return($self->{parsed_conf});
}

sub fetch_keys_list
{
my	$self = shift;
my	%args = @_;
	return(0) if (!defined $args{section});
	return(0) if (!defined $self->{parsed_conf}{$args{section}});

my	@keys_list = grep(!/^\[unsection\]/, keys %{$self->{parsed_conf}{$args{section}}});
return(\@keys_list);
}

sub fetch_keys_hashref
{
my	$self = shift;
my	%args = @_;
	return(0) if (!defined $args{section});
	return(0) if (!defined $self->{parsed_conf}{$args{section}});

return($self->{parsed_conf}{$args{section}});
}

sub fetch_values_arrayref
{
my	$self = shift;
my	%args = @_;
	return(0) if (!defined $args{section});
	return(0) if (!defined $self->{parsed_conf}{$args{section}});
	return(0) if (!defined $args{key});
	return(0) if (!defined $self->{parsed_conf}{$args{section}}{$args{key}});

return($self->{parsed_conf}{$args{section}}{$args{key}});
}

sub reload
{
my	$self = shift;

	#try read
	return(0) if (!defined $self->{file});
	return(0) if (!-e $self->{file});
	open(DATA,"<$self->{'file'}") or die "Asterisk-config Can't Open file : $!";
my	@resource_list = <DATA>;
	close(DATA);
	chomp(@resource_list);

	# save to parsed_conf
my	$parsed_conf = &_parse(\@resource_list,$self->{comment_flag});
	$self->{parsed_conf} = $parsed_conf;

	# save to resource_list
my	$resource_list;
	if (defined $self->{'keep_resource_array'} && $self->{'keep_resource_array'}) {
		$resource_list = \@resource_list;
	}
	$self->{resource_list} = $resource_list;

	# save to commit_list / do clean_when_reload ?
	if (defined $self->{'clean_when_reload'} && $self->{'clean_when_reload'}) {
		&clean_assign($self);
	}


return(1);
}

##############################
#  WRITE METHOD

sub clean_assign
{
my	$self = shift;
#	undef($self->{commit_list});
	$self->{commit_list}=[];
return(1);
}

sub set_objvar
{
my	$self = shift;
my	$key = shift;
my	$value = shift;

	return(0) if (!defined $value);
	return(0) if (!exists $self->{$key});
	$self->{$key} = $value;

return(1);
}

#-----------------------------------------------------------
#  assign method to commit_list
sub assign_cleanfile
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='cleanfile';
	push(@{$self->{commit_list}},\%hash);
}

sub assign_matchreplace
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='matchreplace';
	push(@{$self->{commit_list}},\%hash);
}

sub assign_append
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='append';
	push(@{$self->{commit_list}},\%hash);
}

sub assign_replacesection
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='replacesection';
	push(@{$self->{commit_list}},\%hash);
}

sub assign_delsection
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='delsection';
	push(@{$self->{commit_list}},\%hash);
}

sub assign_addsection
{
my	$self = shift;
my	%hash = @_;
	$hash{action} = 'addsection';
	push(@{$self->{commit_list}}, \%hash);
}

sub assign_editkey
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='editkey';
	push(@{$self->{commit_list}},\%hash);
}

sub assign_delkey
{
my	$self = shift;
my	%hash = @_;
	$hash{'action'}='delkey';
	push(@{$self->{commit_list}},\%hash);
}

#-----------------------------------------------------------
#  save method and save internal method
#  filename: run assign rules and save to file
#  save_file();
sub save_file
{
my	$self = shift;
my	%opts = @_;

	return if ($#{$self->{commit_list}} < 0);

my	$used_resource;
	#check to use resource_list?
	if (defined $self->{'keep_resource_array'} && $self->{'keep_resource_array'}) {
#		$used_resource = $self->{resource_list};
		$used_resource = [ @{ $self->{resource_list} } ];
	}

	if (!defined $used_resource) {
		open(DATA,"<$self->{'file'}") or die "Asterisk-config can't read from $self->{file} : $!";
	my	@DATA = <DATA>;
		close(DATA);
		chomp(@DATA);
		$used_resource = \@DATA;
	}

	foreach my $one_case (@{$self->{commit_list}}) {
		$used_resource = &_do_editkey($one_case,$used_resource,$self) if ($one_case->{'action'} eq 'editkey' || $one_case->{'action'} eq 'delkey');
		$used_resource = &_do_delsection($one_case,$used_resource,$self) if ($one_case->{'action'} eq 'delsection' || $one_case->{'action'} eq 'replacesection');
		$used_resource = &_do_addsection($one_case,$used_resource,$self) if ($one_case->{'action'} eq 'addsection');
		$used_resource = &_do_append($one_case,$used_resource,$self) if ($one_case->{'action'} eq 'append');
		$used_resource = &_do_matchreplace($one_case,$used_resource,$self) if ($one_case->{'action'} eq 'matchreplace');
		if ($one_case->{'action'} eq 'cleanfile') {
			undef($used_resource);
			last;
		}
	}


	#save file and check new_file
	if (defined $opts{'new_file'} && $opts{'new_file'} ne '') {
		open(SAVE,">$opts{'new_file'}") or die "Asterisk-config Save_file can't write : $!";
	} else {
		open(SAVE,">$self->{'file'}") or die "Asterisk-config Save_file can't write : $!";
	}
	flock(SAVE,LOCK_EX);
	print SAVE grep{$_.="\n"} @{$used_resource};
	flock(SAVE,LOCK_UN);
	close(SAVE);

	#reload when save
	if (defined $self->{'reload_when_save'} && $self->{'reload_when_save'}) {
		&reload($self);
	}

return();
}

sub _do_editkey
{
my	$one_case = shift;
my	$data = shift;
my	$class_self = shift;

my	@NEW;
my	$last_section_name='[unsection]';
my	$auto_save=0;

	foreach my $one_line (@$data) {

		#tune on auto save
		if ($auto_save) {			push(@NEW,$one_line);			next;		}

		my $line_sp=&_clean_string($one_line,$class_self->{comment_flag});

		#income new section
		if ($line_sp =~ /^\[(.+)\]/) {
			$last_section_name = $1;
		} elsif ($last_section_name eq $one_case->{section} && $line_sp =~ /\=/) {

			#split data and key
			my ($key,$value)=&_clean_keyvalue($line_sp);

			if ($key eq $one_case->{'key'} && $one_case->{'value_regexp'} && !$one_case->{'value'}) {
				$value =~ /(.+?)\,/;
				if ($one_case->{'action'} eq 'delkey' && $1 eq $one_case->{'value_regexp'}){	undef($one_line);	}

			} elsif ($key eq $one_case->{'key'} && !$one_case->{'value'}) {			#处理全部匹配的key的value值
				if ($one_case->{'action'} eq 'delkey') {	undef($one_line);	}
				else {	$one_line = "$key=".$one_case->{'new_value'};	}
#				$one_line = "$key=".$one_case->{'new_value'};
#				undef($one_line) if ($one_case->{'action'} eq 'delkey');
			} elsif ($key eq $one_case->{'key'} && $one_case->{'value'} eq $value) {	#处理唯一匹配的key的value值
				if ($one_case->{'action'} eq 'delkey') {	undef($one_line);	}
				else {	$one_line = "$key=".$one_case->{'new_value'};	}
#				$one_line = "$key=".$one_case->{'new_value'};
#				undef($one_line) if ($one_case->{'action'} eq 'delkey');
				$auto_save = 1;
			}
		}

		push(@NEW,$one_line) if (defined $one_line);
	}

return(\@NEW);
}

sub _do_delsection
{
my	$one_case = shift;
my	$data = shift;
my	$class_self = shift;

my	@NEW;
my	$last_section_name='[unsection]';
my	$auto_save=0;

	push(@NEW,&_format_convert($one_case->{'data'})) 
		if ($one_case->{'section'} eq '[unsection]' and $one_case->{'action'} eq 'replacesection');

	foreach my $one_line (@$data) {

		#tune on auto save
		if ($auto_save) {			push(@NEW,$one_line);			next;		}

		my $line_sp=&_clean_string($one_line,$class_self->{comment_flag});

		if ($last_section_name eq $one_case->{'section'} && $line_sp =~ /^\[(.+)\]/) {
			#when end of compared section and come new different section
			$auto_save = 1;
		} elsif ($last_section_name eq $one_case->{'section'}) {
			next;
		} elsif ($line_sp =~ /^\[(.+)\]/) {
			#is this new section?
			if ($one_case->{'section'} eq $1) {
				$last_section_name = $1;
				next if ($one_case->{'action'} eq 'delsection');
				push(@NEW,$one_line);
				$one_line=&_format_convert($one_case->{'data'});
			}
		}

		push(@NEW,$one_line);
	}

return(\@NEW);
}

sub _do_addsection
{
my	$one_case = shift;
my	$data = shift;
my	$class_self = shift;

my	$exists = 0;
my	$section = '[' . $one_case->{section} . ']';
	
	foreach my $one_line(@$data) {

		my $line_sp=&_clean_string($one_line,$class_self->{comment_flag});
		if($line_sp =~ /^\[.+\]/) {

			if ($section eq $line_sp) {
				$exists = 1;
				last;
			}
		}
	}
	unless($exists) {

		push(@$data, $section);
	}

return $data;
}

sub _do_append
{
my	$one_case = shift;
my	$data = shift;
my	$class_self = shift;
my	@NEW;

	if ($one_case->{'section'} eq '') {
	#Append data head of source data/foot of source data
		if ($one_case->{'point'} eq 'up') {
			push(@NEW,&_format_convert($one_case->{'data'}),@$data);
		} else {
			push(@NEW,@$data,&_format_convert($one_case->{'data'}));
		}

	} elsif (!defined $one_case->{'comkey'} || $one_case->{'comkey'} eq '') {
	#Append data head/foot of section_name
	my	$auto_save=0;
	my	$save_tmpmem=0;
	my	$offset=0;
		foreach my $one_line (@$data) {
			#tune on auto save
			if ($auto_save) {			push(@NEW,$one_line);			$offset++;	next;		}
			#check section
		my	$line_sp=&_clean_string($one_line,$class_self->{comment_flag});
		my	($section_name) = $line_sp =~ /^\[(.+)\]/;

			# for up / down
			if (defined $section_name && $one_case->{'section'} eq $section_name && $one_case->{'point'} eq 'up') {
				push(@NEW,&_format_convert($one_case->{'data'}));	$auto_save=1;
			} elsif (defined $section_name && $one_case->{'section'} eq $section_name && $one_case->{'point'} eq 'down') {
				push(@NEW,$one_line);	$one_line = join "\n", &_format_convert($one_case->{'data'});		$auto_save=1;
			# for foot matched section
			} elsif (defined $section_name && $one_case->{'section'} eq $section_name && $one_case->{'point'} eq 'foot') {
				$save_tmpmem=1;
			# for foot 发现要从匹配的section换成新section
			} elsif ($save_tmpmem == 1 && $section_name && $one_case->{'section'} ne $section_name) {
				push(@NEW,&_format_convert($one_case->{'data'}));	$auto_save=1;	$save_tmpmem=0;
			# for foot 发现匹配的section已经到达整个结尾
			} 
			if ($save_tmpmem == 1 && $offset==$#{$data}) {
				push(@NEW,$one_line);	$one_line = join "\n", &_format_convert($one_case->{'data'});
				$auto_save=1;	$save_tmpmem=0;
			}

			push(@NEW,$one_line);
			$offset++;
		}

	} else {

		my $last_section_name='[unsection]';
		my $auto_save=0;
		foreach my $one_line (@$data) {

			#tune on auto save
			if ($auto_save) {			push(@NEW,$one_line);			next;		}

			my $line_sp=&_clean_string($one_line,$class_self->{comment_flag});
			#income new section
			if ($line_sp =~ /^\[(.+)\]/) {
				$last_section_name = $1;
			} elsif ($last_section_name eq $one_case->{'section'} && $line_sp =~ /\=/) {
				#split data and key
				my ($key,$value)=&_clean_keyvalue($line_sp);
				if ($key eq $one_case->{comkey}[0] && $value eq $one_case->{comkey}[1] && $one_case->{'point'} eq 'up') {
					push(@NEW,&_format_convert($one_case->{'data'}));	$auto_save=1;
				} elsif ($key eq $one_case->{comkey}[0] && $value eq $one_case->{comkey}[1] && $one_case->{'point'} eq 'down') {
					push(@NEW,$one_line);	$one_line=&_format_convert($one_case->{'data'});
					$auto_save=1;
				} elsif ($key eq $one_case->{comkey}[0] && $value eq $one_case->{comkey}[1] && $one_case->{'point'} eq 'over') {
					$one_line=&_format_convert($one_case->{'data'});		$auto_save=1;
				}
			}
			push(@NEW,$one_line);
		}

	}

return(\@NEW);
}

# income scalar,array ref,hash ref output array data
sub _format_convert
{
my	$string = shift;
	if (ref($string) eq 'ARRAY') {
		return(@$string);
	} elsif (ref($string) eq 'HASH') {
		my @tmp;
		foreach  (keys(%$string)) {
			push(@tmp,"$_=".$string->{$_});
		}
		return(@tmp);
	} else {
		return($string);
	}
}

sub _do_matchreplace
{
my	$one_case = shift;
my	$data = shift;
my	$class_self = shift;
my	@NEW;

	foreach my $one_line (@$data) {
		if ($one_line =~ /$one_case->{'match'}/) {
			$one_line = $one_case->{'replace'};
		}
		push(@NEW,$one_line);
	}

return(\@NEW);
}

=head1 NAME

Asterisk::config - the Asterisk config read and write module.

=head1 SYNOPSIS

    use Asterisk::config;

    my $sip_conf = new Asterisk::config(file=>'/etc/asterisk/sip.conf');
    my $conference = new Asterisk::config(file=>'/etc/asterisk/meetme.conf',
                                              keep_resource_array=>0);

    $allow = $sip_conf->fetch_values_arrayref(section=>'general',key=>'allow');
    print $allow->[0];

    $sip_conf->assign_append(point=>'down',data=>"[userb]\ntype=friend\n");

    $sip_conf->save();


=head1 DESCRIPTION

Asterisk::config can parse and saving data with Asterisk config
files. this module support asterisk 1.0 1.2 1.4 1.6, and it also
support Zaptel config files.

=head1 Note

Version 0.9 syntax incompitable with 0.8.

=head1 CLASS METHOD

=head2 new

    $sip_conf = new Asterisk::config(file=>'file name',
                                     [stream_data=>$string],
                                     [object variable]);

Instantiates a new object of file. Reads data from stream_data or
file.


=head1 OBJECT VARIABLES

FIXME: should all of those be documented in the POD (rather than
in comments in the code?)

=head2 file

Config file name and path. Must be set.
If file does exists (exp. data from C<stream_data>), you will not
be able to save using L<save_file>.

=head2 keep_resource_array

use resource array when save make fast than open file, but need
more memory, default enabled. use set_objvar to change it.

=head2 reload_when_save

When save done, auto call .

Enabled by default. Use set_variable to change it.

FIXME: what is C<set_variable>?

=head2 clean_when_reload

When reload done, auto clean_assign with current object.

Enabled by default. Use L<set_objvar> to change it.

=head2 commit_list

Internal variable listed all command. 

=head2 parsed_conf

Internal variable of parsed. 


=head1 OBJECT READ METHOD

=head2 get_objvar

    $sip_conf->get_objvar(var_name);

Return defined object variables.

=head2 fetch_sections_list

    $sip_conf->fetch_sections_list();

List of sections (not including C<unsection>) in a file.

=head2 fetch_sections_hashref

    $sip_conf->fetch_sections_hashref();

Returns the config file parsed as a hash (section name -> section)
of lists (list of lines).

=head2 fetch_keys_list

    $sip_conf->fetch_keys_list(section=>[section name|unsection]);

return keys list of section name or unsection.

=head2 fetch_keys_hashref

    $sip_conf->fetch_keys_hashref(section=>[section name|unsection]);

return referenced key list (and keys value), section value 'unsection'
return all unsection keys, if section name unreachable return failed.

=head2 fetch_values_arrayref

    $sip_conf->fetch_values_arrayref(section=>[section name|unsection],
                                     key=>key name);

return referenced value list, if section name unreachable return 
failed. if key name unreachable return failed.

=head2 reload

    $sip_conf->reload();

reload and parse config file.
if clean_when_reload true will do clean_assign.

=head1 OBJECT WRITE METHOD

=head2 set_objvar

    $sip_conf->set_objvar('var_name'=>'value');

set the object variables to new value.

=head2 assign_cleanfile

    $sip_conf->assign_cleanfile();

assign clean all to file.

=head2 assign_matchreplace

    $sip_conf->assign_matchreplace(match=>[string],replace=>[string]);

replace new data when matched.

=over 2

=item * match -> string of matched data.

=item * replace -> new data string.

=back

=head2 assign_append

    $sip_conf->assign_append(point=>['up'|'down'|'foot'],
                             section=>[section name],
                             data=>'key=value'|['key=value','key=value']|{key=>'value',key=>'value'});

append data around with section name.

=over 3

=item * point -> append data C<up> / C<down> / C<foot> with section.

=item * section -> matched section name, expect 'unsection'.

=item * data -> new replace data in string/array/hash.

=back

    $sip_conf->assign_append(point=>['up'|'down'|'over'],
                             section=>[section name],
                             comkey=>[key,value],
                             data=>'key=value'|['key=value','key=value']|{key=>'value',key=>'value'};

append data around with section name and key/value in same section.

=over 2

=item * point -> C<over> will overwrite with key/value matched.

=item * comkey -> match key and value.

=back

    $sip_conf->assign_append(point=>'up'|'down',
                             data=>'key=value'|['key=value','key=value']|{key=>'value',key=>'value'});

simple append data without any section.

=head2 assign_replacesection

    $sip_conf->assign_replacesection(section=>[section name|unsection],
                             data=>'key=value'|['key=value','key=value']|{key=>'value',key=>'value'});

replace the section body data.

=over 1

=item * section -> all section name and 'unsection'.

=back

=head2 assign_delsection

    $sip_conf->assign_delsection(section=>[section name|unsection]);

erase section name and section data.

=over 1

=item * section -> all section and 'unsection'.

=back

=head2 assign_addsection

    $sip_conf->assign_addsection(section=>[section]);

add section with name.

=over 1

=item * section -> name of new section.

=back

=head2 assign_editkey

    $sip_conf->assign_editkey(section=>[section name|unsection],key=>[keyname],value=>[value],new_value=>[new_value]);

modify value with matched section.if don't assign value=> will replace all matched key. 

warnning example script:

    $sip_conf->assign_editkey(section=>'990001',key=>'all',new_value=>'gsm');

data:

    all=g711
    all=ilbc

will convert to:

    all=gsm
    all=gsm


=head2 assign_delkey

    $sip_conf->assign_delkey(section=>[section name|unsection],key=>[keyname],value=>[value]);

erase all matched C<keyname> in section or in 'unsection'.

    $sip_conf->assign_delkey(section=>[section name|unsection],key=>[keyname],value_regexp=>[exten_number]);

erase when matched exten number.

	exten => 100,n,...
	exten => 102,n,...

=head2 save_file

    $sip_conf->save_file([new_file=>'filename']);

process commit list and save to file.
if reload_when_save true will do reload.
if no object variable file or file not exists or can't be 
save return failed.
if defined new_file will save to new file, default overwrite
objvar 'file'.

=head2 clean_assign

    $sip_conf->clean_assign();

clean all assign rules.

=head1 EXAMPLES

see example in source tree.

=head1 AUTHORS

Asterisk::config by Sun bing <hoowa.sun@gmail.com>

Version 0.7 patch by Liu Hailong.

=head1 COPYRIGHT

The Asterisk::config module is Copyright (c) Sun bing <hoowa.sun@gmail.com>
All rights reserved.

You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the Perl README file.

=head1 WARRANTY

The Asterisk::config is free Open Source software.

IT COMES WITHOUT WARRANTY OF ANY KIND.

=head1 SUPPORT

Sun bing <hoowa.sun@gmail.com>

The Asterisk::config be Part of FreeIris opensource Telephony Project
Access http://www.freeiris.org for more details.

=cut

1;