RT# 83450 - fixed rateplan export
[freeside.git] / FS / FS / part_export / acct_google.pm
index 748ea57..d153728 100644 (file)
@@ -3,6 +3,7 @@ package FS::part_export::acct_google;
 use strict;
 use vars qw(%info %SIG $CACHE);
 use Tie::IxHash;
+
 use base 'FS::part_export';
 
 tie my %options, 'Tie::IxHash',
@@ -15,10 +16,12 @@ tie my %options, 'Tie::IxHash',
 # admin logins.
 
 %info = (
-  'svc'       => 'svc_acct',
-  'desc'      => 'Google hosted mail',
-  'options'   => \%options,
-  'nodomain'  => 'Y',
+  'svc'        => 'svc_acct',
+  'desc'       => 'Google hosted mail',
+  'options'    => \%options,
+  'nodomain'   => 'Y',
+  'no_machine' => 1,
+  'default_svc_class' => 'Email',
   'notes'    => <<'END'
 Export accounts to the Google Provisioning API.  Requires 
 REST::Google::Apps::Provisioning from CPAN.
@@ -88,6 +91,26 @@ sub _export_unsuspend {
   );
 }
 
+sub auth_error {
+  my $self = shift;
+  my $google = $self->google_handle;
+  if ( $google->{'error'} ) {
+    my $url = $google->{'captcha_url'} || '';
+    $url = "http://www.google.com/accounts/$url" if $url;
+    return { 'captcha_url' => $url,
+             'message'     => 
+               'Unable to connect to the Google API: '.$google->{'error'}.'.',
+           };
+  }
+  return; #nothing on success
+}
+
+sub captcha_auth {
+  my $self = shift;
+  my $response = shift;
+  my $google = $self->google_handle('captcha_response' => $response);
+  return (defined($google->{'token'}));
+}
 
 my %google_error = (
   1000 => 'unknown error',
@@ -113,7 +136,9 @@ my %google_error = (
 
 sub google_request {
   my ($self, $method, %opt) = @_;
-  my $google = $self->google_handle;
+  my $google = $self->google_handle(
+    'captcha_response' => delete $opt{'captcha_response'}
+  );
   return $google->{'error'} if $google->{'error'};
 
   # Throw away the result from this; we don't use it yet.
@@ -132,15 +157,23 @@ sub google_request {
 
 sub google_handle {
   my $self = shift;
-  my $class = 'REST::Google::Apps::Provisioning';
-  eval "use $class";
-
-  die "failed to load $class\n" if $@;
+  my %opt = @_;
+  my @class = ( 
+    'REST::Google::Apps::Provisioning',
+    'Cache::FileCache',
+    'LWP::UserAgent 5.815',
+  );
+  foreach (@class) {
+    eval "use $_";
+    die "failed to load $_\n" if $@;
+  }
   $CACHE ||= new Cache::FileCache( {
       'namespace'   => __PACKAGE__,
       'cache_root'  => "$FS::UID::cache_dir/cache.$FS::UID::datasrc",
   } );
-  my $google = $class->new( 'domain'  => $self->option('domain') );
+  my $google = REST::Google::Apps::Provisioning->new(
+    'domain'  => $self->option('domain') 
+  );
 
   # REST::Google::Apps::Provisioning lacks error reporting.  We deal 
   # with that by hooking HTTP::Response to throw a useful fatal error 
@@ -167,24 +200,71 @@ sub google_handle {
     }
   );
 
-  my $cache_id = $self->exportnum . '_token';
-  $google->{'token'} = $CACHE->get($cache_id);
+  my $cache_token = $self->exportnum . '_token';
+  my $cache_captcha = $self->exportnum . '_captcha_token';
+  $google->{'token'} = $CACHE->get($cache_token);
   if ( !$google->{'token'} ) {
-    eval { 
-      $google->authenticate(
-        'username'  => $self->option('username'),
-        'password'  => $self->option('password'),
-      ) 
-    };
+    my %login = (
+      'username' => $self->option('username'),
+      'password' => $self->option('password'),
+    );
+    if ( $opt{'captcha_response'} ) {
+      $login{'logincaptcha'} = $opt{'captcha_response'};
+      $login{'logintoken'} = $CACHE->get($cache_captcha);
+    }
+    eval { $google->captcha_auth(%login); };
     if ( $@ ) {
-      # XXX CAPTCHA
       $google->{'error'} = $@->{'error'};
-      $CACHE->remove($cache_id);
+      $google->{'captcha_url'} = $@->{'captchaurl'};
+      $CACHE->set($cache_captcha, $@->{'captchatoken'}, '1 minute');
       return $google;
     }
-    $CACHE->set($cache_id, $google->{'token'}, '1 hour');
+    $CACHE->remove($cache_captcha);
+    $CACHE->set($cache_token, $google->{'token'}, '1 hour');
   }
   return $google;
 }
 
+# REST::Google::Apps::Provisioning also lacks a way to do this
+sub REST::Google::Apps::Provisioning::captcha_auth {
+  my $self = shift;
+
+  return( 1 ) if $self->{'token'};
+
+  my ( $arg );
+  %{$arg} = @_;
+
+  map { $arg->{lc($_)} = $arg->{$_} } keys %{$arg};
+
+  foreach my $param ( qw/ username password / ) {
+    $arg->{$param} || croak( "Missing required '$param' argument" );
+  }
+
+  my @postargs = (
+    'accountType' => 'HOSTED',
+    'service'     => 'apps',
+    'Email'       => $arg->{'username'} . '@' . $self->{'domain'},
+    'Passwd'      => $arg->{'password'},
+  );
+  if ( $arg->{'logincaptcha'} ) {
+    push @postargs, 
+      'logintoken'  => $arg->{'logintoken'},
+      'logincaptcha'=> $arg->{'logincaptcha'}
+      ;
+  }
+  my $response = $self->{'lwp'}->post(
+    'https://www.google.com/accounts/ClientLogin',
+    \@postargs
+  );
+
+  $response->is_success() || return( 0 );
+
+  foreach ( split( /\n/, $response->content() ) ) {
+    $self->{'token'} = $1 if /^Auth=(.+)$/;
+    last if $self->{'token'};
+  }
+
+  return( 1 ) if $self->{'token'} || return( 0 );
+}
+
 1;