FreeCalypso > hg > fc-pcsc-tools
view simtool/pbupdate.c @ 8:4a9bf783491d
phone number decoding factored out
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 12 Feb 2021 00:07:24 +0000 |
parents | 2071b28cd0c7 |
children | a76ec3e7da09 |
line wrap: on
line source
/* * This module implements the pb-update command. */ #include <sys/types.h> #include <ctype.h> #include <string.h> #include <strings.h> #include <stdio.h> #include <stdlib.h> #include "simresp.h" #include "curfile.h" static u_char gsm7_encode_table[256] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x00 */ 0xFF, 0xFF, '\n', 0xFF, 0xFF, '\r', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x10 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ' ', '!', '"', '#', 0x02, '%', '&', 0x27, /* 0x20 */ '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', /* 0x30 */ '8', '9', ':', ';', '<', '=', '>', '?', 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40 */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50 */ 'X', 'Y', 'Z', 0xBC, 0xAF, 0xBE, 0x94, 0x11, 0xFF, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60 */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70 */ 'x', 'y', 'z', 0xA8, 0xC0, 0xA9, 0xBD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x80 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x90 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0xFF, 0x01, 0x24, 0x03, 0xFF, 0x5F, /* 0xA0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0xB0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0x5B, 0x0E, 0x1C, 0x09, /* 0xC0 */ 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0xFF, /* 0xD0 */ 0x0B, 0xFF, 0xFF, 0xFF, 0x5E, 0xFF, 0xFF, 0x1E, 0x7F, 0xFF, 0xFF, 0xFF, 0x7B, 0x0F, 0x1D, 0xFF, /* 0xE0 */ 0x04, 0x05, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x7D, 0x08, 0xFF, 0xFF, 0xFF, 0x7C, 0xFF, /* 0xF0 */ 0x0C, 0x06, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF }; static digit_char_to_gsm(ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return (ch - '0'); case '*': return 0xA; case '#': return 0xB; case 'a': case 'b': case 'c': return (ch - 'a' + 0xC); case 'A': case 'B': case 'C': return (ch - 'A' + 0xC); } return (-1); } static void pack_digit_bytes(digits, dest, num_digit_bytes) u_char *digits, *dest; unsigned num_digit_bytes; { u_char *sp, *dp; unsigned n; sp = digits; dp = dest; for (n = 0; n < num_digit_bytes; n++) { *dp++ = sp[0] | (sp[1] << 4); sp += 2; } } static char * decode_qstring_alpha(cp, record, filename_for_errs, lineno_for_errs) char *cp, *filename_for_errs; u_char *record; { unsigned maxlen, acclen, nadd; int c; maxlen = curfile_record_len - 14; for (acclen = 0; ; ) { if (*cp == '\0') { unterm_qstring: fprintf(stderr, "%s line %d: unterminated quoted string\n", filename_for_errs, lineno_for_errs); return(0); } if (*cp == '"') break; c = *cp++; if (c == '\\') { if (*cp == '\0') goto unterm_qstring; c = *cp++; switch (c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case '"': case '\\': break; default: fprintf(stderr, "%s line %d: non-understood backslash escape\n", filename_for_errs, lineno_for_errs); return(0); } } c = gsm7_encode_table[c]; if (c == 0xFF) { fprintf(stderr, "%s line %d: character in quoted string cannot be encoded in GSM7\n", filename_for_errs, lineno_for_errs); return(0); } if (c & 0x80) nadd = 2; else nadd = 1; if (acclen + nadd > maxlen) { fprintf(stderr, "%s line %d: alpha tag string is longer than SIM limit\n", filename_for_errs, lineno_for_errs); return(0); } if (c & 0x80) record[acclen++] = 0x1B; record[acclen++] = c & 0x7F; } return(cp + 1); } static char * decode_hex_alpha(cp, record, filename_for_errs, lineno_for_errs) char *cp, *filename_for_errs; u_char *record; { unsigned maxlen, acclen; maxlen = curfile_record_len - 14; for (acclen = 0; ; ) { if (!isxdigit(cp[0]) || !isxdigit(cp[1])) break; if (acclen >= maxlen) { fprintf(stderr, "%s line %d: alpha tag string is longer than SIM limit\n", filename_for_errs, lineno_for_errs); return(0); } record[acclen++] = (decode_hex_digit(cp[0]) << 4) | decode_hex_digit(cp[1]); cp += 2; } return(cp); } static process_record(line, filename_for_errs, lineno_for_errs) char *line, *filename_for_errs; { unsigned recno; u_char record[255], *fixp; u_char digits[20]; unsigned ndigits, num_digit_bytes; char *cp; int c; recno = strtoul(line+1, 0, 10); if (recno < 1 || recno > curfile_record_count) { fprintf(stderr, "%s line %d: record number is out of range\n", filename_for_errs, lineno_for_errs); return(-1); } cp = line + 1; while (isdigit(*cp)) cp++; if (*cp++ != ':') { inv_syntax: fprintf(stderr, "%s line %d: invalid syntax\n", filename_for_errs, lineno_for_errs); return(-1); } while (isspace(*cp)) cp++; memset(record, 0xFF, curfile_record_len); fixp = record + curfile_record_len - 14; if (digit_char_to_gsm(*cp) < 0) goto inv_syntax; for (ndigits = 0; ; ndigits++) { c = digit_char_to_gsm(*cp); if (c < 0) break; cp++; if (ndigits >= 20) { fprintf(stderr, "%s line %d: too many number digits\n", filename_for_errs, lineno_for_errs); return(-1); } digits[ndigits] = c; } if (ndigits & 1) digits[ndigits++] = 0xF; num_digit_bytes = ndigits >> 1; fixp[0] = num_digit_bytes + 1; pack_digit_bytes(digits, fixp + 2, num_digit_bytes); if (*cp++ != ',') goto inv_syntax; if (cp[0] != '0' || cp[1] != 'x' && cp[1] != 'X' || !isxdigit(cp[2]) || !isxdigit(cp[3]) || !isspace(cp[4])) goto inv_syntax; fixp[1] = strtoul(cp, 0, 16); cp += 5; while (isspace(*cp)) cp++; if (!strncasecmp(cp, "CCP=", 4)) { cp += 4; fixp[12] = strtoul(cp, 0, 0); while (*cp && !isspace(*cp)) cp++; while (isspace(*cp)) cp++; } if (!strncasecmp(cp, "EXT=", 4)) { cp += 4; fixp[13] = strtoul(cp, 0, 0); while (*cp && !isspace(*cp)) cp++; while (isspace(*cp)) cp++; } if (*cp == '"') { cp++; cp = decode_qstring_alpha(cp, record, filename_for_errs, lineno_for_errs); if (!cp) return(-1); } else if (!strncasecmp(cp, "HEX", 3)) { cp += 3; while (isspace(*cp)) cp++; cp = decode_hex_alpha(cp, record, filename_for_errs, lineno_for_errs); if (!cp) return(-1); } else goto inv_syntax; while (isspace(*cp)) cp++; if (*cp) goto inv_syntax; return update_rec_op(recno, 0x04, record, curfile_record_len); } cmd_pb_update(argc, argv) char **argv; { int rc; FILE *inf; int lineno; char linebuf[1024]; rc = phonebook_op_common(argv[1]); if (rc < 0) return(rc); inf = fopen(argv[2], "r"); if (!inf) { perror(argv[2]); return(-1); } for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { if (!index(linebuf, '\n')) { fprintf(stderr, "%s line %d: too long or missing newline\n", argv[2], lineno); fclose(inf); return(-1); } if (linebuf[0] != '#' || !isdigit(linebuf[1])) continue; rc = process_record(linebuf, argv[2], lineno); if (rc < 0) { fclose(inf); return(rc); } } fclose(inf); return(0); } static decode_number_arg(arg, fixp) char *arg; u_char *fixp; { u_char digits[20]; unsigned ndigits, num_digit_bytes; char *cp, *endp; int c; cp = arg; if (*cp == '+') { fixp[1] = 0x91; cp++; } else fixp[1] = 0x81; if (digit_char_to_gsm(*cp) < 0) { inv_arg: fprintf(stderr, "error: invalid phone number argument\n"); return(-1); } for (ndigits = 0; ; ndigits++) { c = digit_char_to_gsm(*cp); if (c < 0) break; cp++; if (ndigits >= 20) { fprintf(stderr, "error: too many number digits\n"); return(-1); } digits[ndigits] = c; } if (ndigits & 1) digits[ndigits++] = 0xF; num_digit_bytes = ndigits >> 1; fixp[0] = num_digit_bytes + 1; pack_digit_bytes(digits, fixp + 2, num_digit_bytes); if (*cp == ',') { cp++; if (!isdigit(*cp)) goto inv_arg; fixp[1] = strtoul(cp, &endp, 0); if (*endp) goto inv_arg; } else if (*cp) goto inv_arg; return(0); } static decode_alphatag_arg(arg, record) char *arg; u_char *record; { unsigned maxlen, acclen, nadd; char *cp; int c; maxlen = curfile_record_len - 14; cp = arg; for (acclen = 0; *cp; ) { c = *cp++; if (c == '\\') { if (*cp == '\0') { fprintf(stderr, "error: dangling backslash escape\n"); return(-1); } c = *cp++; switch (c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case '"': case '\\': break; default: fprintf(stderr, "error: non-understood backslash escape\n"); return(-1); } } c = gsm7_encode_table[c]; if (c == 0xFF) { fprintf(stderr, "error: character in alpha tag string cannot be encoded in GSM7\n"); return(-1); } if (c & 0x80) nadd = 2; else nadd = 1; if (acclen + nadd > maxlen) { fprintf(stderr, "error: alpha tag string is longer than SIM limit\n"); return(-1); } if (c & 0x80) record[acclen++] = 0x1B; record[acclen++] = c & 0x7F; } return(0); } cmd_pb_update_imm(argc, argv) char **argv; { int rc; unsigned recno; u_char record[255], *fixp; rc = phonebook_op_common(argv[1]); if (rc < 0) return(rc); recno = strtoul(argv[2], 0, 0); if (recno < 1 || recno > curfile_record_count) { fprintf(stderr, "error: specified record number is invalid\n"); return(-1); } memset(record, 0xFF, curfile_record_len); fixp = record + curfile_record_len - 14; rc = decode_number_arg(argv[3], fixp); if (rc < 0) return(rc); if (argv[4]) { rc = decode_alphatag_arg(argv[4], record); if (rc < 0) return(rc); } return update_rec_op(recno, 0x04, record, curfile_record_len); } static decode_alphatag_arg_hex(arg, record) char *arg; u_char *record; { unsigned maxlen, acclen; maxlen = curfile_record_len - 14; for (acclen = 0; ; acclen++) { while (isspace(*arg)) arg++; if (!*arg) break; if (!isxdigit(arg[0]) || !isxdigit(arg[1])) { fprintf(stderr, "error: invalid hex string input\n"); return(-1); } if (acclen >= maxlen) { fprintf(stderr, "error: alpha tag string is longer than SIM limit\n"); return(-1); } record[acclen] = (decode_hex_digit(arg[0]) << 4) | decode_hex_digit(arg[1]); arg += 2; } return(0); } cmd_pb_update_imm_hex(argc, argv) char **argv; { int rc; unsigned recno; u_char record[255], *fixp; rc = phonebook_op_common(argv[1]); if (rc < 0) return(rc); recno = strtoul(argv[2], 0, 0); if (recno < 1 || recno > curfile_record_count) { fprintf(stderr, "error: specified record number is invalid\n"); return(-1); } memset(record, 0xFF, curfile_record_len); fixp = record + curfile_record_len - 14; rc = decode_number_arg(argv[3], fixp); if (rc < 0) return(rc); rc = decode_alphatag_arg_hex(argv[4], record); if (rc < 0) return(rc); return update_rec_op(recno, 0x04, record, curfile_record_len); }