/*
Le code est globalement repris de cette page de la msdn :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wnet/wnet/enumerating_network_resources.asp
et juste modifié pour qu'il tourne et affiche un résultat.
*/

#pragma comment (lib,"Mpr") //la bibliothèque tordue Mpr.dll qui contient les fonctions WNet***
#undef UNICODE //parce que ma lib ne le gère pas.
#define _CRT_SECURE_NO_DEPRECATE

//Si vous n'avez pas advio, décommentez cette ligne.
//#define NO_ADVIO
const char drive[]="Z:\\*";   //Si vous avez un lecteur Z, il faut remplacer la lettre Z par une lettre inutilisé, sans toucher au reste de la chaine.

#include <windows.h>
#include <Winnetwk.h>
#include <cstdio>
#include <utility>
using namespace std;

#ifndef NO_ADVIO
#include <advio.h>
advio_console cons;
#else
#include <iostream>
#define cons cout
#define crlf endl
#endif


char *jours[7]={"dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"};
char *mois[13]={"","janvier","février","mars","avril","mai","juin","juillet","aout","septembre","octobre","novembre","decembre"};


char temp[MAX_PATH];

int nb_file=0,nb_dir=0;

#define DEPTH   for (int i=0;i<depth;i++) cons << "  ";

void NetErrorHandler(DWORD result,LPSTR str,int depth);
void DisplayStruct(LPNETRESOURCE lpnr,int depth);
void Index(LPNETRESOURCE lpnr,int depth);
BOOL WINAPI EnumerateFunc(LPNETRESOURCE lpnr,int depth);
char* CreateDirName(const char* name);
pair<int,int> Explore(HANDLE h,const char* dir);
pair<int,int> Enregistre(LPWIN32_FIND_DATA ffd,const char* dir);
char* cat(char* temp,const char* cur,const char* next);

//explore récursivement (et séquentiellement) les ressources du réseau.
BOOL WINAPI EnumerateFunc(LPNETRESOURCE lpnr,int depth)
{ 
  DWORD dwResult, dwResultEnum;
  HANDLE hEnum;
  DWORD cbBuffer = 16384;
  DWORD cEntries = -1;
  LPNETRESOURCE lpnrLocal;
  DWORD i;

  dwResult = WNetOpenEnum(RESOURCE_GLOBALNET,RESOURCETYPE_ANY,0,lpnr,&hEnum);
  FILE* file;
  SYSTEMTIME st;
  GetLocalTime(&st);

  if (dwResult != NO_ERROR)
  {  
    if (file=fopen("index.txt","a+"))
    {
      fprintf(file,"Accès impossible à %hdH%hd le %s %hd %s %hd.\n",st.wHour,st.wMinute,jours[st.wDayOfWeek],st.wDay,mois[st.wMonth],st.wYear);
      fclose(file);
    }
    NetErrorHandler(dwResult, (LPSTR)"WNetOpenEnum",depth);
    return FALSE;
  }
  else
  {
    if (file=fopen("index.txt","a+"))
    {
      fprintf(file,"Accès possible à %hdH%hd le %s %hd %s %hd.\n",st.wHour,st.wMinute,jours[st.wDayOfWeek],st.wDay,mois[st.wMonth],st.wYear);
      fclose(file);
    }
  }

  lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
  if (lpnrLocal == NULL) 
      return FALSE;
  
  do
  {  
    ZeroMemory(lpnrLocal, cbBuffer);
     dwResultEnum = WNetEnumResource(hEnum,&cEntries,lpnrLocal,&cbBuffer);

    if (dwResultEnum == NO_ERROR)
    {
      for(i = 0; i < cEntries; i++)
      {
        DisplayStruct(&lpnrLocal[i],depth);

        if(lpnrLocal[i].dwUsage & RESOURCEUSAGE_CONTAINER)
        {
          CreateDirectory(CreateDirName(lpnrLocal[i].lpRemoteName),NULL);
          SetCurrentDirectory(temp);  //temp est écrit par la fonction CreateDirName.
          EnumerateFunc(&lpnrLocal[i],depth+1);
          SetCurrentDirectory("..");
        }

        else if (lpnrLocal[i].dwType==RESOURCETYPE_DISK)
        {
          CreateDirectory(CreateDirName(lpnrLocal[i].lpRemoteName),NULL);
          SetCurrentDirectory(temp);  //temp est écrit par la fonction CreateDirName.
          Index(&lpnrLocal[i],depth);
          SetCurrentDirectory("..");
        }
      }
    }
    else if (dwResultEnum != ERROR_NO_MORE_ITEMS)
    {
      NetErrorHandler(dwResultEnum, (LPSTR)"WNetEnumResource",depth);
      break;
    }
  } while(dwResultEnum != ERROR_NO_MORE_ITEMS);

  GlobalFree((HGLOBAL)lpnrLocal);
  dwResult = WNetCloseEnum(hEnum);
  
  if(dwResult != NO_ERROR)
  { 
    NetErrorHandler(dwResult, (LPSTR)"WNetCloseEnum",depth);
    return FALSE;
  }

  return TRUE;
}

void NetErrorHandler(DWORD result,LPSTR str,int depth)
{
  DEPTH;
  cons << "Erreur : " << (int)result << " " << (char*)str << crlf;
}

void DisplayStruct(LPNETRESOURCE lpnr,int depth)
{
  if (lpnr==NULL)
    return;
  DEPTH;
  if (lpnr->lpLocalName!=0)
    cons << (char*)(lpnr->lpLocalName) << " : ";
  cons << (char*)(lpnr->lpProvider) << " : " << (char*)(lpnr->lpRemoteName) << "(" << (int)lpnr->dwType << ")" << crlf;
}

//Recois en paramètre un lecteur réseaux afin de l'indexer.
void Index(LPNETRESOURCE lpnr,int depth)
{
  char Z[]="Z:";
  pair<int,int> fic(0,0);
  lpnr->lpLocalName=(LPSTR)Z;
  depth++;
  if (WNetAddConnection2(lpnr,NULL,NULL,0)==NO_ERROR)
  {
    DEPTH;
    cons << "Connection opened." << crlf;
    pair<int,int> ficb;
    WIN32_FIND_DATA ffd;
    HANDLE h=FindFirstFile(drive,&ffd);
    if (h!=INVALID_HANDLE_VALUE)
    {
      fic=Enregistre(&ffd,drive);
      ficb=Explore(h,drive);
      fic.first+=ficb.first;
      fic.second+=ficb.second;
    }

    DEPTH;
    cons << "Indéxé : " << fic.second << " fichiers et " << fic.first << " dossiers." << crlf;
    nb_file+=fic.second;
    nb_dir+=fic.first;
    DEPTH;
    DWORD res=WNetCancelConnection2(Z,0,TRUE);
    cons << "Connexion closed." << crlf;
    if (res!=NO_ERROR)
      NetErrorHandler(res,"Critical Failure : can't close network device.",depth);
  }
  else
    NetErrorHandler(90,(LPSTR)"Can't connect to device",depth);
}

//Recois un fichiers ouun dossier trouvé sur le réseau pour l'indexer, et si besoin,
//lancer l'indéxation récursive de ce dossier.
pair<int,int> Enregistre(LPWIN32_FIND_DATA ffd,const char* dir)
{
  if (ffd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  {
    if (ffd->cFileName[0]=='.')
      return pair<int,int>(0,0);
    char dir_temp[MAX_PATH];
    pair<int,int> fichiers,fichiersb;
    CreateDirectory(ffd->cFileName,NULL);
    SetCurrentDirectory(ffd->cFileName);
    WIN32_FIND_DATA fd;
    HANDLE h=FindFirstFile(cat(dir_temp,dir,ffd->cFileName),&fd);
    if (h!=INVALID_HANDLE_VALUE)
    {
      fichiers=Enregistre(&fd,dir_temp);
      fichiersb=Explore(h,dir_temp);
      fichiers.first+=fichiersb.first+1;
      fichiers.second+=fichiersb.second;

    }
    else
      fichiers=pair<int,int>(1,0);
    SetCurrentDirectory("..");
    return fichiers;
  }
  else
  {
    CreateFile(ffd->cFileName,GENERIC_WRITE,0,NULL,CREATE_NEW,ffd->dwFileAttributes,NULL);
    return pair<int,int>(0,1);
  }
}

//Indexe le contenu d'un dossier du lecteur réseau.
pair<int,int> Explore(HANDLE h,const char* dir)
{
  WIN32_FIND_DATA ffd;
  pair<int,int> fichiers;
  while(FindNextFile(h,&ffd))
  {
    if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      if (ffd.cFileName[0]=='.')
        continue;
      char dir_temp[MAX_PATH];
      pair<int,int> fica,ficb;
      CreateDirectory(ffd.cFileName,NULL);
      SetCurrentDirectory(ffd.cFileName);
      WIN32_FIND_DATA fd;
      HANDLE hh=FindFirstFile(cat(dir_temp,dir,ffd.cFileName),&fd);
      if (hh!=INVALID_HANDLE_VALUE)
      {
        fica=Enregistre(&fd,dir_temp);
        ficb=Explore(hh,dir_temp);
        fichiers.first+=fica.first+ficb.first+1;
        fichiers.second+=fica.second+ficb.second;

      }
      else
        fichiers.first+=1;
      SetCurrentDirectory("..");
    }
    else
    {
      CreateFile(ffd.cFileName,GENERIC_WRITE,0,NULL,CREATE_NEW,ffd.dwFileAttributes,NULL);
      fichiers.second+=1;
    }
  }
  return fichiers;
}


//Concatène un nom de fichier du type "Z:\dossier\truc\*" et un nom du type "bidule"
//pour faire : "Z:\dossier\truc\bidule\*"
//Nécessaire car je me balade dans le dossier courant sur le disque local, mais pour
//parcourir le lecteur réseau, il me faut le nom complet du dossier.
char* cat(char* temp,const char* cur,const char* next)
{
  int i,j;
  for (i=0;cur[i]!='*';i++)
    temp[i]=cur[i];
  for (j=0;next[j]!=0;j++)
    temp[i+j]=next[j];
  temp[i+j]='\\';
  temp[i+j+1]='*';
  temp[i+j+2]=0;
  return temp;
}

//Dans un nom du type "\\machine\service" la fonction renvoie "service"
char* CreateDirName(const char* name)
{
  int i,j,k;
  for (i=0;name[i]!=0;i++);
  for (j=i;j>=0 && name[j]!='\\';j--);
  for (k=j+1;k<=i;k++)
    temp[k-j-1]=name[k];
  return temp;
}

/*
Si cat a besoin d'une chaine temporaire local c'est que les utilisation de la chaine qui
est renvoyée par cat peuvent se faire à des distance arbitraire (et avec des appel
récursif à cat entre eux). Alors que pour CreateDirName, toutes les utilisations se
font au retour de la fonction.
*/


int main(int arc,char** argv)
{
#ifndef NO_ADVIO
  POS p=cons.GetBufferSize();
  p.X=100;
  cons.SetBufferSize(p);
#endif
  CreateDirectory("Rezo",NULL);
  SetCurrentDirectory("Rezo");
  EnumerateFunc(NULL,0);
  cons << "Fin de l'opération." << crlf;
  cons << nb_file << " fichiers et " << nb_dir << " dossiers trouvés." << crlf;
#ifndef NO_ADVIO
  cons.pause(true);
#endif
}

