# ==================================================================
# Plugins::Auth_Facebook - Auto Generated Program Module
#
#   Plugins::Auth_Facebook
#   Author  : Gossamer Threads Inc. (Virginia Lo)
#   Version : 1.1
#   Updated : Wed Feb  2 10:34:55 2011
#
# ==================================================================
#

package Plugins::Auth_Facebook;
# ==================================================================

use strict;
use GT::Base;
use GT::Plugins qw/STOP CONTINUE/;
use Links qw/:objects/;
use vars qw/$TAINTED $GRAPHURL $CALLBACK $AUTHURL $FBMAPPING/;
use GT::WWW;
use GT::JSON;
use Data::Dumper;
use Links::Plugins;

use open qw(:std :utf8);
use LWP::Simple;
use JSON;
use URI;
use utf8;

# Inherit from base class for debug and error methods
@Plugins::Auth_Facebook::ISA = qw(GT::Base);

# Your code begins here.
{
    local $^W = 0;
    $TAINTED = substr("$0$^X", 0, 0);
}

$GRAPHURL = "https://graph.facebook.com";
$AUTHURL  = $GRAPHURL . '/oauth/access_token';

$FBMAPPING = {
    first_name => 'prof_first_name',
    last_name  => 'prof_last_name',
    email      => 'comm_email',
};

# PLUGIN HOOKS
# ===================================================================

sub user_auth {
# -------------------------------------------------------------------
# ether facebook postback or user call it.
    #
    my $cfg = Links::Plugins::get_plugin_user_cfg('Auth_Facebook');

    $CALLBACK = $cfg->{fb_postback_url};

    my $redirect = $IN->param('url');
    my $ajax = $IN->param('ajax');

    my $cuser = $USER;
# Check to see if we have the facebook cookie already.
    $Links::fbcookie ||= get_facebook_cookie();
    my $code = $IN->param('code');
    my $connect = $IN->param('connect');
    if ($code) {
# ---------------------------------------------------------------------
# User has granted permission to the app to access their information.
# Post a request to facebook to verify the code which can be exchanged for an oauth access token
# ---------------------------------------------------------------------
# If the user authorizes your application, we redirect the user back 
# to the redirect URI you specified with a verification string in the 
# argument code, which can be exchanged for an oauth access token. 
# ---------------------------------------------------------------------

        if ($connect) {
            $CALLBACK .= "&connect=1";
        }
        my $linkid = $IN->param('linkid');
        if ($linkid) {
            $CALLBACK .= "$linkid/";
        }
        my $authurl = $AUTHURL . "?client_id=" . $cfg->{fb_appid} . "&redirect_uri=" . GT::CGI::escape($CALLBACK) .  "&client_secret=" . $cfg->{fb_secret_key} . "&code=" . $code;
        my $res = GT::WWW->post($authurl);

        #print $IN->header;
        #print "<pre>".  Dumper($res)."</pre>";

        if ($res->{content} =~ /access_token=(.+)$/) {
# we got the access token 
            my $access_token = $1;
            $access_token =~ s/\&.+$//g; # Fix it by removing the &epxire=[timestamp]
            #print $access_token . "**";

            my $id = $linkid;
            use Plugins::SocialMedia;
            my $result = Plugins::SocialMedia::post_facebook($access_token, $id);
            #my $result = {}; #Plugins::SocialMedia::post_facebook($access_token, $id);
            #print "<pre>".  Dumper($id,$result)."</pre>";

            if ($result) {
                if ($result->{id}) {
                    $DB->table('Links')->update({ facebook_published => $result->{id} }, { ID => $linkid });
                }
                elsif ($result->{error}) {
                    $DB->table('Links')->update({ facebook_published_message => $result->{error}->{message} }, { ID => $linkid });
                }
                $IN->param('t','dev'); #FIXME
                require Plugins::SocialMedia;
                my $link = Plugins::SocialMedia::format_link($linkid);
                print $IN->header;
                print Links::user_page('add_success_publish.html', { success => '1', built => 1, %$link });
                return;
            }
            else {
                $IN->param('t','dev'); #FIXME
                require Plugins::SocialMedia;
                my $link = Plugins::SocialMedia::format_link($id);
                print $IN->header;
                print Links::user_page('add_success_publish.html', { success => '1', built => 1, %$link });
                return;
            }

=tag
# get the user information
            my $userinfo = request_facebook_api('me','email', $access_token);
# check to seee if the user already in community
            my $user = user_by_facebook_id($userinfo->{id});

# ---------------------------------------------------------------------
# Now then login user with their facebook account 
# ---------------------------------------------------------------------
            if ($user) {
                my $cookies = [];
                my $remember = 1;
                my $username = $user->{comm_username};
# create community session.
                $user->{session} = comm_create_session( 
                    comm_id  => $user->{comm_id}, 
                    remember => $remember,
                    ip       => $ENV{REMOTE_ADDR} 
                );
# Get session cookies, and send user to home page or to redirection.
                my $session_cookies = Links::session_cookies($user, $remember ? $CFG->{session_remember_expiry} : undef);
                push @$cookies, @$session_cookies;

                if ($redirect) {
                    comm_debug("user |$username| logged in. Redirecting to |$redirect|") if ($CFG->{debug});
                    print $IN->header( -url => $redirect, -cookie => $cookies );
                    return;
                }
                else {
                    $user->{redirect} = $IN->cookie('fb_redirect');
                    comm_debug("user |$username| logged in.") if ($CFG->{debug});
                    $user->{session_cookie_name_user} = $user->{comm_username};
                    $user->{action} = 'login';
                    print $IN->header( -cookie => $cookies );
                    Links::user_page('user_logged_in.html', { %$user });
                    return;
                }
            }
            else {
# ---------------------------------------------------------------------
# Show signup form if they are not in Community but already authorize our app to access their account
# ---------------------------------------------------------------------
# Write the facebook cookies here so that we can access them again after the user
# provides us with username. I don't see where else this is being done.
# ---------------------------------------------------------------------
                my $cookies = [];
                my $remember = 1;
                my $facebook_cookies = facebook_cookies($userinfo, $remember ? $CFG->{session_remember_expiry} : undef);
                push @$cookies, @$facebook_cookies;

                if ($connect) {
                    if (defined $IN->cookie('fb_uid')) {
                        $userinfo->{facebook_userid} = $IN->cookie('fb_uid');
                    }
                    print $IN->header( -cookie => $cookies );
                    Links::user_page('user_connect.html', { fb_user => $userinfo, user => $cuser });
                    return;
                }
                print $IN->header( -cookie => $cookies );
                my @questions;
                foreach my $question (@{$CFG->{signup_questions}}) {
                    push @questions, { question => $question };
                }
                my $res = {
                    comm_question_loop => \@questions, fb_user => $userinfo, fb_signup => 1,
                };
                $res->{fancybox} = $IN->param('fancybox') ? 1 : 0;
                $res->{iframe} = 1;
                $res->{debuginfo} = { cookies => \@$cookies, %$userinfo };
                foreach (keys %$userinfo) {
                    $res->{querystr} .= "&" if ($res->{querystr});
                    $res->{querystr} .= $_ . "=" . $userinfo->{$_};
                }
                if ($userinfo->{birthday}) {
                    ($res->{prof_mon}, $res->{prof_day}, $res->{prof_year}) = split ('/', $userinfo->{birthday});
                    $res->{prof_month} =~ s/^0//g;
                    $res->{prof_day} =~ s/^0//g;
                }
                Links::user_page('user_signup_fb_popup.html', $res);
                return;
            }
=cut
        }
        else {
            print $IN->header;
        }
    }
    elsif ($IN->param('error')) {
        print $IN->header;
        Links::user_page('user_signup_fb_error.html', { error => $IN->param('error') });
        return;
    }
    else {
# redirect user to facebook and ask for email, offline access, read access
# display=popup mean it's a popup format.
        my $cookie = [
        $IN->cookie(
            -name => "fb_redirect",
            -value => $redirect ? $redirect : '',
            -expires => undef, 
            -path => $CFG->{session_cookie_path} 
        )
        ];
        if ($IN->param('connect')) {
            $CALLBACK .= "&connect=1";
        }
        my $linkid = $IN->param('linkid');
        if ($linkid) {
            $CALLBACK .= "$linkid/";
            my $tags = $IN->param('twitter_hash_tags');
            my $newtags;
            if ($tags) {
                $tags =~ s/\s+//g;
                my @tags = split(',',$tags);
                for (@tags) {
                    $newtags .= ' #' . $_;
                }
            }
            $DB->table('Links')->update({ facebook_hashtags => $newtags  }, { ID => $linkid }) if ($newtags);
        }
        my $redirurl = 'https://graph.facebook.com/oauth/authorize?client_id=' . $cfg->{fb_appid} . '&redirect_uri=' . GT::CGI::escape($CALLBACK) . '&scope=email,read_stream,publish_stream,manage_pages';
        if (1) { # HAS to have display=popup to show "Log in to Facebook" button
            $redirurl .= "&display=popup";
        }
        print $IN->header( -url => $redirurl, -cookie => $cookie );
        return;
    }
}

sub request_facebook_api {
# -------------------------------------------------------------------
# generate facebook api and make a request
# the request results are in json format
    #
    my ($uid, $type, $token, $publish, $args) = @_;
    if (!$token) {
        $Links::fbcookie ||= get_facebook_cookie();
    }

    $type ||= "feed";

    require GT::WWW;
    my $content;
    my $www = new GT::WWW;
# Note: apparently we need to unescape the token, not escape it.
# perhaps it's a difference between $token and access_token?
# http://drupal.org/node/905164 post #7 as of 9/29/2010
    my $host = $GRAPHURL;
    $host =~ s/^(https)?:\/\///g;
    $www->protocol($1);
    $www->host($host);
    if ($publish) {
        $www->path("/$uid/feed");
        $www->parameters(access_token => GT::CGI::unescape($token || $Links::fbcookie->{access_token}));
        $args ||= $IN->get_hash();
        #use Data::Dumper; print Dumper($args);
        for (qw/message picture name caption description link/) {
            $www->parameters($_ => GT::CGI::html_escape($args->{$_}), 1) if ($args->{$_} !~ /^\s*$/);
        }
        $content = $www->post();
    }
    else {
        my $requestpath = "/" . $uid;
        $requestpath .= "/" . $type if ($uid ne 'me');
        $www->path($requestpath);
        $www->parameters(access_token => GT::CGI::unescape($token || $Links::fbcookie->{access_token}));
        $args ||= $IN->get_hash();
        if ($uid ne 'me') {
            for (keys %$args) {
                next if ($_ eq 'arg');
                $www->parameters($_ => GT::CGI::html_escape($args->{$_}), 1) if ($args->{$_} !~ /^\s*$/);
            }
        }
        $content = $www->get();
    }

    return unless ($content);
    my $data = eval { $content };
    return unless ($data and $data->{content});
    my $res = from_json($data->{content});
    return $res;
}

sub get_facebook_cookie {
# -------------------------------------------------------------------
# return facebook cookie in hash if exists
    #
    my $cfg = Links::Plugins::get_plugin_user_cfg('Auth_Facebook');
    my ($app_id, $application_secret) = ($cfg->{fb_appid}, $cfg->{fb_secret_key});
    my $cookiename = 'fbs_' . $cfg->{fb_appid};
    my $cookies = {};

    if (defined $ENV{HTTP_COOKIE}) {
        for (split /;\s*/, $ENV{HTTP_COOKIE}) {
            /(.*)="?(.*)"?/ or next;
            my ($key, $val) = (GT::CGI::unescape($1 . $TAINTED), GT::CGI::unescape($2 . $TAINTED));
            if ($_ =~ /(.*)="([^"]+)+"/) {
                ($key, $val) = (GT::CGI::unescape($1 . $TAINTED), GT::CGI::unescape($2 . $TAINTED));
            }
            $val = [split '&', $val];
            foreach (@$val) {
                my ($k, $v) = split /=/, $_;
                $cookies->{$key}->{$k} = $v;
            }
        }
    }

    my $args = $cookies->{$cookiename};

    my $payload;
    foreach my $key (sort keys %$args) {
        if ($key ne 'sig') {
            $payload .= $key . '=' . $args->{$key};
        }
    }
    require GT::MD5;
    my $md5 = GT::MD5::md5_hex($payload . $application_secret);

    return unless ($md5 eq $args->{'sig'});
    return $args;
}

sub facebook_cookies {
# -------------------------------------------------------------------
# return facebook cookies, and put it into the cookies list 
# in case we need it later.
    #
    my ($facebookinfo, $expiry) = @_;
    $facebookinfo || return;

    my $cookies = [
    $IN->cookie(
        -name => "fb_uid",
        -value => $facebookinfo ? $facebookinfo->{id} : '',
        -expires => $expiry, 
        -path => $CFG->{session_cookie_path} 
    )
    ];
    if ($CFG->{session_cookie_domain}) {
        push @$cookies, 
        $IN->cookie(
            -name => "fb_uid",
            -value => $facebookinfo ? $facebookinfo->{id} : '',
            -expires => $expiry, 
            -path => $CFG->{session_cookie_path},
            -domain => $CFG->{session_cookie_domain}
        );
    }
    return $cookies;
}

sub user_by_facebook_id {
# -------------------------------------------------------------------
# pass in facebook profile id and return the community user 
# associated with it if exists.
    #
    my $uid = shift || return;
    my $db = $DB->table('Users');
    return;
    my $sth = $db->select( { facebook_userid => $uid });
    if ($sth->rows) {
        my $user = $sth->fetchrow_hashref;
        return $user;
    }
    return;
}

# Always end with a 1.
1;
