summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir@cohens.org.il>2009-05-24 17:02:36 +0300
committerTzafrir Cohen <tzafrir@cohens.org.il>2009-05-24 17:02:36 +0300
commite07b80f91c00fcf5013fe2c7066008470d203d30 (patch)
tree184171ff1d0f2dfc0e2e1357b4a560d3b5ab7fe2
Importing Asterisk::config 0.96 from tarball
-rwxr-xr-xChanges60
-rwxr-xr-xMANIFEST18
-rwxr-xr-xMETA.yml13
-rwxr-xr-xMakefile.PL12
-rwxr-xr-xREADME33
-rwxr-xr-xexamples/exten.conf8
-rwxr-xr-xexamples/new.conf20
-rwxr-xr-xexamples/new_exten.conf7
-rwxr-xr-xexamples/sip.conf25
-rwxr-xr-xexamples/test1.pl10
-rwxr-xr-xexamples/test2.pl30
-rwxr-xr-xexamples/test3.pl16
-rwxr-xr-xexamples/test4.pl14
-rwxr-xr-xexamples/test5.pl26
-rwxr-xr-xexamples/test6.pl12
-rwxr-xr-xlib/Asterisk/config.pm944
-rwxr-xr-xt/read.t12
-rwxr-xr-xt/test.conf25
18 files changed, 1285 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100755
index 0000000..4d223fb
--- /dev/null
+++ b/Changes
@@ -0,0 +1,60 @@
+0.96 2008-4-29
+ - error name _format_convert in _do_append
+ - add return when not commit list on save_file
+0.95 2008-3-19
+ - add variable $object->{parsed_section_chunk}
+0.94 2008-3-11
+ - enable reload_when_save function
+0.93 2008-2-1
+ - add value_regexp to assign_delkey
+0.92 2008-1-28
+ - add make test
+0.91 2008-1-25
+ - fix some warnnings syntax.
+
+0.9 2008-1-23
+ - changed object syntax.
+ - add function get_objvar
+ - add function fetch_sections_list
+ - add function fetch_sections_hashref
+ - add function fetch_keys_list
+ - add function fetch_keys_hashref
+ - add function fetch_values_arrayref
+ - add function reload
+ - add function set_objvar
+ - add object variable file
+ - add object variable keep_resource_array
+ - add object variable reload_when_save
+ - add object variable clean_when_reload
+ - add object variable commit_list
+ - add object variable parsed_conf
+ - changed readme
+
+0.8 2006-6-2
+ - new parameter point=>'foot' in assign_append().
+ - fix dejected \n in do_append() and/or do_delsection().
+
+0.7 2006-4-18
+ - add addsection.
+ - fix regexp in sub clean_string.
+ - fixed the bug of @commit_list dirty-write resulted by more than one instance of Asterisk::config
+
+0.6 2006-2-19
+ - change load_config parameters to hash struct.
+ - fix split (.+) to (.*) in sub clean_keyvalue.
+ - fix split (.+) to (.*) in sub load_config.
+ - add use Fcntl ':flock' for disable flock warnings.
+ - more POD docs.
+ - fix VERSION variable.
+ - fix $stream_data warnings in load_config using strict.
+ - fix # warnings in load_config using strict.
+0.5 2006-1-6
+ - support stream in load_config.
+ - my $VERSION
+
+0.4 2005-12-23
+ - add assign_matchreplace.
+
+0.1 2005-11-29
+ - start....
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100755
index 0000000..a95a1fc
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,18 @@
+Changes
+examples/exten.conf
+examples/new.conf
+examples/new_exten.conf
+examples/sip.conf
+examples/test1.pl
+examples/test2.pl
+examples/test3.pl
+examples/test4.pl
+examples/test5.pl
+examples/test6.pl
+lib/Asterisk/config.pm
+Makefile.PL
+MANIFEST This list of files
+README
+t/read.t
+t/test.conf
+META.yml Module meta-data (added by MakeMaker)
diff --git a/META.yml b/META.yml
new file mode 100755
index 0000000..08720bc
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,13 @@
+--- #YAML:1.0
+name: Asterisk-config
+version: 0.95
+abstract: The Asterisk config read and write module.
+license: gpl
+author:
+ - Sun Bing (hoowa.sun@gmail.com)
+generated_by: ExtUtils::MakeMaker version 6.42_01
+distribution_type: module
+requires:
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.3.html
+ version: 1.3
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100755
index 0000000..58fa955
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,12 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ AUTHOR => 'Sun Bing (hoowa.sun@gmail.com)',
+ NAME => 'Asterisk::config',
+ ABSTRACT => 'The Asterisk config read and write module.',
+ VERSION_FROM => 'lib/Asterisk/config.pm',
+ ($ExtUtils::MakeMaker::VERSION >= 6.31
+ ? ( LICENSE => 'gpl' )
+ : ()
+ ),
+);
diff --git a/README b/README
new file mode 100755
index 0000000..97a8cc4
--- /dev/null
+++ b/README
@@ -0,0 +1,33 @@
+This is the README file for Asterisk::config, a module to
+read/write Asterisk config.
+
+Asterisk is most popular Opensource PBX in PBX World!
+
+Access www.asterisk.org for more details.
+
+* Installation
+
+Asterisk::config uses the standard perl module install process:
+
+perl Makefile.PL
+make
+make install
+
+* Copyright
+
+See COPYRIGHT section in pod text below for usage and
+distribution rights.
+
+by Sun bing <hoowa.sun@gmail.com>
+
+* Introduction
+
+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.
+
+The Asterisk::config be Part of FreeIris opensource Telephony Project
+Access http://www.freeiris.org for more details.
+
+Please refer to the POD text for synopsis and usage details.
+
diff --git a/examples/exten.conf b/examples/exten.conf
new file mode 100755
index 0000000..96a92f6
--- /dev/null
+++ b/examples/exten.conf
@@ -0,0 +1,8 @@
+
+[mytest]
+exten => 200,1,Dial(SIP/200)
+exten => 201,1,NoOp('No response')
+exten => 201,2,Hangup()
+
+[demo]
+exten => s,1,NoOp('hello')
diff --git a/examples/new.conf b/examples/new.conf
new file mode 100755
index 0000000..7369ddf
--- /dev/null
+++ b/examples/new.conf
@@ -0,0 +1,20 @@
+type=friend
+secret=123456
+[general]
+useragent=hoowa
+disallow=all
+#include classsip.conf
+
+[usera]
+type=friend;saying
+secret=thisone
+host=192.168.0.1;dynamic
+
+[trunka]
+type=peer
+host=192.168.0.1
+
+#include branchsip.conf
+
+[gan]
+allow=h263
diff --git a/examples/new_exten.conf b/examples/new_exten.conf
new file mode 100755
index 0000000..b3ba1f9
--- /dev/null
+++ b/examples/new_exten.conf
@@ -0,0 +1,7 @@
+
+[mytest]
+exten => 200,1,Dial(SIP/200)
+exten => 201,1,NoOp('No response')
+
+[demo]
+exten => s,1,NoOp('hello')
diff --git a/examples/sip.conf b/examples/sip.conf
new file mode 100755
index 0000000..1cfbf94
--- /dev/null
+++ b/examples/sip.conf
@@ -0,0 +1,25 @@
+key=value
+
+thisis=>unsection
+
+[general]
+useragent=hoowa
+disallow=all
+allow=g729
+allow=>g723 #comment
+allow=ulaw
+#include classsip.conf
+
+[usera]
+type=friend;saying
+secret=thisone
+host=>dynamic
+
+[trunka]
+type=friend
+host=192.168.0.1
+
+#include branchsip.conf
+
+[tempsection]
+delete=me
diff --git a/examples/test1.pl b/examples/test1.pl
new file mode 100755
index 0000000..d8ec230
--- /dev/null
+++ b/examples/test1.pl
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+use Data::Dumper;
+use lib '../lib';
+use Asterisk::config;
+
+my $rc = new Asterisk::config(file=>'sip.conf',keep_resource_array=>0);
+if ($rc) {
+ print "true";
+}
+print Dumper $rc;
diff --git a/examples/test2.pl b/examples/test2.pl
new file mode 100755
index 0000000..1476e66
--- /dev/null
+++ b/examples/test2.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+use Data::Dumper;
+use lib '../lib';
+use Asterisk::config;
+
+my $rc = new Asterisk::config(file=>'sip.conf');
+
+$myfile = $rc->get_objvar('file');
+print $myfile;
+print "\n\n";
+
+$parsed = $rc->fetch_sections_hashref();
+print Dumper $parsed;
+print "\n\n";
+
+$section_list = $rc->fetch_sections_list();
+print Dumper $section_list;
+print "\n\n";
+
+$key_ref = $rc->fetch_keys_hashref(section=>'[unsection]');
+print Dumper $key_ref;
+print "\n\n";
+
+$key_list = $rc->fetch_keys_list(section=>'general');
+print Dumper $key_list;
+print "\n\n";
+
+$value_list = $rc->fetch_values_arrayref(section=>'general',key=>'useragent');
+print Dumper $value_list;
+print "\n\n";
diff --git a/examples/test3.pl b/examples/test3.pl
new file mode 100755
index 0000000..9c8169f
--- /dev/null
+++ b/examples/test3.pl
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+use Data::Dumper;
+use lib '../lib';
+use Asterisk::config;
+
+my $rc = new Asterisk::config(file=>'sip.conf');
+
+print $rc->fetch_sections_hashref();
+print "\n\n";
+
+if ($rc->reload()) {
+ print "true reload\n\n";
+}
+
+print $rc->fetch_sections_hashref();
+print "\n\n";
diff --git a/examples/test4.pl b/examples/test4.pl
new file mode 100755
index 0000000..bb6b1f3
--- /dev/null
+++ b/examples/test4.pl
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+use Data::Dumper;
+use lib '../lib';
+use Asterisk::config;
+
+my $rc = new Asterisk::config(file=>'sip.conf');
+
+if (!$rc->set_objvar(mytest=>'test')) {
+ print "not found\n";
+}
+
+$rc->set_objvar(reload_when_save=>123467);
+
+print $rc->get_objvar('reload_when_save');
diff --git a/examples/test5.pl b/examples/test5.pl
new file mode 100755
index 0000000..6de3eb7
--- /dev/null
+++ b/examples/test5.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+use Data::Dumper;
+use lib '../lib';
+use Asterisk::config;
+
+my $rc = new Asterisk::config(file=>'sip.conf',keep_resource_array=>0);
+
+$rc->assign_matchreplace(match=>'host=>dynamic',replace=>'host=192.168.0.1;dynamic');
+
+$rc->assign_replacesection(section=>'[unsection]',data=>['type=friend','secret=123456']);
+
+$rc->assign_delsection(section=>'tempsection');
+
+$rc->assign_addsection(section=>'gan');
+
+$rc->assign_append(point=>'down',section=>'gan',data=>'allow=h263');
+
+$rc->assign_editkey(section=>'trunka',key=>'type',new_value=>'peer');
+
+$rc->assign_delkey(section=>'general',key=>'allow');
+
+#$rc->assign_cleanfile();
+
+# new_file can save data to newfile
+$rc->save_file(new_file=>'new.conf');
+
diff --git a/examples/test6.pl b/examples/test6.pl
new file mode 100755
index 0000000..c14d0ab
--- /dev/null
+++ b/examples/test6.pl
@@ -0,0 +1,12 @@
+#!/usr/bin/perl -w
+use Data::Dumper;
+use lib '../lib';
+use Asterisk::config;
+
+my $rc = new Asterisk::config(file=>'exten.conf',keep_resource_array=>0);
+
+$rc->assign_delkey(section=>'mytest',key=>'exten',value_regexp=>'201',value=>'201,2,Hangup()');
+
+# new_file can save data to newfile
+$rc->save_file(new_file=>'new_exten.conf');
+
diff --git a/lib/Asterisk/config.pm b/lib/Asterisk/config.pm
new file mode 100755
index 0000000..dc88ebf
--- /dev/null
+++ b/lib/Asterisk/config.pm
@@ -0,0 +1,944 @@
+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=&_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=&_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. read data from stream_data or
+file.
+
+
+=head1 OBJECT VARIABLE
+
+=head2 file
+
+config file name and path.
+if file no exists (exp. data from stream_data ) you can't
+saving by C<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 .
+
+default enable. use set_variable to change it.
+
+=head2 clean_when_reload
+
+when reload done, auto clean_assign with current object.
+default enable. use set_objvar to change it.
+
+=head2 commit_list
+
+internal variable listed all command.
+i suggest don't modify and change this variable.
+
+=head2 parsed_conf
+
+internal variable of parsed.
+i suggest don't modify and change this variable.
+
+
+=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();
+
+only return sections name list. does not include 'unsection'.
+
+=head2 fetch_sections_hashref
+
+ $sip_conf->fetch_sections_hashref();
+
+this function return parsed config files data.
+
+=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;
diff --git a/t/read.t b/t/read.t
new file mode 100755
index 0000000..b267b9a
--- /dev/null
+++ b/t/read.t
@@ -0,0 +1,12 @@
+#make sample read test
+use strict;
+use Test;
+
+use lib '../lib';
+
+BEGIN { plan tests => 1 }
+
+use Asterisk::config;
+
+my $sip_conf = new Asterisk::config(file=>'t/test.conf');
+ok($sip_conf);
diff --git a/t/test.conf b/t/test.conf
new file mode 100755
index 0000000..1cfbf94
--- /dev/null
+++ b/t/test.conf
@@ -0,0 +1,25 @@
+key=value
+
+thisis=>unsection
+
+[general]
+useragent=hoowa
+disallow=all
+allow=g729
+allow=>g723 #comment
+allow=ulaw
+#include classsip.conf
+
+[usera]
+type=friend;saying
+secret=thisone
+host=>dynamic
+
+[trunka]
+type=friend
+host=192.168.0.1
+
+#include branchsip.conf
+
+[tempsection]
+delete=me