/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts

« back to all changes in this revision

Viewing changes to src/org/waxworlds/importcontacts/VCFImporter.java

  • Committer: edam
  • Date: 2009-01-13 06:35:26 UTC
  • Revision ID: edam@waxworlds.org-20090113063526-l9t1s9git4bav60a
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)

Show diffs side-by-side

added added

removed removed

21
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
22
 */
23
23
 
24
 
package org.waxworlds.edam.importcontacts;
 
24
package org.waxworlds.importcontacts;
25
25
 
26
26
import java.io.BufferedReader;
27
27
import java.io.File;
28
 
import java.io.FileInputStream;
29
28
import java.io.FileNotFoundException;
30
29
import java.io.FileReader;
31
30
import java.io.FilenameFilter;
32
31
import java.io.IOException;
33
32
import java.io.UnsupportedEncodingException;
34
 
import java.nio.ByteBuffer;
35
33
import java.util.Arrays;
36
34
import java.util.HashSet;
37
35
import java.util.List;
40
38
import java.util.regex.Matcher;
41
39
import java.util.regex.Pattern;
42
40
 
 
41
import org.waxworlds.importcontacts.Importer.AbortImportException;
 
42
 
43
43
import android.content.SharedPreferences;
44
44
import android.provider.Contacts;
45
45
import android.provider.Contacts.PhonesColumns;
67
67
                try
68
68
                {
69
69
                        // open directory
70
 
                        String path = "/sdcard" + prefs.getString( "location", "/" );
71
 
                        File file = new File( path );
72
 
                        if( !file.exists() )
 
70
                        String location = prefs.getString( "location", "" );
 
71
                        File dir = new File( location );
 
72
                        if( !dir.exists() || !dir.isDirectory() )
73
73
                                showError( R.string.error_locationnotfound );
74
74
 
75
 
                        // directory, or file?
76
 
                        if( file.isDirectory() )
77
 
                        {
78
 
                                // get files
79
 
                                class VCardFilter implements FilenameFilter {
80
 
                                        public boolean accept( File dir, String name ) {
81
 
                                                return name.toLowerCase().endsWith( ".vcf" );
82
 
                                        }
83
 
                                }
84
 
                                files = file.listFiles( new VCardFilter() );
85
 
                        }
86
 
                        else
87
 
                        {
88
 
                                // use just this file
89
 
                                files = new File[ 1 ];
90
 
                                files[ 0 ] = file;
91
 
                        }
 
75
                        // get files
 
76
                        class VCardFilter implements FilenameFilter {
 
77
                            public boolean accept( File dir, String name ) {
 
78
                                return name.toLowerCase().endsWith( ".vcf" );
 
79
                            }
 
80
                        }
 
81
                        files = dir.listFiles( new VCardFilter() );
92
82
                }
93
83
                catch( SecurityException e ) {
94
84
                        showError( R.string.error_locationpermissions );
140
130
 
141
131
                }
142
132
                catch( FileNotFoundException e ) {
143
 
                        showError( getText( R.string.error_filenotfound ) +
144
 
                                file.getName() );
 
133
                        showError( getText( R.string.error_filenotfound ) + file.getName() );
145
134
                }
146
135
                catch( IOException e ) {
147
136
                        showError( getText( R.string.error_ioerror ) + file.getName() );
150
139
 
151
140
        private void importVCardFile( File file ) throws AbortImportException
152
141
        {
153
 
                // check file is good
154
 
                if( !file.exists() )
155
 
                        showError( getText( R.string.error_filenotfound ) +
156
 
                                file.getName() );
157
 
                if( file.length() == 0 )
158
 
                        showError( getText( R.string.error_fileisempty ) +
159
 
                                file.getName() );
160
 
 
161
142
                try
162
143
                {
163
 
                        // open/read file
164
 
                        FileInputStream istream = new FileInputStream( file );
165
 
                        byte[] content = new byte[ (int)file.length() ];
166
 
                        istream.read( content );
167
 
 
168
 
                        // import
169
 
                        importVCardFileContent( content, file.getName() );
 
144
                        // open file
 
145
                        BufferedReader reader = new BufferedReader(
 
146
                                        new FileReader( file ) );
 
147
 
 
148
                        // read
 
149
                        StringBuffer content = new StringBuffer();
 
150
                        String line;
 
151
                        while( ( line = reader.readLine() ) != null )
 
152
                                content.append( line ).append( "\n" );
 
153
 
 
154
                        importVCardFileContent( content.toString(), file.getName() );
170
155
                }
171
156
                catch( FileNotFoundException e ) {
172
 
                        showError( getText( R.string.error_filenotfound ) +
173
 
                                file.getName() );
 
157
                        showError( getText( R.string.error_filenotfound ) + file.getName() );
174
158
                }
175
159
                catch( IOException e ) {
176
160
                        showError( getText( R.string.error_ioerror ) + file.getName() );
177
161
                }
178
162
        }
179
163
 
180
 
        private void importVCardFileContent( byte[] content, String fileName )
 
164
        private void importVCardFileContent( String content, String fileName )
181
165
                        throws AbortImportException
182
166
        {
183
 
                ByteBuffer buffers[] = getLinesFromContent( content );
 
167
                // unfold RFC2425 section 5.8.1 folded lines, except that we must also
 
168
                // handle embedded Quoted-Printable encodings that have a trailing '='.
 
169
                // So we remove these first before doing RFC2425 unfolding.
 
170
                content = content.replaceAll( "=\n[ \\t]", "" )
 
171
                                .replaceAll( "\n[ \\t]", "" );
184
172
 
185
 
                // go through lines
 
173
                // get lines and parse them
 
174
                String[] lines = content.split( "\n" );
186
175
                VCard vCard = null;
187
 
                for( int i = 0; i < buffers.length; i++ )
 
176
                for( int i = 0; i < lines.length; i++ )
188
177
                {
189
 
                        // get a US-ASCII version of the line for processing
190
 
                        String line;
191
 
                        try {
192
 
                                line = new String( buffers[ i ].array(), buffers[ i ].position(),
193
 
                                        buffers[ i ].limit() - buffers[ i ].position(), "US-ASCII" );
194
 
                        }
195
 
                        catch( UnsupportedEncodingException e ) {
196
 
                                // we know US-ASCII is supported, so appease the compiler...
197
 
                                line = "";
198
 
                        }
 
178
                        String line = lines[ i ];
199
179
 
200
180
                        if( vCard == null ) {
201
181
                                // look for vcard beginning
230
210
                                {
231
211
                                        // try giving the line to the vcard
232
212
                                        try {
233
 
                                                vCard.parseLine( buffers[ i ] );
 
213
                                                vCard.parseLine( line );
234
214
                                        }
235
215
                                        catch( VCard.ParseException e ) {
236
216
                                                skipContact();
255
235
                }
256
236
        }
257
237
 
258
 
        private ByteBuffer[] getLinesFromContent( byte[] content )
259
 
        {
260
 
                // count lines in data
261
 
                int num_lines = 1;
262
 
                for( int a = 0; a < content.length; a++ )
263
 
                        if( content[ a ] == '\n' )
264
 
                                num_lines++;
265
 
 
266
 
                // get lines, removing \r's and \n's as we go
267
 
                ByteBuffer lines[] = new ByteBuffer[ num_lines ];
268
 
                int last = 0;
269
 
                for( int a = 0, b = 0; a < content.length; a++ )
270
 
                        if( content[ a ] == '\n' ) {
271
 
                                int to = ( a > 0 && content[ a - 1 ] == '\r' &&
272
 
                                        a - 1 >= last )? a - 1 : a;
273
 
                                lines[ b++ ] = ByteBuffer.wrap( content, last, to - last );
274
 
                                last = a + 1;
275
 
                        }
276
 
                lines[ lines.length - 1 ] = ByteBuffer.wrap( content, last,
277
 
                        content.length - last );
278
 
 
279
 
                return lines;
280
 
        }
281
 
 
282
238
        private class VCard extends ContactData
283
239
        {
284
240
                private final static int NAMELEVEL_NONE = 0;
287
243
                private final static int NAMELEVEL_N = 3;
288
244
 
289
245
                private String _version = null;
290
 
                private Vector< ByteBuffer > _buffers = null;
291
 
                private int _name_level = NAMELEVEL_NONE;
292
 
                private boolean _parser_in_multiline = false;
293
 
                private String _parser_current_name_and_params = null;
294
 
                private String _parser_buffered_value_so_far = "";
295
 
 
296
 
                protected class UnencodeResult
297
 
                {
298
 
                        private boolean _another_line_required;
299
 
                        private ByteBuffer _buffer;
300
 
 
301
 
                        public UnencodeResult( boolean another_line_required,
302
 
                                ByteBuffer buffer )
303
 
                        {
304
 
                                _another_line_required = another_line_required;
305
 
                                _buffer = buffer;
306
 
                        }
307
 
 
308
 
                        public boolean isAnotherLineRequired()
309
 
                        {
310
 
                                return _another_line_required;
311
 
                        }
312
 
 
313
 
                        public ByteBuffer getBuffer()
314
 
                        {
315
 
                                return _buffer;
316
 
                        }
317
 
                }
318
 
 
319
 
                @SuppressWarnings("serial")
 
246
                private Vector< String > _lines = null;
 
247
                private int _nameLevel = NAMELEVEL_NONE;
 
248
 
320
249
                protected class ParseException extends Exception
321
250
                {
322
 
                        @SuppressWarnings("unused")
323
251
                        public ParseException( String error )
324
252
                        {
325
253
                                super( error );
331
259
                        }
332
260
                }
333
261
 
334
 
                @SuppressWarnings("serial")
335
262
                protected class SkipContactException extends Exception { }
336
263
 
337
 
                public void parseLine( ByteBuffer buffer )
 
264
                public void parseLine( String line )
338
265
                                throws ParseException, SkipContactException,
339
266
                                AbortImportException
340
267
                {
341
 
                        // get a US-ASCII version of the line for processing
342
 
                        String line;
343
 
                        try {
344
 
                                line = new String( buffer.array(), buffer.position(),
345
 
                                        buffer.limit() - buffer.position(), "US-ASCII" );
346
 
                        }
347
 
                        catch( UnsupportedEncodingException e ) {
348
 
                                // we know US-ASCII is supported, so appease the compiler...
349
 
                                line = "";
350
 
                        }
351
 
 
352
 
                        // ignore empty lines
353
 
                        if( line.trim() == "" ) return;
354
 
 
355
 
                        // split line into name and value parts (this may turn out to be
356
 
                        // unwanted if the line is a subsequent line in a multi-line
357
 
                        // value, but we have to do this now to check for and handle VCF
358
 
                        // versions first). Also, the value part is only created tentatively
359
 
                        // because it may have an encoding/charset. Since we're treating it
360
 
                        // as UTF-8 (which is compatible with 7-bit US-ASCII) this is ok
361
 
                        // though so long as we later use the raw bytes. ALso we check for
362
 
                        // malformed property:name pairs.
363
 
                        String name_and_params, string_value;
364
 
                        {
365
 
                                String[] parts = line.split( ":", 2 );
366
 
                                if( parts.length == 2 ) {
367
 
                                        name_and_params = parts[ 0 ].trim();
368
 
                                        string_value = parts[ 1 ].trim();
369
 
                                        if( name_and_params.length() == 0 )
370
 
                                                throw new ParseException( R.string.error_vcf_malformed );
371
 
                                }
372
 
                                else
373
 
                                {
374
 
                                        if( !_parser_in_multiline )
375
 
                                                throw new ParseException( R.string.error_vcf_malformed );
376
 
                                        name_and_params = null;
377
 
                                        string_value = null;
378
 
                                }
379
 
                        }
380
 
 
381
 
                        // if we haven't yet got a version, we won't be paring anything!
 
268
                        // get property halves
 
269
                        String[] props = line.split( ":" );
 
270
                        for( int i = 0; i < props.length; i++ )
 
271
                                props[ i ] = props[ i ].trim();
 
272
                        if( props.length < 2 ||
 
273
                                        props[ 0 ].length() < 1 || props[ 1 ].length() < 1 )
 
274
                                throw new ParseException( R.string.error_vcf_malformed );
 
275
 
382
276
                        if( _version == null )
383
277
                        {
384
 
                                // is this a version?
385
 
                                if( name_and_params.equals( "VERSION" ) )
 
278
                                if( props[ 0 ].equals( "VERSION" ) )
386
279
                                {
387
 
                                        // yes, check/store it
388
 
                                        if( !string_value.equals( "2.1" ) &&
389
 
                                                        !string_value.equals( "3.0" ) )
 
280
                                        // get version
 
281
                                        if( !props[ 1 ].equals( "2.1" ) &&
 
282
                                                        !props[ 1 ].equals( "3.0" ) )
390
283
                                                throw new ParseException( R.string.error_vcf_version );
391
 
                                        _version = string_value;
 
284
                                        _version = props[ 1 ];
392
285
 
393
 
                                        // parse any other buffers we've accumulated so far
394
 
                                        if( _buffers != null )
395
 
                                                for( int i = 0; i < _buffers.size(); i++ )
396
 
                                                        parseLine( _buffers.get( i ) );
397
 
                                        _buffers = null;
 
286
                                        // parse any other lines we've accumulated so far
 
287
                                        if( _lines != null )
 
288
                                                for( int i = 0; i < _lines.size(); i++ )
 
289
                                                        parseLine( _lines.get( i ) );
 
290
                                        _lines = null;
398
291
                                }
399
292
                                else
400
293
                                {
401
 
                                        // no, so stash this buffer till we have a version
402
 
                                        if( _buffers == null )
403
 
                                                _buffers = new Vector< ByteBuffer >();
404
 
                                        _buffers.add( buffer );
 
294
                                        // stash this line till we have a version
 
295
                                        if( _lines == null )
 
296
                                                _lines = new Vector< String >();
 
297
                                        _lines.add( line );
405
298
                                }
406
299
                        }
407
300
                        else
408
301
                        {
409
 
                                // value bytes, for processing
410
 
                                ByteBuffer value;
411
 
 
412
 
                                if( _parser_in_multiline )
413
 
                                {
414
 
                                        // if we're currently in a multi-line value, use the stored
415
 
                                        // property name and parameters
416
 
                                        name_and_params = _parser_current_name_and_params;
417
 
 
418
 
                                        // find start of string (skip spaces/tabs)
419
 
                                        int pos = buffer.position();
420
 
                                        byte[] buffer_array = buffer.array();
421
 
                                        while( pos < buffer.limit() && (
422
 
                                                buffer_array[ pos ] == ' ' ||
423
 
                                                buffer_array[ pos ] == '\t' ) )
424
 
                                        {
425
 
                                                pos++;
426
 
                                        }
427
 
 
428
 
                                        // get value from buffer
429
 
                                        value = ByteBuffer.wrap( buffer.array(), pos,
430
 
                                                buffer.limit() - pos );
431
 
                                }
432
 
                                else
433
 
                                {
434
 
                                        // ignore empty values
435
 
                                        if( string_value.length() < 1 ) return;
436
 
 
437
 
                                        // calculate how many chars to skip from beginning of line
438
 
                                        // so we skip the property "name:" part
439
 
                                        int pos = buffer.position() + name_and_params.length() + 1;
440
 
 
441
 
                                        // get value from buffer
442
 
                                        value = ByteBuffer.wrap( buffer.array(), pos,
443
 
                                                buffer.limit() - pos );
444
 
 
445
 
                                        // reset the saved multi-line state
446
 
                                        _parser_current_name_and_params = name_and_params;
447
 
                                        _parser_buffered_value_so_far = "";
448
 
                                }
449
 
 
450
302
                                // get parameter parts
451
 
                                String[] name_param_parts = name_and_params.split( ";", -1 );
452
 
                                for( int i = 0; i < name_param_parts.length; i++ )
453
 
                                        name_param_parts[ i ] = name_param_parts[ i ].trim();
454
 
 
455
 
                                // parse encoding parameter
456
 
                                String encoding = checkParam( name_param_parts, "ENCODING" );
457
 
                                if( encoding != null ) encoding = encoding.toUpperCase();
458
 
                                if( encoding != null && !encoding.equals( "8BIT" ) &&
459
 
                                        !encoding.equals( "QUOTED-PRINTABLE" ) )
460
 
                                        //&& !encoding.equals( "BASE64" ) )
461
 
                                {
462
 
                                        throw new ParseException( R.string.error_vcf_encoding );
463
 
                                }
464
 
 
465
 
                                // parse charset parameter
466
 
                                String charset = checkParam( name_param_parts, "CHARSET" );
467
 
                                if( charset != null ) charset = charset.toUpperCase();
468
 
                                if( charset != null && !charset.equals( "US-ASCII" ) &&
469
 
                                        !charset.equals( "ASCII" ) && !charset.equals( "UTF-8" ) )
470
 
                                {
471
 
                                        throw new ParseException( R.string.error_vcf_charset );
472
 
                                }
473
 
 
474
 
                                // do unencoding (or default to a fake unencoding result with
475
 
                                // the raw string)
476
 
                                UnencodeResult unencoding_result = null;
477
 
                                if( encoding != null && encoding.equals( "QUOTED-PRINTABLE" ) )
478
 
                                        unencoding_result = unencodeQuotedPrintable( value );
479
 
//                              else if( encoding != null && encoding.equals( "BASE64" ) )
480
 
//                                      result = unencodeBase64( props[ 1 ], charset );
481
 
                                if( unencoding_result != null ) {
482
 
                                        value = unencoding_result.getBuffer();
483
 
                                        _parser_in_multiline =
484
 
                                                unencoding_result.isAnotherLineRequired();
485
 
                                }
486
 
 
487
 
                                // convert 8-bit ASCII charset to US-ASCII
488
 
                                if( charset == null || charset == "ASCII" ) {
489
 
                                        value = transcodeAsciiToUtf8( value );
490
 
                                        charset = "UTF-8";
491
 
                                }
492
 
 
493
 
                                // process charset
494
 
                                try {
495
 
                                        string_value =
496
 
                                                new String( value.array(), value.position(),
497
 
                                                        value.limit() - value.position(), charset );
498
 
                                } catch( UnsupportedEncodingException e ) {
499
 
                                        throw new ParseException( R.string.error_vcf_charset );
500
 
                                }
501
 
 
502
 
                                // handle multi-line requests
503
 
                                if( _parser_in_multiline ) {
504
 
                                        _parser_buffered_value_so_far += string_value;
505
 
                                        return;
506
 
                                }
507
 
 
508
 
                                // add on buffered multi-line content
509
 
                                String complete_value =
510
 
                                        _parser_buffered_value_so_far + string_value;
 
303
                                String[] params = props[ 0 ].split( ";" );
 
304
                                for( int i = 0; i < params.length; i++ )
 
305
                                        params[ i ] = params[ i ].trim();
511
306
 
512
307
                                // parse some properties
513
 
                                if( name_param_parts[ 0 ].equals( "N" ) )
514
 
                                        parseN( name_param_parts, complete_value );
515
 
                                else if( name_param_parts[ 0 ].equals( "FN" ) )
516
 
                                        parseFN( name_param_parts, complete_value );
517
 
                                else if( name_param_parts[ 0 ].equals( "ORG" ) )
518
 
                                        parseORG( name_param_parts, complete_value );
519
 
                                else if( name_param_parts[ 0 ].equals( "TEL" ) )
520
 
                                        parseTEL( name_param_parts, complete_value );
521
 
                                else if( name_param_parts[ 0 ].equals( "EMAIL" ) )
522
 
                                        parseEMAIL( name_param_parts, complete_value );
 
308
                                if( params[ 0 ].equals( "N" ) )
 
309
                                        parseN( params, props[ 1 ] );
 
310
                                else if( params[ 0 ].equals( "FN" ) )
 
311
                                        parseFN( params, props[ 1 ] );
 
312
                                else if( params[ 0 ].equals( "ORG" ) )
 
313
                                        parseORG( params, props[ 1 ] );
 
314
                                else if( params[ 0 ].equals( "TEL" ) )
 
315
                                        parseTEL( params, props[ 1 ] );
 
316
                                else if( params[ 0 ].equals( "EMAIL" ) )
 
317
                                        parseEMAIL( params, props[ 1 ] );
523
318
                        }
524
319
                }
525
320
 
528
323
                                AbortImportException
529
324
                {
530
325
                        // already got a better name?
531
 
                        if( _name_level >= NAMELEVEL_N ) return;
 
326
                        if( _nameLevel >= NAMELEVEL_N ) return;
532
327
 
533
328
                        // get name parts
534
 
                        String[] name_parts = value.split( ";" );
535
 
                        for( int i = 0; i < name_parts.length; i++ )
536
 
                                name_parts[ i ] = name_parts[ i ].trim();
 
329
                        String[] nameparts = value.split( ";" );
 
330
                        for( int i = 0; i < nameparts.length; i++ )
 
331
                                nameparts[ i ] = nameparts[ i ].trim();
537
332
 
538
333
                        // build name
539
334
                        value = "";
540
 
                        if( name_parts.length > 1 && name_parts[ 1 ].length() > 0 )
541
 
                                value += name_parts[ 1 ];
542
 
                        if( name_parts.length > 0 && name_parts[ 0 ].length() > 0 )
543
 
                                value += ( value.length() == 0? "" : " " ) + name_parts[ 0 ];
 
335
                        if( nameparts.length > 1 && nameparts[ 1 ].length() > 0 )
 
336
                                value += nameparts[ 1 ];
 
337
                        if( nameparts[ 0 ].length() > 0 )
 
338
                                value += ( value.length() == 0? "" : " " ) + nameparts[ 0 ];
544
339
 
545
340
                        // set name
546
 
                        setName( value );
547
 
                        _name_level = NAMELEVEL_N;
 
341
                        setName( undoCharsetAndEncoding( params, value ) );
 
342
                        _nameLevel = NAMELEVEL_N;
548
343
 
549
344
                        // check now to see if we need to import this contact (to avoid
550
345
                        // parsing the rest of the vCard unnecessarily)
556
351
                                throws ParseException, SkipContactException
557
352
                {
558
353
                        // already got a better name?
559
 
                        if( _name_level >= NAMELEVEL_FN ) return;
 
354
                        if( _nameLevel >= NAMELEVEL_FN ) return;
560
355
 
561
356
                        // set name
562
 
                        setName( value );
563
 
                        _name_level = NAMELEVEL_FN;
 
357
                        setName( undoCharsetAndEncoding( params, value ) );
 
358
                        _nameLevel = NAMELEVEL_FN;
564
359
                }
565
360
 
566
361
                private void parseORG( String[] params, String value )
567
362
                                throws ParseException, SkipContactException
568
363
                {
569
364
                        // already got a better name?
570
 
                        if( _name_level >= NAMELEVEL_ORG ) return;
 
365
                        if( _nameLevel >= NAMELEVEL_ORG ) return;
571
366
 
572
367
                        // get org parts
573
 
                        String[] org_parts = value.split( ";" );
574
 
                        for( int i = 0; i < org_parts.length; i++ )
575
 
                                org_parts[ i ] = org_parts[ i ].trim();
 
368
                        String[] orgparts = value.split( ";" );
 
369
                        for( int i = 0; i < orgparts.length; i++ )
 
370
                                orgparts[ i ] = orgparts[ i ].trim();
576
371
 
577
372
                        // build name
578
 
                        if( org_parts.length > 1 && org_parts[ 0 ].length() == 0 )
579
 
                                value = org_parts[ 1 ];
 
373
                        if( orgparts[ 0 ].length() == 0 && orgparts.length > 1 )
 
374
                                value = orgparts[ 1 ];
580
375
                        else
581
 
                                value = org_parts[ 0 ];
 
376
                                value = orgparts[ 0 ];
582
377
 
583
378
                        // set name
584
 
                        setName( value );
585
 
                        _name_level = NAMELEVEL_ORG;
 
379
                        setName( undoCharsetAndEncoding( params, value ) );
 
380
                        _nameLevel = NAMELEVEL_ORG;
586
381
                }
587
382
 
588
383
                private void parseTEL( String[] params, String value )
596
391
 
597
392
                        // here's the logic...
598
393
                        boolean preferred = types.contains( "PREF" );
599
 
                        int type = PhonesColumns.TYPE_MOBILE;
600
394
                        if( types.contains( "VOICE" ) )
601
395
                                if( types.contains( "WORK" ) )
602
 
                                        type = PhonesColumns.TYPE_WORK;
 
396
                                        addPhone( value, PhonesColumns.TYPE_WORK, preferred );
603
397
                                else
604
 
                                        type = PhonesColumns.TYPE_HOME;
 
398
                                        addPhone( value, PhonesColumns.TYPE_HOME, preferred );
605
399
                        else if( types.contains( "CELL" ) || types.contains( "VIDEO" ) )
606
 
                                type = PhonesColumns.TYPE_MOBILE;
 
400
                                addPhone( value, PhonesColumns.TYPE_MOBILE, preferred );
607
401
                        if( types.contains( "FAX" ) )
608
402
                                if( types.contains( "HOME" ) )
609
 
                                        type = PhonesColumns.TYPE_FAX_HOME;
 
403
                                        addPhone( value, PhonesColumns.TYPE_FAX_HOME, preferred );
610
404
                                else
611
 
                                        type = PhonesColumns.TYPE_FAX_WORK;
 
405
                                        addPhone( value, PhonesColumns.TYPE_FAX_WORK, preferred );
612
406
                        if( types.contains( "PAGER" ) )
613
 
                                type = PhonesColumns.TYPE_PAGER;
614
 
 
615
 
                        // add phone number
616
 
                        addPhone( value, type, preferred );
 
407
                                addPhone( value, PhonesColumns.TYPE_PAGER, preferred );
617
408
                }
618
409
 
619
410
                public void parseEMAIL( String[] params, String value )
620
 
                                throws ParseException
621
411
                {
622
412
                        if( value.length() == 0 ) return;
623
413
 
637
427
                                AbortImportException
638
428
                {
639
429
                        // missing version (and data is present)
640
 
                        if( _version == null && _buffers != null )
 
430
                        if( _version == null && _lines != null )
641
431
                                throw new ParseException( R.string.error_vcf_malformed );
642
432
 
643
433
                        //  missing name properties?
644
 
                        if( _name_level == NAMELEVEL_NONE )
 
434
                        if( _nameLevel == NAMELEVEL_NONE )
645
435
                                throw new ParseException( R.string.error_vcf_noname );
646
436
 
647
437
                        // check if we should import this one? If we've already got an 'N'-
648
438
                        // type name, this will already have been done by parseN() so we
649
439
                        // mustn't do this here (or it could prompt twice!)
650
 
                        if( _name_level < NAMELEVEL_N && !isImportRequired( getName() ) )
 
440
                        if( _nameLevel < NAMELEVEL_N && !isImportRequired( getName() ) )
651
441
                                throw new SkipContactException();
652
442
                }
653
443
 
 
444
                private String undoCharsetAndEncoding( String[] params, String value )
 
445
                                throws ParseException
 
446
                {
 
447
                        // check encoding/charset
 
448
                        String charset, encoding;
 
449
                        if( ( charset = checkParam( params, "CHARSET" ) ) != null &&
 
450
                                        !charset.equals( "UTF-8" ) && !charset.equals( "UTF-16" ) )
 
451
                                throw new ParseException( R.string.error_vcf_charset );
 
452
                        if( ( encoding = checkParam( params, "ENCODING" ) ) != null &&
 
453
                                        !encoding.equals( "QUOTED-PRINTABLE" ) )
 
454
                                throw new ParseException( R.string.error_vcf_encoding );
 
455
 
 
456
                        // do decoding?
 
457
                        if( encoding != null && encoding.equals( "QUOTED-PRINTABLE" ) )
 
458
                                return unencodeQuotedPrintable( value, charset );
 
459
 
 
460
                        // nothing to do!
 
461
                        return value;
 
462
                }
 
463
 
654
464
                private String checkParam( String[] params, String name )
655
465
                {
656
466
                        Pattern p = Pattern.compile( "^" + name + "[ \\t]*=[ \\t]*(.*)$" );
663
473
                }
664
474
 
665
475
                private Set< String > extractTypes( String[] params,
666
 
                                List< String > valid_types )
 
476
                                List< String > validTypes )
667
477
                {
668
478
                        HashSet< String > types = new HashSet< String >();
669
479
 
670
480
                        // get 3.0-style TYPE= param
671
 
                        String type_param;
672
 
                        if( ( type_param = checkParam( params, "TYPE" ) ) != null ) {
673
 
                                String[] parts = type_param.split( "," );
674
 
                                for( int i = 0; i < parts.length; i++ )
675
 
                                        if( valid_types.contains( parts[ i ] ) )
676
 
                                                types.add( parts[ i ] );
 
481
                        String typeParam;
 
482
                        if( ( typeParam = checkParam( params, "TYPE" ) ) != null ) {
 
483
                                String[] bits = typeParam.split( "," );
 
484
                                for( int i = 0; i < bits.length; i++ )
 
485
                                        if( validTypes.contains( bits[ i ] ) )
 
486
                                                types.add( bits[ i ] );
677
487
                        }
678
488
 
679
489
                        // get 2.1-style type param
680
490
                        if( _version.equals( "2.1" ) ) {
681
491
                                for( int i = 1; i < params.length; i++ )
682
 
                                        if( valid_types.contains( params[ i ] ) )
 
492
                                        if( validTypes.contains( params[ i ] ) )
683
493
                                                types.add( params[ i ] );
684
494
                        }
685
495
 
686
496
                        return types;
687
497
                }
688
498
 
689
 
                private UnencodeResult unencodeQuotedPrintable( ByteBuffer in )
 
499
                private String unencodeQuotedPrintable( String str, String charset )
690
500
                {
691
 
                        boolean another = false;
 
501
                        // default encoding scheme
 
502
                        if( charset == null ) charset = "UTF-8";
692
503
 
693
504
                        // unencode quoted-pritable encoding, as per RFC1521 section 5.1
694
 
                        byte[] out = new byte[ in.limit() - in.position() ];
 
505
                        byte[] bytes = new byte[ str.length() ];
695
506
                        int j = 0;
696
 
                        for( int i = in.position(); i < in.limit(); i++ )
697
 
                        {
698
 
                                // get next char and process...
699
 
                                byte ch = in.array()[ i ];
700
 
                                if( ch == '=' && i < in.limit() - 2 )
701
 
                                {
702
 
                                        // we found a =XX format byte, add it
703
 
                                        out[ j ] = (byte)(
704
 
                                                Character.digit( in.array()[ i + 1 ], 16 ) * 16 +
705
 
                                                Character.digit( in.array()[ i + 2 ], 16 ) );
 
507
                        for( int i = 0; i < str.length(); i++, j++ ) {
 
508
                                char ch = str.charAt( i );
 
509
                                if( ch == '=' && i < str.length() - 2 ) {
 
510
                                        bytes[ j ] = (byte)(
 
511
                                                        Character.digit( str.charAt( i + 1 ), 16 ) * 16 +
 
512
                                                        Character.digit( str.charAt( i + 2 ), 16 ) );
706
513
                                        i += 2;
707
514
                                }
708
 
                                else if( ch == '=' && i == in.limit() - 1 )
709
 
                                {
710
 
                                        // we found a '=' at the end of a line signifying a multi-
711
 
                                        // line string, so we don't add it.
712
 
                                        another = true;
713
 
                                        continue;
714
 
                                }
715
515
                                else
716
 
                                        // just a normal char...
717
 
                                        out[ j ] = (byte)ch;
718
 
                                j++;
719
 
                        }
720
 
 
721
 
                        return new UnencodeResult( another, ByteBuffer.wrap( out, 0, j ) );
722
 
                }
723
 
 
724
 
                private ByteBuffer transcodeAsciiToUtf8( ByteBuffer in )
725
 
                {
726
 
                        // transcode
727
 
                        byte[] out = new byte[ ( in.limit() - in.position() ) * 2 ];
728
 
                        int j = 0;
729
 
                        for( int a = in.position(); a < in.limit(); a++ )
730
 
                        {
731
 
                                // if char is < 127, keep it as-is
732
 
                                if( in.array()[ a ] >= 0 )
733
 
                                        out[ j++ ] = in.array()[ a ];
734
 
 
735
 
                                // else, convert it to UTF-8
736
 
                                else {
737
 
                                        int b = 0xff & (int)in.array()[ a ];
738
 
                                        out[ j++ ] = (byte)( 0xc0 | ( b >> 6 ) );
739
 
                                        out[ j++ ] = (byte)( 0x80 | ( b & 0x3f ) );
740
 
                                }
741
 
                        }
742
 
 
743
 
                        return ByteBuffer.wrap( out, 0, j );
 
516
                                        bytes[ j ] = (byte)ch;
 
517
                        }
 
518
                        try {
 
519
                                return new String( bytes, 0, j, charset );
 
520
                        } catch( UnsupportedEncodingException e ) { }
 
521
                        return null;
744
522
                }
745
523
        }
746
524
}