ECClientUpdate.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/zcdefs.h>
  18. #include <memory>
  19. #include <utility>
  20. #include <cerrno>
  21. #include <cstring>
  22. #include <dirent.h>
  23. #include <sys/stat.h>
  24. #include <kopano/platform.h>
  25. #include <kopano/kcodes.h>
  26. #include <kopano/UnixUtil.h>
  27. #include "soapH.h"
  28. #include "ECClientUpdate.h"
  29. #include "ECLicenseClient.h"
  30. #include <kopano/stringutil.h>
  31. #include <kopano/ECLogger.h>
  32. #include <kopano/ECConfig.h>
  33. #include "SOAPUtils.h"
  34. #include "SOAPHelpers.h"
  35. #include "ECSessionManager.h"
  36. #include "ECDatabase.h"
  37. #include "ECStatsCollector.h"
  38. #include "../libserver/cmd.hpp"
  39. #include "ECNotificationManager.h"
  40. extern ECConfig *g_lpConfig;
  41. static bool GetLatestVersionAtServer(const char *, unsigned int, ClientVersion *);
  42. /*
  43. * Handles the HTTP GET command from soap, only the client update install may be downloaded.
  44. *
  45. * This function can only be called when client_update_enabled is set to yes.
  46. *
  47. * @note This function is only use for backward compatibility
  48. */
  49. int HandleClientUpdate(struct soap *soap)
  50. {
  51. std::string strPath;
  52. int nRet = 404; // default return file not found to soap
  53. const char *szClientUpdatePath = NULL;
  54. char *szCurrentVersion = NULL;
  55. char *szReq = NULL;
  56. char *szReqEnd = NULL;
  57. std::string strLicenseRequest;
  58. std::string strLicenseResponse;
  59. unsigned int ulLicenseResponse = 0;
  60. void *lpLicenseResponse = NULL;
  61. ECRESULT er = erSuccess;
  62. ClientVersion currentVersion = {0};
  63. ClientVersion latestVersion = {0};
  64. std::string strClientMSIName;
  65. FILE *fd = NULL;
  66. // Get the server.cfg setting
  67. szClientUpdatePath = g_lpConfig->GetSetting("client_update_path");
  68. if (!szClientUpdatePath || szClientUpdatePath[0] == 0) {
  69. ec_log_err("Client update: The configuration field 'client_update_path' is empty.");
  70. goto exit;
  71. }
  72. // if the version comes as "/autoupdate/6.20.1.1234?licreq", we need to pass the license request
  73. szReq = strrchr(soap->path, '?');
  74. if (szReq != NULL) {
  75. // since we have the ?, that's good enough
  76. szReq = strstr(soap->buf, "X-License: ");
  77. if (szReq == NULL) {
  78. ec_log_debug("Client update: Invalid license request, header not found.");
  79. goto exit;
  80. }
  81. szReq += strlen("X-License: ");
  82. szReqEnd = strstr(szReq, "\r\n"); // TODO: can be be split over multiple lines?
  83. if (szReqEnd == NULL) {
  84. ec_log_debug("Client update: Invalid license request, end of header not found.");
  85. goto exit;
  86. }
  87. strLicenseRequest = base64_decode(std::string(szReq, szReqEnd - szReq));
  88. er = ECLicenseClient(g_lpConfig->GetSetting("license_socket"), atoui(g_lpConfig->GetSetting("license_timeout")))
  89. .Auth((unsigned char*)strLicenseRequest.c_str(), strLicenseRequest.length(), &lpLicenseResponse, &ulLicenseResponse);
  90. if (er != erSuccess) {
  91. ec_log_debug("Client update: Invalid license request, error: 0x%08X.", er);
  92. goto exit;
  93. }
  94. strLicenseResponse = base64_encode(static_cast<const unsigned char *>(lpLicenseResponse), ulLicenseResponse);
  95. soap->http_content = "binary";
  96. soap_response(soap, SOAP_FILE);
  97. nRet = soap_send_raw(soap, strLicenseResponse.c_str(), strLicenseResponse.length());
  98. ec_log_debug("Client update: Processing license request.");
  99. goto exit;
  100. }
  101. // the version comes as "/autoupdate/6.20.1.1234", convert it to "6.20.1.1234"
  102. szCurrentVersion = soap->path + strlen("/autoupdate");
  103. if (szCurrentVersion[0] == '/')
  104. ++szCurrentVersion;
  105. if (szCurrentVersion[0] != '\0') {
  106. ec_log_info("Client update: The current client version is %s.", szCurrentVersion);
  107. if (!GetVersionFromString(szCurrentVersion, &currentVersion))
  108. {
  109. ec_log_info("Client update: Failed in getting version from input data.");
  110. goto exit;
  111. }
  112. }
  113. if (!GetLatestVersionAtServer(szClientUpdatePath, 0, &latestVersion)) {
  114. ec_log_info("Client update: No updates found on server.");
  115. goto exit;
  116. }
  117. if (szCurrentVersion[0] != '\0') {
  118. int res = CompareVersions(currentVersion, latestVersion);
  119. if (res == 0) {
  120. ec_log_debug("Client update: Client already has latest version.");
  121. goto exit;
  122. } else if (res > 0) {
  123. ec_log_debug("Client update: Client has newer version than server.");
  124. goto exit;
  125. }
  126. }
  127. if (!GetClientMSINameFromVersion(latestVersion, &strClientMSIName)) {
  128. ec_log_info("Client update: No suitable version available.");
  129. goto exit;
  130. }
  131. if (ConvertAndValidatePath(szClientUpdatePath, strClientMSIName, &strPath) != true) {
  132. ec_log_info("Client update: Error in path conversion and validation.");
  133. goto exit;
  134. }
  135. fd = fopen(strPath.c_str(), "rb");
  136. if (!fd) {
  137. ec_log_err("Client update: Path not found %s.", strPath.c_str());
  138. goto exit;
  139. }
  140. ec_log_err("Client update: Sending client %s new installer %s", soap->host, strClientMSIName.c_str());
  141. // application/msi-installer ?
  142. soap->http_content = "binary";
  143. soap_response(soap, SOAP_FILE);
  144. while (true) {
  145. // FIXME: tmpbuf is only 1K, good enough?
  146. size_t nSize = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd);
  147. if (!nSize)
  148. break;
  149. if (soap_send_raw(soap, soap->tmpbuf, nSize))
  150. {
  151. ec_log_crit("Client update: Error while sending client new installer");
  152. goto exit;
  153. }
  154. }
  155. nRet = SOAP_OK;
  156. exit:
  157. free(lpLicenseResponse);
  158. if (fd)
  159. fclose(fd);
  160. return nRet;
  161. }
  162. bool ConvertAndValidatePath(const char *lpszClientUpdatePath, const std::string &strMSIName, std::string *lpstrDownloadFile)
  163. {
  164. size_t nTempLen = 0;
  165. std::string strFile;
  166. char cPathSeparator = '/';
  167. if (lpstrDownloadFile == NULL || lpszClientUpdatePath == NULL)
  168. return false;
  169. strFile = lpszClientUpdatePath;
  170. nTempLen = strFile.length();
  171. // not 100% correct, but good enough
  172. if (strstr(strFile.c_str(), "/.."))
  173. {
  174. ec_log_crit("Client update: Update path contains invalid .. to previous path.");
  175. return false;
  176. }
  177. if (strFile[nTempLen - 1] != cPathSeparator)
  178. strFile += cPathSeparator;
  179. strFile += strMSIName;
  180. *lpstrDownloadFile = std::move(strFile);
  181. return true;
  182. }
  183. //<Major>.<Minor>.<Update>.<Build No.>
  184. bool GetVersionFromString(char *szVersion, ClientVersion *lpClientVersion)
  185. {
  186. ClientVersion cv;
  187. std::vector<std::string> vParts;
  188. if (szVersion == NULL)
  189. return false;
  190. vParts = tokenize(szVersion, '.');
  191. if (vParts.size() != 4)
  192. return false;
  193. cv.nMajorVersion = atoi(vParts[0].c_str());
  194. cv.nMinorVersion = atoi(vParts[1].c_str());
  195. cv.nUpdateNumber = atoi(vParts[2].c_str());
  196. cv.nBuildNumber = atoi(vParts[3].c_str());
  197. *lpClientVersion = cv;
  198. return true;
  199. }
  200. /**
  201. * Convert MSI version string to client version struct
  202. *
  203. * @param[in] szVersion MSI filename write as <Major>.<Minor>.<Update>-<Build No.>.msi
  204. * @param[out] Pointer a struct with client version information
  205. */
  206. bool GetVersionFromMSIName(const char *szVersion, ClientVersion *lpClientVersion)
  207. {
  208. ClientVersion cv;
  209. std::vector<std::string> vParts;
  210. if (NULL == szVersion)
  211. return false;
  212. vParts = tokenize(szVersion, ".-");
  213. if (vParts.size() != 5) // 5 because of the .msi at the end
  214. return false;
  215. cv.nMajorVersion = atoi(vParts[0].c_str());
  216. cv.nMinorVersion = atoi(vParts[1].c_str());
  217. cv.nUpdateNumber = atoi(vParts[2].c_str());
  218. cv.nBuildNumber = atoi(vParts[3].c_str());
  219. *lpClientVersion = cv;
  220. return true;
  221. }
  222. /*
  223. The return values are following:
  224. -n - Version1 < Version2 (client should upgrade)
  225. 0 - Version1 == Version2
  226. n - Version1 > Version2
  227. */
  228. int CompareVersions(ClientVersion Version1, ClientVersion Version2)
  229. {
  230. if (Version1.nMajorVersion != Version2.nMajorVersion)
  231. return Version1.nMajorVersion - Version2.nMajorVersion;
  232. if (Version1.nMinorVersion != Version2.nMinorVersion)
  233. return Version1.nMinorVersion - Version2.nMinorVersion;
  234. if (Version1.nUpdateNumber != Version2.nUpdateNumber)
  235. return Version1.nUpdateNumber - Version2.nUpdateNumber;
  236. if (Version1.nBuildNumber != Version2.nBuildNumber)
  237. return Version1.nBuildNumber - Version2.nBuildNumber;
  238. return 0;
  239. }
  240. //zarafaclient-6.20-1234.msi
  241. //zarafaclient-*.*-*.msi
  242. static bool GetLatestVersionAtServer(const char *szUpdatePath,
  243. unsigned int ulTrackid, ClientVersion *lpLatestVersion)
  244. {
  245. ClientVersion tempVersion = {0};
  246. ClientVersion latestVersion = {0};
  247. const char ds_prefix[] = "zarafaclient-";
  248. bool bRet = false;
  249. if (szUpdatePath == NULL)
  250. return false;
  251. std::unique_ptr<DIR, fs_deleter> dh(opendir(szUpdatePath));
  252. if (dh == nullptr) {
  253. ec_log_err("Client update: trackid: 0x%08X, Unable to open client_update_path directory \"%s\": %s",
  254. ulTrackid, szUpdatePath, strerror(errno));
  255. return false;
  256. }
  257. for (const struct dirent *dentry = readdir(dh.get());
  258. dentry != nullptr; dentry = readdir(dh.get())) {
  259. const char *bname = dentry->d_name;
  260. auto fullpath = std::string(szUpdatePath) + "/" + bname;
  261. struct stat sb;
  262. if (stat(fullpath.c_str(), &sb) < 0 || !S_ISREG(sb.st_mode))
  263. continue;
  264. if (strncmp(bname, ds_prefix, std::min(strlen(bname), strlen(ds_prefix))) != 0) {
  265. ec_log_debug("Client update: trackid: 0x%08X, Ignoring file %s for client update", ulTrackid, bname);
  266. continue;
  267. }
  268. ec_log_info("Client update: trackid: 0x%08X, Update Name: %s", ulTrackid, bname);
  269. const char *pTemp = bname + strlen(ds_prefix);
  270. if (!GetVersionFromMSIName(pTemp, &tempVersion))
  271. {
  272. ec_log_warn("Client update: trackid: 0x%08X, Failed in getting version from string '%s'", ulTrackid, pTemp);
  273. continue;
  274. }
  275. // first time, latestVersion will be 0, so always older
  276. if (CompareVersions(latestVersion, tempVersion) < 0) {
  277. bRet = true;
  278. latestVersion = tempVersion;
  279. }
  280. }
  281. if (bRet)
  282. *lpLatestVersion = latestVersion;
  283. return bRet;
  284. }
  285. /**
  286. * Convert clientversion struct to string
  287. */
  288. static bool VersionToString(const ClientVersion &clientVersion,
  289. std::string *lpstrVersion)
  290. {
  291. char szBuf[255];
  292. if (lpstrVersion == NULL)
  293. return false;
  294. snprintf(szBuf, 255, "%d.%d.%d-%d", clientVersion.nMajorVersion, clientVersion.nMinorVersion, clientVersion.nUpdateNumber, clientVersion.nBuildNumber);
  295. lpstrVersion->assign(szBuf);
  296. return true;
  297. }
  298. /**
  299. * Convert clientversion struct to MSI file name
  300. */
  301. bool GetClientMSINameFromVersion(const ClientVersion &clientVersion, std::string *lpstrMSIName)
  302. {
  303. char szMSIName[MAX_PATH];
  304. if (lpstrMSIName == NULL)
  305. return false;
  306. snprintf(szMSIName, MAX_PATH, "zarafaclient-%d.%d.%d-%d.msi", clientVersion.nMajorVersion, clientVersion.nMinorVersion, clientVersion.nUpdateNumber, clientVersion.nBuildNumber);
  307. lpstrMSIName->assign(szMSIName);
  308. return true;
  309. }
  310. int ns__getClientUpdate(struct soap *soap, struct clientUpdateInfoRequest sClientUpdateInfo, struct clientUpdateResponse* lpsResponse)
  311. {
  312. unsigned int er = erSuccess;
  313. ClientVersion sCurrentVersion = {0};
  314. ClientVersion sLatestVersion;
  315. unsigned int ulLicenseResponse = 0;
  316. void *lpLicenseResponse = NULL;
  317. std::string strClientMSIName;
  318. std::string strPath;
  319. FILE *fd = NULL;
  320. int res;
  321. unsigned int ulUserID = 0;
  322. ECSession *lpecSession = NULL;
  323. ECDatabase *lpDatabase = NULL;
  324. std::string strCurVersion;
  325. std::string strLatestVersion;
  326. std::string strQuery;
  327. time_t tNow = 0;
  328. const char *lpszClientUpdatePath = g_lpConfig->GetSetting("client_update_path");
  329. unsigned int ulLogLevel = atoui(g_lpConfig->GetSetting("client_update_log_level"));
  330. if (!parseBool(g_lpConfig->GetSetting("client_update_enabled"))) {
  331. // do not set on high loglevel, since by default the client updater is installed, and this will be quite often in your log
  332. ec_log_notice("Client update: trackid: 0x%08X, Config option 'client_update_enabled' has disabled this feature.", sClientUpdateInfo.ulTrackId);
  333. er = KCERR_NO_SUPPORT;
  334. goto exit;
  335. }
  336. // setup soap
  337. soap_set_imode(soap, SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_ENC_ZLIB | SOAP_ENC_MTOM);
  338. soap_set_omode(soap, SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_ENC_ZLIB | SOAP_ENC_MTOM | SOAP_IO_CHUNK);
  339. if (!lpszClientUpdatePath || lpszClientUpdatePath[0] == 0) {
  340. ec_log_err("Client update: trackid: 0x%08X, The configuration field 'client_update_path' is empty.", sClientUpdateInfo.ulTrackId);
  341. er = KCERR_NO_ACCESS;
  342. goto exit;
  343. }
  344. er = g_lpSessionManager->CreateSessionInternal(&lpecSession);
  345. if(er != erSuccess)
  346. goto exit;
  347. // Lock the session
  348. lpecSession->Lock();
  349. er = lpecSession->GetDatabase(&lpDatabase);
  350. if (er != erSuccess)
  351. goto exit;
  352. //@TODO change loglevel?
  353. ec_log_err("Client update: trackid: 0x%08X, computername: %s, username: %s, clientversion: %s, windowsversion: %s, iplist: %s, soapip: %s",
  354. sClientUpdateInfo.ulTrackId,
  355. (sClientUpdateInfo.szComputerName) ? sClientUpdateInfo.szComputerName : "-",
  356. (sClientUpdateInfo.szUsername) ? sClientUpdateInfo.szUsername : "-",
  357. (sClientUpdateInfo.szClientVersion) ? sClientUpdateInfo.szClientVersion : "-",
  358. (sClientUpdateInfo.szWindowsVersion) ? sClientUpdateInfo.szWindowsVersion : "-",
  359. (sClientUpdateInfo.szClientIPList) ? sClientUpdateInfo.szClientIPList : "-",
  360. soap->host);
  361. if (!sClientUpdateInfo.szComputerName)
  362. sClientUpdateInfo.szComputerName = const_cast<char *>(""); //Client has no name?
  363. if(!sClientUpdateInfo.szUsername) {
  364. er = KCERR_NO_ACCESS;
  365. ec_log_err("Client update: trackid: 0x%08X, Client did not send a username", sClientUpdateInfo.ulTrackId);
  366. }
  367. // validate user name
  368. er = lpecSession->GetUserManagement()->SearchObjectAndSync(sClientUpdateInfo.szUsername, 0x01/*EMS_AB_ADDRESS_LOOKUP*/, &ulUserID);
  369. if (er != erSuccess) {
  370. er = KCERR_NO_ACCESS;
  371. ec_log_err("Client update: trackid: 0x%08X, unknown username '%s'", sClientUpdateInfo.ulTrackId, sClientUpdateInfo.szUsername);
  372. }
  373. if(lpecSession->GetUserManagement()->IsInternalObject(ulUserID)) {
  374. er = KCERR_NO_ACCESS;
  375. ec_log_err("Client update: trackid: 0x%08X, Wrong user data. User name '%s' is a reserved user", sClientUpdateInfo.ulTrackId, sClientUpdateInfo.szUsername);
  376. goto exit;
  377. }
  378. // Check if the user connect to the right server, else redirect
  379. if (lpecSession->GetSessionManager()->IsDistributedSupported() )
  380. {
  381. objectdetails_t sUserDetails;
  382. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserID, &sUserDetails);
  383. if (er != erSuccess)
  384. goto exit;
  385. /* Check if this is the correct server */
  386. string strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
  387. if (strServerName.empty()) {
  388. ec_log_err("Client update: trackid: 0x%08X, User '%s' has no default server", sClientUpdateInfo.ulTrackId, sClientUpdateInfo.szUsername);
  389. er = KCERR_NO_ACCESS;
  390. goto exit;
  391. }
  392. if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
  393. string strServerPath;
  394. er = GetBestServerPath(soap, lpecSession, strServerName, &strServerPath);
  395. if (er != erSuccess)
  396. goto exit;
  397. lpsResponse->lpszServerPath = s_strcpy(soap, strServerPath.c_str());// Server Path must always utf8 (also in 6.40.x)
  398. ec_log_info("Client update: trackid: 0x%08X, User \"%s\" is redirected to \"%s\"", sClientUpdateInfo.ulTrackId, sClientUpdateInfo.szUsername, lpsResponse->lpszServerPath);
  399. g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
  400. er = KCERR_UNABLE_TO_COMPLETE;
  401. goto exit;
  402. }
  403. }
  404. er = ECLicenseClient(g_lpConfig->GetSetting("license_socket"), atoui(g_lpConfig->GetSetting("license_timeout")))
  405. .Auth(sClientUpdateInfo.sLicenseReq.__ptr, sClientUpdateInfo.sLicenseReq.__size, &lpLicenseResponse, &ulLicenseResponse);
  406. if (er != erSuccess) {
  407. ec_log_err("Client update: trackid: 0x%08X, Invalid license request, error: 0x%08X.", sClientUpdateInfo.ulTrackId, er);
  408. goto exit;
  409. }
  410. if (sClientUpdateInfo.szClientVersion == NULL || sClientUpdateInfo.szClientVersion[0] == '\0') {
  411. ec_log_info("Client update: trackid: 0x%08X, The client did not sent the current version number.", sClientUpdateInfo.ulTrackId);
  412. } else if (!GetVersionFromString(sClientUpdateInfo.szClientVersion, &sCurrentVersion)) {
  413. ec_log_err("Client update: trackid: 0x%08X, Failed in getting version from input data.", sClientUpdateInfo.ulTrackId);
  414. goto exit; //@fixme can we give the latest?
  415. }
  416. if (!GetLatestVersionAtServer(lpszClientUpdatePath, sClientUpdateInfo.ulTrackId, &sLatestVersion)) {
  417. ec_log_err("Client update: trackid: 0x%08X, No updates found on server.", sClientUpdateInfo.ulTrackId);
  418. er = KCERR_NO_ACCESS;
  419. goto exit;
  420. }
  421. res = CompareVersions(sCurrentVersion, sLatestVersion);
  422. if (res == 0) {
  423. ec_log_info("Client update: trackid: 0x%08X, Client already has the latest version.", sClientUpdateInfo.ulTrackId);
  424. goto ok;
  425. } else if (res > 0) {
  426. ec_log_info("Client update: trackid: 0x%08X, Client has newer version than server.", sClientUpdateInfo.ulTrackId);
  427. goto ok;
  428. }
  429. if (!GetClientMSINameFromVersion(sLatestVersion, &strClientMSIName)) {
  430. ec_log_info("Client update: trackid: 0x%08X, No suitable version available.", sClientUpdateInfo.ulTrackId);
  431. er = KCERR_NO_ACCESS;
  432. goto exit;
  433. }
  434. if (ConvertAndValidatePath(lpszClientUpdatePath, strClientMSIName, &strPath) != true) {
  435. ec_log_err("Client update: trackid: 0x%08X, Error in path conversion and validation.", sClientUpdateInfo.ulTrackId);
  436. er = KCERR_NO_ACCESS;
  437. goto exit;
  438. }
  439. fd = fopen(strPath.c_str(), "rb");
  440. if (!fd) {
  441. ec_log_err("Client update: trackid: 0x%08X, Path not found %s.", sClientUpdateInfo.ulTrackId, strPath.c_str());
  442. er = KCERR_NO_ACCESS;
  443. goto exit;
  444. }
  445. // Update auto update client status
  446. VersionToString(sCurrentVersion, &strCurVersion);
  447. VersionToString(sLatestVersion, &strLatestVersion);
  448. tNow = time(NULL); // Get current time
  449. strQuery = "REPLACE INTO clientupdatestatus(userid, trackid, updatetime, currentversion, latestversion, computername, status) VALUES ("+
  450. stringify(ulUserID)+", "+stringify(sClientUpdateInfo.ulTrackId)+", FROM_UNIXTIME("+
  451. stringify(tNow) + "), \"" + strCurVersion + "\", \"" + strLatestVersion + "\", \"" +
  452. lpDatabase->Escape(sClientUpdateInfo.szComputerName).c_str()+"\", "+ stringify(UPDATE_STATUS_PENDING) + ")";
  453. // ignore error in database tracking, SQL error logged in server, still send new client
  454. lpDatabase->DoUpdate(strQuery);
  455. soap->fmimereadopen = &mime_file_read_open;
  456. soap->fmimeread = &mime_file_read;
  457. soap->fmimereadclose = &mime_file_read_close;
  458. // Setup the MTOM Attachments
  459. lpsResponse->sStreamData.xop__Include.__ptr = (unsigned char*)fd;
  460. lpsResponse->sStreamData.xop__Include.__size = 0;
  461. lpsResponse->sStreamData.xop__Include.type = s_strcpy(soap, "application/binary");
  462. lpsResponse->sStreamData.xop__Include.id = s_strcpy(soap, "zarafaclient");
  463. ec_log_err("Client update: trackid: 0x%08X, Sending new installer %s", sClientUpdateInfo.ulTrackId, strClientMSIName.c_str());
  464. ok: // Client is already up to date
  465. lpsResponse->sLicenseResponse.__size = ulLicenseResponse;
  466. lpsResponse->sLicenseResponse.__ptr = (unsigned char *)s_memcpy(soap, (const char *)lpLicenseResponse, ulLicenseResponse);
  467. lpsResponse->ulLogLevel = ulLogLevel; // 0 = none, 1 = on errors, 2 = always
  468. exit:
  469. if(lpecSession) {
  470. lpecSession->Unlock();
  471. g_lpSessionManager->RemoveSessionInternal(lpecSession);
  472. }
  473. lpsResponse->er = er;
  474. free(lpLicenseResponse);
  475. if (er && fd)
  476. fclose(fd);
  477. soap->mode &= ~SOAP_XML_TREE;
  478. soap->omode &= ~SOAP_XML_TREE;
  479. return SOAP_OK;
  480. }
  481. _kc_export int ns__setClientUpdateStatus(struct soap *soap,
  482. struct clientUpdateStatusRequest sClientUpdateStatus,
  483. struct clientUpdateStatusResponse *lpsResponse)
  484. {
  485. unsigned int er = erSuccess;
  486. ECSession *lpecSession = NULL;
  487. ECDatabase *lpDatabase = NULL;
  488. std::string strQuery;
  489. const char *lpszClientUpdatePath = g_lpConfig->GetSetting("client_update_path");
  490. const char *lpszLogPath = g_lpConfig->GetSetting("client_update_log_path");
  491. if (!parseBool(g_lpConfig->GetSetting("client_update_enabled"))) {
  492. er = KCERR_NO_SUPPORT;
  493. goto exit;
  494. }
  495. if (!lpszClientUpdatePath || lpszClientUpdatePath[0] == 0) {
  496. ec_log_err("Client update: trackid: 0x%08X, The configuration field 'client_update_path' is empty.", sClientUpdateStatus.ulTrackId);
  497. er = KCERR_NO_ACCESS;
  498. goto exit;
  499. }
  500. if (!lpszLogPath || lpszLogPath[0] == 0) {
  501. ec_log_err("Client update: trackid: 0x%08X, The configuration field 'client_update_log_path' is empty.", sClientUpdateStatus.ulTrackId);
  502. er = KCERR_NO_ACCESS;
  503. goto exit;
  504. }
  505. er = g_lpSessionManager->CreateSessionInternal(&lpecSession);
  506. if(er != erSuccess)
  507. goto exit;
  508. // Lock the session
  509. lpecSession->Lock();
  510. er = lpecSession->GetDatabase(&lpDatabase);
  511. if (er != erSuccess)
  512. goto exit;
  513. soap->fmimewriteopen = mime_file_write_open;
  514. soap->fmimewriteclose = mime_file_write_close;
  515. soap->fmimewrite = mime_file_write;
  516. if (sClientUpdateStatus.ulLastErrorCode){
  517. //@fixme if we know errors we can add a user friendly error message
  518. if( sClientUpdateStatus.ulLastErrorAction == 2 /*ACTION_VALIDATE_CLIENT*/)
  519. ec_log_err("Client update: trackid: 0x%08X, Installation failed, can not validate MSI file, error code 0x%08X", sClientUpdateStatus.ulTrackId, sClientUpdateStatus.ulLastErrorCode);
  520. else if (sClientUpdateStatus.ulLastErrorAction == 3 /*ACTION_INSTALL_CLIENT*/)
  521. ec_log_err("Client update: trackid: 0x%08X, Installation failed, installer returned error code 0x%08X", sClientUpdateStatus.ulTrackId, sClientUpdateStatus.ulLastErrorCode);
  522. else
  523. ec_log_err("Client update: trackid: 0x%08X, Installation failed, error code 0x%08X", sClientUpdateStatus.ulTrackId, sClientUpdateStatus.ulLastErrorCode);
  524. } else {
  525. ec_log_err("Client update: trackid: 0x%08X, Installed successfully updated", sClientUpdateStatus.ulTrackId);
  526. }
  527. if (soap_check_mime_attachments(soap)) {
  528. // attachments are present, channel is still open
  529. struct soap_multipart *content;
  530. std::string strFile;
  531. std::string strFilePath;
  532. // Path to store the log files
  533. strFilePath = lpszLogPath;
  534. strFilePath+= "/";
  535. strFilePath+= stringify(sClientUpdateStatus.ulTrackId, true) + "/";
  536. if (CreatePath(strFilePath.c_str()) != 0) {
  537. ec_log_crit("Client update: trackid: 0x%08X, Unable to create directory '%s'!", sClientUpdateStatus.ulTrackId, strFilePath.c_str());
  538. er = KCERR_NO_ACCESS;
  539. goto exit;
  540. }
  541. ec_log_err("Client update: trackid: 0x%08X, Log files saved in '%s'", sClientUpdateStatus.ulTrackId, strFilePath.c_str());
  542. gsoap_size_t ulFile = 0;
  543. while (true) {
  544. if (ulFile >= sClientUpdateStatus.sFiles.__size)
  545. break;
  546. strFile = strFilePath;
  547. // Check if this not a hack from someone!
  548. if(sClientUpdateStatus.sFiles.__ptr[ulFile].lpszAttachmentName && strstr(sClientUpdateStatus.sFiles.__ptr[ulFile].lpszAttachmentName, "..") == NULL)
  549. strFile += sClientUpdateStatus.sFiles.__ptr[ulFile].lpszAttachmentName;
  550. else
  551. strFile += stringify(rand_mt()) + ".tmp";
  552. content = soap_get_mime_attachment(soap, (void*)strFile.c_str());
  553. if (!content)
  554. break;
  555. ++ulFile;
  556. }
  557. if (soap->error) {
  558. er = KCERR_NO_ACCESS;
  559. goto exit;
  560. }
  561. }
  562. strQuery = "UPDATE clientupdatestatus SET status="+stringify((sClientUpdateStatus.ulLastErrorCode)?UPDATE_STATUS_FAILED : UPDATE_STATUS_SUCCESS)+" WHERE trackid=" + stringify(sClientUpdateStatus.ulTrackId);
  563. er = lpDatabase->DoUpdate(strQuery);
  564. if (er != erSuccess)
  565. goto exit;
  566. exit:
  567. if(lpecSession) {
  568. lpecSession->Unlock();
  569. g_lpSessionManager->RemoveSessionInternal(lpecSession);
  570. }
  571. lpsResponse->er = er;
  572. return SOAP_OK;
  573. }