<?php

/**
 * Implementation of hook_menu().
 *
 * @return
 *   The menu items to create.
 */
function dropbox_menu() {
  // This is where authorized users can send a file.
  $items['user/%user/dropbox'] = array(
    'title' => 'Send file',
    'type' => MENU_LOCAL_TASK,
    'access callback' => '_dropbox_send_enabled',
    'access arguments' => array(1),
    'page callback' => '_dropbox_send_form',
    'page arguments' => array(1),
  );
  return $items;
}

/**
 * Implementation of hook_boot().
 *
 * We need to disable the cache so that when anonymous users are blocked via
 * flood control they don't get the old form.
 */
function dropbox_boot() {
  if (!isset($_REQUEST['q'])) {
    return;
  }
  $arg = explode('/', $_REQUEST['q']);
  if ($arg[0] == 'user' && isset($arg[1]) && is_numeric($arg[1]) && isset($arg[2]) && $arg[2] == 'dropbox') {
    $GLOBALS['conf']['cache'] = FALSE;
  }
}

/**
 * Implementation of hook_permission().
 *
 * The following permissions are currently defined:
 * - Configure Dropbox Account allows users to access Dropbox functionality
 *   for their account.
 * - Send files to users via Dropbox allows users to send files to other user's
 *   Dropbox accounts.
 *
 * @return
 *   Array of permissions available.
 */
function dropbox_permission() {
  return array(
    'Configure Dropbox account' => array(
      'title' => t('Configure Dropbox account'),
      'description' => t('Enable Dropbox integration for their user account.'),
    ),
    'Send files to users via Dropbox' => array(
      'title' => t('Send files to user via Dropbox'),
      'description' => t('Send files to users who have configured their Dropbox account.'),
    ),
  );
}

/**
 * Implementation of hook_user_load().
 */
function dropbox_user_load($users) {
  foreach ($users as $account) {
    $dropbox_data = db_query("SELECT mail, pass, flood, roles FROM {dropbox_user} WHERE uid = :uid", array(':uid' => $account->uid), array('fetch' => PDO::FETCH_OBJ));
    if (count($dropbox_data) > 0) {
      foreach($dropbox_data as $data) {
        $data->roles = unserialize($data->roles);
        $account->dropbox = $data;
      }
    }
    else {
      unset($account->dropbox);
    }
  }
}

/**
 * Implementation of hook_user_insert().
 */
function dropbox_user_insert(&$edit, $account, $category) {
  if (!empty($edit['dropbox']['mail'])) {
    $roles = empty($edit['dropbox']['roles']) ? array() : $edit['dropbox']['roles'];

    $query = db_insert('dropbox_user')
      ->fields(array(
        'uid' => $account->uid,
        'mail' => $edit['dropbox']['mail'],
        'pass' => $edit['dropbox']['pass'],
        'flood' => (int)$edit['dropbox']['flood'],
        'roles' => serialize(array_filter($roles)),
      ))
      ->execute();

    $edit['dropbox']['mail'] = NULL;
    $edit['dropbox']['pass'] = NULL;
    $edit['dropbox']['flood'] = NULL;
    $edit['dropbox']['roles'] = NULL;
    $edit['dropbox'] = NULL;
  }
}

/**
 * Implementation of hook_user_update().
 */
function dropbox_user_update(&$edit, $account, $category) {
  if (isset($edit['dropbox']['mail']) && !empty($edit['dropbox']['mail'])) {
    $roles = empty($edit['dropbox']['roles']) ? array() : $edit['dropbox']['roles'];
    $merge_fields = array(
      'mail' => $edit['dropbox']['mail'],
      'flood' => (int)$edit['dropbox']['flood'],
      'roles' => serialize(array_filter($roles)),
    );
    if (!empty($edit['dropbox']['pass'])) {
      $merge_fields['pass'] = $edit['dropbox']['pass'];
    }
    db_merge('dropbox_user')->
      key(array('uid' => $account->uid))
      ->fields($merge_fields)
      ->execute();
  }
  else {
    db_delete('dropbox_user')->
      condition('uid', $account->uid)
      ->execute();
  }

  $edit['dropbox']['mail'] = NULL;
  $edit['dropbox']['pass'] = NULL;
  $edit['dropbox']['flood'] = NULL;
  $edit['dropbox']['roles'] = NULL;
  $edit['dropbox'] = NULL;
}

/**
 * Implementation of hook_user_delete().
 */
function dropbox_user_delete($account) {
  db_delete('dropbox_user')->
    condition('uid', $account->uid)
    ->execute();
}

/**
 * Implementation of hook_form_alter().
 */
function dropbox_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'user_register_form':
      $roles = user_roles(TRUE, 'Configure Dropbox account');
      // If authenticated users are allowed to use Dropbox, allow them to set
      // it up on the registration form.
      if (array_key_exists(1, $roles)) {
        module_load_include('inc', 'dropbox', 'dropbox.pages');
        dropbox_settings_form($form, (object)$form['#user']);
      }
      break;
    case 'user_profile_form':
      if (user_access('Configure Dropbox account')) {
        module_load_include('inc', 'dropbox', 'dropbox.pages');
        dropbox_settings_form($form, (object)$form['#user']);
      }
      break;
  }
}

/**
 * Return a form containing a user's Dropbox account settings.
 *
 * @param $user
 *   The user who's dropbox settinsg are being configured.
 *
 * @return
 *   A Form API array containing the available Dropbox settings.
 */
function dropbox_settings_form(&$form, $user) {
  $form['dropbox'] = array(
    '#type' => 'fieldset',
    '#title' => t('Dropbox Account Settings'),
    '#collapsible' => TRUE,
    '#tree' => TRUE,
  );
  if (variable_get('dropbox_allow_insecure', FALSE)  || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')) {
    $form['dropbox']['mail'] = array(
      '#type' => 'textfield',
      '#title' => t('Dropbox Account Email Address'),
      '#maxlength' => 64,
      '#default_value' => isset($user->dropbox->mail) ? $user->dropbox->mail : '',
      '#description' => t('Enter the email address associated with your Dropbox account.'),
    );
    $form['dropbox']['pass'] = array(
      '#type' => 'password',
      '#title' => t('Dropbox Account Password'),
      '#maxlength' => 64,
      '#description' => t('Enter your Dropbox account password. Your password will be stored as it is required to access your account.'),
    );
  }
  else {
    $no_ssl = t('You must connect over an SSL connection to view or change your Dropbox account settings.');
    if (securepages_test()) {
      $path = !empty($_REQUEST['q']) ? $_REQUEST['q'] : '';
      $secure_version = securepages_url($path, array('secure' => TRUE));
      $no_ssl .= ' ' . t('Try <a href="@secure">connecting securely</a> to view these settings.', array('@secure' => $secure_version));
    }
    else {
      $no_ssl .= ' ' . t('This web server does not appear to support SSL. Please enable SSL to allow Dropbox integration.');
    }
    $form['dropbox']['no_ssl'] = array(
      '#prefix' => '<p>',
      '#suffix' => '</p>',
      '#markup' => $no_ssl,
    );
    return $form;
  }
  $roles = user_roles(FALSE, 'Send files to users via Dropbox');
  if (!empty($roles)) {
    $form['dropbox']['roles'] = array(
      '#type' => 'checkboxes',
      '#title' => t('Site roles which can send you files'),
      '#options' => $roles,
      '#description' => t('Check off the roles that you wish to allow to send you files. Leave all roles unchecked to only allow site administrators to send you files.'),
    );
    if (!empty($user->dropbox->roles)) {
      $form['dropbox']['roles']['#default_value'] = $user->dropbox->roles;
    }
    $form['dropbox']['flood'] = array(
      '#type' => 'textfield',
      '#title' => t('Maximum number of files an anonymous user can send per hour'),
      '#default_value' => isset($user->dropbox->flood) ? $user->dropbox->flood : '',
      '#maxlength' => 2,
      '#size' => 2,
      '#description' => t('Enter the maximum number of files an anonymous user can send per hour. This helps to limit spam and other annoyances, and only applies if you allow anonymous users to send you files. Leave blank to use the site defaults.'),
    );
  }
  else {
    $form['dropbox']['access_disabled'] = array(
      '#prefix' => '<p>',
      '#suffix' => '</p>',
      '#markup' => t('No roles are currently allowed to send files via Dropbox. No users will be able to send files until at least one role is enabled by the site administrator.'),
    );
  }
}

/**
 * Private function to set the page title on the Send file tab.
 *
 * @param $user
 *   The user who's form is being displayed.
 *
 * @return
 *   The FAPI form array.
 */
function _dropbox_send_form($user) {
  drupal_set_title(t('Send %user a file', array('%user' => $user->name)), PASS_THROUGH);
  return drupal_get_form('dropbox_send_form', $user);
}

/**
 * Forms API callback to show the "Send file" form.
 */
function dropbox_send_form($form, $form_state, $destination_user) {
  global $user;

  $form = array();
  if ($user->uid == 0 && !flood_is_allowed('dropbox_send', $destination_user->dropbox->flood)) {
    $form['access_denied'] = array(
      '#prefix' => '<p>',
      '#suffix' => '</p>',
      '#markup' => t('You have sent the maximum number of files allowed within an hour to %user. Please try again later.', array('%user' => $destination_user->name)),
    );
    return $form;
  }
  $form['#attributes'] = array('enctype' => "multipart/form-data");
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => ($user->uid == 0 ? '' : $user->name),
    '#maxlength' => 32,
    '#description' => t('%name will receive an email notification about this upload from your name.', array('%name' => $destination_user->name)),
  );
  if ($user->uid == 0) {
    $form['mail'] = array(
      '#type' => 'textfield',
      '#title' => t('Email address'),
    );
  }
  else {
    $form['mail'] = array(
      '#type' => 'value',
      '#value' => $user->mail,
    );
  }
  $form['destination_user'] = array(
    '#type' => 'value',
    '#value' => $destination_user,
  );
  $form['upload'] = array(
    '#type' => 'file',
    '#title' => t('Choose a file to send'),
    '#description' => t('Choose the file you wish to send. If you have previously sent this file to this user, the recepient will have access to both copies. You may not upload a file larger than %size.', array('%size' => format_size(file_upload_max_size())))
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Send file'),
  );
  return $form;
}

/**
 * Validate the dropbox_send_form.
 */
function dropbox_send_form_validate($form, &$form_state) {
  if (empty($form_state['values']['name'])) {
    form_set_error('name', t('You must enter a name to send a file.'));
  }
  if (!valid_email_address($form_state['values']['mail'])) {
    form_set_error('mail', t('You must enter a valid email address to send a file.'));
  }
}

/**
 * Submit the dropbox_send_form. This uploads the file to a temporary
 * directory, transfers the file to Dropbox, and sends a notification email
 * to the destination user.
 *
 * @param $form
 * @param $form_state
 * @return unknown_type
 */
function dropbox_send_form_submit($form, &$form_state) {
  global $user;
  $destination_user = $form_state['values']['destination_user'];
  $file = file_save_upload('upload', array());
  if (is_object($file)) {
    $error = dropbox_file_put($destination_user, $file, '/' . variable_get('site_name', 'Drupal') . '/' . strtr($form_state['values']['mail'], "@", "-"));
    if (!$error) {
      $params = array();
      $params['destination_user'] = $destination_user;
      $params['name'] = $form_state['values']['name'];
      $params['mail'] = strtr($form_state['values']['mail'], "@", "-");
      drupal_mail('dropbox', 'send_notify', $destination_user->name . ' <' . $destination_user->mail . '>', user_preferred_language($destination_user), $params, $form_state['values']['mail']);
      drupal_set_message(t('%filename has successfully been sent.', array('%filename' => $file->filename)));
      watchdog('dropbox', "%from sent a file to %to's Dropbox account.", array('%from' => isset($user->name) ? $user->name : $params['name'], '%to' => $destination_user->name));
      file_delete($file);
    }
    else {
      form_set_error('upload', t('Failed to upload the file. Dropbox returned the following error: !dropbox-error', array('!dropbox-error' => $error)));
    }
  }
  else {
    form_set_error('upload', t('Failed to upload the file. Please try again.'));
  }
}

/**
 * Implementation of hook_mail().
 */
function dropbox_mail($key, &$message, $params) {
  $language = $message['language']->language;
  $variables = array(
    '!site-name' => variable_get('site_name', 'Drupal'),
    '!uri' => $GLOBALS['base_url'],
    '!source-name' => $params['name'],
    '!username' => format_username($params['destination_user']),
    '!mail' => $params['mail'],
  );

  switch ($key) {
    case 'send_notify':
      $message['subject'] = t('A user has sent you a file to your Dropbox account from !site-name', $variables, array('langcode' => $language));
      $message['body'][] = t("Dear !username,\n\n!source-name has sent you a file. You can find it in your Dropbox account in the !site-name/!mail folder.\n\n--\n!uri", $variables, array('langcode' => $language));
      break;
  }

}

/**
 * Upload a file to a Dropbox account.
 *
 * @param $account
 *   The user who the file is being sent to.
 *
 * @param $file
 *   The file object of the file being sent.
 *
 * @param $path
 *   The destination path of the file being sent. Any directories will be
 *   created automatically by Dropbox. If the file all ready exists, and
 *   it has changed, a new revision will be created. If the identical file
 *   is the current revision, the file is transfered to Dropbox but it will
 *   not mark the file as changed.
 *
 * @return
 *   FALSE on success, or the message of the Exception object if an error was
 *   detected during the upload.
 */
function dropbox_file_put($account, $file, $path) {
  global $user;
  module_load_include('php', 'dropbox', 'DropboxUploader');

  $dropboxUploader = new DropboxUploader($account->dropbox->mail, $account->dropbox->pass);
  $dropboxUploader->setCaCertificateFile(drupal_get_path('module', 'dropbox') . '/Thawte_Premium_Server_CA.pem');
  try {
    $dropboxUploader->upload(file_directory_temp() . '/' . $file->filename, $path);
  }
  catch (Exception $e) {
    return $e->getMessage();
  }
  if ($user->uid == 0) {
    flood_register_event('dropbox_send');
  }
  return FALSE;
}

// TODO: None of these functions are currently possible, but should be
// implemented when Dropbox has a proper API.
function dropbox_file_get($account, $path) {
  return FALSE;
}

function dropbox_directory_list($account, $path) {
  return FALSE;
}

function dropbox_directory_create($account, $path) {
  return FALSE;
}

/**
 * Determine if a user has permission to send files to other users. This
 * requires that the user have system permissions to send files and that the
 * destination user has enabled the "send" functionality for a role that user
 * belongs to.
 *
 * @param $destination_user
 *   The user who the file is being sent to.
 *
 * @return
 *   TRUE if the user can send the file, FALSE otherwise.
 */
function _dropbox_send_enabled($destination_user) {
  global $user;
  if (!isset($destination_user->dropbox)) {
    return FALSE;
  }
  if (!isset($destination_user->dropbox->roles)) {
    return FALSE;
  }
  $roles = array_intersect(array_keys($user->roles), $destination_user->dropbox->roles);

  if (user_access('Send files to users via Dropbox', $user)
    && !empty($destination_user->dropbox->mail)
    && (!empty($roles) || $user->uid == 1)) {
    return TRUE;
  }
  return FALSE;
}

