Beispiel #1
0
function get_auth_data()
{
    $fields = array("id", "type", "host", "username", "password", "proxy", "token_type", "project", "public_key", "private_key", "certificate");
    $user = $_SESSION['user'];
    $password = $_SESSION['password'];
    // esto por si usamos la autorizacion del servidor web
    //$user = $_SERVER['PHP_AUTH_USER']
    //$password = $_SERVER['PHP_AUTH_PW']
    // Same values as defined in IM REST API
    // Combination of chars used to separate the lines in the AUTH header
    $AUTH_LINE_SEPARATOR = '\\n';
    // Combination of chars used to separate the lines inside the auth data (i.e. in a certificate)
    $AUTH_NEW_LINE_SEPARATOR = '\\\\n';
    $auth = NULL;
    $creds = get_credentials($user, $password);
    if (!is_null($creds)) {
        $auth = "";
        foreach ($creds as $cred) {
            if ($cred['enabled']) {
                foreach ($fields as $field) {
                    if (!is_null($cred[$field]) && strlen(trim($cred[$field])) > 0) {
                        $value = str_replace("\n", $AUTH_NEW_LINE_SEPARATOR, $cred[$field]);
                        if ($field == "certificate") {
                            $auth = $auth . "password = "******"; ";
                        } else {
                            $auth = $auth . $field . " = " . $value . "; ";
                        }
                    }
                }
                $auth = substr($auth, 0, strlen($auth) - 2) . $AUTH_LINE_SEPARATOR;
            }
        }
    }
    return $auth;
}
Beispiel #2
0
function check_user($user, $pass)
{
    global $emergency_password, $admin_dsn;
    if (!isset($admin_dsn)) {
        if (defined('DSN')) {
            $admin_dsn = DSN;
        } else {
            die('No DSN nor admin_dsn, check config.php');
        }
    }
    $alm_user = new alm_userTable();
    $pass = md5($alm_user->escape($pass));
    $alm_user->readEnv();
    $alm_user_data = @$alm_user->readDataFilter("idalm_user='******' AND password='******'");
    if (almdata::basicError($alm_user->data, $admin_dsn) && $pass === $emergency_password) {
        $_SESSION['idalm_user'] = '******';
        $_SESSION['alm_user'] = '******';
        return true;
    } elseif (is_array($alm_user_data)) {
        //Cargar Credenciales
        $_SESSION['credentials'] = get_credentials($alm_user_data[0]['idalm_user']);
        $_SESSION['idalm_role'] = $alm_user_data[0]['idalm_role'];
        $_SESSION['idalm_user'] = $alm_user_data[0]['idalm_user'];
        $_SESSION['alm_user'] = $alm_user_data[0]['alm_user'];
        return true;
    } else {
        return false;
    }
}
function bootstrap_new_user()
{
    global $base_url;
    $client = get_google_api_client();
    $client->setAccessToken(get_credentials($_SESSION['userid']));
    // A glass service for interacting with the Mirror API
    $mirror_service = new Google_MirrorService($client);
    $timeline_item = new Google_TimelineItem();
    $timeline_item->setText("Welcome to the Mirror API PHP Quick Start");
    insert_timeline_item($mirror_service, $timeline_item, null, null);
    insert_contact($mirror_service, "php-quick-start", "PHP Quick Start", $base_url . "/static/images/chipotle-tube-640x360.jpg");
    subscribe_to_notifications($mirror_service, "timeline", $_SESSION['userid'], $base_url . "/notify.php");
}
Beispiel #4
0
function get_auth_data()
{
    $user = $_SESSION['user'];
    $password = $_SESSION['password'];
    // esto por si usamos la autorizacion del servidor web
    //$user = $_SERVER['PHP_AUTH_USER']
    //$password = $_SERVER['PHP_AUTH_PW']
    $auth = NULL;
    $creds = get_credentials($user, $password);
    if (!is_null($creds)) {
        $auth = array();
        foreach ($creds as $cred) {
            if ($cred['enabled']) {
                $auth_cloud = array();
                $auth_cloud['type'] = new xmlrpcval($cred['type']);
                if (!is_null($cred['id']) && strlen(trim($cred['id'])) > 0) {
                    $auth_cloud['id'] = new xmlrpcval($cred['id']);
                }
                if (!is_null($cred['host']) && strlen(trim($cred['host'])) > 0) {
                    $auth_cloud['host'] = new xmlrpcval($cred['host']);
                }
                if (!is_null($cred['username']) && strlen(trim($cred['username'])) > 0) {
                    $auth_cloud['username'] = new xmlrpcval($cred['username']);
                }
                if (!is_null($cred['password']) && strlen(trim($cred['password'])) > 0) {
                    $auth_cloud['password'] = new xmlrpcval($cred['password']);
                }
                if (!is_null($cred['proxy']) && strlen(trim($cred['proxy'])) > 0) {
                    $auth_cloud['proxy'] = new xmlrpcval($cred['proxy']);
                }
                if (!is_null($cred['token_type']) && strlen(trim($cred['token_type'])) > 0) {
                    $auth_cloud['token_type'] = new xmlrpcval($cred['token_type']);
                }
                if (!is_null($cred['project']) && strlen(trim($cred['project'])) > 0) {
                    $auth_cloud['project'] = new xmlrpcval($cred['project']);
                }
                if (!is_null($cred['public_key']) && strlen(trim($cred['public_key'])) > 0) {
                    $auth_cloud['public_key'] = new xmlrpcval($cred['public_key']);
                }
                if (!is_null($cred['private_key']) && strlen(trim($cred['private_key'])) > 0) {
                    $auth_cloud['private_key'] = new xmlrpcval($cred['private_key']);
                }
                if (!is_null($cred['certificate']) && strlen(trim($cred['certificate'])) > 0) {
                    $auth_cloud['password'] = new xmlrpcval($cred['certificate']);
                }
                $auth[] = new xmlrpcval($auth_cloud, "struct");
            }
        }
    }
    return new xmlrpcval($auth, "array");
}
require_once 'config.php';
require_once 'mirror-client.php';
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_Oauth2Service.php';
require_once 'util.php';
$client = get_google_api_client();
if (isset($_GET['code'])) {
    // Handle step 2 of the OAuth 2.0 dance - code exchange
    $client->authenticate();
    $access_token = $client->getAccessToken();
    // Use the identity service to get their ID
    $identity_client = get_google_api_client();
    $identity_client->setAccessToken($access_token);
    $identity_service = new Google_Oauth2Service($identity_client);
    $user = $identity_service->userinfo->get();
    $user_id = $user->getId();
    // Store their credentials and register their ID with their session
    $_SESSION['userid'] = $user_id;
    store_credentials($user_id, $client->getAccessToken());
    // Bootstrap the new user by inserting a welcome message, a contact,
    // and subscribing them to timeline notifications
    bootstrap_new_user();
    // redirect back to the base url
    header('Location: ' . $base_url);
} elseif (!isset($_SESSION['userid']) || get_credentials($_SESSION['userid']) == null) {
    // Handle step 1 of the OAuth 2.0 dance - redirect to Google
    header('Location: ' . $client->createAuthUrl());
} else {
    // We're authenticated, redirect back to base_url
    header('Location: ' . $base_url);
}
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//  Author: Jenny Murphy - http://google.com/+JennyMurphy
// Verify that the parameters we want are there
if (!isset($_GET['timeline_item_id']) || !isset($_GET['attachment_id'])) {
    http_response_code(400);
    exit;
}
require_once 'config.php';
require_once 'mirror-client.php';
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_MirrorService.php';
require_once 'util.php';
$client = get_google_api_client();
// Authenticate if we're not already
if (!isset($_SESSION['userid']) || get_credentials($_SESSION['userid']) == null) {
    header('Location: ' . $base_url . '/oauth2callback.php');
    exit;
} else {
    $client->setAccessToken(get_credentials($_SESSION['userid']));
}
// A glass service for interacting with the Mirror API
$mirror_service = new Google_MirrorService($client);
// fetch the metadata
$attachment = $mirror_service->timeline_attachments->get($_GET['timeline_item_id'], $_GET['attachment_id']);
// set the content type header
header('Content-type: ' . $attachment->getContentType());
// echo the bytes
echo download_attachment($_GET['timeline_item_id'], $attachment);
        }
    }
}
// In the child process (hopefully). Do the processing.
require_once 'config.php';
require_once 'mirror-client.php';
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_MirrorService.php';
require_once 'util.php';
// Parse the request body
$request_bytes = @file_get_contents('php://input');
$request = json_decode($request_bytes, true);
// A notification has come in. If there's an attached photo, bounce it back
// to the user
$user_id = $request['userToken'];
$access_token = get_credentials($user_id);
$client = get_google_api_client();
$client->setAccessToken($access_token);
// A glass service for interacting with the Mirror API
$mirror_service = new Google_MirrorService($client);
switch ($request['collection']) {
    case 'timeline':
        // Verify that it's a share
        foreach ($request['userActions'] as $i => $user_action) {
            if ($user_action['type'] == 'SHARE') {
                $timeline_item_id = $request['itemId'];
                $timeline_item = $mirror_service->timeline->get($timeline_item_id);
                // Patch the item. Notice that since we retrieved the entire item above
                // in order to access the caption, we could have just changed the text
                // in place and used the update method, but we wanted to illustrate the
                // patch method here.
Beispiel #8
0
 private function get_attachments($array_to_send)
 {
     //1er LOOP -> Crea requests (y prepara directorios)
     //2do LOOP -> Graba los archivos y llena el array de return $array_to_send
     //if($this->input->is_cli_request()){
     ini_set('memory_limit', $this->config->item('memory_limit'));
     $server_url = $this->config->item('tableau_server_url');
     $admin_user = $this->config->item('tableau_admin_user');
     $admin_password = $this->config->item('tableau_admin_pass');
     echo '<pre>' . date("Y-m-d H:i:s") . ' Preparando requests de attachments...</pre>' . PHP_EOL;
     foreach ($array_to_send as $index => $schedule) {
         $query = $this->schedule->get_schedules($schedule);
         $schedule = $query->row();
         $schedule_id = $schedule->id;
         $view_name = $schedule->view_name;
         $site_url = $schedule->site_url;
         $view_url = $schedule->view_url;
         $format = $schedule->format;
         $parametersArray = json_decode($schedule->parameters);
         $workbook_id = $schedule->workbook_id;
         $workbook_url = $schedule->workbook_url;
         $site_id = $schedule->site_id;
         $user_id = $schedule->user_id;
         $parametersString = '';
         foreach ($parametersArray as $value) {
             $parametersString = $parametersString . '?' . $value[0] . '=' . $value[2];
         }
         // LOG: Set status = getting attachments
         $this->schedule->update_schedule_process_status($schedule_id, 1);
         if ($this->config->item('log_attachments_verbose') == true) {
             echo date("Y-m-d H:i:s") . ' Obteniendo credenciales para schedule # ' . $schedule_id . PHP_EOL;
         }
         //pido credenciales para impersonar, con el 5to parametro
         $credentials = get_credentials($server_url, $site_url, $admin_user, $admin_password, $user_id);
         $admin_token = $credentials['token'];
         $this->delete_previous_schedules($schedule_id);
         $this->make_dir($schedule_id);
         list($workbook, $no_sirve, $view) = explode("/", $view_url);
         $url = $server_url . '/t/' . $site_url . '/views' . '/' . $workbook . '/' . $view . '.' . strtolower($format) . $parametersString;
         $filename = $this->get_dir($schedule_id) . '\\' . $view_name . '.' . $format;
         switch ($format) {
             case "PDF":
             case "PNG":
                 $url = $server_url . '/t/' . $site_url . '/views' . '/' . $workbook . '/' . $view . '.' . strtolower($format) . $parametersString;
                 break;
             case "TWBX":
                 if ($site_url != '') {
                     $url = $server_url . '/t/' . strtoupper($site_url) . '/workbooks' . '/' . rawurlencode($workbook_url) . '.' . strtolower($format) . $parametersString;
                 } else {
                     $url = $server_url . '/workbooks' . '/' . rawurlencode($workbook_url) . '.' . strtolower($format) . $parametersString;
                 }
                 break;
         }
         //Chequeo el tamaño del attachment contra el limite en un request solo de header
         //si es mas grande, lo descarto
         //Como Tableau no permite HEAD requests, se tuvo que hacer una progress_function que frene al 1er kb bajado (con un request comun)
         //para poder tomar el Content-Length (para chequear el tamaño del attachment)
         $chHeader = curl_init($url);
         $content_length = null;
         curl_setopt($chHeader, CURLOPT_RETURNTRANSFER, true);
         curl_setopt($chHeader, CURLOPT_USERAGENT, 'curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3');
         curl_setopt($chHeader, CURLOPT_HTTPHEADER, array("Cookie: workgroup_session_id=" . $admin_token));
         curl_setopt($chHeader, CURLOPT_NOPROGRESS, false);
         curl_setopt($chHeader, CURLOPT_BUFFERSIZE, 1);
         // buffersize no se si sirve
         curl_setopt($chHeader, CURLOPT_PROGRESSFUNCTION, function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use(&$content_length) {
             if ($downloaded > 0) {
                 $content_length = curl_getinfo($resource, CURLINFO_CONTENT_LENGTH_DOWNLOAD) / 1024 / 1024;
                 return 1;
             }
         });
         $response = curl_exec($chHeader);
         $info = curl_getinfo($chHeader);
         curl_close($chHeader);
         if ($content_length > $this->config->item('max_content_length')) {
             echo date("Y-m-d H:i:s") . ' Error: El archivo del schedule ' . "'" . $schedule_id . "'" . ' supera el limite de ' . $this->config->item('max_content_length') . ' MB. Tiene . ' . round($content_length, 2) . ' MB\'s' . PHP_EOL;
             //LOG: set status = error getting attachment
             $this->schedule->update_schedule_process_status($schedule_id, -1);
         } else {
             $this->aRequests[$schedule_id] = array('url' => $url, 'headers' => array("Cookie: workgroup_session_id=" . $admin_token), 'options' => array(CURLOPT_USERAGENT => 'curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3', CURLOPT_HEADERFUNCTION => array($this, 'header_callback')), 'filename' => $filename);
         }
     }
     // end foreach
     $rollingCurl = new \RollingCurl\RollingCurl();
     foreach ($this->aRequests as $key => $requestInfo) {
         $request = new \RollingCurl\Request($requestInfo['url'], 'GET');
         $request->setExtraInfo(array('schedule_id' => $key));
         $rollingCurl->add($request->setHeaders($requestInfo['headers']), $request->addOptions($requestInfo['options']));
     }
     $rollingCurl->setCallback(function (\RollingCurl\Request $request, \RollingCurl\RollingCurl $rollingCurl) {
         if ($this->config->item('log_memory_usage') == true) {
             echo date("Y-m-d H:i:s") . ' Memoria usada al entrar al callback ' . round(memory_get_usage() / 1024, 2) . ' KB\'s <br>';
         }
         $info = $request->getResponseInfo();
         $extra = $request->getExtraInfo();
         $handler = $request->getHandler();
         $schedule_id = $extra['schedule_id'];
         $http_code = $info['http_code'];
         foreach ($this->aRequests as $key => $requestInfo) {
             //Todo esto es para obtener el filename que me obliga a usar la funcion header_callback
             //En el array inicial aRequests no tengo  el ID del request, pero
             //modifique la libreria de Rolling_curl para incluir en el Request el numero del handler.
             //la funcion de header_callback, agrega al aRequest registros usando el handler como index
             //entonces cuando tengo un callback con un Request, recorro el aRequests y busco matchearlo
             //con el handler que me pusheo el header_callback, cuando lo encuentro, paso toda la informacion
             //al indice de schedule_id correspondiente
             //y si el http_code es 200, grabo el archivo tambien
             if ($key == 'Resource id #' . $handler) {
                 $this->aRequests[$schedule_id]['response_info'] = $request->getResponseInfo();
                 $this->aRequests[$schedule_id]['schedule_id'] = $schedule_id;
                 if (isset($this->aRequests[$key]['filename'])) {
                     $this->aRequests[$schedule_id]['filename'] = $this->aRequests[$key]['filename'];
                 }
                 if ($http_code == 200) {
                     file_put_contents($this->aRequests[$schedule_id]['filename'], $request->getResponseText());
                     if ($this->config->item('log_attachments_verbose') == true) {
                         echo date("Y-m-d H:i:s") . ' File Saved: ' . $this->aRequests[$schedule_id]['filename'] . '. Size: ' . round(filesize($this->aRequests[$schedule_id]['filename']) / 1024 / 1024, 2) . ' MB\'s' . PHP_EOL;
                     }
                     unset($this->aRequests[$key]);
                     gc_collect_cycles();
                 } else {
                     //LOG: set status = error getting attachment
                     $this->schedule->update_schedule_process_status($schedule_id, -1);
                 }
             }
         }
     })->setSimultaneousLimit($this->config->item('max_parallel_requests'))->setOptions(array(CURLOPT_RETURNTRANSFER => 1, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 0, CURLOPT_TIMEOUT => 0));
     echo '<pre>' . date("Y-m-d H:i:s") . ' Bajando Attachments...</pre>' . PHP_EOL;
     $rollingCurl->execute();
     echo '<pre>' . date("Y-m-d H:i:s") . ' Finalizados todos los downloads...</pre>' . PHP_EOL;
     //} //end if cli_request
     //else {echo 'Error: Only script access';}
 }
Beispiel #9
0
 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.  If not, see <http://www.gnu.org/licenses/>.
*/
if (!isset($_SESSION)) {
    session_start();
}
include 'user.php';
if (!check_session_user()) {
    header('Location: index.php?error=Invalid User');
} else {
    $user = $_SESSION['user'];
    include_once 'cred.php';
    $creds = get_credentials($user);
    ?>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE10" >
<title>Infrastructure Manager | GRyCAP | UPV</title>
<link rel="shortcut icon" href="images/favicon.ico">
    <link href="css/style.css" rel="stylesheet" type="text/css" media="all"/>
    <link href="css/datatable.css" rel="stylesheet" type="text/css" media="all"/>
    <link rel="stylesheet" href="css/style_login2.css"> 
    <link rel="stylesheet" href="css/style_intro2.css"> 
    <link rel="stylesheet" href="css/style_menu2.css">
    <link rel="stylesheet" href="css/style_menutab.css">
    <script type="text/javascript" language="javascript" src="js/jquery.js"></script>
    <script type="text/javascript" language="javascript" src="js/jquery.dataTables.min.js"></script>
Beispiel #10
0
 protected function setUp()
 {
     $this->bitly = new \Phitly\Client(get_credentials()->login, get_credentials()->apiKey);
 }
Beispiel #11
0
 private function get_attachment($schedule_id, $format)
 {
     if ($this->input->is_cli_request()) {
         $query = $this->schedule->get_schedules($schedule_id);
         $schedule = $query->row();
         $schedule_id = $schedule->id;
         $server_url = $this->config->item('tableau_server_url');
         $site_url = $schedule->site_url;
         $view_url = $schedule->view_url;
         $workbook_id = $schedule->workbook_id;
         $site_id = $schedule->site_id;
         $user_id = $schedule->user_id;
         $admin_user = $this->config->item('tableau_admin_user');
         $admin_password = $this->config->item('tableau_admin_pass');
         //pido credenciales para impersonar, con el 5to parametro
         $credentials = get_credentials($server_url, $site_url, $admin_user, $admin_password, $user_id);
         $admin_token = $credentials['token'];
         $content_type = $credentials['content_type'];
         list($workbook, $no_sirve, $view) = explode("/", $view_url);
         $url = $server_url . '/t/' . $site_url . '/views' . '/' . $workbook . '/' . $view . '.' . strtolower($format);
         $filename = $schedule->view_name . '.' . strtolower($format);
         switch ($format) {
             case "PDF":
                 $resource = curl_init();
                 curl_setopt($resource, CURLOPT_URL, $url);
                 curl_setopt($resource, CURLOPT_HEADER, 1);
                 curl_setopt($resource, CURLOPT_HTTPHEADER, array('Connection: keep-alive'));
                 curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1);
                 curl_setopt($resource, CURLOPT_BINARYTRANSFER, 1);
                 curl_setopt($resource, CURLOPT_COOKIE, 'workgroup_session_id=' . $admin_token);
                 $file = curl_exec($resource);
                 curl_close($resource);
                 break;
             case "PNG":
                 $resource = curl_init();
                 curl_setopt($resource, CURLOPT_URL, $url);
                 curl_setopt($resource, CURLOPT_HEADER, 0);
                 curl_setopt($resource, CURLOPT_ENCODING, "");
                 curl_setopt($resource, CURLOPT_HTTPHEADER, array('Connection: keep-alive'));
                 curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1);
                 curl_setopt($resource, CURLOPT_COOKIE, 'workgroup_session_id=' . $admin_token);
                 $file = curl_exec($resource);
                 curl_close($resource);
                 break;
             case "TWBX":
                 //si el content type de la response es un xml, guardarlo como twb, sino dejarlo como twbx
                 $response = get_workbook($server_url, $workbook_id, $site_id, $admin_token);
                 $file = $response['result'];
                 $content_type = $response['content_type'];
                 $error = '';
                 if (strpos($content_type, 'application/xml') !== false) {
                     $xml = simplexml_load_string($response['result']);
                     $error = $xml->error['code'];
                     $error = $error->asXML();
                     $filename = $schedule->view_name . '.' . 'twb';
                 }
                 break;
         }
         $response['path'] = $this->get_dir($schedule_id) . '\\' . $filename;
         $response['error'] = $error;
         file_put_contents($response['path'], $file);
         return $response;
     }
 }
Beispiel #12
-1
 public function index()
 {
     $user = $this->session->userdata('login_check');
     if (isset($user)) {
         redirect('main');
     }
     $admin_user = $this->config->item('tableau_admin_user');
     $admin_pass = $this->config->item('tableau_admin_pass');
     $server_url = $this->config->item('tableau_server_url');
     $credentials = get_credentials($this->config->item('tableau_server_url'), '', $admin_user, $admin_pass);
     $this->form_validation->set_rules('user', 'User', 'required');
     $this->form_validation->set_rules('password', 'Password', 'required');
     $this->form_validation->set_rules('site_url', 'Site_url', 'required');
     //obtengo los sitios con credenciales de admin
     $sites_result = get_sites($server_url, $credentials['token']);
     if ($this->form_validation->run() === FALSE) {
         $data = array('sites' => $sites_result);
         $this->load->view('login_view', $data);
     } else {
         $user_id = $this->input->post('user');
         $password = $this->input->post('password');
         $site_url = $this->input->post('site_url');
         $response = get_credentials($server_url, $site_url, $user_id, $password);
         //Si hubo error de Login en el Server, redirecciono al login con un mensaje Flash
         if ($response['error_msg'] != '') {
             $this->session->set_flashdata('msg', (string) $response['error_msg']);
             redirect('login');
         }
         $this->token = $response['token'];
         $this->site_id = $response['site_id'];
         $this->user_id = $response['user_id'];
         $this->user_name = $user_id;
         $this->site_url = $site_url;
         //obtengo el nombre del Sitio para mostrar despues en Main
         foreach ($sites_result->sites->site as $key => $value) {
             if ((string) $this->site_id == (string) $value['id']) {
                 $this->site_name = (string) $value['name'];
             }
         }
         $this->session->set_userdata('site_url', $site_url);
         $this->session->set_userdata('login_check', $user_id);
         //Guardo las variables en cookies
         $this->set_cookie();
         redirect('/main');
     }
 }