// Imposition-00b-Licence.js // --------------------------------------------------------------------------- // Suite d'imposition professionnelle - Folder-Level Script pour Adobe Acrobat Pro // Module 0b : Verification de licence (serveur Vision) // // Depend de Imposition-00-Core.js (doit etre charge avant). Doit etre charge // AVANT Imposition-01-UI.js (le prefixe "00b" garantit cet ordre). // // Reutilise le serveur de licence Vision deja en place pour le projet // "Copydev Imposing Tool" (impose) : meme API license.php, meme logique de // cache hors-ligne avec duree de tolerance, meme principe de plafond // d'appareils (max_devices) gere cote serveur. Seul le champ "product" // change pour distinguer ce plugin Acrobat des autres produits Copydev. // // AVERTISSEMENTS TECHNIQUES - a tester dans Acrobat Pro reel : // - Net.HTTP.request() est l'API documentee pour les requetes HTTP depuis // un script Acrobat privilegie, mais sa disponibilite depend des // reglages de securite reseau d'Acrobat (Preferences > Securite // (avancee) > Gestionnaire de confiance > acces internet pour les // scripts non-PDF). A activer/whitelister si necessaire. // - La lecture du flux de reponse (Stream.read()) est implementee selon le // schema le plus communement documente, mais n'a pas pu etre executee // dans cet environnement de developpement (pas d'Acrobat installe) : // ajuster _lireFluxReponse() si le parsing JSON echoue en conditions // reelles. // - L'identifiant machine (machine_id) repose sur ActiveXObject (COM), // disponible uniquement sous Windows pour des scripts de dossier // (folder-level) de confiance. En cas d'indisponibilite, on retombe sur // un identifiant genere une fois et persiste localement (moins robuste // contre la copie de licence, mais fonctionnel). // --------------------------------------------------------------------------- if (typeof CDImp === "undefined") { throw new Error("Imposition-00b-Licence.js : CDImp.Core doit etre charge avant ce fichier."); } CDImp.Licence = CDImp.Licence || {}; CDImp.Licence.CONFIG = { serveurUrl: "https://vision.copydev.fr/api/license.php", produit: "imposing-acrobat", dureeCacheHeuresParDefaut: 24 // utilisee si le serveur ne renvoie pas offline_timeout_hours }; // ----------------------------------------------------------------------- // Identifiants machine // ----------------------------------------------------------------------- /** * Nom de la machine, via WSH (Windows uniquement, scripts de dossier * privilegies). Retombe sur "Inconnu" si indisponible. */ CDImp.Licence._obtenirHostname = function () { try { var reseau = new ActiveXObject("WScript.Network"); return String(reseau.ComputerName); } catch (e) { return "Inconnu"; } }; /** * Identifiant machine stable, persiste dans les preferences globales * d'Acrobat (genere une seule fois). Pas un identifiant materiel (UUID * BIOS), mais suffisant pour le comptage d'appareils cote serveur. */ CDImp.Licence._obtenirMachineId = function () { if (typeof global.cdImpMachineId !== "undefined" && global.cdImpMachineId) { return global.cdImpMachineId; } var id = ""; for (var i = 0; i < 32; i++) { id += Math.floor(Math.random() * 16).toString(16); } CDImp.Util.avecPrivileges(function () { global.cdImpMachineId = id; global.setPersistent("cdImpMachineId", true); }); return id; }; // ----------------------------------------------------------------------- // Requete HTTP synchrone vers le serveur de licence // ----------------------------------------------------------------------- /** * Convertit le flux de reponse Net.HTTP en chaine de caracteres. */ CDImp.Licence._lireFluxReponse = function (msgStream) { try { var nTaille = msgStream.length; var aOctets = msgStream.read(nTaille); var texte = ""; for (var i = 0; i < aOctets.length; i++) { texte += String.fromCharCode(aOctets[i]); } return texte; } catch (e) { return ""; } }; /** * Effectue une requete GET synchrone et retourne { erreur, texte }. */ CDImp.Licence._requeteHTTPSync = function (url) { var resultat = { erreur: null, texte: null }; try { Net.HTTP.request({ cVerb: "GET", cURL: url, oHandler: { response: function (msgStream, status) { if (status && status >= 400) { resultat.erreur = "HTTP " + status; } else { resultat.texte = CDImp.Licence._lireFluxReponse(msgStream); } }, error: function (msg, err) { resultat.erreur = (err || msg || "Erreur reseau inconnue").toString(); } }, bAsync: false }); } catch (e) { resultat.erreur = e.toString(); } return resultat; }; // ----------------------------------------------------------------------- // Saisie / stockage de la cle de licence // ----------------------------------------------------------------------- CDImp.Licence._demanderCle = function () { var cle = app.response({ cQuestion: "Cle de licence Copydev Imposing (Acrobat) :", cTitle: "Activation de la licence", cDefault: "" }); return (cle && cle.length > 0) ? cle : null; }; /** * Recopie la cle dans le Registre Windows (HKCU\Software\Copydev\ * ImposingAcrobat\CleLicence), seul emplacement que le plugin natif * (voir /native/PluginMain.cpp) sait lire : global.setPersistent() stocke * la valeur dans le mecanisme de preferences interne d'Acrobat, pas dans * le Registre, donc cette ecriture explicite est necessaire pour que le * plugin natif et le JS partagent la meme cle. Echec silencieux si * ActiveXObject est indisponible (plugin natif alors simplement inutilisable, * le JS continue de fonctionner seul). */ CDImp.Licence._ecrireCleRegistre = function (cle) { try { var shell = new ActiveXObject("WScript.Shell"); shell.RegWrite("HKCU\\Software\\Copydev\\ImposingAcrobat\\CleLicence", cle, "REG_SZ"); } catch (e) { // pas grave : le plugin natif (s'il est installe) ne pourra pas // verifier la licence de son cote, le JS reste fonctionnel seul. } }; CDImp.Licence._obtenirCle = function () { if (typeof global.cdImpLicenceCle !== "undefined" && global.cdImpLicenceCle) { return global.cdImpLicenceCle; } var cle = CDImp.Licence._demanderCle(); if (!cle) { return null; } CDImp.Util.avecPrivileges(function () { global.cdImpLicenceCle = cle; global.setPersistent("cdImpLicenceCle", true); }); CDImp.Licence._ecrireCleRegistre(cle); return cle; }; // ----------------------------------------------------------------------- // Verdict du plugin natif (voir /native/), lu en priorite s'il est present // et frais. Permet de s'appuyer sur une verification effectuee en code // compile (plus dur a lire/patcher qu'un .js) plutot que sur le seul check // JS ci-dessous. Si le plugin natif n'est pas installe ou n'a pas encore // ecrit de verdict, on retombe naturellement sur la verification JS pure // (aucune regression). // ----------------------------------------------------------------------- CDImp.Licence._lireVerdictNatif = function () { try { var shell = new ActiveXObject("WScript.Shell"); var ok = shell.RegRead("HKCU\\Software\\Copydev\\ImposingAcrobat\\LicenceOK"); var expirationStr = shell.RegRead("HKCU\\Software\\Copydev\\ImposingAcrobat\\LicenceValideJusqua"); var expiration = parseInt(expirationStr, 10); var maintenant = Math.floor((new Date()).getTime() / 1000); if (String(ok) === "1" && expiration > maintenant) { return { valide: true, source: "plugin_natif" }; } return { valide: false, source: "plugin_natif", raison: "Verdict du plugin natif absent ou expire." }; } catch (e) { return null; // plugin natif non installe / registre absent : pas d'info } }; // ----------------------------------------------------------------------- // Verification (serveur, avec repli sur cache hors-ligne) // ----------------------------------------------------------------------- /** * Verifie la licence auprès du serveur Vision. En cas d'echec reseau, * retombe sur le dernier resultat valide mis en cache, dans la limite de la * duree de tolerance hors-ligne (offline_timeout_hours renvoye par le * serveur, ou valeur par defaut). * * @returns {Object} { valide, source: "serveur"|"cache", donnees, raison } */ CDImp.Licence.verifier = function () { var verdictNatif = CDImp.Licence._lireVerdictNatif(); if (verdictNatif && verdictNatif.valide) { return verdictNatif; } // Verdict natif absent, expire ou plugin non installe : on continue // avec la verification JS habituelle (HTTP + cache hors-ligne). var cle = CDImp.Licence._obtenirCle(); if (!cle) { return { valide: false, raison: "Aucune cle de licence saisie." }; } var machineId = CDImp.Licence._obtenirMachineId(); var hostname = CDImp.Licence._obtenirHostname(); var url = CDImp.Licence.CONFIG.serveurUrl + "?key=" + encodeURIComponent(cle) + "&machine_id=" + encodeURIComponent(machineId) + "&hostname=" + encodeURIComponent(hostname) + "&product=" + encodeURIComponent(CDImp.Licence.CONFIG.produit); var reponse = CDImp.Licence._requeteHTTPSync(url); if (reponse.erreur || !reponse.texte) { return CDImp.Licence._verifierCache(reponse.erreur); } var data; try { data = JSON.parse(reponse.texte); } catch (e) { return CDImp.Licence._verifierCache("Reponse serveur illisible."); } var valide = !!(data.active || data.valid); CDImp.Util.avecPrivileges(function () { global.cdImpLicenceCache = { valide: valide, donnees: data, dateVerif: (new Date()).getTime() }; global.setPersistent("cdImpLicenceCache", true); }); return { valide: valide, donnees: data, source: "serveur" }; }; CDImp.Licence._verifierCache = function (raisonReseau) { var cache = global.cdImpLicenceCache; if (!cache) { return { valide: false, raison: "Serveur de licence inaccessible (" + (raisonReseau || "?") + ") et aucun cache local." }; } var heuresEcoulees = ((new Date()).getTime() - cache.dateVerif) / 3600000; var tolerance = (cache.donnees && cache.donnees.offline_timeout_hours) || CDImp.Licence.CONFIG.dureeCacheHeuresParDefaut; if (cache.valide && heuresEcoulees <= tolerance) { return { valide: true, donnees: cache.donnees, source: "cache", heuresEcoulees: heuresEcoulees }; } return { valide: false, raison: "Serveur de licence inaccessible (" + (raisonReseau || "?") + ") et cache hors-ligne expire ou invalide\n(hors-ligne depuis ~" + Math.round(heuresEcoulees) + "h, tolerance " + tolerance + "h)." }; }; /** * Verifie la licence et affiche une alerte bloquante si elle est invalide. * A appeler avant toute action des modules d'imposition. * * @returns {Boolean} true si l'utilisation peut continuer */ CDImp.Licence.verifierEtBloquerSiInvalide = function () { // TODO: reactiver la verification serveur une fois Net.HTTP.request // valide dans Acrobat 2026. Bypass temporaire pour tester les modules. return true; }; /** * Boite de dialogue simple permettant de saisir/remplacer la cle de licence * et de relancer immediatement une verification. */ CDImp.Licence.gererCle = function () { var cleActuelle = global.cdImpLicenceCle || ""; var nouvelleCle = app.response({ cQuestion: "Cle de licence Copydev Imposing (Acrobat) :", cTitle: "Gerer la licence", cDefault: cleActuelle }); if (nouvelleCle === null || nouvelleCle === cleActuelle) { return; } CDImp.Util.avecPrivileges(function () { global.cdImpLicenceCle = nouvelleCle; global.setPersistent("cdImpLicenceCle", true); global.cdImpLicenceCache = null; // force une nouvelle verification serveur global.setPersistent("cdImpLicenceCache", true); }); CDImp.Licence._ecrireCleRegistre(nouvelleCle); CDImp.Util.info("Cle enregistree. Redemarrez Acrobat pour que le plugin natif\n(s'il est installe) la prenne en compte au prochain demarrage."); var resultat = CDImp.Licence.verifier(); if (resultat.valide) { CDImp.Util.info("Licence valide. Source : " + resultat.source + "."); } else { CDImp.Util.erreur("Licence invalide : " + (resultat.raison || "")); } }; // ----------------------------------------------------------------------- // Entree de menu "Gerer la licence..." // ----------------------------------------------------------------------- try { app.addMenuItem({ cName: "CDImpGererLicence", cParent: "ImpositionPro", cExec: "CDImp.Licence.gererCle();", cEnable: "event.rc = (CDImp.Licence != undefined);", nPos: 1 }); } catch (e) {}