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 }