diff --git a/extensions/webmail/imaputils.py b/extensions/webmail/imaputils.py
index 57bce66aa..1750ac7f6 100644
--- a/extensions/webmail/imaputils.py
+++ b/extensions/webmail/imaputils.py
@@ -12,7 +12,7 @@ from modoboa.lib.connections import *
from modoboa.lib.webutils import static_url
from exceptions import ImapError
-imaplib.Debug = 4
+#imaplib.Debug = 4
class capability(object):
"""
@@ -88,6 +88,32 @@ class IMAPconnector(object):
return None
return self.m.untagged_responses.pop(name)
+ def __find_content_in_bodystruct(self, bodystruct, mtype, stype, prefix=""):
+ """Retrieve the number (index) of a specific part
+
+ This part number will generally be used inside a FETCH request
+ to specify a ``BODY.PEEK`` section.
+
+ :param bodystruct: a BODYSTRUCTURE list
+ :param mtype: the MIME main type (like text)
+ :param stype: the MIME sub type (like plain)
+ :param prefix: the prefix that will be added to the current part number
+ :return: a tuple (index (None on error), encoding (string), size (int))
+ """
+ if type(bodystruct[0]) in [list, tuple]:
+ cpt = 1
+ for part in bodystruct[0]:
+ nprefix = "%s" % cpt if prefix == "" else "%s.%d" % (prefix, cpt)
+ index, encoding, size = \
+ self.__find_content_in_bodystruct(bodystruct[0], mtype, stype, nprefix)
+ if index:
+ return (index, encoding, size)
+ cpt += 1
+
+ if bodystruct[0] == mtype and bodystruct[1] == stype:
+ return ("1" if not len(prefix) else prefix, bodystruct[5], int(bodystruct[6]))
+ return (None, None, 0)
+
def refresh(self, user, password):
"""Check if current connection needs a refresh
@@ -384,18 +410,24 @@ class IMAPconnector(object):
msg = email.message_from_string(data[0][1] + data[1][1])
return msg
- def fetch(self, start, stop=None, folder=None, all=False, **kwargs):
- self.select_mailbox(folder, False)
+ def fetch(self, start, stop=None, mbox=None, **kwargs):
+ """Retrieve information about messages from the server
+
+ Issue a FETCH command to retrieve information about one or
+ more messages (such as headers) from the server.
+
+ :param start: index of the first message
+ :param stop: index of the last message (optionnal)
+ :param mbox: the mailbox that contains the messages
+ """
+ self.select_mailbox(mbox, False)
if start and stop:
submessages = self.messages[start - 1:stop]
range = ",".join(submessages)
else:
submessages = [start]
range = start
- if not all:
- query = '(FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (DATE FROM TO CC SUBJECT)])'
- else:
- query = '(RFC822)'
+ query = '(FLAGS BODY.PEEK[HEADER.FIELDS (DATE FROM TO CC SUBJECT)])'
data = self._cmd("FETCH", range, query)
result = []
for uid in submessages:
@@ -408,6 +440,48 @@ class IMAPconnector(object):
result += [msg]
return result
+ def fetchmail(self, mbox, mailid, fmt="plain"):
+ """Retrieve information about a specific message
+
+ Issue a FETCH command to retrieve a message's content from the
+ server. In order to not overload the server, we first retrieve
+ the BODYSTRUCTURE of the message. Then, according to the
+ result and to the user's preferences, we retrieve the
+ appropriate content (plain, html, etc.).
+
+ :param mbox: the mailbox containing the message
+ :param mailid: the message's unique id
+ :param fmt: the desired content's format
+ """
+ self.select_mailbox(mbox, False)
+ data = self._cmd("FETCH", mailid, "(BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (DATE FROM TO CC SUBJECT)])")
+ content = data[int(mailid)]['BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)]']
+
+ fallback_fmt = "html" if fmt == "plain" else "plain"
+ (pnum, encoding, size) = (None, None, 0)
+ for f in [fmt, fallback_fmt]:
+ pnum, encoding, size = \
+ self.__find_content_in_bodystruct(data[int(mailid)]['BODYSTRUCTURE'],
+ "text", f)
+ if pnum is not None and size:
+ break
+ if pnum is None:
+ return None
+
+ data = self._cmd("FETCH", mailid, "(BODY.PEEK[%s])" % pnum)
+
+ data = data[int(mailid)]['BODY[%s]' % pnum]
+ if encoding == "base64":
+ import base64
+ content += base64.b64decode(data)
+ elif encoding == "quoted-printable":
+ import quopri
+ content += quopri.decodestring(data)
+ else:
+ content += data
+
+ return email.message_from_string(content)
+
def get_imapconnector(request):
imapc = IMAPconnector(user=request.user.username,
password=request.session["password"])
diff --git a/extensions/webmail/static/css/webmail.css b/extensions/webmail/static/css/webmail.css
index ef221f3fc..7c9a7c60c 100644
--- a/extensions/webmail/static/css/webmail.css
+++ b/extensions/webmail/static/css/webmail.css
@@ -12,6 +12,14 @@ html > body #listing {
bottom: 35px;
}
+body.loading {
+ display: block;
+ filter: alpha(opacity=50);
+ opacity: 0.5;
+ z-index: 3;
+ background: #FFF url("../../css/spinner.gif") center center no-repeat;
+}
+
.clearfix:after {
content: ".";
display: block;
diff --git a/extensions/webmail/static/js/webmail.js b/extensions/webmail/static/js/webmail.js
index ec96978a5..09fd0c762 100644
--- a/extensions/webmail/static/js/webmail.js
+++ b/extensions/webmail/static/js/webmail.js
@@ -21,13 +21,13 @@ var Webmail = new Class({
this.rtimer = null;
this.editorid = "id_body";
- this.init_menubar();
this.init_folders_buttons();
this.init_folders_browsing();
/* FIXME: à faire uniquement pour le mode pushState */
/*this.init_emails_table();*/
+ current_anchor.disable_spinner();
current_anchor.register_callback("listmailbox",
this.listmailbox_callback.bind(this));
current_anchor.register_callback("viewmail", this.viewmail_callback.bind(this));
@@ -35,20 +35,17 @@ var Webmail = new Class({
current_anchor.register_callback("reply", this.compose_callback.bind(this));
current_anchor.register_callback("replyall", this.compose_callback.bind(this));
current_anchor.register_callback("forward", this.compose_callback.bind(this));
- },
- /*
- * Menubar initialization
- */
- init_menubar: function() {
- searchbox_init();
- $$("a[name=compose]").addEvent("click", this.compose_loader);
- $$("a[name=mark-read]").addEvent("click", this.send_mark_request.bind(this));
- $$("a[name=mark-unread]").addEvent("click", this.send_mark_request.bind(this));
- $$("a[name=fdaction]").addEvent("click", function(evt) {
- this.simple_request(evt.target.get("href"), {});
- evt.stop();
+ this.gspinner = new Spinner("document.body", {
+ message: gettext("Loading, please wait..."),
+ 'class': "spinner-noop"
+ });
+ this.gspinner.addEvent("hide", function(target) {
+ this.gspinner.destroy();
+ delete(this.gspinner);
+ current_anchor.enable_spinner();
}.bind(this));
+ this.gspinner.show();
},
send_mark_request: function(evt) {
@@ -286,7 +283,7 @@ var Webmail = new Class({
this.init_draggables();
- $$("tbody>tr").addEvent("dblclick", viewmail);
+ $$("tbody>tr").addEvent("dblclick", this.viewmail_loader);
},
/*
@@ -405,7 +402,10 @@ var Webmail = new Class({
*
*/
page_update: function(response) {
-
+ if (this.gspinner) {
+ this.gspinner.hide();
+ }
+ updatelisting(response);
},
/*
@@ -437,16 +437,41 @@ var Webmail = new Class({
*/
listmailbox_callback: function(resp) {
window.removeEvents("resize");
- updatelisting(resp);
+ this.page_update(resp);
+ if (resp.menu != undefined) {
+ searchbox_init();
+ $$("a[name=compose]").addEvent("click", this.compose_loader);
+ $$("a[name=mark-read]")
+ .addEvent("click", this.send_mark_request.bind(this));
+ $$("a[name=mark-unread]")
+ .addEvent("click", this.send_mark_request.bind(this));
+ $$("a[name=fdaction]").addEvent("click", function(evt) {
+ this.simple_request(evt.target.get("href"), {});
+ evt.stop();
+ }.bind(this));
+ }
this.init_emails_table();
},
+ /*
+ * Loader of the 'viemmail' action
+ */
+ viewmail_loader: function(evt) {
+ evt.stop();
+ current_anchor.reset().setparams({
+ action: "viewmail",
+ mailid: this.get("id")
+ }).update();
+ },
+
/*
* Callback of the 'viewmail' action
*/
viewmail_callback: function(resp) {
- wm_updatelisting(resp, "hidden");
- $$("a[name=back]").addEvent("click", this._listmailbox_loader);
+ /*wm_updatelisting(resp, "hidden");*/
+ this.page_update(resp);
+
+ //$$("a[name=back]").addEvent("click", this._listmailbox_loader);
$$("a[name=reply]").addEvent("click", this.reply_loader);
$$("a[name=replyall]").addEvent("click", this.replyall_loader);
$$("a[name=forward]").addEvent("click", this.forward_loader);
@@ -517,7 +542,7 @@ var Webmail = new Class({
*/
compose_callback: function(resp) {
//wm_updatelisting(resp);
- updatelisting(resp);
+ this.page_update(resp);
window.addEvent("resize", this.resize_window_callback.bind(this));
diff --git a/extensions/webmail/templates/webmail/index.html b/extensions/webmail/templates/webmail/index.html
index 7c23f9e27..691b5141a 100644
--- a/extensions/webmail/templates/webmail/index.html
+++ b/extensions/webmail/templates/webmail/index.html
@@ -8,6 +8,7 @@
{{ block.super }}
{% block css %}{% endblock %}