From dbkr at freenetproject.org Thu Aug 3 19:42:05 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Thu, 3 Aug 2006 19:42:05 +0000 (UTC) Subject: [Freemail] r9872 - in trunk/apps/Freemail/src/freemail: . fcp utils Message-ID: <20060803194205.F139F9BD09@emu.freenetproject.org> Author: dbkr Date: 2006-08-03 19:42:01 +0000 (Thu, 03 Aug 2006) New Revision: 9872 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/MailSite.java trunk/apps/Freemail/src/freemail/OutboundContact.java trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java trunk/apps/Freemail/src/freemail/utils/EmailAddress.java Log: Make short addresses (KSK mailsites) work Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -118,6 +118,16 @@ return new EmailAddress("anything@"+Base32.encode(mailsite.getKeyBody().getBytes())+".freemail"); } + public static EmailAddress getKSKFreemailAddress(File accdir) { + PropsFile accfile = getAccountFile(accdir); + + String alias = accfile.get("domain_alias"); + + if (alias == null) return null; + + return new EmailAddress("anything@"+alias+".freemail"); + } + public static RSAKeyParameters getPrivateKey(File accdir) { PropsFile props = getAccountFile(accdir); @@ -194,6 +204,21 @@ System.out.println("Account creation completed."); } + public static void addShortAddress(String username, String alias) throws Exception { + File accountdir = new File(DATADIR, username); + if (!accountdir.exists()) { + throw new Exception("No such account - "+username+"."); + } + + PropsFile accfile = getAccountFile(accountdir); + + MailSite ms = new MailSite(accfile); + + if (ms.insertAlias(alias)) { + accfile.put("domain_alias", alias); + } + } + public static boolean authenticate(String username, String password) { if (!validate_username(username)) return false; Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -32,6 +32,7 @@ String action = ""; String account = null; String newpasswd = null; + String alias = null; for (int i = 0; i < args.length; i++) { if (args[i].equals("--newaccount")) { @@ -52,6 +53,15 @@ } account = args[i - 1]; newpasswd = args[i]; + } else if (args[i].equals("--shortaddress")) { + action = args[i]; + i = i + 2; + if (args.length - 1 < i) { + System.out.println("Usage: --shortaddress "); + return; + } + account = args[i - 1]; + alias = args[i]; } else if (args[i].equals("-h")) { i++; if (args.length - 1 < i) { @@ -100,6 +110,16 @@ e.printStackTrace(); } return; + } else if (action.equals("--shortaddress")) { + try { + AccountManager.addShortAddress(account, alias); + } catch (Exception e) { + System.out.println("Couldn't add short address for "+account+". "+e.getMessage()); + e.printStackTrace(); + return; + } + System.out.println("Your short Freemail address is: 'anything"+alias+".freemail'. Your long address will continue to work."); + return; } File globaldatadir = new File(GLOBALDATADIR); Modified: trunk/apps/Freemail/src/freemail/MailSite.java =================================================================== --- trunk/apps/Freemail/src/freemail/MailSite.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/MailSite.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -1,11 +1,13 @@ package freemail; +import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import freemail.utils.PropsFile; import freemail.fcp.HighLevelFCPClient; import freemail.fcp.FCPInsertErrorMessage; +import freemail.fcp.FCPBadFileException; public class MailSite { private final PropsFile accprops; @@ -77,31 +79,47 @@ // leave this out for now, until we know whether we're doing it // with real USK redirects or fake put-a-USK-in-a-KSK redirect // are we set up to use a KSK domain alias too? - /*String alias = this.accprops.get("domain_alias"); + String alias = this.accprops.get("domain_alias"); if (alias != null) { - String targetKey = this.accprops.get("mailsite.pubkey"); - if (targetKey == null) return -1; - FreenetURI furi; - try { - furi = new FreenetURI(targetKey); - } catch (MalformedURLException mfue) { - return -1; - } - targetKey = "USK@"+furi.getKeyBody()+"/"+AccountManager.MAILSITE_SUFFIX+"/-1/"+MAILPAGE; + if (!this.insertAlias(alias)) return -1; + } + + return actualslot; + } + + public boolean insertAlias(String alias) { + String targetKey = this.accprops.get("mailsite.pubkey"); + if (targetKey == null) return false; + FreenetURI furi; + try { + furi = new FreenetURI(targetKey); + } catch (MalformedURLException mfue) { + return false; + } + targetKey = "USK@"+furi.getKeyBody()+"/"+AccountManager.MAILSITE_SUFFIX+"/-1/"+MAILPAGE; - System.out.println("Inserting mailsite redirect from "+"KSK@"+alias+ALIAS_SUFFIX+" to "+targetKey); + ByteArrayInputStream bis = new ByteArrayInputStream(targetKey.getBytes()); - FCPInsertErrorMessage err = cli.putRedirect("KSK@"+alias+ALIAS_SUFFIX, targetKey); + System.out.println("Inserting mailsite redirect from "+"KSK@"+alias+ALIAS_SUFFIX+" to "+targetKey); + + HighLevelFCPClient cli = new HighLevelFCPClient(); - if (err == null) { - System.out.println("Mailsite redirect inserted successfully"); - } else if (err.errorcode == FCPInsertErrorMessage.COLLISION) { - System.out.println("Mailsite alias collided - somebody is already using that alias! Choose another one!"); - } else { - System.out.println("Mailsite redirect insert failed, but did not collide."); - } - }*/ - - return actualslot; + FCPInsertErrorMessage err = null; + try { + err = cli.put(bis, "KSK@"+alias+ALIAS_SUFFIX); + } catch (FCPBadFileException bfe) { + // impossible + } + + if (err == null) { + System.out.println("Mailsite redirect inserted successfully"); + return true; + } else if (err.errorcode == FCPInsertErrorMessage.COLLISION) { + System.out.println("Mailsite alias collided - somebody is already using that alias! Choose another one!"); + return false; + } else { + System.out.println("Mailsite redirect insert failed, but did not collide."); + return false; + } } } Modified: trunk/apps/Freemail/src/freemail/OutboundContact.java =================================================================== --- trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -3,6 +3,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.BufferedReader; +import java.io.FileReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.FileNotFoundException; @@ -58,7 +60,7 @@ this.accdir = accdir; - if (this.address.getMailsiteKey() == null) { + if (!this.address.is_freemail_address()) { this.contactfile = null; throw new BadFreemailAddressException(); } else { @@ -70,7 +72,7 @@ if (!outbounddir.exists()) outbounddir.mkdir(); - File obctdir = new File(outbounddir, this.address.getMailsiteKey()); + File obctdir = new File(outbounddir, this.address.getSubDomain()); if (!obctdir.exists()) obctdir.mkdir(); @@ -85,7 +87,7 @@ public OutboundContact(File accdir, File ctdir) { this.accdir = accdir; this.address = new EmailAddress(); - this.address.domain = Base32.encode(ctdir.getName().getBytes())+".freemail"; + this.address.domain = ctdir.getName()+".freemail"; this.contactfile = new PropsFile(new File(ctdir, PROPSFILE_NAME)); @@ -127,7 +129,7 @@ } } else { - System.out.println("Sucessfully received CTS for "+this.address.getMailsiteKey()); + System.out.println("Sucessfully received CTS for "+this.address.getSubDomain()); cts.delete(); this.contactfile.put("status", "cts-received"); // delete inital slot for forward secrecy @@ -271,7 +273,7 @@ rtsmessage.append("messagetype=rts\r\n"); // must include who this RTS is to, otherwise we're vulnerable to surruptitious forwarding - rtsmessage.append("to="+this.address.getMailsiteKey()+"\r\n"); + rtsmessage.append("to="+this.address.getSubDomain()+"\r\n"); // get our mailsite URI String our_mailsite_uri = AccountManager.getAccountFile(this.accdir).get("mailsite.pubkey"); @@ -378,6 +380,36 @@ return false; } + if (!this.address.is_ssk_address()) { + // presumably a KSK 'redirect'. Follow it. + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(mailsite_file)); + } catch (FileNotFoundException fnfe) { + // impossible + } + + if (mailsite_file.length() > 512) { + System.out.println("Fatal: mailsite redirect too long. Ignoring."); + throw new OutboundContactFatalException("Mailsite redirect too long."); + } + + String addr; + try { + addr = br.readLine(); + br.close(); + } catch (IOException ioe) { + System.out.println("Warning: IO exception whilst reading mailsite redirect file: "+ioe.getMessage()); + return false; + } + mailsite_file.delete(); + mailsite_file = cli.fetch(addr); + if (mailsite_file == null) { + System.out.println("Failed to retrieve redirected mailsite "+addr); + return false; + } + } + System.out.println("got mailsite"); PropsFile mailsite = new PropsFile(mailsite_file); Modified: trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java =================================================================== --- trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -4,6 +4,7 @@ import java.lang.InterruptedException; import freemail.utils.PropsFile; +import freemail.utils.EmailAddress; public class SingleAccountWatcher implements Runnable { public static final String CONTACTS_DIR = "contacts"; @@ -52,7 +53,14 @@ //this.mf = new MailFetcher(this.mb, inbound_dir, Freemail.getFCPConnection()); // temporary info message until there's a nicer UI :) - System.out.println("Freemail address: "+AccountManager.getFreemailAddress(accdir)); + System.out.println("Secure Freemail address: "+AccountManager.getFreemailAddress(accdir)); + + EmailAddress shortaddr = AccountManager.getKSKFreemailAddress(accdir); + if (shortaddr != null) { + System.out.println("Short Freemail address (*probably* secure): "+shortaddr); + } else { + System.out.println("You don't have a short Freemail address. You could get one by running Freemail with the --shortaddress option, followed by your account name and the name you'd like. For example, 'java -jar freemail.jar --shortaddress bob bob' will give you the address 'anything at bob.freemail'. Try to pick something unique!"); + } } public void run() { Modified: trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java =================================================================== --- trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -137,42 +137,6 @@ } } - public synchronized FCPInsertErrorMessage putRedirect(String fromKey, String targetKey) { - FCPMessage msg = this.conn.getMessage("ClientPut"); - msg.headers.put("URI", fromKey); - msg.headers.put("Persistence", "connection"); - msg.headers.put("UploadFrom", "redirect"); - msg.headers.put("TargetURI", targetKey); - - while (true) { - try { - this.conn.doRequest(this, msg); - break; - } catch (NoNodeConnectionException nnce) { - try { - Thread.sleep(5000); - } catch (InterruptedException ie) { - } - } catch (FCPBadFileException bfe) { - // impossible - } - } - - this.donemsg = null; - while (this.donemsg == null) { - try { - this.wait(); - } catch (InterruptedException ie) { - } - } - - if (this.donemsg.getType().equalsIgnoreCase("PutSuccessful")) { - return null; - } else { - return new FCPInsertErrorMessage(donemsg); - } - } - public int SlotInsert(File data, String basekey, int minslot, String suffix) { int slot = minslot; boolean carryon = true; Modified: trunk/apps/Freemail/src/freemail/utils/EmailAddress.java =================================================================== --- trunk/apps/Freemail/src/freemail/utils/EmailAddress.java 2006-08-03 19:37:24 UTC (rev 9871) +++ trunk/apps/Freemail/src/freemail/utils/EmailAddress.java 2006-08-03 19:42:01 UTC (rev 9872) @@ -68,7 +68,7 @@ return this.getSubDomain().equalsIgnoreCase("nim"); } - private boolean is_ssk_address() { + public boolean is_ssk_address() { if (!this.is_freemail_address()) return false; String key; try { @@ -85,7 +85,7 @@ } // get the part of the domain before the '.freemail' - private String getSubDomain() { + public String getSubDomain() { String[] domparts = this.domain.split("\\.", 2); if (domparts.length < 2) return null; @@ -93,18 +93,15 @@ return domparts[0]; } - public String getMailsiteKey() { - if (this.is_ssk_address()) { - return new String (Base32.decode(this.getSubDomain())); - } else { - return this.getSubDomain(); - } - } - public String getMailpageKey() { if (this.is_ssk_address()) { + System.out.println("detected ssk address"); + return "USK@"+new String (Base32.decode(this.getSubDomain()))+"/"+AccountManager.MAILSITE_SUFFIX+"/"+AccountManager.MAILSITE_VERSION+"/"+MailSite.MAILPAGE; } else { + System.out.println("detected ksk address"); + System.out.println("KSK@"+this.getSubDomain()+MailSite.ALIAS_SUFFIX); + return "KSK@"+this.getSubDomain()+MailSite.ALIAS_SUFFIX; } } From dbkr at freenetproject.org Thu Aug 3 23:47:16 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Thu, 3 Aug 2006 23:47:16 +0000 (UTC) Subject: [Freemail] r9875 - in trunk/apps/Freemail/src/freemail: . utils Message-ID: <20060803234716.8E5F99BD05@emu.freenetproject.org> Author: dbkr Date: 2006-08-03 23:47:12 +0000 (Thu, 03 Aug 2006) New Revision: 9875 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/InboundContact.java trunk/apps/Freemail/src/freemail/MailMessage.java trunk/apps/Freemail/src/freemail/Postman.java trunk/apps/Freemail/src/freemail/RTSFetcher.java trunk/apps/Freemail/src/freemail/utils/EmailAddress.java Log: 'From' checking Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -37,7 +37,7 @@ private static final int ASYM_KEY_CERTAINTY = 80; public static final String MAILSITE_SUFFIX = "mailsite"; - public static final String MAILSITE_VERSION = "1"; + public static final String MAILSITE_VERSION = "-1"; public static void Create(String username) throws IOException { Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -118,7 +118,7 @@ e.printStackTrace(); return; } - System.out.println("Your short Freemail address is: 'anything"+alias+".freemail'. Your long address will continue to work."); + System.out.println("Your short Freemail address is: 'anything@"+alias+".freemail'. Your long address will continue to work."); return; } Modified: trunk/apps/Freemail/src/freemail/InboundContact.java =================================================================== --- trunk/apps/Freemail/src/freemail/InboundContact.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/InboundContact.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -7,11 +7,15 @@ import java.io.PrintStream; import java.io.IOException; import java.io.FileNotFoundException; +import java.net.MalformedURLException; import freemail.FreenetURI; import freemail.utils.PropsFile; +import freemail.utils.EmailAddress; import freemail.fcp.HighLevelFCPClient; +import org.archive.util.Base32; + public class InboundContact extends Postman implements SlotSaveCallback { private static final String IBCT_PROPSFILE = "props"; // how many slots should we poll past the last occupied one? @@ -137,6 +141,47 @@ this.ibct_props.put("slots", s); } + public boolean validateFrom(EmailAddress from) throws IOException { + String sd = from.getSubDomain(); + + if (from.is_ssk_address()) { + return Base32.encode(this.ibct_dir.getName().getBytes()).equalsIgnoreCase(sd); + } else { + // try to fetch that KSK redirect address + HighLevelFCPClient cli = new HighLevelFCPClient(); + + // quick sanity check + if (sd.indexOf("\r") > 0 || sd.indexOf("\n") > 0) return false; + + File result = cli.fetch("KSK@"+sd+MailSite.ALIAS_SUFFIX); + + if (result == null) { + // we just received the message so we can assume our + // network connection is healthy, and the mailsite + // ought to be easily retrievable, so fail. + // If this proves to be an issue, change it. + return false; + } + if (result.length() > 512) { + result.delete(); + return false; + } + BufferedReader br = new BufferedReader(new FileReader(result)); + + String line = br.readLine(); + br.close(); + result.delete(); + FreenetURI furi; + try { + furi = new FreenetURI(line); + } catch (MalformedURLException mfue) { + return false; + } + return this.ibct_dir.getName().equals(furi.getKeyBody()); + } + } + + private class MessageLog { private static final String LOGFILE = "log"; private final File logfile; Modified: trunk/apps/Freemail/src/freemail/MailMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -78,6 +78,45 @@ return buf.toString(); } + public String[] getHeadersAsArray(String name) { + Vector hdrs = new Vector(); + + Enumeration e = this.headers.elements(); + + while (e.hasMoreElements()) { + MailMessageHeader h = (MailMessageHeader) e.nextElement(); + + if (h.name.equalsIgnoreCase(name)) { + hdrs.add(h.val); + } + } + + String[] retval = new String[hdrs.size()]; + + e = hdrs.elements(); + + int i = 0; + while (e.hasMoreElements()) { + retval[i] = (String)e.nextElement(); + i++; + } + + return retval; + } + + public void removeHeader(String name, String val) { + int i; + + for (i = 0; i < this.headers.size(); i++) { + MailMessageHeader h = (MailMessageHeader) this.headers.elementAt(i); + + if (h.name.equalsIgnoreCase(name) && h.val.equalsIgnoreCase(val)) { + this.headers.remove(i); + i--; + } + } + } + public PrintStream writeHeadersAndGetStream() throws FileNotFoundException { this.os = new FileOutputStream(this.file); this.ps = new PrintStream(this.os); Modified: trunk/apps/Freemail/src/freemail/Postman.java =================================================================== --- trunk/apps/Freemail/src/freemail/Postman.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/Postman.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -9,6 +9,8 @@ import java.io.PrintStream; import java.io.IOException; +import freemail.utils.EmailAddress; + /** A postman is any class that delivers mail to an inbox. Simple, * if not politically correct. */ @@ -26,6 +28,20 @@ newmsg.readHeaders(brdr); + // validate the from header - or headers. There could be several. + String[] froms = newmsg.getHeadersAsArray("From"); + + int i; + for (i = 0; i < froms.length; i++) { + EmailAddress addr = new EmailAddress(froms[i]); + + if (!this.validateFrom(addr)) { + newmsg.removeHeader("From", froms[i]); + newmsg.addHeader("From", "**SPOOFED!** "+froms[i]); + } + } + + PrintStream ps = newmsg.writeHeadersAndGetStream(); String line; @@ -136,4 +152,9 @@ } return null; } + + public boolean validateFrom(EmailAddress from) throws IOException { + // override me! + return true; + } } Modified: trunk/apps/Freemail/src/freemail/RTSFetcher.java =================================================================== --- trunk/apps/Freemail/src/freemail/RTSFetcher.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/RTSFetcher.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -31,6 +31,8 @@ import freenet.support.io.LineReadingInputStream; import freenet.support.io.TooLongException; +import org.archive.util.Base32; + public class RTSFetcher implements SlotSaveCallback { private String rtskey; private File contact_dir; @@ -339,8 +341,15 @@ } String our_domain_alias = this.accprops.get("domain_alias"); + FreenetURI mailsite_furi; + try { + mailsite_furi = new FreenetURI(our_mailsite_keybody); + } catch (MalformedURLException mfe) { + return false; + } + String our_subdomain = Base32.encode(mailsite_furi.getKeyBody().getBytes()); - if (!rtsprops.get("to").equals(our_mailsite_keybody) && our_domain_alias != null && !rtsprops.get("to").equals(our_domain_alias)) { + if (!rtsprops.get("to").equalsIgnoreCase(our_subdomain) && our_domain_alias != null && !rtsprops.get("to").equals(our_domain_alias)) { System.out.println("Recieved an RTS message that was not intended for the recipient. Discarding."); msfile.delete(); rtsfile.delete(); Modified: trunk/apps/Freemail/src/freemail/utils/EmailAddress.java =================================================================== --- trunk/apps/Freemail/src/freemail/utils/EmailAddress.java 2006-08-03 21:37:33 UTC (rev 9874) +++ trunk/apps/Freemail/src/freemail/utils/EmailAddress.java 2006-08-03 23:47:12 UTC (rev 9875) @@ -95,13 +95,8 @@ public String getMailpageKey() { if (this.is_ssk_address()) { - System.out.println("detected ssk address"); - return "USK@"+new String (Base32.decode(this.getSubDomain()))+"/"+AccountManager.MAILSITE_SUFFIX+"/"+AccountManager.MAILSITE_VERSION+"/"+MailSite.MAILPAGE; } else { - System.out.println("detected ksk address"); - System.out.println("KSK@"+this.getSubDomain()+MailSite.ALIAS_SUFFIX); - return "KSK@"+this.getSubDomain()+MailSite.ALIAS_SUFFIX; } } From dbkr at freenetproject.org Fri Aug 4 16:29:31 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Fri, 4 Aug 2006 16:29:31 +0000 (UTC) Subject: [Freemail] r9884 - trunk/apps/Freemail/src/freemail Message-ID: <20060804162931.ED1F89C7A9@emu.freenetproject.org> Author: dbkr Date: 2006-08-04 16:29:28 +0000 (Fri, 04 Aug 2006) New Revision: 9884 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/InboundContact.java trunk/apps/Freemail/src/freemail/MessageSender.java trunk/apps/Freemail/src/freemail/OutboundContact.java Log: Fix stuff relating to ACKs, and fights between long addresses and short ones for the same outbound contact. Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-04 15:47:27 UTC (rev 9883) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-04 16:29:28 UTC (rev 9884) @@ -212,6 +212,8 @@ PropsFile accfile = getAccountFile(accountdir); + alias = alias.toLowerCase(); + MailSite ms = new MailSite(accfile); if (ms.insertAlias(alias)) { Modified: trunk/apps/Freemail/src/freemail/InboundContact.java =================================================================== --- trunk/apps/Freemail/src/freemail/InboundContact.java 2006-08-04 15:47:27 UTC (rev 9883) +++ trunk/apps/Freemail/src/freemail/InboundContact.java 2006-08-04 16:29:28 UTC (rev 9884) @@ -134,6 +134,13 @@ } System.out.println("You've got mail!"); sm.slotUsed(); + String ack_key = this.ibct_props.get("ackssk"); + if (ack_key == null) { + System.out.println("Warning! Can't send message acknowledgement - don't have an 'ackssk' entry! This message will eventually bounce, even though you've received it."); + continue; + } + ack_key += "ack-"+id; + AckProcrastinator.put(ack_key); } } Modified: trunk/apps/Freemail/src/freemail/MessageSender.java =================================================================== --- trunk/apps/Freemail/src/freemail/MessageSender.java 2006-08-04 15:47:27 UTC (rev 9883) +++ trunk/apps/Freemail/src/freemail/MessageSender.java 2006-08-04 16:29:28 UTC (rev 9884) @@ -15,6 +15,7 @@ public static final String OUTBOX_DIR = "outbox"; private static final int MIN_RUN_TIME = 60000; public static final String NIM_KEY_PREFIX = "KSK at freemail-nim-"; + private static final int MAX_TRIES = 10; private final File datadir; private Thread senderthread; @@ -35,7 +36,7 @@ this.senderthread.interrupt(); } - private synchronized void copyToOutbox(File src, File outbox, String to) throws IOException { + private void copyToOutbox(File src, File outbox, String to) throws IOException { File tempfile = File.createTempFile("fmail-msg-tmp", null, Freemail.getTempDir()); FileOutputStream fos = new FileOutputStream(tempfile); @@ -49,15 +50,22 @@ fis.close(); fos.close(); + this.moveToOutbox(tempfile, 0, to, outbox); + } + + // save a file to the outbox handling name collisions and atomicity + private void moveToOutbox(File f, int tries, String to, File outbox) { File destfile; int prefix = 1; - do { - String filename = prefix + ":" + to; - destfile = new File(outbox, filename); - prefix++; - } while (destfile.exists()); + synchronized (this.senderthread) { + do { + String filename = prefix + ":" + tries + ":" + to; + destfile = new File(outbox, filename); + prefix++; + } while (destfile.exists()); - tempfile.renameTo(destfile); + f.renameTo(destfile); + } } public void run() { @@ -100,12 +108,16 @@ } private void sendSingle(File accdir, File msg) { - String parts[] = msg.getName().split(":", 2); + String parts[] = msg.getName().split(":", 3); EmailAddress addr; - if (parts.length < 2) { - addr = new EmailAddress(parts[0]); + int tries; + if (parts.length < 3) { + System.out.println("Warning invalid file in outbox - deleting."); + msg.delete(); + return; } else { - addr = new EmailAddress(parts[1]); + tries = Integer.parseInt(parts[1]); + addr = new EmailAddress(parts[2]); } if (addr.domain == null || addr.domain.length() == 0) { @@ -122,6 +134,15 @@ } else { if (this.sendSecure(accdir, addr, msg)) { msg.delete(); + } else { + tries++; + if (tries > MAX_TRIES) { + if (Postman.bounceMessage(msg, new MessageBank(accdir.getName()), "Tried too many times to deliver this message, but it doesn't apear that this address even exists. If you're sure that it does, check your Freenet connection.")) { + msg.delete(); + } + } else { + this.moveToOutbox(msg, tries, parts[2], msg.getParentFile()); + } } } } @@ -134,6 +155,13 @@ } catch (BadFreemailAddressException bfae) { // bounce return Postman.bounceMessage(msg, new MessageBank(accdir.getName()), "The address that this message was destined for ("+addr+") is not a valid Freemail address."); + } catch (OutboundContactFatalException obfe) { + // bounce + return Postman.bounceMessage(msg, new MessageBank(accdir.getName()), obfe.getMessage()); + } catch (IOException ioe) { + // couldn't get the mailsite - try again if you're not ready + //to give up yet + return false; } return ct.sendMessage(msg); Modified: trunk/apps/Freemail/src/freemail/OutboundContact.java =================================================================== --- trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-08-04 15:47:27 UTC (rev 9883) +++ trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-08-04 16:29:28 UTC (rev 9884) @@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.FileNotFoundException; +import java.net.MalformedURLException; import java.math.BigInteger; import java.security.SecureRandom; @@ -52,10 +53,10 @@ private static final int AES_KEY_LENGTH = 256 / 8; // this is defined in the AES standard (although the Rijndael // algorithm does support other block sizes. - // we read 128 bytes for our IV, so it needs to be constant. + // we read 128 bytes for our IV, so it needs to be constant.) private static final int AES_BLOCK_LENGTH = 128 / 8; - public OutboundContact(File accdir, EmailAddress a) throws BadFreemailAddressException { + public OutboundContact(File accdir, EmailAddress a) throws BadFreemailAddressException, IOException, OutboundContactFatalException { this.address = a; this.accdir = accdir; @@ -72,8 +73,23 @@ if (!outbounddir.exists()) outbounddir.mkdir(); - File obctdir = new File(outbounddir, this.address.getSubDomain()); + if (!this.address.is_ssk_address()) { + String ssk_mailsite = this.fetchKSKRedirect(this.address.getMailpageKey()); + + if (ssk_mailsite == null) throw new IOException(); + + FreenetURI furi; + try { + furi = new FreenetURI(ssk_mailsite); + } catch (MalformedURLException mfue) { + throw new OutboundContactFatalException("The Freemail address points to an invalid redirect, and is therefore useless."); + } + + this.address.domain = Base32.encode(furi.getKeyBody().getBytes())+".freemail"; + } + File obctdir = new File(outbounddir, this.address.getSubDomain().toLowerCase()); + if (!obctdir.exists()) obctdir.mkdir(); @@ -111,7 +127,7 @@ if (ctskey == null) { this.init(); } - ctskey += "ack"; + ctskey += "cts"; HighLevelFCPClient fcpcli = new HighLevelFCPClient(); @@ -369,6 +385,43 @@ return true; } + // fetch the redirect (assumes that this is a KSK address) + private String fetchKSKRedirect(String key) throws OutboundContactFatalException { + HighLevelFCPClient cli = new HighLevelFCPClient(); + + System.out.println("Attempting to fetch mailsite redirect "+key); + File result = cli.fetch(key); + + if (result == null) { + System.out.println("Failed to retrieve mailsite redirect "+key); + return null; + } + + if (result.length() > 512) { + System.out.println("Fatal: mailsite redirect too long. Ignoring."); + result.delete(); + throw new OutboundContactFatalException("Mailsite redirect too long."); + } + + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(result)); + } catch (FileNotFoundException fnfe) { + // impossible + } + + String addr; + try { + addr = br.readLine(); + br.close(); + } catch (IOException ioe) { + System.out.println("Warning: IO exception whilst reading mailsite redirect file: "+ioe.getMessage()); + return null; + } + result.delete(); + return addr; + } + private boolean fetchMailSite() throws OutboundContactFatalException { HighLevelFCPClient cli = new HighLevelFCPClient(); @@ -380,36 +433,6 @@ return false; } - if (!this.address.is_ssk_address()) { - // presumably a KSK 'redirect'. Follow it. - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(mailsite_file)); - } catch (FileNotFoundException fnfe) { - // impossible - } - - if (mailsite_file.length() > 512) { - System.out.println("Fatal: mailsite redirect too long. Ignoring."); - throw new OutboundContactFatalException("Mailsite redirect too long."); - } - - String addr; - try { - addr = br.readLine(); - br.close(); - } catch (IOException ioe) { - System.out.println("Warning: IO exception whilst reading mailsite redirect file: "+ioe.getMessage()); - return false; - } - mailsite_file.delete(); - mailsite_file = cli.fetch(addr); - if (mailsite_file == null) { - System.out.println("Failed to retrieve redirected mailsite "+addr); - return false; - } - } - System.out.println("got mailsite"); PropsFile mailsite = new PropsFile(mailsite_file); @@ -610,8 +633,10 @@ continue; } - key += msgs[i].uid; + key += "ack-"+msgs[i].uid; + System.out.println("Looking for message ack on "+key); + File ack = fcpcli.fetch(key); if (ack != null) { System.out.println("Ack received for message "+msgs[i].uid+" on contact "+this.address.domain+". Now that's a job well done."); @@ -622,6 +647,7 @@ // delete inital slot for forward secrecy this.contactfile.remove("initialslot"); } else { + System.out.println("Failed to receive ack on "+key); if (System.currentTimeMillis() > msgs[i].first_send_time + FAIL_DELAY) { // give up and bounce the message File m = msgs[i].getMessageFile(); From dbkr at freenetproject.org Sat Aug 5 14:10:52 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sat, 5 Aug 2006 14:10:52 +0000 (UTC) Subject: [Freemail] r9902 - trunk/apps/Freemail/src/freemail Message-ID: <20060805141052.CBB869BE04@emu.freenetproject.org> Author: dbkr Date: 2006-08-05 14:10:49 +0000 (Sat, 05 Aug 2006) New Revision: 9902 Modified: trunk/apps/Freemail/src/freemail/Postman.java Log: Only check the first From header we find to avoid a DoS attack (bad guy sends mail with thousands of from headers, your client sits there checking them until the cows come home). Modified: trunk/apps/Freemail/src/freemail/Postman.java =================================================================== --- trunk/apps/Freemail/src/freemail/Postman.java 2006-08-05 13:49:40 UTC (rev 9901) +++ trunk/apps/Freemail/src/freemail/Postman.java 2006-08-05 14:10:49 UTC (rev 9902) @@ -32,13 +32,19 @@ String[] froms = newmsg.getHeadersAsArray("From"); int i; + boolean first = true; for (i = 0; i < froms.length; i++) { EmailAddress addr = new EmailAddress(froms[i]); - if (!this.validateFrom(addr)) { + if (first) { + if (!this.validateFrom(addr)) { + newmsg.removeHeader("From", froms[i]); + newmsg.addHeader("From", "**SPOOFED!** "+froms[i]); + } + } else { newmsg.removeHeader("From", froms[i]); - newmsg.addHeader("From", "**SPOOFED!** "+froms[i]); } + first = false; } From dbkr at freenetproject.org Sat Aug 5 22:08:23 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sat, 5 Aug 2006 22:08:23 +0000 (UTC) Subject: [Freemail] r9919 - in trunk/apps/Freemail: . src/freemail Message-ID: <20060805220823.B82EC9C039@emu.freenetproject.org> Author: dbkr Date: 2006-08-05 22:08:19 +0000 (Sat, 05 Aug 2006) New Revision: 9919 Modified: trunk/apps/Freemail/build.xml trunk/apps/Freemail/src/freemail/Freemail.java Log: Version info and build file tweaks. Modified: trunk/apps/Freemail/build.xml =================================================================== --- trunk/apps/Freemail/build.xml 2006-08-05 22:07:22 UTC (rev 9918) +++ trunk/apps/Freemail/build.xml 2006-08-05 22:08:19 UTC (rev 9919) @@ -1,5 +1,5 @@ - + @@ -35,7 +35,7 @@ - + @@ -55,6 +55,15 @@ + + + + + + + + + Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-05 22:07:22 UTC (rev 9918) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-05 22:08:19 UTC (rev 9919) @@ -9,6 +9,12 @@ import freemail.smtp.SMTPListener; public class Freemail { + // version info + public static final int VER_MAJOR = 0; + public static final int VER_MINOR = 1; + public static final int BUILD_NO = 1; + public static final String VERSION_TAG = "Pet Shop"; + private static final String TEMPDIRNAME = "temp"; private static final String DATADIR = "data"; private static final String GLOBALDATADIR = "globaldata"; @@ -34,6 +40,10 @@ String newpasswd = null; String alias = null; + System.out.println("This is Freemail version "+VER_MAJOR+"."+VER_MINOR+" build #"+BUILD_NO+" ("+VERSION_TAG+")"); + System.out.println("Freemail is released under the terms of the GNU Lesser General Public License. Freemail is provided WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see the LICENSE file included with this distribution."); + System.out.println(""); + for (int i = 0; i < args.length; i++) { if (args[i].equals("--newaccount")) { action = args[i]; From dbkr at freenetproject.org Sun Aug 6 16:02:23 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sun, 6 Aug 2006 16:02:23 +0000 (UTC) Subject: [Freemail] r9941 - in trunk/apps/Freemail: . src/freemail Message-ID: <20060806160223.E9E659BE31@emu.freenetproject.org> Author: dbkr Date: 2006-08-06 16:02:08 +0000 (Sun, 06 Aug 2006) New Revision: 9941 Modified: trunk/apps/Freemail/README trunk/apps/Freemail/src/freemail/AccountManager.java Log: Send welcome / new address messages, and update README Modified: trunk/apps/Freemail/README =================================================================== --- trunk/apps/Freemail/README 2006-08-06 13:40:16 UTC (rev 9940) +++ trunk/apps/Freemail/README 2006-08-06 16:02:08 UTC (rev 9941) @@ -1,52 +1,40 @@ -This is the start of an email-over-Freenet 0.7 implementation. It's by no means -finished yet, and hence is only provided as source through subversion. +Thanks for trying Freemail! -Currently only 'NIM Mode' is supported, 'NIM' coming from the concept of a -'nearly instant message' which was simply messages posted to KSKs, usually -used on freesites in older versions of Freenet. I'd also suggest 'Notably -Insecure Messaging'. It's not secure. At all. Anyone can read your mail. -You can use PGP or equivalent, but it's still easy to spam and hijack -addresses and whatnot. +This is the first release of Freemail, and so may (read: does) have bugs that I haven't found yet. Please do report them at http://bugs.freenetproject.org/. -Proper, secure implemenations of the Freemail protocol will come later. +Using Freemail +============== +You can compile from source: -All the data, including your passwd file, will most likley end up world -readable. Under unix, you could try running Freemail with a modified umask -if this bothers you, but I don't believe there is a portable way of doing -this in Java (or I haven't found it). - -Finally, there *will* be backwards incompatable changes, so don't get -attatched to anything just yet. - -Now you've read that (you have read that, right?): - compile: (however you compile Java, an ant buildfile is supplied) run with --newaccount to create an account, eg: -java -cp build/ freemail.Freemail --newaccount fred +...or you can fetch the most recent Freemail jar from: http://downloads.freenetproject.org/alpha/Freemail/Freemail.jar +Once you've done one of those steps, create an account (replace java -jar with however you run jar files on your system): + +java -jar Freemail.jar --newaccount fred + Use --passwd to set your password -java -cp build/ freemail.Freemail --passwd fred fredspassword +java -jar Freemail.jar --passwd fred fredspassword Run: -java -cp build/ Freemail.FNMail +java -jar Freemail.jar +(You can also specify the address and port of your Freenet node using -h and -p +respectively, if they are not the defaults). + Set up your email client to point at IMAP port 3143 and SMTP port 3025. -Your address is @nim.Freemail +You can get a short address by doing: -And yes, in case you were wondering, no - there's nothing to stop someone -else using the same address. I did say it was insecure ;) +java -jar Freemail.jar --shortaddress bob bobshome -Send me a message if you like, I promise to reply if it works :) +...which will give you the address @bobshome.freemail -dbkr at nim.Freemail -(and since anyone can read Freemail messages right now, my Freemail public key -can be found at USK at vjETpEgDH-6EzlngZoO8KgOZm-B8AAlvZ-6oP6aQmow,DZYYfhpOxIrtdCNJiflIPjd0Qy8nA1d3Dwy86dcdhu0,AQABAAE/dbkr/10/contact/pubkey.fnmail.asc, or failing that, http://accidentalegg.co.uk/contact/pubkey.fnmail) +Feel free to Freemail me on dave at dbkr.freemail! If that doesn't work, my real email address is dbkr at freenetproject.org. -If it doesn't, dbkr at freenetproject.org! - Good luck! Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-06 13:40:16 UTC (rev 9940) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-06 16:02:08 UTC (rev 9941) @@ -2,9 +2,12 @@ import java.io.File; import java.io.PrintWriter; +import java.io.PrintStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Random; +import java.util.Date; +import java.text.SimpleDateFormat; import java.security.SecureRandom; import java.math.BigInteger; import java.net.MalformedURLException; @@ -48,7 +51,9 @@ File accountdir = new File(DATADIR, username); if (!accountdir.mkdir()) throw new IOException("Failed to create directory "+username+" in "+DATADIR); - getAccountFile(accountdir); + PropsFile accfile = getAccountFile(accountdir); + + putWelcomeMessage(username, getFreemailAddress(accountdir)); } public static void setupNIM(String username) throws IOException { @@ -218,6 +223,34 @@ if (ms.insertAlias(alias)) { accfile.put("domain_alias", alias); + + SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z"); + EmailAddress to = getKSKFreemailAddress(accountdir); + + MessageBank mb = new MessageBank(username); + + MailMessage m = mb.createMessage(); + + m.addHeader("From", "Freemail Daemon "); + m.addHeader("To", to.toString()); + m.addHeader("Subject", "Your New Address"); + m.addHeader("Date", sdf.format(new Date())); + m.addHeader("Content-Type", "text/plain;charset=\"us-ascii\""); + m.addHeader("Content-Transfer-Encoding", "7bit"); + m.addHeader("Content-Disposition", "inline"); + + PrintStream ps = m.writeHeadersAndGetStream(); + + ps.println("Hi!"); + ps.println(""); + ps.println("This is to inform you that your new short Freemail address is:"); + ps.println(""); + ps.println(to); + ps.println(""); + ps.println("Your long Freemail address will continue to work. If you have had previous short addresses, you should not rely on them working any longer."); + + + m.commit(); } } @@ -252,4 +285,47 @@ if (username.matches("[\\w_]*")) return true; return false; } + + private static void putWelcomeMessage(String username, EmailAddress to) throws IOException { + SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z"); + + MessageBank mb = new MessageBank(username); + + MailMessage m = mb.createMessage(); + + m.addHeader("From", "Dave Baker "); + m.addHeader("To", to.toString()); + m.addHeader("Subject", "Welcome to Freemail!"); + m.addHeader("Date", sdf.format(new Date())); + m.addHeader("Content-Type", "text/plain;charset=\"us-ascii\""); + m.addHeader("Content-Transfer-Encoding", "7bit"); + m.addHeader("Content-Disposition", "inline"); + + PrintStream ps = m.writeHeadersAndGetStream(); + + ps.println("Welcome to Freemail!"); + ps.println(""); + ps.println("Thanks for downloading and testing Freemail. You can get started and send me a Freemail now by hitting 'reply'."); + ps.println("Your new Freemail address is:"); + ps.println(""); + ps.println(to); + ps.println(""); + ps.println("But you'll probably want a shorter one. To do this, run Freemail with the --shortaddress argument, followed by your account name and the part you'd like before the '.freemail'. For example:"); + ps.println(""); + ps.println("java -jar freemail.jar --shortaddress bob bobshouse"); + ps.println(""); + ps.println("Try to pick something unique - Freemail will tell you if somebody has already taken the address you want. These short addresses are *probably* secure, but not absolutely. If you want to be sure, use the long address."); + ps.println(""); + ps.println("If you find a bug, or would like something changed in Freemail, visit our bug tracker at https://bugs.freenetproject.org/(and select 'Freemail' in the top right). You can also drop into #freemail on irc.freenode.net to discuss, or sign up tp the mailing list at http://emu.freenetproject.org/cgi-bin/mailman/listinfo/freemail."); + ps.println(""); + ps.println("Happy Freemailing!"); + ps.println(""); + ps.println(""); + ps.println(""); + ps.println(""); + ps.println("Dave Baker"); + ps.println("(Freemail developer)"); + + m.commit(); + } } From dbkr at freenetproject.org Sun Aug 6 16:43:48 2006 From: dbkr at freenetproject.org (Dave Baker) Date: Sun, 6 Aug 2006 17:43:48 +0100 Subject: [Freemail] Freemail Release 0.1 build 1 - "Pet Shop" Message-ID: <200608061743.48161.dbkr@freenetproject.org> Hi! In the "release early, release often" spirit of open source, the first release of Freemail is here. You can check out build 1 from the usual place: svn co https://emu.freenetproject.org/svn/trunk/apps/Freemail, or in the "here's one I made earlier" spirit, you can get a pre-compiled jar from http://downloads.freenetproject.org/alpha/Freemail/Freemail.jar. For build / usage instructions, take a look at the README file at: http://emu.freenetproject.org/cgi-bin/viewcvs.cgi/*checkout*/trunk/apps/Freemail/README Your feedback would be very welcome, in any or all of the following ways: Bug tracker: https://bugs.freenetproject.org/ (select 'Freemail' in the top right) IRC Channel: #freemail on irc.freenode.net My Freemail address: dave at dbkr.freemail or this mailing list! Happy Freemailing! Dave From toad at amphibian.dyndns.org Mon Aug 7 18:23:51 2006 From: toad at amphibian.dyndns.org (Matthew Toseland) Date: Mon, 7 Aug 2006 19:23:51 +0100 Subject: [Freemail] Freemail doesn't work with fetchmail Message-ID: <20060807182351.GA4995@amphibian.dyndns.org> fetchmail: 6.2.5 querying 127.0.0.1 (protocol IMAP) at Mon 07 Aug 2006 19:03:24BST: poll started fetchmail: IMAP< * OK [CAPABILITY IMAP4rev1] Freemail ready - hit me with your rhythm stick. fetchmail: IMAP> A0001 CAPABILITY fetchmail: IMAP< * CAPABILITY IMAP4rev1 AUTH=LOGIN fetchmail: IMAP< A0001 OK Capability completed fetchmail: IMAP> A0002 LOGIN "toad" * fetchmail: IMAP< A0002 OK Logged in fetchmail: IMAP> A0003 SELECT "INBOX" fetchmail: IMAP< * FLAGS (\Recent \Seen) fetchmail: IMAP< * OK [PERMANENTFLAGS (\*)] fetchmail: IMAP< * 2 EXISTS fetchmail: IMAP< * 2 RECENT fetchmail: IMAP< A0003 OK [READ-WRITE] Done fetchmail: IMAP> A0004 EXPUNGE fetchmail: IMAP< * 0 EXPUNGE fetchmail: IMAP< * 1 EXPUNGE fetchmail: IMAP< A0004 OK Mailbox closed 2 messages for toad at 127.0.0.1. fetchmail: IMAP> A0005 FETCH 1:2 RFC822.SIZE fetchmail: IMAP< * 1 FETCH (RFC822.SIZE 1593) fetchmail: IMAP< * 2 FETCH (RFC822.SIZE 481) fetchmail: IMAP< A0005 OK Fetch completed fetchmail: IMAP> A0006 FETCH 1 RFC822.HEADER fetchmail: IMAP< * 1 FETCH (A0006 BAD Unknown attribute in list or unterminatedlist fetchmail: terminated with signal 2 Sanitized /etc/fetchmailrc attached. If you apt-get install fetchmail you should be able to just drop that in. -- Matthew J Toseland - toad at amphibian.dyndns.org Freenet Project Official Codemonkey - http://freenetproject.org/ ICTHUS - Nothing is impossible. Our Boss says so. -------------- next part -------------- # /etc/fetchmailrc for system-wide daemon mode # This file must be chmod 0600, owner fetchmail # Daemon configuration # Apparently no longer set in /etc/fetchmail/defaults :( set daemon 30 # Pool every 5 minutes set syslog # log through syslog facility set no bouncemail # avoid loss on 4xx errors # on the other hand, 5xx errors get # more dangerous... ########################################################################## # Hosts to pool ########################################################################## # Defaults =============================================================== # Set antispam to -1, since it is far safer to use that together with # no bouncemail defaults: antispam -1 batchlimit 100 fetchall # Freemail poll 127.0.0.1 with protocol imap port 3143 user toad there password blah is toad here; -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: Digital signature Url : http://emu.freenetproject.org/pipermail/freemail/attachments/20060807/151d2b78/attachment.pgp From dbkr at freenetproject.org Mon Aug 7 18:58:59 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Mon, 7 Aug 2006 18:58:59 +0000 (UTC) Subject: [Freemail] r9950 - trunk/apps/Freemail/src/freemail Message-ID: <20060807185859.6C01E9CA42@emu.freenetproject.org> Author: dbkr Date: 2006-08-07 18:58:55 +0000 (Mon, 07 Aug 2006) New Revision: 9950 Modified: trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/MessageSender.java Log: Message about existing accounts of the same name, and fix an NPE. Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-07 14:55:21 UTC (rev 9949) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-07 18:58:55 UTC (rev 9950) @@ -108,7 +108,7 @@ System.out.println("Account created for "+account+". You may now set a password with --passwd "); //System.out.println("For the time being, you address is "+account+"@nim.freemail"); } catch (IOException ioe) { - System.out.println("Couldn't create account. Please check write access to Freemail's working directory. Error: "+ioe.getMessage()); + System.out.println("Couldn't create account. Please check write access to Freemail's working directory. If you want to overwrite your account, delete the appropriate directory manually in 'data' first. Freemail will intentionally not overwrite it. Error: "+ioe.getMessage()); } return; } else if (action.equals("--passwd")) { Modified: trunk/apps/Freemail/src/freemail/MessageSender.java =================================================================== --- trunk/apps/Freemail/src/freemail/MessageSender.java 2006-08-07 14:55:21 UTC (rev 9949) +++ trunk/apps/Freemail/src/freemail/MessageSender.java 2006-08-07 18:58:55 UTC (rev 9950) @@ -99,6 +99,7 @@ private void sendDir(File accdir, File dir) { File[] files = dir.listFiles(); + if (dir == null) return; for (int i = 0; i < files.length; i++) { if (files[i].getName().startsWith(".")) continue; From dbkr at freenetproject.org Mon Aug 7 19:36:12 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Mon, 7 Aug 2006 19:36:12 +0000 (UTC) Subject: [Freemail] r9951 - trunk/apps/Freemail/src/freemail Message-ID: <20060807193612.9F7689CA20@emu.freenetproject.org> Author: dbkr Date: 2006-08-07 19:36:08 +0000 (Mon, 07 Aug 2006) New Revision: 9951 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/Freemail.java Log: More checking - maybe fix #629. Correct a usage message. Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-07 18:58:55 UTC (rev 9950) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-07 19:36:08 UTC (rev 9951) @@ -96,9 +96,10 @@ } public static PropsFile getAccountFile(File accdir) { + System.out.println("==="+accdir); PropsFile accfile = new PropsFile(new File(accdir, ACCOUNT_FILE)); - if (!accfile.exists()) { + if (accdir.exists() && !accfile.exists()) { initAccFile(accfile); } Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-07 18:58:55 UTC (rev 9950) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-07 19:36:08 UTC (rev 9951) @@ -67,7 +67,7 @@ action = args[i]; i = i + 2; if (args.length - 1 < i) { - System.out.println("Usage: --shortaddress "); + System.out.println("Usage: --shortaddress "); return; } account = args[i - 1]; @@ -159,6 +159,7 @@ for (int i = 0; i < files.length; i++) { if (files[i].getName().equals(".") || files[i].getName().equals("..")) continue; + if (!files[i].isDirectory()) continue; Thread t = new Thread(new SingleAccountWatcher(files[i]), "Account Watcher for "+files[i].getName()); t.setDaemon(true); From dbkr at freenetproject.org Tue Aug 8 12:10:29 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Tue, 8 Aug 2006 12:10:29 +0000 (UTC) Subject: [Freemail] r9964 - in trunk/apps/Freemail/src/freemail: . imap Message-ID: <20060808121029.0A5019CA24@emu.freenetproject.org> Author: dbkr Date: 2006-08-08 12:10:25 +0000 (Tue, 08 Aug 2006) New Revision: 9964 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/MailMessage.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java Log: Implement rfc822.header on IMAP fetch command, maybe fixing #629 (fetchmail compatability). Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-08 10:27:06 UTC (rev 9963) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-08 12:10:25 UTC (rev 9964) @@ -96,7 +96,6 @@ } public static PropsFile getAccountFile(File accdir) { - System.out.println("==="+accdir); PropsFile accfile = new PropsFile(new File(accdir, ACCOUNT_FILE)); if (accdir.exists() && !accfile.exists()) { Modified: trunk/apps/Freemail/src/freemail/MailMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-08 10:27:06 UTC (rev 9963) +++ trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-08 12:10:25 UTC (rev 9964) @@ -117,6 +117,22 @@ } } + public String getAllHeadersAsString() { + Enumeration e = this.headers.elements(); + StringBuffer buf = new StringBuffer(); + + while (e.hasMoreElements()) { + MailMessageHeader h = (MailMessageHeader) e.nextElement(); + + buf.append(h.name); + buf.append(": "); + buf.append(h.val); + buf.append("\r\n"); + } + + return buf.toString(); + } + public PrintStream writeHeadersAndGetStream() throws FileNotFoundException { this.os = new FileOutputStream(this.file); this.ps = new PrintStream(this.os); Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-08 10:27:06 UTC (rev 9963) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-08 12:10:25 UTC (rev 9964) @@ -440,6 +440,10 @@ this.ps.flush(); attr = attr.substring("body".length()); return this.sendBody(mmsg, attr); + } else if (attr.startsWith("rfc822.header")) { + this.ps.print(a.substring(0, "rfc822.header".length())); + this.ps.flush(); + return this.sendBody(mmsg, "header"); } if (val == null) @@ -475,7 +479,7 @@ String[] parts = IMAPMessage.doSplit(attr, '(', ')'); for (int i = 0; i < parts.length; i++) { - if (parts[i].toLowerCase().equals("header.fields")) { + if (parts[i].equalsIgnoreCase("header.fields")) { i++; this.ps.print("[HEADER.FIELDS "+parts[i]+"] "); if (parts[i].charAt(0) == '(') @@ -493,6 +497,14 @@ for (int j = 0; j < fields.length; j++) { buf.append(mmsg.getHeaders(fields[j])); } + } else if (parts[i].equalsIgnoreCase("header")) { + // send all the header fields + try { + mmsg.readHeaders(); + } catch (IOException ioe) { + } + + buf.append(mmsg.getAllHeadersAsString()); } this.ps.print("{"+buf.length()+"}\r\n"+buf.toString()); From dbkr at freenetproject.org Tue Aug 8 22:45:26 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Tue, 8 Aug 2006 22:45:26 +0000 (UTC) Subject: [Freemail] r9975 - in trunk/apps/Freemail/src/freemail: . imap Message-ID: <20060808224526.DFF089CA3B@emu.freenetproject.org> Author: dbkr Date: 2006-08-08 22:45:23 +0000 (Tue, 08 Aug 2006) New Revision: 9975 Modified: trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java trunk/apps/Freemail/src/freemail/imap/IMAPMessageFlags.java Log: Build 2: Outlook Express compatability (finally) and general IMAP fixes. Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-08 20:54:04 UTC (rev 9974) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-08 22:45:23 UTC (rev 9975) @@ -12,7 +12,7 @@ // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; - public static final int BUILD_NO = 1; + public static final int BUILD_NO = 2; public static final String VERSION_TAG = "Pet Shop"; private static final String TEMPDIRNAME = "temp"; Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-08 20:54:04 UTC (rev 9974) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-08 22:45:23 UTC (rev 9975) @@ -8,6 +8,8 @@ import java.io.IOException; import java.util.SortedMap; import java.lang.NumberFormatException; +import java.text.SimpleDateFormat; +import java.util.Date; import freemail.MessageBank; import freemail.MailMessage; @@ -15,6 +17,8 @@ import freemail.utils.EmailAddress; public class IMAPHandler implements Runnable { + private static final String CAPABILITY = "IMAP4rev1 AUTH=LOGIN"; + final Socket client; final OutputStream os; final PrintStream ps; @@ -53,7 +57,7 @@ } private void sendWelcome() { - this.ps.print("* OK [CAPABILITY IMAP4rev1] Freemail ready - hit me with your rhythm stick.\r\n"); + this.ps.print("* OK [CAPABILITY "+CAPABILITY+"] Freemail ready - hit me with your rhythm stick.\r\n"); } private void dispatch(IMAPMessage msg) { @@ -84,6 +88,8 @@ this.handle_namespace(msg); } else if (msg.type.equals("lsub")) { this.handle_lsub(msg); + } else if (msg.type.equals("status")) { + this.handle_status(msg); } else { this.reply(msg, "NO Sorry - not implemented"); } @@ -110,7 +116,7 @@ } private void handle_capability(IMAPMessage msg) { - this.sendState("CAPABILITY IMAP4rev1 AUTH=LOGIN"); + this.sendState("CAPABILITY "+CAPABILITY); this.reply(msg, "OK Capability completed"); } @@ -147,7 +153,7 @@ if (mbname == null) { // return hierarchy delimiter this.sendState("LIST (\\Noselect) \".\" \"\""); - } else if (mbname.equals("%") || mbname.equals("INBOX")) { + } else if (mbname.equals("%") || mbname.equals("INBOX") || mbname.equals("*") || mbname.equals("INBOX*")) { this.sendState("LIST (\\NoInferiors) \".\" \"INBOX\""); } @@ -169,8 +175,8 @@ mbname = trimQuotes(msg.args[0]).toLowerCase(); if (mbname.equals("inbox")) { - this.sendState("FLAGS (\\Recent \\Seen)"); - this.sendState("OK [PERMANENTFLAGS (\\*)]"); + this.sendState("FLAGS ("+IMAPMessageFlags.getAllFlagsAsString()+")"); + this.sendState("OK [PERMANENTFLAGS (\\* "+IMAPMessageFlags.getPermanentFlagsAsString()+")] Limited"); SortedMap msgs = this.mb.listMessages(); @@ -193,6 +199,7 @@ this.sendState(numexists+" EXISTS"); this.sendState(numrecent+" RECENT"); + this.sendState("OK [UIDVALIDITY 1] Ok"); this.reply(msg, "OK [READ-WRITE] Done"); } else { @@ -316,7 +323,9 @@ int msgnum = 1; if (msg.args[0].toLowerCase().equals("fetch")) { + int oldsize = msgs.size(); msgs = msgs.tailMap(new Integer(from)); + msgnum += (oldsize - msgs.size()); while (msgs.size() > 0) { Integer curuid = (Integer)msgs.firstKey(); if (curuid.intValue() > to) { @@ -444,6 +453,15 @@ this.ps.print(a.substring(0, "rfc822.header".length())); this.ps.flush(); return this.sendBody(mmsg, "header"); + } else if (attr.startsWith("internaldate")) { + val = mmsg.getFirstHeader("Date"); + if (val == null) { + // possibly should keep our own dates... + SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z"); + + val = sdf.format(new Date()); + } + val = "\""+val+"\""; } if (val == null) @@ -497,6 +515,7 @@ for (int j = 0; j < fields.length; j++) { buf.append(mmsg.getHeaders(fields[j])); } + if (buf.length() == 0) buf.append("\r\n"); } else if (parts[i].equalsIgnoreCase("header")) { // send all the header fields try { @@ -652,6 +671,83 @@ this.reply(msg, "OK Namespace completed"); } + private void handle_status(IMAPMessage msg) { + if (!this.verify_auth(msg)) { + return; + } + + if (msg.args.length < 2) { + this.reply(msg, "BAD Not enough arguments"); + return; + } + + String mbname = trimQuotes(msg.args[0]); + + // for now + if (!mbname.equalsIgnoreCase("INBOX")) { + this.reply(msg, "BAD No such mailbox"); + return; + } + + SortedMap msgs = this.mb.listMessages(); + + // gather statistics + int numrecent = 0; + int numunseen = 0; + int nummessages = msgs.size(); + int lastuid = 0; + while (msgs.size() > 0) { + Integer current = (Integer)(msgs.firstKey()); + MailMessage m =(MailMessage)msgs.get(msgs.firstKey()); + + // if it's recent, add to the tally + if (m.flags.get("\\Recent")) numrecent++; + + // is it unseen? + if (!m.flags.get("\\Seen")) numunseen++; + + if (m.getUID() > lastuid) lastuid = m.getUID(); + + msgs = msgs.tailMap(new Integer(current.intValue()+1)); + } + + StringBuffer buf = new StringBuffer(); + buf.append("STATUS "); + buf.append(msg.args[0]); + buf.append(" ("); + + + // output the required information + int i; + boolean first = true; + for (i = 1; i < msg.args.length; i++) { + String arg = msg.args[i]; + + if (arg.startsWith("(")) arg = arg.substring(1); + if (arg.endsWith(")")) arg = arg.substring(0, arg.length() - 1); + + if (!first) buf.append(" "); + first = false; + buf.append(arg); + buf.append(" "); + if (arg.equalsIgnoreCase("messages")) { + buf.append(Integer.toString(nummessages)); + } else if (arg.equalsIgnoreCase("recent")) { + buf.append(Integer.toString(numrecent)); + } else if (arg.equalsIgnoreCase("unseen")) { + buf.append(Integer.toString(numunseen)); + } else if (arg.equalsIgnoreCase("uidnext")) { + buf.append(Integer.toString(lastuid + 1)); + } else if (arg.equalsIgnoreCase("uidvalidity")) { + buf.append("1"); + } + } + + buf.append(")"); + this.sendState(buf.toString()); + this.reply(msg, "OK STATUS completed"); + } + private String getEnvelope(MailMessage mmsg) { StringBuffer buf = new StringBuffer("("); Modified: trunk/apps/Freemail/src/freemail/imap/IMAPMessageFlags.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPMessageFlags.java 2006-08-08 20:54:04 UTC (rev 9974) +++ trunk/apps/Freemail/src/freemail/imap/IMAPMessageFlags.java 2006-08-08 22:45:23 UTC (rev 9975) @@ -22,6 +22,44 @@ "\\Draft", "\\Recent", }; + + public static final String[] permanentFlags = { + "\\Answered", + "\\Flagged", + "\\Deleted", + "\\Draft", + "\\Recent", + }; + + public static String getAllFlagsAsString() { + int i; + StringBuffer buf = new StringBuffer(); + boolean first = true; + + for (i = 0; i < allFlags.length; i++) { + if (!first) + buf.append(" "); + first = false; + buf.append(allFlags[i]); + } + + return buf.toString(); + } + + public static String getPermanentFlagsAsString() { + int i; + StringBuffer buf = new StringBuffer(); + boolean first = true; + + for (i = 0; i < permanentFlags.length; i++) { + if (!first) + buf.append(" "); + first = false; + buf.append(permanentFlags[i]); + } + + return buf.toString(); + } private Vector flags; From dbkr at freenetproject.org Wed Aug 9 18:24:39 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Wed, 9 Aug 2006 18:24:39 +0000 (UTC) Subject: [Freemail] r10004 - in trunk/apps/Freemail/src/freemail: . imap smtp Message-ID: <20060809182439.0E0629CA45@emu.freenetproject.org> Author: dbkr Date: 2006-08-09 18:24:35 +0000 (Wed, 09 Aug 2006) New Revision: 10004 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/MailMessage.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java trunk/apps/Freemail/src/freemail/smtp/SMTPHandler.java Log: Fix bif SMTP plain auth bug - Thunderbird /mozilla mail couldn't send mail. Also some more IMAP fixes. Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-09 16:51:00 UTC (rev 10003) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-09 18:24:35 UTC (rev 10004) @@ -282,6 +282,7 @@ } private static boolean validate_username(String username) { + if (username.length() < 1) return false; if (username.matches("[\\w_]*")) return true; return false; } Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-09 16:51:00 UTC (rev 10003) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-09 18:24:35 UTC (rev 10004) @@ -12,7 +12,7 @@ // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; - public static final int BUILD_NO = 2; + public static final int BUILD_NO = 3; public static final String VERSION_TAG = "Pet Shop"; private static final String TEMPDIRNAME = "temp"; Modified: trunk/apps/Freemail/src/freemail/MailMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-09 16:51:00 UTC (rev 10003) +++ trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-09 18:24:35 UTC (rev 10004) @@ -176,6 +176,8 @@ } public void readHeaders(BufferedReader bufrdr) throws IOException { + if (this.headers.size() > 0) return; + String line; String[] parts = null; while ( (line = bufrdr.readLine()) != null) { Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-09 16:51:00 UTC (rev 10003) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-09 18:24:35 UTC (rev 10004) @@ -144,6 +144,11 @@ mbname = msg.args[1]; } + String replyprefix = "LIST"; + if (msg.type.equals("lsub")) { + replyprefix = "LSUB"; + } + refname = trimQuotes(refname); if (refname.length() == 0) refname = null; @@ -152,12 +157,12 @@ if (mbname == null) { // return hierarchy delimiter - this.sendState("LIST (\\Noselect) \".\" \"\""); + this.sendState(replyprefix+" (\\Noselect) \".\" \"\""); } else if (mbname.equals("%") || mbname.equals("INBOX") || mbname.equals("*") || mbname.equals("INBOX*")) { - this.sendState("LIST (\\NoInferiors) \".\" \"INBOX\""); + this.sendState(replyprefix+" (\\NoInferiors) \".\" \"INBOX\""); } - this.reply(msg, "OK LIST completed"); + this.reply(msg, "OK "+replyprefix+" completed"); } private void handle_select(IMAPMessage msg) { Modified: trunk/apps/Freemail/src/freemail/smtp/SMTPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/smtp/SMTPHandler.java 2006-08-09 16:51:00 UTC (rev 10003) +++ trunk/apps/Freemail/src/freemail/smtp/SMTPHandler.java 2006-08-09 18:24:35 UTC (rev 10004) @@ -161,6 +161,10 @@ // username twice. Some think only once. // This will work either way. uname = creds[0]; + // there may be a null first (is this always the case?) + if (uname.length() < 1) { + uname = creds[1]; + } password = creds[creds.length - 1]; } else { this.ps.print("504 Auth type unimplemented - weren't you listening?\r\n"); From dbkr at freenetproject.org Wed Aug 9 22:58:39 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Wed, 9 Aug 2006 22:58:39 +0000 (UTC) Subject: [Freemail] r10011 - in trunk/apps/Freemail/src/freemail: . imap Message-ID: <20060809225839.28F049CA22@emu.freenetproject.org> Author: dbkr Date: 2006-08-09 22:58:36 +0000 (Wed, 09 Aug 2006) New Revision: 10011 Modified: trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/MailMessage.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java Log: Build #4: IMAP Compatability for Evolution - The "Even more fussy and broken that Outlook Express" mail client! Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-09 21:50:27 UTC (rev 10010) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-09 22:58:36 UTC (rev 10011) @@ -12,7 +12,7 @@ // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; - public static final int BUILD_NO = 3; + public static final int BUILD_NO = 4; public static final String VERSION_TAG = "Pet Shop"; private static final String TEMPDIRNAME = "temp"; Modified: trunk/apps/Freemail/src/freemail/MailMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-09 21:50:27 UTC (rev 10010) +++ trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-09 22:58:36 UTC (rev 10011) @@ -190,7 +190,7 @@ // contination of previous line if (parts == null || parts[1] == null) continue; - parts[1] += line.trim(); + parts[1] += " "+line.trim(); } else { if (parts != null) this.addHeader(parts[0], parts[1]); Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-09 21:50:27 UTC (rev 10010) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-09 22:58:36 UTC (rev 10011) @@ -445,15 +445,15 @@ } else if (attr.startsWith("body.peek")) { this.ps.print(a.substring(0, "body".length())); this.ps.flush(); - attr = attr.substring("body.peek".length()); - return this.sendBody(mmsg, attr); + a = a.substring("body.peek".length()); + return this.sendBody(mmsg, a); } else if (attr.startsWith("body")) { mmsg.flags.set("\\Seen", true); this.ps.print(a.substring(0, "body".length())); this.ps.flush(); - attr = attr.substring("body".length()); - return this.sendBody(mmsg, attr); + a = a.substring("body".length()); + return this.sendBody(mmsg, a); } else if (attr.startsWith("rfc822.header")) { this.ps.print(a.substring(0, "rfc822.header".length())); this.ps.flush(); @@ -520,7 +520,7 @@ for (int j = 0; j < fields.length; j++) { buf.append(mmsg.getHeaders(fields[j])); } - if (buf.length() == 0) buf.append("\r\n"); + buf.append("\r\n"); } else if (parts[i].equalsIgnoreCase("header")) { // send all the header fields try { From dbkr at freenetproject.org Wed Aug 9 23:50:25 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Wed, 9 Aug 2006 23:50:25 +0000 (UTC) Subject: [Freemail] r10013 - trunk/apps/Freemail/src/freemail Message-ID: <20060809235025.8432B9CA22@emu.freenetproject.org> Author: dbkr Date: 2006-08-09 23:50:22 +0000 (Wed, 09 Aug 2006) New Revision: 10013 Modified: trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/Postman.java Log: Build #5: Fix bug that mangled incoming messages Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-09 23:17:30 UTC (rev 10012) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-09 23:50:22 UTC (rev 10013) @@ -12,7 +12,7 @@ // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; - public static final int BUILD_NO = 4; + public static final int BUILD_NO = 5; public static final String VERSION_TAG = "Pet Shop"; private static final String TEMPDIRNAME = "temp"; Modified: trunk/apps/Freemail/src/freemail/Postman.java =================================================================== --- trunk/apps/Freemail/src/freemail/Postman.java 2006-08-09 23:17:30 UTC (rev 10012) +++ trunk/apps/Freemail/src/freemail/Postman.java 2006-08-09 23:50:22 UTC (rev 10013) @@ -22,12 +22,12 @@ SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z"); - // add our own headers first + newmsg.readHeaders(brdr); + + // add our own headers // recieved and date newmsg.addHeader("Received", "(Freemail); "+sdf.format(new Date())); - newmsg.readHeaders(brdr); - // validate the from header - or headers. There could be several. String[] froms = newmsg.getHeadersAsArray("From"); From dbkr at freenetproject.org Thu Aug 10 12:31:26 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Thu, 10 Aug 2006 12:31:26 +0000 (UTC) Subject: [Freemail] r10021 - in trunk/apps/Freemail/src/freemail: . imap Message-ID: <20060810123126.C555C9CA28@emu.freenetproject.org> Author: dbkr Date: 2006-08-10 12:31:23 +0000 (Thu, 10 Aug 2006) New Revision: 10021 Modified: trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java Log: Build #6: Now fetchmail really works! (fetch body[text] support). Also fixes with the IMAP STORE command. Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-10 11:01:52 UTC (rev 10020) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-10 12:31:23 UTC (rev 10021) @@ -12,7 +12,7 @@ // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; - public static final int BUILD_NO = 5; + public static final int BUILD_NO = 6; public static final String VERSION_TAG = "Pet Shop"; private static final String TEMPDIRNAME = "temp"; Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-10 11:01:52 UTC (rev 10020) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-10 12:31:23 UTC (rev 10021) @@ -357,7 +357,7 @@ targetmsgs[i] = (MailMessage)msgs.values().toArray()[i]; } - this.do_store(msg.args, 2, targetmsgs, msg, true); + this.do_store(msg.args, 2, targetmsgs, msg, -1); this.reply(msg, "OK Store completed"); } else { @@ -529,6 +529,22 @@ } buf.append(mmsg.getAllHeadersAsString()); + } else if (parts[i].equalsIgnoreCase("text")) { + // just send the text of the message without headers + mmsg.closeStream(); + String line; + // fast forward past the headers + try { + while ( (line = mmsg.readLine()) != null) { + if (line.length() == 0) break; + } + while ( (line = mmsg.readLine()) != null) { + buf.append(line+"\r\n"); + } + mmsg.closeStream(); + } catch (IOException ioe) { + // just return whatever we got + } } this.ps.print("{"+buf.length()+"}\r\n"+buf.toString()); @@ -575,27 +591,26 @@ to = from; } - MailMessage[] msgs = new MailMessage[(to - from) + 1]; - // convert to zero based array from--; to--; - if (from < 0 || to < 0 || from > msgs.length || to > msgs.length) { + if (from < 0 || to < 0 || from > allmsgs.length || to > allmsgs.length) { this.reply(msg, "NO No such message"); return; } + MailMessage[] msgs = new MailMessage[(to - from) + 1]; for (int i = from; i <= to; i++) { msgs[i - from] = (MailMessage) allmsgs[i]; } - do_store(msg.args, 1, msgs, msg, false); + do_store(msg.args, 1, msgs, msg, from + 1); this.reply(msg, "OK Store completed"); } - private void do_store(String[] args, int offset, MailMessage[] mmsgs, IMAPMessage msg, boolean UIDMode) { + private void do_store(String[] args, int offset, MailMessage[] mmsgs, IMAPMessage msg, int firstmsgnum) { if (args[offset].toLowerCase().indexOf("flags") < 0) { // IMAP4Rev1 can only store flags, so you're // trying something crazy @@ -635,10 +650,10 @@ for (int i = 0; i < mmsgs.length; i++) { StringBuffer buf = new StringBuffer(""); - if (UIDMode) { + if (firstmsgnum < 0) { buf.append(mmsgs[i].getUID() + " FETCH FLAGS ("); } else { - buf.append((i+1) + " FETCH FLAGS ("); + buf.append((i+firstmsgnum) + " FETCH FLAGS ("); } buf.append(mmsgs[i].flags.getFlags()); From dbkr at freenetproject.org Sat Aug 12 23:34:23 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sat, 12 Aug 2006 23:34:23 +0000 (UTC) Subject: [Freemail] r10055 - in trunk/apps/Freemail/src/freemail: . imap Message-ID: <20060812233423.678B89BF2E@emu.freenetproject.org> Author: dbkr Date: 2006-08-12 23:34:17 +0000 (Sat, 12 Aug 2006) New Revision: 10055 Modified: trunk/apps/Freemail/src/freemail/AccountManager.java trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/MailMessage.java trunk/apps/Freemail/src/freemail/MessageBank.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java Log: Build #7: Support for IMAP folders / subfolders Modified: trunk/apps/Freemail/src/freemail/AccountManager.java =================================================================== --- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-12 23:07:53 UTC (rev 10054) +++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-08-12 23:34:17 UTC (rev 10055) @@ -51,7 +51,6 @@ File accountdir = new File(DATADIR, username); if (!accountdir.mkdir()) throw new IOException("Failed to create directory "+username+" in "+DATADIR); - PropsFile accfile = getAccountFile(accountdir); putWelcomeMessage(username, getFreemailAddress(accountdir)); } Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-12 23:07:53 UTC (rev 10054) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-12 23:34:17 UTC (rev 10055) @@ -12,7 +12,7 @@ // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; - public static final int BUILD_NO = 6; + public static final int BUILD_NO = 7; public static final String VERSION_TAG = "Pet Shop"; private static final String TEMPDIRNAME = "temp"; Modified: trunk/apps/Freemail/src/freemail/MailMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-12 23:07:53 UTC (rev 10054) +++ trunk/apps/Freemail/src/freemail/MailMessage.java 2006-08-12 23:34:17 UTC (rev 10055) @@ -19,7 +19,7 @@ private PrintStream ps; private final Vector headers; private BufferedReader brdr; - public final IMAPMessageFlags flags; + public IMAPMessageFlags flags; MailMessage(File f) { this.file = f; @@ -150,6 +150,13 @@ return this.ps; } + public PrintStream getRawStream() throws FileNotFoundException { + this.os = new FileOutputStream(this.file); + this.ps = new PrintStream(this.os); + + return this.ps; + } + public void commit() { try { this.os.close(); @@ -247,6 +254,25 @@ return this.brdr.readLine(); } + public boolean copyTo(MailMessage msg) { + this.closeStream(); + String line; + try { + PrintStream copyps = msg.getRawStream(); + while ( (line = this.readLine()) != null) { + copyps.println(line); + } + msg.commit(); + } catch (IOException ioe) { + msg.cancel(); + return false; + } + + msg.flags = this.flags; + msg.storeFlags(); + return true; + } + // programming-by-contract - anything that tries to read the message // or suchlike after calling this method is responsible for the // torrent of exceptions they'll get thrown at them! Modified: trunk/apps/Freemail/src/freemail/MessageBank.java =================================================================== --- trunk/apps/Freemail/src/freemail/MessageBank.java 2006-08-12 23:07:53 UTC (rev 10054) +++ trunk/apps/Freemail/src/freemail/MessageBank.java 2006-08-12 23:34:17 UTC (rev 10055) @@ -9,6 +9,8 @@ import java.io.PrintStream; import java.util.TreeMap; import java.util.SortedMap; +import java.util.Vector; +import java.util.Enumeration; public class MessageBank { private static final String MESSAGES_DIR = "inbox"; @@ -25,6 +27,46 @@ } } + private MessageBank(File d) { + this.dir = d; + } + + public String getName() { + return this.dir.getName(); + } + + public String getFolderFlagsString() { + StringBuffer retval = new StringBuffer("("); + + if (this.listSubFolders().length > 0) { + retval.append("\\HasChildren"); + } else { + retval.append("\\HasNoChildren"); + } + + retval.append(")"); + return retval.toString(); + } + + public boolean delete() { + File[] files = this.dir.listFiles(); + + for (int i = 0; i < files.length; i++) { + if (files[i].getName().equals(".")) continue; + if (files[i].getName().equals(NIDFILE)) continue; + if (files[i].getName().equals("..")) continue; + + // this method should will fail if there are directories + // here. It should never be called if this is the case. + if (!files[i].delete()) return false; + } + + // rename it with a dot in front - we need to preserve the UIDs + File newdir = new File(this.dir.getParent(), "."+this.dir.getName()); + + return this.dir.renameTo(newdir); + } + public MailMessage createMessage() { long newid = this.nextId(); File newfile; @@ -54,6 +96,7 @@ for (int i = 0; i < files.length; i++) { if (files[i].getName().startsWith(".")) continue; + if (files[i].isDirectory()) continue; MailMessage msg = new MailMessage(files[i]); @@ -79,6 +122,60 @@ return msgs; } + public MessageBank getSubFolder(String name) { + if (!name.matches("[\\w_]*")) return null; + + File targetdir = new File(this.dir, name); + return new MessageBank(targetdir); + } + + public MessageBank makeSubFolder(String name) { + if (!name.matches("[\\w_]*")) return null; + + File targetdir = new File(this.dir, name); + + // is there a 'deleted' instance of this folder? + File ghostdir = new File(this.dir, "."+name); + if (ghostdir.exists()) { + if (!ghostdir.renameTo(targetdir)) { + return null; + } + return new MessageBank(ghostdir); + } + + if (targetdir.exists()) { + return null; + } + + if (targetdir.mkdir()) { + return new MessageBank(targetdir); + } + return null; + } + + public MessageBank[] listSubFolders() { + File[] files = this.dir.listFiles(); + Vector subfolders = new Vector(); + + for (int i = 0; i < files.length; i++) { + if (files[i].getName().startsWith(".")) continue; + + if (files[i].isDirectory()) { + subfolders.add(files[i]); + } + } + + MessageBank[] retval = new MessageBank[subfolders.size()]; + + Enumeration e = subfolders.elements(); + int i = 0; + while (e.hasMoreElements()) { + retval[i] = new MessageBank((File)e.nextElement()); + i++; + } + return retval; + } + private long nextId() { File nidfile = new File(this.dir, NIDFILE); long retval; Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:07:53 UTC (rev 10054) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:34:17 UTC (rev 10055) @@ -17,13 +17,14 @@ import freemail.utils.EmailAddress; public class IMAPHandler implements Runnable { - private static final String CAPABILITY = "IMAP4rev1 AUTH=LOGIN"; + private static final String CAPABILITY = "IMAP4rev1 AUTH=LOGIN CHILDREN NAMESPACE"; - final Socket client; - final OutputStream os; - final PrintStream ps; - final BufferedReader bufrdr; - MessageBank mb; + private final Socket client; + private final OutputStream os; + private final PrintStream ps; + private final BufferedReader bufrdr; + private MessageBank mb; + private MessageBank inbox; IMAPHandler(Socket client) throws IOException { @@ -90,6 +91,14 @@ this.handle_lsub(msg); } else if (msg.type.equals("status")) { this.handle_status(msg); + } else if (msg.type.equals("create")) { + this.handle_create(msg); + } else if (msg.type.equals("delete")) { + this.handle_delete(msg); + } else if (msg.type.equals("copy")) { + this.handle_copy(msg); + } else if (msg.type.equals("append")) { + this.handle_append(msg); } else { this.reply(msg, "NO Sorry - not implemented"); } @@ -98,7 +107,7 @@ private void handle_login(IMAPMessage msg) { if (msg.args.length < 2) return; if (AccountManager.authenticate(trimQuotes(msg.args[0]), trimQuotes(msg.args[1]))) { - this.mb = new MessageBank(trimQuotes(msg.args[0])); + this.inbox = new MessageBank(trimQuotes(msg.args[0])); this.reply(msg, "OK Logged in"); } else { @@ -149,22 +158,69 @@ replyprefix = "LSUB"; } - refname = trimQuotes(refname); + if (refname != null) refname = trimQuotes(refname); if (refname.length() == 0) refname = null; - mbname = trimQuotes(mbname); + if (mbname != null) mbname = trimQuotes(mbname); if (mbname.length() == 0) mbname = null; if (mbname == null) { // return hierarchy delimiter this.sendState(replyprefix+" (\\Noselect) \".\" \"\""); - } else if (mbname.equals("%") || mbname.equals("INBOX") || mbname.equals("*") || mbname.equals("INBOX*")) { - this.sendState(replyprefix+" (\\NoInferiors) \".\" \"INBOX\""); + } else { + // transform mailbox name into a regex + + // '*' needs to be '.*' + mbname = mbname.replace("*", ".*"); + + // and % is a wildcard not inclusing the hierarchy delimiter + mbname = mbname.replace("%", "[^\\.]*"); + + + this.list_matching_folders(this.inbox, mbname, replyprefix, "INBOX."); + + /// and send the inbox too, if it matches + if ("INBOX".matches(mbname)) { + this.sendState(replyprefix+" "+this.inbox.getFolderFlagsString()+" \".\" \"INBOX\""); + } } this.reply(msg, "OK "+replyprefix+" completed"); } + private void list_matching_folders(MessageBank folder, String pattern, String replyprefix, String folderpath) { + MessageBank[] folders = folder.listSubFolders(); + + for (int i = 0; i < folders.length; i++) { + String fullpath = folderpath+folders[i].getName(); + + this.list_matching_folders(folders[i], pattern, replyprefix, fullpath+"."); + if (fullpath.matches(pattern)) { + this.sendState(replyprefix+" "+folders[i].getFolderFlagsString()+" \".\" \""+fullpath+"\""); + } + } + } + + private MessageBank getMailboxFromPath(String path) { + MessageBank tempmb = this.inbox; + + String[] mbparts = path.split("\\."); + + if (!mbparts[0].equalsIgnoreCase("inbox")) { + return null; + } + + int i; + for (i = 1; i < mbparts.length; i++) { + tempmb = tempmb.getSubFolder(mbparts[i]); + if (tempmb == null) { + return null; + } + } + + return tempmb; + } + private void handle_select(IMAPMessage msg) { String mbname; @@ -177,39 +233,44 @@ return; } - mbname = trimQuotes(msg.args[0]).toLowerCase(); + mbname = trimQuotes(msg.args[0]); - if (mbname.equals("inbox")) { - this.sendState("FLAGS ("+IMAPMessageFlags.getAllFlagsAsString()+")"); - this.sendState("OK [PERMANENTFLAGS (\\* "+IMAPMessageFlags.getPermanentFlagsAsString()+")] Limited"); + MessageBank tempmb = this.getMailboxFromPath(mbname); + + if (tempmb == null) { + this.reply(msg, "NO No such mailbox"); + return; + } else { + this.mb = tempmb; + } + + this.sendState("FLAGS ("+IMAPMessageFlags.getAllFlagsAsString()+")"); + this.sendState("OK [PERMANENTFLAGS (\\* "+IMAPMessageFlags.getPermanentFlagsAsString()+")] Limited"); - SortedMap msgs = this.mb.listMessages(); + SortedMap msgs = this.mb.listMessages(); - int numrecent = 0; - int numexists = msgs.size(); - while (msgs.size() > 0) { - Integer current = (Integer)(msgs.firstKey()); - MailMessage m =(MailMessage)msgs.get(msgs.firstKey()); + int numrecent = 0; + int numexists = msgs.size(); + while (msgs.size() > 0) { + Integer current = (Integer)(msgs.firstKey()); + MailMessage m =(MailMessage)msgs.get(msgs.firstKey()); - // if it's recent, add to the tally - if (m.flags.get("\\Recent")) numrecent++; + // if it's recent, add to the tally + if (m.flags.get("\\Recent")) numrecent++; - // remove the recent flag - m.flags.set("\\Recent", false); - m.storeFlags(); + // remove the recent flag + m.flags.set("\\Recent", false); + m.storeFlags(); - msgs = msgs.tailMap(new Integer(current.intValue()+1)); - } + msgs = msgs.tailMap(new Integer(current.intValue()+1)); + } - this.sendState(numexists+" EXISTS"); - this.sendState(numrecent+" RECENT"); + this.sendState(numexists+" EXISTS"); + this.sendState(numrecent+" RECENT"); - this.sendState("OK [UIDVALIDITY 1] Ok"); + this.sendState("OK [UIDVALIDITY 1] Ok"); - this.reply(msg, "OK [READ-WRITE] Done"); - } else { - this.reply(msg, "NO No such mailbox"); - } + this.reply(msg, "OK [READ-WRITE] Done"); } private void handle_noop(IMAPMessage msg) { @@ -224,6 +285,11 @@ return; } + if (this.mb == null) { + this.reply(msg, "NO No mailbox selected"); + return; + } + SortedMap msgs = this.mb.listMessages(); if (msgs.size() == 0) { @@ -293,6 +359,11 @@ return; } + if (this.mb == null) { + this.reply(msg, "NO No mailbox selected"); + return; + } + SortedMap msgs = this.mb.listMessages(); if (msgs.size() == 0) { @@ -327,7 +398,7 @@ } int msgnum = 1; - if (msg.args[0].toLowerCase().equals("fetch")) { + if (msg.args[0].equalsIgnoreCase("fetch")) { int oldsize = msgs.size(); msgs = msgs.tailMap(new Integer(from)); msgnum += (oldsize - msgs.size()); @@ -347,7 +418,7 @@ } this.reply(msg, "OK Fetch completed"); - } else if (msg.args[0].toLowerCase().equals("store")) { + } else if (msg.args[0].equalsIgnoreCase("store")) { msgs = msgs.tailMap(new Integer(from)); msgs = msgs.headMap(new Integer(to + 1)); @@ -360,6 +431,43 @@ this.do_store(msg.args, 2, targetmsgs, msg, -1); this.reply(msg, "OK Store completed"); + } else if (msg.args[0].equalsIgnoreCase("copy")) { + msgs = msgs.tailMap(new Integer(from)); + + if (msg.args.length < 3) { + this.reply(msg, "BAD Not enough arguments"); + return; + } + + MessageBank target = getMailboxFromPath(trimQuotes(msg.args[2])); + if (target == null) { + this.reply(msg, "NO [TRYCREATE] No such mailbox."); + return; + } + + int copied = 0; + + while (msgs.size() > 0) { + Integer curuid = (Integer)msgs.firstKey(); + if (curuid.intValue() > to) { + break; + } + + MailMessage srcmsg = (MailMessage)msgs.get(msgs.firstKey()); + + MailMessage copymsg = target.createMessage(); + srcmsg.copyTo(copymsg); + + copied++; + + msgs = msgs.tailMap(new Integer(curuid.intValue()+1)); + msgnum++; + } + + if (copied > 0) + this.reply(msg, "OK COPY completed"); + else + this.reply(msg, "NO No messages copied"); } else { this.reply(msg, "BAD Unknown command"); } @@ -564,6 +672,11 @@ return; } + if (this.mb == null) { + this.reply(msg, "NO No mailbox selected"); + return; + } + String rangeparts[] = msg.args[0].split(":"); Object[] allmsgs = this.mb.listMessages().values().toArray(); @@ -666,28 +779,39 @@ } private void handle_expunge(IMAPMessage msg) { - MailMessage[] mmsgs = this.mb.listMessagesArray(); + if (this.mb == null) { + this.reply(msg, "NO No mailbox selected"); + return; + } - for (int i = 0; i < mmsgs.length; i++) { - if (mmsgs[i].flags.get("\\Deleted")) - mmsgs[i].delete(); - this.sendState(i+" EXPUNGE"); + this.expunge(true); + this.reply(msg, "OK Expunge complete"); + } + + private void handle_close(IMAPMessage msg) { + if (this.mb == null) { + this.reply(msg, "NO No mailbox selected"); + return; } + + this.expunge(false); + this.mb = null; + this.reply(msg, "OK Mailbox closed"); } - private void handle_close(IMAPMessage msg) { + private void expunge(boolean verbose) { MailMessage[] mmsgs = this.mb.listMessagesArray(); for (int i = 0; i < mmsgs.length; i++) { if (mmsgs[i].flags.get("\\Deleted")) mmsgs[i].delete(); + if (verbose) this.sendState(i+" EXPUNGE"); } - this.reply(msg, "OK Mailbox closed"); } private void handle_namespace(IMAPMessage msg) { - this.sendState("NAMESPACE ((\"\" \"/\")) NIL NIL"); + this.sendState("NAMESPACE ((\"INBOX.\" \".\")) NIL NIL"); this.reply(msg, "OK Namespace completed"); } @@ -768,6 +892,198 @@ this.reply(msg, "OK STATUS completed"); } + private void handle_create(IMAPMessage msg) { + if (!this.verify_auth(msg)) { + return; + } + + if (msg.args.length < 1) { + this.reply(msg, "NO Not enough arguments"); + return; + } + + msg.args[0] = trimQuotes(msg.args[0]); + + if (msg.args[0].endsWith(".")) { + // ends with a hierarchy delimiter. Ignore it + this.reply(msg, "OK Nothing done"); + return; + } + + String[] mbparts = msg.args[0].split("\\."); + if (!mbparts[0].equalsIgnoreCase("inbox")) { + this.reply(msg, "NO Invalid mailbox name"); + return; + } + + if (mbparts.length < 2) { + this.reply(msg, "NO Inbox already exists!"); + return; + } + + int i; + MessageBank tempmb = this.inbox; + for (i = 1; i < mbparts.length; i++) { + tempmb = tempmb.makeSubFolder(mbparts[i]); + if (tempmb == null) { + this.reply(msg, "NO couldn't create mailbox"); + return; + } + } + this.reply(msg, "OK Mailbox created"); + } + + private void handle_delete(IMAPMessage msg) { + if (!this.verify_auth(msg)) { + return; + } + + if (msg.args.length < 1) { + this.reply(msg, "NO Not enough arguments"); + return; + } + + MessageBank target = getMailboxFromPath(trimQuotes(msg.args[0])); + if (target == null) { + this.reply(msg, "NO No such mailbox."); + return; + } + + if (target.listSubFolders().length > 0) { + this.reply(msg, "NO Mailbox has inferiors."); + return; + } + + if (target.delete()) { + this.reply(msg, "OK Mailbox deleted"); + } else { + this.reply(msg, "NO Unable to delete mailbox"); + } + return; + } + + private void handle_copy(IMAPMessage msg) { + if (!this.verify_auth(msg)) { + return; + } + + if (this.mb == null) { + this.reply(msg, "NO No mailbox selected"); + return; + } + + if (msg.args.length < 2) { + this.reply(msg, "NO Not enough arguments"); + return; + } + + Object[] allmsgs = this.mb.listMessages().values().toArray(); + String rangeparts[] = msg.args[0].split(":"); + + int from; + int to; + try { + from = Integer.parseInt(rangeparts[0]); + } catch (NumberFormatException nfe) { + this.reply(msg, "BAD That's not a number!"); + return; + } + if (rangeparts.length > 1) { + if (rangeparts[1].equals("*")) { + to = allmsgs.length; + } else { + try { + to = Integer.parseInt(rangeparts[1]); + } catch (NumberFormatException nfe) { + this.reply(msg, "BAD That's not a number!"); + return; + } + } + } else { + to = from; + } + + // convert to zero based array + from--; + to--; + + if (from < 0 || to < 0 || from > allmsgs.length || to > allmsgs.length) { + this.reply(msg, "NO No such message"); + return; + } + + MessageBank target = getMailboxFromPath(trimQuotes(msg.args[1])); + if (target == null) { + this.reply(msg, "NO [TRYCREATE] No such mailbox."); + return; + } + + for (int i = from; i <= to; i++) { + MailMessage src = (MailMessage)allmsgs[i]; + MailMessage copy = target.createMessage(); + + src.copyTo(copy); + } + this.reply(msg, "OK COPY completed"); + } + + private void handle_append(IMAPMessage msg) { + if (msg.args.length < 3) { + this.reply(msg, "NO Not enough arguments"); + return; + } + + String mbname = trimQuotes(msg.args[0]); + String sflags = msg.args[1]; + if (sflags.startsWith("(")) sflags = sflags.substring(1); + if (sflags.endsWith(")")) sflags = sflags.substring(0, sflags.length() - 1); + String sdatalen = msg.args[2]; + if (sdatalen.startsWith("{")) sdatalen = sdatalen.substring(1); + if (sdatalen.endsWith("}")) sdatalen = sdatalen.substring(0, sdatalen.length() - 1); + int datalen; + try { + datalen = Integer.parseInt(sdatalen); + } catch (NumberFormatException nfe) { + datalen = 0; + } + + MessageBank destmb = this.getMailboxFromPath(mbname); + if (destmb == null) { + this.reply(msg, "NO [TRYCREATE] No such mailbox"); + return; + } + + MailMessage newmsg = destmb.createMessage(); + this.ps.print("+ OK\r\n"); + try { + PrintStream msgps = newmsg.getRawStream(); + + String line; + int bytesread = 0; + while ( (line = this.bufrdr.readLine()) != null) { + msgps.println(line); + + bytesread += line.getBytes().length; + bytesread += "\r\n".length(); + + if (bytesread >= datalen) break; + } + + newmsg.commit(); + } catch (IOException ioe) { + this.reply(msg, "NO Failed to write message"); + newmsg.cancel(); + return; + } + + String[] flags = sflags.split(" "); + for (int i = 0; i < flags.length; i++) { + newmsg.flags.set(flags[i], true); + } + newmsg.storeFlags(); + this.reply(msg, "OK APPEND completed"); + } + private String getEnvelope(MailMessage mmsg) { StringBuffer buf = new StringBuffer("("); @@ -837,7 +1153,7 @@ } private boolean verify_auth(IMAPMessage msg) { - if (this.mb == null) { + if (this.inbox == null) { this.reply(msg, "NO Must be authenticated"); return false; } From dbkr at freenetproject.org Sat Aug 12 23:42:36 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sat, 12 Aug 2006 23:42:36 +0000 (UTC) Subject: [Freemail] r10057 - trunk/apps/Freemail/src/freemail/imap Message-ID: <20060812234236.68C0B20AFA1@emu.freenetproject.org> Author: dbkr Date: 2006-08-12 23:42:32 +0000 (Sat, 12 Aug 2006) New Revision: 10057 Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java Log: Accidental 1.5-ism Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:41:50 UTC (rev 10056) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:42:32 UTC (rev 10057) @@ -171,7 +171,7 @@ // transform mailbox name into a regex // '*' needs to be '.*' - mbname = mbname.replace("*", ".*"); + mbname = mbname.replaceAll("\\*", ".*"); // and % is a wildcard not inclusing the hierarchy delimiter mbname = mbname.replace("%", "[^\\.]*"); From dbkr at freenetproject.org Sat Aug 12 23:44:30 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sat, 12 Aug 2006 23:44:30 +0000 (UTC) Subject: [Freemail] r10058 - trunk/apps/Freemail/src/freemail/imap Message-ID: <20060812234430.8688B9BCF7@emu.freenetproject.org> Author: dbkr Date: 2006-08-12 23:44:27 +0000 (Sat, 12 Aug 2006) New Revision: 10058 Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java Log: Oh rats. Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:42:32 UTC (rev 10057) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:44:27 UTC (rev 10058) @@ -174,7 +174,7 @@ mbname = mbname.replaceAll("\\*", ".*"); // and % is a wildcard not inclusing the hierarchy delimiter - mbname = mbname.replace("%", "[^\\.]*"); + mbname = mbname.replaceAll("%", "[^\\.]*"); this.list_matching_folders(this.inbox, mbname, replyprefix, "INBOX."); From dbkr at freenetproject.org Sun Aug 13 01:07:08 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sun, 13 Aug 2006 01:07:08 +0000 (UTC) Subject: [Freemail] r10059 - in trunk/apps/Freemail/src/freemail: . imap Message-ID: <20060813010708.CFB1A20AFA1@emu.freenetproject.org> Author: dbkr Date: 2006-08-13 01:07:04 +0000 (Sun, 13 Aug 2006) New Revision: 10059 Modified: trunk/apps/Freemail/src/freemail/MessageBank.java trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java Log: Better handling of quoting in IMAP commands. Allow spaces in IMAP folder names, and general IMAP folder fixes. Modified: trunk/apps/Freemail/src/freemail/MessageBank.java =================================================================== --- trunk/apps/Freemail/src/freemail/MessageBank.java 2006-08-12 23:44:27 UTC (rev 10058) +++ trunk/apps/Freemail/src/freemail/MessageBank.java 2006-08-13 01:07:04 UTC (rev 10059) @@ -123,14 +123,17 @@ } public MessageBank getSubFolder(String name) { - if (!name.matches("[\\w_]*")) return null; + if (!name.matches("[\\w\\s_]*")) return null; File targetdir = new File(this.dir, name); + if (!targetdir.exists()) { + return null; + } return new MessageBank(targetdir); } public MessageBank makeSubFolder(String name) { - if (!name.matches("[\\w_]*")) return null; + if (!name.matches("[\\w\\s_]*")) return null; File targetdir = new File(this.dir, name); @@ -146,7 +149,7 @@ if (targetdir.exists()) { return null; } - + if (targetdir.mkdir()) { return new MessageBank(targetdir); } Modified: trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-12 23:44:27 UTC (rev 10058) +++ trunk/apps/Freemail/src/freemail/imap/IMAPHandler.java 2006-08-13 01:07:04 UTC (rev 10059) @@ -924,10 +924,15 @@ int i; MessageBank tempmb = this.inbox; for (i = 1; i < mbparts.length; i++) { - tempmb = tempmb.makeSubFolder(mbparts[i]); - if (tempmb == null) { - this.reply(msg, "NO couldn't create mailbox"); - return; + MessageBank existingmb = tempmb.getSubFolder(mbparts[i]); + if (existingmb != null) { + tempmb = existingmb; + } else { + tempmb = tempmb.makeSubFolder(mbparts[i]); + if (tempmb == null) { + this.reply(msg, "NO couldn't create mailbox"); + return; + } } } this.reply(msg, "OK Mailbox created"); Modified: trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java 2006-08-12 23:44:27 UTC (rev 10058) +++ trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java 2006-08-13 01:07:04 UTC (rev 10059) @@ -1,6 +1,8 @@ package freemail.imap; import java.util.Vector; +import java.util.Stack; +import java.util.Arrays; public class IMAPMessage { public final String tag; @@ -8,7 +10,10 @@ public final String[] args; IMAPMessage(String raw) throws IMAPBadMessageException { - String[] parts = doSplit(raw, '[', ']'); + char[] a1 = {'[', '"'}; + char[] a2 = {']', '"'}; + + String[] parts = doSplit(raw, a1, a2); if (parts.length < 2) { throw new IMAPBadMessageException(); } @@ -22,24 +27,42 @@ } } - // split on spaces that aren't between two square given characters public static String[] doSplit(String in, char c1, char c2) { - boolean in_brackets = false; + char[] a1 = new char[1]; + a1[0] = c1; + char[] a2 = new char[1]; + a2[0] = c2; + return doSplit(in, a1, a2); + } + + // split on spaces that aren't between two given characters + public static String[] doSplit(String in, char[] c1, char[] c2) { Vector parts = new Vector(); StringBuffer buf = new StringBuffer(""); + Stack context = new Stack(); for (int i = 0; i < in.length(); i++) { char c = in.charAt(i); - if (c == c1) { - in_brackets = true; + int pos = -1; + for (int j = 0; j < c1.length; j++) { + if (c1[j] == c) { + pos = j; + break; + } + } + + if (!context.empty() && c == ((Character)context.peek()).charValue()) { + context.pop(); buf.append(c); - } else if (c == c2) { - in_brackets = false; + } else if (pos >= 0) { + context.push(new Character(c2[pos])); buf.append(c); - } else if (c == ' ' && !in_brackets) { + } else if (c == ' ' && context.empty()) { parts.add(buf.toString()); buf = new StringBuffer(""); + } else if (context.empty()) { + buf.append(c); } else buf.append(c); } From dbkr at freenetproject.org Tue Aug 15 20:38:53 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Tue, 15 Aug 2006 20:38:53 +0000 (UTC) Subject: [Freemail] r10099 - in trunk/apps/Freemail/src/freemail: . config fcp imap smtp utils Message-ID: <20060815203853.0154D20AFA1@emu.freenetproject.org> Author: dbkr Date: 2006-08-15 20:38:49 +0000 (Tue, 15 Aug 2006) New Revision: 10099 Added: trunk/apps/Freemail/src/freemail/config/ trunk/apps/Freemail/src/freemail/config/ConfigClient.java trunk/apps/Freemail/src/freemail/config/Configurator.java Modified: trunk/apps/Freemail/src/freemail/Freemail.java trunk/apps/Freemail/src/freemail/fcp/FCPContext.java trunk/apps/Freemail/src/freemail/imap/IMAPListener.java trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java trunk/apps/Freemail/src/freemail/smtp/SMTPListener.java trunk/apps/Freemail/src/freemail/utils/PropsFile.java Log: Config file support. Modified: trunk/apps/Freemail/src/freemail/Freemail.java =================================================================== --- trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/Freemail.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -7,8 +7,10 @@ import freemail.fcp.FCPConnection; import freemail.imap.IMAPListener; import freemail.smtp.SMTPListener; +import freemail.config.Configurator; +import freemail.config.ConfigClient; -public class Freemail { +public class Freemail implements ConfigClient { // version info public static final int VER_MAJOR = 0; public static final int VER_MINOR = 1; @@ -19,7 +21,9 @@ private static final String DATADIR = "data"; private static final String GLOBALDATADIR = "globaldata"; private static final String ACKDIR = "delayedacks"; + private static final String CFGFILE = "globalconfig"; private static File datadir; + private static File globaldatadir; private static File tempdir; private static FCPConnection fcpconn; @@ -32,8 +36,7 @@ } public static void main(String[] args) { - String fcphost = "localhost"; - int fcpport = 9481; + String cfgfile = CFGFILE; String action = ""; String account = null; @@ -72,29 +75,23 @@ } account = args[i - 1]; alias = args[i]; - } else if (args[i].equals("-h")) { + } else if (args[i].equals("-c")) { i++; if (args.length - 1 < i) { - System.out.println("No hostname supplied, using default"); + System.out.println("No config file supplied, using default"); continue; } - fcphost = args[i]; - } else if (args[i].equals("-p")) { - i++; - if (args.length - 1 < i) { - System.out.println("No port supplied, using default"); - continue; - } - try { - fcpport = Integer.parseInt(args[i]); - } catch (NumberFormatException nfe) { - System.out.println("Bad port supplied, using default"); - } + cfgfile = args[i]; } } - FCPContext fcpctx = new FCPContext(fcphost, fcpport); + Configurator cfg = new Configurator(new File(cfgfile)); + FCPContext fcpctx = new FCPContext(); + + cfg.register("fcp_host", fcpctx, "localhost"); + cfg.register("fcp_port", fcpctx, "9481"); + Freemail.fcpconn = new FCPConnection(fcpctx); Thread fcpthread = new Thread(fcpconn); fcpthread.setDaemon(true); @@ -132,13 +129,13 @@ return; } - File globaldatadir = new File(GLOBALDATADIR); + cfg.register("globaldatadir", new Freemail(), GLOBALDATADIR); if (!globaldatadir.exists()) { globaldatadir.mkdir(); } // start a SingleAccountWatcher for each account - Freemail.datadir = new File(DATADIR); + cfg.register("datadir", new Freemail(), Freemail.DATADIR); if (!Freemail.datadir.exists()) { System.out.println("Starting Freemail for the first time."); System.out.println("You will probably want to add an account by running Freemail with arguments --newaccount "); @@ -147,7 +144,7 @@ System.exit(1); } } - Freemail.tempdir = new File(Freemail.TEMPDIRNAME); + cfg.register("tempdir", new Freemail(), Freemail.TEMPDIRNAME); if (!Freemail.tempdir.exists()) { if (!Freemail.tempdir.mkdir()) { System.out.println("Couldn't create temporary directory. Please ensure that the user you are running Freemail as has write access to its working directory"); @@ -173,7 +170,7 @@ senderthread.start(); // start the SMTP Listener - SMTPListener smtpl = new SMTPListener(sender); + SMTPListener smtpl = new SMTPListener(sender, cfg); Thread smtpthread = new Thread(smtpl, "SMTP Listener"); smtpthread.setDaemon(true); smtpthread.start(); @@ -188,7 +185,17 @@ // start the IMAP listener - IMAPListener imapl = new IMAPListener(); + IMAPListener imapl = new IMAPListener(cfg); imapl.run(); } + + public void setConfigProp(String key, String val) { + if (key.equalsIgnoreCase("datadir")) { + Freemail.datadir = new File(val); + } else if (key.equalsIgnoreCase("tempdir")) { + Freemail.tempdir = new File(val); + } else if (key.equalsIgnoreCase("globaldatadir")) { + Freemail.globaldatadir = new File(val); + } + } } Added: trunk/apps/Freemail/src/freemail/config/ConfigClient.java =================================================================== --- trunk/apps/Freemail/src/freemail/config/ConfigClient.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/config/ConfigClient.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -0,0 +1,5 @@ +package freemail.config; + +public interface ConfigClient { + public void setConfigProp(String key, String val); +} Added: trunk/apps/Freemail/src/freemail/config/Configurator.java =================================================================== --- trunk/apps/Freemail/src/freemail/config/Configurator.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/config/Configurator.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -0,0 +1,53 @@ +package freemail.config; + +import java.io.File; +import java.util.HashMap; + +import freemail.utils.PropsFile; + +/** "Probably the simplest config interface in the world" + * + */ + +public class Configurator { + private final PropsFile props; + private final HashMap callbacks; + + public Configurator(File f) { + this.props = new PropsFile(f); + this.props.setCommentPrefix("#"); + String ls = System.getProperty("line.separator"); + StringBuffer head = new StringBuffer(); + head.append("# This is the configuration file for Freemail."+ls); + head.append("# "+ls); + head.append("# You are free to edit this file, but if Freemail"+ls); + head.append("# breaks as a result, you should delete this file"+ls); + head.append("# and Freemail will reset all values to their"+ls); + head.append("# defaults."+ls); + head.append("# The Freemail community cannot provide support"+ls); + head.append("# to people who have broken Freemail as a result"+ls); + head.append("# of editing this file."); + + this.props.setHeader(head.toString()); + this.callbacks = new HashMap(); + } + + public void register(String key, ConfigClient cb, String defaultval) { + this.callbacks.put(key, cb); + + String val = this.props.get(key); + if (val == null) { + val = defaultval; + props.put(key, val); + } + cb.setConfigProp(key, val); + } + + public void set(String key, String val) { + this.props.put(key, val); + + ConfigClient cb = (ConfigClient)this.callbacks.get(key); + if (cb == null) return; + cb.setConfigProp(key, val); + } +} Modified: trunk/apps/Freemail/src/freemail/fcp/FCPContext.java =================================================================== --- trunk/apps/Freemail/src/freemail/fcp/FCPContext.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/fcp/FCPContext.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -3,16 +3,25 @@ import java.io.IOException; import java.net.Socket; -public class FCPContext { - private final String hostname; - private final int port; +import freemail.config.ConfigClient; + +public class FCPContext implements ConfigClient { + private String hostname; + private int port; - public FCPContext(String h, int p) { - this.hostname = h; - this.port = p; - } - public Socket getConn() throws IOException { return new Socket(this.hostname, this.port); } + + public void setConfigProp(String key, String val) { + if (key.equalsIgnoreCase("fcp_host")) { + hostname = val; + } else if (key.equalsIgnoreCase("fcp_port")) { + try { + port = Integer.parseInt(val); + } catch (NumberFormatException nfe) { + // just leave it as it was + } + } + } } Modified: trunk/apps/Freemail/src/freemail/imap/IMAPListener.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPListener.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/imap/IMAPListener.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -1,11 +1,30 @@ package freemail.imap; import java.net.ServerSocket; +import java.net.InetAddress; import java.io.IOException; -public class IMAPListener implements Runnable { +import freemail.config.Configurator; +import freemail.config.ConfigClient; + +public class IMAPListener implements Runnable,ConfigClient { private static final int LISTENPORT = 3143; - + private String bindaddress; + private int bindport; + + public IMAPListener(Configurator cfg) { + cfg.register("imap_bind_address", this, "127.0.0.1"); + cfg.register("imap_bind_port", this, Integer.toString(LISTENPORT)); + } + + public void setConfigProp(String key, String val) { + if (key.equalsIgnoreCase("imap_bind_address")) { + this.bindaddress = val; + } else if (key.equalsIgnoreCase("imap_bind_port")) { + this.bindport = Integer.parseInt(val); + } + } + public void run() { try { this.realrun(); @@ -15,7 +34,7 @@ } public void realrun() throws IOException { - ServerSocket sock = new ServerSocket(LISTENPORT); + ServerSocket sock = new ServerSocket(this.bindport, 10, InetAddress.getByName(this.bindaddress)); while (!sock.isClosed()) { try { Modified: trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java =================================================================== --- trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/imap/IMAPMessage.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -2,7 +2,6 @@ import java.util.Vector; import java.util.Stack; -import java.util.Arrays; public class IMAPMessage { public final String tag; Modified: trunk/apps/Freemail/src/freemail/smtp/SMTPListener.java =================================================================== --- trunk/apps/Freemail/src/freemail/smtp/SMTPListener.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/smtp/SMTPListener.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -1,16 +1,23 @@ package freemail.smtp; import java.net.ServerSocket; +import java.net.InetAddress; import java.io.IOException; import freemail.MessageSender; +import freemail.config.ConfigClient; +import freemail.config.Configurator; -public class SMTPListener implements Runnable { +public class SMTPListener implements Runnable,ConfigClient { private static final int LISTENPORT = 3025; private final MessageSender msgsender; + private String bindaddress; + private int bindport; - public SMTPListener(MessageSender sender) { + public SMTPListener(MessageSender sender, Configurator cfg) { this.msgsender = sender; + cfg.register("smtp_bind_address", this, "127.0.0.1"); + cfg.register("smtp_bind_port", this, Integer.toString(LISTENPORT)); } public void run() { @@ -21,8 +28,16 @@ } } + public void setConfigProp(String key, String val) { + if (key.equalsIgnoreCase("smtp_bind_address")) { + this.bindaddress = val; + } else if (key.equalsIgnoreCase("smtp_bind_port")) { + this.bindport = Integer.parseInt(val); + } + } + public void realrun() throws IOException { - ServerSocket sock = new ServerSocket(LISTENPORT); + ServerSocket sock = new ServerSocket(this.bindport, 10, InetAddress.getByName(this.bindaddress)); while (!sock.isClosed()) { try { Modified: trunk/apps/Freemail/src/freemail/utils/PropsFile.java =================================================================== --- trunk/apps/Freemail/src/freemail/utils/PropsFile.java 2006-08-15 20:25:45 UTC (rev 10098) +++ trunk/apps/Freemail/src/freemail/utils/PropsFile.java 2006-08-15 20:38:49 UTC (rev 10099) @@ -15,6 +15,8 @@ private final File file; private HashMap data; private BufferedReader bufrdr; + private String commentPrefix; + private String header; /** Pass true into stopAtBlank to cause the reader to stop upon encountering * a blank line. It's the the caller's responsibility to get @@ -30,12 +32,22 @@ } catch (IOException ioe) { } } + this.commentPrefix = null; + this.header = null; } public PropsFile(File f) { this(f, false); } + public void setCommentPrefix(String cp) { + this.commentPrefix = cp; + } + + public void setHeader(String hdr) { + this.header = hdr; + } + private BufferedReader read(boolean stopAtBlank) throws IOException { this.data = new HashMap(); @@ -43,6 +55,9 @@ String line = null; while ( (line = br.readLine()) != null) { + if (this.commentPrefix != null && line.startsWith(this.commentPrefix)) { + continue; + } if (stopAtBlank && line.length() == 0) { return br; } @@ -70,6 +85,8 @@ private void write() throws IOException { PrintWriter pw = new PrintWriter(new FileOutputStream(this.file)); + if (this.header != null) pw.println(this.header); + Iterator i = this.data.entrySet().iterator(); while (i.hasNext()) { Map.Entry e = (Map.Entry) i.next(); From dbkr at freenetproject.org Sun Aug 27 00:11:02 2006 From: dbkr at freenetproject.org (dbkr at freenetproject.org) Date: Sun, 27 Aug 2006 00:11:02 +0000 (UTC) Subject: [Freemail] r10274 - in trunk/apps/Freemail/src: . freemailgui freemailgui/images freemailgui/text Message-ID: <20060827001102.ABA839CA1F@emu.freenetproject.org> Author: dbkr Date: 2006-08-27 00:10:41 +0000 (Sun, 27 Aug 2006) New Revision: 10274 Added: trunk/apps/Freemail/src/freemailgui/ trunk/apps/Freemail/src/freemailgui/SetupWizard.java trunk/apps/Freemail/src/freemailgui/WizardChooseUsername.java trunk/apps/Freemail/src/freemailgui/WizardWelcome.java trunk/apps/Freemail/src/freemailgui/images/ trunk/apps/Freemail/src/freemailgui/images/logo_and_text.png trunk/apps/Freemail/src/freemailgui/images/pigeon_small.png trunk/apps/Freemail/src/freemailgui/text/ trunk/apps/Freemail/src/freemailgui/text/MessageBundle_en_GB.properties Log: The start of a config wizard. Commit this before I take the second page to bits to get it to lay out sensibly. It doesn't do anything useful yet, but it doesn't break anything either. Added: trunk/apps/Freemail/src/freemailgui/SetupWizard.java =================================================================== --- trunk/apps/Freemail/src/freemailgui/SetupWizard.java 2006-08-26 21:26:41 UTC (rev 10273) +++ trunk/apps/Freemail/src/freemailgui/SetupWizard.java 2006-08-27 00:10:41 UTC (rev 10274) @@ -0,0 +1,140 @@ +package freemailgui; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.UIManager; +import javax.swing.ImageIcon; +import javax.swing.BoxLayout; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.Box; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import java.util.ResourceBundle; +import java.util.Locale; +import java.util.MissingResourceException; + +public class SetupWizard implements ActionListener { + private final JFrame frame; + private final JPanel panel; + private JPanel subpanel; + private final JButton backbutton; + private final JButton cancelbutton; + private final JButton nextbutton; + private ResourceBundle bundle; + private final JLabel logolabel; + private int currentstep; + + public static void main(String args[]) { + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + new SetupWizard().show(); + } + }); + } + + public SetupWizard() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + } + + try { + this.bundle = ResourceBundle.getBundle("freemailgui.text.MessageBundle", Locale.getDefault()); + } catch (MissingResourceException mre) { + this.bundle = ResourceBundle.getBundle("freemailgui.text.MessageBundle", new Locale("en", "GB")); + } + + this.frame = new JFrame(this.bundle.getString("wizard_title")); + this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + ImageIcon icon = new ImageIcon(getClass().getResource("images/pigeon_small.png")); + this.frame.setIconImage(icon.getImage()); + + this.panel = new JPanel(); + this.panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + this.panel.setLayout(new BoxLayout(this.panel, BoxLayout.Y_AXIS)); + + ImageIcon logo = new ImageIcon(getClass().getResource("images/logo_and_text.png")); + this.logolabel = new JLabel(); + logolabel.setIcon(logo); + logolabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + + this.backbutton = new JButton("<< "+bundle.getString("back")); + this.backbutton.addActionListener(this); + + this.cancelbutton = new JButton(bundle.getString("cancel")); + this.cancelbutton.addActionListener(this); + + this.nextbutton = new JButton(bundle.getString("next")+" >>"); + this.nextbutton.addActionListener(this); + + this.currentstep = 0; + this.makeGUI(); + this.frame.add(this.panel); + } + + public void show() { + // make the window a fixed size - it's a wizard + this.frame.setSize(500, 400); + // center + this.frame.setLocationRelativeTo(null); + this.frame.validate(); + this.frame.setVisible(true); + } + + private void makeGUI() { + this.panel.removeAll(); + + this.panel.add(logolabel, 0); + this.panel.add(Box.createVerticalGlue()); + + switch (this.currentstep) { + case 0: + this.subpanel = new WizardWelcome(this.bundle); + break; + case 1: + this.subpanel = new WizardChooseUsername(this.bundle); + break; + } + + if (this.currentstep == 0) { + this.backbutton.setEnabled(false); + } else { + this.backbutton.setEnabled(true); + } + + this.panel.add(this.subpanel); + + this.panel.add(Box.createVerticalGlue()); + + JPanel buttons = new JPanel(); + buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS)); + buttons.add(this.backbutton); + buttons.add(Box.createHorizontalGlue()); + buttons.add(this.cancelbutton); + buttons.add(Box.createHorizontalGlue()); + buttons.add(this.nextbutton); + + this.panel.add(buttons); + this.frame.validate(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == this.cancelbutton) { + System.exit(0); + } else if (e.getSource() == this.nextbutton) { + this.currentstep++; + this.makeGUI(); + this.panel.repaint(); + //this.show(); + } else if (e.getSource() == this.backbutton) { + this.currentstep--; + this.makeGUI(); + this.panel.repaint(); + //this.show(); + } + } +} Added: trunk/apps/Freemail/src/freemailgui/WizardChooseUsername.java =================================================================== --- trunk/apps/Freemail/src/freemailgui/WizardChooseUsername.java 2006-08-26 21:26:41 UTC (rev 10273) +++ trunk/apps/Freemail/src/freemailgui/WizardChooseUsername.java 2006-08-27 00:10:41 UTC (rev 10274) @@ -0,0 +1,88 @@ +package freemailgui; + +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.BoxLayout; +import javax.swing.SwingConstants; +import javax.swing.BorderFactory; +import javax.swing.Box; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import javax.swing.JTextField; +import javax.swing.JPasswordField; + +import java.util.ResourceBundle; + +public class WizardChooseUsername extends JPanel { + public static final long serialVersionUID = -1; + private JTextField usernametext; + private JTextField passwordtext; + private JTextField passwordconfirmtext; + + public WizardChooseUsername(ResourceBundle bundle) { + super(); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + this.add(Box.createVerticalGlue()); + + JLabel welcomelabel = new JLabel(bundle.getString("choose_a_username_and_password"), SwingConstants.CENTER); + welcomelabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + this.add(welcomelabel); + + JPanel usernamebox = new JPanel(); + usernamebox.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); + GridBagLayout gb = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + usernamebox.setLayout(gb); + + JLabel usernamelbl = new JLabel(bundle.getString("username")+": ", SwingConstants.RIGHT); + usernamelbl.setAlignmentX(JLabel.RIGHT_ALIGNMENT); + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + gb.setConstraints(usernamelbl, c); + usernamebox.add(usernamelbl); + + this.usernametext = new JTextField(); + c.gridwidth = GridBagConstraints.REMAINDER; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + gb.setConstraints(this.usernametext, c); + usernamebox.add(this.usernametext); + + + JLabel passwordlbl = new JLabel(bundle.getString("password")+": ", SwingConstants.RIGHT); + passwordlbl.setAlignmentX(JLabel.RIGHT_ALIGNMENT); + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + gb.setConstraints(passwordlbl, c); + usernamebox.add(passwordlbl); + + this.passwordtext = new JPasswordField(); + c.gridwidth = GridBagConstraints.REMAINDER; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + gb.setConstraints(this.passwordtext, c); + usernamebox.add(this.passwordtext); + + JLabel passwordconfirmlbl = new JLabel(bundle.getString("confirm_password")+": ", SwingConstant