/* gs1.c - Verifies GS1 data */ /* libzint - the open source barcode library Copyright (C) 2009 - 2020 Robin Stuart Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* vim: set ts=4 sw=4 et : */ #include #include #ifdef _MSC_VER #include #endif #include "common.h" #include "gs1.h" /* This code does some checks on the integrity of GS1 data. It is not intended to be bulletproof, nor does it report very accurately what problem was found or where, but should prevent some of the more common encoding errors */ static void itostr(char ai_string[], int ai_value) { int thou, hund, ten, unit; char temp[2]; strcpy(ai_string, "("); thou = ai_value / 1000; hund = (ai_value - (1000 * thou)) / 100; ten = (ai_value - ((1000 * thou) + (100 * hund))) / 10; unit = ai_value - ((1000 * thou) + (100 * hund) + (10 * ten)); temp[1] = '\0'; if (ai_value >= 1000) { temp[0] = itoc(thou); strcat(ai_string, temp); } if (ai_value >= 100) { temp[0] = itoc(hund); strcat(ai_string, temp); } temp[0] = itoc(ten); strcat(ai_string, temp); temp[0] = itoc(unit); strcat(ai_string, temp); strcat(ai_string, ")"); } INTERNAL int gs1_verify(struct zint_symbol *symbol, const unsigned char source[], const size_t src_len, char reduced[]) { int i, j, last_ai, ai_latch; char ai_string[7]; /* 6 char max "(NNNN)" */ int bracket_level, max_bracket_level, ai_length, max_ai_length, min_ai_length; int ai_count; int error_latch; #ifdef _MSC_VER int *ai_value; int *ai_location; int *data_location; int *data_length; #endif int ai_max = ustrchr_cnt(source, src_len, '[') + 1; /* Plus 1 so non-zero */ #ifndef _MSC_VER int ai_value[ai_max], ai_location[ai_max], data_location[ai_max], data_length[ai_max]; #else ai_value = (int*) _alloca(ai_max * sizeof(int)); ai_location = (int*) _alloca(ai_max * sizeof(int)); data_location = (int*) _alloca(ai_max * sizeof(int)); data_length = (int*) _alloca(ai_max * sizeof(int)); #endif /* Detect extended ASCII characters */ for (i = 0; i < (int) src_len; i++) { if (source[i] >= 128) { strcpy(symbol->errtxt, "250: Extended ASCII characters are not supported by GS1"); return ZINT_ERROR_INVALID_DATA; } if (source[i] == '\0') { strcpy(symbol->errtxt, "262: NUL characters not permitted in GS1 mode"); return ZINT_ERROR_INVALID_DATA; } if (source[i] < 32) { strcpy(symbol->errtxt, "251: Control characters are not supported by GS1"); return ZINT_ERROR_INVALID_DATA; } if (source[i] == 127) { strcpy(symbol->errtxt, "263: DEL characters are not supported by GS1"); return ZINT_ERROR_INVALID_DATA; } } if (source[0] != '[') { strcpy(symbol->errtxt, "252: Data does not start with an AI"); return ZINT_ERROR_INVALID_DATA; } /* Check the position of the brackets */ bracket_level = 0; max_bracket_level = 0; ai_length = 0; max_ai_length = 0; min_ai_length = 5; j = 0; ai_latch = 0; for (i = 0; i < (int) src_len; i++) { ai_length += j; if (((j == 1) && (source[i] != ']')) && ((source[i] < '0') || (source[i] > '9'))) { ai_latch = 1; } if (source[i] == '[') { bracket_level++; j = 1; } if (source[i] == ']') { bracket_level--; if (ai_length < min_ai_length) { min_ai_length = ai_length; } j = 0; ai_length = 0; } if (bracket_level > max_bracket_level) { max_bracket_level = bracket_level; } if (ai_length > max_ai_length) { max_ai_length = ai_length; } } min_ai_length--; if (bracket_level != 0) { /* Not all brackets are closed */ strcpy(symbol->errtxt, "253: Malformed AI in input data (brackets don\'t match)"); return ZINT_ERROR_INVALID_DATA; } if (max_bracket_level > 1) { /* Nested brackets */ strcpy(symbol->errtxt, "254: Found nested brackets in input data"); return ZINT_ERROR_INVALID_DATA; } if (max_ai_length > 4) { /* AI is too long */ strcpy(symbol->errtxt, "255: Invalid AI in input data (AI too long)"); return ZINT_ERROR_INVALID_DATA; } if (min_ai_length <= 1) { /* AI is too short */ strcpy(symbol->errtxt, "256: Invalid AI in input data (AI too short)"); return ZINT_ERROR_INVALID_DATA; } if (ai_latch == 1) { /* Non-numeric data in AI */ strcpy(symbol->errtxt, "257: Invalid AI in input data (non-numeric characters in AI)"); return ZINT_ERROR_INVALID_DATA; } ai_count = 0; for (i = 1; i < (int) src_len; i++) { if (source[i - 1] == '[') { ai_location[ai_count] = i; j = 0; do { ai_string[j] = source[i + j]; j++; } while (ai_string[j - 1] != ']'); ai_string[j - 1] = '\0'; ai_value[ai_count] = atoi(ai_string); ai_count++; } } for (i = 0; i < ai_count; i++) { data_location[i] = ai_location[i] + 3; if (ai_value[i] >= 100) { data_location[i]++; } if (ai_value[i] >= 1000) { data_location[i]++; } data_length[i] = 0; do { data_length[i]++; } while ((source[data_location[i] + data_length[i] - 1] != '[') && (data_location[i] + data_length[i] <= (int) src_len)); data_length[i]--; } for (i = 0; i < ai_count; i++) { if (data_length[i] == 0) { /* No data for given AI */ strcpy(symbol->errtxt, "258: Empty data field in input data"); return ZINT_ERROR_INVALID_DATA; } } strcpy(ai_string, ""); // Check for valid AI values and data lengths according to GS1 General // Specification Release 19, January 2019 for (i = 0; i < ai_count; i++) { error_latch = 2; switch (ai_value[i]) { // Length 2 Fixed case 20: // VARIANT if (data_length[i] != 2) { error_latch = 1; } else { error_latch = 0; } break; // Length 3 Fixed case 422: // ORIGIN case 424: // COUNTRY PROCESS case 426: // COUNTRY FULL PROCESS if (data_length[i] != 3) { error_latch = 1; } else { error_latch = 0; } break; // Length 4 Fixed case 7040: // UIC+EXT case 8111: // POINTS if (data_length[i] != 4) { error_latch = 1; } else { error_latch = 0; } break; // Length 6 Fixed case 11: // PROD DATE case 12: // DUE DATE case 13: // PACK DATE case 15: // BEST BY case 16: // SELL BY case 17: // USE BY case 7006: // FIRST FREEZE DATE case 8005: // PRICE PER UNIT if (data_length[i] != 6) { error_latch = 1; } else { error_latch = 0; } break; // Length 10 Fixed case 7003: // EXPIRY TIME if (data_length[i] != 10) { error_latch = 1; } else { error_latch = 0; } break; // Length 13 Fixed case 410: // SHIP TO LOC case 411: // BILL TO case 412: // PURCHASE FROM case 413: // SHIP FOR LOC case 414: // LOC NO case 415: // PAY TO case 416: // PROD/SERV LOC case 417: // PARTY GLN case 7001: // NSN if (data_length[i] != 13) { error_latch = 1; } else { error_latch = 0; } break; // Length 14 Fixed case 1: // GTIN case 2: // CONTENT case 8001: // DIMENSIONS if (data_length[i] != 14) { error_latch = 1; } else { error_latch = 0; } break; // Length 17 Fixed case 402: // GSIN if (data_length[i] != 17) { error_latch = 1; } else { error_latch = 0; } break; // Length 18 Fixed case 0: // SSCC case 8006: // ITIP case 8017: // GSRN PROVIDER case 8018: // GSRN RECIPIENT case 8026: // ITIP CONTENT if (data_length[i] != 18) { error_latch = 1; } else { error_latch = 0; } break; // Length 2 Max case 7010: // PROD METHOD if (data_length[i] > 2) { error_latch = 1; } else { error_latch = 0; } break; // Length 3 Max case 427: // ORIGIN SUBDIVISION case 7008: // AQUATIC SPECIES if (data_length[i] > 3) { error_latch = 1; } else { error_latch = 0; } break; // Length 4 Max case 7004: // ACTIVE POTENCY if (data_length[i] > 4) { error_latch = 1; } else { error_latch = 0; } break; // Length 6 Max case 242: // MTO VARIANT if (data_length[i] > 6) { error_latch = 1; } else { error_latch = 0; } break; // Length 8 Max case 30: // VAR COUNT case 37: // COUNT if (data_length[i] > 8) { error_latch = 1; } else { error_latch = 0; } break; // Length 10 Max case 7009: // FISHING GEAR TYPE case 8019: // SRIN if (data_length[i] > 10) { error_latch = 1; } else { error_latch = 0; } break; // Length 12 Max case 7005: // CATCH AREA case 8011: // CPID SERIAL if (data_length[i] > 12) { error_latch = 1; } else { error_latch = 0; } break; // Length 20 Max case 10: // BATCH/LOT case 21: // SERIAL case 22: // CPV case 243: // PCN case 254: // GLN EXTENSION COMPONENT case 420: // SHIP TO POST case 7020: // REFURB LOT case 7021: // FUNC STAT case 7022: // REV STAT case 710: // NHRN PZN case 711: // NHRN CIP case 712: // NHRN CN case 713: // NHRN DRN case 714: // NHRN AIM case 7240: // PROTOCOL case 8002: // CMT NO case 8012: // VERSION if (data_length[i] > 20) { error_latch = 1; } else { error_latch = 0; } break; // Length 25 Max case 8020: // REF NO if (data_length[i] > 25) { error_latch = 1; } else { error_latch = 0; } break; // Length 28 Max case 235: // TPX if (data_length[i] > 28) { error_latch = 1; } else { error_latch = 0; } break; // Length 30 Max case 240: // ADDITIONAL ID case 241: // CUST PART NO case 250: // SECONDARY SERIAL case 251: // REF TO SOURCE case 400: // ORDER NUMBER case 401: // GINC case 403: // ROUTE case 7002: // MEAT CUT case 7023: // GIAI ASSEMBLY case 8004: // GIAI case 8010: // CPID case 8013: // BUDI-DI case 90: // INTERNAL if (data_length[i] > 30) { error_latch = 1; } else { error_latch = 0; } break; // Length 34 Max case 8007: // IBAN if (data_length[i] > 34) { error_latch = 1; } else { error_latch = 0; } break; // Length 50 Max case 8009: // OPTSEN if (data_length[i] > 50) { error_latch = 1; } else { error_latch = 0; } break; // Length 70 Max case 8110: // Coupon code case 8112: // Paperless coupon code case 8200: // PRODUCT URL if (data_length[i] > 70) { error_latch = 1; } else { error_latch = 0; } break; } if (ai_value[i] == 253) { // GDTI if ((data_length[i] < 13) || (data_length[i] > 30)) { error_latch = 1; } else { error_latch = 0; } } if (ai_value[i] == 255) { // GCN if ((data_length[i] < 13) || (data_length[i] > 25)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3100) && (ai_value[i] <= 3169)) { if (data_length[i] != 6) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3200) && (ai_value[i] <= 3379)) { if (data_length[i] != 6) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3400) && (ai_value[i] <= 3579)) { if (data_length[i] != 6) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3600) && (ai_value[i] <= 3699)) { if (data_length[i] != 6) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3900) && (ai_value[i] <= 3909)) { // AMOUNT if (data_length[i] > 15) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3910) && (ai_value[i] <= 3919)) { // AMOUNT if ((data_length[i] < 4) || (data_length[i] > 18)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3920) && (ai_value[i] <= 3929)) { // PRICE if (data_length[i] > 15) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3930) && (ai_value[i] <= 3939)) { // PRICE if ((data_length[i] < 4) || (data_length[i] > 18)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 3940) && (ai_value[i] <= 3949)) { // PRCNT OFF if (data_length[i] != 4) { error_latch = 1; } else { error_latch = 0; } } if (ai_value[i] == 421) { // SHIP TO POST if ((data_length[i] < 4) || (data_length[i] > 12)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] == 423) || (ai_value[i] == 425)) { // COUNTRY INITIAL PROCESS || COUNTRY DISASSEMBLY if ((data_length[i] < 4) || (data_length[i] > 15)) { error_latch = 1; } else { error_latch = 0; } } if (ai_value[i] == 7007) { // HARVEST DATE if ((data_length[i] != 6) && (data_length[i] != 12)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 7030) && (ai_value[i] <= 7039)) { // PROCESSOR # if ((data_length[i] < 4) || (data_length[i] > 30)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 7230) && (ai_value[i] <= 7239)) { // CERT # if ((data_length[i] < 3) || (data_length[i] > 30)) { error_latch = 1; } else { error_latch = 0; } } if (ai_value[i] == 8003) { // GRAI if ((data_length[i] < 15) || (data_length[i] > 30)) { error_latch = 1; } else { error_latch = 0; } } if (ai_value[i] == 8008) { // PROD TIME if ((data_length[i] != 8) && (data_length[i] != 10) && (data_length[i] != 12)) { error_latch = 1; } else { error_latch = 0; } } if ((ai_value[i] >= 91) && (ai_value[i] <= 99)) { // INTERNAL if (data_length[i] > 90) { error_latch = 1; } else { error_latch = 0; } } if (error_latch == 1) { itostr(ai_string, ai_value[i]); strcpy(symbol->errtxt, "259: Invalid data length for AI "); strcat(symbol->errtxt, ai_string); return ZINT_ERROR_INVALID_DATA; } if (error_latch == 2) { itostr(ai_string, ai_value[i]); strcpy(symbol->errtxt, "260: Invalid AI value "); strcat(symbol->errtxt, ai_string); return ZINT_ERROR_INVALID_DATA; } } /* Resolve AI data - put resulting string in 'reduced' */ j = 0; last_ai = 0; ai_latch = 1; for (i = 0; i < (int) src_len; i++) { if ((source[i] != '[') && (source[i] != ']')) { reduced[j++] = source[i]; } if (source[i] == '[') { /* Start of an AI string */ if (ai_latch == 0) { reduced[j++] = '['; } ai_string[0] = source[i + 1]; ai_string[1] = source[i + 2]; ai_string[2] = '\0'; last_ai = atoi(ai_string); ai_latch = 0; /* The following values from "GS-1 General Specification version 8.0 issue 2, May 2008" figure 5.4.8.2.1 - 1 "Element Strings with Pre-Defined Length Using Application Identifiers" */ if ( ((last_ai >= 0) && (last_ai <= 4)) || ((last_ai >= 11) && (last_ai <= 20)) || (last_ai == 23) /* legacy support - see 5.3.8.2.2 */ || ((last_ai >= 31) && (last_ai <= 36)) || (last_ai == 41) ) { ai_latch = 1; } } /* The ']' character is simply dropped from the input */ } reduced[j] = '\0'; /* the character '[' in the reduced string refers to the FNC1 character */ return 0; }