This page exists to document the YaleInfo Recent Email channel.
Channel snapshot
The email displaying two recent email messages, warning that deleted messages are present and not being displayed.

Implementation
This channel is implemented portal-side as a simple XSLT transform channel. We apply an XSLT transform to XML generated by a Servlet to which the portal proxy CAS authenticates. The servlet proxies to our IMAP server to get the email and then renders that email as XML.
XSLT
The XSLT lives on the web under the mailchecker channel data source.
Java Servlet
This is a snapshot of the source code for the Java Servlet that gets the email and renders it as XML.
Note: if this code were to be written anew, it would probably do less of the CAS authentication itself and instead rely upon the CASFilter to authenticate the portal to the servlet and to make available a PGTIOU so that this servlet can obtain a Proxy Ticket to the IMAP server.
package edu.yale.its.tp.imap; import java.util.*; import java.io.*; import java.net.*; import javax.servlet.*; import javax.servlet.http.*; import javax.mail.*; import javax.mail.event.*; import javax.mail.internet.*; import javax.activation.*; import javax.xml.parsers.*; import com.sun.mail.imap.*; import org.xml.sax.*; import edu.yale.its.tp.cas.client.*; import edu.yale.its.tp.cas.proxy.*; /** * <p>Provides an XML stream of message in a CAS-protected user's INBOX.</p> * * @author Drew Mazurek * @author Shawn Bayern */ public class ImapProxyServlet extends HttpServlet { private static String[] months = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; private static String[] dow = {"foo","Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; private static String[] ampm = {"AM","PM"}; public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // get the servlet context from which we read parameters ServletContext sc = request.getSession().getServletContext(); // first make sure we have a valid CAS ticket... no point otherwise if(request.getParameter("ticket") == null || request.getParameter("ticket").equals("null")) { // log the attempt this.log("no CAS ticket... remote host: " + request.getRemoteAddr()); // send some nice XML response.setContentType("text/xml; encoding=ISO-8859-1"); PrintWriter out = response.getWriter(); out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); out.println("<error>"); out.println(" <error-code>NOCASTICKET</error-code>"); out.print(" <error-message>"); out.print(sc.getInitParameter("edu.yale.its.tp.imap.noTicketMessage")); out.println("</error-message>"); out.println("</error>"); return; } IMAPStore s = null; IMAPFolder f = null; String user = null; try { // default number of messages to show int showMessages = 10; try { showMessages = Integer.parseInt(sc.getInitParameter("edu.yale.its.tp.imap.defaultShowMessages")); } catch (NumberFormatException ex) {} // conduct a proxy authentication and acquire our own subordinate PGT ProxyTicketValidator pv = new ProxyTicketValidator(); pv.setCasValidateUrl(sc.getInitParameter("edu.yale.its.tp.imap.casValidateUrl")); pv.setProxyCallbackUrl(sc.getInitParameter("edu.yale.its.tp.imap.proxyCallbackUrl")); pv.setServiceTicket(request.getParameter("ticket")); pv.setService(sc.getInitParameter("edu.yale.its.tp.imap.service")); pv.validate(); if (pv.getProxyList() != null && pv.getProxyList().size() > 0) { boolean valid = false; StringTokenizer validProxies = new StringTokenizer(sc.getInitParameter("edu.yale.its.tp.imap.validProxy")); while(validProxies.hasMoreTokens()) { if(((String)pv.getProxyList().get(0)).equals(validProxies.nextToken())) { valid = true; break; } } if(!valid) { throw new ServletException("valid authentication through invalid proxy"); } } user = pv.getUser(); String proxyTicket = ProxyTicketReceptor.getProxyTicket(pv.getPgtIou(), sc.getInitParameter("edu.yale.its.tp.imap.imapService")); String url = "imap://" + user + ":" + proxyTicket + "@" + user + ".mail.yale.edu:143"; // now, connect to the IMAP store and retrieve messages Session session = Session.getDefaultInstance(System.getProperties(), null); s = (IMAPStore)session.getStore(new URLName(url)); s.connect(); f = (IMAPFolder)s.getDefaultFolder().getFolder("INBOX"); int messages = f.getMessageCount(); // check the number of messages in the folder before we open it try { int maxSize = Integer.parseInt(sc.getInitParameter("edu.yale.its.tp.imap.maxMailboxSize")); if(messages > maxSize) { // give an error message response.setContentType("text/xml; encoding=ISO-8859-1"); PrintWriter out = response.getWriter(); out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); out.println("<error>"); out.println(" <error-code>LARGEMBOX</error-code>"); out.print(" <error-message>"); out.print(sc.getInitParameter("edu.yale.its.tp.imap.maxErrorMessage")); out.println("</error-message>"); out.println("</error>"); return; } } catch(NumberFormatException ex) { this.log("Invalid value given for maxMailboxSize... not limiting number of messages"); } f.open(Folder.READ_WRITE); // get the number of messages to show if(request.getParameter("show") != null) { try { showMessages = Integer.parseInt(request.getParameter("show")); } catch (NumberFormatException ex) {} // leave showMessages as default } Message[] m; if(messages < showMessages) { m = f.getMessages(); } else { m = f.getMessages(messages-showMessages+1,messages); } // write the XML response response.setContentType("text/xml; encoding=ISO-8859-1"); PrintWriter out = response.getWriter(); out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); out.println("<messages>"); // if we want to see more messages than there are, be careful // we don't overrun the array if(showMessages > m.length) { showMessages = m.length; } for (int i = showMessages - 1; i >= 0; i--) { out.println(" <message>"); out.println(" <from>" + escapeXml(MimeUtility.decodeText(InternetAddress.toString(m[i].getFrom()))) + "</from>"); out.println(" <date>" + m[i].getSentDate() + "</date>"); if(isToday(m[i].getSentDate())) { out.println(" <dateString>" + getTime(m[i].getSentDate()) + "</dateString>"); } else { out.println(" <dateString>" + getDate(m[i].getSentDate()) + "</dateString>"); } // out.println("<msgNumber>"+m[i].getMessageNumber()+"</msgNumber>"); out.println(" <msgUID>"+f.getUID(m[i])+"</msgUID>"); out.println(" <subject>" + escapeXml(m[i].getSubject()) + "</subject>"); if (m[i].isSet(Flags.Flag.SEEN)) out.println(" <seen/>"); if (m[i].isSet(Flags.Flag.ANSWERED)) out.println(" <answered/>"); if (m[i].isSet(Flags.Flag.DELETED)) out.println(" <deleted/>"); if (m[i].isSet(Flags.Flag.RECENT)) out.println(" <recent/>"); out.println(" </message>"); } out.println("</messages>"); out.flush(); } catch (SAXException ex) { throw new ServletException(ex); } catch (ParserConfigurationException ex) { throw new ServletException(ex); } catch (NoSuchProviderException ex) { throw new ServletException(ex); } catch (MessagingException ex) { if(ex.getCause() != null) { if(ex.getCause() instanceof UnknownHostException) { // log the error this.log("unknown host exception",ex); // send some nice XML response.setContentType("text/xml; encoding=ISO-8859-1"); PrintWriter out = response.getWriter(); out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); out.println("<error>"); out.println(" <error-code>UNKNOWN-HOST</error-code>"); out.print(" <error-message>"); out.print(sc.getInitParameter("edu.yale.its.tp.imap.unknownHostMessage")); out.println("</error-message>"); out.println("</error>"); } else if(ex.getCause() instanceof ConnectException) { // log the error this.log("connection exception!",ex); // send some nice XML response.setContentType("text/xml; encoding=ISO-8859-1"); PrintWriter out = response.getWriter(); out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); out.println("<error>"); out.println(" <error-code>CONNECTION-FAILED</error-code>"); out.print(" <error-message>"); out.print(sc.getInitParameter("edu.yale.its.tp.imap.connectionFailedMessage")); out.println("</error-message>"); out.println("</error>"); } else { throw new ServletException(ex); } } } finally { try { // clean up if(f == null) { this.log("null mail folder in 'finally' block... user " + user); } else { if(f.isOpen()) { f.close(false); } } } catch (MessagingException ex) {} try { if(s != null && s.isConnected()) { s.close(); } } catch (MessagingException ex) {} } } /** * <p>Escapes invalid XML characters</p> * * @return Escaped-XML String * @author Shawn Bayern */ public static String escapeXml(String s) { if (s == null) return null; StringBuffer sb = new StringBuffer(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '&') sb.append("&"); else if (c == '<') sb.append("<"); else if (c == '>') sb.append(">"); else if (c == '"') sb.append("""); else if (c == '\'') sb.append("'"); else if (c >= 0 && c < 32) sb.append(" "); else if (c >= 127 && c < 159) sb.append(" "); else sb.append(c); } return sb.toString(); } /** * <p>Checks to see if the given <code>java.util.Date</code> object * is today.</p> * * @author Drew Mazurek */ private static boolean isToday(Date date) { if(date == null) { return false; } Calendar cal = GregorianCalendar.getInstance(); Calendar today = GregorianCalendar.getInstance(); cal.setTime(date); if(today.get(today.DATE) == cal.get(cal.DATE) && today.get(today.MONTH) == cal.get(cal.MONTH) && today.get(today.YEAR) == cal.get(cal.YEAR)) { return true; } else { return false; } } /** * <p>Returns a String representation of the time portion of the given * <code>java.util.Date</code> object.</p> * * @author Drew Mazurek */ private static String getTime(Date date) { if(date == null) { return ""; } Calendar cal = GregorianCalendar.getInstance(); cal.setTime(date); return getTime(cal); } /** * <p>Returns a String representation of the time portion of the given * <code>java.util.Calendar</code> object.</p> * * @author Drew Mazurek */ private static String getTime(Calendar cal) { if(cal == null) { return ""; } StringBuffer sb = new StringBuffer(); if(cal.get(cal.HOUR) == 0) { sb.append("12"); } else { sb.append(cal.get(cal.HOUR)); } sb.append(":"); if(cal.get(cal.MINUTE) < 10) { sb.append("0"); } sb.append(cal.get(cal.MINUTE)); // sb.append(":"); // if(cal.get(cal.SECOND) < 10) { // sb.append("0"); // } // sb.append(cal.get(cal.SECOND)); sb.append(" "); sb.append(ampm[cal.get(cal.AM_PM)]); return sb.toString(); } /** * <p>Returns a String representation of the date and time of the given * <code>java.util.Date</code> object.</p> * * @author Drew Mazurek */ private static String getDateAndTime(Date date) { if(date == null) { return ""; } Calendar cal = GregorianCalendar.getInstance(); cal.setTime(date); StringBuffer sb = new StringBuffer(); sb.append(dow[cal.get(cal.DAY_OF_WEEK)]); sb.append(", "); sb.append(months[cal.get(cal.MONTH)]); sb.append(" "); sb.append(cal.get(cal.DATE)); sb.append(", "); sb.append(cal.get(cal.YEAR)); sb.append(", "); sb.append(getTime(cal)); return sb.toString(); } /** * <p>Returns a String representation of the date of the given * <code>java.util.Date</code> object.</p> * * @author Drew Mazurek */ private static String getDate(Date date) { if(date == null) { return ""; } Calendar cal = GregorianCalendar.getInstance(); cal.setTime(date); StringBuffer sb = new StringBuffer(); sb.append(cal.get(cal.MONTH) + 1); sb.append("/"); sb.append(cal.get(cal.DATE)); sb.append("/"); sb.append(cal.get(cal.YEAR)); sb.append(", "); sb.append(getTime(cal)); return sb.toString(); } }