You are here

mailhandler.module in Mailhandler 6

Mailhandler module code.

Mailhandler module allows content submission via email: create or edit nodes and comments. Authentication is flexible allowing additional plugins to grant authorization, but a default authentication method is provided based on the "From:" field of the email, the origin address. Users may define additional submission attributes like: taxonomy terms, teasers and others, using the Commands capability.

See also

http://drupal.org/node/38943 Command documentation.

File

mailhandler.module
View source
<?php

/**
 * @file
 * Mailhandler module code.
 *
 * Mailhandler module allows content submission via email: create or edit nodes
 * and comments. Authentication is flexible allowing additional plugins to grant
 * authorization, but a default authentication method is provided based on the 
 * "From:" field of the email, the origin address. Users may define additional 
 * submission attributes like: taxonomy terms, teasers and others, using the 
 * Commands capability.
 *
 * @see http://drupal.org/node/38943 Command documentation.
 */

/**
 * @defgroup mailhandler_drupal_hooks Mailhandler Drupal hooks
 * @{
 * Drupal hook implementations used by Mailhandler.
 */

/**
 * Implementation of hook_cron().
 *
 * Retrieve and process mesages from all enabled mailboxes.
 */
function mailhandler_cron() {

  // @todo: remove this include when the retriever gets decoupled.
  // @todo: add mailhandler_watchdog_record functions.
  module_load_include('inc', 'mailhandler', 'mailhandler.retrieve');

  // Get only enabled for cron mailboxes.
  $mailboxes = mailhandler_mailbox_load_multiple(array(), array(
    'enabled' => TRUE,
  ));

  // Process mailboxes.
  foreach ($mailboxes as $mid => $mailbox) {
    mailhandler_node_process_mailbox((array) $mailbox, 'auto');
  }
}

/**
 * Implementation of hook_perm().
 */
function mailhandler_perm() {
  return array(
    'administer mailhandler',
  );
}

/**
 * Implementation of hook_menu().
 */
function mailhandler_menu() {
  $items = array();
  $items['admin/content/mailhandler'] = array(
    'title' => 'Mailhandler Mailboxes',
    'description' => 'Manage mailboxes and retrieve messages.',
    'page callback' => 'mailhandler_list_mailboxes',
    'access arguments' => array(
      'administer mailhandler',
    ),
    'file' => 'mailhandler.admin.inc',
  );
  $items['admin/content/mailhandler/list'] = array(
    'title' => 'List',
    'description' => 'Manage mailboxes and retrieve messages.',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'access arguments' => array(
      'administer mailhandler',
    ),
    'weight' => -10,
  );
  $items['admin/content/mailhandler/add'] = array(
    'title' => 'Add mailbox',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mailhandler_add_edit_mailbox',
      NULL,
    ),
    'access arguments' => array(
      'administer mailhandler',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'mailhandler.admin.inc',
  );
  $items['admin/content/mailhandler/clone/%mailhandler_mailbox'] = array(
    'title' => 'Add mailbox',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mailhandler_add_edit_mailbox',
      4,
      TRUE,
    ),
    'access arguments' => array(
      'administer mailhandler',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'mailhandler.admin.inc',
  );
  $items['admin/content/mailhandler/retrieve/%mailhandler_mailbox'] = array(
    'title' => 'Retrieve',
    'page callback' => 'mailhandler_node_process_mailbox',
    'page arguments' => array(
      4,
      'ui',
    ),
    'access arguments' => array(
      'administer mailhandler',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'mailhandler.retrieve.inc',
  );
  $items['admin/content/mailhandler/edit/%mailhandler_mailbox'] = array(
    'title' => 'Edit mailbox',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mailhandler_add_edit_mailbox',
      4,
    ),
    'access arguments' => array(
      'administer mailhandler',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'mailhandler.admin.inc',
  );
  $items['admin/content/mailhandler/delete/%mailhandler_mailbox'] = array(
    'title' => 'Delete mailbox',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mailhandler_admin_delete_confirm',
      4,
    ),
    'access arguments' => array(
      'administer mailhandler',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'mailhandler.admin.inc',
  );
  $items['admin/settings/mailhandler'] = array(
    'title' => 'Mailhandler Settings',
    'description' => t('Set the default content type for incoming messages and set the cron limit.'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mailhandler_admin_settings',
    ),
    'access arguments' => array(
      'administer mailhandler',
    ),
    'file' => 'mailhandler.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_help().
 */
function mailhandler_help($path = 'admin/help#mailhandler', $arg) {
  $output = '';

  // Gather examples of useful commands, and build a definition list with them:
  $commands[] = array(
    'command' => 'taxonomy: [term1, term2]',
    'description' => t('Use this to add the terms <em>term1</em> and <em>term2</em> to the node.<br />
                      Both of the terms should already exist. In case they do not exist already, they will be quietly ommitted'),
  );
  $commands[] = array(
    'command' => 'taxonomy[v]: [term1, term2]',
    'description' => t('Similar to the above: adds the terms <em>term1</em> and <em>term2</em> to the node, but uses the vocabulary with the vocabulary id <em>v</em>. For example <em>taxonomy[3]</em> will chose only terms from the vocabulary which id is 3.<br />
                      In case some of the terms do not exist already, the behavior will depend on whether the vocabulary is a free tagging vocabulary or not. If it is a free tagging vocabulary, the term will be added, otherwise, it will be quietly ommitted'),
  );
  $commands_list = '<dl>';
  foreach ($commands as $command) {
    $commands_list .= '<dt>' . $command['command'] . '</dt>';
    $commands_list .= '<dl>' . $command['description'] . '</dl>';
  }
  $commands_list .= '</dl>';
  switch ($path) {
    case 'admin/help#mailhandler':
      $output = '<p>' . t('The mailhandler module allows registered users to create or edit nodes and comments via e-mail. Users may post taxonomy terms, teasers, and other post attributes using the mail commands capability.  This module is useful because e-mail is the preferred method of communication by community members.') . '</p>';
      $output .= '<p>' . t('The mailhandler module requires the use of a custom mailbox.  Administrators can add mailboxes that should be customized to meet the needs of a mailing list. This mailbox will then be checked on every cron job. Administrators may also initiate a manual retrieval of messages.') . '</p>';
      $output .= '<p>' . t('This is particularly useful when you want multiple sets of default commands. For example , if you want to authenticate based on a non-standard mail header like Sender: which is useful for accepting submissions from a listserv. Authentication is usually based on the From: e-mail address.  Administrators can edit the individual mailboxes when they administer mailhandler.') . '</p>';
      $output .= t('<p>You can</p>
<ul>
<li><a href="@run-cron">run cron</a> to retrieve messages from all cron enabled mailboxes.</li>
<li>list mailboxes at <a href="@admin-mailhandler">Administer &gt;&gt; Content management &gt;&gt; Mailhandler</a>.</li>
<li>add a mailbox at <a href="@admin-mailhandler-add">Administer &gt;&gt; Content management &gt;&gt; Mailhandler &gt;&gt; Add  mailbox.</a></li>
<li>set default commands, (password, type, taxonomy, promote, status), for how to work with incoming mail at <a href="%admin-mailhandler">Administer &gt;&gt; Content management &gt;&gt; Mailhandler</a> and select <strong>edit</strong> for the email address being handled.  Set commands in the default command field.</li>
<li>post email, such as from a mailing list, to a forum by adding the term id (number found in the URL) to the default commands using <strong>tid: #</strong>.</li>
<li>alter mailhandler settings (default content type and cron threshold) at <a href="@admin-mailhandler-settings">Administer &gt;&gt; Site configuration &gt;&gt; Mailhandler</a>.</li>
</ul>', array(
        '@run-cron' => url('admin/logs/status/run-cron'),
        '@admin-mailhandler-add' => url('admin/content/mailhandler/add'),
        '@admin-mailhandler' => url('admin/content/mailhandler'),
        '@admin-mailhandler-settings' => url('admin/settings/mailhandler'),
      ));
      $output .= '<h3 id="commands">' . t('Useful Commands') . '</h3>';
      $output .= $commands_list;
      $output .= '<p>' . t('For more information please read the configuration and customization handbook <a href="@mailhandler">Mailhandler page</a>.', array(
        '@mailhandler' => url('http://www.drupal.org/handbook/modules/mailhandler', array(
          'absolute' => TRUE,
        )),
      )) . '</p>';
      return $output;
    case 'admin/content/mailhandler':
      return t('The mailhandler module allows registered users to create or edit nodes and comments via email. Authentication is usually based on the From: email address. There is also an email filter that can be used to prettify incoming email. Users may post taxonomy terms, teasers, and other node parameters using the Command capability.');
    case 'admin/content/mailhandler/add':
      return t('Add a mailbox whose mail you wish to import into Drupal. Can be IMAP, POP3, or local folder.');
    case 'admin/content/mailhandler/edit/%':
      return t('Edit the mailbox whose mail you wish to import into Drupal. Can be IMAP, POP3, or local folder.');
    case 'admin/settings/mailhandler':
      return t('The mailhandler module allows registered users to create or edit nodes and comments via e-mail.');
  }
}

/**
 * @} End of "defgroup mailhandler_drupal_hooks".
 */

/**
 * @defgroup mailhandler_mailbox_crud Mailhandler Mailbox database operations
 * @{
 * The CRUD implemented here mimics the way CRUD operations are being done in
 * most of Drupal API implementations, to make it easy to maintain.
 */

/**
 * Load a mailbox array from database.
 *
 * Return a mailbox information based on the ID passed as parameter.
 *
 * @param $mid
 *   Int mailbox ID to load
 * @param $conditions
 *   An associative array of conditions on the {mailhandler} table, where the
 *   keys are the database fields and the values are the values those fields
 *   must have.
 * @param $reset
 *   Whether to reset the internal mailhandler_mailbox_load cache. Unimplemented
 *   to make module versions easy to maintain.
 * @return
 *   Array of mailbox configuration
 */
function mailhandler_mailbox_load($mid, $conditions = array(), $reset = FALSE) {

  // Convert single mid to array for load multiple calls.
  $mids = isset($mid) ? array(
    $mid,
  ) : array();

  // Load the mailbox information.
  $mailbox = mailhandler_mailbox_load_multiple($mids, $conditions, $reset);

  // Record debug information.
  mailhandler_watchdog_record('Mailbox loaded: @mailbox', array(
    '@mailbox' => var_export($mailbox, TRUE),
  ), WATCHDOG_DEBUG);

  // return the mailbox or FALSE.
  return $mailbox ? (array) reset($mailbox) : FALSE;
}

/**
 * Load mailboxes from the database.
 *
 * This function should be used whenever you need to load more than one mailbox
 * from the database. Mailboxes are loaded into memory and will not require
 * database access if loaded again during the same page request.
 *
 * @param $mids
 *   An array of mailboxes IDs. If specified, the conditions will be applied to
 *   this group of mailboxes.
 * @param $conditions
 *   An associative array of conditions on the {mailhandler} table, where the
 *   keys are the database fields and the values are the values those fields
 *   must have. This filter will return only mailboxes matching all the values.
 * @param $reset
 *   Whether to reset the internal mailhandler_mailbox_load cache.
 * @return
 *   An array of mailboxes indexed by mid
 */
function mailhandler_mailbox_load_multiple($mids = array(), $conditions = array(), $reset = FALSE) {
  $mailboxes = array();

  // Use these variables to compose the query based on $conditions array.
  $schema = drupal_get_schema('mailhandler');
  $fields = drupal_schema_fields_sql('mailhandler');
  $conds = $params = array();

  // Select from this group of mailboxes if defined.
  if (count($mids)) {
    $conds[] = 'mid IN (' . db_placeholders($mids, 'int') . ')';
    foreach ($mids as $mid) {
      $params[] = intval($mid);
    }
  }

  // Apply the conditions filter.
  foreach ($conditions as $key => $value) {
    if (in_array($key, $fields)) {
      $conds[] = $key . " = " . db_type_placeholder($schema['fields'][$key]['type']);
      $params[] = $value;
    }
  }

  // Record debug information.
  if (count($conditions)) {
    mailhandler_watchdog_record('Mailboxes loading conditions: @conditions', array(
      '@conditions' => var_export(array_keys($conditions), TRUE),
    ), WATCHDOG_DEBUG);
  }

  // Build and execute the query.
  $cond = count($conds) ? ' WHERE ' . implode(' AND ', $conds) : '';
  $result = db_query('SELECT ' . implode(', ', $fields) . ' FROM {mailhandler}' . $cond, $params);

  // Populate the return array.
  $search = array();
  while ($mailbox = db_fetch_object($result)) {
    $mailboxes[$mailbox->mid] = $mailbox;
  }

  // Record debug information.
  mailhandler_watchdog_record('Mailboxes loaded: @mailboxes', array(
    '@mailboxes' => var_export(array_keys($mailboxes), TRUE),
  ), WATCHDOG_DEBUG);
  return $mailboxes;
}

/**
 * Validate a Mailhandler Mailbox.
 *
 * Validate the mailbox and set form errors. Even if this function is not used
 * in a form (e.g. validating mailboxes created programatically), the function
 * will still set form errors. These errors can be later recalled using the
 * function form_get_errors().
 *
 * <code>
 *   mailhandler_mailbox_validate($mailbox);
 *   if ($errors = form_get_errors()) {
 *     do_my_error_handling($errors);
 *   }
 *   else {
 *     mailhandler_mailbox_save($mailbox);
 *   }
 * </code>
 *
 * @param $mailbox
 *   A mailbox settings array.
 * @param $form
 *   The $form where mailbox is being edited.
 * @param $form_state
 *   The $form_state values of the form being validated.
 */
function mailhandler_mailbox_validate($mailbox, $form = array(), &$form_state = array()) {

  // Check for valid email address.
  if ($error = user_validate_mail($mailbox['mail'])) {
    form_set_error('mail', $error);
  }

  // Set form error in case the email address does not comply email format.
  if ($form_state['values']['mailto'] && ($error = user_validate_mail($mailbox['mailto']))) {
    form_set_error('mailto', $error);
  }

  // Test POP/IMAP settings, and store result.
  $mailbox_appears_ok = TRUE;

  // Assume external mailbox.
  if ($mailbox['domain'] && $mailbox['port'] && !is_numeric($mailbox['port'])) {
    form_set_error('port', t('Mailbox port must be an integer.'));
    $mailbox_appears_ok = FALSE;
  }

  // Assume local folder.
  if (!$mailbox['domain'] && !$mailbox['port'] && $mailbox['folder']) {

    // check read and write permission
    if (!is_readable($mailbox['folder']) || !is_writable($mailbox['folder'])) {
      form_set_error('port', t('The local folder has to be readable and writable by owner of the webserver process, e.g. nobody.'));
      $mailbox_appears_ok = FALSE;
    }
  }

  // If POP3 mailbox is chosen, messages must be deleted after processing or
  // they will be imported on each retrieval process. Warm about this case when
  // trying to save the mailbox.
  if ($mailbox['imap'] == 0 && $mailbox['delete_after_read'] == 0) {
    form_set_error('delete_after_read', t('You must check off "Delete messages after they are processed" when using a POP3 mailbox.'));
  }
}

/**
 * Save a Mailhandler Mailbox in the database.
 *
 * When $mailbox has 'mid' attribute defined, the database entry with this ID
 * will be updated, otherwise a new mailbox will be created.
 *
 * @param $mailbox
 *   A mailbox settings array.
 */
function mailhandler_mailbox_save(&$mailbox) {

  // Determine this is an update or a new mailbox.
  $update = isset($mailbox['mid']) && $mailbox['mid'] ? array(
    'mid',
  ) : array();

  // Set default values for missing mailbox attributes.
  $mailbox += mailhandler_mailbox_defaults();

  // Save entry into database.
  drupal_write_record('mailhandler', $mailbox, $update);

  // Record information message
  mailhandler_watchdog_record('Mailbox %mailbox saved.', array(
    '%mailbox' => $mailbox['mail'],
  ), WATCHDOG_INFO);
}

/**
 * Delete a Mailhandler Mailbox from the database.
 *
 * When $mailbox has 'mid' attribute defined, the database entry with this ID
 * will be deleted.
 *
 * @param $mid
 *   mailbox ID to be deleted.
 */
function mailhandler_mailbox_delete($mailbox) {
  $mid = isset($mailbox['mid']) ? $mailbox['mid'] : $mailbox;
  db_query("DELETE FROM {mailhandler} WHERE mid = %d", $mid);

  // Record information message
  mailhandler_watchdog_record('Mailbox %mailbox deleted.', array(
    '%mailbox' => isset($mailbox['mail']) ? $mailbox['mail'] : $mid,
  ), WATCHDOG_INFO);
}

/**
 * @} End of "defgroup mailhandler_mailbox_crud".
 */

/**
 * @defgroup mailhandler_mailbox_processing Mailbox processing functions.
 * @{
 * Functions used to process Mailhandler mailboxes. These functions performs the
 * mailbox connection, queue processing and data retrieval, but do not generate
 * site content.
 */

/**
 * Test the connection to a mailbox.
 *
 * Attempt a mailbox connection and set form errors. Even if this function is
 * not used in a form (e.g. testing mailboxes created programatically), the
 * function will still set form errors. These errors can be later recalled using
 * the function form_get_errors().
 *
 * <code>
 *   mailhandler_mailbox_test($mailbox);
 *   if ($errors = form_get_errors()) {
 *     do_my_error_handling($errors);
 *   }
 *   else {
 *     mailhandler_mailbox_save($mailbox);
 *   }
 * </code>
 *
 * This function can be called safely from within a form.
 *
 * @param $mailbox
 *   A mailbox settings array.
 * @param $form
 *   The $form where mailbox is being edited.
 * @param $form_state
 *   The $form_state values of the form being validated.
 */
function mailhandler_mailbox_test($mailbox, $form = array(), &$form_state = array()) {
  $result = FALSE;

  // Verify the mailbox by connecting and getting mailbox status.
  if ($stream = mailhandler_mailbox_stream_open($mailbox)) {

    // Close stream to mailbox.
    mailhandler_mailbox_stream_close($stream);
    $result = TRUE;
  }
  else {
    if ($mailbox['domain']) {
      form_set_error('mailhandler', t('%c connection to %m failed.', array(
        '%c' => $mailbox['imap'] ? 'IMAP' : 'POP3',
        '%m' => $mailbox['mail'],
      )));
    }
    else {
      form_set_error('mailhandler', t('Mailhandler could not access local folder: %m', array(
        '%m' => $mailbox['mail'],
      )));
    }
  }
  return $result;
}

/**
 * @} End of "defgroup mailhandler_mailbox_processing".
 */

/**
 * @defgroup mailhandler_mailbox_stream Mailbox stream functions.
 * @{
 * Functions used to handle mailbox streams. These functions are responsible of
 * connecting or opening mailboxes, reading status and messages and remove the
 * processed entries.
 * 
 * Note: all functions making use of PHP imap extension should go in this group.
 */

/**
 * Establish stream connection to specified mailbox.
 * 
 * @param $mailbox
 *   Array of mailbox configuration.
 * @return 
 *   stream to work with other stream processing functions or FALSE.
 */
function mailhandler_mailbox_stream_open($mailbox) {

  // Build the mailbox string from mailbox settings.
  $proto = array(
    'pop',
    'imap',
  );

  // Build imap_open arguments.
  $box = array(
    'path' => $mailbox['domain'] ? '{' . $mailbox['domain'] . ':' . $mailbox['port'] . '/' . $proto[$mailbox['imap']] . $mailbox['extraimap'] . '}' . $mailbox['folder'] : $mailbox['folder'],
    'user' => $mailbox['domain'] ? $mailbox['name'] : '',
    'pass' => $mailbox['domain'] ? $mailbox['pass'] : '',
  );

  // Open stream (imap, pop or local folder).
  $result = imap_open($box['path'], $box['user'], $box['pass']);

  // Record a debug message to inform about no errors opening the stream.
  mailhandler_watchdog_record('Opening stream to %box: %result.', array(
    '%box' => $box['path'],
    '%result' => !imap_last_error(),
  ), WATCHDOG_DEBUG);

  // Provide a warning message when imap_open returns error.
  if (!$result) {

    // Record error from imap extension.
    mailhandler_watchdog_record('Error opening %box: %error.', array(
      '%box' => $box['path'],
      '%error' => imap_last_error(),
    ), WATCHDOG_WARNING);
  }
  return $result;
}

/**
 * Close stream connection.
 * 
 * @param $stream
 *   stream openned using mailhandler_mailbox_stream_open().
 * @return 
 *   boolean result of closing operation
 */
function mailhandler_mailbox_stream_close($stream) {
  $result = imap_close($stream);

  // Record debug information about stream closing operation.
  mailhandler_watchdog_record('Closing mailbox stream: %result.', array(
    '%result' => !imap_last_error(),
  ), WATCHDOG_DEBUG);
  return $result;
}

/**
 * Get number of messages in the stream connection.
 * 
 * @param $stream
 *   stream openned using mailhandler_mailbox_stream_open().
 * @return 
 *   boolean result of closing operation
 */
function mailhandler_mailbox_stream_count($stream) {
  $result = imap_num_msg($stream);

  // Record debug information about stream closing operation.
  mailhandler_watchdog_record('Messages in stream = %total : %result.', array(
    '%result' => !imap_last_error(),
    '%total' => $result,
  ), WATCHDOG_DEBUG);
  return $result;
}

/**
 * Return a message matching the requested conditions.
 * 
 * @param $stream stream openned using mailhandler_mailbox_stream_open().
 * @param $msgno  message number to retrieve.
 * @param $conditions associative array of conditions to retrieve a message. 
 *   When using IMAP connections, the retrieving process can be improved by 
 *   checking the message header first. Conditions are based on IMAP header
 *   information, E.g. to retrieve only unread emails you can specify the 
 *   'Unseen' => 'U' condition. 
 * @return 
 *   boolean result of closing operation
 */
function mailhandler_mailbox_stream_retrieve($stream, $msgno = 0, $conditions = array()) {

  // Add default conditions, get unread messages.
  $conditions += array(
    'Unseen' => 'U',
  );

  // Get Message header information. In case of POP driver, the function will
  // return the whole message, so we should make no additional calls and return
  // the result.
  $result = imap_headerinfo($stream, $msgno);
  die('@todo: Incomplete function');

  // Record debug information about stream closing operation.
  mailhandler_watchdog_record('Retrieving message %Msgno : %result.', array(
    '%result' => !imap_last_error(),
    '%Msgno' => $conditions['Msgno'],
  ), WATCHDOG_DEBUG);
  return $result;
}

/**
 * Delete a message in the stream connection.
 * 
 * @param $stream stream openned using mailhandler_mailbox_stream_open().
 * @param $msgno  number of message to delete.
 * @return 
 *   boolean result of deleting operation
 */
function mailhandler_mailbox_stream_delete($stream, $msgno = 0) {
  $result = imap_delete($stream, $msgno);

  // Record debug information about stream closing operation.
  mailhandler_watchdog_record('Delete message %msgno : %result.', array(
    '%result' => !imap_last_error(),
    '%msgno' => $msgno,
  ), WATCHDOG_DEBUG);
  return $result;
}

/**
 * @} End of "defgroup mailhandler_mailbox_stream".
 */

/**
 * Return a default content type if the user has not chosen a specific type on the settings page
 * In order of priority, return blog, story, page
 * This assumes that one of these basic types is in use on a site (page and story are active by default)
 * A user can choose other content types via the settings page as this exposes all available types
 */
function mailhandler_default_type() {

  // Get the current default setting, if defined.
  $default_type = variable_get('mailhandler_default_type', NULL);

  // Find out what types are available.
  $available_types = node_get_types('names');

  // Check the default type is still available (it could have been deleted).
  if ($default_type && array_key_exists($default_type, $available_types)) {
    return $default_type;
  }

  // If we get here then either no default is set, or the default type is no longer available
  // Search for the array key (the machine readable name) for blog, story and page basic types
  if (array_key_exists('blog', $available_types)) {
    $default_type = 'blog';
  }
  else {
    if (array_key_exists('story', $available_types)) {
      $default_type = 'story';
    }
    else {
      if (array_key_exists('page', $available_types)) {
        $default_type = 'page';
      }
      else {

        // If basic types not found then return the first item from the array as an alternative default
        $default_type = key($available_types);
      }
    }
  }
  return $default_type;
}

/**
 * Run message retrieval and node processing on a mailbox - is a wrapper around mailhandler_retrieve
 * 
 * Batch API smashes the stack, so this function is called twice when fetching
 * messages via batch in order to return the results from the batch.
 * 
 * @param $mailbox
 *   Array of mailbox configuration
 * @param $mode
 *   String ui or auto (ie cron) processing
 * @param $limit
 *   Int maximum number of messages to retrieve
 * @param $messages
 *   Array of retrieved messages
 */
function mailhandler_node_process_mailbox($mailbox = FALSE, $mode = FALSE, $limit = FALSE, $messages = array()) {

  // Set default maximun message limit according to general settings.
  $limit = isset($limit) ? $limit : variable_get('mailhandler_max_retrieval', 0);
  if (empty($messages)) {
    if ($mode == 'ui') {
      mailhandler_retrieve($mailbox, $mode, $limit);
    }
    else {
      if ($messages = mailhandler_retrieve($mailbox, $mode, $limit)) {
        foreach ($messages as $message) {
          mailhandler_node_process_message($message['header'], $message['origbody'], $message['mailbox'], $message['mimeparts']);
        }
      }
    }
  }
  else {

    // See mailhandler_mailhandler_batch_results.  This hook is invoked
    // and mailhandler_process_mailbox is called a second time in order
    // to obtain the messages that were retrieved and continue processing.
    // This is necessary because we cannot obtain the results back from
    // batch processing directly.
    foreach ($messages as $message) {
      mailhandler_node_process_message($message['header'], $message['origbody'], $message['mailbox'], $message['mimeparts']);
    }
  }
}

/**
 * Implementation of hook_mailhandler_batch_results()
 * 
 * See mailhandler_process_mailbox()
 * 
 */
function mailhandler_mailhandler_batch_results($results) {
  mailhandler_node_process_mailbox(FALSE, FALSE, FALSE, $results);

  // Give the results back for any other modules to use
  return $results;
}

// TODO: add arguments
function mailhandler_node_process_message($header, $origbody, $mailbox, $mimeparts) {
  mailhandler_user_switch();

  // we must process before authenticating because the password may be in Commands
  $node = mailhandler_node_prepare_message($header, $origbody, $mailbox);

  // Authenticate the message
  if (!($node = mailhandler_mailhandler_authenticate('execute', $mailbox['authentication'], array(
    $node,
    $header,
    $origbody,
    $mailbox,
  )))) {
    mailhandler_watchdog_record('Message failed authentication.', array(), WATCHDOG_NOTICE);
    return FALSE;
  }

  // Put $mimeparts on the node
  $node->mimeparts = $mimeparts;

  // we need to change the current user
  // this has to be done here to allow modules
  // to create users
  mailhandler_user_switch($node->uid);

  // modules may override node elements before submitting. they do so by returning the node.
  foreach (module_list() as $name) {
    if (module_hook($name, 'mailhandler')) {
      $function = $name . '_mailhandler';

      // @todo: hook_mailhandler implementation is broken. When this function
      // was moved out of the retrieve loop, some of the variables where not
      // updated.
      $result = $i = NULL;
      if (!($node = $function($node, $result, $i, $header, $mailbox))) {

        // Exit if a module has handled the submitted data.
        break;
      }
    }
  }
  if ($node) {
    if ($node->type == 'comment') {
      $saved = mailhandler_comment_submit($node, $header, $mailbox, $origbody);
      $type = 'comment';
    }
    else {
      $saved = mailhandler_node_submit($node, $header, $mailbox, $origbody);
      $type = 'node';
    }
  }

  // Invoke a second hook for modules to operate on the newly created/edited node/comment.
  foreach (module_list() as $name) {
    if (module_hook($name, 'mailhandler_post_save')) {
      $function = $name . '_mailhandler_post_save';

      // Pass in what's returned from saving the comment (cid) or node (object), the
      // object type, and the original node object (which is the same as $saved in
      // the case of saving a node).
      $function($saved, $type, $node);
    }
  }

  // switch back to original user
  mailhandler_user_switch();

  // Put something in the results array for the counter in the batch finished callback
  $context['results'][] = $mailbox['mail'];
  mailhandler_user_switch();
}

/**
 * Append default commands. Separate commands from body. Strip signature.
 * Return a node object.
 */
function mailhandler_node_prepare_message($header, $body, $mailbox) {

  // Initialise a node object
  $node = new stdClass();
  $node->pass = NULL;

  // Initialize parameters
  $sep = $mailbox['sigseparator'];

  // Copy any name/value pairs from In-Reply-To or References e-mail headers to $node. Useful for maintaining threading info.
  if (!empty($header->references)) {

    // we want the final element in references header, watching out for white space
    $threading = substr(strrchr($header->references, '<'), 0);
  }
  else {
    if (!empty($header->in_reply_to)) {
      $threading = str_replace(strstr($header->in_reply_to, '>'), '>', $header->in_reply_to);

      // Some MUAs send more info in that header.
    }
  }
  if (isset($threading) && ($threading = rtrim(ltrim($threading, '<'), '>'))) {

    //strip angle brackets
    if ($threading) {
      $node->threading = $threading;
    }
    parse_str($threading, $tmp);
    if (isset($tmp['host']) && $tmp['host']) {
      $tmp['host'] = ltrim($tmp['host'], '@');

      // strip unnecessary @ from 'host' element
    }
    foreach ($tmp as $key => $value) {
      $node->{$key} = $value;
    }
  }

  // Prepend the default commands for this mailbox
  if ($mailbox['commands']) {
    $body = trim($mailbox['commands']) . "\n" . $body;
  }
  if ($commands = mailhandler_commands_parse($body, $sep)) {

    // Record debug information.
    mailhandler_watchdog_record('Message unprocessed commands: @commands', array(
      '@commands' => var_export($commands, TRUE),
    ), WATCHDOG_DEBUG);

    // The node type must be set first in order to properly initialize the node
    foreach ($commands['commands'] as $command) {
      if ($command[0] == 'type') {
        $node->type = $command[1];
      }
    }
  }

  // Set a default type if none provided
  if (!isset($node->type)) {
    $node->type = mailhandler_default_type();
  }

  // Apply defaults to the $node object, and allow modules to add default values
  require_once drupal_get_path('module', 'node') . '/node.pages.inc';
  if ($node->type != 'comment') {
    node_object_prepare($node);
  }

  // If cron is called by anon then node_object_prepare sets uid to 0, but otherwise global $user.
  // Manually set this to 0 since that's what the rest of the code expects.
  $node->uid = 0;

  // In order to fall back to the permission system for comment status, the status property must
  // be unset if type is comment.  It will get set by explicit commands, and if not, by
  // comment_save itself.
  if ($node->type == 'comment') {
    unset($node->status);
  }

  // Execute the commands
  if (!empty($commands['commands'])) {
    mailhandler_node_process_message_commands($node, $commands['commands']);

    // Record debug information.
    mailhandler_watchdog_record('Node processed commands: @node', array(
      '@node' => var_export($node, TRUE),
    ), WATCHDOG_DEBUG);
  }

  // Isolate the body from the commands and the sig
  $tmp = array_slice($commands['lines'], $commands['endcommands'], $commands['i'] - $commands['endcommands']);

  // flatten and assign the body to node object. note that filter() is called within node_save() [tested with blog post]
  if ($node->type != 'comment') {
    $type = node_get_types('type', $node->type);
  }
  if (isset($type) && $type && $type->has_body || $node->type == 'comment') {
    $node->body = implode("\n", $tmp);
  }
  if (empty($node->teaser)) {
    $node->teaser = node_teaser($node->body);
  }

  // decode encoded subject line
  $subjectarr = imap_mime_header_decode($header->subject);
  if (empty($subjectarr)) {
    $node->title = truncate_utf8(trim(decode_entities(strip_tags(check_markup($node->body)))), 29, TRUE);
  }
  else {
    for ($i = 0; $i < count($subjectarr); $i++) {
      if ($subjectarr[$i]->charset != 'default') {
        $node->title .= drupal_convert_to_utf8($subjectarr[$i]->text, $subjectarr[$i]->charset);
      }
      else {
        $node->title .= $subjectarr[$i]->text;
      }
    }
  }
  $node->date = $node->changed = format_date($header->udate, 'custom', 'Y-m-d H:i:s O');
  $node->format = $mailbox['format'];

  // If an nid command was supplied, and type is not 'comment', append the revision number
  if (isset($node->nid) && !empty($node->nid) && $node->type != 'comment') {
    $vid = db_result(db_query('SELECT n.vid FROM {node} n WHERE n.nid = %d', $node->nid));
    if ($vid) {
      $node->revision = $node->vid = $vid;
    }
  }

  // Debug message with node before creation.
  mailhandler_watchdog_record('Node or comment: @node', array(
    '@node' => var_export($node, TRUE),
  ), WATCHDOG_DEBUG);
  return $node;
}

/**
 * Create the comment.
 */
function mailhandler_comment_submit($node, $header, $mailbox, $origbody) {
  global $user;
  if (!$node->subject) {
    $node->subject = $node->title;
  }

  // When submitting comments, 'comment' means actualy the comment's body, and not the comments status for a node.
  // We need to reset the comment's body, so it doesn't colide with a default 'comment' command.
  $node->comment = $node->body;

  // comment_save will not fall back to permission system if we set the status explicitly
  // See comment_save.  += will not overwrite an existing array property.
  if (property_exists($node, 'status')) {

    // In comment module, status of 1 means unpublished, status of 0 means published.
    $node->status == 1 ? $node->status = 0 : ($node->status = 1);
  }

  // We want the comment to have the email time, not the current time
  $node->timestamp = $node->created = $header->udate;

  // comment_save gets an array
  $edit = (array) $node;

  // post the comment. if unable, send a failure email when so configured
  $cid = comment_save($edit);
  if (!$cid && $mailbox['replies']) {

    // $fromaddress really refers to the mail header which is authoritative for authentication
    list($fromaddress, $fromname) = mailhandler_get_fromaddress($header, $mailbox);
    $error_text = t('Sorry, your comment experienced an error and was not posted. Possible reasons are that you have insufficient permission to post comments or the node is no longer open for comments.');
    $params = array(
      'body' => $origbody,
      'error_messages' => array(),
      'error_text' => $error_text,
      'from' => $fromaddress,
      'header' => $header,
      'node' => $node,
    );
    drupal_mail('mailhandler', 'mailhandler_error_comment', $fromaddress, user_preferred_language($user), $params);
    mailhandler_watchdog_record('Comment submission failure: %subject.', array(
      '%subject' => $edit['subject'],
    ), WATCHDOG_NOTICE);

    // Record debug information.
    mailhandler_watchdog_record('Comment submission failure: @comment', array(
      '@comment' => var_export($edit, TRUE),
    ), WATCHDOG_DEBUG);
  }
  return $cid;
}

/**
 * Create the node.
 */
function mailhandler_node_submit($node, $header, $mailbox, $origbody) {
  global $user;
  list($fromaddress, $fromname) = mailhandler_get_fromaddress($header, $mailbox);

  // Reset the static cache
  form_set_error(NULL, '', TRUE);
  node_validate($node);
  if (!($error_messages = form_set_error())) {

    // Prepare the node for save and allow modules make changes
    $node = node_submit($node);

    // Save the node
    if (!empty($node->nid)) {
      if (node_access('update', $node)) {
        node_save($node);
        mailhandler_watchdog_record('Updated %title by %from.', array(
          '%title' => $node->title,
          '%from' => $fromaddress,
        ));
      }
      else {
        $error_text = t('The e-mail address !from may not update !type items.', array(
          '!from' => $fromaddress,
          '!type' => $node->type,
        ));
        mailhandler_watchdog_record('Node submission failure: %from may not update %type items.', array(
          '%from' => $fromaddress,
          '%type' => $node->type,
        ), WATCHDOG_NOTICE);
      }
    }
    else {
      $account = user_load($node->uid);
      if (node_access('create', $node, $account)) {
        node_save($node);
        mailhandler_watchdog_record('Added %title by %from.', array(
          '%title' => $node->title,
          '%from' => $fromaddress,
        ));
      }
      else {
        $error_text = t('The e-mail address !from may not create !type items.', array(
          '!from' => $fromaddress,
          '!type' => $node->type,
        ));
        mailhandler_watchdog_record('Node submission failure: %from may not create %type items.', array(
          '%from' => $fromaddress,
          '%type' => $node->type,
        ), WATCHDOG_NOTICE);
      }
    }

    // Return the node is successfully saved
    if (!isset($error_text)) {
      return $node;
    }
  }
  else {
    $error_text = t('Your submission is invalid:');
    mailhandler_watchdog_record('Node submission failure: validation error.', array(), WATCHDOG_NOTICE);
  }
  if (isset($error_text)) {
    if ($mailbox['replies']) {
      $params = array(
        'body' => $origbody,
        'error_messages' => $error_messages,
        'error_text' => $error_text,
        'from' => $fromaddress,
        'header' => $header,
        'node' => $node,
      );
      drupal_mail('mailhandler', 'mailhandler_error_node', $fromaddress, user_preferred_language($user), $params);
    }
  }

  // return FALSE if the node was not successfully saved
  return FALSE;
}

/**
 * Accept a taxonomy term name and replace with a tid. this belongs in taxonomy.module.
 */
function mailhandler_term_map(&$term, $index = array(), $vid = FALSE) {

  // provide case insensitive and trimmed map so as to maximize likelihood of successful mapping
  if ($vid) {
    $and = 'AND vid =' . db_escape_string($vid);
  }
  $term = db_result(db_query("SELECT tid FROM {term_data} WHERE LOWER('%s') LIKE LOWER(name) {$and}", trim($term)));
}
function mailhandler_node_process_message_commands(&$node, $commands) {
  foreach ($commands as $data) {

    // TODO: allow for nested arrays in commands ... Possibly trim() values after explode().
    // If needed, turn this command value into an array
    if (substr($data[1], 0, 1) == '[' && substr($data[1], -1, 1) == ']') {
      $data[1] = rtrim(ltrim($data[1], '['), ']');

      //strip brackets
      $data[1] = explode(",", $data[1]);

      // allow for key value pairs
      foreach ($data[1] as $key => $value) {
        $data_tmp = explode(":", $value, 2);
        if (isset($data_tmp[1])) {

          // is it a key value pair?
          // remove old, add as key value pair
          unset($data[1][$key]);
          $data_tmp[0] = trim($data_tmp[0]);
          $data[1][$data_tmp[0]] = $data_tmp[1];
        }
      }
    }
    $data[0] = strtolower(str_replace(' ', '_', $data[0]));

    // Taxonomy specific command handling.
    if (($data[0] == 'taxonomy' || $data[0] == 'taxonomy') && module_exists('taxonomy')) {
      $vocabs = taxonomy_get_vocabularies($node->type);
      $node->taxonomy = array();

      // if needed, map term names into IDs. this should move to taxonomy_mailhandler()
      if ($data[0] == 'taxonomy' && !is_numeric($data[1][0])) {
        array_walk($data[1], 'mailhandler_term_map');

        // Only add term if node type can use that term's vocab
        $term = taxonomy_get_term($data[1][0]);
        if (array_key_exists($term->vid, $vocabs)) {
          $node->taxonomy = array_merge($node->taxonomy, $data[1]);
        }
        unset($data[0]);
      }
      else {
        if (substr($data[0], 0, 9) == 'taxonomy[' && substr($data[0], -1, 1) == ']') {

          // make sure a valid vid is passed in:
          $vid = substr($data[0], 9, -1);
          $vocabulary = taxonomy_vocabulary_load($vid);

          // if the vocabulary is not activated for that node type, unset $data[0], so the command will be ommited from $node
          // TODO: add an error message
          if (!in_array($node->type, $vocabulary->nodes)) {
            unset($data[0]);
          }
          else {
            if (!$vocabulary->tags) {
              array_walk($data[1], 'mailhandler_term_map', $vid);
              $node->taxonomy = array_merge($node->taxonomy, $data[1]);
              unset($data[0]);
            }
            else {
              if ($vocabulary->tags) {

                // for freetagging vocabularies, we just pass the list of terms
                $node->taxonomy['tags'][$vid] = implode(',', $data[1]);
                unset($data[0]);

                // unset, so it won't be included when populating the node object
              }
            }
          }
        }
      }
    }
    if (!empty($data[0])) {
      $node->{$data}[0] = $data[1];
    }
  }
}

/**
 * Implementation of hook_mail().
 */
function mailhandler_mail($key, &$message, $params) {
  $variables = array(
    '!body' => $params['body'],
    '!from' => $params['from'],
    '!site_name' => variable_get('site_name', 'Drupal'),
    '!subject' => $params['header']->subject,
    '!type' => $params['node']->type,
  );
  $message['subject'] = t('Email submission to !site_name failed - !subject', $variables);
  $message['body'][] = $params['error_text'];
  foreach ($params['error_messages'] as $key => $error) {
    $message['body'][$key] = decode_entities(strip_tags($error));
  }
  $message['body'][] = t("You sent:\n\nFrom: !from\nSubject: !subject\nBody:\n!body", $variables);
}

/**
 * Implementation of hook_mailhandler_authenticate_info()
 */
function mailhandler_mailhandler_authenticate_info() {
  $info = array(
    'mailhandler_default' => array(
      'title' => 'Mailhandler Default',
      'description' => 'Checks whether the sender matches a valid user in the database',
      'callback' => 'mailhandler_authenticate_default',
      'module' => 'mailhandler',
      'extension' => NULL,
      // as in $type in module_load_include
      'basename' => NULL,
    ),
  );
  if (module_exists('tokenauth')) {
    $info += array(
      'mailhandler_tokenauth' => array(
        'title' => 'Mailhandler Tokenauth',
        'description' => 'Authenticate messages based on users token from Tokenauth module',
        'callback' => 'mailhandler_authenticate_tokenauth',
        'module' => 'mailhandler',
        'extension' => NULL,
        'basename' => NULL,
      ),
    );
  }
  return $info;
}

/**
 * Authenticate message based on sender's email address
 *   If the sender's email address matches an email address of a valid user, then assign
 *   that user's uid and name to the node object.
 *   
 * @param $node
 *   Object a node object
 * @param $header
 *   Object of message header information
 * @param $origbody
 *   String message body text
 * @param $mailbox
 *   Array of mailbox configuration
 * 
 * @return object, the node object
 */
function mailhandler_authenticate_default($node, $header, $origbody, $mailbox) {
  list($fromaddress, $fromname) = mailhandler_get_fromaddress($header, $mailbox);
  if ($from_user = mailhandler_user_load($fromaddress, $node->pass, $mailbox)) {
    $node->uid = $from_user->uid;
    $node->name = $from_user->name;
  }
  else {
    if (function_exists('mailalias_user') && ($from_user = mailhandler_user_load_alias($fromaddress, $node, $mailbox))) {
      $node->uid = $from_user->uid;
      $node->name = $from_user->name;
    }
    else {

      // Authentication failed.  Try as anonymous.
      $node->uid = 0;
      $node->name = $fromname;
    }
  }
  return $node;
}

/**
 * Authenticate message based on token from tokenauth module
 *   If the user's token is found somewhere in the "to" field, assign that user's uid and name 
 *   to the node object.  A rough search for the token somewhere in the "toaddress" is performed
 *   instead of an exact, ordered match in order to allow some freedom in the format of allowed
 *   "toaddress".  For example, if using a catchall email address, the toaddress could be:
 *   
 *   f93ksj35dx@example.com - where f93ksj35dx is the user's token
 *   or alternatively:
 *   f93ksj35dx-foo@example.com - where f93ksj35dx is the user's token and foo is the name of an
 *   Organic Group to which the message should be assigned.  
 *   
 *   A rough search allows for different approaches to use this single authentication method.
 *   
 * @param $node
 *   Object a node object
 * @param $header
 *   Object of message header information
 * @param $origbody
 *   String message body text
 * @param $mailbox
 *   Array of mailbox configuration
 * 
 * @return object, the node object
 */
function mailhandler_authenticate_tokenauth($node, $header, $origbody, $mailbox) {
  if (module_exists('tokenauth')) {
    list($fromaddress, $fromname) = mailhandler_get_fromaddress($header, $mailbox);

    // If user with given email address exists and their token is in the toaddress, allow.
    if (($from_user = mailhandler_user_load($fromaddress, $node->pass, $mailbox)) && strpos($header->to[0]->mailbox, tokenauth_get_token($from_user->uid)) !== FALSE) {
      $node->uid = $from_user->uid;
      $node->name = $from_user->name;
    }
    else {
      if (function_exists('mailalias_user') && ($from_user = mailhandler_user_load_alias($fromaddress, $node, $mailbox)) && strpos($header->to[0]->mailbox, tokenauth_get_token($from_user->uid)) !== FALSE) {
        $node->uid = $from_user->uid;
        $node->name = $from_user->name;
      }
      else {

        // If token authentication fails, try as anonymous.
        $node->uid = 0;
        $node->name = $fromname;
      }
    }
  }
  return $node;
}

/**
 * Determine from address either using the mailbox setting or via the header information
 * 
 * @param $header
 *   Object message header information
 * @param $mailbox
 *   Array mailbox settings
 * @return array
 */
function mailhandler_get_fromaddress($header, $mailbox) {
  if (($fromheader = strtolower($mailbox['fromheader'])) && isset($header->{$fromheader})) {
    $from = $header->{$fromheader};
  }
  else {
    $from = $header->from;
  }
  return array(
    $from[0]->mailbox . '@' . $from[0]->host,
    isset($from[0]->personal) ? $from[0]->personal : '',
  );
}

/**
 * Retrieve user information from his email address.
 */
function mailhandler_user_load($mail, $pass, $mailbox) {
  if ($mailbox['security'] == 1) {
    if (!($account = user_load(array(
      'mail' => $mail,
      'pass' => $pass,
    )))) {
      mailhandler_watchdog_record('Wrong password used in message commands for %address', array(
        '%address' => $mail,
      ), WATCHDOG_NOTICE);
    }
    return $account;
  }
  else {
    return user_load(array(
      'mail' => $mail,
    ));
  }
}

/**
 * Look up a user based on their mailalias addresses
 * 
 * Helper function for mailhandler_authenticate_tokenauth()
 * 
 * @param $fromaddress
 *   String from address
 * @param $node
 *   Object node object
 * @param $mailbox
 *   Array of mailhandler mailbox configuration
 *   
 * @return Object user object or FALSE
 */
function mailhandler_user_load_alias($fromaddress, $node, $mailbox) {
  $result = db_query("SELECT mail FROM {users} WHERE data LIKE '%%%s%%'", $fromaddress);
  while ($alias = db_result($result)) {
    if ($from_user = mailhandler_user_load($alias, $node->pass, $mailbox)) {
      return $from_user;
      break;
    }
  }
  return FALSE;
}

/**
 * @defgroup mailhandler_util Mailhandler general purpose functions
 * @{
 * The functions included here are helpers for the module operation.
 */

/**
 * Switch from original user to desired uid for content authoring.
 *
 * Note: You first need to run mailhandler_switch_user without argument to store
 * the current user. Call mailhandler_switch_user agin without argument to set 
 * the user back to the original user.
 *
 * @param $uid The user ID to switch to
 *
 */
function mailhandler_user_switch($uid = NULL) {
  global $user;
  static $orig_user = array();
  if (isset($uid)) {
    session_save_session(FALSE);
    $user = user_load(array(
      'uid' => $uid,
    ));
  }
  else {
    if (count($orig_user)) {
      $user = array_shift($orig_user);
      session_save_session(TRUE);
      array_unshift($orig_user, $user);
    }
    else {
      $orig_user[] = $user;
    }
  }
}

/**
 * Log a Mailhandler message.
 *
 * This is a wrapper call to watchdog, to avoid selective events recording from
 * the Mailhandler configuration. For debug messages, the caller function and
 * line is shown in the 'Operations' column.
 *
 * @param $message
 *   The message to store in the log. See t() for documentation
 *   on how $message and $variables interact. Keep $message
 *   translatable by not concatenating dynamic values into it!
 * @param $variables
 *   Array of variables to replace in the message on display or
 *   NULL if message is already translated or not possible to
 *   translate.
 * @param $severity
 *   The severity of the message, as per RFC 3164. Possible values are
 *   WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
 * @param $link
 *   A link to associate with the message.
 *
 * @see watchdog_severity_levels()
 * @see watchdog()
 */
function mailhandler_watchdog_record($message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
  if ($severity <= variable_get('mailhandler_watchdog_level', WATCHDOG_INFO)) {

    // Use 'Operations' column to show function and line of the message call.
    // @todo: build a link to an updated contrib API site.
    if (!$link) {
      $caller = _mailhandler_get_last_caller(debug_backtrace());
      $link = $caller['line'] . ' : ' . $caller['function'];
    }
    watchdog('mailhandler', $message, $variables, $severity, $link);
  }
}

/**
 * Return mailhandler mailbox default attributes.
 *
 * @return
 *   Associative array with mailhandler mailbox default attributes.
 */
function mailhandler_mailbox_defaults() {
  return array(
    'mail' => '',
    'mailto' => '',
    'folder' => 'INBOX',
    'imap' => 0,
    'domain' => '',
    'port' => '',
    'name' => '',
    'pass' => '',
    'extraimap' => '',
    'mime' => 'TEXT/HTML,TEXT/PLAIN',
    'security' => 0,
    'replies' => 1,
    'fromheader' => '',
    'commands' => '',
    'sigseparator' => '',
    'delete_after_read' => 1,
    'enabled' => 1,
    'format' => variable_get('filter_default_format', 1),
    'authentication' => 'mailhandler_default',
  );
}

/**
 * Return last caller of a backtrace. Used to trac calls to deprecated functions
 * of this module.
 * 
 * @param $backtrace
 * @return 
 * string caller
 */
function _mailhandler_get_last_caller($backtrace) {

  // Errors that occur inside PHP internal functions do not generate
  // information about file and line. Ignore black listed functions.
  $blacklist = array(
    'debug',
  );
  while ($backtrace && !isset($backtrace[1]['line']) || isset($backtrace[2]['function']) && in_array($backtrace[2]['function'], $blacklist)) {
    array_shift($backtrace);
  }

  // The first trace is the call itself.
  // It gives us the line and the file of the last call.
  $call = $backtrace[1];

  // The second call give us the function where the call originated.
  if (isset($backtrace[2])) {
    if (isset($backtrace[2]['class'])) {
      $call['function'] = $backtrace[2]['class'] . $backtrace[2]['type'] . $backtrace[1]['function'] . '()';
    }
    else {
      $call['function'] = $backtrace[2]['function'] . '()';
    }
  }
  else {
    $call['function'] = 'main()';
  }
  return $call;
}

/**
 * Record deprecated function usages using watchdog.
 * 
 * Usefull to remove backward compatibility in a future.
 */
function mailhandler_watchdog_deprecated() {

  // Record warning information about a deprecated function call, so we can
  // track wich modules are still using it.
  $caller = _mailhandler_get_last_caller(debug_backtrace());
  $call = basename($caller['file']) . ':' . $caller['line'] . ' ' . $caller['function'];
  mailhandler_watchdog_record('Deprecated call from: @call', array(
    '@call' => $call,
  ), WATCHDOG_WARNING);
}

/**
 * @} End of "defgroup mailhandler_util".
 */

/**
 * @defgroup mailhandler_deprecated Mailhandler deprecated functions.
 * @{
 * Functions that can be removed, but are kept for backward compatibility.
 * Modules using these functions can be tracked using warning watchdog entries.
 */

/**
 * (DEPRECATED) Fetch data for a specific mailbox from the database.
 *
 * To be removed once it is not in use by other module. Currently this is just a
 * wrapper around mailhandler_mailbox_load(). The only know usage for now is in
 * mail_comment module.
 */
function mailhandler_get_mailbox($mid) {
  mailhandler_watchdog_deprecated();
  return mailhandler_mailbox_load($mid);
}

/**
 * @} End of "defgroup mailhandler_deprecated".
 */

Functions

Namesort descending Description
mailhandler_authenticate_default Authenticate message based on sender's email address If the sender's email address matches an email address of a valid user, then assign that user's uid and name to the node object.
mailhandler_authenticate_tokenauth Authenticate message based on token from tokenauth module If the user's token is found somewhere in the "to" field, assign that user's uid and name to the node object. A rough search for the token somewhere in the…
mailhandler_comment_submit Create the comment.
mailhandler_cron Implementation of hook_cron().
mailhandler_default_type Return a default content type if the user has not chosen a specific type on the settings page In order of priority, return blog, story, page This assumes that one of these basic types is in use on a site (page and story are active by default) A user…
mailhandler_get_fromaddress Determine from address either using the mailbox setting or via the header information
mailhandler_get_mailbox (DEPRECATED) Fetch data for a specific mailbox from the database.
mailhandler_help Implementation of hook_help().
mailhandler_mail Implementation of hook_mail().
mailhandler_mailbox_defaults Return mailhandler mailbox default attributes.
mailhandler_mailbox_delete Delete a Mailhandler Mailbox from the database.
mailhandler_mailbox_load Load a mailbox array from database.
mailhandler_mailbox_load_multiple Load mailboxes from the database.
mailhandler_mailbox_save Save a Mailhandler Mailbox in the database.
mailhandler_mailbox_stream_close Close stream connection.
mailhandler_mailbox_stream_count Get number of messages in the stream connection.
mailhandler_mailbox_stream_delete Delete a message in the stream connection.
mailhandler_mailbox_stream_open Establish stream connection to specified mailbox.
mailhandler_mailbox_stream_retrieve Return a message matching the requested conditions.
mailhandler_mailbox_test Test the connection to a mailbox.
mailhandler_mailbox_validate Validate a Mailhandler Mailbox.
mailhandler_mailhandler_authenticate_info Implementation of hook_mailhandler_authenticate_info()
mailhandler_mailhandler_batch_results Implementation of hook_mailhandler_batch_results()
mailhandler_menu Implementation of hook_menu().
mailhandler_node_prepare_message Append default commands. Separate commands from body. Strip signature. Return a node object.
mailhandler_node_process_mailbox Run message retrieval and node processing on a mailbox - is a wrapper around mailhandler_retrieve
mailhandler_node_process_message
mailhandler_node_process_message_commands
mailhandler_node_submit Create the node.
mailhandler_perm Implementation of hook_perm().
mailhandler_term_map Accept a taxonomy term name and replace with a tid. this belongs in taxonomy.module.
mailhandler_user_load Retrieve user information from his email address.
mailhandler_user_load_alias Look up a user based on their mailalias addresses
mailhandler_user_switch Switch from original user to desired uid for content authoring.
mailhandler_watchdog_deprecated Record deprecated function usages using watchdog.
mailhandler_watchdog_record Log a Mailhandler message.
_mailhandler_get_last_caller Return last caller of a backtrace. Used to trac calls to deprecated functions of this module.