MagickExport Image *DistortImage(const Image *image, DistortMethod method,
  const size_t number_arguments,const double *arguments,
  MagickBooleanType bestfit,ExceptionInfo *exception)
{
#define DistortImageTag  "Distort/Image"

  double
    *coeff,
    output_scaling;

  Image
    *distort_image;

  RectangleInfo
    geometry;  /* geometry of the distorted space viewport */

  MagickBooleanType
    viewport_given;

  PixelInfo
    invalid;  /* the color to assign when distort result is invalid */

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);

  /*
    Handle Special Compound Distortions
  */
  if ( method == ResizeDistortion )
    {
      if ( number_arguments != 2 )
        {
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                    "InvalidArgument","%s : '%s'","Resize",
                    "Invalid number of args: 2 only");
          return((Image *) NULL);
        }
      distort_image=DistortResizeImage(image,(size_t)arguments[0],
         (size_t)arguments[1], exception);
      return(distort_image);
    }

  /*
    Convert input arguments (usually as control points for reverse mapping)
    into mapping coefficients to apply the distortion.

    Note that some distortions are mapped to other distortions,
    and as such do not require specific code after this point.
  */
  coeff = GenerateCoefficients(image, &method, number_arguments,
      arguments, 0, exception);
  if ( coeff == (double *) NULL )
    return((Image *) NULL);

  /*
    Determine the size and offset for a 'bestfit' destination.
    Usally the four corners of the source image is enough.
  */

  /* default output image bounds, when no 'bestfit' is requested */
  geometry.width=image->columns;
  geometry.height=image->rows;
  geometry.x=0;
  geometry.y=0;

  if ( method == ArcDistortion ) {
    bestfit = MagickTrue;  /* always calculate a 'best fit' viewport */
  }

  /* Work out the 'best fit', (required for ArcDistortion) */
  if ( bestfit ) {
    PointInfo
      s,d,min,max;  /* source, dest coords --mapping--> min, max coords */

    MagickBooleanType
      fix_bounds = MagickTrue;   /* enlarge bounds for VP handling */

    s.x=s.y=min.x=max.x=min.y=max.y=0.0;   /* keep compiler happy */

/* defines to figure out the bounds of the distorted image */
#define InitalBounds(p) \
{ \
  /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
  min.x = max.x = p.x; \
  min.y = max.y = p.y; \
}
#define ExpandBounds(p) \
{ \
  /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
  min.x = MagickMin(min.x,p.x); \
  max.x = MagickMax(max.x,p.x); \
  min.y = MagickMin(min.y,p.y); \
  max.y = MagickMax(max.y,p.y); \
}

    switch (method)
    {
      case AffineDistortion:
      case RigidAffineDistortion:
      { double inverse[6];
        InvertAffineCoefficients(coeff, inverse);
        s.x = (double) image->page.x;
        s.y = (double) image->page.y;
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
        InitalBounds(d);
        s.x = (double) image->page.x+image->columns;
        s.y = (double) image->page.y;
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
        ExpandBounds(d);
        s.x = (double) image->page.x;
        s.y = (double) image->page.y+image->rows;
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
        ExpandBounds(d);
        s.x = (double) image->page.x+image->columns;
        s.y = (double) image->page.y+image->rows;
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
        ExpandBounds(d);
        break;
      }
      case PerspectiveDistortion:
      { double inverse[8], scale;
        InvertPerspectiveCoefficients(coeff, inverse);
        s.x = (double) image->page.x;
        s.y = (double) image->page.y;
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
        scale=PerceptibleReciprocal(scale);
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
        InitalBounds(d);
        s.x = (double) image->page.x+image->columns;
        s.y = (double) image->page.y;
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
        scale=PerceptibleReciprocal(scale);
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
        ExpandBounds(d);
        s.x = (double) image->page.x;
        s.y = (double) image->page.y+image->rows;
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
        scale=PerceptibleReciprocal(scale);
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
        ExpandBounds(d);
        s.x = (double) image->page.x+image->columns;
        s.y = (double) image->page.y+image->rows;
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
        scale=PerceptibleReciprocal(scale);
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
        ExpandBounds(d);
        break;
      }
      case ArcDistortion:
      { double a, ca, sa;
        /* Forward Map Corners */
        a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
        d.x = coeff[2]*ca;
        d.y = coeff[2]*sa;
        InitalBounds(d);
        d.x = (coeff[2]-coeff[3])*ca;
        d.y = (coeff[2]-coeff[3])*sa;
        ExpandBounds(d);
        a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
        d.x = coeff[2]*ca;
        d.y = coeff[2]*sa;
        ExpandBounds(d);
        d.x = (coeff[2]-coeff[3])*ca;
        d.y = (coeff[2]-coeff[3])*sa;
        ExpandBounds(d);
        /* Orthogonal points along top of arc */
        for( a=(double) (ceil((double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
               a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
          ca = cos(a); sa = sin(a);
          d.x = coeff[2]*ca;
          d.y = coeff[2]*sa;
          ExpandBounds(d);
        }
        /*
          Convert the angle_to_width and radius_to_height
          to appropriate scaling factors, to allow faster processing
          in the mapping function.
        */
        coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
        coeff[3] = (double)image->rows/coeff[3];
        break;
      }
      case PolarDistortion:
      {
        if (number_arguments < 2)
          coeff[2] = coeff[3] = 0.0;
        min.x = coeff[2]-coeff[0];
        max.x = coeff[2]+coeff[0];
        min.y = coeff[3]-coeff[0];
        max.y = coeff[3]+coeff[0];
        /* should be about 1.0 if Rmin = 0 */
        coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
        break;
      }
      case DePolarDistortion:
      {
        /* direct calculation as it needs to tile correctly
         * for reversibility in a DePolar-Polar cycle */
        fix_bounds = MagickFalse;
        geometry.x = geometry.y = 0;
        geometry.height = (size_t) ceil(coeff[0]-coeff[1]);
        geometry.width = (size_t) ceil((coeff[0]-coeff[1])*
          (coeff[5]-coeff[4])*0.5);
        /* correct scaling factors relative to new size */
        coeff[6]=(coeff[5]-coeff[4])*PerceptibleReciprocal(geometry.width); /* changed width */
        coeff[7]=(coeff[0]-coeff[1])*PerceptibleReciprocal(geometry.height); /* should be about 1.0 */
        break;
      }
      case Cylinder2PlaneDistortion:
      {
        /* direct calculation so center of distortion is either a pixel
         * center, or pixel edge. This allows for reversibility of the
         * distortion */
        geometry.x = geometry.y = 0;
        geometry.width = (size_t) ceil( 2.0*coeff[1]*tan(coeff[0]/2.0) );
        geometry.height = (size_t) ceil( 2.0*coeff[3]/cos(coeff[0]/2.0) );
        /* correct center of distortion relative to new size */
        coeff[4] = (double) geometry.width/2.0;
        coeff[5] = (double) geometry.height/2.0;
        fix_bounds = MagickFalse;
        break;
      }
      case Plane2CylinderDistortion:
      {
        /* direct calculation center is either pixel center, or pixel edge
         * so as to allow reversibility of the image distortion */
        geometry.x = geometry.y = 0;
        geometry.width = (size_t) ceil(coeff[0]*coeff[1]);  /* FOV * radius */
        geometry.height = (size_t) (2*coeff[3]);              /* input image height */
        /* correct center of distortion relative to new size */
        coeff[4] = (double) geometry.width/2.0;
        coeff[5] = (double) geometry.height/2.0;
        fix_bounds = MagickFalse;
        break;
      }
      case ShepardsDistortion:
      case BilinearForwardDistortion:
      case BilinearReverseDistortion:
#if 0
      case QuadrilateralDistortion:
#endif
      case PolynomialDistortion:
      case BarrelDistortion:
      case BarrelInverseDistortion:
      default:
        /* no calculated bestfit available for these distortions */
        bestfit = MagickFalse;
        fix_bounds = MagickFalse;
        break;
    }

    /* Set the output image geometry to calculated 'bestfit'.
       Yes this tends to 'over do' the file image size, ON PURPOSE!
       Do not do this for DePolar which needs to be exact for virtual tiling.
    */
    if ( fix_bounds ) {
      geometry.x = (ssize_t) floor(min.x-0.5);
      geometry.y = (ssize_t) floor(min.y-0.5);
      geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
      geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
    }

  }  /* end bestfit destination image calculations */

  /* The user provided a 'viewport' expert option which may
     overrides some parts of the current output image geometry.
     This also overrides its default 'bestfit' setting.
  */
  { const char *artifact=GetImageArtifact(image,"distort:viewport");
    viewport_given = MagickFalse;
    if ( artifact != (const char *) NULL ) {
      MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
      if (flags==NoValue)
        (void) ThrowMagickException(exception,GetMagickModule(),
             OptionWarning,"InvalidSetting","'%s' '%s'",
             "distort:viewport",artifact);
      else
        viewport_given = MagickTrue;
    }
  }

  /* Verbose output */
  if (IsStringTrue(GetImageArtifact(image,"verbose")) != MagickFalse) {
    ssize_t
       i;
    char image_gen[MagickPathExtent];
    const char *lookup;

    /* Set destination image size and virtual offset */
    if ( bestfit || viewport_given ) {
      (void) FormatLocaleString(image_gen,MagickPathExtent,
        "  -size %.20gx%.20g -page %+.20g%+.20g xc: +insert \\\n",
        (double) geometry.width,(double) geometry.height,(double) geometry.x,
        (double) geometry.y);
      lookup="v.p{xx-v.page.x-0.5,yy-v.page.y-0.5}";
    }
    else {
      image_gen[0] = '\0';             /* no destination to generate */
      lookup = "p{xx-page.x-0.5,yy-page.y-0.5}"; /* simplify lookup */
    }

    switch (method)
    {
      case AffineDistortion:
      case RigidAffineDistortion:
      {
        double
          *inverse;

        inverse=(double *) AcquireQuantumMemory(6,sizeof(*inverse));
        if (inverse == (double *) NULL)
          {
            coeff=(double *) RelinquishMagickMemory(coeff);
            (void) ThrowMagickException(exception,GetMagickModule(),
              ResourceLimitError,"MemoryAllocationFailed","%s","DistortImages");
            return((Image *) NULL);
          }
        InvertAffineCoefficients(coeff, inverse);
        CoefficientsToAffineArgs(inverse);
        (void) FormatLocaleFile(stderr, "Affine projection:\n");
        (void) FormatLocaleFile(stderr,
          "  -distort AffineProjection \\\n    '");
        for (i=0; i < 5; i++)
          (void) FormatLocaleFile(stderr, "%.*g,",GetMagickPrecision(),
            inverse[i]);
        (void) FormatLocaleFile(stderr, "%.*g'\n",GetMagickPrecision(),
          inverse[5]);
        (void) FormatLocaleFile(stderr,
          "Equivalent scale, rotation(deg), translation:\n");
        (void) FormatLocaleFile(stderr,"  %.*g,%.*g,%.*g,%.*g\n",
          GetMagickPrecision(),sqrt(inverse[0]*inverse[0]+
          inverse[1]*inverse[1]),GetMagickPrecision(),
          RadiansToDegrees(atan2(inverse[1],inverse[0])),
          GetMagickPrecision(),inverse[4],GetMagickPrecision(),inverse[5]);
        inverse=(double *) RelinquishMagickMemory(inverse);
        (void) FormatLocaleFile(stderr,"Affine distort, FX equivalent:\n");
        (void) FormatLocaleFile(stderr, "%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
        (void) FormatLocaleFile(stderr,"       xx=%+.*g*ii %+.*g*jj %+.*g;\n",
          GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
          GetMagickPrecision(),coeff[2]);
        (void) FormatLocaleFile(stderr,"       yy=%+.*g*ii %+.*g*jj %+.*g;\n",
          GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
          GetMagickPrecision(),coeff[5]);
        (void) FormatLocaleFile(stderr,"       %s' \\\n",lookup);
        break;
      }
      case PerspectiveDistortion:
      {
        double
          *inverse;

        inverse=(double *) AcquireQuantumMemory(8,sizeof(*inverse));
        if (inverse == (double *) NULL)
          {
            coeff=(double *) RelinquishMagickMemory(coeff);
            (void) ThrowMagickException(exception,GetMagickModule(),
              ResourceLimitError,"MemoryAllocationFailed","%s",
              "DistortCoefficients");
            return((Image *) NULL);
          }
        InvertPerspectiveCoefficients(coeff, inverse);
        (void) FormatLocaleFile(stderr,"Perspective Projection:\n");
        (void) FormatLocaleFile(stderr,
          "  -distort PerspectiveProjection \\\n      '");
        for (i=0; i < 4; i++)
          (void) FormatLocaleFile(stderr, "%.*g, ",GetMagickPrecision(),
            inverse[i]);
        (void) FormatLocaleFile(stderr, "\n       ");
        for ( ; i < 7; i++)
          (void) FormatLocaleFile(stderr, "%.*g, ",GetMagickPrecision(),
            inverse[i]);
        (void) FormatLocaleFile(stderr, "%.*g'\n",GetMagickPrecision(),
          inverse[7]);
        inverse=(double *) RelinquishMagickMemory(inverse);
        (void) FormatLocaleFile(stderr,"Perspective Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%.1024s",image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
        (void) FormatLocaleFile(stderr,"       rr=%+.*g*ii %+.*g*jj + 1;\n",
          GetMagickPrecision(),coeff[6],GetMagickPrecision(),coeff[7]);
        (void) FormatLocaleFile(stderr,
          "       xx=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
          GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
          GetMagickPrecision(),coeff[2]);
        (void) FormatLocaleFile(stderr,
          "       yy=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
          GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
          GetMagickPrecision(),coeff[5]);
        (void) FormatLocaleFile(stderr,"       rr%s0 ? %s : blue' \\\n",
          coeff[8] < 0.0 ? "<" : ">", lookup);
        break;
      }
      case BilinearForwardDistortion:
      {
        (void) FormatLocaleFile(stderr,"BilinearForward Mapping Equations:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,"    i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
          coeff[0],coeff[1],coeff[2],coeff[3]);
        (void) FormatLocaleFile(stderr,"    j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
          coeff[4],coeff[5],coeff[6],coeff[7]);
#if 0
        /* for debugging */
        (void) FormatLocaleFile(stderr, "   c8 = %+lf  c9 = 2*a = %+lf;\n",
            coeff[8], coeff[9]);
#endif
        (void) FormatLocaleFile(stderr,
          "BilinearForward Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",0.5-coeff[3],0.5-
          coeff[7]);
        (void) FormatLocaleFile(stderr,"       bb=%lf*ii %+lf*jj %+lf;\n",
          coeff[6], -coeff[2], coeff[8]);
        /* Handle Special degenerate (non-quadratic) or trapezoidal case */
        if (coeff[9] != 0)
          {
            (void) FormatLocaleFile(stderr,
              "       rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",-2*coeff[9],coeff[4],
              -coeff[0]);
          (void) FormatLocaleFile(stderr,
            "       yy=( -bb + sqrt(rt) ) / %lf;\n",coeff[9]);
          }
        else
          (void) FormatLocaleFile(stderr,"       yy=(%lf*ii%+lf*jj)/bb;\n",
            -coeff[4],coeff[0]);
        (void) FormatLocaleFile(stderr,
          "       xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",-coeff[1],coeff[0],
          coeff[2]);
        if ( coeff[9] != 0 )
          (void) FormatLocaleFile(stderr,"       (rt < 0 ) ? red : %s'\n",
            lookup);
        else
          (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
        break;
      }
      case BilinearReverseDistortion:
      {
#if 0
        (void) FormatLocaleFile(stderr, "Polynomial Projection Distort:\n");
        (void) FormatLocaleFile(stderr, "  -distort PolynomialProjection \\\n");
        (void) FormatLocaleFile(stderr, "      '1.5, %lf, %lf, %lf, %lf,\n",
            coeff[3], coeff[0], coeff[1], coeff[2]);
        (void) FormatLocaleFile(stderr, "            %lf, %lf, %lf, %lf'\n",
            coeff[7], coeff[4], coeff[5], coeff[6]);
#endif
        (void) FormatLocaleFile(stderr,
          "BilinearReverse Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
        (void) FormatLocaleFile(stderr,
          "       xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[0],coeff[1],
          coeff[2], coeff[3]);
        (void) FormatLocaleFile(stderr,
           "       yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[4],coeff[5],
           coeff[6], coeff[7]);
        (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
        break;
      }
      case PolynomialDistortion:
      {
        size_t nterms = (size_t) coeff[1];
        (void) FormatLocaleFile(stderr,
          "Polynomial (order %lg, terms %lu), FX Equivelent\n",coeff[0],
          (unsigned long) nterms);
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
        (void) FormatLocaleFile(stderr, "       xx =");
        for (i=0; i < (ssize_t) nterms; i++)
        {
          if ((i != 0) && (i%4 == 0))
            (void) FormatLocaleFile(stderr, "\n         ");
          (void) FormatLocaleFile(stderr," %+lf%s",coeff[2+i],
            poly_basis_str(i));
        }
        (void) FormatLocaleFile(stderr,";\n       yy =");
        for (i=0; i < (ssize_t) nterms; i++)
        {
          if ((i != 0) && (i%4 == 0))
            (void) FormatLocaleFile(stderr,"\n         ");
          (void) FormatLocaleFile(stderr," %+lf%s",coeff[2+i+nterms],
            poly_basis_str(i));
        }
        (void) FormatLocaleFile(stderr,";\n       %s' \\\n", lookup);
        break;
      }
      case ArcDistortion:
      {
        (void) FormatLocaleFile(stderr,"Arc Distort, Internal Coefficients:\n");
        for (i=0; i < 5; i++)
          (void) FormatLocaleFile(stderr,
            "  c%.20g = %+lf\n",(double) i,coeff[i]);
        (void) FormatLocaleFile(stderr,"Arc Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,"  -fx 'ii=i+page.x; jj=j+page.y;\n");
        (void) FormatLocaleFile(stderr,"       xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
          -coeff[0]);
        (void) FormatLocaleFile(stderr,"       xx=xx-round(xx);\n");
        (void) FormatLocaleFile(stderr,"       xx=xx*%lf %+lf;\n",coeff[1],
          coeff[4]);
        (void) FormatLocaleFile(stderr,
          "       yy=(%lf - hypot(ii,jj)) * %lf;\n",coeff[2],coeff[3]);
        (void) FormatLocaleFile(stderr,"       v.p{xx-.5,yy-.5}' \\\n");
        break;
      }
      case PolarDistortion:
      {
        (void) FormatLocaleFile(stderr,"Polar Distort, Internal Coefficents\n");
        for (i=0; i < 8; i++)
          (void) FormatLocaleFile(stderr,"  c%.20g = %+lf\n",(double) i,
            coeff[i]);
        (void) FormatLocaleFile(stderr,"Polar Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",-coeff[2],-coeff[3]);
        (void) FormatLocaleFile(stderr,"       xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
          -(coeff[4]+coeff[5])/2 );
        (void) FormatLocaleFile(stderr,"       xx=xx-round(xx);\n");
        (void) FormatLocaleFile(stderr,"       xx=xx*2*pi*%lf + v.w/2;\n",
          coeff[6] );
        (void) FormatLocaleFile(stderr,"       yy=(hypot(ii,jj)%+lf)*%lf;\n",
          -coeff[1],coeff[7] );
        (void) FormatLocaleFile(stderr,"       v.p{xx-.5,yy-.5}' \\\n");
        break;
      }
      case DePolarDistortion:
      {
        (void) FormatLocaleFile(stderr,
          "DePolar Distort, Internal Coefficents\n");
        for (i=0; i < 8; i++)
          (void) FormatLocaleFile(stderr,"  c%.20g = %+lf\n",(double) i,
            coeff[i]);
        (void) FormatLocaleFile(stderr,"DePolar Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,"  -fx 'aa=(i+.5)*%lf %+lf;\n",
          coeff[6],+coeff[4]);
        (void) FormatLocaleFile(stderr,"       rr=(j+.5)*%lf %+lf;\n",
          coeff[7],+coeff[1]);
        (void) FormatLocaleFile(stderr,"       xx=rr*sin(aa) %+lf;\n",
          coeff[2]);
        (void) FormatLocaleFile(stderr,"       yy=rr*cos(aa) %+lf;\n",
          coeff[3]);
        (void) FormatLocaleFile(stderr,"       v.p{xx-.5,yy-.5}' \\\n");
        break;
      }
      case Cylinder2PlaneDistortion:
      {
        (void) FormatLocaleFile(stderr,
          "Cylinder to Plane Distort, Internal Coefficents\n");
        (void) FormatLocaleFile(stderr,"  cylinder_radius = %+lf\n",coeff[1]);
        (void) FormatLocaleFile(stderr,
          "Cylinder to Plane Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr, "%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
          -coeff[5]);
        (void) FormatLocaleFile(stderr,"       aa=atan(ii/%+lf);\n",coeff[1]);
        (void) FormatLocaleFile(stderr,"       xx=%lf*aa%+lf;\n",
          coeff[1],coeff[2]);
        (void) FormatLocaleFile(stderr,"       yy=jj*cos(aa)%+lf;\n",coeff[3]);
        (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
        break;
      }
      case Plane2CylinderDistortion:
      {
        (void) FormatLocaleFile(stderr,
          "Plane to Cylinder Distort, Internal Coefficents\n");
        (void) FormatLocaleFile(stderr,"  cylinder_radius = %+lf\n",coeff[1]);
        (void) FormatLocaleFile(stderr,
          "Plane to Cylinder Distort, FX Equivelent:\n");
        (void) FormatLocaleFile(stderr,"%s", image_gen);
        (void) FormatLocaleFile(stderr,
          "  -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
          -coeff[5]);
        (void) FormatLocaleFile(stderr,"       ii=ii/%+lf;\n",coeff[1]);
        (void) FormatLocaleFile(stderr,"       xx=%lf*tan(ii)%+lf;\n",coeff[1],
          coeff[2] );
        (void) FormatLocaleFile(stderr,"       yy=jj/cos(ii)%+lf;\n",coeff[3]);
        (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
        break;
      }
      case BarrelDistortion:
      case BarrelInverseDistortion:
      {
        double
          xc,
          yc;

        /*
          NOTE: This does the barrel roll in pixel coords not image coords
          The internal distortion must do it in image coordinates,
          so that is what the center coeff (8,9) is given in.
        */
        xc=((double)image->columns-1.0)/2.0+image->page.x;
        yc=((double)image->rows-1.0)/2.0+image->page.y;
        (void) FormatLocaleFile(stderr, "Barrel%s Distort, FX Equivelent:\n",
          method == BarrelDistortion ? "" : "Inv");
        (void) FormatLocaleFile(stderr, "%s", image_gen);
        if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
          (void) FormatLocaleFile(stderr,"  -fx 'xc=(w-1)/2;  yc=(h-1)/2;\n");
        else
          (void) FormatLocaleFile(stderr,"  -fx 'xc=%lf;  yc=%lf;\n",coeff[8]-
            0.5,coeff[9]-0.5);
        (void) FormatLocaleFile(stderr,
          "       ii=i-xc;  jj=j-yc;  rr=hypot(ii,jj);\n");
        (void) FormatLocaleFile(stderr,
          "       ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
          method == BarrelDistortion ? "*" : "/",coeff[0],coeff[1],coeff[2],
          coeff[3]);
        (void) FormatLocaleFile(stderr,
          "       jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
          method == BarrelDistortion ? "*" : "/",coeff[4],coeff[5],coeff[6],
          coeff[7]);
        (void) FormatLocaleFile(stderr,"       v.p{fx*ii+xc,fy*jj+yc}' \\\n");
      }
      default:
        break;
    }
  }
  /*
    The user provided a 'scale' expert option will scale the output image size,
    by the factor given allowing for super-sampling of the distorted image
    space.  Any scaling factors must naturally be halved as a result.
  */
  { const char *artifact;
    artifact=GetImageArtifact(image,"distort:scale");
    output_scaling = 1.0;
    if (artifact != (const char *) NULL) {
      output_scaling = fabs(StringToDouble(artifact,(char **) NULL));
      geometry.width=(size_t) (output_scaling*geometry.width+0.5);
      geometry.height=(size_t) (output_scaling*geometry.height+0.5);
      geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
      geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
      if ( output_scaling < 0.1 ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                "InvalidArgument","%s", "-set option:distort:scale" );
        return((Image *) NULL);
      }
      output_scaling = 1/output_scaling;
    }
  }
#define ScaleFilter(F,A,B,C,D) \
    ScaleResampleFilter( (F), \
      output_scaling*(A), output_scaling*(B), \
      output_scaling*(C), output_scaling*(D) )

  /*
    Initialize the distort image attributes.
  */
  distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
    exception);
  if (distort_image == (Image *) NULL)
    {
      coeff=(double *) RelinquishMagickMemory(coeff);
      return((Image *) NULL);
    }
  /* if image is ColorMapped - change it to DirectClass */
  if (SetImageStorageClass(distort_image,DirectClass,exception) == MagickFalse)
    {
      coeff=(double *) RelinquishMagickMemory(coeff);
      distort_image=DestroyImage(distort_image);
      return((Image *) NULL);
    }
  if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
      (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
    (void) SetImageColorspace(distort_image,sRGBColorspace,exception);
  if (distort_image->background_color.alpha_trait != UndefinedPixelTrait)
    distort_image->alpha_trait=BlendPixelTrait;
  distort_image->page.x=geometry.x;
  distort_image->page.y=geometry.y;
  ConformPixelInfo(distort_image,&distort_image->matte_color,&invalid,
    exception);

  { /* ----- MAIN CODE -----
       Sample the source image to each pixel in the distort image.
     */
    CacheView
      *distort_view;

    MagickBooleanType
      status;

    MagickOffsetType
      progress;

    PixelInfo
      zero;

    ResampleFilter
      **magick_restrict resample_filter;

    ssize_t
      j;

    status=MagickTrue;
    progress=0;
    GetPixelInfo(distort_image,&zero);
    resample_filter=AcquireResampleFilterThreadSet(image,
      UndefinedVirtualPixelMethod,MagickFalse,exception);
    distort_view=AcquireAuthenticCacheView(distort_image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
    #pragma omp parallel for schedule(static) shared(progress,status) \
      magick_number_threads(image,distort_image,distort_image->rows,1)
#endif
    for (j=0; j < (ssize_t) distort_image->rows; j++)
    {
      const int
        id = GetOpenMPThreadId();

      double
        validity;  /* how mathematically valid is this the mapping */

      MagickBooleanType
        sync;

      PixelInfo
        pixel;    /* pixel color to assign to distorted image */

      PointInfo
        d,
        s;  /* transform destination image x,y  to source image x,y */

      ssize_t
        i;

      Quantum
        *magick_restrict q;

      q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
        exception);
      if (q == (Quantum *) NULL)
        {
          status=MagickFalse;
          continue;
        }
      pixel=zero;

      /* Define constant scaling vectors for Affine Distortions
        Other methods are either variable, or use interpolated lookup
      */
      switch (method)
      {
        case AffineDistortion:
        case RigidAffineDistortion:
          ScaleFilter( resample_filter[id],
            coeff[0], coeff[1],
            coeff[3], coeff[4] );
          break;
        default:
          break;
      }

      /* Initialize default pixel validity
      *    negative:         pixel is invalid  output 'matte_color'
      *    0.0 to 1.0:       antialiased, mix with resample output
      *    1.0 or greater:   use resampled output.
      */
      validity = 1.0;

      for (i=0; i < (ssize_t) distort_image->columns; i++)
      {
        /* map pixel coordinate to distortion space coordinate */
        d.x = (double) (geometry.x+i+0.5)*output_scaling;
        d.y = (double) (geometry.y+j+0.5)*output_scaling;
        s = d;  /* default is a no-op mapping */
        switch (method)
        {
          case AffineDistortion:
          case RigidAffineDistortion:
          {
            s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
            s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
            /* Affine partial derivitives are constant -- set above */
            break;
          }
          case PerspectiveDistortion:
          {
            double
              p,q,r,abs_r,abs_c6,abs_c7,scale;
            /* perspective is a ratio of affines */
            p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
            q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
            r=coeff[6]*d.x+coeff[7]*d.y+1.0;
            /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
            validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
            /* Determine horizon anti-alias blending */
            abs_r = fabs(r)*2;
            abs_c6 = fabs(coeff[6]);
            abs_c7 = fabs(coeff[7]);
            if ( abs_c6 > abs_c7 ) {
              if ( abs_r < abs_c6*output_scaling )
                validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
            }
            else if ( abs_r < abs_c7*output_scaling )
              validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
            /* Perspective Sampling Point (if valid) */
            if ( validity > 0.0 ) {
              /* divide by r affine, for perspective scaling */
              scale = 1.0/r;
              s.x = p*scale;
              s.y = q*scale;
              /* Perspective Partial Derivatives or Scaling Vectors */
              scale *= scale;
              ScaleFilter( resample_filter[id],
                (r*coeff[0] - p*coeff[6])*scale,
                (r*coeff[1] - p*coeff[7])*scale,
                (r*coeff[3] - q*coeff[6])*scale,
                (r*coeff[4] - q*coeff[7])*scale );
            }
            break;
          }
          case BilinearReverseDistortion:
          {
            /* Reversed Mapped is just a simple polynomial */
            s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
            s.y=coeff[4]*d.x+coeff[5]*d.y
                    +coeff[6]*d.x*d.y+coeff[7];
            /* Bilinear partial derivitives of scaling vectors */
            ScaleFilter( resample_filter[id],
                coeff[0] + coeff[2]*d.y,
                coeff[1] + coeff[2]*d.x,
                coeff[4] + coeff[6]*d.y,
                coeff[5] + coeff[6]*d.x );
            break;
          }
          case BilinearForwardDistortion:
          {
            /* Forward mapped needs reversed polynomial equations
             * which unfortunatally requires a square root!  */
            double b,c;
            d.x -= coeff[3];  d.y -= coeff[7];
            b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
            c = coeff[4]*d.x - coeff[0]*d.y;

            validity = 1.0;
            /* Handle Special degenerate (non-quadratic) case
             * Currently without horizon anti-alising */
            if ( fabs(coeff[9]) < MagickEpsilon )
              s.y =  -c/b;
            else {
              c = b*b - 2*coeff[9]*c;
              if ( c < 0.0 )
                validity = 0.0;
              else
                s.y = ( -b + sqrt(c) )/coeff[9];
            }
            if ( validity > 0.0 )
              s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );

            /* NOTE: the sign of the square root should be -ve for parts
                     where the source image becomes 'flipped' or 'mirrored'.
               FUTURE: Horizon handling
               FUTURE: Scaling factors or Deritives (how?)
            */
            break;
          }
#if 0
          case BilinearDistortion:
            /* Bilinear mapping of any Quadrilateral to any Quadrilateral */
            /* UNDER DEVELOPMENT */
            break;
#endif
          case PolynomialDistortion:
          {
            /* multi-ordered polynomial */
            ssize_t
              k;

            ssize_t
              nterms=(ssize_t)coeff[1];

            PointInfo
              du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */

            s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
            for(k=0; k < nterms; k++) {
              s.x  += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
              du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
              du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
              s.y  += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
              dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
              dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
            }
            ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
            break;
          }
          case ArcDistortion:
          {
            /* what is the angle and radius in the destination image */
            s.x  = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
            s.x -= MagickRound(s.x);     /* angle */
            s.y  = hypot(d.x,d.y);       /* radius */

            /* Arc Distortion Partial Scaling Vectors
              Are derived by mapping the perpendicular unit vectors
              dR  and  dA*R*2PI  rather than trying to map dx and dy
              The results is a very simple orthogonal aligned ellipse.
            */
            if ( s.y > MagickEpsilon )
              ScaleFilter( resample_filter[id],
                  (double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
            else
              ScaleFilter( resample_filter[id],
                  distort_image->columns*2, 0, 0, coeff[3] );

            /* now scale the angle and radius for source image lookup point */
            s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
            s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
            break;
          }
          case PolarDistortion:
          { /* 2D Cartesain to Polar View */
            d.x -= coeff[2];
            d.y -= coeff[3];
            s.x  = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
            s.x /= Magick2PI;
            s.x -= MagickRound(s.x);
            s.x *= Magick2PI;       /* angle - relative to centerline */
            s.y  = hypot(d.x,d.y);  /* radius */

            /* Polar Scaling vectors are based on mapping dR and dA vectors
               This results in very simple orthogonal scaling vectors
            */
            if ( s.y > MagickEpsilon )
              ScaleFilter( resample_filter[id],
                (double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
            else
              ScaleFilter( resample_filter[id],
                  distort_image->columns*2, 0, 0, coeff[7] );

            /* now finish mapping radius/angle to source x,y coords */
            s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
            s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
            break;
          }
          case DePolarDistortion:
          { /* @D Polar to Carteasain  */
            /* ignore all destination virtual offsets */
            d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
            d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
            s.x = d.y*sin(d.x) + coeff[2];
            s.y = d.y*cos(d.x) + coeff[3];
            /* derivatives are usless - better to use SuperSampling */
            break;
          }
          case Cylinder2PlaneDistortion:
          { /* 3D Cylinder to Tangential Plane */
            double ax, cx;
            /* relative to center of distortion */
            d.x -= coeff[4]; d.y -= coeff[5];
            d.x /= coeff[1];        /* x' = x/r */
            ax=atan(d.x);           /* aa = atan(x/r) = u/r  */
            cx=cos(ax);             /* cx = cos(atan(x/r)) = 1/sqrt(x^2+u^2) */
            s.x = coeff[1]*ax;      /* u  = r*atan(x/r) */
            s.y = d.y*cx;           /* v  = y*cos(u/r) */
            /* derivatives... (see personnal notes) */
            ScaleFilter( resample_filter[id],
                  1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
#if 0
if ( i == 0 && j == 0 ) {
  fprintf(stderr, "x=%lf  y=%lf  u=%lf  v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
  fprintf(stderr, "phi = %lf\n", (double)(ax * 180.0/MagickPI) );
  fprintf(stderr, "du/dx=%lf  du/dx=%lf  dv/dx=%lf  dv/dy=%lf\n",
                1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
  fflush(stderr); }
#endif
            /* add center of distortion in source */
            s.x += coeff[2]; s.y += coeff[3];
            break;
          }
          case Plane2CylinderDistortion:
          { /* 3D Cylinder to Tangential Plane */
            /* relative to center of distortion */
            d.x -= coeff[4]; d.y -= coeff[5];

            /* is pixel valid - horizon of a infinite Virtual-Pixel Plane
             * (see Anthony Thyssen's personal note) */
            validity = (double) (coeff[1]*MagickPI2 - fabs(d.x))/output_scaling + 0.5;

            if ( validity > 0.0 ) {
              double cx,tx;
              d.x /= coeff[1];           /* x'= x/r */
              cx = 1/cos(d.x);           /* cx = 1/cos(x/r) */
              tx = tan(d.x);             /* tx = tan(x/r) */
              s.x = coeff[1]*tx;         /* u = r * tan(x/r) */
              s.y = d.y*cx;              /* v = y / cos(x/r) */
              /* derivatives...  (see Anthony Thyssen's personal notes) */
              ScaleFilter( resample_filter[id],
                    cx*cx, 0.0, s.y*cx/coeff[1], cx );
#if 0
/*if ( i == 0 && j == 0 )*/
if ( d.x == 0.5 && d.y == 0.5 ) {
  fprintf(stderr, "x=%lf  y=%lf  u=%lf  v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
  fprintf(stderr, "radius = %lf  phi = %lf  validity = %lf\n",
      coeff[1],  (double)(d.x * 180.0/MagickPI), validity );
  fprintf(stderr, "du/dx=%lf  du/dx=%lf  dv/dx=%lf  dv/dy=%lf\n",
      cx*cx, 0.0, s.y*cx/coeff[1], cx);
  fflush(stderr); }
#endif
            }
            /* add center of distortion in source */
            s.x += coeff[2]; s.y += coeff[3];
            break;
          }
          case BarrelDistortion:
          case BarrelInverseDistortion:
          { /* Lens Barrel Distionion Correction */
            double r,fx,fy,gx,gy;
            /* Radial Polynomial Distortion (de-normalized) */
            d.x -= coeff[8];
            d.y -= coeff[9];
            r = sqrt(d.x*d.x+d.y*d.y);
            if ( r > MagickEpsilon ) {
              fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
              fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
              gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
              gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
              /* adjust functions and scaling for 'inverse' form */
              if ( method == BarrelInverseDistortion ) {
                fx = 1/fx;  fy = 1/fy;
                gx *= -fx*fx;  gy *= -fy*fy;
              }
              /* Set the source pixel to lookup and EWA derivative vectors */
              s.x = d.x*fx + coeff[8];
              s.y = d.y*fy + coeff[9];
              ScaleFilter( resample_filter[id],
                  gx*d.x*d.x + fx, gx*d.x*d.y,
                  gy*d.x*d.y,      gy*d.y*d.y + fy );
            }
            else {
              /* Special handling to avoid divide by zero when r==0
              **
              ** The source and destination pixels match in this case
              ** which was set at the top of the loop using  s = d;
              ** otherwise...   s.x=coeff[8]; s.y=coeff[9];
              */
              if ( method == BarrelDistortion )
                ScaleFilter( resample_filter[id],
                     coeff[3], 0, 0, coeff[7] );
              else /* method == BarrelInverseDistortion */
                /* FUTURE, trap for D==0 causing division by zero */
                ScaleFilter( resample_filter[id],
                     1.0/coeff[3], 0, 0, 1.0/coeff[7] );
            }
            break;
          }
          case ShepardsDistortion:
          { /* Shepards Method, or Inverse Weighted Distance for
               displacement around the destination image control points
               The input arguments are the coefficents to the function.
               This is more of a 'displacement' function rather than an
               absolute distortion function.

               Note: We can not determine derivatives using shepards method
               so only a point sample interpolatation can be used.
            */
            size_t
              i;
            double
              denominator;

            denominator = s.x = s.y = 0;
            for(i=0; i<number_arguments; i+=4) {
              double weight =
                  ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
                + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
              weight = pow(weight,coeff[0]); /* shepards power factor */
              weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;

              s.x += (arguments[ i ]-arguments[i+2])*weight;
              s.y += (arguments[i+1]-arguments[i+3])*weight;
              denominator += weight;
            }
            s.x /= denominator;
            s.y /= denominator;
            s.x += d.x;   /* make it as relative displacement */
            s.y += d.y;
            break;
          }
          default:
            break; /* use the default no-op given above */
        }
        /* map virtual canvas location back to real image coordinate */
        if ( bestfit && method != ArcDistortion ) {
          s.x -= image->page.x;
          s.y -= image->page.y;
        }
        s.x -= 0.5;
        s.y -= 0.5;

        if ( validity <= 0.0 ) {
          /* result of distortion is an invalid pixel - don't resample */
          SetPixelViaPixelInfo(distort_image,&invalid,q);
        }
        else {
          /* resample the source image to find its correct color */
          (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel,
            exception);
          /* if validity between 0.0 and 1.0 mix result with invalid pixel */
          if ( validity < 1.0 ) {
            /* Do a blend of sample color and invalid pixel */
            /* should this be a 'Blend', or an 'Over' compose */
            CompositePixelInfoBlend(&pixel,validity,&invalid,(1.0-validity),
              &pixel);
          }
          SetPixelViaPixelInfo(distort_image,&pixel,q);
        }
        q+=GetPixelChannels(distort_image);
      }
      sync=SyncCacheViewAuthenticPixels(distort_view,exception);
      if (sync == MagickFalse)
        status=MagickFalse;
      if (image->progress_monitor != (MagickProgressMonitor) NULL)
        {
          MagickBooleanType
            proceed;

#if defined(MAGICKCORE_OPENMP_SUPPORT)
          #pragma omp atomic
#endif
          progress++;
          proceed=SetImageProgress(image,DistortImageTag,progress,image->rows);
          if (proceed == MagickFalse)
            status=MagickFalse;
        }
    }
    distort_view=DestroyCacheView(distort_view);
    resample_filter=DestroyResampleFilterThreadSet(resample_filter);

    if (status == MagickFalse)
      distort_image=DestroyImage(distort_image);
  }

  /* Arc does not return an offset unless 'bestfit' is in effect
     And the user has not provided an overriding 'viewport'.
   */
  if ( method == ArcDistortion && !bestfit && !viewport_given ) {
    distort_image->page.x = 0;
    distort_image->page.y = 0;
  }
  coeff=(double *) RelinquishMagickMemory(coeff);
  return(distort_image);
}


static const char *poly_basis_str(ssize_t n)
{
  /* return the result for this polynomial term */
  switch(n) {
    case  0:  return(""); /* constant */
    case  1:  return("*ii");
    case  2:  return("*jj"); /* affine                order = 1   terms = 3 */
    case  3:  return("*ii*jj"); /* bilinear           order = 1.5 terms = 4 */
    case  4:  return("*ii*ii");
    case  5:  return("*jj*jj"); /* quadratic          order = 2   terms = 6 */
    case  6:  return("*ii*ii*ii");
    case  7:  return("*ii*ii*jj");
    case  8:  return("*ii*jj*jj");
    case  9:  return("*jj*jj*jj"); /* cubic           order = 3   terms = 10 */
    case 10:  return("*ii*ii*ii*ii");
    case 11:  return("*ii*ii*ii*jj");
    case 12:  return("*ii*ii*jj*jj");
    case 13:  return("*ii*jj*jj*jj");
    case 14:  return("*jj*jj*jj*jj"); /* quartic      order = 4   terms = 15 */
    case 15:  return("*ii*ii*ii*ii*ii");
    case 16:  return("*ii*ii*ii*ii*jj");
    case 17:  return("*ii*ii*ii*jj*jj");
    case 18:  return("*ii*ii*jj*jj*jj");
    case 19:  return("*ii*jj*jj*jj*jj");
    case 20:  return("*jj*jj*jj*jj*jj"); /* quintic   order = 5   terms = 21 */
  }
  return( "UNKNOWN" ); /* should never happen */
}


static double poly_basis_fn(ssize_t n, double x, double y)
{
  /* Return the result for this polynomial term */
  switch(n) {
    case  0:  return( 1.0 ); /* constant */
    case  1:  return(  x  );
    case  2:  return(  y  ); /* affine          order = 1   terms = 3 */
    case  3:  return( x*y ); /* bilinear        order = 1.5 terms = 4 */
    case  4:  return( x*x );
    case  5:  return( y*y ); /* quadratic       order = 2   terms = 6 */
    case  6:  return( x*x*x );
    case  7:  return( x*x*y );
    case  8:  return( x*y*y );
    case  9:  return( y*y*y ); /* cubic         order = 3   terms = 10 */
    case 10:  return( x*x*x*x );
    case 11:  return( x*x*x*y );
    case 12:  return( x*x*y*y );
    case 13:  return( x*y*y*y );
    case 14:  return( y*y*y*y ); /* quartic     order = 4   terms = 15 */
    case 15:  return( x*x*x*x*x );
    case 16:  return( x*x*x*x*y );
    case 17:  return( x*x*x*y*y );
    case 18:  return( x*x*y*y*y );
    case 19:  return( x*y*y*y*y );
    case 20:  return( y*y*y*y*y ); /* quintic   order = 5   terms = 21 */
  }
  return( 0 ); /* should never happen */
}


static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
	struct sock *sk = sock->sk;
	struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
	struct nfc_llcp_local *local;
	struct nfc_dev *dev;
	struct sockaddr_nfc_llcp llcp_addr;
	int len, ret = 0;

	if (!addr || alen < offsetofend(struct sockaddr, sa_family) ||
	    addr->sa_family != AF_NFC)
		return -EINVAL;

	pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);

	memset(&llcp_addr, 0, sizeof(llcp_addr));
	len = min_t(unsigned int, sizeof(llcp_addr), alen);
	memcpy(&llcp_addr, addr, len);

	/* This is going to be a listening socket, dsap must be 0 */
	if (llcp_addr.dsap != 0)
		return -EINVAL;

	lock_sock(sk);

	if (sk->sk_state != LLCP_CLOSED) {
		ret = -EBADFD;
		goto error;
	}

	dev = nfc_get_device(llcp_addr.dev_idx);
	if (dev == NULL) {
		ret = -ENODEV;
		goto error;
	}

	local = nfc_llcp_find_local(dev);
	if (local == NULL) {
		ret = -ENODEV;
		goto put_dev;
	}

	llcp_sock->dev = dev;
	llcp_sock->local = nfc_llcp_local_get(local);
	llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
	llcp_sock->service_name_len = min_t(unsigned int,
					    llcp_addr.service_name_len,
					    NFC_LLCP_MAX_SERVICE_NAME);
	llcp_sock->service_name = kmemdup(llcp_addr.service_name,
					  llcp_sock->service_name_len,
					  GFP_KERNEL);
	if (!llcp_sock->service_name) {
		nfc_llcp_local_put(llcp_sock->local);
		llcp_sock->local = NULL;
		llcp_sock->dev = NULL;
		ret = -ENOMEM;
		goto put_dev;
	}
	llcp_sock->ssap = nfc_llcp_get_sdp_ssap(local, llcp_sock);
	if (llcp_sock->ssap == LLCP_SAP_MAX) {
		nfc_llcp_local_put(llcp_sock->local);
		llcp_sock->local = NULL;
		kfree(llcp_sock->service_name);
		llcp_sock->service_name = NULL;
		llcp_sock->dev = NULL;
		ret = -EADDRINUSE;
		goto put_dev;
	}

	llcp_sock->reserved_ssap = llcp_sock->ssap;

	nfc_llcp_sock_link(&local->sockets, sk);

	pr_debug("Socket bound to SAP %d\n", llcp_sock->ssap);

	sk->sk_state = LLCP_BOUND;

put_dev:
	nfc_put_device(dev);

error:
	release_sock(sk);
	return ret;
}

nfc: fix NULL ptr dereference in llcp_sock_getname() after failed connect

It's possible to trigger NULL pointer dereference by local unprivileged
user, when calling getsockname() after failed bind() (e.g. the bind
fails because LLCP_SAP_MAX used as SAP):

  BUG: kernel NULL pointer dereference, address: 0000000000000000
  CPU: 1 PID: 426 Comm: llcp_sock_getna Not tainted 5.13.0-rc2-next-20210521+ #9
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-1 04/01/2014
  Call Trace:
   llcp_sock_getname+0xb1/0xe0
   __sys_getpeername+0x95/0xc0
   ? lockdep_hardirqs_on_prepare+0xd5/0x180
   ? syscall_enter_from_user_mode+0x1c/0x40
   __x64_sys_getpeername+0x11/0x20
   do_syscall_64+0x36/0x70
   entry_SYSCALL_64_after_hwframe+0x44/0xae

This can be reproduced with Syzkaller C repro (bind followed by
getpeername):
https://syzkaller.appspot.com/x/repro.c?x=14def446e00000

Cc: <stable@vger.kernel.org>
Fixes: d646960f7986 ("NFC: Initial LLCP support")
Reported-by: syzbot+80fb126e7f7d8b1a5914@syzkaller.appspotmail.com
Reported-by: butt3rflyh4ck <butterflyhuangxx@gmail.com>
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
Link: https://lore.kernel.org/r/20210531072138.5219-1-krzysztof.kozlowski@canonical.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

static struct proto llcp_sock_proto = {
	.name     = "NFC_LLCP",
	.owner    = THIS_MODULE,
	.obj_size = sizeof(struct nfc_llcp_sock),
};

static const struct proto_ops llcp_sock_ops = {
	.family         = PF_NFC,
	.owner          = THIS_MODULE,
	.bind           = llcp_sock_bind,
	.connect        = llcp_sock_connect,
	.release        = llcp_sock_release,
	.socketpair     = sock_no_socketpair,
	.accept         = llcp_sock_accept,
	.getname        = llcp_sock_getname,
	.poll           = llcp_sock_poll,
	.ioctl          = sock_no_ioctl,
	.listen         = llcp_sock_listen,
	.shutdown       = sock_no_shutdown,
	.setsockopt     = nfc_llcp_setsockopt,
	.getsockopt     = nfc_llcp_getsockopt,
	.sendmsg        = llcp_sock_sendmsg,
	.recvmsg        = llcp_sock_recvmsg,
	.mmap           = sock_no_mmap,
};

static const struct proto_ops llcp_rawsock_ops = {
	.family         = PF_NFC,
	.owner          = THIS_MODULE,
	.bind           = llcp_raw_sock_bind,
	.connect        = sock_no_connect,
	.release        = llcp_sock_release,
	.socketpair     = sock_no_socketpair,
	.accept         = sock_no_accept,
	.getname        = llcp_sock_getname,
	.poll           = llcp_sock_poll,
	.ioctl          = sock_no_ioctl,
	.listen         = sock_no_listen,
	.shutdown       = sock_no_shutdown,
	.sendmsg        = sock_no_sendmsg,
	.recvmsg        = llcp_sock_recvmsg,
	.mmap           = sock_no_mmap,
};

static const struct nfc_protocol llcp_nfc_proto = {
	.id	  = NFC_SOCKPROTO_LLCP,
	.proto    = &llcp_sock_proto,
	.owner    = THIS_MODULE,
	.create   = llcp_sock_create
};


g_token_size(gss_OID_const mech, unsigned int body_size)
{
	int hdrsize;

	/*
	 * Initialize the header size to the
	 * MECH_OID byte + the bytes needed to indicate the
	 * length of the OID + the OID itself.
	 *
	 * 0x06 [MECHLENFIELD] MECHDATA
	 */
	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;

	/*
	 * Now add the bytes needed for the initial header
	 * token bytes:
	 * 0x60 + [DER_LEN] + HDRSIZE
	 */
	hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);

	return (hdrsize + body_size);
}

Fix null deref in SPNEGO acceptor [CVE-2014-4344]

When processing a continuation token, acc_ctx_cont was dereferencing
the initial byte of the token without checking the length.  This could
result in a null dereference.

CVE-2014-4344:

In MIT krb5 1.5 and newer, an unauthenticated or partially
authenticated remote attacker can cause a NULL dereference and
application crash during a SPNEGO negotiation by sending an empty
token as the second or later context token from initiator to acceptor.
The attacker must provide at least one valid context token in the
security context negotiation before sending the empty token.  This can
be done by an unauthenticated attacker by forcing SPNEGO to
renegotiate the underlying mechanism, or by using IAKERB to wrap an
unauthenticated AS-REQ as the first token.

    CVSSv2 Vector: AV:N/AC:L/Au:N/C:N/I:N/A:C/E:POC/RL:OF/RC:C

[kaduk@mit.edu: CVE summary, CVSSv2 vector]

ticket: 7970 (new)
subject: NULL dereference in SPNEGO acceptor for continuation tokens [CVE-2014-4344]
target_version: 1.12.2
tags: pullup

typedef const gss_OID_desc *gss_OID_const;

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static int
g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);

static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
			int,
			gss_buffer_t,
			OM_uint32, gss_buffer_t, send_token_flag,
			gss_buffer_t);

static int
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
			gss_buffer_t, send_token_flag,
			gss_buffer_t);

static OM_uint32
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
		 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);

static int
is_kerb_mech(gss_OID oid);

static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);

static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);

int gss_krb5int_lib_init(void);


get_available_mechs(OM_uint32 *minor_status,
	gss_name_t name, gss_cred_usage_t usage,
	gss_const_key_value_set_t cred_store,
	gss_cred_id_t *creds, gss_OID_set *rmechs)
{
	unsigned int	i;
	int		found = 0;
	OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
	gss_OID_set mechs, goodmechs;

	major_status = gss_indicate_mechs(minor_status, &mechs);

	if (major_status != GSS_S_COMPLETE) {
		return (major_status);
	}

	major_status = gss_create_empty_oid_set(minor_status, rmechs);

	if (major_status != GSS_S_COMPLETE) {
		(void) gss_release_oid_set(minor_status, &mechs);
		return (major_status);
	}

	for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
		if ((mechs->elements[i].length
		    != spnego_mechanism.mech_type.length) ||
		    memcmp(mechs->elements[i].elements,
			spnego_mechanism.mech_type.elements,
			spnego_mechanism.mech_type.length)) {

			major_status = gss_add_oid_set_member(minor_status,
							      &mechs->elements[i],
							      rmechs);
			if (major_status == GSS_S_COMPLETE)
				found++;
		}
	}

	/*
	 * If the caller wanted a list of creds returned,
	 * trim the list of mechanisms down to only those
	 * for which the creds are valid.
	 */
	if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
		major_status = gss_acquire_cred_from(minor_status, name,
						     GSS_C_INDEFINITE,
						     *rmechs, usage,
						     cred_store, creds,
						     &goodmechs, NULL);

		/*
		 * Drop the old list in favor of the new
		 * "trimmed" list.
		 */
		(void) gss_release_oid_set(&tmpmin, rmechs);
		if (major_status == GSS_S_COMPLETE) {
			(void) gssint_copy_oid_set(&tmpmin,
					goodmechs, rmechs);
			(void) gss_release_oid_set(&tmpmin, &goodmechs);
		}
	}

	(void) gss_release_oid_set(&tmpmin, &mechs);
	if (found == 0 || major_status != GSS_S_COMPLETE) {
		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
		map_errcode(minor_status);
		if (major_status == GSS_S_COMPLETE)
			major_status = GSS_S_FAILURE;
	}

	return (major_status);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);


spnego_gss_unwrap(
		OM_uint32 *minor_status,
		gss_ctx_id_t context_handle,
		gss_buffer_t input_message_buffer,
		gss_buffer_t output_message_buffer,
		int *conf_state,
		gss_qop_t *qop_state)
{
	OM_uint32 ret;
	ret = gss_unwrap(minor_status,
			context_handle,
			input_message_buffer,
			output_message_buffer,
			conf_state,
			qop_state);

	return (ret);
}

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);


acc_ctx_new(OM_uint32 *minor_status,
	    gss_buffer_t buf,
	    gss_ctx_id_t *ctx,
	    spnego_gss_cred_id_t spcred,
	    gss_buffer_t *mechToken,
	    gss_buffer_t *mechListMIC,
	    OM_uint32 *negState,
	    send_token_flag *return_token)
{
	OM_uint32 tmpmin, ret, req_flags;
	gss_OID_set supported_mechSet, mechTypes;
	gss_buffer_desc der_mechTypes;
	gss_OID mech_wanted;
	spnego_gss_ctx_id_t sc = NULL;

	ret = GSS_S_DEFECTIVE_TOKEN;
	der_mechTypes.length = 0;
	der_mechTypes.value = NULL;
	*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
	supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
	*return_token = ERROR_TOKEN_SEND;
	*negState = REJECT;
	*minor_status = 0;

	ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
			       &mechTypes, &req_flags,
			       mechToken, mechListMIC);
	if (ret != GSS_S_COMPLETE) {
		goto cleanup;
	}
	ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
				   &supported_mechSet);
	if (ret != GSS_S_COMPLETE) {
		*return_token = NO_TOKEN_SEND;
		goto cleanup;
	}
	/*
	 * Select the best match between the list of mechs
	 * that the initiator requested and the list that
	 * the acceptor will support.
	 */
	mech_wanted = negotiate_mech(supported_mechSet, mechTypes, negState);
	if (*negState == REJECT) {
		ret = GSS_S_BAD_MECH;
		goto cleanup;
	}
	sc = (spnego_gss_ctx_id_t)*ctx;
	if (sc != NULL) {
		gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
		assert(mech_wanted != GSS_C_NO_OID);
	} else
		sc = create_spnego_ctx();
	if (sc == NULL) {
		ret = GSS_S_FAILURE;
		*return_token = NO_TOKEN_SEND;
		goto cleanup;
	}
	sc->mech_set = mechTypes;
	mechTypes = GSS_C_NO_OID_SET;
	sc->internal_mech = mech_wanted;
	sc->DER_mechTypes = der_mechTypes;
	der_mechTypes.length = 0;
	der_mechTypes.value = NULL;

	if (*negState == REQUEST_MIC)
		sc->mic_reqd = 1;

	*return_token = INIT_TOKEN_SEND;
	sc->firstpass = 1;
	*ctx = (gss_ctx_id_t)sc;
	ret = GSS_S_COMPLETE;
cleanup:
	gss_release_oid_set(&tmpmin, &mechTypes);
	gss_release_oid_set(&tmpmin, &supported_mechSet);
	if (der_mechTypes.length != 0)
		gss_release_buffer(&tmpmin, &der_mechTypes);

	return ret;
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);


create_spnego_ctx(void)
{
	spnego_gss_ctx_id_t spnego_ctx = NULL;
	spnego_ctx = (spnego_gss_ctx_id_t)
		malloc(sizeof (spnego_gss_ctx_id_rec));

	if (spnego_ctx == NULL) {
		return (NULL);
	}

	spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
	spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
	spnego_ctx->mech_set = NULL;
	spnego_ctx->internal_mech = NULL;
	spnego_ctx->optionStr = NULL;
	spnego_ctx->DER_mechTypes.length = 0;
	spnego_ctx->DER_mechTypes.value = NULL;
	spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
	spnego_ctx->mic_reqd = 0;
	spnego_ctx->mic_sent = 0;
	spnego_ctx->mic_rcvd = 0;
	spnego_ctx->mech_complete = 0;
	spnego_ctx->nego_done = 0;
	spnego_ctx->internal_name = GSS_C_NO_NAME;
	spnego_ctx->actual_mech = GSS_C_NO_OID;

	check_spnego_options(spnego_ctx);

	return (spnego_ctx);
}

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		 gss_buffer_t, gss_OID *, gss_buffer_t,
		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
		 OM_uint32 *, send_token_flag *);

static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
			int,
			gss_buffer_t,
			OM_uint32, gss_buffer_t, send_token_flag,
			gss_buffer_t);


spnego_gss_inquire_cred_by_oid(
		OM_uint32 *minor_status,
		const gss_cred_id_t cred_handle,
		const gss_OID desired_object,
		gss_buffer_set_t *data_set)
{
	OM_uint32 ret;
	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
	gss_cred_id_t mcred;
	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
	ret = gss_inquire_cred_by_oid(minor_status,
				mcred,
				desired_object,
				data_set);
	return (ret);
}

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		 gss_buffer_t, gss_OID *, gss_buffer_t,
		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
		 OM_uint32 *, send_token_flag *);


release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
{
	spnego_gss_ctx_id_t context;
	OM_uint32 minor_stat;
	context = *ctx;

	if (context != NULL) {
		(void) gss_release_buffer(&minor_stat,
					&context->DER_mechTypes);

		(void) gss_release_oid_set(&minor_stat, &context->mech_set);

		(void) gss_release_name(&minor_stat, &context->internal_name);

		if (context->optionStr != NULL) {
			free(context->optionStr);
			context->optionStr = NULL;
		}
		free(context);
		*ctx = NULL;
	}
}

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
			  gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
			  int iov_count)
{
    return gss_verify_mic_iov(minor_status, context_handle, qop_state, iov,
			      iov_count);
}


spnego_gss_inquire_names_for_mech(
				OM_uint32	*minor_status,
				gss_OID		mechanism,
				gss_OID_set	*name_types)
{
	OM_uint32   major, minor;

	dsyslog("Entering inquire_names_for_mech\n");
	/*
	 * We only know how to handle our own mechanism.
	 */
	if ((mechanism != GSS_C_NULL_OID) &&
	    !g_OID_equal(gss_mech_spnego, mechanism)) {
		*minor_status = 0;
		return (GSS_S_FAILURE);
	}

	major = gss_create_empty_oid_set(minor_status, name_types);
	if (major == GSS_S_COMPLETE) {
		/* Now add our members. */
		if (((major = gss_add_oid_set_member(minor_status,
				(gss_OID) GSS_C_NT_USER_NAME,
				name_types)) == GSS_S_COMPLETE) &&
		    ((major = gss_add_oid_set_member(minor_status,
				(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
				name_types)) == GSS_S_COMPLETE) &&
		    ((major = gss_add_oid_set_member(minor_status,
				(gss_OID) GSS_C_NT_STRING_UID_NAME,
				name_types)) == GSS_S_COMPLETE)) {
			major = gss_add_oid_set_member(minor_status,
				(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
				name_types);
		}

		if (major != GSS_S_COMPLETE)
			(void) gss_release_oid_set(&minor, name_types);
	}

	dsyslog("Leaving inquire_names_for_mech\n");
	return (major);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
		  OM_uint32 acc_negState, gss_OID supportedMech,
		  gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
		  OM_uint32 *negState, send_token_flag *tokflag)
{
	OM_uint32 tmpmin;
	size_t i;

	gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
			       GSS_C_NO_BUFFER);

	/* Find supportedMech in sc->mech_set. */
	for (i = 0; i < sc->mech_set->count; i++) {
		if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
			break;
	}
	if (i == sc->mech_set->count)
		return GSS_S_DEFECTIVE_TOKEN;
	sc->internal_mech = &sc->mech_set->elements[i];

	/*
	 * Windows 2003 and earlier don't correctly send a
	 * negState of request-mic when counter-proposing a
	 * mechanism.  They probably don't handle mechListMICs
	 * properly either.
	 */
	if (acc_negState != REQUEST_MIC)
		return GSS_S_DEFECTIVE_TOKEN;

	sc->mech_complete = 0;
	sc->mic_reqd = 1;
	*negState = REQUEST_MIC;
	*tokflag = CONT_TOKEN_SEND;
	return GSS_S_CONTINUE_NEEDED;
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);


acc_ctx_cont(OM_uint32 *minstat,
	     gss_buffer_t buf,
	     gss_ctx_id_t *ctx,
	     gss_buffer_t *responseToken,
	     gss_buffer_t *mechListMIC,
	     OM_uint32 *negState,
	     send_token_flag *return_token)
{
	OM_uint32 ret, tmpmin;
	gss_OID supportedMech;
	spnego_gss_ctx_id_t sc;
	unsigned int len;
	unsigned char *ptr, *bufstart;

	sc = (spnego_gss_ctx_id_t)*ctx;
	ret = GSS_S_DEFECTIVE_TOKEN;
	*negState = REJECT;
	*minstat = 0;
	supportedMech = GSS_C_NO_OID;
	*return_token = ERROR_TOKEN_SEND;
	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;

	ptr = bufstart = buf->value;
#define REMAIN (buf->length - (ptr - bufstart))
	if (REMAIN == 0 || REMAIN > INT_MAX)
		return GSS_S_DEFECTIVE_TOKEN;

	/*
	 * Attempt to work with old Sun SPNEGO.
	 */
	if (*ptr == HEADER_ID) {
		ret = g_verify_token_header(gss_mech_spnego,
					    &len, &ptr, 0, REMAIN);
		if (ret) {
			*minstat = ret;
			return GSS_S_DEFECTIVE_TOKEN;
		}
	}
	if (*ptr != (CONTEXT | 0x01)) {
		return GSS_S_DEFECTIVE_TOKEN;
	}
	ret = get_negTokenResp(minstat, ptr, REMAIN,
			       negState, &supportedMech,
			       responseToken, mechListMIC);
	if (ret != GSS_S_COMPLETE)
		goto cleanup;

	if (*responseToken == GSS_C_NO_BUFFER &&
	    *mechListMIC == GSS_C_NO_BUFFER) {

		ret = GSS_S_DEFECTIVE_TOKEN;
		goto cleanup;
	}
	if (supportedMech != GSS_C_NO_OID) {
		ret = GSS_S_DEFECTIVE_TOKEN;
		goto cleanup;
	}
	sc->firstpass = 0;
	*negState = ACCEPT_INCOMPLETE;
	*return_token = CONT_TOKEN_SEND;
cleanup:
	if (supportedMech != GSS_C_NO_OID) {
		generic_gss_release_oid(&tmpmin, &supportedMech);
	}
	return ret;
#undef REMAIN
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);


spnego_gss_export_name_composite(OM_uint32 *minor_status,
				 gss_name_t name,
				 gss_buffer_t exp_composite_name)
{
	OM_uint32 ret;
	ret = gss_export_name_composite(minor_status,
					name,
					exp_composite_name);
	return (ret);
}

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
                                     const gss_buffer_t sasl_mech_name,
                                     gss_OID *mech_type)
{
	if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
	    memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
		   SPNEGO_SASL_NAME_LEN) == 0) {
		if (mech_type != NULL)
			*mech_type = (gss_OID)gss_mech_spnego;
		return (GSS_S_COMPLETE);
	}

	return (GSS_S_BAD_MECH);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_wrap_size_limit(
	OM_uint32	*minor_status,
	const gss_ctx_id_t context_handle,
	int		conf_req_flag,
	gss_qop_t	qop_req,
	OM_uint32	req_output_size,
	OM_uint32	*max_input_size)
{
	OM_uint32 ret;
	ret = gss_wrap_size_limit(minor_status,
				context_handle,
				conf_req_flag,
				qop_req,
				req_output_size,
				max_input_size);
	return (ret);
}

typedef const gss_OID_desc *gss_OID_const;

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);


spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
                                     const gss_OID desired_mech,
                                     gss_buffer_t sasl_mech_name,
                                     gss_buffer_t mech_name,
                                     gss_buffer_t mech_description)
{
	*minor_status = 0;

	if (!g_OID_equal(desired_mech, gss_mech_spnego))
		return (GSS_S_BAD_MECH);

	if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
	    !g_make_string_buffer("spnego", mech_name) ||
	    !g_make_string_buffer("Simple and Protected GSS-API "
				  "Negotiation Mechanism", mech_description))
		goto fail;

	return (GSS_S_COMPLETE);

fail:
	*minor_status = ENOMEM;
	return (GSS_S_FAILURE);
}