Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
Andris Reinman committed Apr 24, 2012
1 parent 975eeb6 commit bb5a585
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 36 deletions.
262 changes: 233 additions & 29 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ var Stream = require("stream").Stream,
net = require("net"),
tls = require("tls"),
starttls = require("./starttls").starttls,
IMAPLineParser = require("./lineparser");
IMAPLineParser = require("./lineparser"),
mimelib = require("mimelib");

module.exports = IMAPClient;

Expand Down Expand Up @@ -51,17 +52,25 @@ IMAPClient.prototype._init = function(){
this._expectedDataLength = 0;

this._tagCounter = 0;
this._tagQueue = {};
this._currentTag = false;
this._remainder = "";
this._commandQueue = [];

this._capabilities = [];
this._updatedCapabilities = false;

this._idleing = false;
this._idleWait = false;
this._idleTimer = false;

this._collectMailList = false;
this._mailList = [];

this._selectedMailbox = {};

this._streamLiteral = false;
this._streamedMessage = {};

this._mailboxRoot = "";
this._mailboxDelimiter = "/";
this._inboxName = "INBOX";
Expand Down Expand Up @@ -117,7 +126,7 @@ IMAPClient.prototype._onData = function(chunk){
return;
}
var data = chunk && chunk.toString("binary") || "",
line, match;
line, match, envelopeData;

if(this._remainder){
data = this._remainder + data;
Expand All @@ -126,17 +135,32 @@ IMAPClient.prototype._onData = function(chunk){

if(this._currentMode == this.modes.DATA){
if(this._expectedDataLength <= data.length){

if(this._expectedDataLength){
this._processData(data.substr(0, this._expectedDataLength));

if(!this._streamLiteral){
this.lineparser.writeLiteral(data.substr(0, this._expectedDataLength));
}else{
this.emit("messageData", this._streamedMessage, new Buffer(data.substr(0, this._expectedDataLength), "binary"));
this.emit("messageEnd", this._streamedMessage);
}

this._remainder = data.substr(this._expectedDataLength);
this._expectedDataLength = 0;
}else{
this._remainder = data;
}
this._currentMode = this.modes.COMMAND;
return this._onData.bind(); // rerun with the remainder

return this._onData(); // rerun with the remainder
}else{
this.lineparser.writeLiteral(data);

if(!this._streamLiteral){
this.lineparser.writeLiteral(data);
}else{
this.emit("messageData", this._streamedMessage, new Buffer(data, "binary"));
}

this._expectedDataLength -= data.length;
return;
}
Expand All @@ -147,14 +171,24 @@ IMAPClient.prototype._onData = function(chunk){
line = data.substr(0, match.index);
this._remainder = data.substr(match.index + match[0].length) || "";

if(this.debug){
console.log("SERVER: "+line);
}

if((match = line.match(/\{(\d+)\}\s*$/))){
this._expectedDataLength = Number(match[0]);
this._currentMode = this.modes.DATA;
this._expectedDataLength = Number(match[1]);
this.lineparser.write(line);
}else{
if(this.debug){
console.log("SERVER: "+line);

this._currentMode = this.modes.DATA;

if(this._streamLiteral){
envelopeData = this.lineparser.finalize() || [];
this._streamedMessage = this._formatEnvelope(envelopeData[3]);
this.emit("message", this._streamedMessage);
this.lineparser.writeLiteral("");
}
}else{

this.lineparser.end(line);
}

Expand Down Expand Up @@ -193,25 +227,48 @@ IMAPClient.prototype._send = function(data, callback){
if(this._idleing){
this._idleing = false;
clearTimeout(this._idleTimer);
console.log("CLIENT: DONE");

if(this.debug){
console.log("CLIENT: DONE");
}

this.client.write("DONE\r\n");
}


data = (data || "").toString();
var tag = "A" + (++this._tagCounter);
this.client.write(tag + " " + data + "\r\n");

this._commandQueue.push({tag: tag, data: tag + " " + data + "\r\n", callback: callback});

if(!this._currentTag){
this._processCommandQueue();
}
};

IMAPClient.prototype._processCommandQueue = function(){
var command = this._commandQueue.shift();
if(!command){
return;
}

this.client.write(command.data);

if(this.debug){
console.log("CLIENT: "+ tag + " " + data);
console.log("CLIENT: "+ (command.data || "").trim());
}

this._tagQueue[tag] = (function(status, params){
delete this._tagQueue[tag];
if(typeof callback == "function"){
callback(status, params);
}
}).bind(this);
};
this._currentTag = {
tag: command.tag,
callback: (function(status, params){
if(typeof command.callback == "function"){
command.callback(status, params);
}
this._currentTag = false;
this._processCommandQueue();
}).bind(this)
}
}

IMAPClient.prototype._handlerGreeting = function(data){
if(!data || !Array.isArray(data)){
Expand Down Expand Up @@ -433,7 +490,7 @@ IMAPClient.prototype._handlerUntaggedList = function(list){
var tags = list.shift() || [],
delimiter = list.shift() || this._mailboxDelimiter,
fullname = (list.shift() || "").substr(this._mailboxRoot.length),
nameParts = fullname.split(delimiter),
nameParts = delimiter?fullname.split(delimiter):[fullname],
name,
mailboxList = this._mailboxList,
mailbox = {};
Expand Down Expand Up @@ -482,8 +539,25 @@ IMAPClient.prototype._handlerUntaggedList = function(list){
mailboxList[name] = mailbox;
};

IMAPClient.prototype._handlerUntaggedSearch = function(list){

//console.log(list);
}

IMAPClient.prototype._handlerUntaggedFetch = function(list){
console.log(require("util").inspect(list, false, 11));
var nr = list[0] || 0,
envelope = list[1] || [],
nextUID = Number(this._selectedMailbox.UIDNext) || 0,
currentUID = Number(envelope[1]) || 0;

if(!nextUID || nextUID <= currentUID){
this._selectedMailbox.UIDNext = currentUID+1;
}

if(this._collectMailList){
this._mailList.push(this._formatEnvelope((list || [])[3]));
}
//console.log(require("util").inspect(list, false, 11));
}

IMAPClient.prototype._escapeString = function(str){
Expand All @@ -495,8 +569,8 @@ IMAPClient.prototype._responseRouter = function(data){
return;
}

if(data[0] in this._tagQueue){
this._tagQueue[data[0]](data[1], data.slice(2));
if(this._currentTag && this._currentTag.tag == data[0]){
this._currentTag.callback(data[1], data.slice(2));
return;
}

Expand All @@ -511,6 +585,9 @@ IMAPClient.prototype._responseRouter = function(data){
case "FLAGS":
this._selectedMailbox.flags = data[2] || [];
return;
case "SEARCH":
this._handlerUntaggedSearch(data.slice(2));
return;
case "XLIST":
case "LIST":
this._handlerUntaggedList(data.slice(2));
Expand All @@ -537,14 +614,14 @@ IMAPClient.prototype._responseRouter = function(data){
}

if(!isNaN(data[1]) && data[2] == "FETCH"){
this._handlerUntaggedFetch([data[1]].concat(data.slice(3)));
this._handlerUntaggedFetch(data);
return;
}

if(!isNaN(data[1]) && data[2] == "EXISTS"){
this._selectedMailbox.count = data[1];
this._selectedMailbox.count = Number(data[1]) || this._selectedMailbox.count || 0;
if(this._idleing){
// changes! new mail or expunge
this._checkNewMail();
}
return;
}
Expand All @@ -553,6 +630,58 @@ IMAPClient.prototype._responseRouter = function(data){
}
};

IMAPClient.prototype.listMail = function(from, count, callback){
from = Number(from) || 0;

if(typeof count == "function" && !callback){
callback = count;
count = undefined;
}

count = Number(count) || 0;

var to;

if(from < 0){
from = this._selectedMailbox.count + from;
}

if(count){
to = from + count;
}else{
to = "*";
}

from++;

this._collectMailList = true;
this._mailList = [];
this._send("FETCH "+from+":"+to+" (UID FLAGS ENVELOPE)", (function(status){
this._collectMailList = false;

if(typeof callback != "function"){
return;
}

if(status == "OK"){
callback(null, this._mailList);
}else{
callback(new Error("Error fetching list"));
}
}).bind(this));
}

IMAPClient.prototype._checkNewMail = function(err, mailboxList){
if(isNaN(this._selectedMailbox.UIDNext)){
return;
}

this._streamLiteral = true;
this._send("UID FETCH "+this._selectedMailbox.UIDNext+":* (FLAGS ENVELOPE)", (function(status){
this._streamLiteral = false;
}).bind(this));
}

IMAPClient.prototype._selectDefaultMailbox = function(err, mailboxList){
if(err){
this.emit("error", err);
Expand All @@ -561,6 +690,80 @@ IMAPClient.prototype._selectDefaultMailbox = function(err, mailboxList){
this.selectMailbox();
}

IMAPClient.prototype._formatEnvelope = function(envelopeData){

if(!Array.isArray(envelopeData)){
return null;
}

var dataObject = {}, lastKey = false;

for(var i=0, len = envelopeData.length; i<len; i++){
if(!lastKey){
lastKey = (envelopeData[i] || "").toString();
}else{
dataObject[lastKey] = envelopeData[i];
lastKey = false;
}
};

var message = {
UIDValidity: this._selectedMailbox.UIDValidity
}

if(dataObject.UID){
message.UID = Number(dataObject.UID) || 0;
}

if(dataObject.FLAGS){
message.flags = dataObject.FLAGS || [];
}

if(dataObject.ENVELOPE){
message.date = new Date(dataObject.ENVELOPE[0] || Date.now());

message.title = (dataObject.ENVELOPE[1] || "").toString().
replace(/\=\?[^?]+\?[QqBb]\?[^?]+\?=/g,
function(mimeWord){
return mimelib.decodeMimeWord(mimeWord);
});
if(dataObject.ENVELOPE[2] && dataObject.ENVELOPE[2].length){
message.from = dataObject.ENVELOPE[2].map(this._formatEnvelopeAddress);
if(message.from.length == 1){
message.from = message.from[0];
}
}

if(dataObject.ENVELOPE[5] && dataObject.ENVELOPE[5].length){
message.to = dataObject.ENVELOPE[5].map(this._formatEnvelopeAddress);
}
if(dataObject.ENVELOPE[6] && dataObject.ENVELOPE[6].length){
message.cc = dataObject.ENVELOPE[6].map(this._formatEnvelopeAddress);
}
message.messageId = (dataObject.ENVELOPE[9] || "").toString();
}

return message;

}

IMAPClient.prototype._formatEnvelopeAddress = function(address){
var name = address[0],
email = (address[2] || "") + "@" + (address[3] || "");

if(email == "@"){
email = "";
}

return {
name: (name || email).replace(/\=\?[^?]+\?[QqBb]\?[^?]+\?=/g,
function(mimeWord){
return mimelib.decodeMimeWord(mimeWord);
}),
address: email
}
}

IMAPClient.prototype.selectMailbox = function(mailboxName){
mailboxName = mailboxName || this._inboxName || "INBOX";

Expand Down Expand Up @@ -593,4 +796,5 @@ IMAPClient.prototype.idle = function(){
this._send("IDLE");
this._idleing = true;
this._idleTimer = setTimeout(this._handlerIdleTimeout.bind(this), 60*1000);
}
}

Loading

0 comments on commit bb5a585

Please sign in to comment.