Search in sources :

Example 1 with MemoryBuffer

use of i2p.susi.util.MemoryBuffer in project i2p.i2p by i2p.

the class HeaderLine method decode.

/**
 *  Decode all the header lines, up through \r\n\r\n,
 *  and puts them in the ReadBuffer, including the \r\n\r\n
 */
public void decode(InputStream in, Buffer bout) throws IOException {
    OutputStream out = bout.getOutputStream();
    boolean linebreak = false;
    boolean lastCharWasQuoted = false;
    byte[] encodedWord = null;
    // we support one char of pushback,
    // to catch some simple malformed input
    int pushbackChar = 0;
    boolean hasPushback = false;
    while (true) {
        int c;
        if (hasPushback) {
            c = pushbackChar;
            hasPushback = false;
        // Debug.debug(Debug.DEBUG, "Loop " + count + " Using pbchar(dec) " + c);
        } else {
            c = in.read();
            if (c < 0)
                break;
        }
        if (c == '=') {
            // Sadly, base64 can be a lot longer
            if (encodedWord == null)
                encodedWord = new byte[DECODE_MAX];
            int offset = 0;
            int f1 = 0, f2 = 0, f3 = 0, f4 = 0;
            encodedWord[offset++] = (byte) c;
            // but for the most part it gets thrown out, as RFC 2047 allows
            for (; offset < DECODE_MAX; offset++) {
                c = in.read();
                if (c == '?') {
                    if (f1 == 0)
                        f1 = offset;
                    else if (f2 == 0)
                        f2 = offset;
                    else if (f3 == 0)
                        f3 = offset;
                    else if (f4 == 0)
                        f4 = offset;
                } else if (c == -1) {
                    break;
                } else if (c == '\r' || c == '\n') {
                    pushbackChar = c;
                    hasPushback = true;
                    break;
                } else if (offset == 1) {
                    // no '?' after '='
                    out.write('=');
                    pushbackChar = c;
                    hasPushback = true;
                    break;
                }
                encodedWord[offset] = (byte) c;
                // store one past the 4th '?', presumably the '='
                if (f4 > 0 && offset >= f4 + 1) {
                    if (c == '=') {
                        offset++;
                    } else {
                        pushbackChar = c;
                        hasPushback = true;
                    }
                    break;
                }
            }
            // net.i2p.util.HexDump.dump(encodedWord, 0, offset));
            if (f4 == 0) {
                // at most 1 byte is pushed back
                if (f1 == 0) {
                    // This is normal
                    continue;
                } else if (f2 == 0) {
                    // =? but no more ?
                    // output what we buffered
                    Debug.debug(Debug.DEBUG, "2nd '?' not found");
                    for (int i = 0; i < offset; i++) {
                        out.write(encodedWord[i] & 0xff);
                    }
                    continue;
                } else if (f3 == 0) {
                    // discard what we buffered
                    Debug.debug(Debug.DEBUG, "3rd '?' not found");
                    continue;
                } else {
                    // probably just too long, but could be end of line without the "?=".
                    // synthesize a 4th '?' in an attempt to output
                    // something, probably with some trailing garbage
                    Debug.debug(Debug.DEBUG, "4th '?' not found");
                    f4 = offset + 1;
                // keep going and output what we have
                }
            }
            /*
				 * 4th question mark found, we are complete, so lets start
				 */
            String enc = (encodedWord[f2 + 1] == 'Q' || encodedWord[f2 + 1] == 'q') ? "quoted-printable" : ((encodedWord[f2 + 1] == 'B' || encodedWord[f2 + 1] == 'b') ? "base64" : null);
            // System.err.println( "4th ? found at " + f4 + ", encoding=" + enc );
            if (enc != null) {
                Encoding e = EncodingFactory.getEncoding(enc);
                if (e != null) {
                    try {
                        // System.err.println( "decode(" + (f3 + 1) + "," + ( f4 - f3 - 1 ) + ")" );
                        ReadBuffer tmpIn = new ReadBuffer(encodedWord, f3 + 1, f4 - f3 - 1);
                        // decoded won't be longer than encoded
                        MemoryBuffer tmp = new MemoryBuffer(f4 - f3 - 1);
                        try {
                            e.decode(tmpIn, tmp);
                        } catch (EOFException eof) {
                            // Keep going and output what we got, if any
                            if (Debug.getLevel() >= Debug.DEBUG) {
                                Debug.debug(Debug.DEBUG, "q-w " + enc, eof);
                                Debug.debug(Debug.DEBUG, net.i2p.util.HexDump.dump(encodedWord));
                            }
                        }
                        tmp.writeComplete(true);
                        // get charset
                        String charset = new String(encodedWord, f1 + 1, f2 - f1 - 1, "ISO-8859-1");
                        String clc = charset.toLowerCase(Locale.US);
                        if (clc.equals("utf-8") || clc.equals("utf8")) {
                            // FIXME could be more efficient?
                            InputStream tis = tmp.getInputStream();
                            if (enc.equals("quoted-printable")) {
                                int d;
                                while ((d = tis.read()) != -1) {
                                    out.write(d == '_' ? 32 : d);
                                }
                            } else {
                                DataHelper.copy(tis, out);
                            }
                        } else {
                            // FIXME could be more efficient?
                            // decode string
                            String decoded = new String(tmp.getContent(), tmp.getOffset(), tmp.getLength(), charset);
                            // encode string
                            byte[] utf8 = DataHelper.getUTF8(decoded);
                            if (enc.equals("quoted-printable")) {
                                for (int j = 0; j < utf8.length; j++) {
                                    byte d = utf8[j];
                                    out.write(d == '_' ? 32 : d);
                                }
                            } else {
                                out.write(utf8);
                            }
                        }
                        lastCharWasQuoted = true;
                        continue;
                    } catch (IOException e1) {
                        Debug.debug(Debug.ERROR, "q-w " + enc, e1);
                        if (Debug.getLevel() >= Debug.DEBUG) {
                            Debug.debug(Debug.DEBUG, net.i2p.util.HexDump.dump(encodedWord));
                        }
                    } catch (RuntimeException e1) {
                        Debug.debug(Debug.ERROR, "q-w " + enc, e1);
                        if (Debug.getLevel() >= Debug.DEBUG) {
                            Debug.debug(Debug.DEBUG, net.i2p.util.HexDump.dump(encodedWord));
                        }
                    }
                } else {
                    // can't happen
                    Debug.debug(Debug.DEBUG, "No decoder for " + enc);
                }
            // e != null
            } else {
                Debug.debug(Debug.DEBUG, "Invalid encoding '" + (char) encodedWord[f2 + 1] + '\'');
            }
        // enc != null
        } else // c == '='
        if (c == '\r') {
            if ((c = in.read()) == '\n') {
                /*
					 * delay linebreak in case of long line
					 */
                linebreak = true;
            } else {
                // pushback?
                Debug.debug(Debug.DEBUG, "No \\n after \\r");
            }
        }
        // swallow whitespace here if lastCharWasQuoted
        if (linebreak) {
            linebreak = false;
            for (int i = 0; ; i++) {
                c = in.read();
                if (c == -1)
                    break;
                if (c != ' ' && c != '\t') {
                    if (i == 0) {
                        /*
							 * new line does not start with whitespace, so its not a new part of a
							 * long line
							 */
                        out.write('\r');
                        out.write('\n');
                        if (c == '\r') {
                            linebreak = true;
                            // \n
                            in.read();
                            break;
                        }
                    } else {
                        // treat all preceding whitespace as a single one
                        if (!lastCharWasQuoted)
                            out.write(' ');
                    }
                    pushbackChar = c;
                    hasPushback = true;
                    break;
                }
            /*
					 * skip whitespace
					 */
            }
            // if \r\n\r\n, we are done
            if (linebreak)
                break;
        } else {
            /*
				 * print out everything else literally
				 */
            out.write(c);
            lastCharWasQuoted = false;
        }
    }
    // while true
    if (linebreak) {
        out.write('\r');
        out.write('\n');
    }
    bout.writeComplete(true);
}
Also used : InputStream(java.io.InputStream) OutputStream(java.io.OutputStream) IOException(java.io.IOException) ReadBuffer(i2p.susi.util.ReadBuffer) MemoryBuffer(i2p.susi.util.MemoryBuffer) EOFException(java.io.EOFException)

Example 2 with MemoryBuffer

use of i2p.susi.util.MemoryBuffer in project i2p.i2p by i2p.

the class Encoding method decode.

/**
 * This implementation just calls decode(in.content, in.offset, in.length).
 * Most classes will not need to override.
 *
 * @param in
 * @see Encoding#decode(byte[], int, int)
 * @throws DecodingException
 * @since 0.9.33 implementation moved from subclasses
 */
public Buffer decode(Buffer in) throws IOException {
    MemoryBuffer rv = new MemoryBuffer(4096);
    decode(in, rv);
    return rv;
}
Also used : MemoryBuffer(i2p.susi.util.MemoryBuffer)

Example 3 with MemoryBuffer

use of i2p.susi.util.MemoryBuffer in project i2p.i2p by i2p.

the class Mail method parseHeaders.

/**
 * @return all headers, to pass to MailPart, or null on error
 */
private String[] parseHeaders(InputStream in) {
    String[] headerLines = null;
    error = "";
    if (header != null) {
        boolean ok = true;
        Encoding html = EncodingFactory.getEncoding("HTML");
        if (html == null) {
            error += "HTML encoder not found.\n";
            ok = false;
        }
        Encoding hl = EncodingFactory.getEncoding("HEADERLINE");
        if (hl == null) {
            error += "Header line encoder not found.\n";
            ok = false;
        }
        if (ok) {
            try {
                EOFOnMatchInputStream eofin = new EOFOnMatchInputStream(in, HEADER_MATCH);
                MemoryBuffer decoded = new MemoryBuffer(4096);
                hl.decode(eofin, decoded);
                if (!eofin.wasFound())
                    Debug.debug(Debug.DEBUG, "EOF hit before \\r\\n\\r\\n in Mail");
                // Fixme UTF-8 to bytes to UTF-8
                headerLines = DataHelper.split(new String(decoded.getContent(), decoded.getOffset(), decoded.getLength()), "\r\n");
                for (int j = 0; j < headerLines.length; j++) {
                    String line = headerLines[j];
                    if (line.length() == 0)
                        break;
                    String hlc = line.toLowerCase(Locale.US);
                    if (hlc.startsWith("from:")) {
                        sender = line.substring(5).trim();
                        // formattedSender = getAddress( sender );
                        shortSender = sender.replace("\"", "").trim();
                        int lt = shortSender.indexOf('<');
                        if (lt > 0)
                            shortSender = shortSender.substring(0, lt).trim();
                        else if (lt < 0 && shortSender.contains("@"))
                            // add missing <> (but thunderbird doesn't...)
                            shortSender = '<' + shortSender + '>';
                        boolean trim = shortSender.length() > 45;
                        if (trim)
                            shortSender = ServletUtil.truncate(shortSender, 42).trim();
                        shortSender = html.encode(shortSender);
                        if (trim)
                            // must be after html encode
                            shortSender += "&hellip;";
                    } else if (hlc.startsWith("date:")) {
                        dateString = line.substring(5).trim();
                        long dateLong = RFC822Date.parse822Date(dateString);
                        if (dateLong > 0)
                            setDate(dateLong);
                    } else if (hlc.startsWith("subject:")) {
                        subject = line.substring(8).trim();
                        shortSubject = subject;
                        boolean trim = subject.length() > 75;
                        if (trim)
                            shortSubject = ServletUtil.truncate(subject, 72).trim();
                        shortSubject = html.encode(shortSubject);
                        if (trim)
                            // must be after html encode
                            shortSubject += "&hellip;";
                    } else if (hlc.startsWith("reply-to:")) {
                        reply = getAddress(line.substring(9).trim());
                    } else if (hlc.startsWith("to:")) {
                        ArrayList<String> list = new ArrayList<String>();
                        getRecipientsFromList(list, line.substring(3).trim(), true);
                        if (list.isEmpty()) {
                        // don't set
                        } else if (to == null) {
                            to = list.toArray(new String[list.size()]);
                        } else if (cc == null) {
                            // Susimail bug before 0.9.33, sent 2nd To line that was really Cc
                            cc = list.toArray(new String[list.size()]);
                        } else {
                            // add to the array, shouldn't happen
                            for (int i = 0; i < to.length; i++) {
                                list.add(i, to[i]);
                            }
                            to = list.toArray(new String[list.size()]);
                        }
                    } else if (hlc.startsWith("cc:")) {
                        ArrayList<String> list = new ArrayList<String>();
                        getRecipientsFromList(list, line.substring(3).trim(), true);
                        if (list.isEmpty()) {
                        // don't set
                        } else if (cc == null) {
                            cc = list.toArray(new String[list.size()]);
                        } else {
                            // add to the array, shouldn't happen
                            for (int i = 0; i < cc.length; i++) {
                                list.add(i, cc[i]);
                            }
                            cc = list.toArray(new String[list.size()]);
                        }
                    } else if (hlc.equals("x-spam-flag: yes")) {
                        // TODO trust.spam.headers config
                        isSpam = true;
                    } else if (hlc.startsWith("content-type:")) {
                        // this is duplicated in MailPart but
                        // we want to know if we have attachments, even if
                        // we haven't fetched the body
                        contentType = line.substring(13).trim();
                    } else if (hlc.startsWith("message-id:")) {
                        messageID = line.substring(11).trim();
                    }
                }
            } catch (Exception e) {
                error += "Error parsing mail header: " + e.getClass().getName() + '\n';
                Debug.debug(Debug.ERROR, "Parse error", e);
            }
        }
    }
    return headerLines;
}
Also used : MemoryBuffer(i2p.susi.util.MemoryBuffer) ArrayList(java.util.ArrayList) Encoding(i2p.susi.webmail.encoding.Encoding) EOFOnMatchInputStream(i2p.susi.util.EOFOnMatchInputStream) ParseException(java.text.ParseException) IOException(java.io.IOException)

Example 4 with MemoryBuffer

use of i2p.susi.util.MemoryBuffer in project i2p.i2p by i2p.

the class MailCache method getMail.

/**
 * Fetch any needed data from pop3 server.
 * Mail objects are inserted into the requests.
 * After this, call getUIDLs() to get all known mail UIDLs.
 * MUST already be connected, otherwise returns false.
 *
 * Blocking.
 *
 * @param mode HEADER or ALL only
 * @return true if any were fetched
 * @since 0.9.13
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public boolean getMail(FetchMode mode) {
    if (mode == FetchMode.CACHE_ONLY)
        throw new IllegalArgumentException();
    boolean hOnly = mode == FetchMode.HEADER;
    Collection<String> popKnown = mailbox.getUIDLs();
    if (popKnown == null)
        return false;
    List<POP3Request> fetches = new ArrayList<POP3Request>();
    // requests.to send off
    for (String uidl : popKnown) {
        Mail mail = null, newMail = null;
        boolean headerOnly = hOnly;
        /*
			 * synchronize update to hashtable
			 */
        synchronized (mails) {
            mail = mails.get(uidl);
            if (mail == null) {
                newMail = new Mail(uidl);
                mails.put(uidl, newMail);
            }
        }
        if (mail == null) {
            mail = newMail;
            mail.setSize(mailbox.getSize(uidl));
        }
        if (mail.markForDeletion)
            continue;
        long sz = mail.getSize();
        if (sz > 0 && sz <= FETCH_ALL_SIZE)
            headerOnly = false;
        if (headerOnly) {
            if (!mail.hasHeader()) {
                if (disk != null) {
                    if (disk.getMail(mail, true)) {
                        Debug.debug(Debug.DEBUG, "Loaded header from disk cache: " + uidl);
                        // note that disk loaded the full body if it had it
                        if (mail.hasBody() && !Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) {
                            // we already have it, send delete
                            mailbox.queueForDeletion(mail.uidl);
                        }
                        // found on disk, woo
                        continue;
                    }
                }
                POP3Request pr = new POP3Request(mail, true, new MemoryBuffer(1024));
                fetches.add(pr);
            } else {
                if (mail.hasBody() && !Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) {
                    // we already have it, send delete
                    mailbox.queueForDeletion(mail.uidl);
                }
            }
        } else {
            if (!mail.hasBody()) {
                if (disk != null) {
                    if (disk.getMail(mail, false)) {
                        Debug.debug(Debug.DEBUG, "Loaded body from disk cache: " + uidl);
                        // note that disk loaded the full body if it had it
                        if (!Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) {
                            // we already have it, send delete
                            mailbox.queueForDeletion(mail.uidl);
                        }
                        // found on disk, woo
                        continue;
                    }
                }
                File file = new File(_context.getTempDir(), "susimail-new-" + _context.random().nextLong());
                POP3Request pr = new POP3Request(mail, false, new FileBuffer(file));
                fetches.add(pr);
            } else {
                if (!Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) {
                    // we already have it, send delete
                    mailbox.queueForDeletion(mail.uidl);
                }
            }
        }
    }
    boolean rv = false;
    if (!fetches.isEmpty()) {
        // Send off the fetches
        // gaah compiler
        List foo = fetches;
        List<FetchRequest> bar = foo;
        mailbox.getBodies(bar);
        // Process results
        for (POP3Request pr : fetches) {
            if (pr.getSuccess()) {
                Mail mail = pr.mail;
                if (!mail.hasHeader())
                    mail.setNew(true);
                if (pr.getHeaderOnly()) {
                    mail.setHeader(pr.getBuffer());
                } else {
                    mail.setBody(pr.getBuffer());
                }
                rv = true;
                if (disk != null) {
                    if (disk.saveMail(mail) && mail.hasBody() && !Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) {
                        mailbox.queueForDeletion(mail.uidl);
                    }
                }
            }
        }
    }
    return rv;
}
Also used : FileBuffer(i2p.susi.util.FileBuffer) ArrayList(java.util.ArrayList) MemoryBuffer(i2p.susi.util.MemoryBuffer) FetchRequest(i2p.susi.webmail.pop3.POP3MailBox.FetchRequest) ArrayList(java.util.ArrayList) List(java.util.List) File(java.io.File)

Example 5 with MemoryBuffer

use of i2p.susi.util.MemoryBuffer in project i2p.i2p by i2p.

the class POP3MailBox method getHeader.

/**
 * retrieves header from pop3 server (with TOP command and RETR as fallback)
 * Caller must sync.
 *
 * @param id message id
 * @return Byte buffer containing header data or null
 */
private Buffer getHeader(int id) {
    Debug.debug(Debug.DEBUG, "getHeader(" + id + ")");
    Buffer header = null;
    if (id >= 1 && id <= mails) {
        try {
            socket.setSoTimeout(120 * 1000);
        } catch (IOException ioe) {
        }
        /*
				 * try 'TOP n 0' command
				 */
        header = sendCmdN("TOP " + id + " 0", new MemoryBuffer(1024));
        if (header == null) {
            /*
					 * try 'RETR n' command
					 */
            header = sendCmdN("RETR " + id, new MemoryBuffer(2048));
            if (header == null)
                Debug.debug(Debug.DEBUG, "RETR returned null");
        }
        if (socket != null)
            try {
                socket.setSoTimeout(300 * 1000);
            } catch (IOException ioe) {
            }
    } else {
        lastError = "Message id out of range.";
    }
    return header;
}
Also used : MemoryBuffer(i2p.susi.util.MemoryBuffer) Buffer(i2p.susi.util.Buffer) ReadBuffer(i2p.susi.util.ReadBuffer) MemoryBuffer(i2p.susi.util.MemoryBuffer) IOException(java.io.IOException)

Aggregations

MemoryBuffer (i2p.susi.util.MemoryBuffer)5 IOException (java.io.IOException)3 ReadBuffer (i2p.susi.util.ReadBuffer)2 ArrayList (java.util.ArrayList)2 Buffer (i2p.susi.util.Buffer)1 EOFOnMatchInputStream (i2p.susi.util.EOFOnMatchInputStream)1 FileBuffer (i2p.susi.util.FileBuffer)1 Encoding (i2p.susi.webmail.encoding.Encoding)1 FetchRequest (i2p.susi.webmail.pop3.POP3MailBox.FetchRequest)1 EOFException (java.io.EOFException)1 File (java.io.File)1 InputStream (java.io.InputStream)1 OutputStream (java.io.OutputStream)1 ParseException (java.text.ParseException)1 List (java.util.List)1