001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.vfs;
030:
031: import com.caucho.util.CharBuffer;
032: import com.caucho.util.StringCharCursor;
033:
034: import java.io.IOException;
035: import java.util.ArrayList;
036: import java.util.HashMap;
037: import java.util.Map;
038:
039: /**
040: * The mailto: scheme sends mail using the SMTP protocol.
041: * Attributes set headers. Headers can be set as long as no data
042: * has been flushed.
043: *
044: * <code><pre>
045: * WriteStream os = Vfs.openWrite("mailto:nobody@foo.com");
046: * os.setAttribute("subject", "Reminder message");
047: *
048: * os.println("This is just a simple reminder.");
049: * os.close();
050: * </pre></code>
051: *
052: * <p>The attributes set SMTP headers:
053: * <ul>
054: * <li>subject - message subject
055: * <li>to - recipient
056: * <li>cc - copy list
057: * <li>bcc - blind copy list
058: * <li><em>X-foo</em> - user-specified header
059: * </ul>
060: *
061: * <p>You can also set attributes in the URL as query parameters.
062: *
063: * <code><pre>
064: * Vfs.openWrite("mailto:nobody@foo.com?subject=dinner");
065: * </pre></code>
066: */
067: public class MailtoPath extends Path {
068: protected String url;
069: private ArrayList<Recipient> _to;
070: private ArrayList cc;
071: private ArrayList bcc;
072: private HashMap<String, Object> _attributes;
073:
074: MailtoPath(MailtoPath parent, String path, ArrayList<Recipient> to,
075: HashMap<String, Object> attr) {
076: super (parent);
077:
078: this .url = path;
079: _to = to;
080: _attributes = attr;
081: }
082:
083: /**
084: * Parse the scheme for the recipient and the attributes.
085: */
086: protected Path schemeWalk(String userPath,
087: Map<String, Object> attributes, String uri, int offset) {
088: StringCharCursor cursor = new StringCharCursor(uri, offset);
089:
090: ArrayList<Recipient> to = parseAddressList(cursor);
091: HashMap<String, Object> attr = new HashMap<String, Object>();
092:
093: CharBuffer buf = new CharBuffer();
094: if (cursor.current() == '?') {
095: char ch = cursor.next();
096: while (isUserChar(ch)) {
097: buf.clear();
098: for (; isUserChar(ch); ch = cursor.next())
099: buf.append(ch);
100: String key = buf.toString();
101:
102: if (ch != '=')
103: throw new RuntimeException("broken attribute at: "
104: + ch);
105: buf.clear();
106: for (ch = cursor.next(); ch != cursor.DONE && ch != '&'; ch = cursor
107: .next())
108: buf.append(ch);
109:
110: attr.put(key, buf.toString());
111:
112: while (ch == '&' || ch == ' ' || ch == '\t')
113: ch = cursor.next();
114: }
115: }
116:
117: return new MailtoPath(this , userPath, to, attr);
118: }
119:
120: /**
121: * Parses the address list from the URL.
122: *
123: * @param cursor parse cursor for the URL
124: * @return a list of recipient addresses
125: */
126: static ArrayList<Recipient> parseAddressList(StringCharCursor cursor) {
127: ArrayList<Recipient> to = new ArrayList<Recipient>();
128:
129: char ch = cursor.current();
130: CharBuffer buf = new CharBuffer();
131:
132: while (Character.isWhitespace(ch))
133: ch = cursor.next();
134:
135: while (isUserChar(ch)) {
136: buf.clear();
137: for (; isUserChar(ch); ch = cursor.next())
138: buf.append(ch);
139:
140: Recipient rcpt = new Recipient();
141: to.add(rcpt);
142: rcpt.user = buf.toString();
143:
144: if (ch == '@') {
145: ch = cursor.next();
146: if (!isUserChar(ch))
147: throw new RuntimeException("bad url");
148:
149: buf.clear();
150: for (; isUserChar(ch); ch = cursor.next())
151: buf.append(ch);
152:
153: rcpt.host = buf.toString();
154: }
155:
156: while (ch == ',' || ch == ' ' || ch == '\t' || ch == '\n'
157: || ch == '\r') {
158: ch = cursor.next();
159: }
160: }
161:
162: return to;
163: }
164:
165: /**
166: * Returns true if the char is a valid recipient character.
167: */
168: private static boolean isUserChar(int ch) {
169: switch (ch) {
170: case '.':
171: case '-':
172: case '_':
173: case '!':
174: case '$':
175: case '~':
176: case '^':
177: case '*':
178: case '/':
179: case '+':
180: return true;
181:
182: default:
183: return (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0'
184: && ch <= '9');
185: }
186: }
187:
188: /**
189: * The URL looks like "mailto:user@host.com"
190: */
191: public String getURL() {
192: return getPath();
193: }
194:
195: /**
196: * The scheme is "mailto:"
197: */
198: public String getScheme() {
199: return "mailto";
200: }
201:
202: /**
203: * The path looks like "mailto:user@host.com"
204: */
205: public String getPath() {
206: return "mailto:" + url;
207: }
208:
209: /**
210: * Gets the value of the RFC822 message headers.
211: */
212: public Object getAttribute(String name) {
213: return _attributes.get(name);
214: }
215:
216: /**
217: * Implementation to open a WriteStream.
218: */
219: public StreamImpl openWriteImpl() throws IOException {
220: return new SmtpStream(_to, _attributes);
221: }
222:
223: static class Recipient {
224: String user;
225: String host;
226: }
227: }
|