Rest.pm

#
# Copyright (c) 2008 Klaas Freitag <freitag@suse.de>, Novell Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
################################################################
# Contributors:
#  Klaas Freitag <freitag@suse.de>
#
package Hermi::Rest;


use XML::Simple qw(XMLout);
use Data::Dumper;
use strict;


use base 'CGI::Application';
# use CGI::Application::Plugin::ActionDispatch;


use JSON;


use Hermes::Config;
use Hermes::Log;
use Hermes::Message;
use Hermes::Statistics;
use Hermes::Person;
use Hermes::Util;
use Hermes::DB;
use Hermes::Delivery::Mail;


# sub do_stuff : Path('do/stuff') { ... }
# sub do_more_stuff : Regex('^/do/more/stuff\/?$') { ... }
# sub do_something_else : Regex('do/something/else/(\w+)/(\d+)$') { ... }
use vars qw( $htmlTmpl %isAdmin $user );


sub setup {
  my $self = shift;
  $self->start_mode('hello');
  $self->run_modes(
  'ajaxupdate'   => 'ajaxUpdate',
  'notify'       => 'postNotification',
  'hello'        => 'sayHello',
  'doc'          => 'showDoc',
  'type'         => 'editType',
  'httptest'     => 'httpTest',
  'posthttptest' => 'httpTestInput',
  'subscribe'    => 'subscribePerson',
  'subscriptions'=> 'userSubscriptions',
  'subscribe_ml' => 'subscribeToMailinglist'
 );
  $self->mode_param( 'rm' );


  $htmlTmpl = $self->load_tmpl( 'hermes.tmpl',
die_on_bad_params => 0,
cache => 1 );
}


# prerun is needed to handle POST calls that have some of the
# parameters still set as url parameters. prerun analyses the
# rm parameter to call the correct runmode.
sub cgiapp_prerun
{
  my $self = shift;


  # Get CGI query object
  my $q = $self->query();
  my $mode = lc $q->url_param('rm');
  if( $mode eq 'subscribe_ml' ) {
    $self->prerun_mode( 'subscribe_ml' );
  } elsif( $mode eq "notify" ) {
    $self->prerun_mode( 'notify' );
  } elsif( $mode eq "ajaxupdate" ) {
    $self->prerun_mode( 'ajaxupdate' );
  } elsif( $mode eq "posthttptest" ) {
    $self->prerun_mode( 'posthttptest' );
  } elsif( $mode eq "subscribe" ) {
    $self->prerun_mode( 'subscribe' );
  }
  log('info', "Current Runmode: " . $self->get_current_runmode() );


  # Check for the users admin flag
  $user = "anonymous";
  my $loggedInUser;
  if( lc $Hermes::Config::authentication eq "ichain" ) {
    # with iChain authentification, the HTTP request contains trustable header
    # values like the username. Since iChain works like a proxy these can be
    # taken for real and no further dealing with passwords etc. is needed.


    # my @httpHeader = $q->http();
    # log('info', "HTTP-Header: " . join(", ", @httpHeader ) );


    if( $q->http('X_USERNAME') && $q->http('X_USERNAME') ne '' ) {
      $loggedInUser = $q->http('X_USERNAME');
    } else {
      # if we are ichain AND have no X_USERNAME it means that we come through
      # a secret door from the backend. USE THIS AT YOUR OWN RISK!
      $loggedInUser = 'obs_backend';
    }
  } elsif( $Hermes::Config::authentication =~ /^ichaintest-(.+)$/) {
    $loggedInUser = $1;
  } elsif( $Hermes::Config::authentication =~ /^\s*http-server\s*$/ ) {
    $loggedInUser = $ENV{REMOTE_USER};
    if ( ! $loggedInUser ) {
      $loggedInUser = 'obs_backend';
    }
  }



  log('info', "User Name: " . ($loggedInUser ? $loggedInUser : "anonymous") );


  if( $loggedInUser ) {
    my $sql = "SELECT admin FROM persons WHERE stringid=?";
    my $sth = dbh()->prepare( $sql );
    $sth->execute( $loggedInUser );
    my ($admin) = $sth->fetchrow_array();
    $admin = 0 unless ( $admin );
    log( 'info', "Admin flag for user <$loggedInUser>: $admin" );
    $isAdmin{ $loggedInUser } = $admin > 0;
    $user = $loggedInUser;
  }


  my $post = $q->param( 'POSTDATA' );
  log( 'info', "POST data in prerun: <$post>" ) if( $post );
}


sub cgiapp_postrun
{
  my $self = shift();


  $self->header_type( 'header' );
  # $self->header_props( -expires => 'now' );
  $self->header_props( '-Cache-Control' => 'no-cache' );
}


#
# This sub creates a CGI query object for the CGI::Application framework. It needs to
# be overwritten because we need the pragma -oldstyle_urls which is not default for CGI.
# Not setting oldstyle-uri makes the CGI parser to split up parameters at semicolons.
# That's wrong because for us and creates strange additional parameters in the DB
# because for example comments with semicolons are split up.
#
sub cgiapp_get_query
{
  my $self = shift();


  use CGI qw /-oldstyle_urls -utf8/;


  return CGI->new();
}


#
# initialise the frame html template that is around all detail pages.
#
sub initFrame( $ )
{
  my ($header) = @_;


  $htmlTmpl->param( Header => $header );
  $htmlTmpl->param( isAdmin => $isAdmin{ $user } || 0 );
  $htmlTmpl->param( User => $user );


  my ($list, $firstType) = templateTypeList();
  $htmlTmpl->param( NotiTypeLinks => $list );


}


sub sayHello
{
  my $self = shift;


  # Get CGI query object
  my $q = $self->query();
  initFrame( "Welcome to Hermes" );


  my $detailTmpl = $self->load_tmpl( 'info.tmpl', die_on_bad_params => 0, cache => 0 );


  my $msgList = latestNMessages(10);
  my $notiList = latestNRawNotifications( 25 );
  my $cnt = @{$notiList};


  $detailTmpl->param( CntRawNotifications => $cnt );
  $detailTmpl->param( RawNotiLastHour => countRawNotificationsInHours( 1 ) );
  $detailTmpl->param( LatestMessages => $msgList );
  $detailTmpl->param( countMessages => countMessages() );
  $detailTmpl->param( RawNotifications => $notiList );
  # print STDERR $detailTmpl->output;
  $htmlTmpl->param( Content => $detailTmpl->output );


  return $htmlTmpl->output;
}


sub showDoc
{
  my $self = shift;


  my $q = $self->query();
  my $docTmpl = $self->load_tmpl( 'doc.tmpl',
 die_on_bad_params => 0,
 cache => 1 );
  $docTmpl->param( urlbase => "subbotin.suse.de/hermes" );
  initFrame( "Hermes Documentation" );
  $htmlTmpl->param( Content => $docTmpl->output );


  return $htmlTmpl->output;
}


sub postNotification {
  my $self = shift;


  my $q = $self->query();
  my $id;
  
  if( $isAdmin{ $user } ) {
    my $type = $q->param( '_type' );
    my $params = $q->Vars;


    $id = notificationToInbox( $type, $params );
  } else {
    $id = "<h1>Permission denied - need admin flag to post notifications</h1>";
  }


  return "$id";
}


sub subscribePerson()
{
  my $self = shift;
  
  if( $isAdmin{$user} ) {
    my $q = $self->query();
    my $type = $q->param('person');


    log( 'info', "subscribePerson: Not yet implemented!" );
  }
}


sub userSubscriptions()
{
  my $self = shift;


  my $q = $self->query();
  my $query_users_allowed = 1;
  my @persons = $q->param('person');
  
  unless( $isAdmin{ $user } )  {
    # for non admins only allow to query themselves
    @persons = ($user);
    $query_users_allowed = 0;
  }
  
  my @msg_types = $q->param('types');


  my $wantXML = undef;
  my $wantJSON = undef;
  
  if( $q->param( 'contenttype' ) ){
    if( $q->param('contenttype') eq "text/xml" ) {
      $wantXML = 1;
    } elsif( $q->param('contenttype') eq "text/json" ) {
      $wantJSON = 1;
    }
  }


  my @persList;
  my $persCnt = @persons;
  @persons = ($user) unless( $persCnt );
  
  foreach my $person ( @persons ) {
    log( 'info', "doing person $person" );
    my $subsList = subscriptions( $person );
    
    my @subsList;
    foreach my $subsHashRef ( @$subsList ) {
      if( @msg_types ) {
# check if the current type is in the list of wanted types
log( 'info', "Checking for " . $subsHashRef->{msgtype} . "in " . join ( ' ', @msg_types ));
next unless ( grep ( $subsHashRef->{msgtype}, @msg_types ) );
      }
      my %resHash;
      if( $wantXML ) {
$resHash{delivery} = [ deliveryIdToString( $subsHashRef->{delivery_id} ) ];
$resHash{delay}    = [ delayIdToString( $subsHashRef->{delay_id} ) ];
$resHash{msgtype}  = [ $subsHashRef->{msgtype} ];
      } else {
$resHash{id}       = $subsHashRef->{id};
$resHash{delivery} = deliveryIdToString( $subsHashRef->{delivery_id} );
$resHash{delay}    = delayIdToString( $subsHashRef->{delay_id} );
$resHash{msgtype}  = $subsHashRef->{msgtype};
      }
      push @subsList, \%resHash;
    }
    push @persList, { name => $person, subs => \@subsList };
  }


  if( $wantXML ) {
    return XMLout( { person => \@persList }, RootName => "subscriptions", 
   XMLDecl => 1 );
  } elsif( $wantJSON ) {
    return encode_json( \@persList );
  } else {
    # html output
    my $subsTmpl = $self->load_tmpl( 'subscriptions.tmpl',
    die_on_bad_params => 0,
    cache => 1 );
    initFrame( "Hermes User Subscriptions" );
    my @personsList;
    $subsTmpl->param( user => \@persList );
    $subsTmpl->param( query_users_allowed => $query_users_allowed );
    
    # $subsTmpl->param( person => $person || "<no person>" );
    # $subsTmpl->param( subscriptions => \@subsList );


    $htmlTmpl->param( Content => $subsTmpl->output );
  } 


  return $htmlTmpl->output;
}


sub subscribeToMailinglist()
{
  my $self = shift;


  my $q = $self->query();
  unless( $isAdmin{ $user } ) {
    log('info', "Permission denied to subscribe to mailinglists." );
    return ""; # FIXME: Send permission denied error code
  }


  my @mailinglists = $q->param('mailinglist');
  my $email        = $q->param('email');
  my $username     = $q->param('username' );


  unless( $email =~ /.?\@.?/ ) {
    log( 'info', "Invalid email for subscription, can not do anything." );
    return;
  }
  
  log( 'info', "Subscription to Mailinglist wanted: User $username / $email to lists " . 
        join(", ", @mailinglists ) );


  my $re;
  foreach my $ml ( @mailinglists ) {
    my $to = "$ml+subscribe\@opensuse.org";
    if( sendMail( { from    => $email,
   to      => [$to],
   subject => "Subscribe to $ml",
   body    => '',
   # _debug  => 1,
   _skip_user_check => 1} ) ) {
log( 'info', "subscription mail successfully sent" );    
$re .= "<p>Subscribed $to to mailinglist $ml</p>";
    } else {
      log( 'info', "Failed to send subscription mail" );
    }
  }
  print $re;
}




sub httpTestInput()
{
  my $self = shift;
  
  # Set debug in the config file to enable
  unless( $Hermes::Config::Debug ) {
    log( 'info', "httpTest disabled, enable Debug switch in Hermes config!" );
    return;
  }
  
  my $q = $self->query();
  my $type = $q->param('type') || "unknown type";
  log( 'info', "Incoming httptesttype: $type" );
  
  my $timeStr = localtime;


  my @httpTestQueue;
  if( -r "/tmp/httpTestQueue.txt" && open FILE, "</tmp/httpTestQueue.txt" ) {
    @httpTestQueue = <FILE>;
    close FILE;
  }
  my $cnt = unshift( @httpTestQueue, "$type\t$timeStr\n" );
  pop @httpTestQueue if( $cnt > 100 );


  if( open FILE, ">/tmp/httpTestQueue.txt" ) {
    print FILE @httpTestQueue;
    close FILE;
  } else {
    log( 'info', "Could not write log file: $!") ;
  }
}


sub httpTest()
{
  my $self = shift;


  # Get CGI query object
  my $q = $self->query();
  my $tmpl = $self->load_tmpl( 'httptest.tmpl',
      die_on_bad_params => 0,
      cache => 1 );
  $tmpl->param( urlbase => "subbotin.suse.de/hermes" );
  
  initFrame( "Hermes HTTP Delivery Test Page" );


  if( $Hermes::Config::Debug ) {
    my @httpTestQueue;
    if( -r "/tmp/httpTestQueue.txt" && open FILE, "</tmp/httpTestQueue.txt" ) {
      @httpTestQueue = <FILE>;
      close FILE;
    }
    my $cnt = @httpTestQueue;
    log( 'info', "http Test Queue: $cnt" );


    my @hashList;
    foreach my $testLine ( @httpTestQueue ) {
      my ($type, $timeStr) = split( /\t/, $testLine );
      push @hashList, { type => $type, time => $timeStr };
    }
    $tmpl->param( TestQueueLoop => \@hashList );
  } else {
    log( 'info', "No debugging because debug switch off" );
    $tmpl->param( ErrorMsg => "Please enable debug to start the debug output" );
  }
  $htmlTmpl->param( Content => $tmpl->output );


  return $htmlTmpl->output;
}


sub editType()
{
  my $self = shift;


  unless( $isAdmin{ $user } ) {
    return showError( 'Permission denied!', 
      "Sorry, but you don't have proper permissions to edit document types" );
  }
  
  # Get CGI query object
  my $q = $self->query();
  my $type = $q->param( 'type' );


  my $tmpl = $self->load_tmpl( 'edittype.tmpl',
      die_on_bad_params => 0,
      cache => 1 );
  my ($list, $firstType) = templateTypeList( $type );
  $type = $firstType unless( $type );


  my $status;
  my $previewTmpl;
  my $detailsRef = notificationDetails( $type );
  my $tmplFile = templateFileName( $detailsRef->{_type} );


  if ( $q->param( 'tmplEdit' ) && $isAdmin{$user} ) {
    log( 'debug', "template was edited!" );
    $previewTmpl = $q->param('tmplEdit');


    log( 'debug', "Dosave: " . ($q->param('dosave' ) || "false") );


    if ( $q->param( 'dosave' ) && $q->param('dosave') eq ' Save ' ) {
      log('debug', "SAVING the template" );
      # save the template
      if ( ! -e $tmplFile || -w $tmplFile ) {
if ( open F, ">$tmplFile" ) {
 print F $previewTmpl;
 close F;
 $previewTmpl = undef;
 $status = "Template saved!";
} else {
 $status = "ERROR: open failed for file $tmplFile!";
}
      } else {
$status = "ERROR: No write access on $tmplFile";
      }
    } else {
      $status = "Preview Rendering";
    }
  }


  my $admin = $isAdmin{$user} || 0;
  log( 'debug', "User $user is admin: $admin" );
  $tmpl->param( user  => $user || "unknown" );
  $tmpl->param( isAdmin => $admin );
  $tmpl->param( type  => $detailsRef->{_type} );
  $tmpl->param( NotiTypeDesc => $detailsRef->{_description} || "not yet defined" );
  $tmpl->param( NotiTypeId => $detailsRef->{_id} );
  $tmpl->param( added => $detailsRef->{_added} );
  $tmpl->param( delay => $detailsRef->{_defaultdelay} );


  my @names;


  foreach my $paraRef( @{$detailsRef->{_parameterList}} ) {
    log( 'debug', "Parameter : $paraRef->{name}" );
    my $name = $paraRef->{name};
    next if( $name eq "rm" || $name eq "_type" );


    push @names, { 'name'   => $name,
  'hrName' => $paraRef->{_hrName} || "undefined",
  'value'  => $detailsRef->{$name},
  'desc'   => $paraRef->{_desc} || "still empty",
  'nameSpanId' => "pd_name_$name",
  'descSpanId' => "pd_desc_$name",
  'descEditJs' => parameterInplaceEdit( $name, 'desc', $detailsRef->{_id} ),
  'nameEditJs' => parameterInplaceEdit( $name, 'name', $detailsRef->{_id}, 15 ),
  'isAdmin' => $admin
};
  }
  $tmpl->param( parameters => \@names );


  $tmpl->param( testrender => testRender( $detailsRef, $tmplFile, $previewTmpl ) );


  if ( $previewTmpl ) {
    $tmpl->param( templateFile => "from editfield, not yet SAVED!" );
    $tmpl->param( template => $previewTmpl );
  } else {
    $tmpl->param( templateFile => $tmplFile );


    if ( -r $tmplFile ) {
      if ( open F, "$tmplFile" ) {
my @t = <F>;
$tmpl->param( template => join("", @t ) );
close F;
      }
    }
  }
  $tmpl->param( status => $status );
  initFrame( "Hermes Notification Type <i>$type</i>" );


  $htmlTmpl->param( NotiTypeLinks => $list );


  $htmlTmpl->param( Content => $tmpl->output );


  return $htmlTmpl->output;
}


#
# Create a in place editor for both the human readable name and the description
# of notification parameters
# Paramter:
# 1. The name of the parameter
# 2. The destinquischer between name and desc, string 
# 3. The notification type id
# 4. The length of the edit line (optional)
#
sub parameterInplaceEdit( $$$;$ ) 
{
  my ($name, $var, $id, $cols) = @_;
  my $columns = $cols || 40;


  my $domId = "pd_" . $var . "_" . $name;


  my $re = "new Ajax.InPlaceEditor( \'$domId\', \'index.cgi\', \{ cols: $columns, rows: 1,";
  $re .= " callback: function(form, value) { ";
  $re .= "return 'rm=ajaxupdate&paraname=$name&id=$id&value='+escape(value) } } ) ";


  return $re;
}


# this sub returns a test rendering of a freshly edited template
sub testRender( $$;$ )
{
  my ($noti, $tmplFile, $previewTmpl ) = @_;


  return unless $noti;


  log('debug', "The template file: <$tmplFile>" );
  my $tmpl;


  if( $previewTmpl ) {
    $tmpl = HTML::Template->new( scalarref => \$previewTmpl,
die_on_bad_params => 0 );
  } elsif( -r "$tmplFile" ) {
    $tmpl = HTML::Template->new(filename => "$tmplFile",
die_on_bad_params => 0,
cache => 1 );
  }


  if( $tmpl ) {
    my @params = @{$noti->{_parameterList}};


    my %paramHash;
    foreach my $param ( @params ) {
      my $paraName = $param->{name};
      if( $paraName ) {
log('debug', "Adding parameter: <$paraName> = <" . $noti->{$paraName} . ">" );
$paramHash{ $paraName } = $noti->{$paraName};
      }
    }
    $tmpl->param( \%paramHash );


    return $tmpl->output;
  }
  return "no template available!";
}


sub ajaxUpdate
{
  my $self = shift;


  # Get CGI query object
  my $q = $self->query();
  my $value = $q->param( 'value' );
  my $editId  = $q->param( 'editorId' );
  my $id = $q->param( 'id' ) || "go away";


  log('debug', "Ajax-Update: $editId = $id" );
  if( $editId eq "noti_type_desc" && $id =~ /^\d+$/ ) {
    my $sql = "UPDATE msg_types SET description=? WHERE id=?";


    my $sth = dbh()->prepare( $sql );
    $sth->execute( $value, $id );
  } elsif( $editId =~ /pd_desc_(.+)$/ ) {
    # editing parameter description
    my $paraId = parameterId( $1 );
    if( $paraId && $id ) {
      my $sql = "UPDATE msg_types_parameters SET description=? WHERE msg_type_id=? AND parameter_id=?";
      my $sth = dbh()->prepare( $sql );
      $sth->execute( $value, $id, $paraId );
    }
  } elsif( $editId =~ /pd_name_(.+)$/ ) {
    # editing parameter human readable name
    my $paraId = parameterId( $1 );
    if( $paraId && $id ) {
      my $sql = "UPDATE parameters SET hr_name=? WHERE id=?";
      my $sth = dbh()->prepare( $sql );
      $sth->execute( $value, $paraId );
    }
  }


  return "$value";
}


sub templateTypeList(;$)
{
  my ($selected) = @_;


  my @types;
  my $sql = "SELECT msgtype FROM msg_types ORDER by msgtype";
  my $typesRef = dbh()->selectcol_arrayref( $sql );


  my $res = ""; #  = "<p>Message Types:</p><ul>\n";
  unless( $selected ) {
    $res = "<option selected value=\"index.cgi\"> -- Pick a Notification Type --</option>";
  }
  my $firstType = @$typesRef[0];


  foreach my $t ( @$typesRef ) {
    my $dipType = $t;


    my $oneEntry = "<option";
    
    $oneEntry .= " selected" if( $selected && $selected eq $dipType );
    $oneEntry .= " value=\"index.cgi?rm=type&type=$t\">$dipType</option>";


    $res .= $oneEntry;
  }
  return ($res, $firstType) ;
}


sub showError( $$ )
{
  my ($header, $content) = @_;


  initFrame( "<i>$header</i>" );
  
  my ($list, $firstType) = templateTypeList( '' );


  $htmlTmpl->param( NotiTypeLinks => $list );


  $htmlTmpl->param( Content => '<div id="error">' . $content . '</div>' );


  return $htmlTmpl->output;
}




setLogFileName('herminator');


1;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值