def __init__(self, mbox, mailid, request, dformat="DISPLAYMODE", addrfull=False, links=0): mformat = parameters.get_user(request.user, "DISPLAYMODE") self.dformat = parameters.get_user(request.user, dformat) self.headers = [] self.attachments = {} self.imapc = get_imapconnector(request) msg = self.imapc.fetchmail(mbox, mailid, readonly=False, headers=self.headers_as_list) self.mbox = mbox self.mailid = mailid headers = msg["BODY[HEADER.FIELDS (%s)]" % self.headers_as_text] fallback_fmt = "html" if self.dformat == "plain" else "plain" self.bs = BodyStructure(msg["BODYSTRUCTURE"]) data = None mformat = self.dformat if self.bs.contents.has_key(self.dformat) else fallback_fmt if len(self.bs.contents): bodyc = u"" for part in self.bs.contents[mformat]: pnum = part["pnum"] data = self.imapc._cmd("FETCH", mailid, "(BODY.PEEK[%s])" % pnum) content = decode_payload(part["encoding"], data[int(mailid)]["BODY[%s]" % pnum]) charset = self._find_content_charset(part) if charset is not None: try: content = content.decode(charset) except (UnicodeDecodeError, LookupError): result = chardet.detect(content) content = content.decode(result["encoding"]) bodyc += content self._fetch_inlines() self.body = getattr(self, "viewmail_%s" % mformat)(bodyc, links=links) else: self.body = None self._find_attachments() msg = email.message_from_string(headers) for hdr in self.headernames: label = hdr[0] name = hdr[0] if not name in msg.keys(): name = name.upper() if not name in msg.keys(): continue try: key = re.sub("-", "_", name).lower() value = getattr(IMAPheader, "parse_%s" % key)(msg[name], full=addrfull) except AttributeError: value = msg[name] if hdr[1]: self.headers += [{"name": label, "value": value}] try: label = re.sub("-", "_", label) setattr(self, label, value) except: pass
def send_mail(request, form, posturl=None): """Email verification and sending. If the form does not present any error, a new MIME message is constructed. Then, a connection is established with the defined SMTP server and the message is finally sent. :param request: a Request object :param posturl: the url to post the message form to :return: a 2-uple (True|False, HttpResponse) """ if not form.is_valid(): editormode = parameters.get_user(request.user, "EDITOR") listing = _render_to_string( request, "modoboa_webmail/compose.html", { "form": form, "noerrors": True, "body": form.cleaned_data.get("body", "").strip(), "posturl": posturl }) return False, dict(status="ko", listing=listing, editor=editormode) msg = form.to_msg(request) rcpts = prepare_addresses(form.cleaned_data["to"], "envelope") for hdr in ["cc", "cci"]: if form.cleaned_data[hdr]: msg[hdr.capitalize()] = prepare_addresses(form.cleaned_data[hdr]) rcpts += prepare_addresses(form.cleaned_data[hdr], "envelope") try: secmode = parameters.get_admin("SMTP_SECURED_MODE") if secmode == "ssl": s = smtplib.SMTP_SSL(parameters.get_admin("SMTP_SERVER"), int(parameters.get_admin("SMTP_PORT"))) else: s = smtplib.SMTP(parameters.get_admin("SMTP_SERVER"), int(parameters.get_admin("SMTP_PORT"))) if secmode == "starttls": s.starttls() except Exception as text: raise WebmailInternalError(str(text)) if parameters.get_admin("SMTP_AUTHENTICATION") == "yes": try: s.login(request.user.username, get_password(request)) except smtplib.SMTPException as err: raise WebmailInternalError(str(err)) try: s.sendmail(request.user.email, rcpts, msg.as_string()) s.quit() except smtplib.SMTPException as err: raise WebmailInternalError(str(err)) sentfolder = parameters.get_user(request.user, "SENT_FOLDER") get_imapconnector(request).push_mail(sentfolder, msg) clean_attachments(request.session["compose_mail"]["attachments"]) del request.session["compose_mail"] return True, {}
def send_mail(request, form, posturl=None): """Email verification and sending. If the form does not present any error, a new MIME message is constructed. Then, a connection is established with the defined SMTP server and the message is finally sent. :param request: a Request object :param posturl: the url to post the message form to :return: a 2-uple (True|False, HttpResponse) """ if not form.is_valid(): editormode = parameters.get_user(request.user, "EDITOR") listing = _render_to_string( request, "webmail/compose.html", {"form": form, "noerrors": True, "body": form.cleaned_data.get("body", "").strip(), "posturl": posturl} ) return False, dict(status="ko", listing=listing, editor=editormode) msg = form.to_msg(request) rcpts = prepare_addresses(form.cleaned_data["to"], "envelope") for hdr in ["cc", "cci"]: if form.cleaned_data[hdr]: msg[hdr.capitalize()] = prepare_addresses(form.cleaned_data[hdr]) rcpts += prepare_addresses(form.cleaned_data[hdr], "envelope") try: secmode = parameters.get_admin("SMTP_SECURED_MODE") if secmode == "ssl": s = smtplib.SMTP_SSL(parameters.get_admin("SMTP_SERVER"), int(parameters.get_admin("SMTP_PORT"))) else: s = smtplib.SMTP(parameters.get_admin("SMTP_SERVER"), int(parameters.get_admin("SMTP_PORT"))) if secmode == "starttls": s.starttls() except Exception as text: raise WebmailInternalError(str(text)) if parameters.get_admin("SMTP_AUTHENTICATION") == "yes": try: s.login(request.user.username, get_password(request)) except smtplib.SMTPException as err: raise WebmailInternalError(str(err)) try: s.sendmail(request.user.email, rcpts, msg.as_string()) s.quit() except smtplib.SMTPException as err: raise WebmailInternalError(str(err)) sentfolder = parameters.get_user(request.user, "SENT_FOLDER") get_imapconnector(request).push_mail(sentfolder, msg) clean_attachments(request.session["compose_mail"]["attachments"]) del request.session["compose_mail"] return True, {}
def getmboxes(self, user, topmailbox='', until_mailbox=None, unseen_messages=True): """Returns a list of mailboxes for a particular user By default, only the first level of mailboxes under ``topmailbox`` is returned. If ``until_mailbox`` is specified, all levels needed to access this mailbox will be returned. :param user: a ``User`` instance :param topmailbox: the mailbox where to start in the tree :param until_mailbox: the deepest needed mailbox :param unseen_messages: include unseen messages counters or not :return: a list """ if topmailbox: md_mailboxes = [] else: md_mailboxes = [{ "name": "INBOX", "class": "icon-inbox" }, { "name": parameters.get_user(user, "DRAFTS_FOLDER"), "class": "icon-file" }, { "name": 'Junk', "class": "icon-fire" }, { "name": parameters.get_user(user, "SENT_FOLDER"), "class": "icon-envelope" }, { "name": parameters.get_user(user, "TRASH_FOLDER"), "class": "icon-trash" }] if until_mailbox: name, parent = separate_mailbox(until_mailbox, self.hdelimiter) if parent: until_mailbox = parent self._listmboxes(topmailbox, md_mailboxes, until_mailbox) if unseen_messages: for mb in md_mailboxes: if not "send_status" in mb: continue del mb["send_status"] key = "path" if "path" in mb else "name" if not mb.get("removed", False): count = self.unseen_messages(mb[key]) if count == 0: continue mb["unseen"] = count return md_mailboxes
def getfs(request, name): sc = SieveClient(user=request.user.username, password=request.session["password"]) editormode = parameters.get_user(request.user, "EDITOR_MODE") error = None try: content = sc.getscript(name, format=editormode) except SieveClientError as e: error = str(e) else: if content is None: error = _("Failed to retrieve filters set") if error is not None: return ajax_response(request, "ko", respmsg=error) if editormode == "raw": htmlcontent = render_to_string("modoboa_sievefilters/rawfilter.html", dict( name=name, scriptcontent=content )) else: htmlcontent = render_to_string("modoboa_sievefilters/guieditor.html", dict( fs=content )) menu = '<ul id="fsetmenu" class="nav nav-sidebar"><li class="nav-header">%s</li>%s</ul>' % \ (_("Actions"), fset_menu(editormode, name)) resp = dict(menu=menu, content=htmlcontent) return render_to_json_response(resp)
def empty(request, name): if name != parameters.get_user(request.user, "TRASH_FOLDER"): raise WebmailError(_("Invalid request")) get_imapconnector(request).empty(name) content = "<div class='alert alert-info'>%s</div>" % _("Empty mailbox") return ajax_simple_response( dict(status="ok", listing=content, mailbox=name))
def listmailbox(request, defmailbox="INBOX", update_session=True): """Mailbox content listing Return a list of messages contained in the specified mailbox. The number of elements returned depends on the ``MESSAGES_PER_PAGE`` parameter. (user preferences) :param request: a ``Request`` object :param defmailbox: the default mailbox (when not present inside request arguments) :return: a dictionnary """ mbox = request.GET.get("mbox", defmailbox) if update_session: set_nav_params(request) request.session["mbox"] = mbox lst = ImapListing(request.user, request.session["password"], baseurl="?action=listmailbox&mbox=%s&" % mbox, folder=mbox, elems_per_page=int( parameters.get_user(request.user, "MESSAGES_PER_PAGE")), **request.session["navparams"]) return lst.render(request, request.session["pageid"])
def extra_js(user): return [ """function autoreply_cb() { $('#id_untildate').datepicker({format: 'yyyy-mm-dd', language: '%s'}); } """ % parameters.get_user(user, "LANG", app="general") ]
def _listing(request): if not request.user.is_superuser and request.user.group != 'SimpleUsers': if not Domain.objects.get_for_admin(request.user).count(): return empty_quarantine(request) navparams = QuarantineNavigationParameters(request) navparams.store() lst = SQLlisting( request.user, navparams=navparams, elems_per_page=int( parameters.get_user(request.user, "MESSAGES_PER_PAGE") ) ) page = lst.paginator.getpage(navparams.get('page')) if not page: return empty_quarantine(request) content = lst.fetch(request, page.id_start, page.id_stop) ctx = getctx( "ok", listing=content, paginbar=pagination_bar(page), page=page.number ) if request.session.get('location', 'listing') != 'listing': ctx['menu'] = quar_menu() request.session['location'] = 'listing' return render_to_json_response(ctx)
def getfs(request, name): sc = SieveClient(user=request.user.username, password=request.session["password"]) editormode = parameters.get_user(request.user, "EDITOR_MODE") error = None try: content = sc.getscript(name, format=editormode) except SieveClientError as e: error = str(e) else: if content is None: error = _("Failed to retrieve filters set") if error is not None: return ajax_response(request, "ko", respmsg=error) if editormode == "raw": htmlcontent = render_to_string("modoboa_sievefilters/rawfilter.html", dict(name=name, scriptcontent=content)) else: htmlcontent = render_to_string("modoboa_sievefilters/guieditor.html", dict(fs=content)) menu = '<ul id="fsetmenu" class="nav nav-sidebar"><li class="nav-header">%s</li>%s</ul>' % \ (_("Actions"), fset_menu(editormode, name)) resp = dict(menu=menu, content=htmlcontent) return render_to_json_response(resp)
def listmailbox_menu(selection, folder, user): entries = [{ "name": "compose", "url": "compose", "img": "icon-edit", "label": _("New message"), "class": "btn" }, { "name": "mark", "label": _("Mark messages"), "class": "btn", "menu": [{ "name": "mark-read", "label": _("As read"), "url": reverse(webmail.views.mark, args=[folder]) + "?status=read" }, { "name": "mark-unread", "label": _("As unread"), "url": reverse(webmail.views.mark, args=[folder]) + "?status=unread" }] }, { "name": "actions", "label": _("Actions"), "class": "btn", "menu": [{ "name": "compress", "label": _("Compress folder"), "url": "compact/%s/" % folder }] }] if folder == parameters.get_user(user, "TRASH_FOLDER"): entries[2]["menu"] += [{ "name": "empty", "label": _("Empty folder"), "url": reverse(webmail.views.empty, args=[folder]) }] searchbar = render_to_string('common/email_searchbar.html', {"STATIC_URL": settings.STATIC_URL}) return render_to_string( 'common/buttons_list.html', dict(selection=selection, entries=entries, extracontent=searchbar, user=user, css="nav"))
def _listing(request): flt = None rcptfilter = None msgs = None if not request.user.is_superuser and request.user.group != 'SimpleUsers': if not Domain.objects.get_for_admin(request.user).count(): return empty_quarantine(request) order = request.GET.get("order", "-date") if not "navparams" in request.session: request.session["navparams"] = {} request.session["navparams"]["order"] = order parse_search_parameters(request) if "pattern" in request.session: criteria = request.session["criteria"] if criteria == "both": criteria = "from_addr,subject,to" for c in criteria.split(","): if c == "from_addr": nfilter = Q( mail__from_addr__contains=request.session["pattern"]) elif c == "subject": nfilter = Q(mail__subject__contains=request.session["pattern"]) elif c == "to": rcptfilter = request.session["pattern"] continue else: raise Exception("unsupported search criteria %s" % c) flt = nfilter if flt is None else flt | nfilter msgs = get_wrapper().get_mails(request, rcptfilter) if "page" in request.GET: request.session["page"] = request.GET["page"] pageid = int(request.session["page"]) else: if "page" in request.session: del request.session["page"] pageid = 1 lst = SQLlisting(request.user, msgs, flt, navparams=request.session["navparams"], elems_per_page=int( parameters.get_user(request.user, "MESSAGES_PER_PAGE"))) page = lst.paginator.getpage(pageid) if not page: return empty_quarantine(request) content = lst.fetch(request, page.id_start, page.id_stop) navbar = lst.render_navbar(page, "listing/?") ctx = getctx("ok", listing=content, navbar=navbar, menu=quar_menu(request.user)) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
def empty(request, name): if name != parameters.get_user(request.user, "TRASH_FOLDER"): raise BadRequest(_("Invalid request")) get_imapconnector(request).empty(name) content = "<div class='alert alert-info'>%s</div>" % _("Empty mailbox") return render_to_json_response({ 'listing': content, 'mailbox': name })
def getfs(request, name): sc = SieveClient(user=request.user.username, password=request.session["password"]) editormode = parameters.get_user(request.user, "EDITOR_MODE") error = None try: content = sc.getscript(name, format=editormode) except SieveClientError, e: error = str(e)
def getmboxes( self, user, topmailbox='', until_mailbox=None, unseen_messages=True): """Returns a list of mailboxes for a particular user By default, only the first level of mailboxes under ``topmailbox`` is returned. If ``until_mailbox`` is specified, all levels needed to access this mailbox will be returned. :param user: a ``User`` instance :param topmailbox: the mailbox where to start in the tree :param until_mailbox: the deepest needed mailbox :param unseen_messages: include unseen messages counters or not :return: a list """ if topmailbox: md_mailboxes = [] else: md_mailboxes = [ {"name": "INBOX", "class": "fa fa-inbox"}, {"name": parameters.get_user(user, "DRAFTS_FOLDER"), "class": "fa fa-file"}, {"name": 'Junk', "class": "fa fa-fire"}, {"name": parameters.get_user(user, "SENT_FOLDER"), "class": "fa fa-envelope"}, {"name": parameters.get_user(user, "TRASH_FOLDER"), "class": "fa fa-trash"} ] if until_mailbox: name, parent = separate_mailbox(until_mailbox, self.hdelimiter) if parent: until_mailbox = parent self._listmboxes(topmailbox, md_mailboxes, until_mailbox) if unseen_messages: for mb in md_mailboxes: if "send_status" not in mb: continue del mb["send_status"] key = "path" if "path" in mb else "name" if not mb.get("removed", False): count = self.unseen_messages(mb[key]) if count == 0: continue mb["unseen"] = count return md_mailboxes
def empty(request, name): if name != parameters.get_user(request.user, "TRASH_FOLDER"): raise WebmailError(_("Invalid request")) get_imapconnector(request).empty(name) content = "<div class='alert alert-info'>%s</div>" % _("Empty mailbox") return ajax_simple_response(dict( status="ok", listing=content, mailbox=name ))
def extra_js(user): return [ """function autoreply_cb() { $('#id_untildate').datepicker({format: 'yyyy-mm-dd', language: '%s'}); } """ % parameters.get_user(user, "LANG", app="core") ]
def listmailbox_menu(selection, folder, user): """The menu of the listmailbox action.""" entries = [ { "name": "totrash", "title": _("Delete"), "class": "btn-danger", "img": "fa fa-trash", "url": reverse("modoboa_webmail:mail_delete") }, { "name": "actions", "label": _("Actions"), "class": "btn btn-default", "menu": [ { "name": "mark-read", "label": _("Mark as read"), "url": u"{0}?status=read".format( reverse("modoboa_webmail:mail_mark", args=[folder])) }, { "name": "mark-unread", "label": _("Mark as unread"), "url": u"{0}?status=unread".format( reverse("modoboa_webmail:mail_mark", args=[folder])) }, ] }, ] if folder == parameters.get_user(user, "TRASH_FOLDER"): entries[0]["class"] += " disabled" entries[1]["menu"] += [{ "name": "empty", "label": _("Empty folder"), "url": u"{0}?name={1}".format(reverse("modoboa_webmail:trash_empty"), folder) }] return render_to_string( 'modoboa_webmail/main_action_bar.html', { 'selection': selection, 'entries': entries, 'user': user, 'css': "nav", 'STATIC_URL': settings.STATIC_URL })
def delete(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) if mbox is None or mailid is None: raise WebmailError(_("Invalid request")) mbc = get_imapconnector(request) mbc.move(mailid, mbox, parameters.get_user(request.user, "TRASH_FOLDER")) resp = dict(status="ok") return ajax_simple_response(resp)
def extra_js(user): return ["""function autoreply_cb() { $('.datefield').datetimepicker({ format: 'YYYY-MM-DD hh:mm:ss', language: '%(lang)s' }); } """ % {'lang': parameters.get_user(user, "LANG", app="core")} ]
def dologin(request): """Try to authenticate.""" error = None if request.method == "POST": form = LoginForm(request.POST) if form.is_valid(): logger = logging.getLogger('modoboa.auth') user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"]) if user and user.is_active: login(request, user) if not form.cleaned_data["rememberme"]: request.session.set_expiry(0) request.session["django_language"] = \ parameters.get_user(request.user, "LANG") logger.info( _("User '%s' successfully logged in" % user.username)) events.raiseEvent("UserLogin", request, form.cleaned_data["username"], form.cleaned_data["password"]) nextlocation = request.POST.get("next", None) if nextlocation is None or nextlocation == "None": if user.group == "SimpleUsers": nextlocation = reverse("topredirection") else: # FIXME nextlocation = reverse("modoboa_admin:domain_list") return HttpResponseRedirect(nextlocation) error = _( "Your username and password didn't match. Please try again.") logger.warning( "Failed connection attempt from '%(addr)s' as user '%(user)s'" % { "addr": request.META["REMOTE_ADDR"], "user": form.cleaned_data["username"] }) nextlocation = request.POST.get("next", None) httpcode = 401 else: form = LoginForm() nextlocation = request.GET.get("next", None) httpcode = 200 return HttpResponse(_render_to_string( request, "registration/login.html", { "form": form, "error": error, "next": nextlocation, "annoucements": events.raiseQueryEvent("GetAnnouncement", "loginpage") }), status=httpcode)
def __init__(self, user, password, **kwargs): self.user = user self.mbc = IMAPconnector(user=user.username, password=password) if kwargs.has_key("pattern"): self.parse_search_parameters(kwargs["criteria"], kwargs["pattern"]) else: self.mbc.criterions = [] super(ImapListing, self).__init__(**kwargs) self.extravars["refreshrate"] = int(parameters.get_user(user, "REFRESH_INTERVAL")) * 1000
def empty(request): """Empty the trash folder.""" name = request.GET.get("name", None) if name != parameters.get_user(request.user, "TRASH_FOLDER"): raise BadRequest(_("Invalid request")) get_imapconnector(request).empty(name) content = "<div class='alert alert-info'>%s</div>" % _("Empty mailbox") return render_to_json_response({ 'listing': content, 'mailbox': name, 'pages': [1] })
def _listing(request): flt = None rcptfilter = None msgs = None if not request.user.is_superuser and request.user.group != 'SimpleUsers': if not Domain.objects.get_for_admin(request.user).count(): return empty_quarantine(request) order = request.GET.get("order", "-date") if not "navparams" in request.session: request.session["navparams"] = {} request.session["navparams"]["order"] = order parse_search_parameters(request) if "pattern" in request.session: criteria = request.session["criteria"] if criteria == "both": criteria = "from_addr,subject,to" for c in criteria.split(","): if c == "from_addr": nfilter = Q(mail__from_addr__contains=request.session["pattern"]) elif c == "subject": nfilter = Q(mail__subject__contains=request.session["pattern"]) elif c == "to": rcptfilter = request.session["pattern"] continue else: raise Exception("unsupported search criteria %s" % c) flt = nfilter if flt is None else flt | nfilter msgs = get_wrapper().get_mails(request, rcptfilter) if "page" in request.GET: request.session["page"] = request.GET["page"] pageid = int(request.session["page"]) else: if "page" in request.session: del request.session["page"] pageid = 1 lst = SQLlisting( request.user, msgs, flt, navparams=request.session["navparams"], elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")) ) page = lst.paginator.getpage(pageid) if not page: return empty_quarantine(request) content = lst.fetch(request, page.id_start, page.id_stop) navbar = lst.render_navbar(page, "listing/?") ctx = getctx("ok", listing=content, navbar=navbar, menu=quar_menu(request.user)) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
def __init__(self, user, password, **kwargs): self.user = user self.mbc = IMAPconnector(user=user.username, password=password) if kwargs.has_key("pattern"): self.parse_search_parameters(kwargs["criteria"], kwargs["pattern"]) else: self.mbc.criterions = [] super(ImapListing, self).__init__(**kwargs) self.extravars["refreshrate"] = \ int(parameters.get_user(user, "REFRESH_INTERVAL")) * 1000
def delete(request): mbox = request.GET.get("mbox", None) selection = request.GET.getlist("selection[]", None) if mbox is None or selection is None: raise BadRequest(_("Invalid request")) selection = [item for item in selection if item.isdigit()] mbc = get_imapconnector(request) mbc.move(",".join(selection), mbox, parameters.get_user(request.user, "TRASH_FOLDER")) count = len(selection) message = ungettext("%(count)d message deleted", "%(count)d messages deleted", count) % {"count": count} return render_to_json_response(message)
def _listing(request): flt = None rcptfilter = None msgs = None if not request.user.is_superuser and request.user.group != 'SimpleUsers': if not Domain.objects.get_for_admin(request.user).count(): return empty_quarantine(request) navparams = QuarantineNavigationParameters(request) navparams.store() pattern = navparams.get('pattern', '') if pattern: criteria = navparams.get('criteria') if criteria == "both": criteria = "from_addr,subject,to" for c in criteria.split(","): if c == "from_addr": nfilter = Q(mail__from_addr__contains=pattern) elif c == "subject": nfilter = Q(mail__subject__contains=pattern) elif c == "to": rcptfilter = pattern continue else: raise BadRequest("unsupported search criteria %s" % c) flt = nfilter if flt is None else flt | nfilter msgtype = navparams.get('msgtype', None) if msgtype is not None: nfilter = Q(mail__msgrcpt__content=msgtype) flt = flt | nfilter if flt is not None else nfilter msgs = get_wrapper().get_mails(request, rcptfilter) page = navparams.get('page') lst = SQLlisting(request.user, msgs, flt, navparams=request.session["quarantine_navparams"], elems_per_page=int( parameters.get_user(request.user, "MESSAGES_PER_PAGE"))) page = lst.paginator.getpage(page) if not page: return empty_quarantine(request) content = lst.fetch(request, page.id_start, page.id_stop) ctx = getctx("ok", listing=content, paginbar=pagination_bar(page), page=page.number) if request.session.get('location', 'listing') != 'listing': ctx['menu'] = quar_menu() request.session['location'] = 'listing' return render_to_json_response(ctx)
def extra_js(user): return [ """function autoreply_cb() { $('.datefield').datetimepicker({ format: 'YYYY-MM-DD hh:mm:ss', language: '%(lang)s' }); } """ % { 'lang': parameters.get_user(user, "LANG", app="core") } ]
def extra_js(user): return ["""function autoreply_cb() { $('.datefield').datetimepicker({ format: 'yyyy-mm-dd hh:ii:ss', language: '%(lang)s', autoclose: true, todayHighlight: true, todayBtn: 'linked' }); } """ % {'lang': parameters.get_user(user, "LANG", app="core")} ]
def msg(self): """ """ if self._msg is None: self._msg = self.imapc.fetchmail(self.mbox, self.mailid, readonly=False, headers=self.headers_as_list) self.bs = BodyStructure(self._msg["BODYSTRUCTURE"]) self._find_attachments() if not self.dformat in ["plain", "html"]: self.dformat = parameters.get_user(self.request.user, self.dformat) fallback_fmt = "html" if self.dformat == "plain" else "plain" self.mformat = self.dformat if self.dformat in self.bs.contents else fallback_fmt return self._msg
def _listing(request): flt = None rcptfilter = None msgs = None if not request.user.is_superuser and request.user.group != 'SimpleUsers': if not Domain.objects.get_for_admin(request.user).count(): return empty_quarantine(request) navparams = QuarantineNavigationParameters(request) navparams.store() pattern = navparams.get('pattern', '') if pattern: criteria = navparams.get('criteria') if criteria == "both": criteria = "from_addr,subject,to" for c in criteria.split(","): if c == "from_addr": nfilter = Q(mail__from_addr__contains=pattern) elif c == "subject": nfilter = Q(mail__subject__contains=pattern) elif c == "to": rcptfilter = pattern continue else: raise BadRequest("unsupported search criteria %s" % c) flt = nfilter if flt is None else flt | nfilter msgtype = navparams.get('msgtype', None) if msgtype is not None: nfilter = Q(mail__msgrcpt__content=msgtype) flt = flt | nfilter if flt is not None else nfilter msgs = get_wrapper().get_mails(request, rcptfilter) page = navparams.get('page') lst = SQLlisting( request.user, msgs, flt, navparams=request.session["quarantine_navparams"], elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")) ) page = lst.paginator.getpage(page) if not page: return empty_quarantine(request) content = lst.fetch(request, page.id_start, page.id_stop) ctx = getctx( "ok", listing=content, paginbar=pagination_bar(page), page=page.number ) if request.session.get('location', 'listing') != 'listing': ctx['menu'] = quar_menu() request.session['location'] = 'listing' return render_to_json_response(ctx)
def dologin(request): error = None if request.method == "POST": form = LoginForm(request.POST) if form.is_valid(): logger = logging.getLogger("modoboa.auth") user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"]) if user and user.is_active: login(request, user) if not form.cleaned_data["rememberme"]: request.session.set_expiry(0) if request.user.has_mailbox: request.session["password"] = encrypt(form.cleaned_data["password"]) request.session["django_language"] = parameters.get_user(request.user, "LANG", app="general") logger.info(_("User '%s' successfully logged in" % user.username)) events.raiseEvent("UserLogin", request, form.cleaned_data["username"], form.cleaned_data["password"]) nextlocation = request.POST.get("next", None) if nextlocation is None or nextlocation == "None": if user.group == "SimpleUsers": nextlocation = reverse("modoboa.lib.webutils.topredirection") else: nextlocation = reverse("domains") return HttpResponseRedirect(nextlocation) error = _("Your username and password didn't match. Please try again.") logger.warning( "Failed connection attempt from '%(addr)s' as user '%(user)s'" % {"addr": request.META["REMOTE_ADDR"], "user": form.cleaned_data["username"]} ) nextlocation = request.POST.get("next", None) httpcode = 401 else: form = LoginForm() nextlocation = request.GET.get("next", None) httpcode = 200 return HttpResponse( _render_to_string( request, "registration/login.html", { "form": form, "error": error, "next": nextlocation, "annoucements": events.raiseQueryEvent("GetAnnouncement", "loginpage"), }, ), status=httpcode, )
def _build_msg(self, request): """Convert form's content to a MIME message. """ editormode = parameters.get_user(request.user, "EDITOR") msg = getattr(self, "_%s_msg" % editormode)() if request.session["compose_mail"]["attachments"]: wrapper = MIMEMultipart(_subtype="mixed") wrapper.attach(msg) for attdef in request.session["compose_mail"]["attachments"]: wrapper.attach(create_mail_attachment(attdef)) msg = wrapper return msg
def delete(request): mbox = request.GET.get("mbox", None) selection = request.GET.getlist("selection[]", None) if mbox is None or selection is None: raise WebmailError(_("Invalid request")) selection = [item for item in selection if item.isdigit()] mbc = get_imapconnector(request) mbc.move(",".join(selection), mbox, parameters.get_user(request.user, "TRASH_FOLDER")) count = len(selection) message = ungettext("%(count)d message deleted", "%(count)d messages deleted", count) % {"count": count} resp = dict(status="ok", respmsg=message) return ajax_simple_response(resp)
def listmailbox_menu(selection, folder, user): entries = [ {"name" : "compose", "url" : "compose", "img" : "icon-edit", "label" : _("New message"), "class" : "btn"}, {"name": "totrash", "label": "", "class": "", "img": "icon-trash", "url": reverse("modoboa.extensions.webmail.views.delete"), }, {"name" : "mark", "label" : _("Mark messages"), "class" : "btn", "menu" : [ {"name" : "mark-read", "label" : _("As read"), "url" : reverse(webmail.views.mark, args=[folder]) + "?status=read"}, {"name" : "mark-unread", "label" : _("As unread"), "url" : reverse(webmail.views.mark, args=[folder]) + "?status=unread"} ] }, {"name" : "actions", "label" : _("Actions"), "class" : "btn", "menu" : [ {"name" : "compress", "label" : _("Compress folder"), "url" : "compact/%s/" % folder} ] }, ] if folder == parameters.get_user(user, "TRASH_FOLDER"): entries[1]["class"] += " disabled" entries[3]["menu"] += [ {"name" : "empty", "label" : _("Empty folder"), "url" : reverse(webmail.views.empty, args=[folder])} ] searchbar = render_to_string('common/email_searchbar.html', { "STATIC_URL" : settings.STATIC_URL }) return render_to_string('common/buttons_list.html', dict( selection=selection, entries=entries, extracontent=searchbar, user=user, css="nav" ))
def extra_js(user): return [ """function autoreply_cb() { $('.datefield').datetimepicker({ format: 'yyyy-mm-dd hh:ii:ss', language: '%(lang)s', autoclose: true, todayHighlight: true, todayBtn: 'linked' }); } """ % { 'lang': parameters.get_user(user, "LANG", app="core") } ]
def delete(request): mbox = request.GET.get("mbox", None) selection = request.GET.getlist("selection[]", None) if mbox is None or selection is None: raise BadRequest(_("Invalid request")) selection = [item for item in selection if item.isdigit()] mbc = get_imapconnector(request) mbc.move(",".join(selection), mbox, parameters.get_user(request.user, "TRASH_FOLDER")) count = len(selection) message = ungettext("%(count)d message deleted", "%(count)d messages deleted", count) % { "count": count } return render_to_json_response(message)
def get_listing_pages(request, connector): """Return listing pages.""" paginator = Paginator( connector.messages_count(), int(parameters.get_user(request.user, "MESSAGES_PER_PAGE"))) page_id = int(connector.navparams.get("page")) page = paginator.getpage(page_id) if not page: return None pages = [page] if not page.has_next and page.has_previous and page.items < 40: pages = [paginator.getpage(page_id - 1)] + pages email_list = [] for page in pages: email_list += connector.fetch(page.id_start, page.id_stop) return {"pages": [page.number for page in pages], "rows": email_list}
def delete(request): mbox = request.GET.get("mbox", None) selection = request.GET.getlist("selection[]", None) if mbox is None or selection is None: raise WebmailError(_("Invalid request")) selection = [item for item in selection if item.isdigit()] mbc = get_imapconnector(request) mbc.move(",".join(selection), mbox, parameters.get_user(request.user, "TRASH_FOLDER")) count = len(selection) message = ungettext("%(count)d message deleted", "%(count)d messages deleted", count) % { "count": count } resp = dict(status="ok", respmsg=message) return ajax_simple_response(resp)
def get_listing_pages(request, connector): """Return listing pages.""" paginator = Paginator( connector.messages_count(), int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")) ) page_id = int(connector.navparams.get("page")) page = paginator.getpage(page_id) if not page: return None pages = [page] if not page.has_next and page.has_previous and page.items < 40: pages = [paginator.getpage(page_id - 1)] + pages email_list = [] for page in pages: email_list += connector.fetch(page.id_start, page.id_stop) return {"pages": [page.number for page in pages], "rows": email_list}
def msg(self): """ """ if self._msg is None: self._msg = self.imapc.fetchmail(self.mbox, self.mailid, readonly=False, headers=self.headers_as_list) self.bs = BodyStructure(self._msg['BODYSTRUCTURE']) self._find_attachments() if self.dformat not in ["plain", "html"]: self.dformat = parameters.get_user(self.request.user, self.dformat) fallback_fmt = "html" if self.dformat == "plain" else "plain" self.mformat = self.dformat \ if self.dformat in self.bs.contents else fallback_fmt return self._msg
def listmailbox_menu(selection, folder, user): entries = [ {"name": "compose", "url": "compose", "img": "icon-edit", "label": _("New message"), "class": "btn"}, {"name": "totrash", "label": "", "class": "", "img": "icon-trash", "url": reverse("modoboa.extensions.webmail.views.delete"), }, {"name": "mark", "label": _("Mark messages"), "class": "btn", "menu": [ {"name": "mark-read", "label": _("As read"), "url": reverse(webmail.views.mark, args=[folder]) + "?status=read"}, {"name": "mark-unread", "label": _("As unread"), "url": reverse(webmail.views.mark, args=[folder]) + "?status=unread"} ] }, {"name": "actions", "label": _("Actions"), "class": "btn", "menu": [ {"name": "compress", "label": _("Compress folder"), "url": "compact/%s/" % folder} ] }, ] if folder == parameters.get_user(user, "TRASH_FOLDER"): entries[1]["class"] += " disabled" entries[3]["menu"] += [ {"name": "empty", "label": _("Empty folder"), "url": reverse(webmail.views.empty, args=[folder])} ] return render_to_string('webmail/main_action_bar.html', { 'selection': selection, 'entries': entries, 'user': user, 'css': "nav", 'STATIC_URL': settings.STATIC_URL })
def listmailbox(request, defmailbox="INBOX", update_session=True): """Mailbox content listing. Return a list of messages contained in the specified mailbox. The number of elements returned depends on the ``MESSAGES_PER_PAGE`` parameter. (user preferences) :param request: a ``Request`` object :param defmailbox: the default mailbox (when not present inside request arguments) :return: a dictionnary """ navparams = WebmailNavigationParameters(request, defmailbox) previous_page_id = int(navparams["page"]) if "page" in navparams else None if update_session: navparams.store() mbox = navparams.get('mbox') page_id = int(navparams["page"]) mbc = get_imapconnector(request) mbc.parse_search_parameters( navparams.get("criteria"), navparams.get("pattern")) paginator = Paginator( mbc.messages_count(folder=mbox, order=navparams.get("order")), int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")) ) page = paginator.getpage(page_id) content = "" if page is not None: email_list = mbc.fetch(page.id_start, page.id_stop, mbox) content = _render_to_string( request, "modoboa_webmail/email_list.html", { "email_list": email_list, "page": page_id, "with_top_div": request.GET.get("scroll", "false") == "false" }) length = len(content) else: if page_id == 1: content = "<div class='alert alert-info'>{0}</div>".format( _("Empty mailbox") ) length = 0 if previous_page_id is not None: navparams["page"] = previous_page_id return {"listing": content, "length": length, "pages": [page_id]}
def viewmail(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) if mbox is None or mailid is None: raise WebmailError(_("Invalid request")) links = request.GET.get("links", None) if links is None: links = 1 if parameters.get_user(request.user, "ENABLE_LINKS") == "yes" else 0 else: links = int(links) url = reverse(getmailcontent) + "?mbox=%s&mailid=%s&links=%d" % \ (mbox, mailid, links) content = Template(""" <iframe src="{{ url }}" id="mailcontent"></iframe> """).render(Context({"url" : url})) return dict(listing=content, menuargs=dict(mail_id=mailid))
def listmailbox(request, defmailbox="INBOX", update_session=True): """Mailbox content listing. Return a list of messages contained in the specified mailbox. The number of elements returned depends on the ``MESSAGES_PER_PAGE`` parameter. (user preferences) :param request: a ``Request`` object :param defmailbox: the default mailbox (when not present inside request arguments) :return: a dictionnary """ navparams = WebmailNavigationParameters(request, defmailbox) previous_page_id = int(navparams["page"]) if "page" in navparams else None if update_session: navparams.store() mbox = navparams.get('mbox') page_id = int(navparams["page"]) mbc = get_imapconnector(request) mbc.parse_search_parameters(navparams.get("criteria"), navparams.get("pattern")) paginator = Paginator( mbc.messages_count(folder=mbox, order=navparams.get("order")), int(parameters.get_user(request.user, "MESSAGES_PER_PAGE"))) page = paginator.getpage(page_id) content = "" if page is not None: email_list = mbc.fetch(page.id_start, page.id_stop, mbox) content = _render_to_string( request, "webmail/email_list.html", { "email_list": email_list, "page": page_id, "with_top_div": request.GET.get("scroll", "false") == "false" }) length = len(content) else: if page_id == 1: content = "<div class='alert alert-info'>{0}</div>".format( _("Empty mailbox")) length = 0 if previous_page_id is not None: navparams["page"] = previous_page_id return {"listing": content, "length": length, "pages": [page_id]}
def render_compose(request, form, posturl, email=None, insert_signature=False): editor = parameters.get_user(request.user, "EDITOR") if email is None: body = "" textheader = "" else: body = email.body textheader = email.textheader if insert_signature: signature = EmailSignature(request.user) body += str(signature) randid = None if not "id" in request.GET: if "compose_mail" in request.session: clean_attachments(request.session["compose_mail"]["attachments"]) randid = set_compose_session(request) elif not "compose_mail" in request.session \ or request.session["compose_mail"]["id"] != request.GET["id"]: randid = set_compose_session(request) attachments = request.session["compose_mail"]["attachments"] if attachments: short_att_list = "(%s)" % ", ".join([ att['fname'] for att in (attachments[:2] + [{ "fname": "..." }] if len(attachments) > 2 else attachments) ]) else: short_att_list = "" content = _render_to_string( request, "webmail/compose.html", { "form": form, "bodyheader": textheader, "body": body, "posturl": posturl, "attachments": attachments, "short_att_list": short_att_list }) ctx = dict(listing=content, editor=editor) if randid is not None: ctx["id"] = randid return ctx
def viewmail(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) if mbox is None or mailid is None: raise WebmailError(_("Invalid request")) links = request.GET.get("links", None) if links is None: links = 1 if parameters.get_user(request.user, "ENABLE_LINKS") == "yes" else 0 else: links = int(links) url = reverse(getmailcontent) + "?mbox=%s&mailid=%s&links=%d" % \ (mbox, mailid, links) content = Template(""" <iframe src="{{ url }}" id="mailcontent"></iframe> """).render(Context({"url": url})) return dict(listing=content, menuargs=dict(mail_id=mailid))
def viewmail(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) if mbox is None or mailid is None: raise BadRequest(_("Invalid request")) links = request.GET.get("links", None) if links is None: links = 1 if parameters.get_user( request.user, "ENABLE_LINKS") == "yes" else 0 else: links = int(links) url = "{0}?mbox={1}&mailid={2}&links={3}".format( reverse("webmail:mailcontent_get"), mbox, mailid, links) content = Template(""" <iframe src="{{ url }}" id="mailcontent"></iframe> """).render(Context({"url": url})) return dict(listing=content, menuargs=dict(mail_id=mailid))
def viewmail(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) if mbox is None or mailid is None: raise BadRequest(_("Invalid request")) links = request.GET.get("links", None) if links is None: links = 1 if parameters.get_user(request.user, "ENABLE_LINKS") == "yes" else 0 else: links = int(links) url = "{0}?mbox={1}&mailid={2}&links={3}".format( reverse("webmail:mailcontent_get"), mbox, mailid, links) content = Template(""" <iframe src="{{ url }}" id="mailcontent"></iframe> """).render(Context({"url": url})) return dict(listing=content, menuargs=dict(mail_id=mailid))
def render_compose(request, form, posturl, email=None, insert_signature=False): editor = parameters.get_user(request.user, "EDITOR") if email is None: body = "" textheader = "" else: body = email.body textheader = email.textheader if insert_signature: signature = EmailSignature(request.user) body += str(signature) randid = None if not "id" in request.GET: if "compose_mail" in request.session: clean_attachments(request.session["compose_mail"]["attachments"]) randid = set_compose_session(request) elif not "compose_mail" in request.session or request.session["compose_mail"]["id"] != request.GET["id"]: randid = set_compose_session(request) attachments = request.session["compose_mail"]["attachments"] if attachments: short_att_list = "(%s)" % ", ".join( [att["fname"] for att in (attachments[:2] + [{"fname": "..."}] if len(attachments) > 2 else attachments)] ) else: short_att_list = "" content = _render_to_string( request, "webmail/compose.html", { "form": form, "bodyheader": textheader, "body": body, "posturl": posturl, "attachments": attachments, "short_att_list": short_att_list, }, ) ctx = dict(listing=content, editor=editor) if randid is not None: ctx["id"] = randid return ctx
def listmailbox_menu(selection, folder, user): """The menu of the listmailbox action.""" entries = [ { "name": "totrash", "title": _("Delete"), "class": "btn-danger", "img": "fa fa-trash", "url": reverse("modoboa_webmail:mail_delete"), }, { "name": "actions", "label": _("Actions"), "class": "btn btn-default", "menu": [ { "name": "mark-read", "label": _("Mark as read"), "url": "{0}?status=read".format(reverse("modoboa_webmail:mail_mark", args=[folder])), }, { "name": "mark-unread", "label": _("Mark as unread"), "url": "{0}?status=unread".format(reverse("modoboa_webmail:mail_mark", args=[folder])), }, ], }, ] if folder == parameters.get_user(user, "TRASH_FOLDER"): entries[0]["class"] += " disabled" entries[1]["menu"] += [ { "name": "empty", "label": _("Empty folder"), "url": "{0}?name={1}".format(reverse("modoboa_webmail:trash_empty"), folder), } ] return render_to_string( "modoboa_webmail/main_action_bar.html", {"selection": selection, "entries": entries, "user": user, "css": "nav", "STATIC_URL": settings.STATIC_URL}, )
def render_compose(request, form, posturl, email=None, insert_signature=False): """Render the compose form.""" resp = {} if email is None: body = u"" textheader = u"" else: body = email.body textheader = email.textheader if insert_signature: signature = EmailSignature(request.user) body += unicode(signature) randid = None if "id" not in request.GET: if "compose_mail" in request.session: clean_attachments(request.session["compose_mail"]["attachments"]) randid = set_compose_session(request) elif "compose_mail" not in request.session \ or request.session["compose_mail"]["id"] != request.GET["id"]: randid = set_compose_session(request) attachment_list = request.session["compose_mail"]["attachments"] if attachment_list: resp["menuargs"] = {"attachment_counter": len(attachment_list)} content = _render_to_string(request, "webmail/compose.html", { "form": form, "bodyheader": textheader, "body": body, "posturl": posturl }) resp.update({ "listing": content, "editor": parameters.get_user(request.user, "EDITOR") }) if randid is not None: resp["id"] = randid return resp
def listmailbox(request, defmailbox="INBOX", update_session=True): """Mailbox content listing Return a list of messages contained in the specified mailbox. The number of elements returned depends on the ``MESSAGES_PER_PAGE`` parameter. (user preferences) :param request: a ``Request`` object :param defmailbox: the default mailbox (when not present inside request arguments) :return: a dictionnary """ mbox = request.GET.get("mbox", defmailbox) if update_session: set_nav_params(request) request.session["mbox"] = mbox lst = ImapListing(request.user, request.session["password"], baseurl="?action=listmailbox&mbox=%s&" % mbox, folder=mbox, elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")), **request.session["navparams"]) return lst.render(request, request.session["pageid"])
def test_get_user(self): self.assertEqual(parameters.get_user(self.user, "PARAM1", "test"), "titi") with self.assertRaises(parameters.NotDefined): parameters.get_user(self.user, "TOTO", "test")
def test_save_user(self): parameters.save_user(self.user, "PARAM1", "pouet", "test") self.assertEqual(parameters.get_user(self.user, "PARAM1", "test"), "pouet")
def index(request): """Webmail actions handler Problèmes liés à la navigation 'anchor based' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lors d'un rafraichissemt complet, une première requête est envoyée vers /webmail/. On ne connait pas encore l'action qui va être demandée mais on peut déjà envoyer des informations indépendantes (comme les dossiers, le quota). Si on se contente de cela, l'affichage donnera un aspect décomposé qui n'est pas très séduisant (à cause de la latence notamment). Il faudrait pouvoir envoyer le menu par la même occasion, le souci étant de savoir lequel... Une solution possible : il suffirait de déplacer le menu vers la droite pour l'aligner avec le contenu, remonter la liste des dossiers (même hauteur que le menu) et renvoyer le menu en même temps que le contenu. Le rendu sera plus uniforme je pense. """ action = request.GET.get("action", None) if action is not None: if not action in globals(): raise WebmailError(_("Undefined action")) response = globals()[action](request) else: if request.is_ajax(): raise WebmailError(_("Bad request")) response = dict(selection="webmail") curmbox = request.session.get("mbox", "INBOX") if not request.is_ajax(): request.session["lastaction"] = None imapc = get_imapconnector(request) response["hdelimiter"] = imapc.hdelimiter response["mboxes"] = render_mboxes_list(request, imapc) imapc.getquota(curmbox) response["refreshrate"] = \ int(parameters.get_user(request.user, "REFRESH_INTERVAL")) response["quota"] = ImapListing.computequota(imapc) trash = parameters.get_user(request.user, "TRASH_FOLDER") response["trash"] = trash response["ro_mboxes"] = [ "INBOX", "Junk", parameters.get_user(request.user, "SENT_FOLDER"), trash, parameters.get_user(request.user, "DRAFTS_FOLDER") ] return render(request, "webmail/index.html", response) if action in ["reply", "forward"]: action = "compose" if request.session["lastaction"] != action: extra_args = {} if "menuargs" in response: extra_args = response["menuargs"] del response["menuargs"] try: response["menu"] = \ getattr(webmail_tags, "%s_menu" % action)("", curmbox, request.user, **extra_args) except KeyError: pass response.update(callback=action) if not "status" in response: response.update(status="ok") return ajax_simple_response(response)