1 /* (c) 2001-2005 by Marcin Wiacek, Michal Cihar... */
2 
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 
8 #include <gammu-misc.h>
9 #include <gammu-bitmap.h>
10 
11 #include "../misc/coding/coding.h"
12 #include "../debug.h"
13 #include "gsmpbk.h"
14 #include "gsmmisc.h"
15 
GSM_StringToMemoryType(const char * s)16 GSM_MemoryType GSM_StringToMemoryType(const char *s) {
17     if (strcmp(s, "ME") == 0)      return MEM_ME;
18     else if (strcmp(s, "SM") == 0) return MEM_SM;
19 		else if (strcmp(s, "SR") == 0) return MEM_SR;
20     else if (strcmp(s, "ON") == 0) return MEM_ON;
21     else if (strcmp(s, "DC") == 0) return MEM_DC;
22     else if (strcmp(s, "RC") == 0) return MEM_RC;
23     else if (strcmp(s, "MC") == 0) return MEM_MC;
24     else if (strcmp(s, "MT") == 0) return MEM_MT;
25     else if (strcmp(s, "FD") == 0) return MEM_FD;
26     else if (strcmp(s, "VM") == 0) return MEM_VM;
27     else if (strcmp(s, "SL") == 0) return MEM_SL;
28     else if (strcmp(s, "QD") == 0) return MEM_QD;
29     else {
30 		return 0;
31     }
32 }
33 
GSM_PhonebookGetEntryName(const GSM_MemoryEntry * entry)34 unsigned char *GSM_PhonebookGetEntryName (const GSM_MemoryEntry *entry)
35 {
36 	/* We possibly store here "LastName, FirstName" so allocate enough memory */
37 	static char     dest[(GSM_PHONEBOOK_TEXT_LENGTH*2+2+1)*2];
38 	static char     split[] = { '\0', ',', '\0', ' ', '\0', '\0'};
39 	int	     i;
40 	int	     first = -1, last = -1, name = -1;
41 	int	     len = 0;
42 
43 	for (i = 0; i < entry->EntriesNum; i++) {
44 		switch (entry->Entries[i].EntryType) {
45 			case PBK_Text_LastName:
46 				last = i;
47 				break;
48 			case PBK_Text_FirstName:
49 				first = i;
50 				break;
51 			case PBK_Text_Name:
52 				name = i;
53 				break;
54 			default:
55 				break;
56 		}
57 	}
58 
59 	if (name != -1) {
60 		CopyUnicodeString(dest, entry->Entries[name].Text);
61 	} else {
62 		if (last != -1 && first != -1) {
63 			len = UnicodeLength(entry->Entries[last].Text);
64 			CopyUnicodeString(dest, entry->Entries[last].Text);
65 			CopyUnicodeString(dest + 2*len, split);
66 			CopyUnicodeString(dest + 2*len + 4, entry->Entries[first].Text);
67 		} else if (last != -1) {
68 			CopyUnicodeString(dest, entry->Entries[last].Text);
69 		} else if (first != -1) {
70 			CopyUnicodeString(dest, entry->Entries[first].Text);
71 		} else {
72 			return NULL;
73 		}
74 	}
75 
76 	return dest;
77 }
78 
GSM_PhonebookFindDefaultNameNumberGroup(const GSM_MemoryEntry * entry,int * Name,int * Number,int * Group)79 void GSM_PhonebookFindDefaultNameNumberGroup(const GSM_MemoryEntry *entry, int *Name, int *Number, int *Group)
80 {
81 	int i;
82 
83 	*Name   = -1;
84 	*Number = -1;
85 	*Group  = -1;
86 	for (i = 0; i < entry->EntriesNum; i++) {
87 		switch (entry->Entries[i].EntryType) {
88 		case PBK_Number_General : if (*Number   == -1) *Number  = i; break;
89 		case PBK_Text_Name      : if (*Name     == -1) *Name    = i; break;
90 		case PBK_Caller_Group   : if (*Group    == -1) *Group   = i; break;
91 		default		 :				    break;
92 		}
93 	}
94 	if ((*Number) == -1) {
95 		for (i = 0; i < entry->EntriesNum; i++) {
96 			switch (entry->Entries[i].EntryType) {
97 				case PBK_Number_Mobile:
98 				case PBK_Number_General:
99 				case PBK_Number_Fax:
100 				case PBK_Number_Pager:
101 				case PBK_Number_Other:
102 					*Number = i;
103 					break;
104 				default:
105 					break;
106 			}
107 			if (*Number != -1) break;
108 		}
109 	}
110 	if ((*Name) == -1) {
111 		for (i = 0; i < entry->EntriesNum; i++) {
112 			if (entry->Entries[i].EntryType != PBK_Text_LastName) continue;
113 			*Name = i;
114 			break;
115 		}
116 	}
117 	if ((*Name) == -1) {
118 		for (i = 0; i < entry->EntriesNum; i++) {
119 			if (entry->Entries[i].EntryType != PBK_Text_FirstName) continue;
120 			*Name = i;
121 			break;
122 		}
123 	}
124 }
125 
126 
GSM_EncodeVCARD(GSM_Debug_Info * di,char * Buffer,const size_t buff_len,size_t * Length,GSM_MemoryEntry * pbk,const gboolean header,const GSM_VCardVersion Version)127 GSM_Error GSM_EncodeVCARD(GSM_Debug_Info *di, char *Buffer, const size_t buff_len, size_t *Length, GSM_MemoryEntry *pbk, const gboolean header, const GSM_VCardVersion Version)
128 {
129 	int Name, Number, Group, i;
130 	int firstname = -1, secondname = -1, lastname = -1;
131 	int address = -1, city = -1, state = -1, zip = -1, country = -1;
132 	int workaddress = -1, workcity = -1, workstate = -1, workzip = -1, workcountry = -1;
133 	unsigned char buffer[1024];
134 	int pos;
135 	gboolean ignore;
136 	GSM_Error error;
137 	GSM_BinaryPicture *bitmap;
138 
139 	GSM_PhonebookFindDefaultNameNumberGroup(pbk, &Name, &Number, &Group);
140 
141 	if (header) {
142 		error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VCARD");
143 		if (error != ERR_NONE) return error;
144 	}
145 	if (Version == Nokia_VCard10 || Version == SonyEricsson_VCard10) {
146 		if (Name != -1) {
147 			error = VC_StoreLine(Buffer, buff_len, Length, "N:%s",
148 				DecodeUnicodeString(pbk->Entries[Name].Text));
149 			if (error != ERR_NONE) return error;
150 		}
151 		if (Number != -1) {
152 			error = VC_StoreLine(Buffer, buff_len, Length, "TEL:%s",
153 				DecodeUnicodeString(pbk->Entries[Number].Text));
154 			if (error != ERR_NONE) return error;
155 		}
156 	} else if (Version == Nokia_VCard21 || Version == SonyEricsson_VCard21_Phone || Version == SonyEricsson_VCard21) {
157 		if (header) {
158 			error = VC_StoreLine(Buffer, buff_len, Length, "VERSION:2.1");
159 			if (error != ERR_NONE) return error;
160 		}
161 
162 		if (Version != SonyEricsson_VCard21_Phone) {
163 			error = VC_StoreLine(Buffer, buff_len, Length, "X-GAMMU-LOCATION:%d", pbk->Location);
164 			if (error != ERR_NONE) return error;
165 
166 			error = VC_StoreLine(Buffer, buff_len, Length, "X-GAMMU-MEMORY:%s", GSM_MemoryTypeToString(pbk->MemoryType));
167 			if (error != ERR_NONE) return error;
168 		}
169 
170 		for (i=0; i < pbk->EntriesNum; i++) {
171 			ignore = FALSE;
172 			pbk->Entries[i].AddError = ERR_NONE;
173 			switch(pbk->Entries[i].EntryType) {
174 				case PBK_Text_Name:
175 					error = VC_Store(Buffer, buff_len, Length, "N");
176 					if (error != ERR_NONE) return error;
177 					break;
178 				case PBK_Text_NickName:
179 					error = VC_Store(Buffer, buff_len, Length, "NICKNAME");
180 					if (error != ERR_NONE) return error;
181 					break;
182 				case PBK_Text_FormalName:
183 					error = VC_Store(Buffer, buff_len, Length, "FN");
184 					if (error != ERR_NONE) return error;
185 					break;
186 				case PBK_Text_NamePrefix:
187 					error = VC_Store(Buffer, buff_len, Length, "X-NAME-PREFIX");
188 					if (error != ERR_NONE) return error;
189 					break;
190 				case PBK_Text_NameSuffix:
191 					error = VC_Store(Buffer, buff_len, Length, "X-NAME-SUFFIX");
192 					if (error != ERR_NONE) return error;
193 					break;
194 				case PBK_Text_FirstName:
195 					firstname = i;
196 					ignore = TRUE;
197 					break;
198 				case PBK_Text_SecondName:
199 					secondname = i;
200 					ignore = TRUE;
201 					break;
202 				case PBK_Text_LastName:
203 					lastname = i;
204 					ignore = TRUE;
205 					break;
206 				case PBK_Text_StreetAddress:
207 					if (pbk->Entries[i].Location == PBK_Location_Work) {
208 						workaddress = i;
209 					} else {
210 						address = i;
211 					}
212 					ignore = TRUE;
213 					break;
214 				case PBK_Text_City:
215 					if (pbk->Entries[i].Location == PBK_Location_Work) {
216 						workcity = i;
217 					} else {
218 						city = i;
219 					}
220 					ignore = TRUE;
221 					break;
222 				case PBK_Text_State:
223 					if (pbk->Entries[i].Location == PBK_Location_Work) {
224 						workstate = i;
225 					} else {
226 						state = i;
227 					}
228 					ignore = TRUE;
229 					break;
230 				case PBK_Text_Zip:
231 					if (pbk->Entries[i].Location == PBK_Location_Work) {
232 						workzip = i;
233 					} else {
234 						zip = i;
235 					}
236 					ignore = TRUE;
237 					break;
238 				case PBK_Text_Country:
239 					if (pbk->Entries[i].Location == PBK_Location_Work) {
240 						workcountry = i;
241 					} else {
242 						country = i;
243 					}
244 					ignore = TRUE;
245 					break;
246 				case PBK_Date:
247 					error = VC_StoreDate(Buffer, buff_len, Length, &(pbk->Entries[i].Date), "BDAY");
248 					if (error != ERR_NONE) return error;
249 					ignore = TRUE;
250 					break;
251 				case PBK_LastModified:
252 					error = VC_StoreDateTime(Buffer, buff_len, Length, &(pbk->Entries[i].Date), "LAST-MODIFIED");
253 					if (error != ERR_NONE) return error;
254 					ignore = TRUE;
255 					break;
256 				case PBK_Number_General:
257 				case PBK_Number_Video:
258 				case PBK_Number_Other:
259 				case PBK_Number_Pager:
260 				case PBK_Number_Mobile  :
261 				case PBK_Number_Fax     :
262 				case PBK_Number_Messaging    :
263 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
264 						ignore = TRUE;
265 						break;
266 					}
267 					error = VC_Store(Buffer, buff_len, Length, "TEL");
268 					if (error != ERR_NONE) return error;
269 
270 					if (Version != SonyEricsson_VCard21 && Number == i) {
271 						error = VC_Store(Buffer, buff_len, Length, ";PREF");
272 						if (error != ERR_NONE) return error;
273 					}
274 					switch (pbk->Entries[i].Location) {
275 						case PBK_Location_Home:
276 							error = VC_Store(Buffer, buff_len, Length, ";HOME");
277 							if (error != ERR_NONE) return error;
278 							break;
279 						case PBK_Location_Work:
280 							error = VC_Store(Buffer, buff_len, Length, ";WORK");
281 							if (error != ERR_NONE) return error;
282 							break;
283 						case PBK_Location_Unknown:
284 							break;
285 					}
286 					switch (pbk->Entries[i].EntryType) {
287 						case PBK_Number_Other:
288 							error = VC_Store(Buffer, buff_len, Length, ";OTHER");
289 							if (error != ERR_NONE) return error;
290 							break;
291 						case PBK_Number_Pager:
292 							error = VC_Store(Buffer, buff_len, Length, ";PAGER");
293 							if (error != ERR_NONE) return error;
294 							break;
295 						case PBK_Number_Mobile:
296 							error = VC_Store(Buffer, buff_len, Length, ";CELL");
297 							if (error != ERR_NONE) return error;
298 							break;
299 						case PBK_Number_Fax:
300 							error = VC_Store(Buffer, buff_len, Length, ";FAX");
301 							if (error != ERR_NONE) return error;
302 							break;
303 						case PBK_Number_Messaging:
304 							error = VC_Store(Buffer, buff_len, Length, ";MSG");
305 							if (error != ERR_NONE) return error;
306 							break;
307 						case PBK_Number_Video:
308 							error = VC_Store(Buffer, buff_len, Length, ";VIDEO");
309 							if (error != ERR_NONE) return error;
310 							break;
311 						default:
312 							break;
313 					}
314 					break;
315 				case PBK_Text_Note      :
316 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
317 						ignore = TRUE;
318 						break;
319 					}
320 					error = VC_Store(Buffer, buff_len, Length, "NOTE");
321 					if (error != ERR_NONE) return error;
322 					break;
323 				case PBK_Text_Postal    :
324 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
325 						ignore = TRUE;
326 						break;
327 					}
328 					/* Don't ask why. Nokia phones save postal address
329 					 * double - once like LABEL, second like ADR
330 					 */
331 					error = VC_StoreText(Buffer, buff_len, Length, pbk->Entries[i].Text, "LABEL", FALSE);
332 					if (error != ERR_NONE) return error;
333 					if (pbk->Entries[i].Location == PBK_Location_Work) {
334 						error = VC_Store(Buffer, buff_len, Length, "ADR;WORK");
335 					} else if (pbk->Entries[i].Location == PBK_Location_Home) {
336 						error = VC_Store(Buffer, buff_len, Length, "ADR;HOME");
337 					} else {
338 						error = VC_Store(Buffer, buff_len, Length, "ADR");
339 					}
340 					if (error != ERR_NONE) return error;
341 					break;
342 				case PBK_Text_Email     :
343 				case PBK_Text_Email2    :
344 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
345 						ignore = TRUE;
346 						break;
347 					}
348 					error = VC_Store(Buffer, buff_len, Length, "EMAIL");
349 					if (error != ERR_NONE) return error;
350 					break;
351 				case PBK_Text_URL       :
352 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
353 						ignore = TRUE;
354 						break;
355 					}
356 					error = VC_Store(Buffer, buff_len, Length, "URL");
357 					if (error != ERR_NONE) return error;
358 					break;
359 				case PBK_Text_LUID      :
360 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
361 						ignore = TRUE;
362 						break;
363 					}
364 					error = VC_Store(Buffer, buff_len, Length, "X-IRMC-LUID");
365 					if (error != ERR_NONE) return error;
366 					break;
367 				case PBK_Text_VOIP      :
368 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
369 						ignore = TRUE;
370 						break;
371 					}
372 					error = VC_Store(Buffer, buff_len, Length, "X-SIP;VOIP");
373 					if (error != ERR_NONE) return error;
374 					break;
375 				case PBK_Text_SWIS      :
376 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
377 						ignore = TRUE;
378 						break;
379 					}
380 					error = VC_Store(Buffer, buff_len, Length, "X-SIP;SWIS");
381 					if (error != ERR_NONE) return error;
382 					break;
383 				case PBK_Text_WVID      :
384 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
385 						ignore = TRUE;
386 						break;
387 					}
388 					error = VC_Store(Buffer, buff_len, Length, "X-WV-ID");
389 					if (error != ERR_NONE) return error;
390 					break;
391 				case PBK_Text_SIP      :
392 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
393 						ignore = TRUE;
394 						break;
395 					}
396 					error = VC_Store(Buffer, buff_len, Length, "X-SIP");
397 					if (error != ERR_NONE) return error;
398 					break;
399 				case PBK_Text_DTMF      :
400 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
401 						ignore = TRUE;
402 						break;
403 					}
404 					error = VC_Store(Buffer, buff_len, Length, "X-DTMF");
405 					if (error != ERR_NONE) return error;
406 					break;
407 				case PBK_PushToTalkID:
408 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
409 						ignore = TRUE;
410 						break;
411 					}
412 					error = VC_Store(Buffer, buff_len, Length, "X-SIP;POC");
413 					if (error != ERR_NONE) return error;
414 					break;
415 				case PBK_Text_JobTitle:
416 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
417 						ignore = TRUE;
418 						break;
419 					}
420 					error = VC_Store(Buffer, buff_len, Length, "TITLE");
421 					if (error != ERR_NONE) return error;
422 					break;
423 				case PBK_Text_Company:
424 					if (UnicodeLength(pbk->Entries[i].Text) == 0) {
425 						ignore = TRUE;
426 						break;
427 					}
428 					error = VC_Store(Buffer, buff_len, Length, "ORG");
429 					if (error != ERR_NONE) return error;
430 					break;
431 				case PBK_Photo:
432 					bitmap = &(pbk->Entries[i].Picture);
433 					error = VC_Store(Buffer, buff_len, Length, "PHOTO;TYPE=");
434 					if (error != ERR_NONE) return error;
435 					switch (bitmap->Type) {
436 						case PICTURE_BMP:
437 							error = VC_Store(Buffer, buff_len, Length, "BMP;");
438 							break;
439 						case PICTURE_GIF:
440 							error = VC_Store(Buffer, buff_len, Length, "GIF;");
441 							break;
442 						case PICTURE_JPG:
443 							error = VC_Store(Buffer, buff_len, Length, "JPEG;");
444 							break;
445 						case PICTURE_ICN:
446 							error = VC_Store(Buffer, buff_len, Length, "ICO;");
447 							break;
448 						case PICTURE_PNG:
449 							error = VC_Store(Buffer, buff_len, Length, "PNG;");
450 							break;
451 						default:
452 							smfprintf(di, "Unknown picture format: %d\n", bitmap->Type);
453 							error = VC_Store(Buffer, buff_len, Length, "UNKNOWN;");
454 							break;
455 					}
456 					if (error != ERR_NONE) return error;
457 					error = VC_Store(Buffer, buff_len, Length, "ENCODING=BASE64:");
458 					if (error != ERR_NONE) return error;
459 					error = VC_StoreBase64(Buffer, buff_len, Length, bitmap->Buffer, bitmap->Length);
460 					if (error != ERR_NONE) return error;
461 					ignore = TRUE;
462 					break;
463 				case PBK_Caller_Group:
464 					error = VC_StoreLine(Buffer, buff_len, Length, "X-CALLER-GROUP:%d", pbk->Entries[i].Number);
465 					if (error != ERR_NONE) return error;
466 					ignore = TRUE;
467 					break;
468 				case PBK_Private:
469 					error = VC_StoreLine(Buffer, buff_len, Length, "X-PRIVATE:%d", pbk->Entries[i].Number);
470 					if (error != ERR_NONE) return error;
471 					ignore = TRUE;
472 					break;
473 				/* Not supported fields */
474 				case PBK_Category:
475 				case PBK_RingtoneID:
476 				case PBK_PictureID:
477 				case PBK_Text_UserID:
478 				case PBK_CallLength:
479 				case PBK_Text_Custom1:
480 				case PBK_Text_Custom2:
481 				case PBK_Text_Custom3:
482 				case PBK_Text_Custom4:
483 				case PBK_Text_PictureName:
484 					pbk->Entries[i].AddError = ERR_NOTSUPPORTED;
485 					ignore = TRUE;
486 					break;
487 			}
488 			if (!ignore) {
489 				error = VC_StoreText(Buffer, buff_len, Length, pbk->Entries[i].Text, "", FALSE);
490 				if (error != ERR_NONE) return error;
491 			}
492 		}
493 		/* Save name if it is composed from parts */
494 		if (firstname != -1 || secondname != -1 || lastname != -1) {
495 			pos = 0;
496 			if (lastname != -1) {
497 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[lastname].Text);
498 				pos += UnicodeLength(pbk->Entries[lastname].Text);
499 			}
500 			buffer[2*pos] = 0;
501 			buffer[2*pos + 1] = ';';
502 			pos++;
503 			if (firstname != -1) {
504 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[firstname].Text);
505 				pos += UnicodeLength(pbk->Entries[firstname].Text);
506 			}
507 			if (secondname != -1) {
508 				buffer[2*pos] = 0;
509 				buffer[2*pos + 1] = ' ';
510 				pos++;
511 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[secondname].Text);
512 				pos += UnicodeLength(pbk->Entries[secondname].Text);
513 			}
514 			buffer[2*pos] = 0;
515 			buffer[2*pos + 1] = 0;
516 			error = VC_StoreText(Buffer, buff_len, Length, buffer, "N", FALSE);
517 			if (error != ERR_NONE) return error;
518 		}
519 		/* Save workaddress if it is composed from parts */
520 		if (workaddress != -1 || workcity != -1 || workstate != -1 || workzip != -1 || workcountry != -1) {
521 			pos = 0;
522 			buffer[2*pos] = 0;
523 			buffer[2*pos + 1] = ';';
524 			pos++;
525 			buffer[2*pos] = 0;
526 			buffer[2*pos + 1] = ';';
527 			pos++;
528 			if (workaddress != -1) {
529 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[workaddress].Text);
530 				pos += UnicodeLength(pbk->Entries[workaddress].Text);
531 			}
532 			buffer[2*pos] = 0;
533 			buffer[2*pos + 1] = ';';
534 			pos++;
535 			if (workcity != -1) {
536 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[workcity].Text);
537 				pos += UnicodeLength(pbk->Entries[workcity].Text);
538 			}
539 			buffer[2*pos] = 0;
540 			buffer[2*pos + 1] = ';';
541 			pos++;
542 			if (workstate != -1) {
543 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[workstate].Text);
544 				pos += UnicodeLength(pbk->Entries[workstate].Text);
545 			}
546 			buffer[2*pos] = 0;
547 			buffer[2*pos + 1] = ';';
548 			pos++;
549 			if (workzip != -1) {
550 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[workzip].Text);
551 				pos += UnicodeLength(pbk->Entries[workzip].Text);
552 			}
553 			buffer[2*pos] = 0;
554 			buffer[2*pos + 1] = ';';
555 			pos++;
556 			if (workcountry != -1) {
557 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[workcountry].Text);
558 				pos += UnicodeLength(pbk->Entries[workcountry].Text);
559 			}
560 			buffer[2*pos] = 0;
561 			buffer[2*pos + 1] = 0;
562 			error = VC_StoreText(Buffer, buff_len, Length, buffer, "ADR;WORK", FALSE);
563 			if (error != ERR_NONE) return error;
564 		}
565 		/* Save address if it is composed from parts */
566 		if (address != -1 || city != -1 || state != -1 || zip != -1 || country != -1) {
567 			pos = 0;
568 			buffer[2*pos] = 0;
569 			buffer[2*pos + 1] = ';';
570 			pos++;
571 			buffer[2*pos] = 0;
572 			buffer[2*pos + 1] = ';';
573 			pos++;
574 			if (address != -1) {
575 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[address].Text);
576 				pos += UnicodeLength(pbk->Entries[address].Text);
577 			}
578 			buffer[2*pos] = 0;
579 			buffer[2*pos + 1] = ';';
580 			pos++;
581 			if (city != -1) {
582 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[city].Text);
583 				pos += UnicodeLength(pbk->Entries[city].Text);
584 			}
585 			buffer[2*pos] = 0;
586 			buffer[2*pos + 1] = ';';
587 			pos++;
588 			if (state != -1) {
589 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[state].Text);
590 				pos += UnicodeLength(pbk->Entries[state].Text);
591 			}
592 			buffer[2*pos] = 0;
593 			buffer[2*pos + 1] = ';';
594 			pos++;
595 			if (zip != -1) {
596 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[zip].Text);
597 				pos += UnicodeLength(pbk->Entries[zip].Text);
598 			}
599 			buffer[2*pos] = 0;
600 			buffer[2*pos + 1] = ';';
601 			pos++;
602 			if (country != -1) {
603 				CopyUnicodeString(buffer + 2*pos, pbk->Entries[country].Text);
604 				pos += UnicodeLength(pbk->Entries[country].Text);
605 			}
606 			buffer[2*pos] = 0;
607 			buffer[2*pos + 1] = 0;
608 			error = VC_StoreText(Buffer, buff_len, Length, buffer, "ADR;HOME", FALSE);
609 			if (error != ERR_NONE) return error;
610 		}
611 	} else {
612 		return ERR_NOTSUPPORTED;
613 	}
614 	if (header) {
615 		error = VC_StoreLine(Buffer, buff_len, Length, "END:VCARD");
616 		if (error != ERR_NONE) return error;
617 	}
618 	return ERR_NONE;
619 }
620 
GSM_TweakInternationalNumber(unsigned char * Number,const GSM_NumberType numType)621 void GSM_TweakInternationalNumber(unsigned char *Number, const GSM_NumberType numType)
622 {
623 	/* Checks if International number needs to be corrected */
624 	char* pos; /* current position in the buffer */
625 	char buf[500]; /* Taken from DecodeUnicodeString(). How to get length of the encoded unicode string? There may be embedded 0s. */
626 
627 	if (numType == NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN || numType + 1 == NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN) {
628 		sprintf(buf+1,"%s",DecodeUnicodeString(Number)); /* leave 1 free char before the number, we'll need it */
629 		/* International number may be without + (e.g. (Sony)Ericsson)
630 			we can add it, but must handle numbers in the form:
631 			         NNNNNN         N - any digit (char)
632 			   *code#NNNNNN         any number of Ns
633 			*[*]code*NNNNNN[...]
634 			other combinations (like *code1*code2*number#)
635 			will have to be added if found in real life
636 			Or does somebody know the exact allowed syntax
637 			from some standard?
638 		*/
639 		pos=buf+1;
640 		if (*pos=='*') { /* Code? Skip it. */
641 			/* probably with code */
642 			while (*pos=='*') { /* skip leading asterisks */
643 				*(pos-1)=*pos; /* shift the chars by one */
644 				pos++;
645 			}
646 			while ((*pos!='*')&&(*pos!='#')) { /* skip code - anything except * or # */
647 				*(pos-1)=*pos;
648 				pos++;
649 			}
650 			*(pos-1)=*pos; /* shift the last delimiter */
651 			pos++;
652 	        }
653 		/* check the guessed location, if + is correctly there */
654 		if (*pos=='+') {
655 			/* yes, just shift the rest of the string */
656 			while (*pos) {
657 				*(pos-1) = *pos;
658 				pos++;
659 			}
660 			*(pos-1)=0; /* kill the last char, which now got doubled */
661 		} else {
662 			/* no, insert + and exit, no more shifting */
663 			*(pos-1)='+';
664 		}
665 		EncodeUnicode(Number,buf,strlen(buf));
666 	}
667 }
668 
669 
670 #define CHECK_NUM_ENTRIES { \
671 	if (Pbk->EntriesNum >= GSM_PHONEBOOK_ENTRIES) { error = ERR_MOREMEMORY; goto vcard_done; } \
672 	Pbk->Entries[Pbk->EntriesNum].AddError = ERR_NONE; \
673 }
674 
675 /**
676  * \bug We should avoid using static buffers here.
677  */
GSM_DecodeVCARD(GSM_Debug_Info * di,char * Buffer,size_t * Pos,GSM_MemoryEntry * Pbk,GSM_VCardVersion Version)678 GSM_Error GSM_DecodeVCARD(GSM_Debug_Info *di, char *Buffer, size_t *Pos, GSM_MemoryEntry *Pbk, GSM_VCardVersion Version)
679 {
680 	char   Buff[20000];
681 	int	     Level = 0;
682 	char   *s;
683 	int		pos;
684 	int		version = 1;
685 	GSM_Error	error;
686 	char	*Line = NULL;
687 	GSM_EntryLocation location;
688 
689 	Buff[0]	 = 0;
690 	Pbk->EntriesNum = 0;
691 	if (Version != SonyEricsson_VCard21_Phone) {
692 		Pbk->Location = 0;
693 		Pbk->MemoryType = MEM_MT;
694 	}
695 
696 	while (1) {
697 		free(Line);
698 		Line = NULL;
699 		error = GSM_GetVCSLine(&Line, Buffer, Pos, strlen(Buffer), TRUE);
700 		if (error != ERR_NONE) goto vcard_done;
701 		if (strlen(Line) == 0) break;
702 		switch (Level) {
703 		case 0:
704 			if (strstr(Line,"BEGIN:VCARD")) Level = 1;
705 			break;
706 		case 1:
707 			CHECK_NUM_ENTRIES;
708 			if (strstr(Line,"END:VCARD")) {
709 				goto vcard_complete;
710 			}
711 			if (strstr(Line, "VERSION:") != NULL) {
712 				version = atoi(Line + 8);
713 				dbgprintf(di, "vCard version %d\n", version);
714 			}
715 			if (ReadVCALText(Line, "N", Buff,  (version >= 3), NULL)) {
716 				pos = 0;
717 				s = VCALGetTextPart(Buff, &pos);
718 				if (s == NULL) {
719 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
720 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Name;
721 					Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
722 					Pbk->EntriesNum++;
723 					continue;
724 				} else {
725 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
726 					/* Skip empty name */
727 					if (UnicodeLength(Pbk->Entries[Pbk->EntriesNum].Text) > 0) {
728 						Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_LastName;
729 						Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
730 						Pbk->EntriesNum++;
731 					}
732 
733 					s = VCALGetTextPart(Buff, &pos);
734 					if (s == NULL) continue;
735 					CHECK_NUM_ENTRIES;
736 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
737 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_FirstName;
738 					Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
739 					Pbk->EntriesNum++;
740 					continue;
741 				}
742 			}
743 			if (strncmp(Line, "PHOTO;JPEG;BASE64:", 18) == 0 ||
744 				strncmp(Line, "PHOTO;BASE64;JPEG:", 18) == 0 ||
745 				strncmp(Line, "PHOTO;TYPE=JPEG;BASE64:", 23) == 0 ||
746 				strncmp(Line, "PHOTO;BASE64;TYPE=JPEG:", 23) == 0 ||
747 				strncmp(Line, "PHOTO;TYPE=JPEG;ENCODING=BASE64:", 32) == 0 ||
748 				strncmp(Line, "PHOTO;ENCODING=BASE64;TYPE=JPEG:", 32) == 0 ||
749 				strncmp(Line, "PHOTO;JPEG;ENCODING=BASE64:", 27) == 0 ||
750 				strncmp(Line, "PHOTO;ENCODING=BASE64;JPEG:", 27) == 0) {
751 				/* Find : (it should be there we matched it above) */
752 				s = strchr(Line, ':');
753 				s++;
754 				/* Skip whitespace */
755 				while (isspace((int)*s) && *s) s++;
756 
757 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Photo;
758 				Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
759 				Pbk->Entries[Pbk->EntriesNum].Picture.Type = PICTURE_JPG;
760 
761 				/* We allocate here more memory than is actually required */
762 				Pbk->Entries[Pbk->EntriesNum].Picture.Buffer = (unsigned char *)malloc(strlen(s));
763 				if (Pbk->Entries[Pbk->EntriesNum].Picture.Buffer == NULL)
764 					return ERR_MOREMEMORY;
765 
766 				Pbk->Entries[Pbk->EntriesNum].Picture.Length =
767 					DecodeBASE64(s, Pbk->Entries[Pbk->EntriesNum].Picture.Buffer, strlen(s));
768 				Pbk->EntriesNum++;
769 				continue;
770 			}
771 
772 			if (ReadVCALText(Line, "TEL",		   Buff,  (version >= 3), &location) ||
773 			    ReadVCALText(Line, "TEL;VOICE",	     Buff,  (version >= 3), &location) ||
774 			    ReadVCALText(Line, "TEL;MAIN",	     Buff,  (version >= 3), &location)) {
775 				if (Buff[1] == '+') {
776 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
777 				}
778 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
779 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_General;
780 				Pbk->Entries[Pbk->EntriesNum].Location = location;
781 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
782 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
783 				Pbk->EntriesNum++;
784 				continue;
785 			}
786 			if (ReadVCALText(Line, "TEL;VIDEO",	     Buff,  (version >= 3), &location)) {
787 				if (Buff[1] == '+') {
788 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
789 				}
790 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
791 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_Video;
792 				Pbk->Entries[Pbk->EntriesNum].Location = location;
793 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
794 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
795 				Pbk->EntriesNum++;
796 				continue;
797 			}
798 			if (ReadVCALText(Line, "TEL;CELL",	      Buff,  (version >= 3), &location) ||
799 			    ReadVCALText(Line, "TEL;CELL;VOICE",	Buff,  (version >= 3), &location)) {
800 				if (Buff[1] == '+') {
801 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
802 				}
803 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
804 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_Mobile;
805 				Pbk->Entries[Pbk->EntriesNum].Location = location;
806 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
807 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
808 				Pbk->EntriesNum++;
809 				continue;
810 			}
811 			if (ReadVCALText(Line, "TEL;OTHER",	      Buff,  (version >= 3), &location) ||
812 			    ReadVCALText(Line, "TEL;OTHER;VOICE",	Buff,  (version >= 3), &location)) {
813 				if (Buff[1] == '+') {
814 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
815 				}
816 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
817 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_Other;
818 				Pbk->Entries[Pbk->EntriesNum].Location = location;
819 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
820 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
821 				Pbk->EntriesNum++;
822 				continue;
823 			}
824 			if (ReadVCALText(Line, "TEL;PAGER",	      Buff,  (version >= 3), &location) ||
825 			    ReadVCALText(Line, "TEL;PAGER;VOICE",	Buff,  (version >= 3), &location)) {
826 				if (Buff[1] == '+') {
827 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
828 				}
829 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
830 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_Other;
831 				Pbk->Entries[Pbk->EntriesNum].Location = location;
832 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
833 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
834 				Pbk->EntriesNum++;
835 				continue;
836 			}
837 			if (ReadVCALText(Line, "TEL;MSG",	      Buff,  (version >= 3), &location) ||
838 			    ReadVCALText(Line, "TEL;MSG;VOICE",	Buff,  (version >= 3), &location)) {
839 				if (Buff[1] == '+') {
840 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
841 				}
842 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
843 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_Other;
844 				Pbk->Entries[Pbk->EntriesNum].Location = location;
845 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
846 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
847 				Pbk->EntriesNum++;
848 				continue;
849 			}
850 			/* FAX + VOICE looks like nonsense */
851 			if (ReadVCALText(Line, "TEL;FAX",	       Buff,  (version >= 3), &location) ||
852 			    ReadVCALText(Line, "TEL;FAX;VOICE",	 Buff,  (version >= 3), &location)) {
853 				if (Buff[1] == '+') {
854 					GSM_TweakInternationalNumber(Buff, NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN);
855 				}
856 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
857 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Number_Fax;
858 				Pbk->Entries[Pbk->EntriesNum].Location = location;
859 				Pbk->Entries[Pbk->EntriesNum].SMSList[0] = 0;
860 				Pbk->Entries[Pbk->EntriesNum].VoiceTag = 0;
861 				Pbk->EntriesNum++;
862 				continue;
863 			}
864 			if (ReadVCALText(Line, "TITLE", Buff,  (version >= 3), &location)) {
865 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
866 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_JobTitle;
867 				Pbk->Entries[Pbk->EntriesNum].Location = location;
868 				Pbk->EntriesNum++;
869 				continue;
870 			}
871 			if (ReadVCALText(Line, "NOTE", Buff,  (version >= 3), &location)) {
872 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
873 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Note;
874 				Pbk->Entries[Pbk->EntriesNum].Location = location;
875 				Pbk->EntriesNum++;
876 				continue;
877 			}
878 			if (ReadVCALText(Line, "LABEL", Buff,  (version >= 3), &location) ||
879 			    ReadVCALText(Line, "ADR", Buff,  (version >= 3), &location)) {
880 				pos = 0;
881 				s = VCALGetTextPart(Buff, &pos); /* PO box, ignore for now */
882 				if (s == NULL) {
883 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
884 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Postal;
885 					Pbk->Entries[Pbk->EntriesNum].Location = location;
886 					Pbk->EntriesNum++;
887 					continue;
888 				} else {
889 					s = VCALGetTextPart(Buff, &pos); /* Don't know ... */
890 
891 					s = VCALGetTextPart(Buff, &pos);
892 					if (s == NULL) continue;
893 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
894 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_StreetAddress;
895 					Pbk->Entries[Pbk->EntriesNum].Location = location;
896 					Pbk->EntriesNum++;
897 					CHECK_NUM_ENTRIES;
898 
899 					s = VCALGetTextPart(Buff, &pos);
900 					if (s == NULL) continue;
901 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
902 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_City;
903 					Pbk->Entries[Pbk->EntriesNum].Location = location;
904 					Pbk->EntriesNum++;
905 					CHECK_NUM_ENTRIES;
906 
907 					s = VCALGetTextPart(Buff, &pos);
908 					if (s == NULL) continue;
909 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
910 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_State;
911 					Pbk->Entries[Pbk->EntriesNum].Location = location;
912 					Pbk->EntriesNum++;
913 					CHECK_NUM_ENTRIES;
914 
915 					s = VCALGetTextPart(Buff, &pos);
916 					if (s == NULL) continue;
917 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
918 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Zip;
919 					Pbk->Entries[Pbk->EntriesNum].Location = location;
920 					Pbk->EntriesNum++;
921 					CHECK_NUM_ENTRIES;
922 
923 					s = VCALGetTextPart(Buff, &pos);
924 					if (s == NULL) continue;
925 					CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text, s);
926 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Country;
927 					Pbk->Entries[Pbk->EntriesNum].Location = location;
928 					Pbk->EntriesNum++;
929 					continue;
930 				}
931 			}
932 			if (ReadVCALText(Line, "EMAIL", Buff,  (version >= 3), &location) ||
933 			    ReadVCALText(Line, "EMAIL;OTHER", Buff,  (version >= 3), &location) ||
934 			    ReadVCALText(Line, "EMAIL;INTERNET", Buff,  (version >= 3), &location)) {
935 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
936 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Email;
937 				Pbk->Entries[Pbk->EntriesNum].Location = location;
938 				Pbk->EntriesNum++;
939 				continue;
940 			}
941 			if (ReadVCALText(Line, "X-IRMC-LUID", Buff,  (version >= 3), &location)) {
942 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
943 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_LUID;
944 				Pbk->Entries[Pbk->EntriesNum].Location = location;
945 				Pbk->EntriesNum++;
946 				continue;
947 			}
948 			if (ReadVCALText(Line, "X-DTMF", Buff,  (version >= 3), &location)) {
949 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
950 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_DTMF;
951 				Pbk->Entries[Pbk->EntriesNum].Location = location;
952 				Pbk->EntriesNum++;
953 				continue;
954 			}
955 			if (ReadVCALText(Line, "X-SIP", Buff,  (version >= 3), &location)) {
956 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
957 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_SIP;
958 				Pbk->Entries[Pbk->EntriesNum].Location = location;
959 				Pbk->EntriesNum++;
960 				continue;
961 			}
962 			if (ReadVCALText(Line, "X-SIP;VOIP", Buff,  (version >= 3), &location)) {
963 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
964 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_VOIP;
965 				Pbk->Entries[Pbk->EntriesNum].Location = location;
966 				Pbk->EntriesNum++;
967 				continue;
968 			}
969 			if (ReadVCALText(Line, "X-WV-ID", Buff,  (version >= 3), &location)) {
970 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
971 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_WVID;
972 				Pbk->Entries[Pbk->EntriesNum].Location = location;
973 				Pbk->EntriesNum++;
974 				continue;
975 			}
976 			if (ReadVCALText(Line, "X-SIP;SWIS", Buff,  (version >= 3), &location)) {
977 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
978 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_SWIS;
979 				Pbk->Entries[Pbk->EntriesNum].Location = location;
980 				Pbk->EntriesNum++;
981 				continue;
982 			}
983 			if (ReadVCALText(Line, "X-SIP;POC", Buff,  (version >= 3), &location)) {
984 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
985 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_PushToTalkID;
986 				Pbk->Entries[Pbk->EntriesNum].Location = location;
987 				Pbk->EntriesNum++;
988 				continue;
989 			}
990 			if (ReadVCALText(Line, "URL", Buff,  (version >= 3), &location)) {
991 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
992 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_URL;
993 				Pbk->Entries[Pbk->EntriesNum].Location = location;
994 				Pbk->EntriesNum++;
995 				continue;
996 			}
997 			if (ReadVCALText(Line, "ORG", Buff,  (version >= 3), &location)) {
998 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
999 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_Company;
1000 				Pbk->Entries[Pbk->EntriesNum].Location = location;
1001 				Pbk->EntriesNum++;
1002 				continue;
1003 			}
1004 			if (ReadVCALText(Line, "NICKNAME", Buff,  (version >= 3), &location)) {
1005 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
1006 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_NickName;
1007 				Pbk->Entries[Pbk->EntriesNum].Location = location;
1008 				Pbk->EntriesNum++;
1009 				continue;
1010 			}
1011 			if (ReadVCALText(Line, "FN", Buff,  (version >= 3), &location)) {
1012 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
1013 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_FormalName;
1014 				Pbk->Entries[Pbk->EntriesNum].Location = location;
1015 				Pbk->EntriesNum++;
1016 				continue;
1017 			}
1018 			if (ReadVCALText(Line, "X-NAME-PREFIX", Buff,  (version >= 3), &location)) {
1019 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
1020 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_NamePrefix;
1021 				Pbk->Entries[Pbk->EntriesNum].Location = location;
1022 				Pbk->EntriesNum++;
1023 				continue;
1024 			}
1025 			if (ReadVCALText(Line, "X-NAME-SUFFIX", Buff,  (version >= 3), &location)) {
1026 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
1027 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Text_NameSuffix;
1028 				Pbk->Entries[Pbk->EntriesNum].Location = location;
1029 				Pbk->EntriesNum++;
1030 				continue;
1031 			}
1032 			if (ReadVCALText(Line, "CATEGORIES", Buff,  (version >= 3), &location)) {
1033 				CopyUnicodeString(Pbk->Entries[Pbk->EntriesNum].Text,Buff);
1034 				Pbk->Entries[Pbk->EntriesNum].Number = -1;
1035 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Category;
1036 				Pbk->Entries[Pbk->EntriesNum].Location = location;
1037 				Pbk->EntriesNum++;
1038 				continue;
1039 			}
1040 			if (ReadVCALText(Line, "BDAY", Buff,  (version >= 3), NULL)) {
1041 				if (ReadVCALDateTime(DecodeUnicodeString(Buff), &Pbk->Entries[Pbk->EntriesNum].Date)) {
1042 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Date;
1043 					Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
1044 					Pbk->EntriesNum++;
1045 					continue;
1046 				}
1047 			}
1048 			if (ReadVCALText(Line, "LAST-MODIFIED", Buff,  (version >= 3), NULL)) {
1049 				if (ReadVCALDateTime(DecodeUnicodeString(Buff), &Pbk->Entries[Pbk->EntriesNum].Date)) {
1050 					Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_LastModified;
1051 					Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
1052 					Pbk->EntriesNum++;
1053 					continue;
1054 				}
1055 			}
1056 			if (ReadVCALText(Line, "X-PRIVATE", Buff,  (version >= 3), NULL)) {
1057 				Pbk->Entries[Pbk->EntriesNum].Number = atoi(DecodeUnicodeString(Buff));
1058 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Private;
1059 				Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
1060 				Pbk->EntriesNum++;
1061 				continue;
1062 			}
1063 			if (ReadVCALText(Line, "X-CALLER-GROUP", Buff,  (version >= 3), NULL)) {
1064 				Pbk->Entries[Pbk->EntriesNum].Number = atoi(DecodeUnicodeString(Buff));
1065 				Pbk->Entries[Pbk->EntriesNum].EntryType = PBK_Caller_Group;
1066 				Pbk->Entries[Pbk->EntriesNum].Location = PBK_Location_Unknown;
1067 				Pbk->EntriesNum++;
1068 				continue;
1069 			}
1070 			if (ReadVCALText(Line, "X-GAMMU-LOCATION", Buff,  (version >= 3), NULL)) {
1071 				Pbk->Location = atoi(DecodeUnicodeString(Buff));
1072 			}
1073 			if (ReadVCALText(Line, "X-GAMMU-MEMORY", Buff,  (version >= 3), NULL)) {
1074 				Pbk->MemoryType = GSM_StringToMemoryType(DecodeUnicodeString(Buff));
1075 			}
1076 			break;
1077 		}
1078 	}
1079 
1080 vcard_complete:
1081 	if (Pbk->EntriesNum == 0) error = ERR_EMPTY;
1082 	else error = ERR_NONE;
1083 
1084 vcard_done:
1085 	free(Line);
1086 	Line=NULL;
1087 	return error;
1088 }
1089 
GSM_FreeMemoryEntry(GSM_MemoryEntry * Entry)1090 void GSM_FreeMemoryEntry(GSM_MemoryEntry *Entry)
1091 {
1092 	int i;
1093 
1094 	for (i = 0; i < Entry->EntriesNum; i++) {
1095 		switch (Entry->Entries[i].EntryType) {
1096 			case PBK_Photo:
1097 				free(Entry->Entries[i].Picture.Buffer);
1098 				Entry->Entries[i].Picture.Buffer = NULL;
1099 				Entry->Entries[i].Picture.Length = 0;
1100 				break;
1101 			default:
1102 				break;
1103 		}
1104 	}
1105 }
1106 
1107 /* How should editor hadle tabs in this file? Add editor commands here.
1108  * vim: noexpandtab sw=8 ts=8 sts=8:
1109  */
1110