var utils = require('./imap.utilities'); var reCRLF = /\r\n/g, reHdr = /^([^:]+):\s(.+)?$/, reHdrFold = /^\s+(.+)$/; exports.convStr = function(str, literals) { if (str[0] === '"') return str.substring(1, str.length-1); else if (str === 'NIL') return null; else if (/^\d+$/.test(str)) { // some IMAP extensions utilize large (64-bit) integers, which JavaScript // can't handle natively, so we'll just keep it as a string if it's too big var val = parseInt(str, 10); return (val.toString() === str ? val : str); } else if (literals && literals.lp < literals.length && /^\{\d+\}$/.test(str)) return literals[literals.lp++]; else return str; }; exports.parseHeaders = function(str) { var lines = str.split(reCRLF), headers = {}, m; for (var i = 0, h, len = lines.length; i < len; ++i) { if (lines[i].length === 0) continue; if (lines[i][0] === '\t' || lines[i][0] === ' ') { // folded header content m = reHdrFold.exec(lines[i]); headers[h][headers[h].length - 1] += m[1]; } else { m = reHdr.exec(lines[i]); h = m[1].toLowerCase(); if (m[2]) { if (headers[h] === undefined) headers[h] = [m[2]]; else headers[h].push(m[2]); } else headers[h] = ['']; } } return headers; }; exports.parseNamespaces = function(str, literals) { var result, vals; if (str.length === 3 && str.toUpperCase() === 'NIL') vals = null; else { result = exports.parseExpr(str, literals); vals = []; for (var i=0,len=result.length; i 2) { // extension data val.extensions = []; for (var j=2,len2=result[i].length; j next) { if (Array.isArray(cur[next])) { part.params = {}; for (i=0,len=cur[next].length; i next && Array.isArray(cur[next])) { part.envelope = {}; for (i=0,len=cur[next].length; i= 2 && i <= 7) { var val = cur[next][i]; if (Array.isArray(val)) { var addresses = [], inGroup = false, curGroup; for (var j=0,len2=val.length; j next && Array.isArray(cur[next])) part.body = exports.parseBodyStructure(cur[next], literals, prefix, 1); else part.body = null; ++next; } if ((part.type === 'text' || (part.type === 'message' && part.subtype === 'rfc822')) && partLen > next) part.lines = cur[next++]; if (typeof cur[1] === 'string' && partLen > next) part.md5 = cur[next++]; } // add any extra fields that may or may not be omitted entirely exports.parseStructExtra(part, partLen, cur, next); ret.unshift(part); } return ret; }; exports.parseStructExtra = function(part, partLen, cur, next) { if (partLen > next) { // disposition // null or a special k/v list with these kinds of values: // e.g.: ['Foo', null] // ['Foo', ['Bar', 'Baz']] // ['Foo', ['Bar', 'Baz', 'Bam', 'Pow']] var disposition = { type: null, params: null }; if (Array.isArray(cur[next])) { disposition.type = cur[next][0]; if (Array.isArray(cur[next][1])) { disposition.params = {}; for (var i=0,len=cur[next][1].length; i next) { // language can be a string or a list of one or more strings, so let's // make this more consistent ... if (cur[next] !== null) part.language = (Array.isArray(cur[next]) ? cur[next] : [cur[next]]); else part.language = null; ++next; } if (partLen > next) part.location = cur[next++]; if (partLen > next) { // extension stuff introduced by later RFCs // this can really be any value: a string, number, or (un)nested list // let's not parse it for now ... part.extensions = cur[next]; } }; exports.parseExpr = function(o, literals, result, start) { start = start || 0; var inQuote = false, lastPos = start - 1, isTop = false; if (!result) result = []; if (typeof o === 'string') { o = { str: o }; isTop = true; } for (var i=start,len=o.str.length; i 0) result.push(exports.convStr(o.str.substring(lastPos+1, i), literals)); if ((o.str[i] === ')' || o.str[i] === ']') && !isTop) return i; lastPos = i; } else if ((o.str[i] === '(' || o.str[i] === '[') /*&& (i === 0 || !isAlpha(o.str.charCodeAt(i-1)))*/) { var innerResult = []; i = exports.parseExpr(o, literals, innerResult, i+1); lastPos = i; result.push(innerResult); } } else if (o.str[i] === '"' && (o.str[i-1] && (o.str[i-1] !== '\\' || (o.str[i-2] && o.str[i-2] === '\\')))) inQuote = false; if (i+1 === len && len - (lastPos+1) > 0) result.push(exports.convStr(o.str.substring(lastPos+1), literals)); } return (isTop ? result : start); };