root/ext/standard/iptc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_iptc_put1
  2. php_iptc_get1
  3. php_iptc_read_remaining
  4. php_iptc_skip_variable
  5. php_iptc_next_marker
  6. PHP_FUNCTION
  7. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Author: Thies C. Arntzen <thies@thieso.net>                          |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 /*
  22  * Functions to parse & compse IPTC data.
  23  * PhotoShop >= 3.0 can read and write textual data to JPEG files.
  24  * ... more to come .....
  25  *
  26  * i know, parts of this is now duplicated in image.c
  27  * but in this case i think it's okay!
  28  */
  29 
  30 /*
  31  * TODO:
  32  *  - add IPTC translation table
  33  */
  34 
  35 #include "php.h"
  36 #include "php_iptc.h"
  37 #include "ext/standard/head.h"
  38 
  39 #include <sys/stat.h>
  40 
  41 #ifdef PHP_WIN32
  42 # include "win32/php_stdint.h"
  43 #else
  44 # if HAVE_INTTYPES_H
  45 #  include <inttypes.h>
  46 # elif HAVE_STDINT_H
  47 #  include <stdint.h>
  48 # endif
  49 #endif
  50 
  51 /* some defines for the different JPEG block types */
  52 #define M_SOF0  0xC0            /* Start Of Frame N */
  53 #define M_SOF1  0xC1            /* N indicates which compression process */
  54 #define M_SOF2  0xC2            /* Only SOF0-SOF2 are now in common use */
  55 #define M_SOF3  0xC3
  56 #define M_SOF5  0xC5            /* NB: codes C4 and CC are NOT SOF markers */
  57 #define M_SOF6  0xC6
  58 #define M_SOF7  0xC7
  59 #define M_SOF9  0xC9
  60 #define M_SOF10 0xCA
  61 #define M_SOF11 0xCB
  62 #define M_SOF13 0xCD
  63 #define M_SOF14 0xCE
  64 #define M_SOF15 0xCF
  65 #define M_SOI   0xD8
  66 #define M_EOI   0xD9            /* End Of Image (end of datastream) */
  67 #define M_SOS   0xDA            /* Start Of Scan (begins compressed data) */
  68 #define M_APP0  0xe0
  69 #define M_APP1  0xe1
  70 #define M_APP2  0xe2
  71 #define M_APP3  0xe3
  72 #define M_APP4  0xe4
  73 #define M_APP5  0xe5
  74 #define M_APP6  0xe6
  75 #define M_APP7  0xe7
  76 #define M_APP8  0xe8
  77 #define M_APP9  0xe9
  78 #define M_APP10 0xea
  79 #define M_APP11 0xeb
  80 #define M_APP12 0xec
  81 #define M_APP13 0xed
  82 #define M_APP14 0xee
  83 #define M_APP15 0xef
  84 
  85 /* {{{ php_iptc_put1
  86  */
  87 static int php_iptc_put1(FILE *fp, int spool, unsigned char c, unsigned char **spoolbuf)
  88 {
  89         if (spool > 0)
  90                 PUTC(c);
  91 
  92         if (spoolbuf) *(*spoolbuf)++ = c;
  93 
  94         return c;
  95 }
  96 /* }}} */
  97 
  98 /* {{{ php_iptc_get1
  99  */
 100 static int php_iptc_get1(FILE *fp, int spool, unsigned char **spoolbuf)
 101 {
 102         int c;
 103         char cc;
 104 
 105         c = getc(fp);
 106 
 107         if (c == EOF) return EOF;
 108 
 109         if (spool > 0) {
 110                 cc = c;
 111                 PUTC(cc);
 112         }
 113 
 114         if (spoolbuf) *(*spoolbuf)++ = c;
 115 
 116         return c;
 117 }
 118 /* }}} */
 119 
 120 /* {{{ php_iptc_read_remaining
 121  */
 122 static int php_iptc_read_remaining(FILE *fp, int spool, unsigned char **spoolbuf)
 123 {
 124         while (php_iptc_get1(fp, spool, spoolbuf) != EOF) continue;
 125 
 126         return M_EOI;
 127 }
 128 /* }}} */
 129 
 130 /* {{{ php_iptc_skip_variable
 131  */
 132 static int php_iptc_skip_variable(FILE *fp, int spool, unsigned char **spoolbuf)
 133 {
 134         unsigned int  length;
 135         int c1, c2;
 136 
 137     if ((c1 = php_iptc_get1(fp, spool, spoolbuf)) == EOF) return M_EOI;
 138 
 139     if ((c2 = php_iptc_get1(fp, spool, spoolbuf)) == EOF) return M_EOI;
 140 
 141         length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
 142 
 143         length -= 2;
 144 
 145         while (length--)
 146                 if (php_iptc_get1(fp, spool, spoolbuf) == EOF) return M_EOI;
 147 
 148         return 0;
 149 }
 150 /* }}} */
 151 
 152 /* {{{ php_iptc_next_marker
 153  */
 154 static int php_iptc_next_marker(FILE *fp, int spool, unsigned char **spoolbuf)
 155 {
 156     int c;
 157 
 158     /* skip unimportant stuff */
 159 
 160     c = php_iptc_get1(fp, spool, spoolbuf);
 161 
 162         if (c == EOF) return M_EOI;
 163 
 164     while (c != 0xff) {
 165         if ((c = php_iptc_get1(fp, spool, spoolbuf)) == EOF)
 166             return M_EOI; /* we hit EOF */
 167     }
 168 
 169     /* get marker byte, swallowing possible padding */
 170     do {
 171         c = php_iptc_get1(fp, 0, 0);
 172                 if (c == EOF)
 173             return M_EOI;       /* we hit EOF */
 174                 else
 175                 if (c == 0xff)
 176                         php_iptc_put1(fp, spool, (unsigned char)c, spoolbuf);
 177     } while (c == 0xff);
 178 
 179     return (unsigned int) c;
 180 }
 181 /* }}} */
 182 
 183 static char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0\0";
 184 
 185 /* {{{ proto array iptcembed(string iptcdata, string jpeg_file_name [, int spool])
 186    Embed binary IPTC data into a JPEG image. */
 187 PHP_FUNCTION(iptcembed)
 188 {
 189         char *iptcdata, *jpeg_file;
 190         size_t iptcdata_len, jpeg_file_len;
 191         zend_long spool = 0;
 192         FILE *fp;
 193         unsigned int marker, done = 0;
 194         int inx;
 195         zend_string *spoolbuf = NULL;
 196         unsigned char *poi = NULL;
 197         zend_stat_t sb;
 198         zend_bool written = 0;
 199 
 200         if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|l", &iptcdata, &iptcdata_len, &jpeg_file, &jpeg_file_len, &spool) != SUCCESS) {
 201                 return;
 202         }
 203 
 204         if (php_check_open_basedir(jpeg_file)) {
 205                 RETURN_FALSE;
 206         }
 207 
 208         if (iptcdata_len >= SIZE_MAX - sizeof(psheader) - 1025) {
 209                 php_error_docref(NULL, E_WARNING, "IPTC data too large");
 210                 RETURN_FALSE;
 211         }
 212 
 213         if ((fp = VCWD_FOPEN(jpeg_file, "rb")) == 0) {
 214                 php_error_docref(NULL, E_WARNING, "Unable to open %s", jpeg_file);
 215                 RETURN_FALSE;
 216         }
 217 
 218         if (spool < 2) {
 219                 zend_fstat(fileno(fp), &sb);
 220 
 221                 spoolbuf = zend_string_safe_alloc(1, iptcdata_len + sizeof(psheader) + 1024 + 1, sb.st_size, 0);
 222                 poi = (unsigned char*)ZSTR_VAL(spoolbuf);
 223                 memset(poi, 0, iptcdata_len + sizeof(psheader) + sb.st_size + 1024 + 1);
 224         }
 225 
 226         if (php_iptc_get1(fp, spool, poi?&poi:0) != 0xFF) {
 227                 fclose(fp);
 228                 if (spoolbuf) {
 229                         zend_string_free(spoolbuf);
 230                 }
 231                 RETURN_FALSE;
 232         }
 233 
 234         if (php_iptc_get1(fp, spool, poi?&poi:0) != 0xD8) {
 235                 fclose(fp);
 236                 if (spoolbuf) {
 237                         zend_string_free(spoolbuf);
 238                 }
 239                 RETURN_FALSE;
 240         }
 241 
 242         while (!done) {
 243                 marker = php_iptc_next_marker(fp, spool, poi?&poi:0);
 244 
 245                 if (marker == M_EOI) { /* EOF */
 246                         break;
 247                 } else if (marker != M_APP13) {
 248                         php_iptc_put1(fp, spool, (unsigned char)marker, poi?&poi:0);
 249                 }
 250 
 251                 switch (marker) {
 252                         case M_APP13:
 253                                 /* we are going to write a new APP13 marker, so don't output the old one */
 254                                 php_iptc_skip_variable(fp, 0, 0);
 255                                 fgetc(fp); /* skip already copied 0xFF byte */
 256                                 php_iptc_read_remaining(fp, spool, poi?&poi:0);
 257                                 done = 1;
 258                                 break;
 259 
 260                         case M_APP0:
 261                                 /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
 262                         case M_APP1:
 263                                 if (written) {
 264                                         /* don't try to write the data twice */
 265                                         break;
 266                                 }
 267                                 written = 1;
 268 
 269                                 php_iptc_skip_variable(fp, spool, poi?&poi:0);
 270 
 271                                 if (iptcdata_len & 1) {
 272                                         iptcdata_len++; /* make the length even */
 273                                 }
 274 
 275                                 psheader[ 2 ] = (iptcdata_len+28)>>8;
 276                                 psheader[ 3 ] = (iptcdata_len+28)&0xff;
 277 
 278                                 for (inx = 0; inx < 28; inx++) {
 279                                         php_iptc_put1(fp, spool, psheader[inx], poi?&poi:0);
 280                                 }
 281 
 282                                 php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len>>8), poi?&poi:0);
 283                                 php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len&0xff), poi?&poi:0);
 284 
 285                                 for (inx = 0; inx < iptcdata_len; inx++) {
 286                                         php_iptc_put1(fp, spool, iptcdata[inx], poi?&poi:0);
 287                                 }
 288                                 break;
 289 
 290                         case M_SOS:
 291                                 /* we hit data, no more marker-inserting can be done! */
 292                                 php_iptc_read_remaining(fp, spool, poi?&poi:0);
 293                                 done = 1;
 294                                 break;
 295 
 296                         default:
 297                                 php_iptc_skip_variable(fp, spool, poi?&poi:0);
 298                                 break;
 299                 }
 300         }
 301 
 302         fclose(fp);
 303 
 304         if (spool < 2) {
 305                 spoolbuf = zend_string_truncate(spoolbuf, poi - (unsigned char*)ZSTR_VAL(spoolbuf), 0);
 306                 RETURN_NEW_STR(spoolbuf);
 307         } else {
 308                 RETURN_TRUE;
 309         }
 310 }
 311 /* }}} */
 312 
 313 /* {{{ proto array iptcparse(string iptcdata)
 314    Parse binary IPTC-data into associative array */
 315 PHP_FUNCTION(iptcparse)
 316 {
 317         int inx = 0, len;
 318         unsigned int tagsfound = 0;
 319         unsigned char *buffer, recnum, dataset;
 320         char *str, key[16];
 321         size_t str_len;
 322         zval values, *element;
 323 
 324         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) != SUCCESS) {
 325                 return;
 326         }
 327 
 328         buffer = (unsigned char *)str;
 329 
 330         while (inx < str_len) { /* find 1st tag */
 331                 if ((buffer[inx] == 0x1c) && ((buffer[inx+1] == 0x01) || (buffer[inx+1] == 0x02))){
 332                         break;
 333                 } else {
 334                         inx++;
 335                 }
 336         }
 337 
 338         while (inx < str_len) {
 339                 if (buffer[ inx++ ] != 0x1c) {
 340                         break;   /* we ran against some data which does not conform to IPTC - stop parsing! */
 341                 }
 342 
 343                 if ((inx + 4) >= str_len)
 344                         break;
 345 
 346                 dataset = buffer[ inx++ ];
 347                 recnum = buffer[ inx++ ];
 348 
 349                 if (buffer[ inx ] & (unsigned char) 0x80) { /* long tag */
 350                         if((inx+6) >= str_len) {
 351                                 break;
 352                         }
 353                         len = (((zend_long) buffer[ inx + 2 ]) << 24) + (((zend_long) buffer[ inx + 3 ]) << 16) +
 354                                   (((zend_long) buffer[ inx + 4 ]) <<  8) + (((zend_long) buffer[ inx + 5 ]));
 355                         inx += 6;
 356                 } else { /* short tag */
 357                         len = (((unsigned short) buffer[ inx ])<<8) | (unsigned short)buffer[ inx+1 ];
 358                         inx += 2;
 359                 }
 360 
 361                 if ((len < 0) || (len > str_len) || (inx + len) > str_len) {
 362                         break;
 363                 }
 364 
 365                 snprintf(key, sizeof(key), "%d#%03d", (unsigned int) dataset, (unsigned int) recnum);
 366 
 367                 if (tagsfound == 0) { /* found the 1st tag - initialize the return array */
 368                         array_init(return_value);
 369                 }
 370 
 371                 if ((element = zend_hash_str_find(Z_ARRVAL_P(return_value), key, strlen(key))) == NULL) {
 372                         array_init(&values);
 373 
 374                         element = zend_hash_str_update(Z_ARRVAL_P(return_value), key, strlen(key), &values);
 375                 }
 376 
 377                 add_next_index_stringl(element, (char *) buffer+inx, len);
 378                 inx += len;
 379                 tagsfound++;
 380         }
 381 
 382         if (! tagsfound) {
 383                 RETURN_FALSE;
 384         }
 385 }
 386 /* }}} */
 387 
 388 /*
 389  * Local variables:
 390  * tab-width: 4
 391  * c-basic-offset: 4
 392  * End:
 393  * vim600: sw=4 ts=4 fdm=marker
 394  * vim<600: sw=4 ts=4
 395  */

/* [<][>][^][v][top][bottom][index][help] */