Home | History | Annotate | Download | only in imd
      1 /*
      2  * Copyright 2001-2004 Brandon Long
      3  * All Rights Reserved.
      4  *
      5  * ClearSilver Templating System
      6  *
      7  * This code is made available under the terms of the ClearSilver License.
      8  * http://www.clearsilver.net/license.hdf
      9  *
     10  */
     11 
     12 /* Bring in gd library functions */
     13 #include "gd.h"
     14 
     15 /* Bring in standard I/O so we can output the PNG to a file */
     16 #include <stdio.h>
     17 #include <unistd.h>
     18 #include <string.h>
     19 #include <sys/stat.h>
     20 #include <stdlib.h>
     21 #include <dirent.h>
     22 #include <errno.h>
     23 #include <sys/fcntl.h>
     24 #include <time.h>
     25 #include <ctype.h>
     26 
     27 #include "ClearSilver.h"
     28 
     29 /* from httpd util.c : made infamous with Roy owes Rob beer. */
     30 static char *months[] = {
     31   "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
     32 };
     33 
     34 int find_month(char *mon) {
     35   register int x;
     36 
     37   for(x=0;x<12;x++)
     38     if(!strcmp(months[x],mon))
     39       return x;
     40   return -1;
     41 }
     42 
     43 int later_than(struct tm *lms, char *ims) {
     44   char *ip;
     45   char mname[256];
     46   int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, x;
     47 
     48   /* Whatever format we're looking at, it will start
     49    * with weekday. */
     50   /* Skip to first space. */
     51   if(!(ip = strchr(ims,' ')))
     52     return 0;
     53   else
     54     while(isspace(*ip))
     55       ++ip;
     56 
     57   if(isalpha(*ip)) {
     58     /* ctime */
     59     sscanf(ip,"%25s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
     60   }
     61   else if(ip[2] == '-') {
     62     /* RFC 850 (normal HTTP) */
     63     char t[256];
     64     sscanf(ip,"%s %d:%d:%d",t,&hour,&min,&sec);
     65     t[2] = '\0';
     66     day = atoi(t);
     67     t[6] = '\0';
     68     strcpy(mname,&t[3]);
     69     x = atoi(&t[7]);
     70     /* Prevent wraparound from ambiguity */
     71     if(x < 70)
     72       x += 100;
     73     year = 1900 + x;
     74   }
     75   else {
     76     /* RFC 822 */
     77     sscanf(ip,"%d %s %d %d:%d:%d",&day,mname,&year,&hour,&min,&sec);
     78   }
     79   month = find_month(mname);
     80 
     81   if((x = (1900+lms->tm_year) - year))
     82     return x < 0;
     83   if((x = lms->tm_mon - month))
     84     return x < 0;
     85   if((x = lms->tm_mday - day))
     86     return x < 0;
     87   if((x = lms->tm_hour - hour))
     88     return x < 0;
     89   if((x = lms->tm_min - min))
     90     return x < 0;
     91   if((x = lms->tm_sec - sec))
     92     return x < 0;
     93 
     94   return 1;
     95 }
     96 
     97 
     98 
     99 int gif_size (char *file, int *width, int *height)
    100 {
    101   UINT8 data[256];
    102   int fd;
    103   int blen;
    104 
    105   *width = 0; *height = 0;
    106   fd = open (file, O_RDONLY);
    107   if (fd == -1)
    108     return -1;
    109 
    110   blen = read(fd, data, sizeof(data));
    111   close(fd);
    112 
    113   if (blen < 10) return -1;
    114   if (strncmp(data, "GIF87a", 6) && strncmp(data, "GIF89a", 6))
    115     return -1;
    116 
    117   *width = data[6] + data[7]*256;
    118   *height = data[8] + data[9]*256;
    119 
    120   return 0;
    121 }
    122 
    123 int jpeg_size (char *file, int *width, int *height)
    124 {
    125   UINT8 data[64*1024];
    126   int blen;
    127   int fd;
    128   int pos;
    129   int length;
    130   UINT8 tag, marker;
    131 
    132 
    133   *width = 0; *height = 0;
    134   fd = open (file, O_RDONLY);
    135   if (fd == -1)
    136     return -1;
    137 
    138   blen = read(fd, data, sizeof(data));
    139   close(fd);
    140   pos = 2;
    141   while (pos+8 < blen)
    142   {
    143     tag = data[pos+0];
    144     if (tag != 0xff) return -1;
    145     marker = data[pos+1];
    146     length = data[pos+2] * 256 + data[pos+3] + 2;
    147     if (marker >= 0xc0 && marker <= 0xcf && marker != 0xc4 &&
    148 	marker != 0xc8 && marker != 0xcc)
    149     {
    150       *height = data[pos+5] * 256 + data[pos+6];
    151       *width = data[pos+7] * 256 + data[pos+8];
    152       ne_warn("%s: %dx%d", file, *width, *height);
    153       return 0;
    154     }
    155     pos += length;
    156   }
    157   return -1;
    158 }
    159 
    160 int isdir(char *dir) {
    161   struct stat statinfo;
    162   if ( stat(dir, &statinfo) != 0) {
    163     return 0;
    164   }
    165 
    166   return S_ISDIR(statinfo.st_mode);
    167 }
    168 
    169 int create_directories(char *fullpath) {
    170   char s[4000];
    171   char *last_slash;
    172   int last_slash_pos;
    173 
    174   if ((fullpath == NULL) || (strlen(fullpath) > 4000)) {
    175     return 1;
    176   }
    177 
    178   last_slash = strrchr(fullpath,'/');
    179   last_slash_pos = (last_slash - fullpath);
    180   /* fprintf(stderr,"dira(%d): %s\n", last_slash_pos,fullpath); */
    181 
    182   if (last_slash_pos > 2) {
    183     strncpy(s,fullpath,last_slash_pos);
    184     s[last_slash_pos] = 0;
    185     /* fprintf(stderr,"dir: %s\n", s); */
    186 
    187     if (!isdir(s)) {
    188       char s2[4000];
    189       sprintf(s2,"mkdir -p %s", s);
    190       return system(s2);
    191     }
    192 
    193   } else {
    194     return 1;
    195   }
    196 
    197   return 0;
    198 }
    199 
    200 NEOERR *rotate_image(char *path, char *file, int degree, char *rpath)
    201 {
    202   char cmd[256];
    203   char nfile[_POSIX_PATH_MAX];
    204   char ofile[_POSIX_PATH_MAX];
    205   char *ch, *opt;
    206   int is_jpeg = 0;
    207   struct stat s;
    208   int r;
    209 
    210   snprintf (ofile, sizeof(ofile), "%s/%s", path, file);
    211   snprintf (rpath, _POSIX_PATH_MAX, "%s/%s", path, file);
    212   ch = strrchr(rpath, '.');
    213   if ((!strcasecmp(ch, ".jpg")) ||
    214       (!strcasecmp(ch, ".jpeg")) ||
    215       (!strcasecmp(ch, ".thm")))
    216   {
    217     is_jpeg = 1;
    218   }
    219   else if (strcasecmp(ch, ".gif"))
    220   {
    221     return nerr_raise(NERR_ASSERT, "Only support gif/jpeg for rotation, ext %s",
    222 	ch);
    223   }
    224   *ch = '\0';
    225   if (degree == 90)
    226   {
    227     strcat(rpath, "_r");
    228     opt = "-cw";
    229   }
    230   else if (degree == -90)
    231   {
    232     strcat(rpath, "_l");
    233     opt = "-ccw";
    234   }
    235   else if (degree == 180)
    236   {
    237     strcat(rpath, "_u");
    238     opt = "-rotate180";
    239   }
    240   else
    241   {
    242     return nerr_raise(NERR_ASSERT, "currently only support 90/-90/180 rotations");
    243   }
    244   if (is_jpeg)
    245   {
    246     strcat(rpath, ".jpg");
    247     snprintf(cmd, sizeof(cmd), "djpeg -pnm %s | pnmflip %s | cjpeg -quality 85 > %s", ofile, opt, rpath);
    248   }
    249   else
    250   {
    251     strcat(rpath, ".gif");
    252     snprintf(cmd, sizeof(cmd), "giftopnm %s | pnmflip %s | ppmtogif > %s", ofile, opt, rpath);
    253   }
    254   /* already exists? */
    255   if (!stat(rpath, &s))
    256   {
    257     return STATUS_OK;
    258   }
    259   r = system(cmd);
    260   if (r) return nerr_raise_errno (NERR_SYSTEM, "%s returned %d", cmd, r);
    261   /* always save off the old file */
    262   snprintf (nfile, sizeof(nfile), "%s/%s.orig", path, file);
    263   if (stat(nfile, &s))
    264   {
    265     if (link(ofile, nfile))
    266       return nerr_raise_errno (NERR_SYSTEM, "Unable to link %s -> %s", ofile, nfile);
    267     unlink(ofile);
    268   }
    269   return STATUS_OK;
    270 }
    271 
    272 NEOERR *scale_and_display_image(char *fname,int maxW,int maxH,char *cachepath,
    273     int quality)
    274 {
    275   NEOERR *err = STATUS_OK;
    276   /* Declare the image */
    277   gdImagePtr src_im = 0;
    278   /* Declare input file */
    279   FILE *infile=0, *cachefile=0;
    280   int srcX,srcY,srcW,srcH;
    281   FILE *dispfile=0;
    282   struct stat s;
    283 
    284   /* if we can open the cachepath, then just print it */
    285   if (!stat(cachepath, &s) && s.st_size)
    286     cachefile = fopen(cachepath,"rb");
    287   if (cachefile) {
    288     /* we should probably stat the files and make sure the thumbnail
    289        is current */
    290     /* fprintf(stderr,"using cachefile: %s\n",cachepath); */
    291     dispfile = cachefile;
    292   } else {
    293     char cmd[1024];
    294     int factor=1;
    295     int l;
    296     int is_jpeg = 0, is_gif = 0;
    297 
    298     l = strlen(fname);
    299     if ((l>4 && !strcasecmp(fname+l-4, ".jpg")) ||
    300 	(l>4 && !strcasecmp(fname+l-4, ".thm")) ||
    301 	(l>5 && !strcasecmp(fname+l-5, ".jpeg")))
    302       is_jpeg = 1;
    303     else if (l>4 && !strcasecmp(fname+l-4, ".gif"))
    304       is_gif = 1;
    305 
    306 
    307     if (is_jpeg)
    308     {
    309       if (!quality)
    310       {
    311 	if (!jpeg_size (fname, &srcW, &srcH))
    312 	{
    313 	  if ((srcW > maxW) || (srcH > maxH))
    314 	  {
    315 	    factor = 2;
    316 	    if (srcW / factor > maxW)
    317 	    {
    318 	      factor = 4;
    319 	      if (srcW / factor > maxW)
    320 		factor = 8;
    321 	    }
    322 	  }
    323 	}
    324 
    325 	/* ne_warn("factor %d\n", factor); */
    326 	snprintf (cmd, sizeof(cmd), "/usr/bin/djpeg -fast -scale 1/%d '%s' | /usr/bin/cjpeg -quality 60 -progressive -dct fast -outfile '%s'", factor, fname, cachepath);
    327 
    328 	create_directories(cachepath);
    329 	system(cmd);
    330 	if (!stat(cachepath, &s) && s.st_size)
    331 	  cachefile = fopen(cachepath,"rb");
    332 	else
    333 	  ne_warn("external command failed to create file\n");
    334       }
    335       if (cachefile) {
    336 	dispfile = cachefile;
    337 
    338       } else /* no cachefile */ {
    339 
    340 
    341 	/* fprintf(stderr,"reading image\n"); */
    342 	/* Read the image in */
    343 	infile = fopen(fname,"rb");
    344 	src_im = gdImageCreateFromJpeg(infile);
    345 	srcX=0; srcY=0; srcW=src_im->sx; srcH=src_im->sy;
    346 
    347 
    348 	/* figure out if we need to scale it */
    349 
    350 	if ((maxW && srcW > maxW) || (maxH && srcH > maxH)) {
    351 	  /* scale paramaters */
    352 	  int dstX,dstY,dstW,dstH;
    353 	  /* Declare output file */
    354 	  FILE *jpegout;
    355 	  gdImagePtr dest_im;
    356 	  float srcAspect,dstAspect;
    357 
    358 	  /* create the destination image */
    359 
    360 	  dstX=0; dstY=0;
    361 
    362 
    363 	  srcAspect = ((float)srcW/(float)srcH);
    364 	  dstAspect = ((float)maxW/(float)maxH);
    365 
    366 	  if (srcAspect == dstAspect) {
    367 	    /* they are the same aspect ratio */
    368 	    dstW = maxW;
    369 	    dstH = maxH;
    370 	  } else if ( srcAspect > dstAspect ) {
    371 	    /* if the src image has wider aspect ratio than the max */
    372 	    dstW = maxW;
    373 	    dstH = (int) ( ((float)dstW/(float)srcW) * srcH );
    374 	  } else {
    375 	    /* if the src image has taller aspect ratio than the max */
    376 	    dstH = maxW;
    377 	    dstW = (int) ( ((float)dstH/(float)srcH) * srcW );
    378 	  }
    379 
    380 #ifdef GD2_VERS
    381 	  dest_im = gdImageCreateTrueColor(dstW,dstH);
    382 #else
    383 	  dest_im = gdImageCreate(dstW,dstH);
    384 #endif
    385 
    386 	  /* fprintf(stderr,"scaling to (%d,%d)\n",dstW,dstH); */
    387 
    388 	  /* Scale it to the destination image */
    389 
    390 	  gdImageCopyResized(dest_im,src_im,dstX,dstY,srcX,srcY,dstW,dstH,srcW,srcH);
    391 
    392 	  /* fprintf(stderr,"scaling finished\n"); */
    393 
    394 	  /* write the output image */
    395 	  create_directories(cachepath);
    396 	  jpegout = fopen(cachepath,"wb+");
    397 	  if (!jpegout) {
    398 	    jpegout = fopen("/tmp/foobar.jpg","wb+");
    399 	  }
    400 	  if (jpegout) {
    401 	    gdImageJpeg(dest_im,jpegout,-1);
    402 	    fflush(jpegout);
    403 
    404 	    /* now print that data out the stream */
    405 	    dispfile = jpegout;
    406 	  } else {
    407 	    return nerr_raise_errno(NERR_IO, "Unable to create output file: %s", cachepath);
    408 	  }
    409 
    410 
    411 	  gdImageDestroy(dest_im);
    412 
    413 	} else {
    414 	  /* just print the input file because it's small enough */
    415 	  dispfile = infile;
    416 	}
    417 
    418       }
    419     }
    420     else if (is_gif)
    421     {
    422       float scale = 1.0;
    423       if (!gif_size (fname, &srcW, &srcH))
    424       {
    425 	if ((srcW > maxW) || (srcH > maxH))
    426 	{
    427 	  scale = 0.5;
    428 	  if (srcW * scale > maxW)
    429 	  {
    430 	    scale = 0.25;
    431 	    if (srcW * scale > maxW)
    432 	      factor = 0.125;
    433 	  }
    434 	}
    435       }
    436 
    437       if (scale < 1.0)
    438       {
    439 	snprintf (cmd, sizeof(cmd), "/usr/bin/giftopnm '%s' | /usr/bin/pnmscale  %5.3f | ppmquant 256 | ppmtogif > '%s'", fname, scale, cachepath);
    440 
    441 	create_directories(cachepath);
    442 	system(cmd);
    443 	dispfile = fopen(cachepath,"rb");
    444 	if (dispfile == NULL)
    445 	  return nerr_raise_errno(NERR_IO, "Unable to open file: %s", cachepath);
    446 
    447       }
    448       else
    449       {
    450 	dispfile = fopen(fname, "rb");
    451 	if (dispfile == NULL)
    452 	  return nerr_raise_errno(NERR_IO, "Unable to open file: %s", fname);
    453       }
    454     }
    455     else {
    456       dispfile = fopen(fname,"rb");
    457     }
    458   }
    459 
    460   /* the data in "dispfile" is going to be printed now */
    461   {
    462 
    463     char buf[8192];
    464     int count;
    465 
    466     if (!fstat(fileno(dispfile), &s) && s.st_size)
    467     {
    468       cgiwrap_writef("Content-Length: %ld\n\n", s.st_size);
    469     }
    470     else
    471     {
    472       cgiwrap_writef("\n");
    473     }
    474 
    475     fseek(dispfile,0,SEEK_SET);
    476 
    477     do {
    478       count = fread(buf,1,sizeof(buf),dispfile);
    479       if (count > 0) {
    480 	err = cgiwrap_write(buf,count);
    481       }
    482     } while (count > 0);
    483 
    484   }
    485 
    486   if (dispfile) fclose(dispfile);
    487   if (src_im) gdImageDestroy(src_im);
    488 
    489   return nerr_pass(err);
    490 }
    491 
    492 NEOERR *load_images (char *path, ULIST **rfiles, char *partial, int descend)
    493 {
    494   NEOERR *err = STATUS_OK;
    495   DIR *dp;
    496   struct dirent *de;
    497   int is_jpeg, is_gif, l;
    498   char fpath[_POSIX_PATH_MAX];
    499   char ppath[_POSIX_PATH_MAX];
    500   ULIST *files = NULL;
    501 
    502   if ((dp = opendir (path)) == NULL)
    503   {
    504     return nerr_raise(NERR_IO, "Unable to opendir %s: [%d] %s", path, errno,
    505 	strerror(errno));
    506   }
    507 
    508   if (rfiles == NULL || *rfiles == NULL)
    509   {
    510     err = uListInit(&files, 50, 0);
    511     if (err) return nerr_pass(err);
    512     *rfiles = files;
    513   }
    514   else
    515   {
    516     files = *rfiles;
    517   }
    518 
    519   while ((de = readdir (dp)) != NULL)
    520   {
    521     if (de->d_name[0] != '.')
    522     {
    523       snprintf(fpath, sizeof(fpath), "%s/%s", path, de->d_name);
    524       if (partial)
    525       {
    526 	snprintf(ppath, sizeof(ppath), "%s/%s", partial, de->d_name);
    527       }
    528       else
    529       {
    530 	strncpy(ppath, de->d_name, sizeof(ppath));
    531       }
    532       if (descend && isdir(fpath))
    533       {
    534 	err = load_images(fpath, rfiles, ppath, descend);
    535 	if (err) break;
    536       }
    537       else
    538       {
    539 	l = strlen(de->d_name);
    540 	is_jpeg = 0; is_gif = 0;
    541 
    542 	if ((l>4 && !strcasecmp(de->d_name+l-4, ".jpg")) ||
    543 	    (l>4 && !strcasecmp(de->d_name+l-4, ".thm")) ||
    544 	    (l>5 && !strcasecmp(de->d_name+l-5, ".jpeg")))
    545 	  is_jpeg = 1;
    546 	else if (l>4 && !strcasecmp(de->d_name+l-4, ".gif"))
    547 	  is_gif = 1;
    548 
    549 	if (is_gif || is_jpeg)
    550 	{
    551 	  err = uListAppend(files, strdup(ppath));
    552 	  if (err) break;
    553 	}
    554       }
    555     }
    556   }
    557   closedir(dp);
    558   if (err)
    559   {
    560     uListDestroy(&files, ULIST_FREE);
    561   }
    562   else
    563   {
    564     *rfiles = files;
    565   }
    566   return nerr_pass(err);
    567 }
    568 
    569 NEOERR *export_image(CGI *cgi, char *prefix, char *path, char *file)
    570 {
    571   NEOERR *err;
    572   char buf[256];
    573   char num[20];
    574   int i = 0;
    575   int r, l;
    576   int width, height;
    577   char ipath[_POSIX_PATH_MAX];
    578   int is_jpeg = 0, is_gif = 0, is_thm = 0;
    579 
    580   l = strlen(file);
    581   if ((l>4 && !strcasecmp(file+l-4, ".jpg")) ||
    582       (l>5 && !strcasecmp(file+l-5, ".jpeg")))
    583     is_jpeg = 1;
    584   else if (l>4 && !strcasecmp(file+l-4, ".gif"))
    585     is_gif = 1;
    586   else if (l>4 && !strcasecmp(file+l-4, ".thm"))
    587     is_thm = 1;
    588 
    589   snprintf (buf, sizeof(buf), "%s.%d", prefix, i);
    590   err = hdf_set_value (cgi->hdf, prefix, file);
    591   if (err != STATUS_OK) return nerr_pass(err);
    592   snprintf (ipath, sizeof(ipath), "%s/%s", path, file);
    593   if (is_jpeg || is_thm)
    594     r = jpeg_size(ipath, &width, &height);
    595   else
    596     r = gif_size(ipath, &width, &height);
    597   if (!r)
    598   {
    599     snprintf (buf, sizeof(buf), "%s.width", prefix);
    600     snprintf (num, sizeof(num), "%d", width);
    601     err = hdf_set_value (cgi->hdf, buf, num);
    602     if (err != STATUS_OK) return nerr_pass(err);
    603     snprintf (buf, sizeof(buf), "%s.height", prefix);
    604     snprintf (num, sizeof(num), "%d", height);
    605     err = hdf_set_value (cgi->hdf, buf, num);
    606     if (err != STATUS_OK) return nerr_pass(err);
    607   }
    608   if (is_thm)
    609   {
    610     strcpy(ipath, file);
    611     strcpy(ipath+l-4, ".avi");
    612     snprintf(buf, sizeof(buf), "%s.avi", prefix);
    613     err = hdf_set_value (cgi->hdf, buf, ipath);
    614     if (err != STATUS_OK) return nerr_pass(err);
    615   }
    616   return STATUS_OK;
    617 }
    618 
    619 NEOERR *scale_images (CGI *cgi, char *prefix, int width, int height, int force)
    620 {
    621   NEOERR *err;
    622   char num[20];
    623   HDF *obj;
    624   int i, x;
    625   int factor;
    626 
    627   obj = hdf_get_obj (cgi->hdf, prefix);
    628   if (obj) obj = hdf_obj_child (obj);
    629   while (obj)
    630   {
    631     factor = 1;
    632     i = hdf_get_int_value(obj, "height", -1);
    633     if (i != -1)
    634     {
    635       x = i;
    636       while (x > height)
    637       {
    638 	/* factor = factor * 2;*/
    639 	factor++;
    640 	x = i / factor;
    641       }
    642       snprintf (num, sizeof(num), "%d", x);
    643       err = hdf_set_value (obj, "height", num);
    644       if (err != STATUS_OK) return nerr_pass (err);
    645 
    646       i = hdf_get_int_value(obj, "width", -1);
    647       if (i != -1)
    648       {
    649 	i = i / factor;
    650 	snprintf (num, sizeof(num), "%d", i);
    651 	err = hdf_set_value (obj, "width", num);
    652 	if (err != STATUS_OK) return nerr_pass (err);
    653       }
    654     }
    655     else
    656     {
    657       snprintf (num, sizeof(num), "%d", height);
    658       err = hdf_set_value (obj, "height", num);
    659       if (err != STATUS_OK) return nerr_pass (err);
    660       snprintf (num, sizeof(num), "%d", width);
    661       err = hdf_set_value (obj, "width", num);
    662       if (err != STATUS_OK) return nerr_pass (err);
    663     }
    664     obj = hdf_obj_next(obj);
    665   }
    666   return STATUS_OK;
    667 }
    668 
    669 int alpha_sort(const void *a, const void *b)
    670 {
    671   char **sa = (char **)a;
    672   char **sb = (char **)b;
    673 
    674   /* ne_warn("%s %s: %d", *sa, *sb, strcmp(*sa, *sb)); */
    675 
    676   return strcmp(*sa, *sb);
    677 }
    678 
    679 static NEOERR *export_album_path(CGI *cgi, char *album, char *prefix)
    680 {
    681   NEOERR *err = STATUS_OK;
    682   char *p, *l;
    683   int n = 0;
    684   char buf[256];
    685 
    686   l = album;
    687   p = strchr(album, '/');
    688 
    689   while (p != NULL)
    690   {
    691     *p = '\0';
    692     snprintf(buf, sizeof(buf), "%s.%d", prefix, n);
    693     err = hdf_set_value(cgi->hdf, buf, l);
    694     if (err) break;
    695     snprintf(buf, sizeof(buf), "%s.%d.path", prefix, n++);
    696     err = hdf_set_value(cgi->hdf, buf, album);
    697     if (err) break;
    698     *p = '/';
    699     l = p+1;
    700     p = strchr(l, '/');
    701   }
    702   if (err) return nerr_pass(err);
    703   if (strlen(l))
    704   {
    705     snprintf(buf, sizeof(buf), "%s.%d", prefix, n);
    706     err = hdf_set_value(cgi->hdf, buf, l);
    707     if (err) return nerr_pass(err);
    708     snprintf(buf, sizeof(buf), "%s.%d.path", prefix, n++);
    709     err = hdf_set_value(cgi->hdf, buf, album);
    710     if (err) return nerr_pass(err);
    711   }
    712 
    713   return STATUS_OK;
    714 }
    715 
    716 
    717 NEOERR *dowork_picture (CGI *cgi, char *album, char *picture)
    718 {
    719   NEOERR *err = STATUS_OK;
    720   char *base, *name;
    721   char path[_POSIX_PATH_MAX];
    722   char buf[256];
    723   int i, x, factor, y;
    724   int thumb_width, thumb_height;
    725   int pic_width, pic_height;
    726   ULIST *files = NULL;
    727   char t_album[_POSIX_PATH_MAX];
    728   char t_pic[_POSIX_PATH_MAX];
    729   char nfile[_POSIX_PATH_MAX];
    730   char *ch;
    731   char *avi = NULL;
    732   int rotate;
    733 
    734   ch = strrchr(picture, '/');
    735   if (ch != NULL)
    736   {
    737     *ch = '\0';
    738     snprintf(t_album, sizeof(t_album), "%s/%s", album, picture);
    739     *ch = '/';
    740     strncpy(t_pic, ch+1, sizeof(t_pic));
    741     picture = t_pic;
    742     album = t_album;
    743   }
    744 
    745   base = hdf_get_value (cgi->hdf, "BASEDIR", NULL);
    746   if (base == NULL)
    747   {
    748     cgi_error (cgi, "No BASEDIR in imd file");
    749     return nerr_raise(CGIFinished, "Finished");
    750   }
    751 
    752   thumb_width = hdf_get_int_value (cgi->hdf, "ThumbWidth", 120);
    753   thumb_height = hdf_get_int_value (cgi->hdf, "ThumbWidth", 90);
    754   pic_width = hdf_get_int_value (cgi->hdf, "PictureWidth", 120);
    755   pic_height = hdf_get_int_value (cgi->hdf, "PictureWidth", 90);
    756 
    757   err = hdf_set_value (cgi->hdf, "Context", "picture");
    758   if (err != STATUS_OK) return nerr_pass(err);
    759 
    760   snprintf (path, sizeof(path), "%s/%s", base, album);
    761   rotate = hdf_get_int_value(cgi->hdf, "Query.rotate", 0);
    762   if (rotate)
    763   {
    764     err = rotate_image(path, picture, rotate, nfile);
    765     if (err) return nerr_pass(err);
    766     picture = strrchr(nfile, '/') + 1;
    767   }
    768 
    769   err = hdf_set_value (cgi->hdf, "Album", album);
    770   if (err != STATUS_OK) return nerr_pass(err);
    771   err = hdf_set_value (cgi->hdf, "Album.Raw", album);
    772   if (err != STATUS_OK) return nerr_pass(err);
    773   err = export_album_path(cgi, album, "Album.Path");
    774   if (err) return nerr_pass(err);
    775   err = hdf_set_value (cgi->hdf, "Picture", picture);
    776   if (err != STATUS_OK) return nerr_pass(err);
    777 
    778   err = load_images(path, &files, NULL, 0);
    779   if (err != STATUS_OK) return nerr_pass(err);
    780   err = uListSort(files, alpha_sort);
    781   if (err != STATUS_OK) return nerr_pass(err);
    782 
    783   i = -1;
    784   for (x = 0; x < uListLength(files); x++)
    785   {
    786     err = uListGet(files, x, (void *)&name);
    787     if (err) break;
    788     if (!strcmp(name, picture))
    789     {
    790       i = x;
    791       break;
    792     }
    793   }
    794   if (i != -1)
    795   {
    796     for (x = 2; x > 0; x--)
    797     {
    798       if (i - x < 0) continue;
    799       err = uListGet(files, i-x, (void *)&name);
    800       if (err) break;
    801       snprintf(buf, sizeof(buf), "Show.%d", i-x);
    802       err = export_image(cgi, buf, path, name);
    803       if (err) break;
    804     }
    805     for (x = 0; x < 3; x++)
    806     {
    807       if (i + x > uListLength(files)) break;
    808       err = uListGet(files, i+x, (void *)&name);
    809       if (err) break;
    810       snprintf(buf, sizeof(buf), "Show.%d", i+x);
    811       err = export_image(cgi, buf, path, name);
    812       if (err) break;
    813     }
    814     snprintf (buf, sizeof(buf), "Show.%d.width", i);
    815     x = hdf_get_int_value (cgi->hdf, buf, -1);
    816     if (x != -1)
    817     {
    818       factor = 1;
    819       y = x;
    820       while (y > pic_width)
    821       {
    822 	factor = factor * 2;
    823 	/* factor++; */
    824 	y = x / factor;
    825 	ne_warn("factor = %d, y = %d", factor, y);
    826       }
    827       snprintf (buf, sizeof(buf), "%d", y);
    828       hdf_set_value (cgi->hdf, "Picture.width", buf);
    829       snprintf (buf, sizeof(buf), "Show.%d.height", i);
    830       x = hdf_get_int_value (cgi->hdf, buf, -1);
    831       y = x / factor;
    832       snprintf (buf, sizeof(buf), "%d", y);
    833       hdf_set_value (cgi->hdf, "Picture.height", buf);
    834     }
    835     else
    836     {
    837       snprintf (buf, sizeof(buf), "%d", pic_width);
    838       hdf_set_value (cgi->hdf, "Picture.width", buf);
    839       snprintf (buf, sizeof(buf), "%d", pic_height);
    840       hdf_set_value (cgi->hdf, "Picture.height", buf);
    841     }
    842     snprintf (buf, sizeof(buf), "Show.%d.avi", i);
    843     avi = hdf_get_value (cgi->hdf, buf, NULL);
    844     if (avi)
    845     {
    846       err = hdf_set_value(cgi->hdf, "Picture.avi", avi);
    847     }
    848 
    849     err = scale_images (cgi, "Show", thumb_width, thumb_height, 0);
    850   }
    851   uListDestroy(&files, ULIST_FREE);
    852 
    853   return nerr_pass(err);
    854 }
    855 
    856 static int is_album(void *rock, char *filename)
    857 {
    858   char path[_POSIX_PATH_MAX];
    859   char *prefix = (char *)rock;
    860 
    861   if (filename[0] == '.') return 0;
    862   snprintf(path, sizeof(path), "%s/%s", prefix, filename);
    863   if (isdir(path)) return 1;
    864   return 0;
    865 }
    866 
    867 NEOERR *dowork_album_overview (CGI *cgi, char *album)
    868 {
    869   NEOERR *err = STATUS_OK;
    870   DIR *dp;
    871   struct dirent *de;
    872   char path[_POSIX_PATH_MAX];
    873   char buf[256];
    874   int i = 0, x, y;
    875   int thumb_width, thumb_height;
    876   ULIST *files = NULL;
    877   ULIST *albums = NULL;
    878   char *name;
    879 
    880   thumb_width = hdf_get_int_value (cgi->hdf, "ThumbWidth", 120);
    881   thumb_height = hdf_get_int_value (cgi->hdf, "ThumbWidth", 90);
    882 
    883   err = ne_listdir_fmatch(album, &albums, is_album, album);
    884   if (err) return nerr_pass(err);
    885 
    886 
    887   err = uListSort(albums, alpha_sort);
    888   if (err) return nerr_pass(err);
    889   for (y = 0; y < uListLength(albums); y++)
    890   {
    891     err = uListGet(albums, y, (void *)&name);
    892     if (err) break;
    893 
    894     snprintf(path, sizeof(path), "%s/%s", album, name);
    895     snprintf(buf, sizeof(buf), "Albums.%d", i);
    896     err = hdf_set_value (cgi->hdf, buf, name);
    897     if (err != STATUS_OK) break;
    898     err = load_images(path, &files, NULL, 1);
    899     if (err != STATUS_OK) break;
    900     err = uListSort(files, alpha_sort);
    901     if (err != STATUS_OK) break;
    902     snprintf(buf, sizeof(buf), "Albums.%d.Count", i);
    903     err = hdf_set_int_value(cgi->hdf, buf, uListLength(files));
    904     if (err != STATUS_OK) break;
    905     for (x = 0; (x < 4) && (x < uListLength(files)); x++)
    906     {
    907       err = uListGet(files, x, (void *)&name);
    908       if (err) break;
    909       snprintf(buf, sizeof(buf), "Albums.%d.Images.%d", i, x);
    910       err = export_image(cgi, buf, path, name);
    911       if (err) break;
    912     }
    913     uListDestroy(&files, ULIST_FREE);
    914     if (err != STATUS_OK) break;
    915     snprintf(buf, sizeof(buf), "Albums.%d.Images", i);
    916     err = scale_images (cgi, buf, thumb_width, thumb_height, 0);
    917     if (err != STATUS_OK) break;
    918     i++;
    919   }
    920   return nerr_pass(err);
    921 }
    922 
    923 NEOERR *dowork_album (CGI *cgi, char *album)
    924 {
    925   NEOERR *err;
    926   char *base;
    927   char buf[256];
    928   char path[_POSIX_PATH_MAX];
    929   int thumb_width, thumb_height;
    930   int per_page, start, next, prev, last;
    931   ULIST *files = NULL;
    932   char *name;
    933   int x;
    934 
    935   base = hdf_get_value (cgi->hdf, "BASEDIR", NULL);
    936   if (base == NULL)
    937   {
    938     cgi_error (cgi, "No BASEDIR in imd file");
    939     return nerr_raise(CGIFinished, "Finished");
    940   }
    941   thumb_width = hdf_get_int_value (cgi->hdf, "ThumbWidth", 120);
    942   thumb_height = hdf_get_int_value (cgi->hdf, "ThumbWidth", 90);
    943   per_page = hdf_get_int_value (cgi->hdf, "PerPage", 50);
    944   start = hdf_get_int_value (cgi->hdf, "Query.start", 0);
    945 
    946   err = hdf_set_value (cgi->hdf, "Album", album);
    947   if (err != STATUS_OK) return nerr_pass(err);
    948   err = hdf_set_value (cgi->hdf, "Album.Raw", album);
    949   if (err != STATUS_OK) return nerr_pass(err);
    950   err = export_album_path(cgi, album, "Album.Path");
    951   if (err) return nerr_pass(err);
    952 
    953 
    954   err = hdf_set_value (cgi->hdf, "Context", "album");
    955   if (err != STATUS_OK) return nerr_pass(err);
    956 
    957 
    958   snprintf (path, sizeof(path), "%s/%s", base, album);
    959   err = dowork_album_overview(cgi, path);
    960   if (err != STATUS_OK) return nerr_pass(err);
    961 
    962   err = load_images(path, &files, NULL, 0);
    963   if (err != STATUS_OK) return nerr_pass (err);
    964   err = uListSort(files, alpha_sort);
    965   if (err != STATUS_OK) return nerr_pass (err);
    966   err = hdf_set_int_value(cgi->hdf, "Album.Count", uListLength(files));
    967   if (err != STATUS_OK) return nerr_pass (err);
    968   if (start > uListLength(files)) start = 0;
    969   next = start + per_page;
    970   if (next > uListLength(files)) next = uListLength(files);
    971   prev = start - per_page;
    972   if (prev < 0) prev = 0;
    973   last = uListLength(files) - per_page;
    974   if (last < 0) last = 0;
    975   err = hdf_set_int_value(cgi->hdf, "Album.Start", start);
    976   if (err != STATUS_OK) return nerr_pass (err);
    977   err = hdf_set_int_value(cgi->hdf, "Album.Next", next);
    978   if (err != STATUS_OK) return nerr_pass (err);
    979   err = hdf_set_int_value(cgi->hdf, "Album.Prev", prev);
    980   if (err != STATUS_OK) return nerr_pass (err);
    981   err = hdf_set_int_value(cgi->hdf, "Album.Last", last);
    982   if (err != STATUS_OK) return nerr_pass (err);
    983   for (x = start; x < next; x++)
    984   {
    985     err = uListGet(files, x, (void *)&name);
    986     if (err) break;
    987     snprintf(buf, sizeof(buf), "Images.%d", x);
    988     err = export_image(cgi, buf, path, name);
    989     if (err) break;
    990   }
    991   uListDestroy(&files, ULIST_FREE);
    992   if (err != STATUS_OK) return nerr_pass (err);
    993   err = scale_images (cgi, "Images", thumb_width, thumb_height, 0);
    994   if (err != STATUS_OK) return nerr_pass (err);
    995   return STATUS_OK;
    996 }
    997 
    998 NEOERR *dowork_image (CGI *cgi, char *image)
    999 {
   1000   NEOERR *err = STATUS_OK;
   1001   int maxW = 0, maxH = 0;
   1002   char *basepath = "";
   1003   char *cache_basepath = "/tmp/.imgcache/";
   1004   char srcpath[_POSIX_PATH_MAX] = "";
   1005   char cachepath[_POSIX_PATH_MAX] = "";
   1006   char buf[256];
   1007   char *if_mod;
   1008   int i, l, quality;
   1009   struct stat s;
   1010   struct tm *t;
   1011 
   1012   if ((i = hdf_get_int_value(cgi->hdf, "Query.width", 0)) != 0) {
   1013     maxW = i;
   1014   }
   1015 
   1016   if ((i = hdf_get_int_value(cgi->hdf, "Query.height", 0)) != 0) {
   1017     maxH = i;
   1018   }
   1019   quality = hdf_get_int_value(cgi->hdf, "Query.quality", 0);
   1020 
   1021   if_mod = hdf_get_value(cgi->hdf, "HTTP.IfModifiedSince", NULL);
   1022 
   1023   basepath = hdf_get_value(cgi->hdf, "BASEDIR", NULL);
   1024   if (basepath == NULL)
   1025   {
   1026     cgi_error (cgi, "No BASEDIR in imd file");
   1027     return nerr_raise(CGIFinished, "Finished");
   1028   }
   1029 
   1030   snprintf (srcpath, sizeof(srcpath), "%s/%s", basepath, image);
   1031   snprintf (cachepath, sizeof(cachepath), "%s/%dx%d/%s", cache_basepath,
   1032       maxW, maxH,image);
   1033 
   1034   if (stat(srcpath, &s))
   1035   {
   1036     cgiwrap_writef("Status: 404\nContent-Type: text/html\n\n");
   1037     cgiwrap_writef("File %s not found.", srcpath);
   1038     return nerr_raise_errno(NERR_IO, "Unable to stat file %s", srcpath);
   1039   }
   1040 
   1041   t = gmtime(&(s.st_mtime));
   1042   if (if_mod && later_than(t, if_mod))
   1043   {
   1044     cgiwrap_writef("Status: 304\nContent-Type: text/html\n\n");
   1045     cgiwrap_writef("Use Local Copy");
   1046     return STATUS_OK;
   1047   }
   1048 
   1049   /* fprintf(stderr,"cachepath: %s\n",cachepath); */
   1050 
   1051   ne_warn("srcpath: %s", srcpath);
   1052   l = strlen(srcpath);
   1053   if ((l>4 && !strcasecmp(srcpath+l-4, ".jpg")) ||
   1054       (l>4 && !strcasecmp(srcpath+l-4, ".thm")) ||
   1055       (l>5 && !strcasecmp(srcpath+l-5, ".jpeg")))
   1056     cgiwrap_writef("Content-Type: image/jpeg\n");
   1057   else if (l>4 && !strcasecmp(srcpath+l-4, ".gif"))
   1058     cgiwrap_writef("Content-Type: image/gif\n");
   1059   else if (l>4 && !strcasecmp(srcpath+l-4, ".avi"))
   1060   {
   1061     ne_warn("found avi");
   1062     cgiwrap_writef("Content-Type: video/x-msvideo\n");
   1063   }
   1064   t = gmtime(&(s.st_mtime));
   1065   strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", t);
   1066   cgiwrap_writef("Last-modified: %s\n", buf);
   1067 
   1068   err = scale_and_display_image(srcpath,maxW,maxH,cachepath,quality);
   1069   return nerr_pass(err);
   1070 }
   1071 
   1072 int main(int argc, char **argv, char **envp)
   1073 {
   1074   NEOERR *err;
   1075   CGI *cgi;
   1076   char *image;
   1077   char *album;
   1078   char *imd_file;
   1079   char *cs_file;
   1080   char *picture;
   1081 
   1082   ne_warn("Starting IMD");
   1083   cgi_debug_init (argc,argv);
   1084   cgiwrap_init_std (argc, argv, envp);
   1085 
   1086   nerr_init();
   1087 
   1088   ne_warn("CGI init");
   1089   err = cgi_init(&cgi, NULL);
   1090   if (err != STATUS_OK)
   1091   {
   1092     nerr_log_error(err);
   1093     cgi_destroy(&cgi);
   1094     return -1;
   1095   }
   1096   imd_file = hdf_get_value(cgi->hdf, "CGI.PathTranslated", NULL);
   1097   ne_warn("Reading IMD file %s", imd_file);
   1098   err = hdf_read_file (cgi->hdf, imd_file);
   1099   if (err != STATUS_OK)
   1100   {
   1101     cgi_neo_error(cgi, err);
   1102     nerr_log_error(err);
   1103     cgi_destroy(&cgi);
   1104     return -1;
   1105   }
   1106 
   1107   cs_file = hdf_get_value(cgi->hdf, "Template", NULL);
   1108   image = hdf_get_value(cgi->hdf, "Query.image", NULL);
   1109   album = hdf_get_value(cgi->hdf, "Query.album", "");
   1110   picture = hdf_get_value(cgi->hdf, "Query.picture", NULL);
   1111   if (image)
   1112   {
   1113     err = dowork_image(cgi, image);
   1114     if (err)
   1115     {
   1116       nerr_log_error(err);
   1117       cgi_destroy(&cgi);
   1118       return -1;
   1119     }
   1120   }
   1121   else
   1122   {
   1123     if (!picture)
   1124     {
   1125       err = dowork_album (cgi, album);
   1126     }
   1127     else
   1128     {
   1129       err = dowork_picture (cgi, album, picture);
   1130     }
   1131     if (err != STATUS_OK)
   1132     {
   1133       if (nerr_handle(&err, CGIFinished))
   1134       {
   1135 	/* pass */
   1136       }
   1137       else
   1138       {
   1139 	cgi_neo_error(cgi, err);
   1140 	nerr_log_error(err);
   1141         cgi_destroy(&cgi);
   1142 	return -1;
   1143       }
   1144     }
   1145     else
   1146     {
   1147       err = cgi_display(cgi, cs_file);
   1148       if (err != STATUS_OK)
   1149       {
   1150 	cgi_neo_error(cgi, err);
   1151 	nerr_log_error(err);
   1152         cgi_destroy(&cgi);
   1153 	return -1;
   1154       }
   1155     }
   1156   }
   1157   cgi_destroy(&cgi);
   1158   return 0;
   1159 }
   1160