package LedgerSMB::RESTXML::Handler; use strict; use warnings; use Carp; use LedgerSMB::User; use LedgerSMB::Sysconfig; use LedgerSMB::Log; use Scalar::Util qw(blessed); use DBI; =head3 cgi_handle CGI_handle is the gateway for the RESTful lsmb API. =head3 NOTES =cut sub cgi_handle { my $self = shift; my $method = $ENV{REQUEST_METHOD}; my $pathinfo = $ENV{PATH_INFO}; #pull off the leading slash, we need it in the form document/arguments/foo $pathinfo =~ s#^/##; my $function = 'handle_' . lc($method); my ( $user, $module, @args ) = split '/', $pathinfo; $user = LedgerSMB::User->fetch_config($user); my $dbh = $self->connect_db($user); # non-word characters are forbidden, usually a sign of someone being sneaky. $module =~ s#\W##; my $document_module = $self->try_to_load($module); if ($document_module) { if ( $document_module->can($function) ) { my $returnValue = $document_module->$function( { dbh => $dbh, args => \@args, handler => $self, user => $user } ); #return $self->return_serialized_response($returnValue); } else { return $self->unsupported("$module cannot handle method $method"); } } else { return $self->not_found( "Could not find a handler for document type $module:
$@
" ); } } sub cgi_report_error { my $self = shift; my $message = shift; my $code = shift || 500; print "Status: $code\n"; print "Content-Type: text/html\n\n"; print "\n"; print "

REST API error

"; print "
$message
"; print ""; } sub cgi_read_query { my $self = shift; use CGI::Simple; my $cgi = CGI::Simple->new(); return $cgi; } # ------------------------------------------------------------------------------------------------------------------------ =head3 try_to_load try_to_load will try to load a RESTXML document handler module. returns undef if it cannot load the given module for any reason. passed the type of RESTXML document to try to load. returns a blessed anonymous hashref if the module *can*, and is successfully loaded. =cut sub try_to_load { my $self = shift; my $module = shift; eval qq{ use LedgerSMB::RESTXML::Document::$module; }; if ($@) { warn "Cannot load $module: $@" unless $@ =~ /Can't locate LedgerSMB\//i; return undef; } else { return bless {}, "LedgerSMB::RESTXML::Document::$module"; } } =head3 connect_db Given a user's config, returns a database connection handle. =cut sub connect_db { my ( $self, $myconfig ) = @_; my $dbh = DBI->connect( $myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd} ) or carp "Error connecting to the db :$DBI::errstr"; $dbh->{pg_enable_utf8} = 1; return $dbh; } # lets see how far XML::Simple can take us. use XML::Simple; use Scalar::Util qw(blessed); sub return_serialized_response { my ( $self, $response ) = @_; print "Content-type: text/xml\n\n"; if ( blessed $response && $response->isa('XML::Twig::Elt') ) { print qq{\n}; print $response->sprint(); } else { my $xs = XML::Simple->new( NoAttr => 1, RootName => 'LedgerSMBResponse', XMLDecl => 1 ); print $xs->XMLout($response); } return; } sub read_query { my ($self) = @_; # for now. return $self->cgi_read_query(); } # =------------------------- POSSIBLE WAYS FOR MODULES TO RESPOND. sub respond { my ( $self, $data ) = @_; return $self->return_serialized_response($data); } sub not_found { my ( $self, $message ) = @_; $self->cgi_report_error( $message, 404 ); } sub unsupported { my ( $self, $message ) = @_; $self->cgi_report_error( $message, 501 ); } sub error { my ( $self, $message ) = @_; $self->cgi_report_error( $message, 500 ); } 1;