FreeCalypso > hg > fc-pcsc-tools
comparison simtool/pbupdate.c @ 1:2071b28cd0c7
simtool: first refactored version
| author | Mychaela Falconia <falcon@freecalypso.org> |
|---|---|
| date | Thu, 11 Feb 2021 23:04:28 +0000 |
| parents | |
| children | a76ec3e7da09 |
comparison
equal
deleted
inserted
replaced
| 0:f7145c77b7fb | 1:2071b28cd0c7 |
|---|---|
| 1 /* | |
| 2 * This module implements the pb-update command. | |
| 3 */ | |
| 4 | |
| 5 #include <sys/types.h> | |
| 6 #include <ctype.h> | |
| 7 #include <string.h> | |
| 8 #include <strings.h> | |
| 9 #include <stdio.h> | |
| 10 #include <stdlib.h> | |
| 11 #include "simresp.h" | |
| 12 #include "curfile.h" | |
| 13 | |
| 14 static u_char gsm7_encode_table[256] = { | |
| 15 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x00 */ | |
| 16 0xFF, 0xFF, '\n', 0xFF, 0xFF, '\r', 0xFF, 0xFF, | |
| 17 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x10 */ | |
| 18 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
| 19 ' ', '!', '"', '#', 0x02, '%', '&', 0x27, /* 0x20 */ | |
| 20 '(', ')', '*', '+', ',', '-', '.', '/', | |
| 21 '0', '1', '2', '3', '4', '5', '6', '7', /* 0x30 */ | |
| 22 '8', '9', ':', ';', '<', '=', '>', '?', | |
| 23 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40 */ | |
| 24 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | |
| 25 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50 */ | |
| 26 'X', 'Y', 'Z', 0xBC, 0xAF, 0xBE, 0x94, 0x11, | |
| 27 0xFF, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60 */ | |
| 28 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | |
| 29 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70 */ | |
| 30 'x', 'y', 'z', 0xA8, 0xC0, 0xA9, 0xBD, 0xFF, | |
| 31 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x80 */ | |
| 32 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
| 33 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x90 */ | |
| 34 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
| 35 0xFF, 0x40, 0xFF, 0x01, 0x24, 0x03, 0xFF, 0x5F, /* 0xA0 */ | |
| 36 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
| 37 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0xB0 */ | |
| 38 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, | |
| 39 0xFF, 0xFF, 0xFF, 0xFF, 0x5B, 0x0E, 0x1C, 0x09, /* 0xC0 */ | |
| 40 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
| 41 0xFF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0xFF, /* 0xD0 */ | |
| 42 0x0B, 0xFF, 0xFF, 0xFF, 0x5E, 0xFF, 0xFF, 0x1E, | |
| 43 0x7F, 0xFF, 0xFF, 0xFF, 0x7B, 0x0F, 0x1D, 0xFF, /* 0xE0 */ | |
| 44 0x04, 0x05, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, | |
| 45 0xFF, 0x7D, 0x08, 0xFF, 0xFF, 0xFF, 0x7C, 0xFF, /* 0xF0 */ | |
| 46 0x0C, 0x06, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF | |
| 47 }; | |
| 48 | |
| 49 static | |
| 50 digit_char_to_gsm(ch) | |
| 51 { | |
| 52 switch (ch) { | |
| 53 case '0': | |
| 54 case '1': | |
| 55 case '2': | |
| 56 case '3': | |
| 57 case '4': | |
| 58 case '5': | |
| 59 case '6': | |
| 60 case '7': | |
| 61 case '8': | |
| 62 case '9': | |
| 63 return (ch - '0'); | |
| 64 case '*': | |
| 65 return 0xA; | |
| 66 case '#': | |
| 67 return 0xB; | |
| 68 case 'a': | |
| 69 case 'b': | |
| 70 case 'c': | |
| 71 return (ch - 'a' + 0xC); | |
| 72 case 'A': | |
| 73 case 'B': | |
| 74 case 'C': | |
| 75 return (ch - 'A' + 0xC); | |
| 76 } | |
| 77 return (-1); | |
| 78 } | |
| 79 | |
| 80 static void | |
| 81 pack_digit_bytes(digits, dest, num_digit_bytes) | |
| 82 u_char *digits, *dest; | |
| 83 unsigned num_digit_bytes; | |
| 84 { | |
| 85 u_char *sp, *dp; | |
| 86 unsigned n; | |
| 87 | |
| 88 sp = digits; | |
| 89 dp = dest; | |
| 90 for (n = 0; n < num_digit_bytes; n++) { | |
| 91 *dp++ = sp[0] | (sp[1] << 4); | |
| 92 sp += 2; | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 static char * | |
| 97 decode_qstring_alpha(cp, record, filename_for_errs, lineno_for_errs) | |
| 98 char *cp, *filename_for_errs; | |
| 99 u_char *record; | |
| 100 { | |
| 101 unsigned maxlen, acclen, nadd; | |
| 102 int c; | |
| 103 | |
| 104 maxlen = curfile_record_len - 14; | |
| 105 for (acclen = 0; ; ) { | |
| 106 if (*cp == '\0') { | |
| 107 unterm_qstring: fprintf(stderr, | |
| 108 "%s line %d: unterminated quoted string\n", | |
| 109 filename_for_errs, lineno_for_errs); | |
| 110 return(0); | |
| 111 } | |
| 112 if (*cp == '"') | |
| 113 break; | |
| 114 c = *cp++; | |
| 115 if (c == '\\') { | |
| 116 if (*cp == '\0') | |
| 117 goto unterm_qstring; | |
| 118 c = *cp++; | |
| 119 switch (c) { | |
| 120 case 'n': | |
| 121 c = '\n'; | |
| 122 break; | |
| 123 case 'r': | |
| 124 c = '\r'; | |
| 125 break; | |
| 126 case '"': | |
| 127 case '\\': | |
| 128 break; | |
| 129 default: | |
| 130 fprintf(stderr, | |
| 131 "%s line %d: non-understood backslash escape\n", | |
| 132 filename_for_errs, lineno_for_errs); | |
| 133 return(0); | |
| 134 } | |
| 135 } | |
| 136 c = gsm7_encode_table[c]; | |
| 137 if (c == 0xFF) { | |
| 138 fprintf(stderr, | |
| 139 "%s line %d: character in quoted string cannot be encoded in GSM7\n", | |
| 140 filename_for_errs, lineno_for_errs); | |
| 141 return(0); | |
| 142 } | |
| 143 if (c & 0x80) | |
| 144 nadd = 2; | |
| 145 else | |
| 146 nadd = 1; | |
| 147 if (acclen + nadd > maxlen) { | |
| 148 fprintf(stderr, | |
| 149 "%s line %d: alpha tag string is longer than SIM limit\n", | |
| 150 filename_for_errs, lineno_for_errs); | |
| 151 return(0); | |
| 152 } | |
| 153 if (c & 0x80) | |
| 154 record[acclen++] = 0x1B; | |
| 155 record[acclen++] = c & 0x7F; | |
| 156 } | |
| 157 return(cp + 1); | |
| 158 } | |
| 159 | |
| 160 static char * | |
| 161 decode_hex_alpha(cp, record, filename_for_errs, lineno_for_errs) | |
| 162 char *cp, *filename_for_errs; | |
| 163 u_char *record; | |
| 164 { | |
| 165 unsigned maxlen, acclen; | |
| 166 | |
| 167 maxlen = curfile_record_len - 14; | |
| 168 for (acclen = 0; ; ) { | |
| 169 if (!isxdigit(cp[0]) || !isxdigit(cp[1])) | |
| 170 break; | |
| 171 if (acclen >= maxlen) { | |
| 172 fprintf(stderr, | |
| 173 "%s line %d: alpha tag string is longer than SIM limit\n", | |
| 174 filename_for_errs, lineno_for_errs); | |
| 175 return(0); | |
| 176 } | |
| 177 record[acclen++] = (decode_hex_digit(cp[0]) << 4) | | |
| 178 decode_hex_digit(cp[1]); | |
| 179 cp += 2; | |
| 180 } | |
| 181 return(cp); | |
| 182 } | |
| 183 | |
| 184 static | |
| 185 process_record(line, filename_for_errs, lineno_for_errs) | |
| 186 char *line, *filename_for_errs; | |
| 187 { | |
| 188 unsigned recno; | |
| 189 u_char record[255], *fixp; | |
| 190 u_char digits[20]; | |
| 191 unsigned ndigits, num_digit_bytes; | |
| 192 char *cp; | |
| 193 int c; | |
| 194 | |
| 195 recno = strtoul(line+1, 0, 10); | |
| 196 if (recno < 1 || recno > curfile_record_count) { | |
| 197 fprintf(stderr, "%s line %d: record number is out of range\n", | |
| 198 filename_for_errs, lineno_for_errs); | |
| 199 return(-1); | |
| 200 } | |
| 201 cp = line + 1; | |
| 202 while (isdigit(*cp)) | |
| 203 cp++; | |
| 204 if (*cp++ != ':') { | |
| 205 inv_syntax: fprintf(stderr, "%s line %d: invalid syntax\n", | |
| 206 filename_for_errs, lineno_for_errs); | |
| 207 return(-1); | |
| 208 } | |
| 209 while (isspace(*cp)) | |
| 210 cp++; | |
| 211 memset(record, 0xFF, curfile_record_len); | |
| 212 fixp = record + curfile_record_len - 14; | |
| 213 if (digit_char_to_gsm(*cp) < 0) | |
| 214 goto inv_syntax; | |
| 215 for (ndigits = 0; ; ndigits++) { | |
| 216 c = digit_char_to_gsm(*cp); | |
| 217 if (c < 0) | |
| 218 break; | |
| 219 cp++; | |
| 220 if (ndigits >= 20) { | |
| 221 fprintf(stderr, "%s line %d: too many number digits\n", | |
| 222 filename_for_errs, lineno_for_errs); | |
| 223 return(-1); | |
| 224 } | |
| 225 digits[ndigits] = c; | |
| 226 } | |
| 227 if (ndigits & 1) | |
| 228 digits[ndigits++] = 0xF; | |
| 229 num_digit_bytes = ndigits >> 1; | |
| 230 fixp[0] = num_digit_bytes + 1; | |
| 231 pack_digit_bytes(digits, fixp + 2, num_digit_bytes); | |
| 232 if (*cp++ != ',') | |
| 233 goto inv_syntax; | |
| 234 if (cp[0] != '0' || cp[1] != 'x' && cp[1] != 'X' || !isxdigit(cp[2]) || | |
| 235 !isxdigit(cp[3]) || !isspace(cp[4])) | |
| 236 goto inv_syntax; | |
| 237 fixp[1] = strtoul(cp, 0, 16); | |
| 238 cp += 5; | |
| 239 while (isspace(*cp)) | |
| 240 cp++; | |
| 241 if (!strncasecmp(cp, "CCP=", 4)) { | |
| 242 cp += 4; | |
| 243 fixp[12] = strtoul(cp, 0, 0); | |
| 244 while (*cp && !isspace(*cp)) | |
| 245 cp++; | |
| 246 while (isspace(*cp)) | |
| 247 cp++; | |
| 248 } | |
| 249 if (!strncasecmp(cp, "EXT=", 4)) { | |
| 250 cp += 4; | |
| 251 fixp[13] = strtoul(cp, 0, 0); | |
| 252 while (*cp && !isspace(*cp)) | |
| 253 cp++; | |
| 254 while (isspace(*cp)) | |
| 255 cp++; | |
| 256 } | |
| 257 if (*cp == '"') { | |
| 258 cp++; | |
| 259 cp = decode_qstring_alpha(cp, record, filename_for_errs, | |
| 260 lineno_for_errs); | |
| 261 if (!cp) | |
| 262 return(-1); | |
| 263 } else if (!strncasecmp(cp, "HEX", 3)) { | |
| 264 cp += 3; | |
| 265 while (isspace(*cp)) | |
| 266 cp++; | |
| 267 cp = decode_hex_alpha(cp, record, filename_for_errs, | |
| 268 lineno_for_errs); | |
| 269 if (!cp) | |
| 270 return(-1); | |
| 271 } else | |
| 272 goto inv_syntax; | |
| 273 while (isspace(*cp)) | |
| 274 cp++; | |
| 275 if (*cp) | |
| 276 goto inv_syntax; | |
| 277 return update_rec_op(recno, 0x04, record, curfile_record_len); | |
| 278 } | |
| 279 | |
| 280 cmd_pb_update(argc, argv) | |
| 281 char **argv; | |
| 282 { | |
| 283 int rc; | |
| 284 FILE *inf; | |
| 285 int lineno; | |
| 286 char linebuf[1024]; | |
| 287 | |
| 288 rc = phonebook_op_common(argv[1]); | |
| 289 if (rc < 0) | |
| 290 return(rc); | |
| 291 inf = fopen(argv[2], "r"); | |
| 292 if (!inf) { | |
| 293 perror(argv[2]); | |
| 294 return(-1); | |
| 295 } | |
| 296 for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { | |
| 297 if (!index(linebuf, '\n')) { | |
| 298 fprintf(stderr, | |
| 299 "%s line %d: too long or missing newline\n", | |
| 300 argv[2], lineno); | |
| 301 fclose(inf); | |
| 302 return(-1); | |
| 303 } | |
| 304 if (linebuf[0] != '#' || !isdigit(linebuf[1])) | |
| 305 continue; | |
| 306 rc = process_record(linebuf, argv[2], lineno); | |
| 307 if (rc < 0) { | |
| 308 fclose(inf); | |
| 309 return(rc); | |
| 310 } | |
| 311 } | |
| 312 fclose(inf); | |
| 313 return(0); | |
| 314 } | |
| 315 | |
| 316 static | |
| 317 decode_number_arg(arg, fixp) | |
| 318 char *arg; | |
| 319 u_char *fixp; | |
| 320 { | |
| 321 u_char digits[20]; | |
| 322 unsigned ndigits, num_digit_bytes; | |
| 323 char *cp, *endp; | |
| 324 int c; | |
| 325 | |
| 326 cp = arg; | |
| 327 if (*cp == '+') { | |
| 328 fixp[1] = 0x91; | |
| 329 cp++; | |
| 330 } else | |
| 331 fixp[1] = 0x81; | |
| 332 if (digit_char_to_gsm(*cp) < 0) { | |
| 333 inv_arg: fprintf(stderr, "error: invalid phone number argument\n"); | |
| 334 return(-1); | |
| 335 } | |
| 336 for (ndigits = 0; ; ndigits++) { | |
| 337 c = digit_char_to_gsm(*cp); | |
| 338 if (c < 0) | |
| 339 break; | |
| 340 cp++; | |
| 341 if (ndigits >= 20) { | |
| 342 fprintf(stderr, "error: too many number digits\n"); | |
| 343 return(-1); | |
| 344 } | |
| 345 digits[ndigits] = c; | |
| 346 } | |
| 347 if (ndigits & 1) | |
| 348 digits[ndigits++] = 0xF; | |
| 349 num_digit_bytes = ndigits >> 1; | |
| 350 fixp[0] = num_digit_bytes + 1; | |
| 351 pack_digit_bytes(digits, fixp + 2, num_digit_bytes); | |
| 352 if (*cp == ',') { | |
| 353 cp++; | |
| 354 if (!isdigit(*cp)) | |
| 355 goto inv_arg; | |
| 356 fixp[1] = strtoul(cp, &endp, 0); | |
| 357 if (*endp) | |
| 358 goto inv_arg; | |
| 359 } else if (*cp) | |
| 360 goto inv_arg; | |
| 361 return(0); | |
| 362 } | |
| 363 | |
| 364 static | |
| 365 decode_alphatag_arg(arg, record) | |
| 366 char *arg; | |
| 367 u_char *record; | |
| 368 { | |
| 369 unsigned maxlen, acclen, nadd; | |
| 370 char *cp; | |
| 371 int c; | |
| 372 | |
| 373 maxlen = curfile_record_len - 14; | |
| 374 cp = arg; | |
| 375 for (acclen = 0; *cp; ) { | |
| 376 c = *cp++; | |
| 377 if (c == '\\') { | |
| 378 if (*cp == '\0') { | |
| 379 fprintf(stderr, | |
| 380 "error: dangling backslash escape\n"); | |
| 381 return(-1); | |
| 382 } | |
| 383 c = *cp++; | |
| 384 switch (c) { | |
| 385 case 'n': | |
| 386 c = '\n'; | |
| 387 break; | |
| 388 case 'r': | |
| 389 c = '\r'; | |
| 390 break; | |
| 391 case '"': | |
| 392 case '\\': | |
| 393 break; | |
| 394 default: | |
| 395 fprintf(stderr, | |
| 396 "error: non-understood backslash escape\n"); | |
| 397 return(-1); | |
| 398 } | |
| 399 } | |
| 400 c = gsm7_encode_table[c]; | |
| 401 if (c == 0xFF) { | |
| 402 fprintf(stderr, | |
| 403 "error: character in alpha tag string cannot be encoded in GSM7\n"); | |
| 404 return(-1); | |
| 405 } | |
| 406 if (c & 0x80) | |
| 407 nadd = 2; | |
| 408 else | |
| 409 nadd = 1; | |
| 410 if (acclen + nadd > maxlen) { | |
| 411 fprintf(stderr, | |
| 412 "error: alpha tag string is longer than SIM limit\n"); | |
| 413 return(-1); | |
| 414 } | |
| 415 if (c & 0x80) | |
| 416 record[acclen++] = 0x1B; | |
| 417 record[acclen++] = c & 0x7F; | |
| 418 } | |
| 419 return(0); | |
| 420 } | |
| 421 | |
| 422 cmd_pb_update_imm(argc, argv) | |
| 423 char **argv; | |
| 424 { | |
| 425 int rc; | |
| 426 unsigned recno; | |
| 427 u_char record[255], *fixp; | |
| 428 | |
| 429 rc = phonebook_op_common(argv[1]); | |
| 430 if (rc < 0) | |
| 431 return(rc); | |
| 432 recno = strtoul(argv[2], 0, 0); | |
| 433 if (recno < 1 || recno > curfile_record_count) { | |
| 434 fprintf(stderr, "error: specified record number is invalid\n"); | |
| 435 return(-1); | |
| 436 } | |
| 437 memset(record, 0xFF, curfile_record_len); | |
| 438 fixp = record + curfile_record_len - 14; | |
| 439 rc = decode_number_arg(argv[3], fixp); | |
| 440 if (rc < 0) | |
| 441 return(rc); | |
| 442 if (argv[4]) { | |
| 443 rc = decode_alphatag_arg(argv[4], record); | |
| 444 if (rc < 0) | |
| 445 return(rc); | |
| 446 } | |
| 447 return update_rec_op(recno, 0x04, record, curfile_record_len); | |
| 448 } | |
| 449 | |
| 450 static | |
| 451 decode_alphatag_arg_hex(arg, record) | |
| 452 char *arg; | |
| 453 u_char *record; | |
| 454 { | |
| 455 unsigned maxlen, acclen; | |
| 456 | |
| 457 maxlen = curfile_record_len - 14; | |
| 458 for (acclen = 0; ; acclen++) { | |
| 459 while (isspace(*arg)) | |
| 460 arg++; | |
| 461 if (!*arg) | |
| 462 break; | |
| 463 if (!isxdigit(arg[0]) || !isxdigit(arg[1])) { | |
| 464 fprintf(stderr, "error: invalid hex string input\n"); | |
| 465 return(-1); | |
| 466 } | |
| 467 if (acclen >= maxlen) { | |
| 468 fprintf(stderr, | |
| 469 "error: alpha tag string is longer than SIM limit\n"); | |
| 470 return(-1); | |
| 471 } | |
| 472 record[acclen] = (decode_hex_digit(arg[0]) << 4) | | |
| 473 decode_hex_digit(arg[1]); | |
| 474 arg += 2; | |
| 475 } | |
| 476 return(0); | |
| 477 } | |
| 478 | |
| 479 cmd_pb_update_imm_hex(argc, argv) | |
| 480 char **argv; | |
| 481 { | |
| 482 int rc; | |
| 483 unsigned recno; | |
| 484 u_char record[255], *fixp; | |
| 485 | |
| 486 rc = phonebook_op_common(argv[1]); | |
| 487 if (rc < 0) | |
| 488 return(rc); | |
| 489 recno = strtoul(argv[2], 0, 0); | |
| 490 if (recno < 1 || recno > curfile_record_count) { | |
| 491 fprintf(stderr, "error: specified record number is invalid\n"); | |
| 492 return(-1); | |
| 493 } | |
| 494 memset(record, 0xFF, curfile_record_len); | |
| 495 fixp = record + curfile_record_len - 14; | |
| 496 rc = decode_number_arg(argv[3], fixp); | |
| 497 if (rc < 0) | |
| 498 return(rc); | |
| 499 rc = decode_alphatag_arg_hex(argv[4], record); | |
| 500 if (rc < 0) | |
| 501 return(rc); | |
| 502 return update_rec_op(recno, 0x04, record, curfile_record_len); | |
| 503 } |
