#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "cifs.h"

static Pkt *
tnthdr(Session *s, Share *sp, int cmd)
{
       Pkt *p;

       p = cifshdr(s, sp, SMB_COM_NT_TRANSACT);
       p->tbase = p8(p, 0);            /*  0  Max setup count to return */
       pl16(p, 0);                     /*  1  reserved */
       pl32(p, 0);                     /*  3  Total parameter count */
       pl32(p, 0);                     /*  7  Total data count */
       pl32(p, 64);                    /* 11  Max parameter count to return */
       pl32(p, (MTU - T2HDRLEN)-64);   /* 15  Max data count to return */
       pl32(p, 0);                     /* 19  Parameter count (in this buffer) */
       pl32(p, 0);                     /* 23  Offset to parameters (in this buffer) */
       pl32(p, 0);                     /* 27  Count of data  in this buffer */
       pl32(p, 0);                     /* 31  Offset to data in this buffer */
       p8(p, 1);                       /* 35  Count of setup words */
       pl16(p, cmd);                   /* 37  setup[0] */
       pl16(p, 0);                     /* padding ??!?!? */
       pbytes(p);
       return p;
}

static void
ptntparam(Pkt *p)
{
       uchar *pos = p->pos;
       assert(p->tbase != 0);

       p->pos = p->tbase +23;
       pl32(p, (pos - p->buf) - NBHDRLEN); /* param offset */

       p->tparam = p->pos = pos;
}

static void
ptntdata(Pkt *p)
{
       uchar *pos = p->pos;
       assert(p->tbase != 0);
       assert(p->tparam != 0);

       p->pos = p->tbase +3;
       pl32(p, pos - p->tparam);               /* total param count */

       p->pos = p->tbase +19;
       pl32(p, pos - p->tparam);               /* param count */

       p->pos = p->tbase +31;
       pl32(p, (pos - p->buf) - NBHDRLEN);     /* data offset */
       p->tdata = p->pos = pos;
}

static int
tntrpc(Pkt *p)
{
       int got;
       uchar *pos;
       assert(p->tbase != 0);
       assert(p->tdata != 0);

       pos = p->pos;

       p->pos = p->tbase +7;
       pl32(p, pos - p->tdata);                /* total data count */

       p->pos = p->tbase +27;
       pl32(p, pos - p->tdata);                /* data count */

       p->pos = pos;
       if((got = cifsrpc(p)) == -1)
               return -1;

       g8(p);                          /* Reserved */
       g8(p);                          /* Reserved */
       g8(p);                          /* Reserved */
       gl32(p);                        /* Total parameter count */
       gl32(p);                        /* Total data count */
       gl32(p);                        /* Parameter count in this buffer */
       p->tparam = p->buf +NBHDRLEN +gl32(p); /* Parameter offset */
       gl32(p);                        /* Parameter displacement */
       gl32(p);                        /* Data count (this buffer); */
       p->tdata = p->buf +NBHDRLEN +gl32(p); /* Data offset */
       gl32(p);                        /* Data displacement */
       g8(p);                          /* Setup count */
       gl16(p);                        /* padding ???  */

       return got;
}

static void
gtntparam(Pkt *p)
{
       p->pos = p->tparam;
}

static void
gtntdata(Pkt *p)
{
       p->pos = p->tdata;
}


int
TNTquerysecurity(Session *s, Share *sp, int fh, char **usid, char **gsid)
{
       Pkt *p;
       uchar *base;
       Fmt fmt, *f = &fmt;
       int n, i, off2owner, off2group;

       p = tnthdr(s, sp, NT_TRANSACT_QUERY_SECURITY_DESC);
       ptntparam(p);

       pl16(p, fh);            /* File handle */
       pl16(p, 0);             /* Reserved */
       pl32(p, QUERY_OWNER_SECURITY_INFORMATION |
               QUERY_GROUP_SECURITY_INFORMATION);

       ptntdata(p);

       if(tntrpc(p) == -1){
               free(p);
               return -1;
       }

       gtntdata(p);

       base = p->pos;
       gl16(p);                        /* revision */
       gl16(p);                        /* type */
       off2owner = gl32(p);            /* offset to owner */
       off2group = gl32(p);            /* offset to group */
       gl32(p);
       gl32(p);

       if(off2owner){
               p->pos = base +  off2owner;
               fmtstrinit(f);
               fmtprint(f, "S-%ud", g8(p));    /* revision */
               n = g8(p);                      /* num auth */
               fmtprint(f, "-%llud", gb48(p)); /* authority */
               for(i = 0; i < n; i++)
                       fmtprint(f, "-%ud", gl32(p));   /* sub-authorities */
               *usid = fmtstrflush(f);
       }

       if(off2group){
               p->pos = base +  off2group;
               fmtstrinit(f);
               fmtprint(f, "S-%ud", g8(p));    /* revision */
               n = g8(p);                      /* num auth */
               fmtprint(f, "-%llud", gb48(p)); /* authority */
               for(i = 0; i < n; i++)
                       fmtprint(f, "-%ud", gl32(p));   /* sub-authorities */
               *gsid = fmtstrflush(f);
       }
       free(p);
       return 0;
}