Пример #1
0
    def send(self, subject="", to="", body="", smtp=""):  # TODO add "cc", "bcc"
        if len(to) == 0:
            raise Exception('send(): recipient email address is not provided')

        if not self.is_simulate:
            send_email(subject, self._imap_account.email, to, body)
        logger.debug("send(): sent a message to  %s" % str(to))
Пример #2
0
    def handle(self, *args, **options):

        # iterate over all the user accounts in the database
        imapAccounts = ImapAccount.objects.filter(is_initialized=False)

        for imapAccount in imapAccounts:
            if imapAccount.is_running:
                continue
            imapAccount.is_running = True
            imapAccount.save()

            res = {'status': False, 'imap_error': False}
            logger.info("run initial sync for email: %s" % imapAccount.email)

            # authenticate with the user's imap server
            auth_res = authenticate(imapAccount)
            # if authentication failed we can't run anything
            if not auth_res['status']:
                continue

            # get an imapclient which is authenticated
            imap = auth_res['imap']

            try:
                # create the mailbox
                mailbox = MailBox(imapAccount, imap)
                # sync the mailbox with imap
                mailbox._sync()
                logger.info("Mailbox sync done")
                # after sync, logout to prevent multi-connection issue
                imap.logout()
                logger.info(
                    "Mailbox logged out to prevent multi-connection issue")
                mailbox._run_user_code()
            except Exception:
                logger.exception("mailbox task running failed %s " %
                                 imapAccount.email)
                send_email("Your YoUPS account is ready!",
                           "no-reply@" + BASE_URL, '*****@*****.**',
                           "%s register inbox failed " % imapAccount.email)

                continue

            imapAccount.is_initialized = True
            imapAccount.is_running = False
            imapAccount.save()

            res['status'] = True
Пример #3
0
            logger.exception("IMAPClient.Error - failed to authenticate email")
            res['imap_error'] = e
            res['code'] = "Can't authenticate your email"
        except Exception, e:
            logger.exception("Unknown exception %s failed to authenticate email" %e)
            # TODO add exception
            res['imap_error'] = e
            res['code'] = msg_code['UNKNOWN_ERROR']

    if res['status'] is False:
        # email to the user that there is error at authenticating email
        if imap_account.is_oauth and len(imap_account.email) > 0:
            subject = "[" + WEBSITE + "] Authentication error occurs"
            body = "Authentication error occurs! \n" + str(res['imap_error'])
            body += "\nPlease log in again at " + BASE_URL + "/editor"
            send_email(subject, WEBSITE + "@" + BASE_URL, imap_account.email, body)

        # TODO don't delete
        # Delete this ImapAccount information so that it requires user to reauthenticate
        # imap_account.password = ""
        imap_account.access_token = ""

        # turn off the email engine
        # imap_account.is_running = False
        imap_account.save()

    return res

def decrypt_plain_password(encrypted_password):
    aes = AES.new(IMAP_SECRET, AES.MODE_CBC, 'This is an IV456')
    password = aes.decrypt( base64.b64decode(encrypted_password) )
Пример #4
0
def register_inbox():
    """Do the initial sync on an inbox.
    """

    lockFile = 'register_inbox.lock'
    with open(lockFile, 'w') as f:
        have_lock = get_lock(f)
        if not have_lock:
            logger.info('Lock already taken %s' % lockFile)
            return

        for imapAccount in ImapAccount.objects.filter(is_initialized=False):
            try:
                logger.info('registering inbox: %s', imapAccount.email)

                while True:
                    try:
                        # authenticate with the user's imap server
                        auth_res = authenticate(imapAccount)
                        # if authentication failed we can't run anything
                        if not auth_res['status']:
                            # Stop doing loop
                            # TODO maybe we should email the user
                            logger.critical(
                                'register authentication failed for %s',
                                imapAccount.email)
                            continue

                        # get an imapclient which is authenticated
                        imap = auth_res['imap']  # type: IMAPClient

                        # create the mailbox
                        mailbox = MailBox(imapAccount, imap)
                        # sync the mailbox with imap
                        done = mailbox._sync()
                        if done:
                            break

                    # if we catch an EOF error we continue
                    except imaplib.IMAP4.abort:
                        logger.exception("Caught EOF error while syncing")
                        try:
                            imap.logout()
                        except Exception:
                            logger.exception(
                                "Failure while logging out due to EOF bug")
                        continue
                    # if we catch any other type of exception we abort to avoid infinite loop
                    except Exception:
                        logger.critical("Failure while initially syncing")
                        logger.exception("Failure while initially syncing")
                        raise

                logger.info("Mailbox sync done: %s" % (imapAccount.email))

                # after sync, logout to prevent multi-connection issue
                imap.logout()

                imapAccount.is_initialized = True
                imapAccount.save()

                site = Site.objects.get_current()
                send_email(
                    "Your YoUPS account is ready!", "no-reply@" + BASE_URL,
                    imapAccount.email,
                    "Start writing your automation rule here! %s://%s" %
                    (PROTOCOL, site.domain))

                logger.info('Register done for %s', imapAccount.email)
            except ImapAccount.DoesNotExist:
                imapAccount.is_initialized = False
                imapAccount.save()
                logger.exception(
                    "syncing fails Remove periodic tasks. imap_account not exist %s"
                    % (imapAccount.email))

            except Exception as e:
                logger.exception(
                    "User inbox syncing fails %s. Stop syncing %s" %
                    (imapAccount.email, e))
Пример #5
0
def interpret(mailbox, mode):
    """This function executes users' code.  

        Args:
            mailbox (Mailbox): user's mailbox
            mode (MailbotMode or None): current mode. if mode is null, it will bypass executing user's code and just print logs
            is_simulate (boolean): if True, it looks into extra_info to test run user's code
            extra_info (dict): it includes code, which is to be test ran, and msg-id, which is a id field of MessageSchema 

    """
    # type: (MailBox, MailbotMode, bool) -> t.Dict[t.AnyStr, t.Any]

    from schema.youps import EmailRule, MailbotMode

    # set up the default result
    res = {'status': True, 'imap_error': False, 'imap_log': ""}

    # assert we actually got a mailbox
    assert isinstance(mailbox, MailBox)
    # assert the mode is the mailboat mode
    # assert isinstance(mode, MailbotMode)
    assert mailbox.new_message_handler is not None

    def on_message_arrival(func):
        mailbox.new_message_handler += func

    # get the logger for user output
    userLogger = logging.getLogger('youps.user')  # type: logging.Logger
    # get the stream handler associated with the user output
    userLoggerStreamHandlers = filter(
        lambda h: isinstance(h, logging.StreamHandler), userLogger.handlers)
    userLoggerStream = userLoggerStreamHandlers[
        0].stream if userLoggerStreamHandlers else None
    assert userLoggerStream is not None

    # create a string buffer to store stdout
    user_std_out = StringIO()

    new_log = {}

    # execute user code
    try:
        # set the stdout to a string
        sys.stdout = user_std_out

        # set the user logger to
        userLoggerStream = user_std_out

        from schema.youps import FolderSchema

        # define the variables accessible to the user
        user_environ = {
            'create_draft':
            mailbox.create_draft,
            'create_folder':
            mailbox.create_folder,
            'get_email_mode':
            mailbox.get_email_mode,
            'set_email_mode':
            mailbox.set_email_mode,
            'send':
            mailbox.send,
            'handle_on_message':
            lambda f: mailbox.new_message_handler.handle(f),
            'handle_on_flag_added':
            lambda f: mailbox.added_flag_handler.handle(f),
            'handle_on_flag_removed':
            lambda f: mailbox.removed_flag_handler.handle(f),
            'handle_on_deadline':
            lambda f: mailbox.deadline_handler.handle(f),
            'Calendar':
            MyCalendar,
        }

        # iterate through event queue
        for event_data in mailbox.event_data_list:
            # Iterate through email rule at the current mode
            # TODO maybe use this instead of mode.rules
            if mode is None:
                continue

            for rule in EmailRule.objects.filter(mode=mode):
                is_fired = False
                copy_msg = {}
                copy_msg["timestamp"] = str(
                    datetime.datetime.now().strftime("%m/%d %H:%M:%S,%f"))

                assert isinstance(rule, EmailRule)

                # TODO why the reassignment of valid folders
                valid_folders = []

                code = rule.code

                # logger.info(code)

                # add the user's functions to the event handlers
                # basically at the end of the user's code we need to attach the user's code to
                # the event
                # user code strings can be found at http_handler/static/javascript/youps/login_imap.js ~ line 300
                # our handlers are in mailbox and the user environment
                if rule.type.startswith("new-message"):
                    valid_folders = FolderSchema.objects.filter(
                        imap_account=mailbox._imap_account, rules=rule)
                    code = code + "\nhandle_on_message(on_message)"
                elif rule.type == "flag-change":
                    code = code + "\nhandle_on_flag_added(on_flag_added)"
                    code = code + "\nhandle_on_flag_removed(on_flag_removed)"
                elif rule.type.startswith("deadline"):
                    valid_folders = FolderSchema.objects.filter(
                        imap_account=mailbox._imap_account).filter(
                            is_selectable=True)
                    code = code + "\nhandle_on_deadline(on_deadline)"
                # else:
                #     continue
                #     # some_handler or something += repeat_every

                try:
                    # execute the user's code
                    # exec cant register new function (e.g., on_message_arrival) when there is a user_env
                    # logger.exception(rule.id)
                    # logger.exception(code)
                    exec(code, user_environ)

                    # TODO this should be cleaned up. accessing class name is ugly and this is very wet (Not DRY)
                    if event_data.message._schema.folder in valid_folders:
                        event_class_name = type(event_data).__name__
                        if (event_class_name == "MessageArrivalData" and rule.type =="new-message") or \
                                (event_class_name == "NewMessageDataScheduled" and rule.type.startswith("new-message-")):
                            is_fired = True
                            event_data.fire_event(mailbox.new_message_handler)
                        if (event_class_name == "NewFlagsData"
                                and rule.type == "flag-change"):
                            is_fired = True
                            event_data.fire_event(mailbox.added_flag_handler)
                        if (event_class_name == "RemovedFlagsData"
                                and rule.type == "flag-change"):
                            is_fired = True
                            event_data.fire_event(mailbox.removed_flag_handler)
                        if (event_class_name == "NewMessageDataDue"
                                and rule.type.startswith("deadline")):
                            is_fired = True
                            event_data.fire_event(mailbox.deadline_handler)

                        if is_fired:
                            logger.debug(
                                "firing %s %s" %
                                (rule.name, event_data.message.subject))

                except Exception as e:
                    # Get error message for users if occurs
                    # print out error messages for user
                    # if len(inspect.trace()) < 2:
                    #     logger.exception("System error during running user code")
                    # else:

                    exc_type, exc_obj, exc_tb = sys.exc_info()
                    logger.exception("failure running user %s code" %
                                     mailbox._imap_account.email)
                    error_msg = str(e) + traceback.format_tb(exc_tb)[-1]
                    try:
                        send_email(
                            "failure running user %s code" %
                            mailbox._imap_account.email,
                            "*****@*****.**",
                            "*****@*****.**", error_msg.decode('utf-8'),
                            error_msg.decode('utf-8'))
                    except Exception:
                        logger.exception("Can't send error emails to admin :P")
                    copy_msg["log"] = error_msg
                    copy_msg["error"] = True
                finally:
                    if is_fired:
                        copy_msg.update(print_execution_log(
                            event_data.message))
                        logger.debug("handling fired %s %s" %
                                     (rule.name, event_data.message.subject))
                        copy_msg["trigger"] = rule.name or (
                            rule.type.replace("_", " ") + " untitled")

                        copy_msg["log"] = "%s\n%s" % (user_std_out.getvalue(),
                                                      copy_msg["log"] if "log"
                                                      in copy_msg else "")

                        new_log[copy_msg["timestamp"]] = copy_msg

                    # flush buffer
                    user_std_out = StringIO()

                    # set the stdout to a string
                    sys.stdout = user_std_out

                    # set the user logger to
                    userLoggerStream = user_std_out

                mailbox.new_message_handler.removeAllHandles()
                mailbox.added_flag_handler.removeAllHandles()
                mailbox.deadline_handler.removeAllHandles()

        # Task manager
        for task in TaskManager.objects.filter(
                imap_account=mailbox._imap_account):
            now = timezone.now().replace(microsecond=0)
            is_fired = False
            logger.critical("%s %s" % (task.date, now))
            if task.date > now:
                continue

            new_msg = {}

            new_msg["timestamp"] = str(
                datetime.datetime.now().strftime("%m/%d %H:%M:%S,%f"))
            new_msg["type"] = "see-later"

            copy_msg = copy.deepcopy(new_msg)
            copy_msg["timestamp"] = str(
                datetime.datetime.now().strftime("%m/%d %H:%M:%S,%f"))

            try:
                if new_msg["type"] == "see-later":
                    user_environ = json.loads(task.email_rule.code) if len(
                        task.email_rule.code) else {}

                    msg_schema = MessageSchema.objects.get(
                        base_message__id=user_environ["base_message_id"],
                        folder__name=user_environ["hide_in"])
                    mailbox._imap_client.select_folder(user_environ["hide_in"])
                    msg = Message(msg_schema, mailbox._imap_client)
                    msg.move(user_environ["current_folder"])
                elif new_msg["type"] == "remind":
                    user_environ = json.loads(task.email_rule.code) if len(
                        task.email_rule.code) else {}

                    for msg_schema in task.base_message.messages.all():
                        mailbox._imap_client.select_folder(
                            user_environ["hide_in"])
                        msg = Message(msg_schema, mailbox._imap_client)
                        msg.forward(user_environ["note"])
                        break
                else:
                    # TODO replace with Task schema and make it more extensible
                    # TODO task; id, type="hide-show", string='{}'
                    pass
                is_fired = True
            except Exception as e:
                logger.critical("Error during task managing %s " % e)
                copy_msg["error"] = True
                exc_type, exc_obj, exc_tb = sys.exc_info()
                logger.info(e)
                logger.debug(exc_obj)
                # logger.info(traceback.print_exception())

                # TODO find keyword 'in on_message' or on_flag_change
                logger.info(traceback.format_tb(exc_tb))
                logger.info(sys.exc_info())

                copy_msg["log"] = str(e) + traceback.format_tb(exc_tb)[-1]
                task.delete()
            finally:
                if is_fired:
                    copy_msg["trigger"] = task.email_rule.name
                    task.delete()

                    # copy_msg["log"] = "%s\n%s" % (user_std_out.getvalue(), copy_msg["log"] )

                    # new_log[copy_msg["timestamp"]] = copy_msg

                # flush buffer
                user_std_out = StringIO()

                # set the stdout to a string
                sys.stdout = user_std_out

                # set the user logger to
                userLoggerStream = user_std_out

    except Exception as e:
        res['status'] = False
        logger.exception("failure running user %s code" %
                         mailbox._imap_account.email)
    finally:
        # set the stdout back to what it was
        sys.stdout = sys.__stdout__
        userLoggerStream = sys.__stdout__

        # if it is simulate don't save to db
        if mailbox.is_simulate:
            logger.debug(res)

        # save logs to db
        else:
            # logger.info(new_log)
            res['imap_log'] = new_log

        user_std_out.close()
        return res