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    }