1 | /*␊ |
2 | Copyright 2021 Grégory Soutadé␊ |
3 | ␊ |
4 | This file is part of libgourou.␊ |
5 | ␊ |
6 | libgourou is free software: you can redistribute it and/or modify␊ |
7 | it under the terms of the GNU Lesser General Public License as published by␊ |
8 | the Free Software Foundation, either version 3 of the License, or␊ |
9 | (at your option) any later version.␊ |
10 | ␊ |
11 | libgourou is distributed in the hope that it will be useful,␊ |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of␊ |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the␊ |
14 | GNU Lesser General Public License for more details.␊ |
15 | ␊ |
16 | You should have received a copy of the GNU Lesser General Public License␊ |
17 | along with libgourou. If not, see <http://www.gnu.org/licenses/>.␊ |
18 | */␊ |
19 | ␊ |
20 | #include <libgourou.h>␊ |
21 | #include <libgourou_common.h>␊ |
22 | #include <libgourou_log.h>␊ |
23 | #include <user.h>␊ |
24 | ␊ |
25 | namespace gourou {␊ |
26 | User::User(DRMProcessor* processor):processor(processor) {}␊ |
27 | ␊ |
28 | User::User(DRMProcessor* processor, const std::string& activationFile):␊ |
29 | ␉processor(processor), activationFile(activationFile)␊ |
30 | {␊ |
31 | ␉parseActivationFile();␊ |
32 | }␊ |
33 | ␊ |
34 | void User::parseActivationFile(bool throwOnNull)␊ |
35 | {␊ |
36 | ␉GOUROU_LOG(DEBUG, "Parse activation file " << activationFile);␊ |
37 | ␉␊ |
38 | ␉if (!activationDoc.load_file(activationFile.c_str()))␊ |
39 | ␉{␊ |
40 | ␉ if (throwOnNull)␊ |
41 | ␉␉EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");␊ |
42 | ␉ return;␊ |
43 | ␉}␊ |
44 | ␊ |
45 | ␉try␊ |
46 | ␉{␊ |
47 | ␉ pkcs12 = gourou::extractTextElem(activationDoc, "//adept:pkcs12", throwOnNull);␊ |
48 | ␉ uuid = gourou::extractTextElem(activationDoc, "//adept:user", throwOnNull);␊ |
49 | ␉ deviceUUID = gourou::extractTextElem(activationDoc, "//device", throwOnNull);␊ |
50 | ␉ deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull);␊ |
51 | ␉ authenticationCertificate = gourou::extractTextElem(activationDoc, "//adept:authenticationCertificate", throwOnNull);␊ |
52 | ␉ privateLicenseKey = gourou::extractTextElem(activationDoc, "//adept:privateLicenseKey", throwOnNull);␊ |
53 | ␊ |
54 | ␉ pugi::xpath_node xpath_node = activationDoc.select_node("//adept:username");␊ |
55 | ␉ if (xpath_node)␊ |
56 | ␉␉loginMethod = xpath_node.node().attribute("method").value();␊ |
57 | ␉ else␊ |
58 | ␉ {␊ |
59 | ␉␉if (throwOnNull)␊ |
60 | ␉␉ EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");␊ |
61 | ␉ }␊ |
62 | ␉ ␊ |
63 | ␉ if (loginMethod == "anonymous")␊ |
64 | ␉␉username = "anonymous";␊ |
65 | ␉ else␊ |
66 | ␉␉username = gourou::extractTextElem(activationDoc, "//adept:username", throwOnNull);␊ |
67 | ␉ ␊ |
68 | ␉ pugi::xpath_node_set nodeSet = activationDoc.select_nodes("//adept:licenseServices/adept:licenseServiceInfo");␊ |
69 | ␉ for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();␊ |
70 | ␉␉ it != nodeSet.end(); ++it)␊ |
71 | ␉ {␊ |
72 | ␉␉std::string url = gourou::extractTextElem(it->node(), "adept:licenseURL");␊ |
73 | ␉␉std::string certificate = gourou::extractTextElem(it->node(), "adept:certificate");␊ |
74 | ␉␉licenseServiceCertificates[url] = certificate;␊ |
75 | ␉ }␊ |
76 | ␉}␊ |
77 | ␉catch(gourou::Exception& e)␊ |
78 | ␉{␊ |
79 | ␉ EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");␊ |
80 | ␉}␊ |
81 | }␊ |
82 | ␊ |
83 | std::string& User::getUUID() { return uuid; }␊ |
84 | std::string& User::getPKCS12() { return pkcs12; }␊ |
85 | std::string& User::getDeviceUUID() { return deviceUUID; }␊ |
86 | std::string& User::getDeviceFingerprint() { return deviceFingerprint; }␊ |
87 | std::string& User::getUsername() { return username; }␊ |
88 | std::string& User::getLoginMethod() { return loginMethod; }␊ |
89 | std::string& User::getAuthenticationCertificate() { return authenticationCertificate; }␊ |
90 | std::string& User::getPrivateLicenseKey() { return privateLicenseKey; }␊ |
91 | ␊ |
92 | void User::readActivation(pugi::xml_document& doc)␊ |
93 | {␊ |
94 | ␉if (!doc.load_file(activationFile.c_str()))␊ |
95 | ␉ EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");␊ |
96 | }␊ |
97 | ␊ |
98 | void User::updateActivationFile(const char* data)␊ |
99 | {␊ |
100 | ␉GOUROU_LOG(INFO, "Update Activation file : " << std::endl << data);␊ |
101 | ␊ |
102 | ␉writeFile(activationFile, (unsigned char*)data, strlen(data));␊ |
103 | ␉␊ |
104 | ␉parseActivationFile(false);␊ |
105 | }␊ |
106 | ␊ |
107 | void User::updateActivationFile(const pugi::xml_document& doc)␊ |
108 | {␊ |
109 | ␉StringXMLWriter xmlWriter;␊ |
110 | ␉doc.save(xmlWriter, " ");␊ |
111 | ␉updateActivationFile(xmlWriter.getResult().c_str());␊ |
112 | }␊ |
113 | ␊ |
114 | std::string User::getProperty(const std::string property)␊ |
115 | {␊ |
116 | ␉pugi::xpath_node xpathRes = activationDoc.select_node(property.c_str());␊ |
117 | ␉if (!xpathRes)␊ |
118 | ␉ EXCEPTION(USER_NO_PROPERTY, "Property " << property << " not found in activation.xml");␊ |
119 | ␊ |
120 | ␉std::string res = xpathRes.node().first_child().value();␊ |
121 | ␉return trim(res);␊ |
122 | }␊ |
123 | ␊ |
124 | pugi::xpath_node_set User::getProperties(const std::string property)␊ |
125 | {␊ |
126 | ␉return activationDoc.select_nodes(property.c_str());␊ |
127 | }␊ |
128 | ␊ |
129 | User* User::createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer)␊ |
130 | {␊ |
131 | ␉struct stat _stat;␊ |
132 | ␊ |
133 | ␉if (stat(dirName.c_str(), &_stat) != 0)␊ |
134 | ␉{␊ |
135 | ␉ if (mkdir_p(dirName.c_str(), S_IRWXU))␊ |
136 | ␉␉EXCEPTION(USER_MKPATH, "Unable to create " << dirName)␊ |
137 | ␉}␊ |
138 | ␊ |
139 | ␉User* user = new User(processor);␊ |
140 | ␉bool doUpdate = false;␊ |
141 | ␉␊ |
142 | ␉user->activationFile = dirName + "/activation.xml";␊ |
143 | ␉user->parseActivationFile(false);␊ |
144 | ␊ |
145 | ␉pugi::xpath_node nodeActivationInfo = user->activationDoc.select_node("activation_info");␊ |
146 | ␉pugi::xpath_node nodeActivationServiceInfo = nodeActivationInfo.node().select_node("adept:activationServiceInfo");␊ |
147 | ␉pugi::xml_node activationInfo;␊ |
148 | ␉pugi::xml_node activationServiceInfo;␊ |
149 | ␉␊ |
150 | ␉if (nodeActivationInfo && nodeActivationServiceInfo)␊ |
151 | ␉{␊ |
152 | ␉ GOUROU_LOG(DEBUG, "Read previous activation configuration");␊ |
153 | ␉ activationInfo = nodeActivationInfo.node();␊ |
154 | ␉ activationServiceInfo = nodeActivationServiceInfo.node();␊ |
155 | ␉}␊ |
156 | ␉else␊ |
157 | ␉{␊ |
158 | ␉ GOUROU_LOG(DEBUG, "Create new activation");␊ |
159 | ␊ |
160 | ␉ user->activationDoc.reset();␊ |
161 | ␉ ␊ |
162 | ␉ pugi::xml_node decl = user->activationDoc.append_child(pugi::node_declaration);␊ |
163 | ␉ decl.append_attribute("version") = "1.0";␊ |
164 | ␉ activationInfo = user->activationDoc.append_child("activationInfo");␊ |
165 | ␉ activationInfo.append_attribute("xmlns") = ADOBE_ADEPT_NS;␊ |
166 | ␉ activationServiceInfo = activationInfo.append_child("adept:activationServiceInfo");␊ |
167 | ␉ activationServiceInfo.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
168 | ␉␊ |
169 | ␉ // Go to activation Service Info␊ |
170 | ␉ std::string activationURL = ACSServer + "/ActivationServiceInfo";␊ |
171 | ␉ ByteArray activationServiceInfoReply = processor->sendRequest(activationURL);␊ |
172 | ␉ pugi::xml_document docActivationServiceInfo;␊ |
173 | ␉ docActivationServiceInfo.load_buffer(activationServiceInfoReply.data(),␊ |
174 | ␉␉␉␉␉␉ activationServiceInfoReply.length());␊ |
175 | ␊ |
176 | ␉ pugi::xpath_node path = docActivationServiceInfo.select_node("//authURL");␊ |
177 | ␉ appendTextElem(activationServiceInfo, "adept:authURL", path.node().first_child().value());␊ |
178 | ␉ path = docActivationServiceInfo.select_node("//userInfoURL");␊ |
179 | ␉ appendTextElem(activationServiceInfo, "adept:userInfoURL", path.node().first_child().value());␊ |
180 | ␉ appendTextElem(activationServiceInfo, "adept:activationURL", ACSServer);␊ |
181 | ␉ path = docActivationServiceInfo.select_node("//certificate");␊ |
182 | ␉ appendTextElem(activationServiceInfo, "adept:certificate", path.node().first_child().value());␊ |
183 | ␉ doUpdate = true;␊ |
184 | ␉}␊ |
185 | ␉␊ |
186 | ␉pugi::xpath_node nodeAuthenticationCertificate = activationServiceInfo.select_node("adept:authenticationCertificate");␊ |
187 | ␊ |
188 | ␉if (!nodeAuthenticationCertificate)␊ |
189 | ␉{␊ |
190 | ␉ GOUROU_LOG(DEBUG, "Create new activation, authentication part");␊ |
191 | ␊ |
192 | ␉ pugi::xpath_node xpathRes = activationServiceInfo.select_node("adept:authURL");␊ |
193 | ␉ if (!xpathRes)␊ |
194 | ␉␉EXCEPTION(USER_NO_AUTHENTICATION_URL, "No authentication URL");␊ |
195 | ␉␊ |
196 | ␉ std::string authenticationURL = xpathRes.node().first_child().value();␊ |
197 | ␉ authenticationURL = trim(authenticationURL) + "/AuthenticationServiceInfo";␊ |
198 | ␉ ␊ |
199 | ␉ // Go to authentication Service Info␊ |
200 | ␉ ByteArray authenticationServiceInfo = processor->sendRequest(authenticationURL);␊ |
201 | ␉ pugi::xml_document docAuthenticationServiceInfo;␊ |
202 | ␉ docAuthenticationServiceInfo.load_buffer(authenticationServiceInfo.data(), authenticationServiceInfo.length());␊ |
203 | ␉ pugi::xpath_node path = docAuthenticationServiceInfo.select_node("//certificate");␊ |
204 | ␉ appendTextElem(activationServiceInfo, "adept:authenticationCertificate", path.node().first_child().value());␊ |
205 | ␉ doUpdate = true;␊ |
206 | ␉}␊ |
207 | ␊ |
208 | ␉if (doUpdate)␊ |
209 | ␉ user->updateActivationFile(user->activationDoc);␊ |
210 | ␉ ␊ |
211 | ␉␊ |
212 | ␉return user;␊ |
213 | }␊ |
214 | ␊ |
215 | std::string User::getLicenseServiceCertificate(std::string url)␊ |
216 | {␊ |
217 | ␉if (licenseServiceCertificates.count(trim(url)))␊ |
218 | ␉ return licenseServiceCertificates[trim(url)];␊ |
219 | ␊ |
220 | ␉return "";␊ |
221 | }␊ |
222 | ␊ |
223 | }␊ |