001 package ezvcard.types;
002
003 import java.io.File;
004 import java.io.IOException;
005 import java.io.InputStream;
006 import java.util.List;
007
008 import ezvcard.VCard;
009 import ezvcard.VCardSubTypes;
010 import ezvcard.VCardVersion;
011 import ezvcard.io.CompatibilityMode;
012 import ezvcard.io.SkipMeException;
013 import ezvcard.parameters.KeyTypeParameter;
014 import ezvcard.parameters.MediaTypeParameter;
015 import ezvcard.parameters.ValueParameter;
016 import ezvcard.util.DataUri;
017 import ezvcard.util.HCardElement;
018 import ezvcard.util.VCardStringUtils;
019 import ezvcard.util.XCardElement;
020
021 /*
022 Copyright (c) 2012, Michael Angstadt
023 All rights reserved.
024
025 Redistribution and use in source and binary forms, with or without
026 modification, are permitted provided that the following conditions are met:
027
028 1. Redistributions of source code must retain the above copyright notice, this
029 list of conditions and the following disclaimer.
030 2. Redistributions in binary form must reproduce the above copyright notice,
031 this list of conditions and the following disclaimer in the documentation
032 and/or other materials provided with the distribution.
033
034 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
035 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
036 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
038 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
039 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
040 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
043 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 The views and conclusions contained in the software and documentation are those
046 of the authors and should not be interpreted as representing official policies,
047 either expressed or implied, of the FreeBSD Project.
048 */
049
050 /**
051 * A public key for encryption.
052 *
053 * <p>
054 * <b>Adding a key</b>
055 * </p>
056 *
057 * <pre>
058 * VCard vcard = new VCard();
059 *
060 * //URL (vCard 4.0 only; KEYs cannot have URLs in vCard 2.1 and 3.0)
061 * KeyType key = new KeyType("http://www.mywebsite.com/pubkey.pgp", KeyTypeParameter.PGP);
062 * vcard.addKey(key);
063 *
064 * //binary data
065 * byte data[] = ...
066 * key = new KeyType(data, KeyTypeParameter.PGP);
067 * vcard.addKey(key);
068 *
069 * //plain text value
070 * key = new KeyType();
071 * key.setText("...", KeyTypeParameter.PGP);
072 * vcard.addKey(key);
073 *
074 * //if "KeyTypeParameter" does not have the pre-defined constant that you need, then create a new instance
075 * //arg 1: the value of the 2.1/3.0 TYPE parameter
076 * //arg 2: the value to use for the 4.0 MEDIATYPE parameter and for 4.0 data URIs
077 * //arg 3: the file extension of the data type (optional)
078 * KeyTypeParameter param = new KeyTypeParameter("mykey", "application/my-key", "mkey");
079 * key = new KeyType("http://www.mywebsite.com/pubkey.enc", param);
080 * vcard.addKey(key);
081 * </pre>
082 *
083 * <p>
084 * <b>Getting the keys</b>
085 * </p>
086 *
087 * <pre>
088 * VCard vcard = ...
089 *
090 * int fileCount = 0;
091 * for (KeyType key : vcard.getKeys()){
092 * //the key will have either a URL or a binary data
093 * //only 4.0 vCards are allowed to use URLs for keys
094 * if (key.getData() == null){
095 * System.out.println("Key URL: " + key.getUrl());
096 * } else {
097 * KeyTypeParameter type = key.getContentType();
098 *
099 * if (type == null) {
100 * //the vCard may not have any content type data associated with the key
101 * System.out.println("Saving a key file...");
102 * } else {
103 * System.out.println("Saving a \"" + type.getMediaType() + "\" file...");
104 * }
105 *
106 * String folder;
107 * if (type == KeyTypeParameter.PGP){ //it is safe to use "==" instead of "equals()"
108 * folder = "pgp-keys";
109 * } else {
110 * folder = "other-keys";
111 * }
112 *
113 * byte data[] = key.getData();
114 * String filename = "key" + fileCount;
115 * if (type != null && type.getExtension() != null){
116 * filename += "." + type.getExtension();
117 * }
118 * OutputStream out = new FileOutputStream(new File(folder, filename));
119 * out.write(data);
120 * out.close();
121 * fileCount++;
122 * }
123 * }
124 * </pre>
125 *
126 * <p>
127 * vCard property name: KEY
128 * </p>
129 * <p>
130 * vCard versions: 2.1, 3.0, 4.0
131 * </p>
132 * @author Michael Angstadt
133 */
134 public class KeyType extends BinaryType<KeyTypeParameter> {
135 public static final String NAME = "KEY";
136
137 private String text;
138
139 public KeyType() {
140 super(NAME);
141 }
142
143 /**
144 * @param data the binary data
145 * @param type the type of key (e.g. PGP)
146 */
147 public KeyType(byte data[], KeyTypeParameter type) {
148 super(NAME, data, type);
149 }
150
151 /**
152 * @param url the URL to the key (vCard 4.0 only)
153 * @param type the type of key (e.g. PGP)
154 */
155 public KeyType(String url, KeyTypeParameter type) {
156 super(NAME, url, type);
157 }
158
159 /**
160 * @param in an input stream to the binary data (will be closed)
161 * @param type the content type (e.g. PGP)
162 * @throws IOException if there's a problem reading from the input stream
163 */
164 public KeyType(InputStream in, KeyTypeParameter type) throws IOException {
165 super(NAME, in, type);
166 }
167
168 /**
169 * @param file the key file
170 * @param type the content type (e.g. PGP)
171 * @throws IOException if there's a problem reading from the file
172 */
173 public KeyType(File file, KeyTypeParameter type) throws IOException {
174 super(NAME, file, type);
175 }
176
177 /**
178 * Sets a plain text representation of the key.
179 * @param text the key in plain text
180 * @param type the key type
181 */
182 public void setText(String text, KeyTypeParameter type) {
183 this.text = text;
184 data = null;
185 url = null;
186 setContentType(type);
187 }
188
189 /**
190 * Gets the plain text representation of the key.
191 * @return the key in plain text
192 */
193 public String getText() {
194 return text;
195 }
196
197 @Override
198 protected KeyTypeParameter buildTypeObj(String type) {
199 KeyTypeParameter param = KeyTypeParameter.valueOf(type);
200 if (param == null) {
201 param = new KeyTypeParameter(type, "application/" + type, null);
202 }
203 return param;
204 }
205
206 @Override
207 protected KeyTypeParameter buildMediaTypeObj(String mediaType) {
208 KeyTypeParameter p = KeyTypeParameter.findByMediaType(mediaType);
209 if (p == null) {
210 int slashPos = mediaType.indexOf('/');
211 String type;
212 if (slashPos == -1 || slashPos < mediaType.length() - 1) {
213 type = "";
214 } else {
215 type = mediaType.substring(slashPos + 1);
216 }
217 p = new KeyTypeParameter(type, mediaType, null);
218 }
219 return p;
220 }
221
222 @Override
223 protected void doMarshalSubTypes(VCardSubTypes copy, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, VCard vcard) {
224 if (text != null) {
225 MediaTypeParameter contentType = getContentType();
226 if (contentType == null) {
227 contentType = new MediaTypeParameter(null, null, null);
228 }
229
230 copy.setValue(ValueParameter.TEXT);
231 copy.setEncoding(null);
232 if (version == VCardVersion.V4_0) {
233 //don't null out TYPE, it could be set to "home" or "work"
234 copy.setMediaType(contentType.getMediaType());
235 } else {
236 copy.setType(contentType.getValue());
237 copy.setMediaType(null);
238 }
239 }
240 }
241
242 @Override
243 protected void doMarshalText(StringBuilder sb, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
244 if (text != null) {
245 sb.append(VCardStringUtils.escape(text));
246 } else {
247 if ((version == VCardVersion.V2_1 || version == VCardVersion.V3_0) && getUrl() != null) {
248 warnings.add("vCard version " + version + " specs do not allow URLs to be used in the " + NAME + " type.");
249 }
250 super.doMarshalText(sb, version, warnings, compatibilityMode);
251 }
252 }
253
254 @Override
255 protected void doMarshalXml(XCardElement parent, List<String> warnings, CompatibilityMode compatibilityMode) {
256 if (text != null) {
257 parent.text(text);
258 } else {
259 VCardVersion version = parent.version();
260 if ((version == VCardVersion.V2_1 || version == VCardVersion.V3_0) && getUrl() != null) {
261 warnings.add("vCard version " + version + " specs do not allow URLs to be used in the " + NAME + " type.");
262 }
263 super.doMarshalXml(parent, warnings, compatibilityMode);
264 }
265 }
266
267 @Override
268 protected void doUnmarshalXml(XCardElement element, List<String> warnings, CompatibilityMode compatibilityMode) {
269 String value = element.uri();
270 if (value != null) {
271 super.doUnmarshalText(value, element.version(), warnings, compatibilityMode);
272 } else {
273 value = element.text();
274 if (value != null) {
275 String mediaType = subTypes.getMediaType();
276 KeyTypeParameter contentType = (mediaType != null) ? buildMediaTypeObj(mediaType) : null;
277 setText(value, contentType);
278 }
279 }
280 }
281
282 @Override
283 protected void doUnmarshalHtml(HCardElement element, List<String> warnings) {
284 String elementName = element.tagName();
285 if ("a".equals(elementName)) {
286 String href = element.absUrl("href");
287 if (href.length() > 0) {
288 try {
289 DataUri uri = new DataUri(href);
290 KeyTypeParameter mediaType = buildMediaTypeObj(uri.getContentType());
291 setData(uri.getData(), mediaType);
292 } catch (IllegalArgumentException e) {
293 //TODO create buildTypeObjFromExtension() method
294 setUrl(href, null);
295 }
296 } else {
297 throw new SkipMeException("<a> tag does not have a \"href\" attribute.");
298 }
299 } else {
300 super.doUnmarshalHtml(element, warnings);
301 }
302 }
303
304 @Override
305 protected void cannotUnmarshalValue(String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, KeyTypeParameter contentType) {
306 //unmarshal it as a plain text key
307 setText(VCardStringUtils.unescape(value), contentType);
308 }
309 }