1 /*
2 
3   $Id$
4 
5   G N O K I I
6 
7   A Linux/Unix toolset and driver for the mobile phones.
8 
9   This file is part of gnokii.
10 
11   Gnokii is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 2 of the License, or
14   (at your option) any later version.
15 
16   Gnokii is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20 
21   You should have received a copy of the GNU General Public License
22   along with gnokii; if not, write to the Free Software
23   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 
25   Copyright (C) 1999-2000  Hugh Blemings & Pavel Janik ml.
26   Copyright (C) 1999-2000  Gary Reuter, Reinhold Jordan
27   Copyright (C) 1999-2006  Pawel Kot
28   Copyright (C) 2000-2002  Marcin Wiacek, Chris Kemp, Manfred Jonsson
29   Copyright (C) 2001       Marian Jancar, Bartek Klepacz
30   Copyright (C) 2001-2002  Pavel Machek, Markus Plail
31   Copyright (C) 2002       Ladis Michl, Simon Huggins
32   Copyright (C) 2002-2004  BORBELY Zoltan
33   Copyright (C) 2003       Bertrik Sikken
34   Copyright (C) 2004       Martin Goldhahn
35 
36   Mainline code for gnokii utility. Phonebook functions.
37 
38 */
39 
40 #include "config.h"
41 #include "misc.h"
42 #include "compat.h"
43 
44 #include <stdio.h>
45 #ifndef _GNU_SOURCE
46 #  define _GNU_SOURCE 1
47 #endif
48 #include <getopt.h>
49 
50 #include "gnokii-app.h"
51 #include "gnokii.h"
52 
phonebook_usage(FILE * f)53 void phonebook_usage(FILE *f)
54 {
55 	fprintf(f, _("Phonebook options:\n"
56 		     "          --getphonebook memory_type start_number [end_number|end]\n"
57 		     "                 [[-r|--raw]|[-v|--vcard]|[-l|--ldif]]\n"
58 		     "          --writephonebook [[-o|--overwrite]|[-f|--find-free]]\n"
59 		     "                 [-m|--memory-type|--memory memory_type]\n"
60 		     "                 [-n|--memory-location|--location number]\n"
61 		     "                 [[-v|--vcard]|[-l|--ldif]]\n"
62 		     "          --deletephonebook memory_type start_number [end_number|end]\n"));
63 }
64 
65 /* Displays usage of --getphonebook command */
getphonebook_usage(FILE * f,int exitval)66 int getphonebook_usage(FILE *f, int exitval)
67 {
68 	fprintf(f, _(" usage: --getphonebook memory start [end]  reads phonebook entries from memory type\n"
69 			"                                         (SM, ME, IN, OU, ...) messages starting\n"
70 			"                                         from location start and ending with end;\n"
71 			"                                         if end option is omitted, just one entry\n"
72 			"                                         is read;\n"
73 			"                                         if 'end' is used all entries are read\n"
74 			"       -r\n"
75 			"       --raw                             output in raw form (comma separated)\n"
76 			"       -v\n"
77 			"       --vcard                           output in vcard format\n"
78 			"       -l\n"
79 			"       --ldif                            output in ldif format\n"
80 			"\n"
81 		));
82 	return exitval;
83 }
84 
85 /* Get requested range of memory storage entries and output to stdout in
86    easy-to-parse format */
getphonebook(int argc,char * argv[],gn_data * data,struct gn_statemachine * state)87 gn_error getphonebook(int argc, char *argv[], gn_data *data, struct gn_statemachine *state)
88 {
89 	gn_phonebook_entry entry;
90 	gn_memory_status memstat;
91 	int i, count, start_entry, end_entry, num_entries = INT_MAX, explicit_end = true;
92 	gn_error error = GN_ERR_NONE;
93 	char *memory_type_string;
94 	int type = 0; /* Output type:
95 				0 - not formatted
96 				1 - CSV
97 				2 - vCard
98 				3 - LDIF
99 			*/
100 	struct option options[] = {
101 		{ "raw",    no_argument, NULL, 'r' },
102 		{ "vcard",  no_argument, NULL, 'v' },
103 		{ "ldif",   no_argument, NULL, 'l' },
104 		{ NULL,     0,           NULL, 0 }
105 	};
106 
107 	/* Handle command line args that set type, start and end locations. */
108 	memory_type_string = optarg;
109 	memstat.memory_type = gn_str2memory_type(memory_type_string);
110 	if (memstat.memory_type == GN_MT_XX) {
111 		fprintf(stderr, _("Unknown memory type %s (use ME, SM, ...)!\n"), optarg);
112 		return GN_ERR_INVALIDMEMORYTYPE;
113 	}
114 
115 	start_entry = gnokii_atoi(argv[optind]);
116 	if (errno || start_entry < 0)
117 		return getphonebook_usage(stderr, -1);
118 	end_entry = parse_end_value_option(argc, argv, optind + 1, start_entry);
119 	if (errno || end_entry < 0)
120 		return getphonebook_usage(stderr, -1);
121 
122 	i = getopt_long(argc, argv, "rvl", options, NULL);
123 	switch (i) {
124 	case 'r':
125 		type = 1;
126 		break;
127 	case 'v':
128 		type = 2;
129 		break;
130 	case 'l':
131 		type = 3;
132 		break;
133 	case -1:
134 		/* default */
135 		break;
136 	default:
137 		return getphonebook_usage(stderr, -1);
138 	}
139 	if (argc - optind > 2) {
140 		/* There are too many arguments that don't start with '-' */
141 		return getphonebook_usage(stderr, -1);
142 	}
143 
144 	if (end_entry == INT_MAX) {
145 		explicit_end = false;
146 		data->memory_status = &memstat;
147 		if ((error = gn_sm_functions(GN_OP_GetMemoryStatus, data, state)) == GN_ERR_NONE) {
148 			num_entries = memstat.used;
149 			end_entry = memstat.used + memstat.free;
150 			/* FIXME warn the user like parse_end_value_option() when end_entry < start_entry */
151 			if (end_entry < start_entry) end_entry = start_entry;
152 		}
153 	}
154 
155 	/* Now retrieve the requested entries. */
156 	count = start_entry;
157 	while (num_entries > 0 && count <= end_entry) {
158 		memset(&entry, 0, sizeof(gn_phonebook_entry));
159 		entry.memory_type = memstat.memory_type;
160 		entry.location = count;
161 
162 		data->phonebook_entry = &entry;
163 		error = gn_sm_functions(GN_OP_ReadPhonebook, data, state);
164 
165 		switch (error) {
166 			int i;
167 		case GN_ERR_NONE:
168 			if (entry.empty != false)
169 				break;
170 			num_entries--;
171 			switch (type) {
172 			case 1:
173 				gn_file_phonebook_raw_write(stdout, &entry, memory_type_string);
174 				break;
175 			case 2: {
176 					char *s;
177 
178 					s = gn_phonebook2vcardstr(&entry);
179 					fprintf (stdout, "%s", s);
180 					free (s);
181 				}
182 				break;
183 			case 3:
184 				gn_phonebook2ldif(stdout, &entry);
185 				break;
186 			default:
187 				fprintf(stdout, _("%d. %s: %s\n"), entry.location, _("Name"), entry.name);
188 				/* Print out personal information */
189 				if (entry.person.has_person) {
190 					if (entry.person.honorific_prefixes[0])
191 						fprintf(stdout, "%s ", entry.person.honorific_prefixes);
192 					if (entry.person.given_name[0])
193 						fprintf(stdout, "%s ", entry.person.given_name);
194 					if (entry.person.family_name[0])
195 						fprintf(stdout, "%s", entry.person.family_name);
196 					fprintf(stdout, "\n");
197 				}
198 
199 				fprintf(stdout, _("%s: %s\n"), _("Caller group"), gn_phonebook_group_type2str(entry.caller_group));
200 
201 				/* FIXME: AT driver doesn't set subentries */
202 				if (!entry.subentries_count && entry.number) {
203 					fprintf(stdout, _("%s: %s\n"), _("Number"), entry.number);
204 				}
205 
206 				/* Print out address information */
207 				if (entry.address.has_address) {
208 					fprintf(stdout, _("Address information:\n"));
209 					if (entry.address.post_office_box[0])
210 						fprintf(stdout, _("  %s: %s\n"), _("Post office address"), entry.address.post_office_box);
211 					if (entry.address.extended_address[0])
212 						fprintf(stdout, _("  %s: %s\n"), _("Extended address"), entry.address.extended_address);
213 					if (entry.address.street[0])
214 						fprintf(stdout, _("  %s: %s\n"), _("Street"), entry.address.street);
215 					if (entry.address.city[0])
216 						fprintf(stdout, _("  %s: %s\n"), _("City"), entry.address.city);
217 					if (entry.address.state_province[0])
218 						fprintf(stdout, _("  %s: %s\n"), _("State or province"), entry.address.state_province);
219 					if (entry.address.zipcode[0])
220 						fprintf(stdout, _("  %s: %s\n"), _("Zip code"), entry.address.zipcode);
221 					if (entry.address.country[0])
222 						fprintf(stdout, _("  %s: %s\n"), _("Country"), entry.address.country);
223 				}
224 				dprintf("subentries count: %d\n", entry.subentries_count);
225 				for (i = 0; i < entry.subentries_count; i++) {
226 					fprintf(stdout, "%s: ", gn_subentrytype2string(entry.subentries[i].entry_type, entry.subentries[i].number_type));
227 					switch (entry.subentries[i].entry_type) {
228 					case GN_PHONEBOOK_ENTRY_ExtGroup:
229 					        fprintf(stdout, "%d", entry.subentries[i].data.id);
230 					        break;
231 					case GN_PHONEBOOK_ENTRY_Birthday:
232 					case GN_PHONEBOOK_ENTRY_Date:
233 						fprintf(stdout, _("%04u.%02u.%02u %02u:%02u:%02u"), entry.subentries[i].data.date.year, entry.subentries[i].data.date.month, entry.subentries[i].data.date.day, entry.subentries[i].data.date.hour, entry.subentries[i].data.date.minute, entry.subentries[i].data.date.second);
234 						break;
235 					case GN_PHONEBOOK_ENTRY_Image:
236 						break;
237 					default:
238 						fprintf(stdout, "%s", entry.subentries[i].data.number);
239 						break;
240 					}
241 					fprintf(stdout, "\n");
242 				}
243 				if ((entry.memory_type == GN_MT_MC ||
244 				     entry.memory_type == GN_MT_DC ||
245 				     entry.memory_type == GN_MT_RC) &&
246 				    entry.date.year)
247 					fprintf(stdout, _("Date: %04u.%02u.%02u %02u:%02u:%02u\n"), entry.date.year, entry.date.month, entry.date.day, entry.date.hour, entry.date.minute, entry.date.second);
248 				break;
249 			}
250 			break;
251 		case GN_ERR_EMPTYLOCATION:
252 			fprintf(stderr, _("Empty memory location. Skipping.\n"));
253 			break;
254 		default:
255 			fprintf(stderr, _("Error reading from the location %d in memory %s\n"), count, memory_type_string);
256 			fprintf(stderr, _("Error: %s\n"), gn_error_print(error));
257 			/* some older phones might return GN_ERR_INVALIDLOCATION for lower numbered locations
258 			so make it non fatal when a numeric end location was given */
259 			if (!((error == GN_ERR_INVALIDLOCATION) && explicit_end)) {
260 				return error;
261 			}
262 		}
263 		count++;
264 	}
265 	/* ignore non fatal errors that might have occurred above */
266 	return GN_ERR_NONE;
267 }
268 
269 /* Displays usage of --getphonebook command */
writephonebook_usage(FILE * f,int exitval)270 int writephonebook_usage(FILE *f, int exitval)
271 {
272 	fprintf(f, _(" usage:  --writephonebook [[-o|--overwrite]|[-f|--find-free]]\n"
273 		     "                 [-m|--memory-type|--memory memory_type]\n"
274 		     "                 [-n|--memory-location|--location number]\n"
275 		     "                 [[-v|--vcard]|[-l|--ldif]]\n"
276 		     "\n"
277 		));
278 	return exitval;
279 }
280 
281 /* Read data from stdin, parse and write to phone.  The parsing is relatively
282    crude and doesn't allow for much variation from the stipulated format. */
283 /* FIXME: I guess there's *very* similar code in xgnokii */
writephonebook(int argc,char * argv[],gn_data * data,struct gn_statemachine * state)284 gn_error writephonebook(int argc, char *argv[], gn_data *data, struct gn_statemachine *state)
285 {
286 	gn_phonebook_entry entry;
287 	gn_error error = GN_ERR_NONE;
288 	gn_memory_type default_mt = GN_MT_ME; /* Default memory_type. Changed if given in the command line */
289 	int default_location = 1; /* default location. Changed if given in the command line */
290 	int find_free = 0; /* By default don't try to find a free location */
291 	int confirm = 0; /* By default don't overwrite existing entries */
292 	int type = 0; /* type of the output:
293 				0 - CSV (default)
294 				1 - vCard
295 				2 - LDIF
296 			*/
297 	char *line, oline[MAX_INPUT_LINE_LEN];
298 	int i;
299 
300 	struct option options[] = {
301 		{ "overwrite",		0,			NULL, 'o'},
302 		{ "vcard",		0,			NULL, 'v'},
303 		{ "ldif",		0,			NULL, 'l'},
304 		{ "find-free",		0,			NULL, 'f'},
305 		{ "memory-type",	required_argument,	NULL, 'm'},
306 		{ "memory",		required_argument,	NULL, 'm'},
307 		{ "memory-location",	required_argument,	NULL, 'n'},
308 		{ "location",		required_argument,	NULL, 'n'},
309 		{ NULL,			0,			NULL, 0}
310 	};
311 
312 	/* Option parsing */
313 	while ((i = getopt_long(argc, argv, "ovlfm:n:", options, NULL)) != -1) {
314 		switch (i) {
315 		case 'o':
316 			confirm = 1;
317 			break;
318 		case 'v':
319 			if (type)
320 				return writephonebook_usage(stderr, -1);
321 			type = 1;
322 			break;
323 		case 'l':
324 			if (type)
325 				return writephonebook_usage(stderr, -1);
326 			type = 2;
327 			break;
328 		case 'f':
329 			find_free = 1;
330 			break;
331 		case 'm':
332 			default_mt = gn_str2memory_type(optarg);
333 			break;
334 		case 'n':
335 			default_location = gnokii_atoi(optarg);
336 			if (errno || default_location < 0)
337 				return writephonebook_usage(stderr, -1);
338 			break;
339 		default:
340 			return writephonebook_usage(stderr, -1);
341 		}
342 	}
343 	if (argc > optind) {
344 		/* There are too many arguments that don't start with '-' */
345 		return writephonebook_usage(stderr, -1);
346 	}
347 
348 	line = oline;
349 
350 	/* Go through data from stdin. */
351 	while (!feof(stdin)) {
352 		error = GN_ERR_NONE;
353 
354 		memset(&entry, 0, sizeof(gn_phonebook_entry));
355 		entry.memory_type = default_mt;
356 		entry.location = default_location;
357 		switch (type) {
358 		case 1:
359 			if (gn_vcard2phonebook(stdin, &entry))
360 				error = GN_ERR_WRONGDATAFORMAT;
361 			break;
362 		case 2:
363 			if (gn_ldif2phonebook(stdin, &entry))
364 				error = GN_ERR_WRONGDATAFORMAT;
365 			break;
366 		default:
367 			if (!gn_line_get(stdin, line, MAX_INPUT_LINE_LEN))
368 				goto out; /* it means we read an empty line, but that's not an error */
369 			else
370 				error = gn_file_phonebook_raw_parse(&entry, oline);
371 			break;
372 		}
373 
374 		if (error != GN_ERR_NONE)
375 			goto out;
376 
377 		if (find_free) {
378 #if 0
379 			error = gn_sm_functions(GN_OP_FindFreePhonebookEntry, data, state);
380 			if (error == GN_ERR_NOTIMPLEMENTED) {
381 #endif
382 			for (i = 1; ; i++) {
383 				gn_phonebook_entry aux;
384 
385 				memcpy(&aux, &entry, sizeof(gn_phonebook_entry));
386 				data->phonebook_entry = &aux;
387 				data->phonebook_entry->location = i;
388 				error = gn_sm_functions(GN_OP_ReadPhonebook, data, state);
389 				if (error != GN_ERR_NONE && error != GN_ERR_EMPTYLOCATION) {
390 					break;
391 				}
392 				if (aux.empty || error == GN_ERR_EMPTYLOCATION) {
393 					entry.location = aux.location;
394 					error = GN_ERR_NONE;
395 					break;
396 				}
397 			}
398 #if 0
399 			}
400 #endif
401 			if (error != GN_ERR_NONE)
402 				goto out;
403 		}
404 
405 		if (!confirm) {
406 			gn_phonebook_entry aux;
407 
408 			memcpy(&aux, &entry, sizeof(gn_phonebook_entry));
409 			data->phonebook_entry = &aux;
410 			error = gn_sm_functions(GN_OP_ReadPhonebook, data, state);
411 
412 			if (error == GN_ERR_NONE || error == GN_ERR_EMPTYLOCATION) {
413 				if (!aux.empty && error != GN_ERR_EMPTYLOCATION) {
414 					char ans[8] = "";
415 
416 					fprintf(stdout, _("Location busy. "));
417 					confirm = -1;
418 					while (confirm < 0) {
419 						fprintf(stdout, _("Overwrite? (yes/no) "));
420 						gn_line_get(stdin, ans, 7);
421 						if (!strcmp(ans, _("yes")))
422 							confirm = 1;
423 						else if (!strcmp(ans, _("no")))
424 							confirm = 0;
425 						else {
426 							fprintf(stdout, _("\nIncorrect answer [%s]. Assuming 'no'.\n"), ans);
427 							confirm = 0;
428 						}
429 					}
430 					/* User chose not to overwrite */
431 					if (!confirm) continue;
432 					confirm = 0;
433 				}
434 			} else {
435 				goto out;
436 			}
437 		}
438 
439 		/* Do write and report success/failure. */
440 		gn_phonebook_entry_sanitize(&entry);
441 		data->phonebook_entry = &entry;
442 		error = gn_sm_functions(GN_OP_WritePhonebook, data, state);
443 
444 		if (error == GN_ERR_NONE) {
445 			fprintf(stderr,
446 				 _("Write Succeeded: memory type: %s, loc: %d, name: %s, number: %s\n"),
447 				 gn_memory_type2str(entry.memory_type), entry.location, entry.name, entry.number);
448 			/* If the location was not specified and there are
449 			 * multiple entries, don't write them to the same
450 			 * location */
451 			default_location++;
452 		} else
453 			fprintf(stderr, _("Write failed (%s): memory type: %s, loc: %d, name: %s, number: %s\n"),
454 				 gn_error_print(error), gn_memory_type2str(entry.memory_type), entry.location, entry.name, entry.number);
455 	}
456 out:
457 	if (error != GN_ERR_NONE)
458 		fprintf(stderr, _("Write failed (%s): memory type: %s, loc: %d, name: %s, number: %s\n"),
459 			gn_error_print(error), gn_memory_type2str(entry.memory_type), entry.location, entry.name, entry.number);
460 	return error;
461 }
462 
463 /* Displays usage of --deletephonebook command */
deletephonebook_usage(FILE * f,int exitval)464 int deletephonebook_usage(FILE *f, int exitval)
465 {
466 	fprintf(f, _(" usage: --deletephonebook memory_type start_number [end_number|end]\n"
467 			"\n"
468 		));
469 	return exitval;
470 }
471 
472 /* Delete phonebook entry */
deletephonebook(int argc,char * argv[],gn_data * data,struct gn_statemachine * state)473 gn_error deletephonebook(int argc, char *argv[], gn_data *data, struct gn_statemachine *state)
474 {
475 	gn_phonebook_entry entry;
476 	gn_error error;
477 	char *memory_type_string;
478 	int i, first_location, last_location;
479 
480 	if (argc < 3)
481 		return deletephonebook_usage(stderr, -1);
482 
483 	/* Handle command line args that set memory type and location. */
484 	memory_type_string = optarg;
485 	entry.memory_type = gn_str2memory_type(memory_type_string);
486 	if (entry.memory_type == GN_MT_XX) {
487 		fprintf(stderr, _("Unknown memory type %s (use ME, SM, ...)!\n"), optarg);
488 		return GN_ERR_INVALIDMEMORYTYPE;
489 	}
490 
491 	first_location = gnokii_atoi(argv[optind]);
492 	if (errno || first_location < 0)
493 		return deletephonebook_usage(stderr, -1);
494 	last_location = parse_end_value_option(argc, argv, optind + 1, first_location);
495 	if (errno || last_location < 0)
496 		return deletephonebook_usage(stderr, -1);
497 
498 	for (i = first_location; i <= last_location; i++) {
499 		entry.location = i;
500 		entry.empty = true;
501 		data->phonebook_entry = &entry;
502 		error = gn_sm_functions(GN_OP_DeletePhonebook, data, state);
503 		switch (error) {
504 		case GN_ERR_NONE:
505 			fprintf (stderr, _("Phonebook entry removed: memory type: %s, loc: %d\n"),
506 				 gn_memory_type2str(entry.memory_type), entry.location);
507 			break;
508 		default:
509 			if (last_location == INT_MAX)
510 				last_location = 0;
511 			else
512 				fprintf (stderr, _("Phonebook entry removal FAILED (%s): memory type: %s, loc: %d\n"),
513 					 gn_error_print(error), gn_memory_type2str(entry.memory_type), entry.location);
514 			break;
515 		}
516 	}
517 	return GN_ERR_NONE;
518 }
519