#include "makuosan.h"

static void mrecv_req(mdata *data, struct sockaddr_in *addr);
static void mrecv_ack(mdata *data, struct sockaddr_in *addr);

/******************************************************************
*
* Receive common functions (private)
*
*******************************************************************/
static mfile *mrecv_mfdel(mfile *m)
{
  mfile *r;
  if(!m)
    return(NULL);
  r = m->next;
  if(m->fd != -1){
    close(m->fd);
    m->fd = -1;
    if(S_ISREG(m->fs.st_mode))
      mremove(moption.base_dir, m->tn);
  }
  if(m->mark){
    free(m->mark);
    m->mark = NULL;
  }
  mfdel(m);
  return(r);
}

static int mrecv_decrypt(mdata *data, struct sockaddr_in *addr)
{
  int i;
  MD5_CTX ctx;
  uint8_t hash[16];

  if(data->head.flags & MAKUO_FLAG_CRYPT){
    if(!moption.cryptena){
      lprintf(0, "%s: recv encrypt packet from %s. I have not key!", __func__, inet_ntoa(addr->sin_addr));
      return(-1);
    }
    if(data->head.szdata){
      for(i=0;i<data->head.szdata;i+=8){
        BF_decrypt((BF_LONG *)(data->data + i), &EncKey);
      }
      MD5_Init(&ctx);
      MD5_Update(&ctx, data->data, data->head.szdata);
      MD5_Final(hash, &ctx);
      if(memcmp(hash,data->head.hash,16)){
        lprintf(0, "%s: protocol checksum error from %s\n", __func__, inet_ntoa(addr->sin_addr));
        return(-1);
      }
    }
  }
  return(0);
}

static int mrecv_packet(int s, mdata *data, struct sockaddr_in *addr)
{
  int recvsize;
  socklen_t addr_len;

  while(1){
    addr_len = sizeof(struct sockaddr_in);
    recvsize = recvfrom(s, data, sizeof(mdata), 0, (struct sockaddr *)addr, &addr_len);
    if(recvsize != -1){
      break;
    }else{
      if(errno == EAGAIN || errno == EINTR){
        continue;
      }else{
        lprintf(0, "%s: recv error from %s\n", __func__, inet_ntoa(addr->sin_addr));
        return(-1);
      }
    }
  }
  if(recvsize < sizeof(data->head)){
    lprintf(0, "%s: recv head size error\n", __func__);
    return(-1);
  }

  data->head.szdata = ntohs(data->head.szdata);
  data->head.flags  = ntohs(data->head.flags);
  data->head.reqid  = ntohl(data->head.reqid);
  data->head.seqno  = ntohl(data->head.seqno);

  if(data->head.vproto != PROTOCOL_VERSION){
    lprintf(0, "%s: protocol version error(%d != %d) from %s\n", __func__,
       data->head.vproto, PROTOCOL_VERSION, inet_ntoa(addr->sin_addr));
    return(-1);
  }

  return(mrecv_decrypt(data, addr));
}

/******************************************************************
*
* Receive common functions (public)
*
*******************************************************************/
void mrecv(int s)
{
  mdata  data;
  struct sockaddr_in addr;
  if(mrecv_packet(s, &data, &addr) == -1){
    return;
  }
  if(data.head.flags & MAKUO_FLAG_ACK){
    mrecv_ack(&data, &addr);
  }else{
    mrecv_req(&data, &addr);
  }
}

void mrecv_gc()
{
  mhost *t = members;
  mfile *m = mftop[1]; 

  /* file timeout */
  while(m){
    if(mtimeout(&(m->lastrecv), MAKUO_RECV_GCWAIT)){
      lprintf(0,"%s: mfile object GC state=%d %s\n", __func__, m->mdata.head.nstate, m->fn);
      m = mrecv_mfdel(m);
      continue;
    }
    m = m->next;
  }

  /* pong timeout */
  while(t){
    if(!mtimeout(&(t->lastrecv), MAKUO_PONG_TIMEOUT)){
      t = t->next;
    }else{
      lprintf(0,"%s: pong timeout %s\n", __func__, t->hostname);
      if(t->next){
        t = t->next;
        member_del(t->prev);
      }else{
        member_del(t);
        t = NULL;
     } 
    }      
  }
}

/******************************************************************
*
* ack receive functions (for source node tasks)
*
*******************************************************************/
static int mrecv_ack_search(mhost **lpt, mfile **lpm, mdata *data, struct sockaddr_in *addr)
{
  mhost *t;
  mfile *m;
  *lpt = NULL;
  *lpm = NULL;
  t = member_add(&addr->sin_addr, NULL);
  if(!t){
    lprintf(0, "%s: member not found %s\n", __func__, inet_ntoa(addr->sin_addr));
    return(-1);
  }
  for(m=mftop[0];m;m=m->next)
    if(m->mdata.head.reqid == data->head.reqid)
      break;
  if(!m){
    lprintf(4, "%s: mfile not found rid=%06d state=%02d %s(%s)\n", __func__,
      data->head.reqid, data->head.nstate, inet_ntoa(addr->sin_addr), t->hostname);
    return(-1);
  }
  *lpt = t;
  *lpm = m;
  return(0);
}

static void mrecv_ack_report(mfile *m, mhost *h, mdata *data)
{
  if(data->head.nstate == MAKUO_RECVSTATE_OPENERROR){
    cprintf(0, m->comm, "%s: file open error %s\n", h->hostname, m->fn);
    lprintf(0,          "%s: file open error rid=%06d state=%02d %s(%s) %s\n", __func__,
      data->head.reqid, data->head.nstate, inet_ntoa(h->ad), h->hostname, m->fn);
  }
  if(data->head.nstate == MAKUO_RECVSTATE_WRITEERROR){
    cprintf(0, m->comm, "%s: file write error %s\n", h->hostname, m->fn);
    lprintf(0,          "%s: file write error rid=%06d state=%02d %s(%s) %s\n", __func__,
     data->head.reqid, data->head.nstate, inet_ntoa(h->ad), h->hostname, m->fn);
  }
  if(data->head.nstate == MAKUO_RECVSTATE_CLOSEERROR){
    cprintf(0, m->comm, "%s: file close error %s\n", h->hostname, m->fn);
    lprintf(0,          "%s: file close error rid=%06d state=%02d %s(%s) %s\n", __func__,
      data->head.reqid, data->head.nstate, inet_ntoa(h->ad), h->hostname, m->fn);
  }
}

static void mrecv_ack_ping(mdata *data, struct sockaddr_in *addr)
{
  member_add(&addr->sin_addr, data);
}

static void mrecv_ack_file(mdata *data, struct sockaddr_in *addr)
{
  mhost *t;
  mfile *m;

  if(mrecv_ack_search(&t, &m, data, addr)){
    return;
  }
  mtimeget(&m->lastrecv);
  if(data->head.nstate == MAKUO_RECVSTATE_IGNORE){
    cprintf(4, m->comm, "%s: file update ignore %s\n", t->hostname, m->fn);
    lprintf(0,          "%s: file update ignore rid=%06d state=%02d %s(%s) %s\n", __func__, 
      data->head.reqid, data->head.nstate, inet_ntoa(t->ad), t->hostname, m->fn);
  }
  if(data->head.nstate == MAKUO_RECVSTATE_OPEN){
    uint32_t *d = (uint32_t *)(data->data);
    while(d < (uint32_t *)&data->data[data->head.szdata]){
      seq_addmark(m, *d, (*d) + 1);
      d++;
    }
  }
  t->state = data->head.nstate;
  mrecv_ack_report(m, t, data);
}

static void mrecv_ack_md5(mdata *data, struct sockaddr_in *addr)
{
  mhost *t;
  mfile *m;

  mrecv_ack_search(&t, &m, data, addr);
  if(!t || !m){
    return;
  }
  mtimeget(&m->lastrecv);
  if(t->state != data->head.nstate){
    if(data->head.nstate == MAKUO_RECVSTATE_MD5OK){
      cprintf(1, m->comm, "%s: OK %s\r\n", t->hostname, m->fn);
      lprintf(1,          "%s: OK %s\n", __func__, m->fn);
    }
    if(data->head.nstate == MAKUO_RECVSTATE_MD5NG){
      cprintf(0, m->comm, "%s: NG %s\r\n", t->hostname, m->fn);
      lprintf(0,          "%s: NG %s\n", __func__, m->fn);
    }
  }
  t->state = data->head.nstate;
  mrecv_ack_report(m, t, data);
}

static void mrecv_ack(mdata *data, struct sockaddr_in *addr)
{
  switch(data->head.opcode){
    case MAKUO_OP_PING:
      mrecv_ack_ping(data, addr);
      break;
    case MAKUO_OP_SEND:
      mrecv_ack_file(data, addr);
      break;
    case MAKUO_OP_MD5:
      mrecv_ack_md5(data, addr);
      break;

    /* æ©è½è¿½å ã¯ãã㸠*/
  }
}

/******************************************************************
*
* Request receive functions (for destination node tasks)
*
*******************************************************************/
static void mrecv_req_ping(mdata *data, struct sockaddr_in *addr)
{
  mping *p;
  mfile *m;
  char buff[MAKUO_HOSTNAME_MAX + 1];
  member_add(&addr->sin_addr, data);
  m = mfadd(0);
  if(!m){
    lprintf(0,"%s: out of memory\n", __func__);
    return;
  }
  m->mdata.head.opcode = MAKUO_OP_PING;
  m->mdata.head.flags |= MAKUO_FLAG_ACK;
  m->mdata.head.reqid  = data->head.reqid;
  m->mdata.head.seqno  = 0;
  m->mdata.head.szdata = 0;
  memcpy(&(m->addr), addr, sizeof(m->addr));
  if(gethostname(buff, sizeof(buff)) == -1){
    buff[0] = 0;
  }
  p = (mping *)(m->mdata.data);
  p->hostnamelen = strlen(buff);
  p->versionlen  = strlen(MAKUOSAN_VERSION);
  m->mdata.head.szdata = sizeof(mping) + p->hostnamelen + p->versionlen;
  m->mdata.p = p->data;
  memcpy(m->mdata.p, buff, p->hostnamelen);
  m->mdata.p += p->hostnamelen;
  memcpy(m->mdata.p, MAKUOSAN_VERSION, p->versionlen);
  m->mdata.p += p->versionlen;
  p->hostnamelen = htons(p->hostnamelen);
  p->versionlen  = htons(p->versionlen);
}

static void mrecv_req_exit(mdata *data, struct sockaddr_in *addr)
{
  mhost *t;
  for(t=members;t;t=t->next)
    if(!memcmp(&(t->ad), &(addr->sin_addr), sizeof(t->ad)))
      break;
  member_del(t);
}

static void mrecv_req_file_data(mfile *m,  mdata *r)
{
  if(m->mdata.head.nstate != MAKUO_RECVSTATE_OPEN)
    return;

  if(m->lickflag){
    if(!seq_delmark(m, r->head.seqno)){
      return;
    }
  }else{
    if(r->head.seqno < m->mdata.head.seqno){
      seq_delmark(m, r->head.seqno);
    }else{
      m->mdata.head.seqno++;
      if(m->mdata.head.seqno < r->head.seqno){
        seq_addmark(m, m->mdata.head.seqno, r->head.seqno);
        m->mdata.head.seqno = r->head.seqno;
      }
    }
  }
  if(lseek(m->fd, (r->head.seqno - 1) * MAKUO_BUFFER_SIZE, SEEK_SET) == -1){
    lprintf(0, "%s: seek error seq=%d size=%d fd=%d err=%d\n", __func__, (int)r->head.seqno, r->head.szdata, m->fd, errno);
    m->mdata.head.nstate = MAKUO_RECVSTATE_WRITEERROR;
  }else{
    if(write(m->fd, r->data, r->head.szdata) != -1){
      m->recvcount++;
    }else{
      lprintf(0, "%s: write error seqno=%d size=%d fd=%d err=%d\n", __func__, (int)r->head.seqno, r->head.szdata, m->fd, errno);
      m->mdata.head.nstate = MAKUO_RECVSTATE_WRITEERROR;
    }
  }
}

static void mrecv_req_file_break(mfile *m, mdata *r)
{
  mrecv_mfdel(m);
}

static void mrecv_req_file_open(mfile *m, mdata *r)
{
  char fpath[PATH_MAX];
  char tpath[PATH_MAX];

  if(m->mdata.head.nstate != MAKUO_RECVSTATE_UPDATE)
    return;

  sprintf(fpath, "%s/%s", moption.base_dir, m->fn);
  sprintf(tpath, "%s/%s", moption.base_dir, m->tn);

  mfile *a = mfins(0);
  a->mdata.head.flags |= MAKUO_FLAG_ACK;
  a->mdata.head.opcode = r->head.opcode;
  a->mdata.head.reqid  = r->head.reqid;
  a->mdata.head.seqno  = r->head.seqno;
  a->mdata.head.ostate = m->mdata.head.nstate;
  a->mdata.head.nstate = MAKUO_RECVSTATE_OPEN;
  m->mdata.head.nstate = MAKUO_RECVSTATE_OPEN;
  memcpy(&(a->addr), &(m->addr), sizeof(a->addr));
  if(S_ISLNK(m->fs.st_mode)){
    mtempname(moption.base_dir, m->fn, m->tn);
    sprintf(tpath, "%s/%s", moption.base_dir, m->tn);
    if(symlink(m->ln, m->tn) != -1){
      lprintf(2, "%s: open %s -> %s\n", __func__, m->ln, m->fn);
    }else{
      lprintf(0, "%s: symlink error %s\n", __func__, m->fn);
      m->mdata.head.nstate = MAKUO_RECVSTATE_OPENERROR;
      a->mdata.head.nstate = m->mdata.head.nstate;
    }
  }else{
    if(S_ISDIR(m->fs.st_mode)){
      if(!is_dir(fpath)){
        mcreatedir(moption.base_dir, m->fn, m->fs.st_mode & 0xFFF);
        mkdir(fpath, m->fs.st_mode & 0xFFF);
      }else{
        chmod(fpath, m->fs.st_mode & 0xFFF);
      }
      if(!is_dir(fpath)){
        lprintf(0,"%s: mkdir error %s\n", __func__, m->fn);
        m->mdata.head.nstate = MAKUO_RECVSTATE_OPENERROR;
        a->mdata.head.nstate = m->mdata.head.nstate;
      }
    }
    if(S_ISREG(m->fs.st_mode)){
      mtempname(moption.base_dir, m->fn, m->tn);
      sprintf(tpath, "%s/%s", moption.base_dir, m->tn);
      m->fd = mcreate(moption.base_dir, m->tn, m->fs.st_mode);
      if(m->fd == -1){
        lprintf(0, "%s: open error %s\n", __func__, m->fn);
        m->mdata.head.nstate = MAKUO_RECVSTATE_OPENERROR;
        a->mdata.head.nstate = m->mdata.head.nstate;
      }
    }
  }
}

static void mrecv_req_file_last(mfile *m, mdata *r)
{
  mrecv_mfdel(m);
}

static void mrecv_req_file_close(mfile *m, mdata *r)
{
  struct utimbuf mftime;
  char  fpath[PATH_MAX];
  char  tpath[PATH_MAX];
  sprintf(fpath, "%s/%s", moption.base_dir, m->fn);
  sprintf(tpath, "%s/%s", moption.base_dir, m->tn);

  switch(m->mdata.head.nstate){
    case MAKUO_RECVSTATE_OPEN:
    case MAKUO_RECVSTATE_UPDATE:
    case MAKUO_RECVSTATE_CLOSE:
    case MAKUO_RECVSTATE_CLOSEERROR:
      break;
    default:
      return;
  }

  mfile *a = mfins(0);
  a->mdata.head.flags |= MAKUO_FLAG_ACK;
  a->mdata.head.opcode = r->head.opcode;
  a->mdata.head.reqid  = r->head.reqid;
  a->mdata.head.seqno  = r->head.seqno;
  a->mdata.head.ostate = m->mdata.head.nstate;
  a->mdata.head.nstate = MAKUO_RECVSTATE_CLOSE;
  memcpy(&(a->addr), &(m->addr), sizeof(a->addr));

  switch(m->mdata.head.nstate){
    case MAKUO_RECVSTATE_OPEN:
      break;
    case MAKUO_RECVSTATE_UPDATE:
    case MAKUO_RECVSTATE_CLOSE:
      return;
    case MAKUO_RECVSTATE_CLOSEERROR:
      a->mdata.head.nstate = MAKUO_RECVSTATE_CLOSEERROR;
      return;
  }

  if(m->fd != -1){
    fstat(m->fd, &(a->fs));
    close(m->fd);
  }
  m->fd = -1;
  mftime.actime  = m->fs.st_ctime; 
  mftime.modtime = m->fs.st_mtime;
  if(S_ISLNK(m->fs.st_mode)){
    if(!mrename(moption.base_dir, m->tn, m->fn)){
      lprintf(2, "%s: close %s -> %s\n", __func__, m->ln, m->fn);
    }else{
      a->mdata.head.nstate = MAKUO_RECVSTATE_CLOSEERROR;
      mremove(moption.base_dir, m->tn);
      lprintf(0, "%s: close error %s -> %s\n", __func__, m->ln, m->fn);
    }
    return;
  }
  if(S_ISDIR(m->fs.st_mode)){
    utime(fpath, &mftime);
  }else{
    utime(tpath, &mftime);
    if(a->fs.st_size != m->fs.st_size){
      a->mdata.head.nstate = MAKUO_RECVSTATE_CLOSEERROR;
      mremove(moption.base_dir, m->tn);
      lprintf(0, "%s: close error %s (file size mismatch %d != %d)\n", __func__, m->fn, (int)(a->fs.st_size), (int)(m->fs.st_size));
      lprintf(0, "%s: seq=%d max=%d markcnt=%d\n", __func__, m->mdata.head.seqno, m->seqnomax, m->markcount);
    }else{
      if(!mrename(moption.base_dir, m->tn, m->fn)){
        lprintf(2, "%s: close %s recv=%d mark=%d\n", __func__, m->fn , m->recvcount, m->markcount);
      }else{
        a->mdata.head.nstate = MAKUO_RECVSTATE_CLOSEERROR;
        mremove(moption.base_dir, m->tn);
        lprintf(0, "%s: close error %s\n", __func__, m->fn);
      }
    }
  }
  if(!geteuid()){
    chown(fpath, m->fs.st_uid, m->fs.st_gid);
  }
}

static void mrecv_req_file_mark(mfile *m, mdata *r)
{
  if(m->mdata.head.nstate != MAKUO_RECVSTATE_OPEN)
    return;

  mfile *a = mfins(0);
  a->mdata.head.flags |= MAKUO_FLAG_ACK;
  a->mdata.head.opcode = r->head.opcode;
  a->mdata.head.reqid  = r->head.reqid;
  a->mdata.head.seqno  = r->head.seqno;
  a->mdata.head.nstate = m->mdata.head.nstate;
  a->mdata.head.szdata = 0;
  memcpy(&(a->addr), &(m->addr), sizeof(a->addr));
  m->lickflag = 1;
  a->lickflag = 1;
  if(m->mdata.head.seqno < m->seqnomax){
    seq_addmark(m, m->mdata.head.seqno, m->seqnomax + 1);
    m->mdata.head.seqno = m->seqnomax;
  }
  if(m->markcount){
    if(MAKUO_BUFFER_SIZE < m->markcount * sizeof(uint32_t)){
      a->marksize = MAKUO_BUFFER_SIZE / sizeof(uint32_t);
    }else{
      a->marksize = m->markcount;
    }
    a->markcount = a->marksize;
    a->mark = malloc(a->marksize * sizeof(uint32_t));
    memcpy(a->mark, m->mark, a->marksize * sizeof(uint32_t));
    lprintf(3, "%s: retry %s recv=%d mark=%d reqest=%d\n", __func__, m->fn , m->recvcount, m->markcount, a->markcount);
  }
}

static void mrecv_req_file_stat(mfile *m, mdata *r)
{
  mstat fs;
  mfile *a;
  struct utimbuf mftime;

  a = mfins(0);
  a->mdata.head.flags |= MAKUO_FLAG_ACK;
  a->mdata.head.opcode = m->mdata.head.opcode;
  a->mdata.head.reqid  = m->mdata.head.reqid;
  a->mdata.head.szdata = 0;
  a->mdata.head.seqno  = m->mdata.head.seqno;
  memcpy(&(a->addr), &(m->addr), sizeof(a->addr));
  if(moption.dontrecv){
    m->mdata.head.nstate = MAKUO_RECVSTATE_READONLY;
  }else{
    if(S_ISLNK(m->fs.st_mode)){
      m->mdata.head.nstate = linkcmp(m);
    }else{
      if(lstat(m->fn, &(a->fs)) == -1){
        m->mdata.head.nstate = MAKUO_RECVSTATE_UPDATE;
      }else{
        m->mdata.head.nstate = statcmp(&(m->fs), &(a->fs));
      }
    }
  }
  a->mdata.head.nstate = m->mdata.head.nstate;
}

static void mrecv_req_file_next(mfile *m,  mdata *r)
{
  switch(r->head.nstate){
    case MAKUO_SENDSTATE_STAT:
      lprintf(9,"%s: MAKUO_SENDSTATE_STAT: state=%d %s\n", __func__,  m->mdata.head.nstate, m->fn);
      mrecv_req_file_stat(m, r);
      break;

    case MAKUO_SENDSTATE_OPEN:
      lprintf(9,"%s: MAKUO_SENDSTATE_OPEN : state=%d %s\n", __func__,  m->mdata.head.nstate, m->fn);
      mrecv_req_file_open(m, r);
      break;

    case MAKUO_SENDSTATE_DATA:
      mrecv_req_file_data(m, r);
      break;

    case MAKUO_SENDSTATE_MARK:
      lprintf(9,"%s: MAKUO_SENDSTATE_MARK : state=%d seqno=%d max=%d cnt=%d %s\n", __func__,
             m->mdata.head.nstate, m->mdata.head.seqno, m->seqnomax, m->markcount, m->fn);
      mrecv_req_file_mark(m, r);
      break;

    case MAKUO_SENDSTATE_CLOSE:
      lprintf(9,"%s: MAKUO_SENDSTATE_CLOSE: state=%d %s\n", __func__,  m->mdata.head.nstate, m->fn);
      mrecv_req_file_close(m, r);
      break;

    case MAKUO_SENDSTATE_LAST:
      lprintf(9,"%s: MAKUO_SENDSTATE_LAST : state=%d %s\n", __func__,  m->mdata.head.nstate, m->fn);
      mrecv_req_file_last(m, r);
      break;

    case MAKUO_SENDSTATE_BREAK:
      lprintf(9,"%s: MAKUO_SENDSTATE_BREAK: state=%d %s\n", __func__,  m->mdata.head.nstate, m->fn);
      mrecv_req_file_break(m, r);
      break;
  }
}


static void mrecv_req_send(mdata *data, struct sockaddr_in *addr)
{
  mstat fs;
  mfile *m; 
  uint16_t fnlen;
  uint16_t lnlen;

  for(m=mftop[1];m;m=m->next){
    if(!memcmp(&m->addr, addr, sizeof(m->addr)) && m->mdata.head.reqid == data->head.reqid){
      break;
    }
  }

  if(!m){
    /* create object */
    m = mfadd(1);
    if(!m){
      lprintf(0, "%s: out of memory\n", __func__);
      return;
    }

    /* copy header */
    memcpy(&(m->addr), addr, sizeof(m->addr));
    memcpy(&(m->mdata.head), &(data->head), sizeof(m->mdata.head));
    data->p = data->data;

    /* read mstat */
    memcpy(&fs, data->p, sizeof(fs));
    data->p += sizeof(fs);

    /* stat = mstat */
    m->fs.st_mode  = ntohl(fs.mode);
    m->fs.st_uid   = ntohs(fs.uid);
    m->fs.st_gid   = ntohs(fs.gid);
    m->fs.st_size  = ((off_t)ntohl(fs.sizeh) << 32) + (off_t)ntohl(fs.sizel);
    m->fs.st_mtime = ntohl(fs.mtime);
    m->fs.st_ctime = ntohl(fs.ctime);
    fnlen = ntohs(fs.fnlen);
    lnlen = ntohs(fs.lnlen);

    /* read filename */
    memcpy(m->fn, data->p, fnlen);
    m->fn[fnlen] = 0;
    data->p += fnlen;

    /* read linkname */
    memcpy(m->ln, data->p, lnlen);    
    m->ln[lnlen] = 0;
    data->p += lnlen;

    /* Number of blocks */
    m->seqnomax = m->fs.st_size / MAKUO_BUFFER_SIZE;
    if(m->fs.st_size % MAKUO_BUFFER_SIZE){
      m->seqnomax++; 
    }
  }
  mtimeget(&(m->lastrecv));
  mrecv_req_file_next(m, data);
}

static void mrecv_req_md5_open(mfile *m, mdata *data, struct sockaddr_in *addr)
{
  int    r;
  int    l;
  mfile *a;
  mhash *h;

  if(!m){
    m = mfadd(1);
    memcpy(&(m->addr), addr, sizeof(m->addr));
    memcpy(&(m->mdata.head), &(data->head), sizeof(m->mdata.head));
    h = (mhash *)(data->data);
    l = ntohs(h->fnlen);
    memcpy(m->fn, h->filename, l);
    m->fn[l] = 0;
    m->fd = open(m->fn, O_RDONLY);
    if(m->fd == -1){
      m->mdata.head.nstate = MAKUO_RECVSTATE_OPENERROR;
    }else{
      r = md5sum(m->fd, m->mdata.data);
      close(m->fd);
      m->fd = -1;
      if(r == -1){
	      lprintf(0, "%s: file read error %s\n", __func__, m->fn);
        m->mdata.head.nstate = MAKUO_RECVSTATE_READERROR;
      }else{
        if(!memcmp(m->mdata.data, data->data, 16)){
          m->mdata.head.nstate = MAKUO_RECVSTATE_MD5OK;
        }else{
          m->mdata.head.nstate = MAKUO_RECVSTATE_MD5NG;
        }
      }
    }
  }
  a=mfadd(0);
  a->mdata.head.flags |= MAKUO_FLAG_ACK;
  a->mdata.head.opcode = m->mdata.head.opcode;
  a->mdata.head.reqid  = m->mdata.head.reqid;
  a->mdata.head.seqno  = 0;
  a->mdata.head.szdata = 0;
  a->mdata.head.nstate = m->mdata.head.nstate;
  memcpy(&(a->addr), addr, sizeof(a->addr));
  mtimeget(&(m->lastrecv));
}

static void mrecv_req_md5_close(mfile *m, mdata *data, struct sockaddr_in *addr)
{
  mfile *a = mfadd(0);
  a->mdata.head.flags |= MAKUO_FLAG_ACK;
  a->mdata.head.opcode = data->head.opcode;
  a->mdata.head.reqid  = data->head.reqid;
  a->mdata.head.szdata = 0;
  a->mdata.head.seqno  = 0;
  a->mdata.head.nstate = MAKUO_RECVSTATE_CLOSE;
  memcpy(&(a->addr), addr, sizeof(a->addr));
  mrecv_mfdel(m);
}

/*
 * md5ãã§ãã¯è¦æ±ãåãåã£ãã¨ãã®å¦ç
 * mfileãªãã¸ã§ã¯ããçæãã¦
 * 対象ãã¡ã¤ã«ã®md5ãåå¾ãã
 */
static void mrecv_req_md5(mdata *data, struct sockaddr_in *addr)
{
  mfile *m = mftop[1];
  while(m){
    if(!memcmp(&m->addr, addr, sizeof(m->addr)) && m->mdata.head.reqid == data->head.reqid){
      mtimeget(&m->lastrecv);
      break;
    }
    m = m->next;
  }
  switch(data->head.nstate){
    case MAKUO_SENDSTATE_OPEN:
      mrecv_req_md5_open(m, data, addr);
      break;
    case MAKUO_SENDSTATE_CLOSE:
      mrecv_req_md5_close(m, data, addr);
      break;
  }
}

static void mrecv_req(mdata *data, struct sockaddr_in *addr)
{
  switch(data->head.opcode){
    case MAKUO_OP_PING:
      mrecv_req_ping(data, addr);
      break;
    case MAKUO_OP_EXIT:
      mrecv_req_exit(data, addr);
      break;
    case MAKUO_OP_SEND:
      mrecv_req_send(data, addr);
      break;
    case MAKUO_OP_MD5:
      mrecv_req_md5(data, addr);
      break;

    /* æ©è½è¿½å ã¯ãã㸠*/
  }
}