From ebebc8d189857fb8601c6910d05a69f8e1c2ae3b Mon Sep 17 00:00:00 2001 From: ArmandBahi <armand.bahi@veremes.com> Date: Mon, 15 Oct 2018 09:22:32 +0200 Subject: [PATCH] add gtf engines --- gtf.engine/engine/README.txt | 11 + gtf.engine/engine/compil-linux.bat | 21 + gtf.engine/engine/compil.bat | 34 + gtf.engine/engine/engine.c | 545 ++++++++++++ gtf.engine/engine/engine.h | 11 + gtf.engine/engine/php.ini | 162 ++++ gtf.engine/engine/sha1.c | 371 +++++++++ gtf.engine/engine/sha1.h | 54 ++ gtf.engine/gtf.engines/Changelog.txt | 1 + gtf.engine/gtf.engines/Traitement.class.inc | 781 +++++++++++++++++ gtf.engine/gtf.engines/constants.inc | 10 + gtf.engine/gtf.engines/engine | Bin 0 -> 32888 bytes gtf.engine/gtf.engines/engine.exe | Bin 0 -> 75276 bytes gtf.engine/gtf.engines/engine.php | 787 ++++++++++++++++++ gtf.engine/gtf.engines/engine.sql.inc | 17 + .../gtf.engines/lang_engines/en-lang.inc | 7 + .../gtf.engines/lang_engines/fr-lang.inc | 8 + gtf.engine/gtf.engines/php_engine_conf.inc | 4 + gtf.engine/gtf.engines/properties_engine.inc | 19 + gtf.engine/gtf.engines/regex2.dll | Bin 0 -> 79360 bytes gtf.engine/gtf.engines/string.inc | 59 ++ gtf.engine/gtf.engines/subscription.bat | 5 + gtf.engine/gtf.engines/subscription.php | 359 ++++++++ gtf.engine/gtf.engines/subscription.sql.inc | 8 + gtf.engine/gtf.engines/tcl/Traitement.tcl | 64 ++ gtf.engine/gtf.engines/tcl/commande.tcl | 0 gtf.engine/gtf.engines/tcl/tclUtil.tcl | 50 ++ .../gtf.messages/class/Action.class.inc | 74 ++ .../gtf.messages/class/BoAction.class.inc | 43 + .../gtf.messages/class/EmailAction.class.inc | 40 + .../gtf.messages/class/Message.class.inc | 155 ++++ .../gtf.messages/class/Message.class.sql.inc | 4 + gtf.engine/gtf.messages/processMessages.bat | 5 + gtf.engine/gtf.messages/processMessages.php | 173 ++++ gtf.engine/gtf.messages/processMessages.sh | 5 + .../gtf.messages/processMessages.sql.inc | 4 + 36 files changed, 3891 insertions(+) create mode 100755 gtf.engine/engine/README.txt create mode 100755 gtf.engine/engine/compil-linux.bat create mode 100755 gtf.engine/engine/compil.bat create mode 100755 gtf.engine/engine/engine.c create mode 100755 gtf.engine/engine/engine.h create mode 100755 gtf.engine/engine/php.ini create mode 100755 gtf.engine/engine/sha1.c create mode 100755 gtf.engine/engine/sha1.h create mode 100755 gtf.engine/gtf.engines/Changelog.txt create mode 100755 gtf.engine/gtf.engines/Traitement.class.inc create mode 100755 gtf.engine/gtf.engines/constants.inc create mode 100755 gtf.engine/gtf.engines/engine create mode 100755 gtf.engine/gtf.engines/engine.exe create mode 100755 gtf.engine/gtf.engines/engine.php create mode 100755 gtf.engine/gtf.engines/engine.sql.inc create mode 100755 gtf.engine/gtf.engines/lang_engines/en-lang.inc create mode 100755 gtf.engine/gtf.engines/lang_engines/fr-lang.inc create mode 100755 gtf.engine/gtf.engines/php_engine_conf.inc create mode 100755 gtf.engine/gtf.engines/properties_engine.inc create mode 100755 gtf.engine/gtf.engines/regex2.dll create mode 100755 gtf.engine/gtf.engines/string.inc create mode 100755 gtf.engine/gtf.engines/subscription.bat create mode 100755 gtf.engine/gtf.engines/subscription.php create mode 100755 gtf.engine/gtf.engines/subscription.sql.inc create mode 100755 gtf.engine/gtf.engines/tcl/Traitement.tcl create mode 100755 gtf.engine/gtf.engines/tcl/commande.tcl create mode 100755 gtf.engine/gtf.engines/tcl/tclUtil.tcl create mode 100755 gtf.engine/gtf.messages/class/Action.class.inc create mode 100755 gtf.engine/gtf.messages/class/BoAction.class.inc create mode 100755 gtf.engine/gtf.messages/class/EmailAction.class.inc create mode 100755 gtf.engine/gtf.messages/class/Message.class.inc create mode 100755 gtf.engine/gtf.messages/class/Message.class.sql.inc create mode 100755 gtf.engine/gtf.messages/processMessages.bat create mode 100755 gtf.engine/gtf.messages/processMessages.php create mode 100755 gtf.engine/gtf.messages/processMessages.sh create mode 100755 gtf.engine/gtf.messages/processMessages.sql.inc diff --git a/gtf.engine/engine/README.txt b/gtf.engine/engine/README.txt new file mode 100755 index 00000000..96459bda --- /dev/null +++ b/gtf.engine/engine/README.txt @@ -0,0 +1,11 @@ +Compilateur + + Le compilateur utilis� est MinGW version 5.1.6 + Il est disponible sur PICPOUL --> Partage --> Logiciel --> MinGW --> mingw-get-inst-20111118.exe + Une fois install�, copier le dossier openssl du dossier MinGW vers <MinGW>/include + + +Compilation + + Pour compiler, il suffit de double-cliquer sur compil.bat + La compilation r�cup�re le num�ro de r�vision SVN l'�crit dans le fichier Changelog.txt, compile l'application puis copie les fichiers dans le dossier gtf.engine diff --git a/gtf.engine/engine/compil-linux.bat b/gtf.engine/engine/compil-linux.bat new file mode 100755 index 00000000..d395b209 --- /dev/null +++ b/gtf.engine/engine/compil-linux.bat @@ -0,0 +1,21 @@ +@ECHO OFF +SET letter=%~d0 +SET currentPath=%~p0 +CALL :ChangeSlashes currentPath +CALL :LoCase letter + +REM Compilation +bash -c "cd /mnt/%letter%%currentPath% && gcc -g engine.c sha1.h sha1.c -o engine -LC:\MingW\lib\ -lregex" +REM copie vers le dossier gtf.engine +COPY "engine" "../gtf.engines/engine" +pause + +GOTO:EOF + +:LoCase +:: Subroutine to convert a variable VALUE to all lower case. +:: The argument for this subroutine is the variable NAME. +FOR %%i IN (":=" "A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%" + +:ChangeSlashes +FOR %%i IN ("\=/") DO CALL SET "%1=%%%1:%%~i%%" diff --git a/gtf.engine/engine/compil.bat b/gtf.engine/engine/compil.bat new file mode 100755 index 00000000..cee98ded --- /dev/null +++ b/gtf.engine/engine/compil.bat @@ -0,0 +1,34 @@ +@Echo OFF +%~d0 +cd %~dp0 +REM Recuperation du numero de version +for /f "delims=" %%i in ('svnversion "../engine"') do Set versionComp=%%i +REM Initialisation de la variable de recuperation de version +set versionComp = 2473:2481M +REM Verification de la structure de num de version renvoye pas SVN +echo %versionComp% | find ":" +REM Si le num de version contient : version complexe sinon version simple +if errorlevel 1 (call :simpleversion) else (call :complexeversion) + + +:simpleversion +REM Recuperation du num de version simple +for /f "delims=" %%i in ('echo %versionComp:~0,4%') do Set version=%%i +goto compil + +:complexeversion +REM Recuperation du num de version simple +for /f "delims=" %%i in ('echo %versionComp:~5,4%') do Set version=%%i +goto compil + +:compil +REM Suppression du fichier Changelog +IF EXIST Changelog.txt DEL Changelog.txt +REM Ecriture du num de version +echo %version% > Changelog.txt +REM Compilation en incluant la librairie psapi +gcc -g engine.c sha1.h sha1.c -o engine.exe -l"psapi" -LC:\MingW\lib\ -lregex +REM copie vers le dossier gtf.engine +COPY "engine.exe" "../gtf.engines/engine.exe" +COPY "Changelog.txt" "../gtf.engines/Changelog.txt" +pause \ No newline at end of file diff --git a/gtf.engine/engine/engine.c b/gtf.engine/engine/engine.c new file mode 100755 index 00000000..5dd2e7c7 --- /dev/null +++ b/gtf.engine/engine/engine.c @@ -0,0 +1,545 @@ +#ifdef __unix__ + #define OS_Windows 0 + #define MAX_PATH 4096 + #include <locale.h> + typedef unsigned long DWORD; + const char * separator = "/"; +#else + #define OS_Windows 1 + #include <windows.h> + #include <Winbase.h> + #include <tchar.h> + #include <psapi.h> + const char * separator = "\\"; +#endif +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include "sha1.h" +#include <time.h> +#include "regex.h" +#include "engine.h" + +#define TAILLE_MAX 1000 + +int num_pid = 0, debug = 0; +char *current_directory; +// const char * separator = "/"; + +int main(int argc, char *argv[]){ + int valid = 0;; + unsigned int i; + if (argc > 1){ + for (i = 0; i < strlen(argv[1]); ++i){ + if (!isdigit(argv[1][i])) + { + valid = 1; + break; + } + } + if (valid ==0){ + if (argc > 2){ + if (strcmp(argv[2],"d") == 0){ + debug = 1; + } + } + }else{ + printf("Syntaxe : engine.exe [moteur_number] [d]\n"); + printf("d : Debug mode\n"); + + exit(1); + } + }else{ + printf("Syntaxe : engine.exe [motor_number] [d]\n"); + printf("d : Debug mode\n"); + + exit(1); + } + //declaration des variables + int exist_pid_file = 0; + + // Nom du fichier pid + char* pid_file = malloc(strlen("pid_.txt") + strlen(argv[1]) + 1); + sprintf(pid_file, "pid_%s.txt", argv[1]); + int result = 0; + printf("1\n"); + //R�cup�ration du r�pertoire courant du fichier courant + #ifdef __unix__ + char *cwd_buffer = malloc(sizeof(char) * MAX_PATH); + current_directory = getcwd(cwd_buffer, MAX_PATH); + #else + char current_path[MAX_PATH]; + GetModuleFileName( NULL, current_path, MAX_PATH ); + current_directory = RemplacerFragment((char *)current_path, "\\engine.exe", ""); + #endif + writeToDebugLog("R�pertoire courant : ", current_directory); + //remplacement du nom de l exe par le nom du fichier pid + char* pid_file_path = malloc(strlen(current_directory) + strlen("/") + strlen(pid_file) + 1); + sprintf(pid_file_path, "%s%s%s", current_directory, separator, pid_file); + + writeToDebugLog("fichier PID : ", pid_file_path); + //test de l'existence des fichiers + exist_pid_file = findPidFile(pid_file_path); + + char tmp[5]; + sprintf(tmp, "%i", exist_pid_file); + writeToDebugLog("existence Fichier pid (0, n existe pas) : ", tmp); + char *motor_number = argv[1]; + //si fichier inexistant + if (exist_pid_file == 0){ + writeToDebugLog("Execution", " robot non existence fichier pid."); + result = execRobot(pid_file_path, motor_number); + } else { + //si fichier vide + if (num_pid == 0){ + writeToDebugLog("Execution", " robot fichier pid vide."); + result = execRobot(pid_file_path, motor_number); + } else { + sprintf(tmp, "%i", num_pid); + writeToDebugLog("num PID : ", tmp); + int process = 0; + process = verifyProcess(num_pid); + if (process == 0){ + writeToDebugLog("Execution", " robot processus fini."); + result = execRobot(pid_file_path, motor_number); + } + } + } + return result; +} + +//fonction de recherche d'un numero de pid +int findPidFile(char *pid_file_path){ + //declaration des variables + char line[512]; + int nb_lines = 0; + FILE *fic; + //ouverture du fichier + fic = fopen(pid_file_path, "r"); // ouvrir en lecture + + //si le programme n a pas trouve de fichier + if(fic == NULL){ + return 0; + } + //si le programme a trouvee un fichier + else{ + //lecture du fichier + while(fgets(line, 512, fic) != NULL){ + //recuperation du pid + num_pid = atoi(line); + //passage a la ligne suivante + nb_lines++; + } + //fermeture du fichier + fclose(fic); + return 1; + } +} + +//Fonction de remplacement de chaine de caractere par une autre : n'existe pas par defaut en c. +char *RemplacerFragment(char *source, char *vieux, char *nouveau){ + //Recuperation des parametres + char *original = source; + char temp[256]; + int ancienne_long = strlen (vieux); + int i, j, k, place = -1; + + //recherche de la position de l'occurence tant que la fonction n a pas trouvee + for (i = 0; source[i] && (place == -1); ++i){ + for (j = i, k = 0; source[j] == vieux[k]; j++, k++){ + if (!vieux[k+1]){ + place = i; + } + } + } + //remplacement de lancienne chaine par la nouvelle + if (place != -1){ + //recuperation de la chaine jusqu a l'emplacement de la chaine a remplacer + for (j=0; j < place; j++){ + temp[j] = source[j]; + } + //remplacement de l ancienne valeur par la nouvelle + for (i=0; nouveau[i]; i++, j++){ + temp[j] = nouveau[i]; + } + //reucperation de la fin de la chaine de caracteres + for (k = place + ancienne_long; source[k]; k++, j++){ + temp[j] = source[k]; + } + temp[j] = 0; + for (i=0; (source[i] = temp[i]); i++){ + } + } + return original; +} + +int execRobot(char *pid_file_path, char *motor_number){ + writeToDebugLog("path pid : ", pid_file_path); + getWritePid(pid_file_path); + char var_env_engine[MAX_PATH]; + char var_env_php[MAX_PATH]; + + strcpy(var_env_engine, "GTF_ENGINE_HOME="); + strcat(var_env_engine, current_directory); + + strcpy(var_env_php, "PHP_HOME="); + strcat(var_env_php, current_directory); + strcat(var_env_php, "/php"); + + writeToDebugLog("", var_env_php); + writeToDebugLog("", var_env_engine); + putenv(var_env_engine); + putenv(var_env_php); + putenv("PYTHONPATH="); + // Changement de la locale pour traiter les nombres + setlocale(LC_NUMERIC, "en_US.UTF8"); + + char *argument = verifyLicense(motor_number); + writeToDebugLog("value verification : ", argument); + char function_robot[512]; + if (OS_Windows == 0) { + sprintf(function_robot, "\"%s%sphp%sbin%sphp\" \"%s%sengine.php\" %s %s", current_directory, separator, separator, separator, current_directory, separator, motor_number, argument); + } else { + sprintf(function_robot, "\"\"%s%sphp%sphp.exe\" \"%s%sengine.php\" %s %s\"", current_directory, separator, separator, current_directory, separator, motor_number, argument); + } + writeToDebugLog("function : ", function_robot); + system(function_robot); + + remove(pid_file_path); + return 0; +} + +void getWritePid(char *pid_file_path){ + FILE *fic; + //ouverture du fichier + fic = fopen(pid_file_path, "w"); //"w" : mode texte en �criture (cr�ation) + //recuperation du pid + pid_t pid = getpid(); + fprintf(fic, "%i", pid); + fclose(fic); +} + + +int verifyProcess(int num_pid){ + int running = 0; + int i = 0; + int j = 0; + char *szProcessName; + char *szProcessNameWithSpaces; + #ifdef __unix__ + if (getpgid(num_pid) >= 0){ + FILE *fic; + char* process_path = malloc(strlen("/proc//comm") + 6); + sprintf(process_path, "/proc/%i/comm", num_pid); + //ouverture du fichier + fic = fopen(process_path, "r"); // ouvrir en lecture + + //si le programme n a pas trouve de fichier + if(fic != NULL){ + //lecture du fichier + char line[512]; + int nb_lines = 0; + while(fgets(line, 512, fic) != NULL){ + //recuperation du nom du pid + szProcessNameWithSpaces = line; + //passage a la ligne suivante + nb_lines++; + } + //trim + while(i < strlen(szProcessNameWithSpaces)){ + if (&szProcessNameWithSpaces[i] != " " || &szProcessNameWithSpaces[i] != "\n"){ + i++; + } else { + szProcessName[j] = szProcessNameWithSpaces[i]; + i++; + j++; + } + } + //fermeture du fichier + fclose(fic); + writeToDebugLog("Pid name: ", szProcessName); + writeToDebugLog("Exe file : engine", ""); + if (strcmp(szProcessName, "engine") == 0){ + running = 1; + } + } + } + #elif defined(_WIN32) + HANDLE hProcess=OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION,FALSE, num_pid); + if(hProcess){ + HMODULE hMod; + DWORD unused; + if(EnumProcessModules(hProcess, &hMod, sizeof(hMod), &unused)){ + GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(char)); + writeToDebugLog("Pid name: ", szProcessName); + writeToDebugLog("Exe file : engine.exe", ""); + if (strcmp(szProcessName, "engine.exe") == 0){ + running = 1; + } + } + } + #endif + return running; +} + +char *verifyLicense(char *motor_number){ + char *returnValue = ""; + char expireDate[TAILLE_MAX] = ""; + char motorNumber[TAILLE_MAX] = ""; + + char* licence_path = malloc(strlen(current_directory) + strlen("licenseslicense.txt") + 3); + sprintf(licence_path, "%s%slicenses%slicense.txt", current_directory, separator, separator); + + FILE* fichier = NULL; + writeToDebugLog("Fichier License : ",licence_path); + fichier = fopen(licence_path, "r"); + if (fichier != NULL){ + char chaine[TAILLE_MAX] = ""; + char testChaine[TAILLE_MAX] = ""; + // char *chaine = ""; + char *key; + char *temp; + char toKey[TAILLE_MAX] = ""; + int z = 0; + while (fgets(chaine, TAILLE_MAX, fichier) != NULL){ // On lit le fichier tant qu'on ne re�oit pas d'erreur (NULL) + if (chaine[0]!= '#'){ + RemplacerFragment(chaine, "\r", ""); + copie(chaine, testChaine); + if (strncmp( verifyRegexp(testChaine, "^Expiry date: (Permanent|[0-9]{6,8})"), "error", 1000) != 0){ + RemplacerFragment(testChaine, "Expiry date: ", ""); + copie(testChaine, expireDate); + } + if (strncmp( verifyRegexp(testChaine, "^Engines: ([0-9]+)"), "error", 1000) != 0){ + RemplacerFragment(testChaine, "Engines: ", ""); + copie(testChaine, motorNumber); + } + if (chaine[0]!= 'K'){ + if (z == 0){ + z = z+1; + }else{ + strcat(toKey, "\n"); + } + temp = RemplacerFragment(chaine, "\n", ""); + strcat(toKey, temp); + }else{ + if (chaine[1]!= 'e'){ + if (z == 0){ + z = z+1; + }else{ + strcat(toKey, "\n"); + } + temp = RemplacerFragment(chaine, "\n", ""); + strcat(toKey, temp); + }else{ + if (chaine[2]!= 'y'){ + if (z == 0){ + z = z+1; + }else{ + strcat(toKey, "\n"); + } + temp = RemplacerFragment(chaine, "\n", ""); + strcat(toKey, temp); + }else{ + key=RemplacerFragment(chaine, "Key: ", ""); + } + } + } + } + } + writeToDebugLog("Expire Date: ",expireDate); + writeToDebugLog("Motor Number: ",motorNumber); + writeToDebugLog("clef: ", key); + writeToDebugLog("Toclef: ", toKey); + SHA1Context sha; + int i; + SHA1Reset(&sha); + for(i = 1; i <= 1000; i++) { + SHA1Input(&sha, toKey, strlen(toKey)); + } + if (!SHA1Result(&sha)){ + writeToErrorLog("Erreur licence : ", "Impossible de hasher la clef"); + returnValue = "E001"; + }else{ + char clefTemp[5][TAILLE_MAX]; + char *clef, *clefTest; + for(i = 0; i < 5; i++){ + sprintf(clefTemp[i], "%X ", sha.Message_Digest[i]); + } + int j=0; + for(j = 0; j < 5; j++){ + clefTest= clefTemp[j]; + if (j == 0){ + clef = clefTest; + }else{ + strcat(clef, clefTest); + } + } + writeToDebugLog("clef calcul�e: ", clef); + int result = strncmp(clef, key, 1000); + if (result == 0){ + writeToDebugLog("Comparaison Clefs: ", "Egales"); + time_t current_time; + current_time = time(NULL); + struct tm *nowtm; + int yy, mm, dd; + yy = makeInt(expireDate + 0, 4) -1900; + mm = makeInt(expireDate + 4, 2)-1; + dd = makeInt(expireDate + 6, 2); + nowtm = localtime(¤t_time); + writeToDebugLog("Comparaison Date Nombre moteur: ",motorNumber); + writeToDebugLog("Comparaison Date num�ro moteur: ",motor_number); + if (strncmp(expireDate, "permanent", 1000) ==0){ + writeToDebugLog("Comparaison Date : ", "OK"); + if (atoi(motor_number) <= atoi(motorNumber)){ + writeToDebugLog("Comparaison Moteur : ", "Ok"); + returnValue = "0"; + }else{ + writeToDebugLog("Comparaison Moteur : ", "Num�ro de moteur trop grand"); + returnValue = "E003"; + } + }else{ + if (yy < nowtm->tm_year){ + writeToDebugLog("Comparaison Date : ", "Ann�e courante sup�rieure"); + writeToErrorLog("Erreur licence : ", "Ann�e courante sup�rieure"); + returnValue = "E002"; + }else{ + if (mm < nowtm->tm_mon){ + writeToDebugLog("Comparaison Date : ", "Mois courant sup�rieur"); + returnValue = "E002"; + }else{ + if (dd < nowtm->tm_mday){ + writeToDebugLog("Comparaison Date : ", "Jour courant sup�rieur"); + returnValue = "E002"; + }else{ + writeToDebugLog("Comparaison Date : ", "OK"); + if (atoi(motor_number) <= atoi(motorNumber)){ + writeToDebugLog("Comparaison Moteur : ", "Ok"); + returnValue = "0"; + }else{ + writeToDebugLog("Comparaison Moteur : ", "Num�ro de moteur trop grand"); + writeToErrorLog("Erreur licence : ", "Num�ro de moteur trop grand"); + returnValue = "E003"; + } + } + } + } + } + + }else{ + returnValue = "E001"; + writeToDebugLog("Comparaison Clefs : ", "Diff�rentes"); + writeToErrorLog("Erreur licence : ", "Clefs diff�rentes"); + } + } + fclose(fichier); + }else{ + // On affiche un message d'erreur si on veut + writeToErrorLog("Erreur licence : ", "Ouverture du fichier impossible"); + returnValue = "E001"; + } + + return returnValue; +} + +void writeToDebugLog(char *titre, char *error){ + if (debug == 1){ + FILE *fic; + //ouverture du fichier + char* debug_file_path = malloc(strlen(current_directory) + strlen("engine.log") + 2); + sprintf(debug_file_path, "%s%sengine.log", current_directory, separator); + fic = fopen(debug_file_path, "a"); //"w" : mode texte en �criture (cr�ation) + char resultat[MAX_PATH]; + strcpy(resultat, titre); + strcat(resultat, error); + strcat(resultat, "\n"); + //recuperation du pid + fprintf(fic, "%s", resultat); + fclose(fic); + } +} + +void writeToErrorLog(char *titre, char *error){ + FILE *fic; + //ouverture du fichier + char* error_file_path = malloc(strlen(current_directory) + strlen("engine_error.log") + 2); + sprintf(error_file_path, "%s%sengine_error.log", current_directory, separator); + fic = fopen(error_file_path, "a"); //"w" : mode texte en �criture (cr�ation) + char resultat[MAX_PATH]; + strcpy(resultat, titre); + strcat(resultat, error); + strcat(resultat, "\n"); + //recuperation du pid + fprintf(fic, "%s", resultat); + fclose(fic); +} + +char *verifyRegexp (char *str_request, char *str_regex){ + char *returnValue = "error"; + int err; + regex_t preg; + err = regcomp (&preg, str_regex, REG_EXTENDED); + if (err == 0){ + int match; + size_t nmatch = 0; + regmatch_t *pmatch = NULL; + nmatch = preg.re_nsub; + pmatch = malloc (sizeof (*pmatch) * nmatch); + if (pmatch){ + match = regexec (&preg, str_request, nmatch, pmatch, 0); + regfree (&preg); + if (match == 0){ + char *site = NULL; + int start = pmatch[0].rm_so; + int end = pmatch[0].rm_eo; + size_t size = end - start; + site = malloc (sizeof (*site) * (size + 1)); + if (site){ + strncpy (site, &str_request[start], size); + site[size] = '\0'; + returnValue = site; + free (site); + } + }else if (match == REG_NOMATCH){ + returnValue = "error"; + }else{ + char *text; + size_t size; + size = regerror (err, &preg, NULL, 0); + text = malloc (sizeof (*text) * size); + if (text){ + regerror (err, &preg, text, size); + returnValue = text; + free (text); + }else{ + returnValue = "error"; + } + } + }else{ + returnValue = "error"; + } + }else{ + returnValue = "error"; + } + + return returnValue; +} + +void copie(char srce[] , char dest[]) { + register int i; /* un indice est necessaire */ + for( i=0 ; (dest[i] = srce[i]) != 0 ; i++) + ; +} + +int makeInt(const char *p, int size){ + const char *endp; + int intval = 0; + endp = p + size; + while (p < endp){ + intval = intval * 10 + *p - '0'; + p++; + } + return intval; +} diff --git a/gtf.engine/engine/engine.h b/gtf.engine/engine/engine.h new file mode 100755 index 00000000..88fa8fbd --- /dev/null +++ b/gtf.engine/engine/engine.h @@ -0,0 +1,11 @@ +int execRobot(char *pid_file_path, char *motor_number); +void getWritePid(char *pid_file_path); +int verifyProcess(int num_pid); +int findPidFile(char *pid_file_path); +void copie(char srce[] , char dest[]); +void writeToDebugLog(char *titre, char *error); +void writeToErrorLog(char *titre, char *error); +char *RemplacerFragment(char *source, char *vieux, char *nouveau); +char *verifyRegexp(char *str_request, char *str_regex); +char *verifyLicense(char *motor_number); +int makeInt(const char *,int); \ No newline at end of file diff --git a/gtf.engine/engine/php.ini b/gtf.engine/engine/php.ini new file mode 100755 index 00000000..982525bc --- /dev/null +++ b/gtf.engine/engine/php.ini @@ -0,0 +1,162 @@ +[PHP] +engine = On +short_open_tag = Off +asp_tags = Off +precision = 14 +output_buffering = 4096 +zlib.output_compression = Off +implicit_flush = Off +unserialize_callback_func = +serialize_precision = 17 +disable_functions = +disable_classes = +zend.enable_gc = On +expose_php = On +max_execution_time = 30 +max_input_time = 60 +memory_limit = 128M +error_reporting = E_ALL & ~E_NOTICE +display_errors = Off +display_startup_errors = Off +log_errors = On +log_errors_max_len = 1024 +ignore_repeated_errors = Off +ignore_repeated_source = Off +report_memleaks = On +track_errors = Off +html_errors = On +error_log = php.log +variables_order = "GPCS" +request_order = "GP" +register_argc_argv = Off +auto_globals_jit = On +post_max_size = 8M +auto_prepend_file = +auto_append_file = +default_mimetype = "text/html" +include_path = ".;" +doc_root = +user_dir = + extension_dir = "ext" +enable_dl = Off +file_uploads = On +upload_max_filesize = 2M +max_file_uploads = 20 +allow_url_fopen = On +allow_url_include = Off +default_socket_timeout = 60 +extension=php_curl.dll +extension=php_gd2.dll +extension=php_ldap.dll +extension=php_mbstring.dll +extension=php_openssl.dll +extension=php_pdo_pgsql.dll +extension=php_xsl.dll +[CLI Server] +cli_server.color = On +[Date] +date.timezone = "EUROPE/Paris" +[Pdo_mysql] +pdo_mysql.cache_size = 2000 +pdo_mysql.default_socket= +[mail function] +SMTP = localhost +smtp_port = 25 +mail.add_x_header = On +[SQL] +sql.safe_mode = Off +[ODBC] +odbc.allow_persistent = On +odbc.check_persistent = On +odbc.max_persistent = -1 +odbc.max_links = -1 +odbc.defaultlrl = 4096 +odbc.defaultbinmode = 1 +[Interbase] +ibase.allow_persistent = 1 +ibase.max_persistent = -1 +ibase.max_links = -1 +ibase.timestampformat = "%Y-%m-%d %H:%M:%S" +ibase.dateformat = "%Y-%m-%d" +ibase.timeformat = "%H:%M:%S" +[MySQL] +mysql.allow_local_infile = On +mysql.allow_persistent = On +mysql.cache_size = 2000 +mysql.max_persistent = -1 +mysql.max_links = -1 +mysql.default_port = +mysql.default_socket = +mysql.default_host = +mysql.default_user = +mysql.default_password = +mysql.connect_timeout = 60 +mysql.trace_mode = Off +[MySQLi] +mysqli.max_persistent = -1 +mysqli.allow_persistent = On +mysqli.max_links = -1 +mysqli.cache_size = 2000 +mysqli.default_port = 3306 +mysqli.default_socket = +mysqli.default_host = +mysqli.default_user = +mysqli.default_pw = +mysqli.reconnect = Off +[mysqlnd] +mysqlnd.collect_statistics = On +mysqlnd.collect_memory_statistics = Off +[PostgreSQL] +pgsql.allow_persistent = On +pgsql.auto_reset_persistent = Off +pgsql.max_persistent = -1 +pgsql.max_links = -1 +pgsql.ignore_notice = 0 +pgsql.log_notice = 0 +[Sybase-CT] +sybct.allow_persistent = On +sybct.max_persistent = -1 +sybct.max_links = -1 +sybct.min_server_severity = 10 +sybct.min_client_severity = 10 +[bcmath] +bcmath.scale = 0 +[Session] +session.save_handler = files +session.use_strict_mode = 0 +session.use_cookies = 1 +session.use_only_cookies = 1 +session.name = PHPSESSID +session.auto_start = 0 +session.cookie_lifetime = 0 +session.cookie_path = / +session.cookie_domain = +session.cookie_httponly = +session.serialize_handler = php +session.gc_probability = 1 +session.gc_divisor = 1000 +session.gc_maxlifetime = 1440 +session.referer_check = +session.cache_limiter = nocache +session.cache_expire = 180 +session.use_trans_sid = 0 +session.hash_function = 0 +session.hash_bits_per_character = 5 +url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry" +[MSSQL] +mssql.allow_persistent = On +mssql.max_persistent = -1 +mssql.max_links = -1 +mssql.min_error_severity = 10 +mssql.min_message_severity = 10 +mssql.compatibility_mode = Off +mssql.secure_connection = Off +[Tidy] +tidy.clean_output = Off +[soap] +soap.wsdl_cache_enabled=1 +soap.wsdl_cache_dir="/tmp" +soap.wsdl_cache_ttl=86400 +soap.wsdl_cache_limit = 5 +[ldap] +ldap.max_links = -1 diff --git a/gtf.engine/engine/sha1.c b/gtf.engine/engine/sha1.c new file mode 100755 index 00000000..d87c7f48 --- /dev/null +++ b/gtf.engine/engine/sha1.c @@ -0,0 +1,371 @@ +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones <paulej@packetizer.com> + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} diff --git a/gtf.engine/engine/sha1.h b/gtf.engine/engine/sha1.h new file mode 100755 index 00000000..1ca4b104 --- /dev/null +++ b/gtf.engine/engine/sha1.h @@ -0,0 +1,54 @@ +/* + * sha1.h + * + * Copyright (C) 1998, 2009 + * Paul E. Jones <paulej@packetizer.com> + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, + const unsigned char *, + unsigned); + +#endif diff --git a/gtf.engine/gtf.engines/Changelog.txt b/gtf.engine/gtf.engines/Changelog.txt new file mode 100755 index 00000000..34a7e1fc --- /dev/null +++ b/gtf.engine/gtf.engines/Changelog.txt @@ -0,0 +1 @@ +2194 diff --git a/gtf.engine/gtf.engines/Traitement.class.inc b/gtf.engine/gtf.engines/Traitement.class.inc new file mode 100755 index 00000000..1a5c2f39 --- /dev/null +++ b/gtf.engine/gtf.engines/Traitement.class.inc @@ -0,0 +1,781 @@ +<?php + +/** + * \file Traitement.class.inc + * \brief Traitement.class.inc \n \n Ce fichier contient la classe php Traitement. + * + * Cette classe virtuelle permet de générer les éléments nécessaires à la création d'un rapport ou + * à l'éxécution d'un traitement FME. + * + * Cette classe permet de générer des fichiers textes, csv ou d'images pour la création de rapport. + * Elle permet aussi de lancer la création d'un rapport ou l'éxécution d'un traitement FME. + * + * \author Fabien Marty <fabien.marty@veremes.com> + * \update Matthieu Ambrosy <matthieu.ambrosy@veremes.com> + */ +require_once ("vmlib/phpUtil.inc"); +require_once ("vmlib/logUtil.inc"); +require_once ("vmlib/stringUtil.inc"); +require_once ("gtf_lib/GtfFmwParser.class.inc"); +require_once 'string.inc'; + +class Traitement { + + //Variable stockant l'url du log FME + var $sLogFme = ""; + + /** + * Identifiant de la demande. + */ + var $iDemandeId; + + /** + * Fichier de log. + */ + var $sRobotLogFile; + + /** + * Objet de la classe Bd. Connexion à la base de données + */ + var $oBd; + + /** + * Boolean spécifiant si le traitement est en erreur ou pas. + */ + var $bErreur = false; + + /** + * Boolean spécifiant si FME a subit un crash. + */ + var $bFmeCrash = false; + + /** + * Message d'erreur. + */ + var $sMessageErreur; + + /** + * Tableau des paramètres + */ + var $aParametre; + + /** + * Chaine contenant les parametres + */ + var $sParams; + + /** + * Emplacement du fichier projet FMW + */ + var $sFmwFileName; + + /** + * Tableau des propriétés provenant du fichier properties_robot.inc + */ + var $aProperties; + + /** + * Tableau des extensions associées à une source de données GUI + */ + var $aExtension; + var $sDestination; + var $sSource; + var $sSourceTemp = ""; + + /** + * Cette méthode permet d'initialiser les variables. + * \param $oBd Connexion à la base de données. + * \param $iDemandeId Identifiant de la demande. + * \param $sRobotLogFile Fichier de log. + */ + function __construct($oBd, $iDemandeId, $sRobotLogFile, $sParams, $aProperties, $sFmwFileName) { + $this->oBd = $oBd; + $this->iDemandeId = $iDemandeId; + $this->sRobotLogFile = $sRobotLogFile; + $this->sFmwFileName = $sFmwFileName; + $this->aParametre = $this->dbParamsAsArray($sParams); + $this->sParams = $sParams; + $this->aProperties = $aProperties; + // table de correspondance entre GTF et le FMW utiliser dans les fonction d'encodage + $this->aReplaceGTF = array("\r\n", " ", "(", ")", "$", "&", "@", "°", "'", ",", "\"", "{", "}", "[", "]", ";", "/", "\\", "é", "à"); + $this->aReplaceFME = array("<lf>", "<space>", "<openparen>", "<closeparen>", "<dollar>", "<amp>", "<at>", "<u00b0>", "<apos>", "<comma>", "<quote>", "<opencurly>", "<closecurly>", "<openbracket>", "<closebracket>", "<semicolon>", "<solidus>", "<backslash>", "<u00e9>", "<u00e0>"); + + writeToLog(str_replace('[this->sFmwFileName]', $this->sFmwFileName, str_replace('[sParams]', $sParams, INFO_RESOURCES)), $this->sRobotLogFile); + } + + /* + * Initialisation du traitement. + * \param $oBd Connexion à la base de données. + * \param $iDemandeId Identifiant de la demande. + * \param $sLogFile Fichier de log. + * \param $sParams Liste des paramètres saisis par l'utilisateur à la création de la demande. + * \param $aProperties Tableau des properties associés à l'application + * \param $sFmwFileName Nom du fichier FMW associé à la demande. + */ + + /** + * Cette méthode permet de générer un nom de fichier unique. + * \return une chaîne de caractères. + */ + function UniqFileName() { + $sUniqFileName = date('YmdHis') . rand(1, 100000); + return $sUniqFileName; + } + + /** + * Cette méthode permet de générer un tableau de paramètres à partir d'une chaîne dont les délimiteurs sont "|" et "=". + * \param $sAllParams Chaine composé de tous les paramètres. + * \return un tableau contenant les paramètres. + */ + function dbParamsAsArray($sAllParams) { + $aAllParams = Array(); + $aLigne = explode("|", $sAllParams); + //$aLigne = stringExplode($sAllParams, "|", "\"", "\""); + foreach ($aLigne as $sValeur) { + $aValeurs = explode("=", $sValeur, 2); + if ($aValeurs[1] == "") { + writeToLog(INFO_NULL_ELEMENT . $aValeurs[0] . ".", $this->sRobotLogFile); + } + if (!empty($aValeurs[2])) { + $sTemp = $aValeurs[1] . "=" . $aValeurs[2]; + $aValeurs[1] = $sTemp; + } + $aAllParams[$aValeurs[0]] = $aValeurs[1]; + writeToLog(str_replace('[aValeurs[0]]', $aValeurs[0], INFO_DB_PARAM) . $aValeurs[1], $this->sRobotLogFile); + } + return $aAllParams; + } + + /** + * Cette méthode permet d'éxécuter une ligne de commande et de stocker les paramètres dans un fichier + * \param $sCommand Ligne de commande à éxécuter. + */ + function execute($sCommand, $sFMELogFile) { + $sExeName = "FME"; + if (!$this->bErreur) { + set_time_limit(0); + //$sResultCommande = exec('"'.$this->aProperties["fme_path"].'" APPLY_SETTINGS "Python/Python Interpreter"', $aResult, $iResult); + $sResultCommande = exec('"' . $this->aProperties["fme_path"] . '" APPLY_SETTINGS "Python/Use Custom Python" false', $aResult, $iResult); + //writeToLog(INFO_INFORM.$sExeName."| ".'"'.$this->aProperties["fme_path"].'" APPLY_SETTINGS "Python/Python Interpreter"', $this->sRobotLogFile); + //writeToLog(INFO_INFORM.$sExeName."| ".$sResultCommande, $this->sRobotLogFile); + // $sResultCommande = exec('"'.$this->aProperties["fme_path"].'" APPLY_SETTINGS "Python/Python Interpreter" "C:/apps/FME-2015.1-b15477-32/fmepython27/python27.dll"', $aResult, $iResult); + // writeToLog(INFO_INFORM.$sExeName."| APPLY_SETTINGS \"Python/Python Interpreter\" \"C:/apps/FME-2015.1-b15477-32/fmepython27\"", $this->sRobotLogFile); + // writeToLog(INFO_INFORM.$sExeName."| ".$sResultCommande, $this->sRobotLogFile); + $sResultCommande = exec($sCommand, $aResult, $iResult); + if ($sResultCommande == "") { + $aResultCommande[0] = true; + } else { + $aResultCommande = explode("|", $sResultCommande); + } + //Récupération d'une erreur. + if ($iResult !=0){ + //if ($iResult || ($aResultCommande[0] != 0)) { + $this->bErreur = true; + if (sizeOf($aResultCommande) == 1) + $this->bFmeCrash = false; + } + //Écriture dans le log + if ($this->bErreur) { + if (!empty($aResultCommande[1])) { + if ($this->aProperties["convert_utf8"]) { + $aResultCommande[1] = utf8_decode($aResultCommande[1]); + } + $this->sMessageErreur = "Erreur lors de l exécution de la commande." . $aResultCommande[1] . "."; + } else { + $this->sMessageErreur = "Erreur lors de l exécution de la commande. Consultez le log du robot."; + } + $bExistFMELogFile = true; + if (file_exists($sFMELogFile) === false) { + $bExistFMELogFile = false; + } + foreach ($aResult as $sValeur) { + writeToLog(INFO_ERROR . " " . "| " . $sValeur, $this->sRobotLogFile); + if ($bExistFMELogFile === false){ + $monfichier = fopen($sFMELogFile, 'a'); + fputs($monfichier, $sValeur. "\n"); + fclose($monfichier); + } + } + writeToLog(INFO_COMMAND_ERROR . $sCommand . ".", $this->sRobotLogFile); + } else { + foreach ($aResult as $sValeur) { + if (mb_detect_encoding($sValeur, "UTF-8, ISO-8859-1") != "UTF-8") { + $sValeur = iconv("ISO-8859-1", "UTF-8", $sValeur); + } + writeToLog(INFO_INFORM . $sExeName . "| " . $sValeur, $this->sRobotLogFile); + } + writeToLog(INFO_SUCCESSFUL_TREATMENT, $this->sRobotLogFile); + } + } + } + + /** + * Cette méthode permet de 7zipper des fichiers. + * \param $sZipDir Emplacement des fichiers à zipper. + * \param $sZipUrl URL du fichier zip. + * \param $sZipFileName Nom du fichier zip à compresser. + * \return L'url du fichier zip. + */ + function zipFile($sZipDir, $sZipFileDir, $sZipUrl, $sZipFileName) { + writeToLog(str_replace('[sZipDir]', $sZipDir, INFO_FILE_COMPRESSION), $this->sRobotLogFile); + if (createZip($sZipDir, $sZipFileDir . '/' . $sZipFileName . '.zip', 'log')) { + $sZipFile = $sZipUrl . "/" . $sZipFileName . ".zip"; + writeToLog(INFO_COMPRESSED_FILES . $sZipFile . ".", $this->sRobotLogFile); + } else { + $this->bErreur = true; + $sZipFile = ""; + writeToLog(INFO_TREATMENT_FAILURE, $this->sRobotLogFile); + writeToLog(str_replace('[sZipFileDir]', $sZipFileDir, str_replace('[sZipDir]', $sZipDir, INFO_FILES_COMPRESSION_FAILURE)) . $sZipFileName, $this->sRobotLogFile); + $this->sMessageErreur = "Le traitement n\'a produit aucun résultat. Consultez son log."; + } + return $sZipFile; + } + + function unZipSource($sParam) { + $this->sSource = $sParam; + // Si la chaine contient un accent, la chaine sera en UTF-8, la fonction file_exists et la fonction de dézippage ne gèrent pas utf-8, il faut donc convertir la chaine en ISO-8859-1 + $bUtf8Param = false; + if (mb_check_encoding($sParam, 'UTF-8')) { + $sParam = utf8_decode($sParam); + $bUtf8Param = true; + } + // Si le fichier n'est pas trouvé alors il se trouve dans le répertoire upload + if (!file_exists($sParam)) + $sParam = $this->aProperties["upload_dir"] . "/" . $sParam; + + // If faut gérer l'ensemble des zip (.zip, .7z, .rar, bzip2, gzip, tar, wim, xz) + if (mb_eregi("(.ZIP)$", strtoupper($sParam), $aOccurence)) { + // Décompression du fichier zip. + $sDir = $this->unZipFile($sParam); + } else { // Le fichier uploadé n'est pas un fichier zippé + $sDir = $sParam; + $this->sSourceTemp = ""; + } + // Si le paramètre passé était encodé en UTF-8 il faut le réencoder sinon problème avec les accents. + if ($bUtf8Param) + $sDir = utf8_encode($sDir); + return $sDir; + } + + /** + * Cette méthode permet de dézipper un fichier. + * \param $sZipFile Le fichier zip à décompresser. + * \return L'emplacement des fichiers qui ont été extraits. + */ + function unZipFile($sZipFile) { + if (!$this->bErreur) { + writeToLog(INFO_DECOMPRESSING_FILE . $sZipFile . ".", $this->sRobotLogFile); + // Les backslashes du chemin de la variable d'environnement TEMP sont remplacés par des slashes + $this->aProperties["extract_dir"] = str_replace("\\", "/", $this->aProperties["extract_dir"]); + $sExtractDir = $this->aProperties["extract_dir"] . "/" . $this->UniqFileName() . "/"; + $this->sSourceTemp = $sExtractDir; + if (unZip($sZipFile, $sExtractDir)) { + writeToLog(INFO_FILE_COMPRESSION_DIRECTORY . $sExtractDir . ".", $this->sRobotLogFile); + } else { + $this->bErreur = true; + writeToLog(INFO_TREATMENT_FAILURE, $this->sRobotLogFile); + writeToLog(str_replace('[sZipFile]', $sZipFile, INFO_FILE_COMPRESSION_FAILURE) . $sExtractDir . ".", $this->sRobotLogFile); + $this->sMessageErreur = "Impossible de dézipper le fichier. Consultez le log du robot."; + $sExtractDir = ""; + } + return $sExtractDir; + } + } + + /* + * Fonction qui se charge de la mise à jour du paramètre "source" s'il existe + * $oGtfFmwParser : Objet GtfFmwParser contenant les GUI + * $oGui : Objet GUI de la source + * $sDirSource : Chaine de caractère contenant le chemin d'accés à la source de données + */ + + function updateSourceParam($oGui, $oGtfFmwParser, $sDirSource, $bUsePattern) { + $sdelimiterFichier = '"""'; + $sdelimiterParam = '"'; + $sSourceMacro = $sdelimiterFichier; + $aSourceMacro = array(); + $i = 0; + + if (is_dir($sDirSource)) { + $this->aExtension = $oGui->getExtensionArrayNoLower($oGui->sFiltreSource); + $iExtension = count($this->aExtension); + if (($this->aExtension[0] == "*.*" and $oGui->sType == "FILE_OR_URL") or $oGui->sType == "DIRNAME_SRC" or $oGui->sType == "SOURCE_GEODATABASE") { + writeToLog(INFO_VALID_DATA_SOURCE_FILE_EXTENSION . $this->aExtension[0], $this->sRobotLogFile); + $sSourceMacro.= $sdelimiterParam . $sDirSource . '/**/' . $sdelimiterParam; + } else { + if ($bUsePattern){ + writeToLog(INFO_USE_PATTERN, $this->sRobotLogFile); + foreach ($this->aExtension as $sExtension) { + if ($sExtension != "*.*" or ( $sExtension == "*.*" and $iExtension == 1)) { // Si le filtre est différent de *.* (ex : *.mdb) ou si le filtre est seulement *.*. Si le filtre est multiple et que l'un d'eux est *.* (ex : *.*, *.mdb...), il ne sera pas pris en compte + $aExtension = explode(".", $sExtension); + if ($this->countFilesExtension($sDirSource, $aExtension[1]) > 0) { + if ($i != 0) + $sSourceMacro.= "<space>"; + writeToLog(INFO_VALID_DATA_SOURCE_FILE_EXTENSION . $sExtension, $this->sRobotLogFile); + $sSourceMacro.= $sdelimiterParam . $sDirSource . '/**/' . $sExtension . $sdelimiterParam; + $i++; + } + } + } + }else{ + writeToLog(INFO_NO_USE_PATTERN, $this->sRobotLogFile); + foreach ($this->aExtension as $sExtension) { + if ($sExtension != "*.*" or ( $sExtension == "*.*" and $iExtension == 1)) { // Si le filtre est différent de *.* (ex : *.mdb) ou si le filtre est seulement *.*. Si le + $aExtension = explode(".", $sExtension); + $aSourceMacroTmp = array_merge($aSourceMacro, $this->returnFileListToString($sDirSource, $aExtension[1])); + $aSourceMacro = array_unique($aSourceMacroTmp); + $i++; + } + } + foreach ($aSourceMacro as $source) { + if ($sSourceMacro != $sdelimiterFichier){ + $sSourceMacro.= "<space>"; + } + $sSourceMacro.= $sdelimiterParam . $source . $sdelimiterParam; + } + } + } + }else { + $sSourceMacro.= $sdelimiterParam . $sDirSource . $sdelimiterParam; + } + $sSourceMacro .= $sdelimiterFichier; + $this->aParametre[$oGui->sDefault_Macro] = $sSourceMacro; + + return $sSourceMacro; + } + + // Compte le nombre de fichier + // $sDirSource : Répertoire de base + // $sExtension : Filtre + function countFilesExtension($sDirSource, $sExtension) { + $num = 0; + if (!is_dir($sDirSource)) { + $path_parts = pathinfo($sDirSource); + if (is_file($sDirSource) && $path_parts['extension'] == $sExtension) + return 1; + else + return false; + } + foreach (scandir($sDirSource) AS $entry) { + if (!in_array($entry, array('..', '.'))) + $num += $this->countFilesExtension($sDirSource . "/" . $entry, $sExtension); + } + return $num; + } + + function returnFileListToString($sDirSource, $sExtension) { + $aFileList = array(); + if (!is_dir($sDirSource)) { + $path_parts = pathinfo($sDirSource); + if (is_file($sDirSource) && strtolower($path_parts['extension']) == strtolower($sExtension)){ + array_push($aFileList,$sDirSource); + } + + }else{ + foreach (scandir($sDirSource) AS $entry) { + if (!in_array($entry, array('..', '.'))){ + $aFileListTmp = $aFileList; + $aFileList = array_merge($aFileListTmp,$this->returnFileListToString($sDirSource . "/" . $entry, $sExtension)); + } + + } + } + return $aFileList; + } + + /* + * Fonction qui se charge de la mise à jour du paramètre "source" s'il existe + * $oGtfFmwParser : Objet GtfFmwParser contenant les GUI + * $oGui : Objet GUI de la source + * $sDirSource : Chaine de caractère contenant le chemin d'accés à la source de données + */ + + function updateDestParam($oGui, $oGtfFmwParser, $sNewDir, $sDest) { + $this->aExtension = $oGui->getExtensionArray($oGui->sFiltreDest); + if ($sDest == "" && $oGui->sType != "DIRNAME") { + $sDest = $sNewDir; + } + $this->aParametre[$oGui->sDefault_Macro] = ""; + $sDest = normalizeString($sDest); + $aDest = explode(".", $sDest); + if ($oGui->sType == "DIRNAME" or $oGui->sType == "DEST_GEODATABASE") { + $this->aParametre[$oGui->sDefault_Macro] = $this->aProperties["dir_export"] . "/gtf/" . $sNewDir . "/" . $sDest; + writeToLog(INFO_OUTPUT_DATA_DIRECTORY . $this->aParametre[$oGui->sDefault_Macro], $this->sRobotLogFile); + } + if ($oGui->sType == "FILENAME") { + if (count($aDest) > 1) { // Dans GTF, la valeur du champ [nom de fichier en sortie] comporte une extension. Ex : result.xls (le fichier résultat sera de l'excel et sera nommé result. + $sResult = $sDest; + } else { + $sResult = str_replace("*", $sDest, $this->aExtension[0]); + } + $this->aParametre[$oGui->sDefault_Macro].= $this->aProperties["dir_export"] . "/gtf/" . $sNewDir . '/' . $sResult; + $this->sDestination = $sResult; + + writeToLog(INFO_OUTPUT_DATA_FILE . $this->aParametre[$oGui->sDefault_Macro], $this->sRobotLogFile); + } + else if ($oGui->sType == "FILENAME_EXISTING") + $this->aParametre[$oGui->sDefault_Macro] = $sDest; + return $this->aParametre[$oGtfFmwParser->sDestMacro]; + } + + /* * *************************************************************************************** */ + /* Codage */ + + function unichr($u) { + return mb_convert_encoding('&#' . intval($u) . ';', 'UTF-8', 'HTML-ENTITIES'); + } + + function ordutf8($string, &$offset) { + $code = ord(substr($string, $offset, 1)); + if ($code >= 128) { //otherwise 0xxxxxxx + if ($code < 224) + $bytesnumber = 2; //110xxxxx + else if ($code < 240) + $bytesnumber = 3; //1110xxxx + else if ($code < 248) + $bytesnumber = 4; //11110xxx + $codetemp = $code - 192 - ($bytesnumber > 2 ? 32 : 0) - ($bytesnumber > 3 ? 16 : 0); + for ($i = 2; $i <= $bytesnumber; $i++) { + $offset ++; + $code2 = ord(substr($string, $offset, 1)) - 128; //10xxxxxx + $codetemp = $codetemp * 64 + $code2; + } + $code = $codetemp; + } + $offset += 1; + if ($offset >= strlen($string)) + $offset = -1; + return $code; + } + + function encoderFME($sStr) { + //table de correcpondance des symboles UTF-8 avec les codes FME/GTF + // ATTENTION : laisser < > en premier sinon replace remplace les balises par d'autre balise + $aReplaceGTFToFMW = array("<" => "<lt>", ">" => "<gt>"); + $sReplaced = $sStr; + + //on gere les chevrons simultanèment pour ne pas gener l'encodage après + if (strpos($sReplaced, "<") !== false && strpos($sReplaced, ">") !== false) { + $sReplaced = strtr($sReplaced, $aReplaceGTFToFMW); + } + + //on remplace tous les encodage par leurs symboles + for ($i = 0; $i < count($this->aReplaceGTF); $i++) { + if (strpos($sReplaced, $this->aReplaceGTF[$i]) !== false) { + $sReplaced = str_replace($this->aReplaceGTF[$i], $this->aReplaceFME[$i], $sReplaced); + } + } + + // on encode les balises UTF-8 + + $offset = 0; + $iPos = 0; + + while ($offset >= 0) { + $iCode = $this->ordutf8($sReplaced, $offset); + if ($iCode > 127) { + $cLetter = dechex($iCode); + while (strlen($sLetter) < 4) { + $cLetter = "0" . $cLetter; + } + $sReplaced = str_replace($sReplaced[$iPos], "<u" . $cLetter . ">", $sReplaced); + } + $iPos += 1; + } + + return $sReplaced; + } + + function decoderFME($sStr) { + //table de correcpondance des symboles UTF-8 avec les codes FME/GTF + // ATTENTION : laisser < > en premier sinon replace remplace les balises par d'autre balise + $aReplaceFMWToGTF = array("<lt>" => "<", "<gt>" => ">"); + $sReplaced = $sStr; + + //on gere les chevrons simultanèment pour ne pas gener l'encodage après + if (strpos($sReplaced, "<lt>") !== false && strpos($sReplaced, "<gt>") !== false) { + $sReplaced = strtr($sReplaced, $aReplaceFMWToGTF); + } + //on remplace tous les encodage par leurs symboles + for ($i = 0; $i < count($this->aReplaceGTF); $i++) { + if (strpos($sReplaced, $this->aReplaceFME[$i]) !== false) { + $sReplaced = str_replace($this->aReplaceFME[$i], $this->aReplaceGTF[$i], $sReplaced); + } + } + + // on decode les balises UTF-8 + $matches = array(); + + preg_match_all("/<u[0-9a-fA-F]{4}>/", $sStr, $matches); + + for ($i = 0; $i < count($matches[0]); $i++) { + $iCode = hexdec(substr($matches[0][$i], 2, 4)); + $cChr = $this->unichr($iCode); + $sReplaced = str_replace($matches[0][$i], $cChr, $sReplaced); + } + return $sReplaced; + } + + function isDecode($sStr) { + for ($i = 0; $i < count($this->aReplaceFME); $i++) { + if (strpos($sStr, $this->aReplaceFME[$i]) !== false && strpos($sStr, $this->aReplaceGTF[$i]) === false) { + return false; + } + } + return true; + } + + function isEncode($sStr) { + for ($i = 0; $i < count($this->aReplaceFME); $i++) { + if (strpos($sStr, $this->aReplaceFME[$i]) === false && strpos($sStr, $this->aReplaceGTF[$i]) !== false) { + return false; + } + } + return true; + } + + /* * *************************************************************************************** */ + + /* + * Fonction qui permet de concaténer tous les paramètres nécessaire pour l'appel du TCL + * et qui retourne $sChaine, la chaine des paramètres à passer au TCL + * $oGtfFmwParser : Objet GtfFmwParser contenant les GUI + */ + + function getTclParams($oGtfFmwParser, $sNewDir) { + $aTGui = array(); + $sChaine = ""; + $bUsePattern = false; + foreach ($oGtfFmwParser->aGuiObject as $aGui) { + if ($aGui->sDefault_Macro != "") { + array_push($aTGui, $aGui); + } + if ($aGui->sDefault_Macro == "GTF_FORCE_PATTERN") { + if (strtolower($aGui->sDefaultValue) == "true"){ + $bUsePattern = true; + } + } + } + foreach ($aTGui as $oGui) { + //Teste si le paramètre est une source + if ($oGui->bIsSource) { + if ($this->aParametre[$oGui->sDefault_Macro] != "" && substr($this->aParametre[$oGui->sDefault_Macro], 0, 7) != "<quote>") { + $sDirSource = $this->unZipSource($this->aParametre[$oGui->sDefault_Macro]); + $this->updateSourceParam($oGui, $oGtfFmwParser, $sDirSource, $bUsePattern); + } + } + if ($oGui->bIsDest) { + $sDest = $this->aParametre[$oGui->sDefault_Macro]; + $this->updateDestParam($oGui, $oGtfFmwParser, $sNewDir, $sDest); + } + + if ($this->aParametre[$oGui->sDefault_Macro] != "") { + + if (!$oGui->bIsSource) { + // Protection des guillemets de chaque valeur pour qu'ils soient correctement envoyé à traitement.tcl + // Cette protection s'effectue uniquement pour les paramètre qui ne sont pas sources (un paramètre source est de ce type : """"param1"" ""param2"" ""param3"""" : les guillemets seraient donc protégé et cela fait planter l'exécution) + //$this->aParametre[$oGui->sDefault_Macro] = str_replace('"', '""', $this->aParametre[$oGui->sDefault_Macro]); + } + $aBaliseToReplace = array('<GTF_EQUAL>', '<GTF_PIPE>'); + $aReplacedBalise = array('=', '|'); + $sGTFOutput = str_replace($aBaliseToReplace, $aReplacedBalise, $this->aParametre[$oGui->sDefault_Macro]); + switch ($oGui->sType) { + case "FLOAT": + $sGTFOutput = str_replace(",", ".", $sGTFOutput); + if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + case "LOOKUP_CHOICE": + if (!$this->isEncode($sGTFOutput)) + $sGTFOutput = $this->encoderFME($sGTFOutput); + break; + case "TEXT_EDIT" : + case "TEXT_EDIT_OR_NUM" : + if (!$this->isEncode($sGTFOutput)) { + //$sGTFOutput = $this->decoderFME($sGTFOutput); + $sGTFOutput = $this->encoderFME($sGTFOutput); + } + break; + case "CHOICE": + case "LOOKUP_LISTBOX": + $sGTFOutput = str_replace('"', '""', $sGTFOutput); + if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + default : + //$sGTFOutput = str_replace('"', '""', $sGTFOutput); + if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + } + // Echappement des doubles quotes pour FME. + $sGTFOutput = str_replace('"', '\"', $sGTFOutput); + // + $sChaine .= " --" . $oGui->sDefault_Macro . " \"" . $sGTFOutput . "\""; + } + } + + // $sChaine = substr($sChaine, 0, -1)."\""; + //$sChaine = substr($sChaine, 0, -1); + return $sChaine; + } + + function listDir($sDossier, $iDemandeId) { + $ouverture = @opendir($sDossier); + if (!$ouverture) + return false; + while ($sFichier = readdir($ouverture)) { + if ($sFichier == '.' || $sFichier == '..') + continue; + if (is_dir($sDossier . $sFichier)) { + closedir($ouverture); + return true; + } else { + $path_parts = pathinfo($sDossier . $sFichier); + if ($sFichier != "fme_" . $iDemandeId . ".log") { + closedir($ouverture); + return true; + } + } + } + closedir($ouverture); + return False; + } + + /* + * Fonction qui retourne le nom du fichier résultat qui sera zippé. + * $sDossier correspond au chemin complet dans lequel les fichiers/dossiers résultats seront générés + * $sNewDir correspond au numéro unique du dossier dans lequel les fichiers/dossiers résultats seront générés + * $iDemandeId correspond au numéro de la demande + * \return Retourne NULL (s'il y a plusieurs fichiers résultat à zipper) ou le fichier résultat qui sera zippé sous la forme 20140324141501876/ventes.xlsx + */ + + function CheckResult($sDossier, $sNewDir, $iDemandeId) { + $ouverture = @opendir($sDossier); + $NbFile = 0; + while ($sFichier = readdir($ouverture)) { + if ($sFichier == '.' || $sFichier == '..') + continue; + if (is_dir($sDossier . $sFichier)) { + $NbFile = $NbFile + 2; + } else { + $path_parts = pathinfo($sDossier . $sFichier); + if ($sFichier != "fme_" . $iDemandeId . ".log") { + $NbFile = $NbFile + 1; + } + } + } + closedir($ouverture); + $ouverture = @opendir($sDossier); + if ($NbFile != 1) { + return ""; + } else { + $pattern = '/^' . str_replace("*", ".+", str_replace(".", "\\.", $this->aProperties['not_compressed_extension'])) . '$/'; + while ($sFichier = readdir($ouverture)) { + if ($sFichier == '.' || $sFichier == '..') + continue; + if ($sFichier != "fme_" . $iDemandeId . ".log") { + if (preg_match($pattern, $sFichier)) { + return $sNewDir . "/" . $sFichier; + } else { + return ""; + } + } + } + } + closedir($ouverture); + } + + /* + * fonction qui lance la génération de la ligne de commande, l'éxécution de la demande et la mise en forme du résultat + * \return Retourne les fichiers résultant zippés + */ + + function Process() { + $oGtfFmwParser = new GtfFmwParser($this->aProperties["workspace_dir"] . "/" . $this->sFmwFileName); + // Création du répertoire de sortie des données + $sNewDir = $this->UniqFileName(); + if (!file_exists($this->aProperties["dir_export"] . "/gtf/")){ + mkdir($this->aProperties["dir_export"] . "/gtf/"); + } + $sDestDir = $this->aProperties["dir_export"] . "/gtf/" . $sNewDir; + mkdir($sDestDir); + // Création de l'URL de téléchargement des données + $sDestUrl = $this->aProperties["url_export"] . "/gtf/" . $sNewDir; + + //1 Initialisation & calcul du chemin d'accés du fichier TCL + $sCommandLine = '"' . $this->aProperties["fme_path"] . '" "' . getenv("GTF_HOME") . "/" . utf8_decode($this->sFmwFileName) . '"'; + + + + //3 Ajout des paramètres propres au traitement + $sTclParams = $this->getTclParams($oGtfFmwParser, $sNewDir); + if ($sTclParams != "") + $sCommandLine .= $sTclParams; + + $aTGui = array(); + $sChaine = ""; + // Ajout des paramètres GTF_ s'ils sont associés à un paramètre publié du traitement + foreach ($oGtfFmwParser->aGuiObject as $aGui) { + switch($aGui->sDefault_Macro) { + case 'GTF_ORDER_ID': + $sCommandLine .= " --GTF_ORDER_ID \"" . $this->iDemandeId . "\""; + break; + case 'GTF_SHARED_DIR': + $sCommandLine .= " --GTF_SHARED_DIR \"" . $this->aProperties["shared_dir"] . "\""; + break; + case 'GTF_CONNECTION_STRING': + if ($aGui->sType == "PASSWORD"){ + $sCommandLine .= " --GTF_CONNECTION_STRING \"" . $this->aProperties["server"] . "," . $this->aProperties["port"] . "," . $this->aProperties["database"] . "," . $this->aProperties["login_scheduler"] . "," . $this->aProperties["password_scheduler"] . "\""; + } + break; + case 'GTF_REST_URL': + $sCommandLine .= " --GTF_REST_URL \"" . $this->aProperties["web_server_name"] . "/" . $this->aProperties["services_alias"] . "/\""; + break; + //case 'GTF_FORCE_PATTERN': + } + + } + /* + * 2 Passage des paramètres généraux + * - FMWFILENAME : fichier Fmw + * - LOG_ROBOT : fichier log du robot + * - LOG_FILENAME : fichier log pour FME + */ + $sCommandLine.= " -LOG_FILENAME \"" . $sDestDir . "/fme_" . $this->iDemandeId . ".log\" 2>&1"; + writeToLog(INFO_GTF_HOME . getenv("GTF_HOME"), $this->sRobotLogFile); + writeToLog(INFO_FME_PATH . getenv("FME_PATH"), $this->sRobotLogFile); + writeToLog(INFO_TREATMENT_COMMAND_LINE . $sCommandLine, $this->sRobotLogFile); + + //4 Lancement de la ligne de commande + $this->sLogFme = $sNewDir . "/fme_" . $this->iDemandeId . ".log"; + $sSql = "UPDATE " . $this->aProperties['schema_gtf'] . ".order SET log_url='" . $this->sLogFme . "' WHERE order_id=" . $this->iDemandeId; + $this->oBd->execute($sSql); + $this->execute($sCommandLine, $sDestDir . "/fme_" . $this->iDemandeId . ".log"); + writeToLog(INFO_TREATMENT_COMMAND_LINE . $sCommandLine, $this->sRobotLogFile); + if (file_exists($sDestDir . "/fme_" . $this->iDemandeId . ".log") == false) { + $this->sLogFme = ""; + } + $bHasFile = $this->listDir($sDestDir, $this->iDemandeId); + if ($this->sSourceTemp != "" && $this->aProperties["debug_mode"] == false) { + clearDir($this->sSourceTemp); + } + if ($bHasFile) { + $sIsToZip = $this->CheckResult($sDestDir, $sNewDir, $this->iDemandeId); + + if ($sIsToZip == "") { + //Compression en zip des fichiers résultant. Le parametres Array() permet de filtrer les extensions à compresser. Exemples : Array("shp","shx","dbf") ; Array("*") + $sZipFile = $this->zipFile($sDestDir, $sDestDir, $sDestUrl, $this->iDemandeId); + $sZipFile = str_replace($sDestUrl, $sNewDir, $sZipFile); + } else { + $sZipFile = str_replace($sDestUrl, $sNewDir, $sIsToZip); + } + } else { + $sZipFile = ""; + } + return $sZipFile; + } + +} + +?> diff --git a/gtf.engine/gtf.engines/constants.inc b/gtf.engine/gtf.engines/constants.inc new file mode 100755 index 00000000..d4d3d5ab --- /dev/null +++ b/gtf.engine/gtf.engines/constants.inc @@ -0,0 +1,10 @@ +<?php +// Définition des constantes +if (!defined("CR")) { + define ("CR",chr(13)); + define ("LF",chr(10)); + define ("HT",chr(9)); +} +define ("VM_FALSE",0); +define ("VM_TRUE",1); +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/engine b/gtf.engine/gtf.engines/engine new file mode 100755 index 0000000000000000000000000000000000000000..53b01477108bf7e5f77ce52eb5b0c48c458e2dd6 GIT binary patch literal 32888 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 literal 0 HcmV?d00001 diff --git a/gtf.engine/gtf.engines/engine.exe b/gtf.engine/gtf.engines/engine.exe new file mode 100755 index 0000000000000000000000000000000000000000..299a2bc1ca56950134511a6694772126a6f3c03c GIT binary patch literal 75276 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 Zxaa`_8bM41^%TH9#F77@NgO)20RSHYV^{zH literal 0 HcmV?d00001 diff --git a/gtf.engine/gtf.engines/engine.php b/gtf.engine/gtf.engines/engine.php new file mode 100755 index 00000000..70a33d33 --- /dev/null +++ b/gtf.engine/gtf.engines/engine.php @@ -0,0 +1,787 @@ +<?php + +/** + * \file engine.php + * \brief engine.php \n \n Ce fichier contient un programme php. + * + * Ce programme permet de traiter des demandes de traitements. + * + * \author Fabien Marty <fabien.marty@veremes.com> + */ +$properties["id_gtf_engine"] = $_SERVER['argv'][1]; //Identifiant du moteur calculé à partir de la valeur passée en argument +$ErrorLicense = $_SERVER['argv'][2]; //Identifiant du moteur calculé à partir de la valeur passée en argument + +require_once ("php_engine_conf.inc"); +require_once ("vmlib/BD.class.inc"); +require_once ("vmlib/logUtil.inc"); +require_once("vmlib/dbUtil.inc"); +require_once ("gtf_lib/phpUtility.inc"); +require_once("vmlib/phpUtil.inc"); + +require_once ("properties_engine.inc"); +require_once ("engine.sql.inc"); +//require_once ("PEAR/Mail.php"); +require_once("Ldap.class.inc"); + +require_once("vmlib/Email.class.inc"); +require_once("gtf_lib/gtf_object/Order.class.inc"); +require_once("vmlib/error.inc"); +require_once 'string.inc'; + +loadLang("engines", $properties["language"]); +if ($ErrorLicense == "0") { + //writeToLog('|INFO |PHP| dump prop '. var_export($properties,true),$properties["engine_log_file"]); + + $sDateDebut = new DateTime(date("Y-m-d H:i:s")); + $sDateFin = new DateTime(date("Y-m-d H:i:s")); + + //Connexion à la base de données de Données gtf : pas de mot de passe pour le robot + $oBd = new BD($properties['login_scheduler'], $properties['password_scheduler'], $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); + //$oBd = new Vm ('admin', 'admin', $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); + if ($oBd->erreurRencontree) { + writeToLog(INFO_BASE_CONNECTION_ERROR . $properties["database"], $properties["engine_log_file"]); + } else { + writeToLog(INFO_BASE_CONNECTION . $properties["database"], $properties["engine_log_file"]); + + //Recherche du Moteur V2 (ExecuteWithParams) + //Selection du chemin fme + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); + $aParams['gtf_engine_id'] = array('value' => $properties['id_gtf_engine'], 'type' => 'number'); + $oPDOResult = $oBd->executeWithParams($aSql['pgsql']['select_fme_path'], $aParams); + + if ($aRecord = $oBd->ligneSuivante($oPDOResult)) { + $properties["fme_path"] = $aRecord['fme_path']; + } else { + $properties["fme_path"] = ''; + } + putenv("FME_PATH=" . $properties["fme_path"]); + putenv("GTF_HOME=" . $properties["workspace_dir"] . "/"); + + /* + // Traitement des demandes en fonction du moteur + $iNbTraitement = 0; + $aUserToRefresh = Array(); + //$sSql = $aSql[$properties["sgbd"]]["boucle_1"]; + if (TesteHeureCreuse($properties["heure_creuse_min"], $properties["heure_creuse_max"])) { // On est en heure creuse (0 correspond à "Heure Creuse") + $iPlageHorraire = 0; + $sSql = $aSql[$properties["sgbd"]]["boucle_1"]; + } else { // On est PAS en heure creuse + $iPlageHorraire = 0; + $sSql = $aSql[$properties["sgbd"]]["boucle_2"]; + } + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iPriorityId]", $iPlageHorraire, $sSql); + $sSql = str_replace("[iIdGtfEngine]", $properties["id_gtf_engine"], $sSql); + if ($properties["nbr_order_max"] < 1) + $properties["nbr_order_max"] = 1; + $sSql = str_replace("[iNnbrOrderMax]", $properties["nbr_order_max"], $sSql); + $sEnErreur = 2; + $oPDOResult = $oBd->execute($sSql); + //*/ + + ///* + // Traitement des demandes en fonction du moteur7 + // Gestion des heures creuses + $iNbTraitement = 0; + $aUserToRefresh = Array(); + $aParams = array(); + + $iPlageHorraire = 0; + + $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); + $aParams['iPriorityId'] = array('value' => $iPlageHorraire, 'type' => 'number'); + $aParams['iIdGtfEngine'] = array('value' => $properties["id_gtf_engine"], 'type' => 'number'); + if ($properties["nbr_order_max"] < 1) + $properties["nbr_order_max"] = 1; + $aParams['iNnbrOrderMax'] = array('value' => $properties["nbr_order_max"], 'type' => 'number'); + + $sEnErreur = 2; + + if (TesteHeureCreuse($properties["heure_creuse_min"], $properties["heure_creuse_max"])) { // On est en heure creuse (0 correspond à "Heure Creuse") + $oPDOResult = $oBd->executeWithParams($aSql[$properties["sgbd"]]["boucle_1"], $aParams); + } else { // On est PAS en heure creuse + $oPDOResult = $oBd->executeWithParams($aSql[$properties["sgbd"]]["boucle_2"], $aParams); + } + + while ($aDemande = $oBd->ligneSuivante($oPDOResult)) { + //test pour connaitre les nombres de tentatives de chaque demande + //if ($aDemande['attempt']>=1){ modif par og 26/04/13 + if ($properties["max_attempt"] == '') { + $properties["max_attempt"] = 2; + } + if ($aDemande['attempt'] >= $properties["max_attempt"]) { + writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_TOO_MANY_ORDER_ATTEMPT), $properties["engine_log_file"]); + $iStatut = 4; + + /* + //Mise à jour de la demande. + $sDateTraitement = Date('Y-m-d H:i:s'); + $sSql = $aSql[$properties["sgbd"]]["update_demande"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iStatut]", $iStatut, $sSql); + $sSql = str_replace("[iNbSec]", 0, $sSql); + $sSql = str_replace("[sMessage]", $sMessage, $sSql); + $sSql = str_replace("[sDateTraitement]", $sDateTraitement, $sSql); + $sSql = str_replace("[sResultat]", $sResultat, $sSql); + $sSql = str_replace("[sLogFme]", $sLogFme, $sSql); + + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + + //Mise à jour de la demande V2 + // Requete d'update de la demande + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); + $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); + $aParams['iNbSec'] = array('value' => 0, 'type' => 'number'); + $aParams['sDateTraitement'] = array('value' => $sDateTraitement, 'type' => 'string'); + $aParams['sResultat'] = array('value' => $sResultat, 'type' => 'string'); + $aParams['sLogFme'] = array('value' => $sLogFme, 'type' => 'string'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande"], $aParams); + + if ($oBd->erreurRencontree) { + writeToLog(INFO_ORDER_UPDATE_ERROR . "(SQL : $sSql)", $properties["engine_log_file"]); + } else { + writeToLog(INFO_ORDER_UPDATE, $properties["engine_log_file"]); + // La demande passe en cours + sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( + 'action' => 'event', + 'service' => 'GtfEvents', + 'data' => array( + 'event' => 'order_started', + 'order' => array( + 'order_id' => $aDemande['order_id'], + 'order_status_id' => $aDemande['order_status_id'], + 'user_id' => $aDemande['user_id'], + 'workspace_id' => $aDemande['workspace_id'] + ) + ) + )); + } + $oPDOResult2 = $oBd->fermeResultat(); + } else { + + /* + //Vérification des droits de l'utilsateur + $sSql = $aSql[$properties["sgbd"]]["getLogin"]; + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + + //Vérification des droits de l'utilsateur + // Recuperation des login et droits de l'utilisateur en cour + $aParams = array(); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["getLogin"], $aParams); + + $aUser = $oBd->ligneSuivante($oPDOResult2); + $sGroupListId = getUserGroupsEngines($aUser["login"], $oBd, $properties["mixed_rights_management"]); + // Mise à jour du nombre de tentative dans la base + $iTentative = $aDemande['attempt'] + 1; + $sTable = $properties["schema_gtf"] . ".order"; + $sChamp = "attempt"; + $sWhere = "order_id"; + $sListDemande = $aDemande['order_id']; + $oBd->updateLinkedTable($sTable, $sChamp, $sWhere, $sListDemande, $iTentative); + + // Mise à jour de l'état de la demande de traitement + $oBd->updateLinkedTable($sTable, "order_status_id", $sWhere, $sListDemande, 5); + + writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_ORDER_PROCESSING), $properties["engine_log_file"]); + // OG 27/03/2013 for table job + $sBeginExecutionDate = Date('Y-m-d H:i:s'); + $date = new DateTime(); + $iTimeStampBegin = $date->getTimestamp(); // Pour calcul de la durée + // fin OG 27/03/2013 for job + // $sContenuMail = ""; + $iNbTraitement = $iNbTraitement + 1; + $aUserToRefresh[$iNbTraitement] = $aDemande['user_id']; + $aUserToRefresh[$iNbTraitement] = $aDemande['user_id']; + + /* + //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. + $sSql = $aSql[$properties["sgbd"]]["right_user"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[sGroupListId]", $sGroupListId, $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + + ///* + //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['sGroupListId'] = array('value' => str_replace(',', '|', $sGroupListId), 'type' => 'group'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["right_user"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_REQUEST_ERROR . $sSql, $properties["engine_log_file"]); + writeToLog(INFO_PHP_ERROR, $properties["engine_log_file"]); + $sMessage = "Erreur SQL."; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + $iStatut = $sEnErreur; + } else { + if ($sGroupListId == "0") { + $bAutorisationDemande = true; + } else { + $bAutorisationDemande = false; + while ($aTraitementsAutorise = $oBd->ligneSuivante($oPDOResult2)) { + + if ($aTraitementsAutorise['workspace_id'] == $aDemande['workspace_id']) { + $bAutorisationDemande = true; + } + } + } + if ($bAutorisationDemande == true) { + if (class_exists("Traitement.class.inc") == false) { + include_once ("Traitement.class.inc"); + } + + /* + // recuperer le nom du fichier fmw a traiter + $sSql = $aSql[$properties["sgbd"]]["select_traitement"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iWorkspaceId]", $aDemande['workspace_id'], $sSql); + $oPDOResult3 = $oBd->execute($sSql); + // */ + + ///* + // recuperer le nom du fichier fmw a traiter + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iWorkspaceId'] = array('value' => $aDemande['workspace_id'], 'type' => 'number'); + $oPDOResult3 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_traitement"], $aParams); + //*/ + + $aTraitement = $oBd->ligneSuivante($oPDOResult3); + $iEmailTemplateId = $aTraitement['email_template_id']; + $oPDOResult3 = $oBd->fermeResultat(); + // $sSql = $aSql[$properties["sgbd"]]["select_transit_dir"]; + // $oPDOResult4 = $oBd->execute($sSql); + // $aAction = $oBd->ligneSuivante ($oPDOResult4); + // $properties["transit_dir"] = $aAction["value"]; + // $oPDOResult4 = $oBd->fermeResultat(); + //Lancement du traitement correspondant à la demande. + $aLicence = getFmeLicence($properties['fme_path']); + $bLicenceIsValid = $aLicence['valid']; + $aLicence['valid'] = 1; + if ($aLicence['valid'] == 1) { + $sDateDebut = new DateTime(date("Y-m-d H:i:s")); + $oTraitement = new Traitement($oBd, $aDemande['order_id'], $properties["engine_log_file"], $aDemande['wk_params'], $properties, $aDemande['workspace_id'] . "/fme/" . $aTraitement['fmw_file']); + + if (!$oTraitement->bErreur) { + $sResultat = $oTraitement->Process(); + $sDateFin = new DateTime(date("Y-m-d H:i:s")); + $sLogFme = $oTraitement->sLogFme; + } + //Gestion des erreurs. + + if ($oTraitement->bFmeCrash) + $iStatut = 1; + elseif ($oTraitement->bErreur) { + if ($aTraitement["failed_action_id"] != "" && $oTraitement->sSource != "") { + $aSource = explode("/", $oTraitement->sSource); + $sSourceDirectory = $properties["upload_dir"] . $aSource[0]; + + if ($aTraitement["failed_action_id"] == 1) { + clearDir($sSourceDirectory); + writeToLog(INFO_DELETE_DIRECTORY . $sSourceDirectory . '.', $properties["engine_log_file"]); + } elseif ($aTraitement["failed_action_id"] == 2) { + /* + $sSql = $aSql[$properties["sgbd"]]["select_user"]; + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Selection de l'utilisateur + $aParams = array(); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_user"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); + } else { + $aUser = $oBd->ligneSuivante($oPDOResult2); + + $sUser = userToFolder($aUser["name"]); + if (file_exists($properties["transit_dir"])) { + if (!file_exists($properties["transit_dir"] . "/failed/")) { + mkdir($properties["transit_dir"] . "/failed/"); + } + if (!file_exists($properties["transit_dir"] . "/failed/" . $sUser)) { + mkdir($properties["transit_dir"] . "/failed/" . $sUser); + } + if (!file_exists($properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"])) { + mkdir($properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"]); + } + rename($sSourceDirectory, $properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"]); + writeToLog(str_replace('[sSourceDirectory]', $sSourceDirectory, INFO_MOVING_FILE) . $properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"] . '.', $properties["engine_log_file"]); + } else { + writeToLog(str_replace("[properties['transit_dir']]", $properties['transit_dir'], INFO_DIRECTORY_NOT_FOUND_ENGINE), $properties["engine_log_file"]); + $iStatut = $sEnErreur; + } + } + } + } + $sMessage = $oTraitement->sMessageErreur; + $iStatut = $sEnErreur; + $sResultat = ''; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + // $sContenuMail .= "Contactez l'administrateur pour consulter le fichier de log du robot.<br>"; + $sLienLogFme = $sLogFme; + } else { + if ($aTraitement["success_action_id"] != "" && $oTraitement->sSource != "") { + $aSource = explode("/", $oTraitement->sSource); + $sSourceDirectory = $properties["upload_dir"] . $aSource[0]; + if ($aTraitement["success_action_id"] == 1) { + clearDir($sSourceDirectory); + writeToLog(INFO_DELETE_DIRECTORY . $sSourceDirectory . '.', $properties["engine_log_file"]); + } elseif ($aTraitement["success_action_id"] == 2) { + + /* + $sSql = $aSql[$properties["sgbd"]]["select_user"]; + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + //Selection de l'utilisateur + $aParams = array(); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_user"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); + } else { + $aUser = $oBd->ligneSuivante($oPDOResult2); + + $sUser = userToFolder($aUser["name"]); + if (file_exists($properties["transit_dir"])) { + if (!file_exists($properties["transit_dir"] . "/success/")) { + mkdir($properties["transit_dir"] . "/success/"); + } + if (!file_exists($properties["transit_dir"] . "/success/" . $sUser)) { + mkdir($properties["transit_dir"] . "/success/" . $sUser); + } + if (!file_exists($properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"])) { + mkdir($properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"]); + } + rename($sSourceDirectory, $properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"]); + writeToLog(str_replace('[sSourceDirectory]', $sSourceDirectory, INFO_MOVING_FILE) . $properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"] . '.', $properties["engine_log_file"]); + } else { + writeToLog(str_replace("[properties['transit_dir']]", $properties['transit_dir'], INFO_DIRECTORY_NOT_FOUND_ENGINE), $properties["engine_log_file"]); + $iStatut = $sEnErreur; + } + } + } + } + $sMessage = ""; + $iStatut = 3; + // $sContenuMail = "Votre demande de traitement '".$aDemande['workspace_nom']."' a réussi.<br>"; + if ($sResultat != "") { + // $sContenuMail .= '<A href="'.$properties["url_export"]."/".$sResultat.'">Télécharger le résultat</A><br>'; + $sLienResultat = $sResultat; + } + $sLienLogFme = $sLogFme; + } + unset($oTraitement); + } else { + $iTentative = $iTentative - 1; + $sTable = $properties["schema_gtf"] . ".order"; + $sChamp = "attempt"; + $sWhere = "order_id"; + $sListDemande = $aDemande['order_id']; + $oBd->updateLinkedTable($sTable, $sChamp, $sWhere, $sListDemande, $iTentative); + writeToLog(INFO_INVALID_FME_LICENCE_FILE, $properties["engine_log_file"]); + writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["engine_log_file"]); + $sMessage = "Invalid FME licence file. "; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + $iStatut = 1; + $sLienLogFme = 'Non disponible'; + } + } else { + writeToLog(INFO_NO_USER_GRANT . $aDemande['workspace_nom'] . ' n\'a pas été défini.', $properties["engine_log_file"]); + writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["engine_log_file"]); + $sMessage = "Vous n''avez pas les droits nécessaires pour réaliser ce traitement."; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + $iStatut = 6; + + if (file_exists($sLogFme)) { + $sLienLogFme = $sLogFme; + } else { + $sLienLogFme = 'Non disponible'; + } + } + } + $oPDOResult2 = $oBd->fermeResultat(); + + //Mise à jour de la demande. + + if ($iStatut == 3) { + /* + $sSql = $aSql[$properties["sgbd"]]["update_message"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Mise a jour du message + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } + // Fin - OG 27/03/2013 for table job + $oPDOResult2 = $oBd->fermeResultat(); + } + if ($iStatut == 2 || $iStatut == 4) { + + /* + $sSql = $aSql[$properties["sgbd"]]["delete_message"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + + // Mise a jour du message + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } + // Fin - OG 27/03/2013 for table job + $oPDOResult2 = $oBd->fermeResultat(); + } + $sDateTraitement = Date('Y-m-d H:i:s'); + + /* + $sSql = $aSql[$properties["sgbd"]]["update_demande"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + if ($iStatut != 1) { + $timeFirst = strtotime(date_format($sDateDebut, 'Y-m-d H:i:s')); + $timeSecond = strtotime(date_format($sDateFin, 'Y-m-d H:i:s')); + $diff = $timeSecond - $timeFirst; + $iNbSec = $diff; + } else { + $iNbSec = "NULL"; + } + // OG 27/03/2013 for table job + $date = new DateTime(); + $iTimeStampEnd = $date->getTimestamp(); + $iDelay = $iTimeStampEnd - $iTimeStampBegin; + $sSql = str_replace("[iNbSec]", $iDelay, $sSql); + $sSql = str_replace("[$iStatut]", $iStatut, $sSql); + /* $sSql = str_replace("[sMessage]", $sMessage, $sSql); *//* + $sSql = str_replace("[sDateTraitement]", $sDateTraitement, $sSql); + $sSql = str_replace("[sResultat]", utf8_encode($sResultat), $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + if ($sLogFme == "") { + $sSql = str_replace("'[sLogFme]'", "NULL", $sSql); + } else { + $sSql = str_replace("[sLogFme]", $sLogFme, $sSql); + } + + $oPDOResult2 = $oBd->execute($sSql); + //*/ + + //* + // Mise a jour de la demande + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + if ($iStatut != 1) { + $timeFirst = strtotime(date_format($sDateDebut, 'Y-m-d H:i:s')); + $timeSecond = strtotime(date_format($sDateFin, 'Y-m-d H:i:s')); + $diff = $timeSecond - $timeFirst; + $iNbSec = $diff; + } else { + $iNbSec = "NULL"; + } + // OG 27/03/2013 for table job + $date = new DateTime(); + $iTimeStampEnd = $date->getTimestamp(); + $iDelay = $iTimeStampEnd - $iTimeStampBegin; + $aParams['iNbSec'] = array('value' => $iDelay, 'type' => 'number'); + $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); + $aParams['sDateTraitement'] = array('value' => $sDateTraitement, 'type' => 'string'); + $aParams['sResultat'] = array('value' => utf8_encode($sResultat), 'type' => 'string'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + if ($sLogFme == "") { + $aParams['sLogFme'] = array('value' => "NULL", 'type' => 'string'); + } else { + $aParams['sLogFme'] = array('value' => $sLogFme, 'type' => 'string'); + } + + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande"], $aParams); + // */ + + + if ($oBd->erreurRencontree) { + writeToLog(INFO_ORDER_UPDATE_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } else { + writeToLog(INFO_ORDER_UPDATE, $properties["engine_log_file"]); + // La demande est terminée avec un résultat + sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( + 'action' => 'event', + 'service' => 'GtfEvents', + 'data' => array( + 'event' => 'order_finished_with_result', + 'order' => array( + 'order_id' => $aDemande['order_id'], + 'order_status_id' => $aDemande['order_status_id'], + 'user_id' => $aDemande['user_id'], + 'workspace_id' => $aDemande['workspace_id'] + ) + ) + )); + } + if ($iStatut != 1) { + /* + $sSql = $aSql[$properties["sgbd"]]["insertJob"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $sSql = str_replace("[iWorkspaceId]", $aDemande["workspace_id"], $sSql); + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sBeginExecutionDate]", $sBeginExecutionDate, $sSql); + $sSql = str_replace("[iDelay]", $iDelay, $sSql); + $sSql = str_replace("[iGtfEngineId]", $properties["id_gtf_engine"], $sSql); + $sSql = str_replace("[iStatut]", $iStatut, $sSql); + //writeToLog('|INFORM|PHP|Test requête OG : '.$sSql,$properties["engine_log_file"]); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Insert de schemaGtf + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $aParams['iWorkspaceId'] = array('value' => $aDemande["workspace_id"], 'type' => 'number'); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $aParams['sBeginExecutionDate'] = array('value' => $sBeginExecutionDate, 'type' => 'string'); + $aParams['iDelay'] = array('value' => $iDelay, 'type' => 'number'); + $aParams['iGtfEngineId'] = array('value' => $properties["id_gtf_engine"], 'type' => 'number'); + $aParams['iDelay'] = array('value' => $iDelay, 'type' => 'number'); + $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); + //writeToLog('|INFORM|PHP|Test requête OG : '.$sSql,$properties["engine_log_file"]); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["insertJob"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } + // Fin - OG 27/03/2013 for table job + $oPDOResult2 = $oBd->fermeResultat(); + } + // Mise à jour de la demande si resultat est vide + if ($sResultat == "") { + + /* + $sSql = $aSql[$properties["sgbd"]]["update_demande_resultat_null"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + + ///* + // Mise a jour de la demande si resultat null + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande_resultat_null"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_ORDER_UPDATE_ERROR . ' (' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } else { + writeToLog(INFO_INFO_ORDER_UPDATE_NULL, $properties["engine_log_file"]); + // La demande est terminée où en erreur + sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( + 'action' => 'event', + 'service' => 'GtfEvents', + 'data' => array( + 'event' => 'order_finished_or_error', + 'order' => array( + 'order_id' => $aDemande['order_id'], + 'order_status_id' => $aDemande['order_status_id'], + 'user_id' => $aDemande['user_id'], + 'workspace_id' => $aDemande['workspace_id'] + ) + ) + )); + } + $oPDOResult2 = $oBd->fermeResultat(); + } + + // Notification par e-mail. + $bMailToSend = false; + switch (strtoupper(trim($aDemande['email_option_id']))) { + case 1: + $bMailToSend = True; + break; + case 2: + $bMailToSend = False; + break; + case 3: + if ($iStatut == $sEnErreur) { + $bMailToSend = True; + } + break; + case 4: + if ($iStatut != $sEnErreur) { + $bMailToSend = True; + } + break; + default: + $bMailToSend = True; + } + + // Licence invalide. + if ($iStatut == 2 && !$bLicenceIsValid) { + // Mise à jour de l'état de la demande de traitement (en attente). + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "order_status_id", "order_id", $aDemande['order_id'], 1); + // Licence invalide : 1 mail par jour max. + $sToday = date('d/m/Y'); + $sMailLicenceFile = __DIR__ . '\mail_licence.txt'; + if (file_exists($sMailLicenceFile)) + if (file_get_contents($sMailLicenceFile) == $sToday) + $bMailToSend = false; + file_put_contents($sMailLicenceFile, $sToday); + // Mise à jour du nombre de tentative dans la base. + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "attempt", "order_id", $aDemande['order_id'], 0); + } + + // Envoi du mail. + if ($bMailToSend == true) { + /* + $sSql = $aSql[$properties["sgbd"]]["select_user"]; + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + + //Mise a jour de la demande si resultat null + /* + $aParams = array(); + $aParams['iOrderId'] = array('value' => $aDemande['order_id'], 'type' => 'number'); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande_resultat_null"], $aParams); + if ($oBd->erreurRencontree) { + writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); + } else { + */ + // $aUser = $oBd->ligneSuivante ($oPDOResult2); + // $sContenuMail = "Bonjour ".$aUser["name"].",<br><br><br>".$sContenuMail; + // $sContenuMail .= "<br><br><cite><h6>Cet e-mail a été envoyé par un robot, merci de ne pas répondre.</h6></cite><br>"; + // + + if (empty($iEmailTemplateId)) + $iEmailTemplateId = $properties['default_mail_model']; + + /* + // Nom des moteurs GTF et FME + $sSql = $aSql[$properties["sgbd"]]["get_gtf_engine_name"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace('[gtf_engine_id]', $aDemande['gtf_engine_id'], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Nom des moteurs GTF et FME + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['gtf_engine_id'] = array('value' => $aDemande['gtf_engine_id'], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($sSql = $aSql[$properties["sgbd"]]["get_gtf_engine_name"], $aParams); + //*/ + + + if ($oBd->erreurRencontree) { + writeToErrorLog(ERROR_0001); + } else + $aRow = $oBd->ligneSuivante($oPDOResult2); + + // URL de téléchargement du résultat + $sResultUrl = ''; + if ($sResultat != "") + $sResultUrl = '<a href="' . $properties["url_export"] . "/gtf/" . $sResultat . '">Télécharger le résultat</a>'; + + // Dates + $sOrderDate = date_format(date_create($aDemande['order_date']), 'd/m/Y H:i:s'); + + // Envoi du mail + $aObjects = array(); + if (!empty($sMessage)) + $aObjects['error'] = $sMessage; + $aObjects['oOrder'] = new OrderLib($oBd, $aDemande["order_id"], $properties, "v_order"); + $aObjects['oOrder']->formatOrderEmail(); + $oEmail = new Email($oBd, $iEmailTemplateId, $properties, $aObjects); + if (!empty($oEmail->oEmailTemplate->name)) + $oEmail->send(); + //} + $oPDOResult2 = $oBd->fermeResultat(); + } else { + // pas d'envoi de mail + writeToLog(INFO_NO_MAIL_SEND, $properties["engine_log_file"]); + } + } //fin du if sur les tentatives + //fin de while + } + if ($iNbTraitement >= 1) { + //Crée ou modifie le fichier de maj de l'application pour l'utilisateur. + writeToLog(INFO_END_ORDER_PROCESSING, $properties["engine_log_file"]); + } + $oPDOResult = $oBd->fermeResultat(); + } +} else { + writeToErrorLog(constant($ErrorLicense)); +} + +//Teste si le traitement est autorisé si la demande est en heure creuse. +//retourne true si le traitement est autorisé, retourne false si le traitement n'est pas autorisé. +function TesteHeureCreuse($sHeureMin, $sHeureMax) { + $sHeure = date('H'); + $sMinute = date('i'); + $aHeureMin = explode(":", $sHeureMin); + $aHeureMax = explode(":", $sHeureMax); + $bMax = false; + $bMin = false; + //Test de l'heure minimale. + if ($sHeure > $aHeureMin[0]) { + $bMin = true; + } else { + if ($sHeure == $aHeureMin[0] && $sMinute >= $aHeureMin[1]) { + $bMin = true; + } + } + //Test de l'heure maximale. + if ($sHeure < $aHeureMax[0]) { + $bMax = true; + } else { + if (($aHeureMax[0] < $aHeureMin[0]) && ($sHeure < 24)) { + $bMax = true; + } + if ($sHeure == $aHeureMax[0] && $sMinute <= $aHeureMax[1]) { + $bMax = true; + } + } + if ($bMin && $bMax) { + return true; + } else { + return false; + } +} + +?> diff --git a/gtf.engine/gtf.engines/engine.sql.inc b/gtf.engine/gtf.engines/engine.sql.inc new file mode 100755 index 00000000..3a36a236 --- /dev/null +++ b/gtf.engine/gtf.engines/engine.sql.inc @@ -0,0 +1,17 @@ +<?php +//Requête pour engine.php pgsql +$aSql['pgsql']['boucle_1']='SELECT "order".*, rt_priority.priority_id, rt_priority.label_id as priorite_libelle_id, workspace.name as workspace_nom FROM ([sSchemaGtf]."order" LEFT JOIN [sSchemaGtf].rt_priority ON "order".priority_id = rt_priority.priority_id) LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id WHERE (order_status_id = 1 or order_status_id = 5) AND "order".period_id IS NULL AND "order".deleted IS not TRUE AND "order".gtf_engine_id=[iIdGtfEngine] AND "order".priority_id >= [iPriorityId] ORDER BY "order".priority_id ASC, "order".order_id ASC LIMIT [iNnbrOrderMax]'; +$aSql['pgsql']['boucle_2']='SELECT "order".*, rt_priority.priority_id, rt_priority.label_id as priorite_libelle_id, workspace.name as workspace_nom FROM ([sSchemaGtf]."order" LEFT JOIN [sSchemaGtf].rt_priority ON "order".priority_id = rt_priority.priority_id) LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id WHERE (order_status_id = 1 or order_status_id = 5) AND "order".period_id IS NULL AND "order".deleted IS not TRUE AND "order".gtf_engine_id=[iIdGtfEngine] AND "order".priority_id > [iPriorityId] ORDER BY "order".priority_id ASC, "order".order_id ASC LIMIT [iNnbrOrderMax]'; +$aSql['pgsql']['right_user']='SELECT workspace."workspace_id" FROM [sSchemaGtf].workspace RIGHT JOIN [sSchemaGtf].workspace_group ON workspace.workspace_id = workspace_group.workspace_id GROUP BY workspace."workspace_id", workspace_group."group_id" HAVING workspace_group."group_id" IN ([sGroupListId]) ORDER BY workspace."workspace_id" ASC'; +$aSql['pgsql']['update_demande']="UPDATE [sSchemaGtf].order SET length_sec = [iNbSec], order_status_id=[iStatut], execution_date=[sDateTraitement], result_url=[sResultat], log_url=[sLogFme] WHERE order_id=[iOrderId]"; +$aSql['pgsql']['update_demande_resultat_null']="UPDATE [sSchemaGtf].order SET result_url=NULL WHERE order_id=[iOrderId]"; +$aSql['pgsql']['select_user']='SELECT * FROM [sSchemaFramework].user WHERE "user_id"=[iUserId]'; +$aSql['pgsql']['select_traitement']='SELECT * FROM [sSchemaGtf].workspace WHERE workspace_id=[iWorkspaceId]'; +//$aSql['pgsql']['select_transit_dir']='SELECT value FROM [sSchemaGtf].property WHERE property=\'$properties["transit_dir"]\''; +$aSql['pgsql']['select_fme_path']='select local_path as fme_path from [sSchemaGtf].gtf_engine, [sSchemaGtf].fme_engine where fme_engine.fme_engine_id = gtf_engine.fme_engine_id AND gtf_engine.gtf_engine_id = [gtf_engine_id]'; +$aSql['pgsql']['update_message']="UPDATE [sSchemaGtf].message SET status=1 WHERE order_id=[iOrderId]"; +$aSql['pgsql']['delete_message']="DELETE FROM [sSchemaGtf].message where order_id=[iOrderId]"; +$aSql['pgsql']['get_gtf_engine_name'] = 'SELECT gtf_engine.name AS gtf_engine_name,fme_engine.name AS fme_engine_name FROM [sSchemaGtf].gtf_engine,[sSchemaGtf].fme_engine WHERE gtf_engine.fme_engine_id=fme_engine.fme_engine_id AND gtf_engine_id=[gtf_engine_id]'; +$aSql['pgsql']["getLogin"] = 'SELECT "login" from [sSchemaFramework]."user" where user_id = [iUserId]'; +$aSql['pgsql']["insertJob"] = 'INSERT INTO [sSchemaGtf].job(order_id, workspace_id, "user_id", execution_date, length_sec, engine_id, order_status_id) VALUES ([iOrderId], [iWorkspaceId], [iUserId], [sBeginExecutionDate] , [iDelay], [iGtfEngineId], [iStatut])'; +?> diff --git a/gtf.engine/gtf.engines/lang_engines/en-lang.inc b/gtf.engine/gtf.engines/lang_engines/en-lang.inc new file mode 100755 index 00000000..be577b8a --- /dev/null +++ b/gtf.engine/gtf.engines/lang_engines/en-lang.inc @@ -0,0 +1,7 @@ +<?php + +//Error Licence +define('E001', "Orders unprocessed: Invalid lincense file"); +define('E002', "Orders unprocessed: Expired license file"); +define('E003', "Orders unprocessed: Number of the GTF engine upper than available engines"); +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/lang_engines/fr-lang.inc b/gtf.engine/gtf.engines/lang_engines/fr-lang.inc new file mode 100755 index 00000000..7d5d5046 --- /dev/null +++ b/gtf.engine/gtf.engines/lang_engines/fr-lang.inc @@ -0,0 +1,8 @@ +<?php + +//Error Licence +define('E001', "Demandes non trait�es : Fichier de licence invalide"); +define('E002', "Demandes non trait�es : Fichier de licence expir�"); +define('E003', "Demandes non trait�es : Num�ro du moteur GTF sup�rieur au nombre de moteurs disponibles"); + +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/php_engine_conf.inc b/gtf.engine/gtf.engines/php_engine_conf.inc new file mode 100755 index 00000000..6985372b --- /dev/null +++ b/gtf.engine/gtf.engines/php_engine_conf.inc @@ -0,0 +1,4 @@ +<?php +set_include_path(dirname($_SERVER["PHP_SELF"]). PATH_SEPARATOR .dirname($_SERVER["PHP_SELF"]).'/../vas/rest/conf'. PATH_SEPARATOR .dirname($_SERVER["PHP_SELF"]).'/../vas/rest/class'); +ini_set ('error_log', dirname($_SERVER["PHP_SELF"]).'/php/log/php.log'); +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/properties_engine.inc b/gtf.engine/gtf.engines/properties_engine.inc new file mode 100755 index 00000000..faf47c8d --- /dev/null +++ b/gtf.engine/gtf.engines/properties_engine.inc @@ -0,0 +1,19 @@ +<?php +require_once ("properties.inc"); +$properties["robot_root_dir"] = str_replace("\\","/",dirname($_SERVER["PHP_SELF"])); // Chemin du répertoire courant : gtf.engines +if ($properties["id_gtf_engine"] != '') { + $subdir = '/engine_'.$properties["id_gtf_engine"]; +} else { + $subdir = '/subscription'; +} + +$properties["engine_log_home"] = $properties['vas_home']."/log/engines/".date($properties['log_period']).$subdir; +$properties["debug_log_file"] = $properties["engine_log_home"]."/debug.log"; +$properties["engine_log_file"] = $properties["engine_log_home"]."/engine.log"; +$properties["notify_log_file"] = $properties["engine_log_home"]."/notify.log"; +$properties["sql_log_file"] = $properties["engine_log_home"]."/sql.log"; +$properties["subscription_log_file"] = $properties["engine_log_home"]."/subscription.log"; +$properties["error_log_file"] = $properties['vas_home']."/log/engines"."/error.log"; + +$properties['web_server_name'] = 'https://dev.veremes.net'; +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/regex2.dll b/gtf.engine/gtf.engines/regex2.dll new file mode 100755 index 0000000000000000000000000000000000000000..f84a847a0de92fc59fa2ff8494ff700e62b87326 GIT binary patch literal 79360 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 XFtagtFn2NcF!wPBrSwrexI+K{pWT)4 literal 0 HcmV?d00001 diff --git a/gtf.engine/gtf.engines/string.inc b/gtf.engine/gtf.engines/string.inc new file mode 100755 index 00000000..24e8c0e4 --- /dev/null +++ b/gtf.engine/gtf.engines/string.inc @@ -0,0 +1,59 @@ +<?php +// subscription.php + define('INFO_BASE_CONNECTION_ERROR', '|FATAL |PHP|Connexion impossible a la base '); + define('INFO_BASE_CONNECT', '|INFORM|PHP|Connect to '); + define('INFO_SUBSCRIPTION_PROCESSING', '|INFORM|PHP|Processing subscription order_id= '); + define('INFO_REQUEST_ERROR', '|FATAL |PHP| Erreur lors de l éxécution de la requête : '); + define('INFO_PHP_ERROR', '|ERROR|PHP| .'); + define('INFO_ORDER_CREATED', '|INFORM|PHP|Order [iNouvelId] has been created.'); + define('INFO_ORDER_CREATION_ERROR', '|ERROR|PHP|Problème lors de la création d\'un nouvel enregistrement dans la table "demande"'); + define('INFO_CHECK_SQL_LOG', '|FATAL |PHP|Consultez le log sql.'); + define('INFO_NO_USER_GRANT', '|FATAL |PHP|L utilisateur n a pas les droits nécessaires pour réaliser un traitement :'); + define('INFO_CONTACT_ADMINISTRATOR', '|ERROR|PHP|Contactez l\'administrateur.'); + define('INFO_CHECK_ORDER_FILES', '***************** Vérification des fichiers présents pour la demande [aDemande["order_id"]] *******************'); + define('INFO_CHECK_DIRECTORY', '|INFORM|PHP|Check directory '); + define('INFO_MOVE_DIRECTORY', '|INFORM|PHP|Move directory from '); + define('INFO_MOVE_FILE', '|INFORM|PHP|Move file '); + define('INFO_DIRECTORY_NOT_FOUND_SUBSCRIPTION', "|ERROR|PHP| Le dossier [sFolder] n'existe pas. Contactez l'administrateur et vérifiez le dossier configuré dans l'interface de configuration de surveillance des dossier"); + define('INFO_ORDER_PERIOD_NOT_FOUND', "|ERROR|PHP| La période d'abonnement n°[iPeriodeId] n'existe pas. Contactez l'administrateur et vérifiez la période d'abonnement ajoutée dans l'utilitaire pycron"); + define('INFO_END_ORDER_PROCESSING', '***************** Fin du traitement des demandes *******************'); + +// Traitement.class.inc + define('INFO_RESOURCES', '|INFORM|PHP| Ressources : robot.php, Traitement.class.inc, Paramètres = [sParams], Fmw = [this->sFmwFileName]'); + define('INFO_NULL_ELEMENT', '|WARN |PHP| Un élément est null lors de la création de la demande : '); + define('INFO_DB_PARAM', 'Params : [aValeurs[0]] - Valeur : '); + define('INFO_ERROR', '|ERROR|'); + define('INFO_COMMAND_ERROR', '|ERROR|PHP| Erreur lors de l exécution de la commande : '); + define('INFO_INFORM', '|INFORM|'); + define('INFO_SUCCESSFUL_TREATMENT', '|INFORM|PHP| Traitement réussi.'); + define('INFO_FILE_COMPRESSION', '|INFORM|PHP| Compression des fichiers du répertoire [sZipDir] en zip.'); + define('INFO_TREATMENT_FAILURE', '|FATAL |PHP| Treatment failure.'); + define('INFO_FILES_COMPRESSION_FAILURE', '|ERROR|PHP| Impossible de zipper les fichiers du répertoire [sZipDir] vers [sZipFileDir] dans fichier '); + define('INFO_COMPRESSED_FILES', '|INFORM|PHP| Compressed files : '); + define('INFO_DECOMPRESSING_FILE', '|INFORM|PHP| Décompression du fichier '); + define('INFO_FILE_COMPRESSION_FAILURE', '|ERROR|PHP| Impossible de dézipper le fichier [sZipFile] vers '); + define('INFO_FILE_COMPRESSION_DIRECTORY', '|INFORM|PHP| Fichiers décompressés vers '); + define('INFO_VALID_DATA_SOURCE_FILE_EXTENSION', '|INFORM|PHP| Extension valide pour le fichier de la source de données : '); + define('INFO_OUTPUT_DATA_DIRECTORY', '|INFORM|PHP| Répertoire de sortie des données : '); + define('INFO_OUTPUT_DATA_FILE', '|INFORM|PHP| Fichier de sortie des données : '); + define('INFO_GTF_HOME', '|INFORM|PHP| GTF_HOME='); + define('INFO_FME_PATH', '|INFORM|PHP| FME_PATH='); + define('INFO_TREATMENT_COMMAND_LINE', '|INFORM|PHP| Ligne de commande exécutant le traitement : '); + define('INFO_NO_USE_PATTERN', '|INFORM|PHP| Utilisation des noms de fichier, sans utilisation des motifs'); + define('INFO_USE_PATTERN', '|INFORM|PHP| Utilisation des motifs '); + +// engine.php + define('INFO_BASE_CONNECTION', '|INFORM|PHP| Connexion à la base de données '); + define('INFO_TOO_MANY_ORDER_ATTEMPT', "|INFORM|PHP| La demande [aDemande['order_id']] n'a pas été traitée suite à de trop nombreuses tentatives."); + define('INFO_ORDER_UPDATE_ERROR', '|ERROR|PHP| Impossible de mettre à jour la demande. '); + define('INFO_ORDER_UPDATE', '|INFORM|PHP| Mise à jour de la demande.'); + define('INFO_ORDER_PROCESSING', "***************** Traitement de la demande [aDemande['order_id']] *******************"); + define('INFO_DELETE_DIRECTORY', '|INFORM|PHP| Supression du dossier '); + define('INFO_GET_USER_INFO_ERROR', "|ERROR|PHP| Impossible de récupérer les informations sur l'utilisateur numéro "); + define('INFO_MOVING_FILE', '|INFORM|PHP| Déplacement du dossier [sSourceDirectory] vers le dossier '); + define('INFO_DIRECTORY_NOT_FOUND_ENGINE', "|ERROR|PHP| Le dossier [properties['transit_dir']] n'existe pas."); + define('INFO_INVALID_FME_LICENCE_FILE', '|FATAL |PHP| Invalid FME Licence file.'); + define('INFO_TEST_REQUEST_ERROR', '|ERROR|PHP|Requête test en erreur. '); + define('INFO_INFO_ORDER_UPDATE_NULL', '|INFORM|PHP| Mise à jour de la demande (resultat null).'); + define('INFO_NO_MAIL_SEND', "|INFORM|PHP| Aucun mail n'a été envoyé "); +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/subscription.bat b/gtf.engine/gtf.engines/subscription.bat new file mode 100755 index 00000000..f1830d76 --- /dev/null +++ b/gtf.engine/gtf.engines/subscription.bat @@ -0,0 +1,5 @@ +@echo off +set GTF_ENGINE_HOME=%~dp0 +set PHP_HOME=%GTF_ENGINE_HOME%php +rem %1 correspond au param�tre pass� par pycron +"%PHP_HOME%\php.exe" "%GTF_ENGINE_HOME%\subscription.php" %1 \ No newline at end of file diff --git a/gtf.engine/gtf.engines/subscription.php b/gtf.engine/gtf.engines/subscription.php new file mode 100755 index 00000000..c12c25e3 --- /dev/null +++ b/gtf.engine/gtf.engines/subscription.php @@ -0,0 +1,359 @@ +<?php + +/** + * \file subscription.php + * \brief subscription.php \n \n Ce fichier contient un programme php. + * + * Ce programme permet de traiter les demandes d'abonnement de traitements. + * + * \author Laurent Panabieres <laurent.panabieres@veremes.com> + */ +//set_include_path(dirname($_SERVER["PHP_SELF"]).'/../gtf.client'.';'.dirname($_SERVER["PHP_SELF"]).'/php/lib'); +//ini_set ('writeToErrorLog', dirname($_SERVER["PHP_SELF"]).'/php/php.log'); +require_once ("php_engine_conf.inc"); +require_once ("gtf_lib/GtfFmwParser.class.inc"); +require_once ("vmlib/Vm.class.inc"); +require_once ("vmlib/phpUtil.inc"); +require_once("vmlib/dbUtil.inc"); +//require_once ("robot_properties.inc"); +//require_once ("engine_properties.inc"); +$properties["id_gtf_engine"] = ""; +require_once ("properties_engine.inc"); +require_once ("subscription.sql.inc"); +require_once ("vmlib/logUtil.inc"); +require_once("Ldap.class.inc"); +require_once 'vmlib/error.inc'; +require_once 'string.inc'; + +//writeToLog('|INFO |PHP| dump prop '. var_export($properties,true),$properties["subscription_log_file"]); + +/* + // Création du dossier journalier pour l'écriture du log + if (!is_dir($properties["engine_log_home"]. "/".date($properties["log_period"]))){ + // Le dossier pour l'écritude du log n'existe pas, il est créé + if (!mkdir($properties["engine_log_home"]. "/".($properties["log_period"]), 0777, true)) { + writeToErrorLog(ERROR_0025.$properties["engine_log_home"]. "/".date($properties["log_period"]).'"'); + } + } + */ +//Pas besoin de mot de passe pour le robot +$oBd = new Vm($properties["login_scheduler"], $properties['password_scheduler'], $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); +if ($oBd->erreurRencontree) { + writeToLog(INFO_BASE_CONNECTION_ERROR . $properties["database"], $properties["subscription_log_file"]); +} else { + writeToLog(INFO_BASE_CONNECT . $properties["database"], $properties["subscription_log_file"]); + // Traitement des demandes d'abonnement. + $iNbTraitement = 0; + // Récupération de l'identifiant de la période d'abonnement qui est lancé ! + $iPeriodeId = $_SERVER['argv'][1]; + + /* + $sSql = $aSql[$properties["sgbd"]]["verif_period_id"]; + $sSql = str_replace("[iOrderPeriodId]", $iPeriodeId, $sSql); + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $oPDOResult = $oBd->execute($sSql); + */ + + // Récupération de l'identifiant de la période d'abonnement qui est lancé ! + $aParams = array(); + $aParams['iOrderPeriodId'] = array('value' => $iPeriodeId, 'type' => 'number'); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $oPDOResult = $oBd->executeWithParams($aSql[$properties["sgbd"]]["verif_period_id"], $aParams); + + if ($oBd->ligneSuivante($oPDOResult) != "") { + + /* + $sSql = $aSql[$properties["sgbd"]]["abonnement"]; + $sSql = str_replace("[iOrderPeriodId]", $iPeriodeId, $sSql); + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $oPDOResult = $oBd->execute($sSql); + */ + + // Recuperation des éléments de gestion des abonnements Gtf + $aParams = array(); + $aParams['iOrderPeriodId'] = array('value' => $iPeriodeId, 'type' => 'number'); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $oPDOResult = $oBd->executeWithParams($aSql[$properties["sgbd"]]["abonnement"], $aParams); + + while ($aDemande = $oBd->ligneSuivante($oPDOResult)) { + if (is_null($aDemande['order_date'])) { + $sDateTraitement = $aDemande['order_date']; + } else { + $sDateTraitement = $aDemande['execution_date']; + } + + /* + $sSql = $aSql[$properties["sgbd"]]["getLogin"]; + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + */ + + // Récupération des information de connection de l'utilisateur et de ses droits + $aParams = array(); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["getLogin"], $aParams); + + $aUser = $oBd->ligneSuivante($oPDOResult2); + $sGroupListId = getUserGroupsEngines($aUser["login"], $oBd, $properties["mixed_rights_management"]); + writeToLog(INFO_SUBSCRIPTION_PROCESSING . $aDemande["order_id"], $properties["subscription_log_file"]); + $iNbTraitement = $iNbTraitement + 1; + //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. + /* + $sSql = $aSql[$properties["sgbd"]]["right_user"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[sGroupListId]", $sGroupListId, $sSql); + $oPDOResult2 = $oBd->execute($sSql); + */ + + // Verification des droits utilisateur sur la demande + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['sGroupListId'] = array('value' => str_replace(',', '|', $sGroupListId), 'type' => 'group'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["right_user"], $aParams); + + if ($oBd->erreurRencontree) { + writeToLog(INFO_REQUEST_ERROR . $sSql, $properties["subscription_log_file"]); + writeToLog(INFO_PHP_ERROR, $properties["subscription_log_file"]); + } else { + if ($sGroupListId == "0") { + $bAutorisationDemande = true; + } else { + $bAutorisationDemande = false; + while ($aTraitementsAutorise = $oBd->ligneSuivante($oPDOResult2)) { + if ($aTraitementsAutorise['workspace_id'] == $aDemande['workspace_id']) { + $bAutorisationDemande = true; + } + } + } + if ($bAutorisationDemande == true) { + //Création d'une nouvelle demande. + require_once("gtf_lib/gtf_object//Order.class.inc"); + $iNouvelId = $oBd->insert($properties["schema_gtf"], "order", Array("priority_id" => 1, "workspace_id" => $aDemande['workspace_id'], "order_status_id" => 1, "user_id" => $aDemande['user_id'], "period_id" => "", "order_date" => date("Y-m-d H:i:s"), "execution_date" => null, "wk_params" => $aDemande['wk_params'], "result_url" => "", "message" => "", "email_option_id" => $aDemande['email_option_id'], "gtf_engine_id" => $aDemande['gtf_engine_id'], "email_notifications" => $aDemande['email_notifications']), $properties["schema_gtf"] . ".seq_order", "order_id"); + $oOrder = new OrderLib($oBd, -1, $properties); + $oOrder->getIdEngine($iNouvelId, "order", $aDemande['workspace_id']); + $oOrder->update(); + if ($iNouvelId != "") { + writeToLog(str_replace('[iNouvelId]', $iNouvelId, INFO_ORDER_CREATED), $properties["subscription_log_file"]); + // L'abonnement crée une demande + sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( + 'action' => 'event', + 'service' => 'GtfEvents', + 'data' => array( + 'event' => 'subscription_creating_order', + 'order' => array( + 'order_id' => $aDemande['order_id'], + 'order_status_id' => $aDemande['order_status_id'], + 'user_id' => $aDemande['user_id'], + 'workspace_id' => $aDemande['workspace_id'] + ) + ) + )); + } else { + writeToLog(INFO_ORDER_CREATION_ERROR, $properties["subscription_log_file"]); + writeToLog(INFO_CHECK_SQL_LOG, $properties["subscription_log_file"]); + } + } else { + writeToLog(INFO_NO_USER_GRANT . $aDemande['name'] . ' n\'as pas été définie.', $properties["subscription_log_file"]); + writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["subscription_log_file"]); + $iNouvelId = $oBd->update($properties["schema_gtf"], "order", Array("enabled" => FALSE), "order_id", $aDemande["order_id"], ""); + } + } + $oPDOResult2 = $oBd->fermeResultat(); + } + /* + $sSql = $aSql[$properties["sgbd"]]["dir_survey"]; + $sSql = str_replace("[iOrderPeriodId]", $iPeriodeId, $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + writeToErrorLog($sSql); + $oPDOResult = $oBd->execute($sSql); + */ + + $aParams = array(); + $aParams['iOrderPeriodId'] = array('value' => $iPeriodeId, 'type' => 'number'); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $oPDOResult = $oBd->executeWithParams($aSql[$properties["sgbd"]]["dir_survey"], $aParams); + + while ($aDemande = $oBd->ligneSuivante($oPDOResult)) { + if (is_null($aDemande['order_date'])) { + $sDateTraitement = $aDemande['order_date']; + } else { + $sDateTraitement = $aDemande['execution_date']; + } + //writeToLog(str_replace('[aDemande["order_id"]]', $aDemande["order_id"], INFO_CHECK_ORDER_FILES), $properties["subscription_log_file"]); + writeToLog(INFO_CHECK_DIRECTORY . $aDemande["condition"] . ' for survey order_id=' . $aDemande["order_id"], $properties["subscription_log_file"]); + //parcours du dossier de la condition de la demande + $sFolder = utf8_decode($aDemande["condition"]); + if (is_dir($sFolder)) { + $MyDirectory = opendir($sFolder); + while ($Entry = @readdir($MyDirectory)) { + + if ($Entry != '.' && $Entry != '..') { + //deplacement vers le dossier upload en fonction du type + $bCopie = false; + $bIsFile = false; + $sUnique = UniqFileName(); + if (is_dir($sFolder . '/' . $Entry)) { + $bIsFile = false; + $bCopie = rename($sFolder . "/" . $Entry, $properties["upload_dir"] . "/" . $sUnique); + writeToLog(INFO_MOVE_DIRECTORY . $sFolder . "/" . $Entry . ' to ' . $properties["upload_dir"] . "/" . $sUnique, $properties["subscription_log_file"]); + } else { + $bIsFile = true; + mkdir($properties["upload_dir"] . "/" . $sUnique); + $bCopie = rename($sFolder . "/" . $Entry, $properties["upload_dir"] . "/" . $sUnique . "/" . $Entry); + writeToLog(INFO_MOVE_FILE . $sFolder . "/" . $Entry . ' to ' . $properties["upload_dir"] . "/" . $sUnique . "/" . $Entry, $properties["subscription_log_file"]); + } + if ($bCopie) { + //creation de la demande + //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. + /* + $sSql = $aSql[$properties["sgbd"]]["getLogin"]; + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + */ + + // Récupération des infos utilisateurs + $aParams = array(); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["getLogin"], $aParams); + + $aUser = $oBd->ligneSuivante($oPDOResult2); + $sGroupListId = getUserGroupsEngines($aUser["login"], $oBd, $properties["mixed_rights_management"]); + /* + $sSql = $aSql[$properties["sgbd"]]["right_user"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[sGroupListId]", $sGroupListId, $sSql); + $oPDOResult2 = $oBd->execute($sSql); + */ + + // Vérification des droits de l'utilisateurs pour la demande + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['sGroupListId'] = array('value' => str_replace(',', '|', $sGroupListId), 'type' => 'group'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["right_user"], $aParams); + + if ($oBd->erreurRencontree) { + writeToLog(INFO_REQUEST_ERROR . $sSql, $properties["subscription_log_file"]); + writeToLog(INFO_PHP_ERROR, $properties["subscription_log_file"]); + } else { + if ($sGroupListId == "0") { + $bAutorisationDemande = true; + } else { + $bAutorisationDemande = false; + while ($aTraitementsAutorise = $oBd->ligneSuivante($oPDOResult2)) { + if ($aTraitementsAutorise['workspace_id'] == $aDemande['workspace_id']) { + $bAutorisationDemande = true; + } + } + } + if ($bAutorisationDemande == true) { + //Création d'une nouvelle demande. + //writeToLog($properties["workspace_dir"],$properties["subscription_log_file"]); + $oGtfFmwParser = new GtfFmwParser($properties['vas_home'] . '/ws_data/gtf/workspace' . "/" . $aDemande['workspace_id'] . "/fme/" . $aDemande['fmw_file']); + $iNouvelId = ""; + + if ($bIsFile) { + $aDemande['wk_params'] = getParams($oGtfFmwParser, $sUnique . "/" . utf8_encode($Entry), $aDemande['wk_params']); + } else { + $aDemande['wk_params'] = getParams($oGtfFmwParser, $sUnique . "/", $aDemande['wk_params']); + } + require_once("gtf_lib/gtf_object/Order.class.inc"); + $iNouvelId = $oBd->insert($properties["schema_gtf"], "order", Array("priority_id" => 1, "workspace_id" => $aDemande['workspace_id'], "order_status_id" => 1, "user_id" => $aDemande['user_id'], "period_id" => "", "order_date" => date("Y-m-d H:i:s"), "execution_date" => null, "wk_params" => $aDemande['wk_params'], "result_url" => "", "message" => "", "email_option_id" => $aDemande['email_option_id'], "gtf_engine_id" => $aDemande['gtf_engine_id'], "email_notifications" => $aDemande['email_notifications']), $properties["schema_gtf"] . ".seq_order", "order_id"); + $oOrder = new OrderLib($oBd, -1, $properties); + $oOrder->getIdEngine($iNouvelId, "order", $aDemande['workspace_id']); + $oOrder->update(); + if ($iNouvelId != "") { + writeToLog(str_replace('[iNouvelId]', $iNouvelId, INFO_ORDER_CREATED), $properties["subscription_log_file"]); + // La surveillance crée une demande + sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( + 'action' => 'event', + 'service' => 'GtfEvents', + 'data' => array( + 'event' => 'survey_creating_order', + 'order' => array( + 'order_id' => $aDemande['order_id'], + 'order_status_id' => $aDemande['order_status_id'], + 'user_id' => $aDemande['user_id'], + 'workspace_id' => $aDemande['workspace_id'] + ) + ) + )); + } else { + writeToLog(INFO_ORDER_CREATION_ERROR, $properties["subscription_log_file"]); + writeToLog(INFO_CHECK_SQL_LOG, $properties["subscription_log_file"]); + } + } else { + writeToLog(INFO_NO_USER_GRANT . $aDemande['name'] . ' n\'as pas été définie.', $properties["subscription_log_file"]); + writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["subscription_log_file"]); + $iNouvelId = $oBd->update($properties["schema_gtf"], "order", Array("enabled" => FALSE), "order_id", $aDemande["order_id"], ""); + } + } + } + } + } + } else { + writeToLog(str_replace('[sFolder]', $sFolder, INFO_DIRECTORY_NOT_FOUND_SUBSCRIPTION), $properties["subscription_log_file"]); + } + } + } else { + writeToLog(str_replace('[iPeriodeId]', $iPeriodeId, INFO_ORDER_PERIOD_NOT_FOUND), $properties["subscription_log_file"]); + } + + if ($iNbTraitement >= 1) { + //writeToLog(INFO_END_ORDER_PROCESSING, $properties["subscription_log_file"]); + } + $oPDOResult = $oBd->fermeResultat(); +} + +function getParams($oGtfFmwParser, $sNewSource, $sParams) { + $aTGui = array(); + $sChaine = ""; + $aParams = dbParamsAsArray($sParams); + foreach ($oGtfFmwParser->aGuiObject as $aGui) { + if ($aGui->sDefault_Macro != "") { + array_push($aTGui, $aGui); + } + } + foreach ($aTGui as $oGui) { + //Teste si le paramètre est une source + if ($oGui->bIsSource) { + writeToErrorLog($oGui->sDefault_Macro); + writeToErrorLog($sNewSource); + $aParams[$oGui->sDefault_Macro] = $sNewSource; + } + } + $aKeys = array_keys($aParams); + + for ($i = 0; $i < count($aKeys); $i++) { + if ($sChaine == "") { + $sChaine = $aKeys[$i] . "=" . $aParams[$aKeys[$i]]; + } else { + $sChaine .= "|" . $aKeys[$i] . "=" . $aParams[$aKeys[$i]]; + } + } + //$sChaine = substr($sChaine, 0, -1)."\""; + return $sChaine; +} + +function dbParamsAsArray($sAllParams) { + $aAllParams = Array(); + //$aLigne = explode("|", $sAllParams); + $aLigne = stringExplode($sAllParams, "|", "\"", "\""); + foreach ($aLigne as $sValeur) { + $sValeur = str_replace("\"", "", $sValeur); + $aValeurs = explode("=", $sValeur); + if ($aValeurs[2] != "") { + $sTemp = $aValeurs[1] . "=" . $aValeurs[2]; + $aValeurs[1] = $sTemp; + } + $aAllParams[$aValeurs[0]] = $aValeurs[1]; + } + return $aAllParams; +} + +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/subscription.sql.inc b/gtf.engine/gtf.engines/subscription.sql.inc new file mode 100755 index 00000000..46786f56 --- /dev/null +++ b/gtf.engine/gtf.engines/subscription.sql.inc @@ -0,0 +1,8 @@ +<?php +//Requête pour subscription.php pgsql +$aSql['pgsql']['verif_period_id']='SELECT period_id, name FROM [sSchemaGtf].period WHERE period_id = [iOrderPeriodId]'; +$aSql['pgsql']['abonnement']='SELECT "order".*, workspace.*, period.cron_expression FROM ([sSchemaGtf].order LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id) LEFT JOIN [sSchemaGtf].period ON period.period_id = "order".period_id WHERE order_status_id = 1 AND "order".period_id = [iOrderPeriodId] AND "order".period_id IS NOT NULL AND "order".survey_id IS NULL AND "order"."enabled" = TRUE '; +$aSql['pgsql']['dir_survey']='SELECT "order".*, workspace.*, period.cron_expression, REPLACE(inbox.name, \'$user\', "user"."login") || \'/\' || "order".condition as condition FROM ([sSchemaGtf].order LEFT JOIN [sSchemaFramework].user on "order".user_id = "user"."user_id" LEFT JOIN [sSchemaGtf].inbox on "order".inbox_id = inbox.inbox_id LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id) LEFT JOIN [sSchemaGtf].period ON period.period_id = "order".period_id WHERE order_status_id = 1 AND "order".period_id = [iOrderPeriodId] AND "order".period_id IS NOT NULL AND "order".survey_id = 1 AND "order"."enabled" = TRUE'; +$aSql['pgsql']['right_user']='SELECT workspace."workspace_id" FROM [sSchemaGtf].workspace RIGHT JOIN [sSchemaGtf].workspace_group ON workspace.workspace_id = workspace_group.workspace_id GROUP BY workspace."workspace_id", workspace_group."group_id" HAVING workspace_group."group_id" IN ([sGroupListId]) ORDER BY workspace."workspace_id" ASC'; +$aSql['pgsql']["getLogin"]='SELECT "login" FROM [sSchemaFramework]."user" WHERE user_id = [iUserId]'; +?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/tcl/Traitement.tcl b/gtf.engine/gtf.engines/tcl/Traitement.tcl new file mode 100755 index 00000000..84defdd9 --- /dev/null +++ b/gtf.engine/gtf.engines/tcl/Traitement.tcl @@ -0,0 +1,64 @@ +# Récupération des variables d'environnement +set sEnginesDir [file dirname $argv0]/.. + +set sGtfDir $env(GTF_HOME) +set sFmePath \{$env(FME_PATH)\} + +# Lecture de la librairie tclUtil +source "$sEnginesDir/tcl/tclUtil.tcl" + +# Récupération des parametres sous forme de variables TCL +set lLParams [stringToParam [lindex $argv 0] "|" "="] +set lParametre [lindex $lLParams 0] +set lValeur [lindex $lLParams 1] + +set sFmwFileName [lindex $lValeur 0] +set sRobotLogName [lindex $lValeur 1] +set sFmeLogName [lindex $lValeur 2] + +set i 0 +set sFmeParam "" +foreach sParam $lParametre { + if {$sParam!="LOG_ROBOT" && $sParam!="LOG_FILENAME" && $sParam!="FMWFILENAME"} then { + set valeur [lindex $lValeur $i] + + # Vérification de l'emplacement de la balise <space> + set iSpacePlace [string first <space> $valeur] + # Si la balise existe, elle est remplacée par un espace (ça permet de gérer les cas ou la source (ex : de type access) peut contenir plusieurs extensions (mdb, accdb) + if {$iSpacePlace > 0} then { + set valeur [string map {<space> \ } $valeur] + } + set sFmeParam "$sFmeParam --$sParam \{$valeur\}" + } + incr i +} + +# Création de nouvelles variables +set sScriptPath $sGtfDir/$sFmwFileName + +# Ecriture des parametres du traitement FME dans le log +set sLigne "$sFmePath {$sScriptPath} $sFmeParam -LOG_FILENAME {$sFmeLogName}" + +# Variable qui va permettre d'écrire la commande TCL dans un fichier avec un nom unique +set rand [expr int([expr rand() * 100000])] + +set sCommandeFile "$sEnginesDir/tcl/$rand.tcl" + +# Permet de récupérer un guillement (ajouté pour Edgar) +set sLigne [string map {<quote> \"} $sLigne] +# Permet de récupérer un backslash (ajouté pour Edgar) +set sLigne [string map {<backslashes> \\} $sLigne] + +set fCommande [open $sCommandeFile w+] +# subst -nobackslashes $sLigne -- ligne inutile. Mis en commentaire le 20/06/12 par L.P. +puts $fCommande $sLigne +close $fCommande + +if [ catch { source "$sCommandeFile" } sErreurFME] { + puts stdout "1|$sErreurFME" +} else { + puts stdout "0|Traitement réalisé avec succés" +} + +file delete -force $sCommandeFile + diff --git a/gtf.engine/gtf.engines/tcl/commande.tcl b/gtf.engine/gtf.engines/tcl/commande.tcl new file mode 100755 index 00000000..e69de29b diff --git a/gtf.engine/gtf.engines/tcl/tclUtil.tcl b/gtf.engine/gtf.engines/tcl/tclUtil.tcl new file mode 100755 index 00000000..873ec019 --- /dev/null +++ b/gtf.engine/gtf.engines/tcl/tclUtil.tcl @@ -0,0 +1,50 @@ +# + ---------------------------------------------------------------------- + +# + tclUtil.tcl + +# + Fonction TCL qui peuvent �tre r�utilis�es + +# + Veremes - Fabien Marty - Mai 2007 + +# + ---------------------------------------------------------------------- + + + +## +#D�coupe une chaine et renvoie les �l�ments d'une liste +#contenant une liste des param�tres et une liste des valeurs. +## + +proc stringToParam {sString sDelimited1 sDelimited2} { + set lParams [split $sString $sDelimited1] + + foreach sParam $lParams { + set lResult [split $sParam =] + set sParametre [lindex $lResult 0] + set iLength [llength $lResult] + + if { $iLength > 2 } { + set i 1 + while {$i < $iLength} { + set sTemp [lindex $lResult $i] + append sValeur $sTemp = + set i [incr i] + } + + set sValue [string trimright $sValeur =] + } else { + + set sValue [lindex $lResult 1] + } + lappend lListeParam $sParametre + lappend lListeValue $sValue + } + return [list $lListeParam $lListeValue] +} + +## +#�criture dans le log de la chaine pass�e en param�tre. +## + +proc writeToLog {sLogName sString sType} { + set oLogFile [open $sLogName "a"] + set sHeure [clock seconds] + set sHeureFormat [clock format $sHeure -format "%d/%m/%Y %H:%M:%S" -gmt false] + puts $oLogFile "$sHeureFormat |$sType|TCL| $sString" + close $oLogFile +} \ No newline at end of file diff --git a/gtf.engine/gtf.messages/class/Action.class.inc b/gtf.engine/gtf.messages/class/Action.class.inc new file mode 100755 index 00000000..037a03a7 --- /dev/null +++ b/gtf.engine/gtf.messages/class/Action.class.inc @@ -0,0 +1,74 @@ +<?php +require_once("vmlib/logUtil.inc"); + +class Action { + protected $messageclass_action_id; + protected $aParams; + protected $oMessage; + protected $aProperties; + +/*********************************************************** + Récupère les paramètres de l'action d'un message + \$messageclass_action_id : id de l'actionclass + \$oMessage : objet du message + \$aParams : paramètres de l'actionclass +***********************************************************/ + function __construct($messageclass_action_id, $oMessage, $aParams) { + $this->oMessage = $oMessage; + $this->messageclass_action_id = $messageclass_action_id; + $this->aProperties = $oMessage->aProperties; + // Application des paramètres + $this->setActionClassParameters($aParams); + } + +/******************************************************* +*******************************************************/ + function execute() { + } + + function getError(){ + writeToLog("You need to implement your action error controller", $properties["processMessage_log_file"], false); + writeToErrorLog("You need to implement your action error controller"); + return true; + } + +/******************************************************* + + \$aParams : Tableau des paramètres définis en base +*******************************************************/ + function setActionClassParameters($aParams) { + if (!empty($aParams)) { + $this->aParams = array(); + foreach($aParams as $sActionParam) { + list($sActionParamName, $sActionParamValue) = explode('=', $sActionParam); + eval('$this->aParams[\'' . $sActionParamName . '\'] = ' . $sActionParamValue . ';'); + } + $this->aParams["token"] = $this->getToken($this->aProperties); + } + } + + function getToken ($properties) { + + if($properties['web_server_name'] === "[HTTP_HOST]"){ + $properties['web_server_name'] = "https://localhost"; + } + $sUrl = $properties['web_server_name'] . '/' . $properties['services_alias'] . '/vitis/privatetoken'; + $postfields = array('user'=>$properties["login_bot"], 'password'=>$properties["pass_bot"]); + + $oToken = json_decode($this->postCurlRequest ($sUrl, $postfields)); + return $oToken->token; + } + + function postCurlRequest ($sUrl, $postfields) { + $ch = curl_init($sUrl); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/json")); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + return curl_exec($ch); + } +} +?> diff --git a/gtf.engine/gtf.messages/class/BoAction.class.inc b/gtf.engine/gtf.messages/class/BoAction.class.inc new file mode 100755 index 00000000..46eb9354 --- /dev/null +++ b/gtf.engine/gtf.messages/class/BoAction.class.inc @@ -0,0 +1,43 @@ +<?php +require_once("vmlib/logUtil.inc"); +require_once("Action.class.inc"); + +class BoAction Extends Action { + protected $oBusinessObject; + protected $bError; +/********************************************************** + Class constructor + \$oBd : objet PDO + \$messageclass_action_id : id de l'actionclass + \$oMessage : Objet de la classe message + \$aParams : paramètres de l'actionclass +**********************************************************/ + function __construct($oBd, $messageclass_action_id, $oMessage, $aParams, $properties) { + parent::__construct($messageclass_action_id, $oMessage, $aParams); + $iBoId = $oMessage->getBody($this->aParams['bo_id_attribute']); + if (!empty($iBoId)){ + require_once($properties["vas_home"] ."/rest/ws/" . $this->aParams['module'] . "/" . $this->aParams['class'] . ".class.inc"); + $aPath = array($this->aParams['module'], $this->aParams['class'], $iBoId); + $aValues = array($this->aParams['bo_id_attribute'] => $iBoId, "token" => $this->aParams['token']); + + $this->oBusinessObject = new $this->aParams['class']($aPath, $aValues, $properties); + $this->oBusinessObject->oConnection->oBd = $oBd; + $this->bError = false; + } else { + $this->bError = true; + } + } + +/******************************************************* + Change le statut. +*******************************************************/ + function execute() { + $sTransitionMethod = $this->aParams['method']; + $this->oBusinessObject->$sTransitionMethod(); + } + + function getError() { + return $this->bError; + } +} +?> diff --git a/gtf.engine/gtf.messages/class/EmailAction.class.inc b/gtf.engine/gtf.messages/class/EmailAction.class.inc new file mode 100755 index 00000000..fa4d14cb --- /dev/null +++ b/gtf.engine/gtf.messages/class/EmailAction.class.inc @@ -0,0 +1,40 @@ +<?php +require_once("vmlib/logUtil.inc"); +require_once("vmlib/Email.class.inc"); +require_once("Action.class.inc"); + +class EmailAction Extends Action { + protected $oEmail; + protected $bError; +/********************************************************** + - Récupère les paramètres de l'action d'un message + - Crée un objet de la classe "Email". + - Applique les paramètres + \$oBd : objet PDO + \$messageclass_action_id : id de l'actionclass + \$oMessage : Objet de la classe message + \$aParams : paramètres de l'actionclass +**********************************************************/ + function __construct($oBd, $messageclass_action_id, $oMessage, $aParams) { + parent::__construct($messageclass_action_id, $oMessage, $aParams); + $aObjects['oMessage'] = $oMessage; + $iEmailTemplateId = $this->aParams['template_id']; + if (empty($iEmailTemplateId)) + $iEmailTemplateId = $oMessage->aProperties['default_mail_model']; + $this->oEmail = new Email($oBd, $iEmailTemplateId, $oMessage->aProperties, $aObjects); + $this->bError = false; + } + +/******************************************************* + Envoi un email. +*******************************************************/ + function execute() { + if (!empty($this->oEmail->oEmailTemplate->name)) + $this->oEmail->send(); + } + + function getError() { + return $this->bError; + } +} +?> diff --git a/gtf.engine/gtf.messages/class/Message.class.inc b/gtf.engine/gtf.messages/class/Message.class.inc new file mode 100755 index 00000000..59ad4a9e --- /dev/null +++ b/gtf.engine/gtf.messages/class/Message.class.inc @@ -0,0 +1,155 @@ +<?php +/** +* \class Message +* \file Message.class.inc +*/ + +require_once("vmlib/logUtil.inc"); + +class Message { + protected $message_id; + public $messageclass; + protected $creation_date; + protected $status; + protected $body; + protected $bo_class_name; + protected $bo_class; + protected $bo_id; + protected $sender; + protected $oBo; + protected $oBd; + protected $aBodyValue; + public $aProperties; + protected $aSql; + + /** + * Class constructor + * \param $oBd Connection object. + * \param $message_id Message id. + * \param $aProperties Array of properties. + */ + function __construct($oBd, $message_id , $aProperties) { + require("Message.class.sql.inc"); + $this->aProperties = $aProperties; + $this->oBd = $oBd; + $this->aSql = $aSql; + $sSql = $this->aSql[$oBd->sgbd]['getMessage']; + //$sSql = str_replace('[message_id]',$message_id , $sSql); + //$sSql = str_replace('[schema]',$aProperties['schema_gtf'] , $sSql); + $aParams = array( + "message_id" => array("value"=> $message_id, "type"=> 'number'), + "schema" => array("value"=> $aProperties['schema_gtf'], "type"=> 'column_name') + ); + $oResult = $oBd->executeWithParams($sSql, $aParams); + if($oBd->erreurRencontree) + $sErrorMsg = $oBd->getBDMessage(); + else { + if ($oBd->nombreLigne ($oResult) > 0) { + $oRow = $oBd->objetSuivant ($oResult); + $this->message_id = $oRow->message_id; + $this->messageclass = $oRow->messageclass; + $this->creation_date = $oRow->creation_date; + $this->status = $oRow->status; + $this->body = $oRow->body; + //$this->bo_class_name = $oRow->bo_class_name; + //$this->bo_id = $oRow->bo_id; + $this->sender = $oRow->sender; + // Extrait les paramètres pour les actions de body + if (!empty($this->body)) { + preg_match_all('/(.)+:(.)+/', $this->body, $aActionParams); + if (!empty($aActionParams[0])) { + foreach ($aActionParams[0] as $sActionParam) { + list($sIndex, $sValue) = explode(':', $sActionParam); + $this->aBodyValue[trim($sIndex)] = trim($sValue); + } + } + } + } + } + if (isset($sErrorMsg)){ + writeToLog("The message " . $message_id . "encountered this error : " . $sErrorMsg, $properties["processMessage_log_file"], false); + } + } + + /* + * function setProcessed + * \brief Change the status of a message. + * \param $processed boolean. + */ + function setProcessed($processed) { + if ($processed === true) + $this->status = 3; // Traité + else + $this->status = 2; // En erreur + } + + /* + * function process + * \brief Executes all actions of the message. + */ + function process() { + // Liste des actions du message + $sSql = $this->aSql['pgsql']['getMessageActions']; + //$sSql = str_replace("[messageclass]", $this->messageclass, $sSql); + //$sSql = str_replace("[schema]", $this->aProperties['schema_gtf'], $sSql); + $aParams = array( + "messageclass" => array("value"=> $this->messageclass, "type"=> 'string'), + "schema" => array("value"=> $this->aProperties['schema_gtf'], "type"=> 'column_name') + ); + $oActionResult = $this->oBd->executeWithParams($sSql, $aParams); + if(!$this->oBd->erreurRencontree) { + if ($this->oBd->nombreLigne($oActionResult) > 0) { + while ($oActionRow = $this->oBd->objetSuivant ($oActionResult)) { + if (file_exists(__DIR__ . '/' . $oActionRow->actionclass . '.class.inc')) { + require_once __DIR__ . '/' . $oActionRow->actionclass . '.class.inc'; + if (!empty($oActionRow->parameters)) + $aParams = explode('|', $oActionRow->parameters); + $oAction = new $oActionRow->actionclass($this->oBd, $oActionRow->messageclass_action_id, $this, $aParams, $this->aProperties); + if($oAction->getError() == false){ + $oAction->execute(); + } else { + writeToLog("ERROR : cannot create action with this body's parameters", $properties["processMessage_log_file"], false); + throw new Exception("ERROR : cannot create action with this body's parameters"); + } + } + } + } + } + } + + /* + * function getBody + * \param $key boolean. + * \return Returns the value of a parameter of action (or all the parameters and values). + */ + function getBody($key = null) { + if (is_null($key)) + return $this->body; + else { + if (isset($this->aBodyValue[$key])) + return $this->aBodyValue[$key]; + else + return null; + } + } + + /* + * function update + * \brief Saves the settings of a message. + */ + function update() { + // Tous les paramètres sauf les tableaux et objets + foreach (get_object_vars($this) as $sIndex => $value) { + if (!is_array($value) && !is_object($value)) { + // Le paramètre est une date ? + /*if (strtotime($value) !== false) + $aValues[$sIndex] = "'$value'"; + else*/ + $aValues[$sIndex] = $value; + } + } + //error_log(print_r($this->oBd, true)); + $this->oBd->update($this->aProperties['schema_gtf'], 'message', $aValues, 'message_id', $this->message_id, 'numeric'); + } +} +?> diff --git a/gtf.engine/gtf.messages/class/Message.class.sql.inc b/gtf.engine/gtf.messages/class/Message.class.sql.inc new file mode 100755 index 00000000..47a0f8e9 --- /dev/null +++ b/gtf.engine/gtf.messages/class/Message.class.sql.inc @@ -0,0 +1,4 @@ +<?php + $aSql['pgsql']['getMessage'] = "SELECT * FROM [schema].message WHERE message_id = [message_id]"; + $aSql['pgsql']['getMessageActions'] = "SELECT * FROM [schema].messageclass_action WHERE messageclass = [messageclass] ORDER BY index"; +?> \ No newline at end of file diff --git a/gtf.engine/gtf.messages/processMessages.bat b/gtf.engine/gtf.messages/processMessages.bat new file mode 100755 index 00000000..ae8efcb5 --- /dev/null +++ b/gtf.engine/gtf.messages/processMessages.bat @@ -0,0 +1,5 @@ +@echo off +set GTF_MESSAGE_HOME=%~dp0 +set PHP_HOME=%GTF_MESSAGE_HOME%..\gtf.engines\php +rem %1 correspond au paramètre passé par pycron +"%PHP_HOME%\php.exe" "%GTF_MESSAGE_HOME%processMessages.php" %1 diff --git a/gtf.engine/gtf.messages/processMessages.php b/gtf.engine/gtf.messages/processMessages.php new file mode 100755 index 00000000..bfa42008 --- /dev/null +++ b/gtf.engine/gtf.messages/processMessages.php @@ -0,0 +1,173 @@ +<?php + session_start(); + + require_once dirname($_SERVER['PHP_SELF']).'/../gtf.engines/php_engine_conf.inc'; + require_once dirname($_SERVER['PHP_SELF']).'/../gtf.engines/properties_engine.inc'; + + require_once("vmlib/BD.class.inc"); + require_once("vmlib/Vm.class.inc"); + require_once("processMessages.sql.inc"); + require_once("class/Message.class.inc"); + + $sPidFile = __DIR__ . "/PID_processMessages.pid"; + + $oNow = new DateTime(); + $properties["processMessage_log_file"] = $properties['log_directories'] ['Application'] . "/processMessage/log_" . $oNow->format('Y-m-d') . ".log"; + + processIsRunnning ($sPidFile, $properties); + createPidFile($sPidFile, $properties); + + //journalisation des logs pour le mailReader aussi + // instanciation d'un objet pour accéder à la base compte u_vitis + $oBd = new BD ($properties['login_scheduler'], $properties['password_scheduler'], $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); + + $iMessages = 0; + $iMessagesTraite = 0; + + if ($oBd->erreurRencontree) + writeToLog('can\'t connect to database ' . $properties["database"] . " on " . $properties["server"] . ":" . $properties["port"] , $properties["processMessage_log_file"], false); + else { + // Traitement des messages en attente. + $sSql = $aSql['pgsql']['getWaitingMessages']; + + $aParams = array( + "messageclass_type" => array("value"=> 'action', "type"=> 'string'), + "schema" => array("value"=> $properties['schema_gtf'], "type"=> 'column_name') + ); + + $oMessageResult=$oBd->executeWithParams($sSql, $aParams); + if(!$oBd->erreurRencontree) { + $iMessages = $oBd->nombreLigne($oMessageResult); + if ($iMessages > 0) { + // fonction à appeler à la fin du script. + register_shutdown_function('shutdownCallBack', $oBd, $properties); + // Traitement des messages en attente. + while ($oMessageRow = $oBd->objetSuivant ($oMessageResult)) { + $iMessageId = $oMessageRow->message_id; + $oMessage = new Message($oBd, $oMessageRow->message_id, $properties); + $iOrderId = $oMessage->getBody("order_id"); + if (!empty($iOrderId)){ + $sSql = $aSql['pgsql']['getOrderStatus'];; + $sSql = str_replace("[ORDER_ID]", $iOrderId, $sSql); + $oOrderResult=$oBd->execute($sSql); + if(!$oBd->erreurRencontree) { + $oOrderRow = $oBd->objetSuivant ($oOrderResult); + if ($oOrderRow->order_status_id == 3 ){ + $oMessage->process(); + $oMessage->setProcessed(true); + $oMessage->update(); + $iMessagesTraite ++; + }else{ + writeToLog("message " . $iMessageId . " : can't be processing for the moment because the linked GTF order is not completed or it status is not 'valid'", $properties["processMessage_log_file"], false); + } + } else { + $oMessage->setProcessed(false); + $oMessage->update(); + writeToLog("message " . $iMessageId . " : can't be processing because we can't find the linked GTF order", $properties["processMessage_log_file"], false); + } + } else { + $oMessage->process(); + $oMessage->setProcessed(true); + $oMessage->update(); + $iMessagesTraite ++; + } + } + } + }else{ + writeToLog($oBd->getBDMessage(), $properties["processMessage_log_file"], false); + } + } + + writeToLog("Iteration of the process completed : " . $iMessages . "(". $iMessagesTraite ." as OK)", $properties["processMessage_log_file"], false); + + unlink($sPidFile); + + /* + * function shutDownCallBack + * @oBd Database connection object. + * @properties + */ + function shutDownCallBack($oBd, $properties) { + $aError = error_get_last(); + // Erreur fatale pendant le traitement d'un message ? + if ($aError['type'] & E_ERROR > 0) { + writeToLog("[ERROR] [PHP] while processing the message " . $GLOBALS['iMessageId'] . "check the main log file", $properties["processMessage_log_file"], false); + // Change le statut du message : "en erreur". + $oMessage = new Message($oBd, $GLOBALS['iMessageId'], $properties); + $oMessage->setProcessed(false); + $oMessage->update(); + // Re-exécute le script (au cas où il resterait des messages non traités). + if(DIRECTORY_SEPARATOR === "/"){ + // if linux + exec(__DIR__ . '/processMessages.sh'); + } else { + // if windows + exec(__DIR__ . '\processMessages.bat'); + } + } + } + + function processIsRunnning ($sPidFile, $properties){ + if (file_exists ($sPidFile)){ + // récupération du fichier .pid + $sPid = file_get_contents($sPidFile); + + $bProcessIsRunning = true; + $sOsName = PHP_OS; + // detection de l'os pour savoir comment controler le PID + if (strtoupper(substr($sOsName, 0, 3)) === 'WIN') { + $bProcessIsRunning = windowsPidCheck($sPid); + } else { + $bProcessIsRunning = linuxPidCheck($sPid); + } + + if($bProcessIsRunning === false){ + //si le fichier PID est présent mais que le PID n'existe pas sur le serveur + // on supprime le fichier .pid et on lance le mailReader + writeToLog("previous process exited without cleaning pidfile, removing", $properties["processMessage_log_file"], false); + unlink($sPidFile); + } else { + //si le process tourne déjà on ferme le process courant + writeToLog("found a running instance of processMessages.php, exiting.", $properties["processMessage_log_file"], false); + exit(0); + } + } + return true; + } + + function windowsPidCheck ($iPid){ + // fonction pour controller si un pid existe sous windows + $processes = explode( "\n", shell_exec('tasklist.exe /fi "PID eq ' . $iPid . '"')); + + foreach( $processes as $process ) { + + if( ! empty($process) && (strpos("===", $process ) === 0) ){ + continue; + } + + $matches = array(); + preg_match("/(.*?)\s+(\d+).*$/", $process, $matches); + + if(count($matches) > 0){ + return true; + } + } + return false; + } + + function linuxPidCheck ($iPid){ + // fonction pour controller si un pid existe sous linux + return file_exists("/proc/" . $iPid); + } + + function createPidFile($sPidFile , $properties){ + $file = fopen($sPidFile, 'w'); + + if($file){ + writeToLog("PID file creation with pid : " . getmypid(), $properties["processMessage_log_file"], false); + fwrite($file, getmypid()); + } + + fclose($file); + } +?> \ No newline at end of file diff --git a/gtf.engine/gtf.messages/processMessages.sh b/gtf.engine/gtf.messages/processMessages.sh new file mode 100755 index 00000000..89dadbfb --- /dev/null +++ b/gtf.engine/gtf.messages/processMessages.sh @@ -0,0 +1,5 @@ +@echo off +set GTF_MESSAGE_HOME=%~dp0 +set PHP_HOME=%GTF_MESSAGE_HOME%..\gtf.engines\php +#rem %1 correspond au paramètre passé par pycron +"../gtf.engines/php/bin/php" "./processMessages.php" %1 \ No newline at end of file diff --git a/gtf.engine/gtf.messages/processMessages.sql.inc b/gtf.engine/gtf.messages/processMessages.sql.inc new file mode 100755 index 00000000..6f481457 --- /dev/null +++ b/gtf.engine/gtf.messages/processMessages.sql.inc @@ -0,0 +1,4 @@ +<?php + $aSql['pgsql']['getWaitingMessages']='SELECT message.message_id FROM [schema].message,[schema].messageclass WHERE status=1 AND message.messageclass=messageclass.messageclass AND lower(messageclass_type)=lower([messageclass_type]) ORDER BY message_id;'; + $aSql['pgsql']['getOrderStatus']='SELECT order_status_id FROM s_gtf.order WHERE order_id = [ORDER_ID] LIMIT 1'; +?> \ No newline at end of file -- GitLab