<?php
/**
 * Class for importing mail from mailboxes
 *
 * @package activeCollab.modules.project_exporter
 * @subpackage models
 *
 */
class IncomingMailImporter {

  /**
   * Returns true if environment has necessary libraries to import email
   *
   * @return boolean
   */
  static function checkEnvironment() {
    return extension_loaded('imap');
  } // checkEnvironment

  /**
   * Filter which is applied over incoming mail
   *
   * @var IncomingMailFilter
   */
  protected $applied_filter;

  /**
   * Open's connection to mailboxes and download emails with limit of $max_emails
   *
   * @param array $mailboxes
   * @param integer $max_emails
   */
  function importEmails (&$mailboxes, $max_emails = 20) {
    require_once EMAIL_FRAMEWORK_PATH . '/models/incoming_mail_body_processors/IncomingMailBodyProcessor.class.php';

    $import_date = new DateTimeValue();
    $imported_emails_count = 0;

    if (is_foreachable($mailboxes)) {
      foreach ($mailboxes as $mailbox) {
        $manager = $mailbox->getMailboxManager();
        // open connection to mailbox
        try {
          $manager->connect();
        } catch(Error $e) {
          // we didn't connect, so we need to log it
          $log = new IncomingMessageServerErrorActivityLog();
          $log->log($mailbox,$e->getMessage());

          $mailbox->setLastStatus(IncomingMailbox::LAST_CONNECTION_STATUS_ERROR);
          $mailbox->incrementFailureAttemps();
          if($mailbox->getFailureAttempts() == INCOMING_MAIL_FAILURE_ATTEMPTS_NUM) {
            $mailbox->setIsEnabled(false);
          } //if
          $mailbox->save();

          if(NOTIFY_ADMIN_ON_DISABLE_MAILBOX && !$mailbox->getIsEnabled()) {
            ApplicationMailer::notifier()->notifyAdministrators(null, 'email/mailbox_disabled', array(
              'resolve_url' => Router::assemble('incoming_email_admin_mailboxes'),
              'mailbox_name' => $mailbox->getDisplayName()
            ), array('method' => ApplicationMailer::SEND_INSTANTNLY));
          } // if
          continue;
        } // try
        
        try {
          $mailbox->setLastStatus(IncomingMailbox::LAST_CONNECTION_STATUS_OK);
          $mailbox->setFailureAttempts(0);
          $mailbox->save();
        } catch (Error $e) {
          
          ob_start();
          dump_error($e, false);
          $error = ob_get_clean();
            
          ApplicationMailer::notifier()->notifyAdministrators(null, 'email/mailbox_not_checked', array(
            'mailbox_name' => $mailbox->getDisplayName(),
            'error' => $error
          ), array(
          		'method' => ApplicationMailer::SEND_INSTANTNLY
          	));
          continue;
        }//try


        $email_count = $manager->countMessages();


        for ($mid = 1; $mid < ($email_count+1); $mid++) {
          if ($imported_emails_count >= $max_emails) {
            return true;
          } // if

          $current_message_id = 1;

          //get message
          try {
            $email = $manager->getMessage($current_message_id, INCOMING_MAIL_ATTACHMENTS_FOLDER);
          } catch (Error $e) {
            $log = new IncomingMessageServerErrorActivityLog();
            $log->log($mailbox,$e->getMessage());
            continue;
          } // try
          
          if($email->getIsAutoRespond()) {
            $manager->deleteMessage($current_message_id, true);
            $auto_log = new IncomingMessageAutoRespondActivityLog();
            $auto_log->log($email, $mailbox,lang('Auto respond or delivery failure message deleted.'));
            continue;
          }//if

          //import mail into db
          try {
            $pending_email = $this->createPendingEmail($email, $mailbox);
          } catch (Error $e) {
            $import_log = new IncomingMessageServerErrorActivityLog();
            $import_log->log($mailbox,$e->getMessage());
            continue;
          } //

          //delete message from server
          $manager->deleteMessage($current_message_id, true);

          //apply filters and import incoming mail into activeCollab
          try {
            $imported_object = IncomingMailImporter::importPendingEmail($pending_email);
          } catch (Error $e) {
            //make conflict
            $import_error_log = new IncomingMessageImportErrorActivityLog();
            $import_error_log->log($mailbox,$pending_email,$e->getMessage(), $this->applied_filter);
            
            //if there is conflicts and send_instantly is configured
            if(ConfigOptions::getValue('conflict_notifications_delivery') == IncomingMail::CONFLICT_NOTIFY_INSTANTLY) {
              ApplicationMailer::notifier()->notifyAdministrators(null, 'email/conflict_notify_instantly', array(
                'pending_email' => $pending_email,
                'conflict_page_url' => Router::assemble('incoming_email_admin_conflict'),
                'conflict_reason'	=> $e->getMessage()
              ));
            }//if
            continue;
          } //try

          if(!$this->applied_filter) {
            $performed_action = new IncomingMailCommentAction();
          } else {
            $performed_action = $this->applied_filter->getActionObject();
          }//if

          $succ_log = new IncomingMessageReceivedActivityLog();
          $succ_log->log($mailbox, $performed_action, $pending_email,  $this->applied_filter, $imported_object);

          $user = $pending_email->getCreatedBy();
          if ($user instanceof User) {
            $user->setLastActivityOn(new DateTimeValue());
            $user->save();
          } // if
          //delete from incoming_emails table
          $pending_email->delete();

          $imported_emails_count ++;
        } // for
      } // foreach
    } // if
  } // importEmails

  /**
   * Creates pending incoming email from email message
   *
   * @param MailboxManagerEmail $email
   * @param IncomingMailbox $mailbox
   *
   * @return mixed
   */
  function createPendingEmail(MailboxManagerEmail &$email, &$mailbox) {

    $incoming_mail = new IncomingMail();
    $incoming_mail->setIncomingMailboxId($mailbox->getId());
    $incoming_mail->setHeaders($email->getHeaders());

    // object subject
    $subject = $email->getSubject();

    //if reply to notification
    preg_match("/\{(.*?)\/(.*?)\}/is", $subject, $results);
    if (count($results) > 0) {
      $name = $results[1];
      $ids = $results[2];

      $object = null;
      EventsManager::trigger('on_object_from_notification_context', array(&$object, $name, $ids));

      if($object && $object instanceof IComments) {
        $incoming_mail->setParent($object);
        $incoming_mail->setIsReplayToNotification(1);
      }//if
      
      $subject = trim(str_replace($results[0],'',$subject));
    } // if

    $incoming_mail->setSubject($subject);

    // object body

    $body_procesor = new IncomingMailBodyProcessor($email);
    $incoming_mail->setBody($body_procesor->extractReply());
    $incoming_mail->setAdditionalData($body_procesor->getAdditionalData());

    
    //set to, cc, bcc, replay_to
    if($email->getAddress('to')) {
      $incoming_mail->setToEmail(serialize($email->getAddress('to')));
    } //if

    if($email->getAddresses('cc')) {
      $incoming_mail->setCcTo(serialize($email->getAddresses('cc')));
    }
    if($email->getAddresses('bcc')) {
      $incoming_mail->setBccTo(serialize($email->getAddresses('bcc')));
    }
    if($email->getAddresses('reply_to')) {
      $incoming_mail->setReplyTo(serialize($email->getAddresses('reply_to')));
    }

    if ($incoming_mail->getSubject() || $incoming_mail->getBody()) {
      if (!$incoming_mail->getSubject()) {
        $incoming_mail->setSubject(lang('[SUBJECT NOT PROVIDED]'));
      } // if
      if (!$incoming_mail->getBody()) {
        $incoming_mail->setBody(lang('[CONTENT NOT PROVIDED]'));
      } // if
    } // if

    if($email->getPriority() == IncomingMail::IM_PRIORITY_HIGHEST || $email->getPriority() == IncomingMail::IM_PRIORITY_HIGH) {
      $priority = IncomingMailFilter::IM_FILTER_IMPORTANT;
      $incoming_mail->setPriority($priority);
    }  //if


    $sender = $email->getAddress('from');
    
    // user details
    $email_address = array_var($sender, 'email', null);
    $user = Users::findByEmail($email_address);
    if (!$user instanceof User) {
      //if email isn't valid set dummy email
      if(!is_valid_email($email_address)) {
        $incoming_mail->setOriginalFromEmail($email_address);
        $email_address = INCOMING_MAIL_INVALID_EMAIL_ADDRESS;
      }//if
      $user = new AnonymousUser(array_var($sender, 'name', null) ? array_var($sender, 'name', null) : $email_address, $email_address);
    } // if
    $incoming_mail->setCreatedBy($user);

    // creation time
    $incoming_mail->setCreatedOn(new DateTimeValue());

    $incoming_mail->save();


    // create attachment objects
    $attachments = $email->getAttachments();
    if (is_foreachable($attachments)) {
      foreach ($attachments as $attachment) {
        $incoming_attachment = new IncomingMailAttachment();
        $incoming_attachment->setTemporaryFilename(basename(array_var($attachment, 'path', null)));
        $incoming_attachment->setOriginalFilename(array_var($attachment,'filename', null));
        $incoming_attachment->setContentType(array_var($attachment, 'content_type', null));
        $incoming_attachment->setFileSize(array_var($attachment, 'size', null));
        $incoming_attachment->setMailId($incoming_mail->getId());
        $attachment_save = $incoming_attachment->save();
        if (!$attachment_save || is_error($attachment_save)) {
          // we couldn't create object in database so we need to remove file from system
          //@unlink(array_var($attachment,'path'));
        } // if
      } // foreach
    } // if
    
    return $incoming_mail;
  } // createPendingEmail

  /**
   * Use $incoming_mail as a base for creating ProjectObject
   *
   * @param IncomingMail $incoming_mail
   * @return integer
   */
  function importPendingEmail($incoming_mail) {

    if($incoming_mail->getIsReplayToNotification()) {
      $action = new IncomingMailCommentAction();
      return $action->doActions($incoming_mail);
    } else {
      $filters = IncomingMailFilters::findAllActive();
      if(is_foreachable($filters)) {
        foreach($filters as $filter) {
          if($filter->match($incoming_mail) !== false) {
            $this->applied_filter = $filter;
            return $filter->apply();
            break;
          }//if
        }//foreach
      }//if
    }//if
    
    throw new Error(IncomingMessageImportErrorActivityLog::ERROR_NO_FILTER_APPLIED);

  } // importPendingEmail


  /**
   * Attach files from incoming mail to $project_object
   *
   * @param IncomingMail $incoming_mail
   * @param ProjectObject $project_object
   * @return null
   */
  function attachFilesToProjectObject(&$incoming_mail, &$project_object) {
    $attachments = $incoming_mail->getAttachments();
    $formated_attachments = array();
    if (is_foreachable($attachments)) {
      foreach ($attachments as $attachment) {
        $formated_attachments[] = array(
          'path' => INCOMING_MAIL_ATTACHMENTS_FOLDER.'/'.$attachment->getTemporaryFilename(),
          'filename' => $attachment->getOriginalFilename(),
          'type' => strtolower($attachment->getContentType()),
        );
      } // foreach
      if(!$project_object instanceof IAttachments) {
        throw new Error(lang('This object is not instance of IAttachments @ ' . __CLASS__));
      }//if
      $project_object->attachments()->attachFromArray($formated_attachments);
    } // if
  } // attachFilesToProjectObject
}