/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts
6 by edam
- added GPL header comments to all files
1
/*
2
 * VCFImporter.java
3
 *
4
 * Copyright (C) 2009 Tim Marston <edam@waxworlds.org>
5
 *
6
 * This file is part of the Import Contacts program (hereafter referred
7
 * to as "this program"). For more information, see
8
 * http://www.waxworlds.org/edam/software/android/import-contacts
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
24
package org.waxworlds.edam.importcontacts;
1 by edam
Initial import
25
26
import java.io.BufferedReader;
27
import java.io.File;
28
import java.io.FileNotFoundException;
29
import java.io.FileReader;
30
import java.io.FilenameFilter;
31
import java.io.IOException;
32
import java.io.UnsupportedEncodingException;
33
import java.util.Arrays;
34
import java.util.HashSet;
35
import java.util.List;
36
import java.util.Set;
37
import java.util.Vector;
38
import java.util.regex.Matcher;
39
import java.util.regex.Pattern;
40
41
import android.content.SharedPreferences;
42
import android.provider.Contacts;
43
import android.provider.Contacts.PhonesColumns;
44
45
public class VCFImporter extends Importer
46
{
47
	private int _vCardCount = 0;
48
	private int _progress = 0;
49
50
	public VCFImporter( Doit doit )
51
	{
52
		super( doit );
53
	}
54
55
	@Override
56
	protected void onImport() throws AbortImportException
57
	{
58
		SharedPreferences prefs = getSharedPreferences();
59
60
		// update UI
61
		setProgressMessage( R.string.doit_scanning );
62
63
		// get a list of vcf files
64
		File[] files = null;
65
		try
66
		{
67
			// open directory
19 by edam
- added file chooser
68
			String path = "/sdcard" + prefs.getString( "location", "/" );
69
			File file = new File( path );
70
			if( !file.exists() )
1 by edam
Initial import
71
				showError( R.string.error_locationnotfound );
72
15 by edam
- added facility to enter a filename (instead of a directory to scan) and just use that
73
			// directory, or file?
19 by edam
- added file chooser
74
			if( file.isDirectory() )
15 by edam
- added facility to enter a filename (instead of a directory to scan) and just use that
75
			{
76
				// get files
77
				class VCardFilter implements FilenameFilter {
78
					public boolean accept( File dir, String name ) {
79
						return name.toLowerCase().endsWith( ".vcf" );
80
					}
13 by edam
- converted project to use Android 1.5 SDK
81
				}
19 by edam
- added file chooser
82
				files = file.listFiles( new VCardFilter() );
15 by edam
- added facility to enter a filename (instead of a directory to scan) and just use that
83
			}
84
			else
85
			{
86
				// use just this file
87
				files = new File[ 1 ];
19 by edam
- added file chooser
88
				files[ 0 ] = file;
15 by edam
- added facility to enter a filename (instead of a directory to scan) and just use that
89
			}
1 by edam
Initial import
90
		}
91
		catch( SecurityException e ) {
92
			showError( R.string.error_locationpermissions );
93
		}
94
95
		// check num files and set progress max
96
		if( files != null && files.length > 0 )
97
			setProgressMax( files.length );
98
		else
99
			showError( R.string.error_locationnofiles );
100
101
		// scan through the files
102
		setTmpProgress( 0 );
103
		for( int i = 0; i < files.length; i++ ) {
104
			countVCardFile( files[ i ] );
105
			setTmpProgress( i );
106
		}
107
		setProgressMax( _vCardCount );	// will also update tmp progress
108
109
		// import them
110
		setProgress( 0 );
111
		for( int i = 0; i < files.length; i++ )
112
			importVCardFile( files[ i ] );
113
	}
114
115
	private void countVCardFile( File file ) throws AbortImportException
116
	{
117
		try
118
		{
119
			// open file
120
			BufferedReader reader = new BufferedReader(
121
					new FileReader( file ) );
122
123
			// read
124
			String line;
125
			boolean inVCard = false;
126
			while( ( line = reader.readLine() ) != null )
127
			{
128
				if( !inVCard ) {
129
					// look for vcard beginning
130
					if( line.matches( "^BEGIN[ \\t]*:[ \\t]*VCARD" ) ) {
131
						inVCard = true;
132
						_vCardCount++;
133
					}
134
				}
135
				else if( line.matches( "^END[ \\t]*:[ \\t]*VCARD" ) )
136
					inVCard = false;
137
			}
138
139
		}
140
		catch( FileNotFoundException e ) {
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
141
			showError( getText( R.string.error_filenotfound ) +
142
				file.getName() );
1 by edam
Initial import
143
		}
144
		catch( IOException e ) {
145
			showError( getText( R.string.error_ioerror ) + file.getName() );
146
		}
147
	}
148
149
	private void importVCardFile( File file ) throws AbortImportException
150
	{
151
		try
152
		{
153
			// open file
154
			BufferedReader reader = new BufferedReader(
155
					new FileReader( file ) );
156
157
			// read
158
			StringBuffer content = new StringBuffer();
159
			String line;
160
			while( ( line = reader.readLine() ) != null )
161
				content.append( line ).append( "\n" );
162
163
			importVCardFileContent( content.toString(), file.getName() );
164
		}
165
		catch( FileNotFoundException e ) {
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
166
			showError( getText( R.string.error_filenotfound ) +
167
				file.getName() );
1 by edam
Initial import
168
		}
169
		catch( IOException e ) {
170
			showError( getText( R.string.error_ioerror ) + file.getName() );
171
		}
172
	}
173
174
	private void importVCardFileContent( String content, String fileName )
175
			throws AbortImportException
176
	{
177
		// get lines and parse them
178
		String[] lines = content.split( "\n" );
179
		VCard vCard = null;
180
		for( int i = 0; i < lines.length; i++ )
181
		{
182
			String line = lines[ i ];
183
184
			if( vCard == null ) {
185
				// look for vcard beginning
186
				if( line.matches( "^BEGIN[ \\t]*:[ \\t]*VCARD" ) ) {
187
					setProgress( ++_progress );
188
					vCard = new VCard();
189
				}
190
			}
191
			else {
192
				// look for vcard content or ending
193
				if( line.matches( "^END[ \\t]*:[ \\t]*VCARD" ) )
194
				{
195
					// store vcard and do away with it
196
					try {
197
						vCard.finaliseParsing();
198
						importContact( vCard );
199
					}
200
					catch( VCard.ParseException e ) {
201
						skipContact();
202
						if( !showContinue(
203
								getText( R.string.error_vcf_parse ).toString()
204
								+ fileName + "\n" + e.getMessage() ) )
3 by edam
- added "all done" message
205
							finish( ACTION_ABORT );
1 by edam
Initial import
206
					}
207
					catch( VCard.SkipContactException e ) {
208
						skipContact();
209
						// do nothing
210
					}
211
					vCard = null;
212
				}
213
				else
214
				{
215
					// try giving the line to the vcard
216
					try {
217
						vCard.parseLine( line );
218
					}
219
					catch( VCard.ParseException e ) {
220
						skipContact();
221
						if( !showContinue(
222
								getText( R.string.error_vcf_parse ).toString()
223
								+ fileName + "\n" + e.getMessage() ) )
3 by edam
- added "all done" message
224
							finish( ACTION_ABORT );
1 by edam
Initial import
225
226
						// although we're continuing, we still need to abort
227
						// this vCard. Further lines will be ignored until we
228
						// get to another BEGIN:VCARD line.
229
						vCard = null;
230
					}
231
					catch( VCard.SkipContactException e ) {
232
						skipContact();
233
						// abort this vCard. Further lines will be ignored until
234
						// we get to another BEGIN:VCARD line.
235
						vCard = null;
236
					}
237
				}
238
			}
239
		}
240
	}
241
242
	private class VCard extends ContactData
243
	{
244
		private final static int NAMELEVEL_NONE = 0;
245
		private final static int NAMELEVEL_ORG = 1;
246
		private final static int NAMELEVEL_FN = 2;
247
		private final static int NAMELEVEL_N = 3;
248
249
		private String _version = null;
250
		private Vector< String > _lines = null;
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
251
		private int _name_level = NAMELEVEL_NONE;
252
		private boolean _parser_in_multiline = false;
253
		private String _parser_current_name_and_params = null;
254
		private String _parser_buffered_value_so_far = "";
255
256
		protected class UnencodeResult
257
		{
258
			private boolean _another_line_required;
259
			private byte[] _bytes;
260
			private int _num_bytes;
261
262
			public UnencodeResult( boolean another_line_required, byte[] bytes,
263
				int num_bytes )
264
			{
265
				_another_line_required = another_line_required;
266
				_bytes = bytes;
267
				_num_bytes = num_bytes;
268
			}
269
270
			public boolean isAnotherLineRequired()
271
			{
272
				return _another_line_required;
273
			}
274
275
			public byte[] getBytes()
276
			{
277
				return _bytes;
278
			}
279
280
			public int getNumBytes()
281
			{
282
				return _num_bytes;
283
			}
284
		}
1 by edam
Initial import
285
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
286
		@SuppressWarnings("serial")
1 by edam
Initial import
287
		protected class ParseException extends Exception
288
		{
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
289
			@SuppressWarnings("unused")
1 by edam
Initial import
290
			public ParseException( String error )
291
			{
292
				super( error );
293
			}
294
295
			public ParseException( int res )
296
			{
297
				super( VCFImporter.this.getText( res ).toString() );
298
			}
299
		}
300
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
301
		@SuppressWarnings("serial")
1 by edam
Initial import
302
		protected class SkipContactException extends Exception { }
303
304
		public void parseLine( String line )
305
				throws ParseException, SkipContactException,
306
				AbortImportException
307
		{
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
308
			// ignore empty lines
309
			if( line.trim() == "" ) return;
310
311
			// split line into name and value parts (this may turn out to be
312
			// unwanted if the line is a subsequent line in a multi-line
313
			// value, but we have to do this now to check for and handle VCF
314
			// versions first)
315
			String[] props = line.split(  ":", 2 );
1 by edam
Initial import
316
			for( int i = 0; i < props.length; i++ )
317
				props[ i ] = props[ i ].trim();
318
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
319
			// if we haven't yet got a version, we won't be paring anything!
1 by edam
Initial import
320
			if( _version == null )
321
			{
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
322
				// is this a version?
323
				if( props.length == 2 && props[ 0 ].equals( "VERSION" ) )
1 by edam
Initial import
324
				{
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
325
					// yes, check/store it
1 by edam
Initial import
326
					if( !props[ 1 ].equals( "2.1" ) &&
327
							!props[ 1 ].equals( "3.0" ) )
328
						throw new ParseException( R.string.error_vcf_version );
329
					_version = props[ 1 ];
330
331
					// parse any other lines we've accumulated so far
332
					if( _lines != null )
333
						for( int i = 0; i < _lines.size(); i++ )
334
							parseLine( _lines.get( i ) );
335
					_lines = null;
336
				}
337
				else
338
				{
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
339
					// no, so stash this line till we have a version
1 by edam
Initial import
340
					if( _lines == null )
341
						_lines = new Vector< String >();
342
					_lines.add( line );
343
				}
344
			}
345
			else
346
			{
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
347
				if( _parser_in_multiline )
348
				{
349
					// if we're currently in a multi-line value, use the stored
350
					// property name and parameters
351
					props = new String[ 2 ];
352
					props[ 0 ] = _parser_current_name_and_params;
353
					props[ 1 ] = line.trim();
354
				}
355
				else
356
				{
357
					// for normal lines, check the property name/value bits
358
					if( props.length < 2 || props[ 0 ].length() == 0 )
359
						throw new ParseException(
360
							R.string.error_vcf_malformed );
361
362
					// ignore empty properties
363
					if( props[ 1 ].length() < 1 )
364
						return;
365
366
					// reset the saved multi-line state
367
					_parser_current_name_and_params = props[ 0 ];
368
					_parser_buffered_value_so_far = "";
369
				}
370
1 by edam
Initial import
371
				// get parameter parts
372
				String[] params = props[ 0 ].split( ";" );
373
				for( int i = 0; i < params.length; i++ )
374
					params[ i ] = params[ i ].trim();
375
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
376
				// parse charset and encoding parameters
377
				String charset, encoding;
378
				if( ( charset = checkParam( params, "CHARSET" ) ) != null &&
379
					!charset.equals( "UTF-8" ) && !charset.equals( "UTF-16" ) )
380
				{
381
					throw new ParseException( R.string.error_vcf_charset );
382
				}
383
				if( ( encoding = checkParam( params, "ENCODING" ) ) != null &&
384
					!encoding.equals( "QUOTED-PRINTABLE" ) &&
385
					!encoding.equals( "8BIT" ) )
386
					//&& !encoding.equals( "BASE64" ) )
387
				{
388
					throw new ParseException( R.string.error_vcf_encoding );
389
				}
390
391
				// do unencoding (or default to a fake unencoding result with
392
				// the raw string)
393
				UnencodeResult result;
394
				if( encoding != null && encoding.equals( "QUOTED-PRINTABLE" ) )
395
					result = unencodeQuotedPrintable( props[ 1 ], charset );
396
//				else if( encoding != null && encoding.equals( "BASE64" ) )
397
//					result = unencodeBase64( props[ 1 ], charset );
398
				else
399
					result = new UnencodeResult( false, props[ 1 ].getBytes(),
400
						props[ 1 ].getBytes().length );
401
402
				// process charset
403
				try {
404
					props[ 1 ] = new String( result.getBytes(), 0,
405
						result.getNumBytes(),
406
						charset == null? "UTF-8" : charset );
407
				} catch( UnsupportedEncodingException e ) {
408
					throw new ParseException( R.string.error_vcf_charset );
409
				}
410
411
				// handle multi-line requests
412
				_parser_in_multiline = result.isAnotherLineRequired();
413
				if( _parser_in_multiline ) {
414
					_parser_buffered_value_so_far += props[ 1 ];
415
					return;
416
				}
417
418
				// add on buffered multi-line content
419
				String value = _parser_buffered_value_so_far + props[ 1 ];
420
1 by edam
Initial import
421
				// parse some properties
422
				if( params[ 0 ].equals( "N" ) )
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
423
					parseN( params, value );
1 by edam
Initial import
424
				else if( params[ 0 ].equals( "FN" ) )
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
425
					parseFN( params, value );
1 by edam
Initial import
426
				else if( params[ 0 ].equals( "ORG" ) )
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
427
					parseORG( params, value );
1 by edam
Initial import
428
				else if( params[ 0 ].equals( "TEL" ) )
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
429
					parseTEL( params, value );
1 by edam
Initial import
430
				else if( params[ 0 ].equals( "EMAIL" ) )
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
431
					parseEMAIL( params, value );
1 by edam
Initial import
432
			}
433
		}
434
435
		private void parseN( String[] params, String value )
436
				throws ParseException, SkipContactException,
437
				AbortImportException
438
		{
439
			// already got a better name?
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
440
			if( _name_level >= NAMELEVEL_N ) return;
1 by edam
Initial import
441
442
			// get name parts
443
			String[] nameparts = value.split( ";" );
444
			for( int i = 0; i < nameparts.length; i++ )
445
				nameparts[ i ] = nameparts[ i ].trim();
446
447
			// build name
448
			value = "";
449
			if( nameparts.length > 1 && nameparts[ 1 ].length() > 0 )
450
				value += nameparts[ 1 ];
451
			if( nameparts[ 0 ].length() > 0 )
452
				value += ( value.length() == 0? "" : " " ) + nameparts[ 0 ];
453
454
			// set name
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
455
			setName( value );
456
			_name_level = NAMELEVEL_N;
1 by edam
Initial import
457
458
			// check now to see if we need to import this contact (to avoid
459
			// parsing the rest of the vCard unnecessarily)
460
			if( !isImportRequired( getName() ) )
461
				throw new SkipContactException();
462
		}
463
464
		private void parseFN( String[] params, String value )
465
				throws ParseException, SkipContactException
466
		{
467
			// already got a better name?
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
468
			if( _name_level >= NAMELEVEL_FN ) return;
1 by edam
Initial import
469
470
			// set name
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
471
			setName( value );
472
			_name_level = NAMELEVEL_FN;
1 by edam
Initial import
473
		}
474
475
		private void parseORG( String[] params, String value )
476
				throws ParseException, SkipContactException
477
		{
478
			// already got a better name?
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
479
			if( _name_level >= NAMELEVEL_ORG ) return;
1 by edam
Initial import
480
481
			// get org parts
482
			String[] orgparts = value.split( ";" );
483
			for( int i = 0; i < orgparts.length; i++ )
484
				orgparts[ i ] = orgparts[ i ].trim();
485
486
			// build name
487
			if( orgparts[ 0 ].length() == 0 && orgparts.length > 1 )
488
				value = orgparts[ 1 ];
489
			else
490
				value = orgparts[ 0 ];
491
492
			// set name
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
493
			setName( value );
494
			_name_level = NAMELEVEL_ORG;
1 by edam
Initial import
495
		}
496
497
		private void parseTEL( String[] params, String value )
498
				throws ParseException
499
		{
500
			if( value.length() == 0 ) return;
501
502
			Set< String > types = extractTypes( params, Arrays.asList(
503
					"PREF", "HOME", "WORK", "VOICE", "FAX", "MSG", "CELL",
504
					"PAGER", "BBS", "MODEM", "CAR", "ISDN", "VIDEO" ) );
505
506
			// here's the logic...
507
			boolean preferred = types.contains( "PREF" );
508
			if( types.contains( "VOICE" ) )
509
				if( types.contains( "WORK" ) )
510
					addPhone( value, PhonesColumns.TYPE_WORK, preferred );
511
				else
512
					addPhone( value, PhonesColumns.TYPE_HOME, preferred );
513
			else if( types.contains( "CELL" ) || types.contains( "VIDEO" ) )
514
				addPhone( value, PhonesColumns.TYPE_MOBILE, preferred );
515
			if( types.contains( "FAX" ) )
516
				if( types.contains( "HOME" ) )
517
					addPhone( value, PhonesColumns.TYPE_FAX_HOME, preferred );
518
				else
519
					addPhone( value, PhonesColumns.TYPE_FAX_WORK, preferred );
520
			if( types.contains( "PAGER" ) )
521
				addPhone( value, PhonesColumns.TYPE_PAGER, preferred );
522
		}
523
524
		public void parseEMAIL( String[] params, String value )
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
525
				throws ParseException
1 by edam
Initial import
526
		{
527
			if( value.length() == 0 ) return;
528
529
			Set< String > types = extractTypes( params, Arrays.asList(
530
					"PREF", "WORK", "HOME", "INTERNET" ) );
531
532
			// here's the logic...
533
			boolean preferred = types.contains( "PREF" );
534
			if( types.contains( "WORK" ) )
535
				addEmail( value, Contacts.ContactMethods.TYPE_WORK, preferred );
536
			else
537
				addEmail( value, Contacts.ContactMethods.TYPE_HOME, preferred );
538
		}
539
540
		public void finaliseParsing()
541
				throws ParseException, SkipContactException,
542
				AbortImportException
543
		{
544
			// missing version (and data is present)
545
			if( _version == null && _lines != null )
546
				throw new ParseException( R.string.error_vcf_malformed );
547
548
			//  missing name properties?
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
549
			if( _name_level == NAMELEVEL_NONE )
1 by edam
Initial import
550
				throw new ParseException( R.string.error_vcf_noname );
551
552
			// check if we should import this one? If we've already got an 'N'-
553
			// type name, this will already have been done by parseN() so we
554
			// mustn't do this here (or it could prompt twice!)
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
555
			if( _name_level < NAMELEVEL_N && !isImportRequired( getName() ) )
1 by edam
Initial import
556
				throw new SkipContactException();
557
		}
558
559
		private String checkParam( String[] params, String name )
560
		{
561
			Pattern p = Pattern.compile( "^" + name + "[ \\t]*=[ \\t]*(.*)$" );
562
			for( int i = 0; i < params.length; i++ ) {
563
				Matcher m = p.matcher( params[ i ] );
564
				if( m.matches() )
565
					return m.group( 1 );
566
			}
567
			return null;
568
		}
569
570
		private Set< String > extractTypes( String[] params,
571
				List< String > validTypes )
572
		{
573
			HashSet< String > types = new HashSet< String >();
574
575
			// get 3.0-style TYPE= param
576
			String typeParam;
577
			if( ( typeParam = checkParam( params, "TYPE" ) ) != null ) {
578
				String[] bits = typeParam.split( "," );
579
				for( int i = 0; i < bits.length; i++ )
580
					if( validTypes.contains( bits[ i ] ) )
581
						types.add( bits[ i ] );
582
			}
583
584
			// get 2.1-style type param
585
			if( _version.equals( "2.1" ) ) {
586
				for( int i = 1; i < params.length; i++ )
587
					if( validTypes.contains( params[ i ] ) )
588
						types.add( params[ i ] );
589
			}
590
591
			return types;
592
		}
593
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
594
		private UnencodeResult unencodeQuotedPrintable( String str, String charset )
1 by edam
Initial import
595
		{
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
596
			boolean another = false;
597
1 by edam
Initial import
598
			// default encoding scheme
599
			if( charset == null ) charset = "UTF-8";
600
601
			// unencode quoted-pritable encoding, as per RFC1521 section 5.1
602
			byte[] bytes = new byte[ str.length() ];
603
			int j = 0;
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
604
			for( int i = 0; i < str.length(); i++ )
605
			{
606
				// get next char and process...
1 by edam
Initial import
607
				char ch = str.charAt( i );
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
608
				if( ch == '=' && i < str.length() - 2 )
609
				{
610
					// we found a =XX format byte, add it
1 by edam
Initial import
611
					bytes[ j ] = (byte)(
612
							Character.digit( str.charAt( i + 1 ), 16 ) * 16 +
613
							Character.digit( str.charAt( i + 2 ), 16 ) );
614
					i += 2;
615
				}
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
616
				else if( ch == '=' && i == str.length() - 1 )
617
				{
618
					// we found a '=' at the end of a line signifying a multi-
619
					// line string, so we don't add it.
620
					another = true;
621
					continue;
622
				}
1 by edam
Initial import
623
				else
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
624
					// just a normal char...
1 by edam
Initial import
625
					bytes[ j ] = (byte)ch;
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
626
				j++;
1 by edam
Initial import
627
			}
18 by edam
- changed case on charset and encoding warning strings (it looked bad)
628
629
			return new UnencodeResult( another, bytes, j );
1 by edam
Initial import
630
		}
631
	}
632
}