extensions/updater/extension.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Mon May 13 15:33:55 2013 -0400 (2013-05-13)
changeset 3914 ec79ea87c2e7
parent 3630 b72450a8be42
child 4188 89782d5fc91b
permissions -rw-r--r--
One more dota build fix.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod Updater Extension
     5  * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <sourcemod_version.h>
    33 #include <stdio.h>
    34 #include <stdarg.h>
    35 #include <string.h>
    36 #include <stdlib.h>
    37 #include "extension.h"
    38 #include "Updater.h"
    39 #include <sh_list.h>
    40 #include <sh_string.h>
    41 
    42 #define DEFAULT_UPDATE_URL			"http://www.sourcemod.net/update/"
    43 
    44 using namespace SourceHook;
    45 
    46 SmUpdater g_Updater;		/**< Global singleton for extension's main interface */
    47 SMEXT_LINK(&g_Updater);
    48 
    49 IWebternet *webternet;
    50 static List<String *> update_errors;
    51 static IThreadHandle *update_thread;
    52 static String update_url;
    53 
    54 bool SmUpdater::SDK_OnLoad(char *error, size_t maxlength, bool late)
    55 {
    56 	sharesys->AddDependency(myself, "webternet.ext", true, true);
    57 
    58 	SM_GET_IFACE(WEBTERNET, webternet);
    59 
    60 	const char *url = smutils->GetCoreConfigValue("AutoUpdateURL");
    61 	if (url == NULL)
    62 	{
    63 		url = DEFAULT_UPDATE_URL;
    64 	}
    65 	update_url.assign(url);
    66 	
    67 	ThreadParams params;
    68 	params.flags = Thread_Default;
    69 	params.prio = ThreadPrio_Low;
    70 	update_thread = threader->MakeThread(this, &params);
    71 
    72 	if (update_thread == NULL)
    73 	{
    74 		smutils->Format(error, maxlength, "Could not create thread");
    75 		return false;
    76 	}
    77 
    78 	return true;
    79 }
    80 
    81 void SmUpdater::SDK_OnUnload()
    82 {
    83 	/* An interface drop might have killed this thread.  
    84 	 * But if the handle is still there, we have to wait.
    85 	 */
    86 	if (update_thread != NULL)
    87 	{
    88 		update_thread->WaitForThread();
    89 		update_thread->DestroyThis();
    90 	}
    91 
    92 	/* Clear message tables */
    93 	List<String *>::iterator iter = update_errors.begin();
    94 	while (iter != update_errors.end())
    95 	{
    96 		iter = update_errors.erase(iter);
    97 	}
    98 }
    99 
   100 bool SmUpdater::QueryInterfaceDrop(SourceMod::SMInterface *pInterface)
   101 {
   102 	if (pInterface == webternet)
   103 	{
   104 		return false;
   105 	}
   106 
   107 	return true;
   108 }
   109 
   110 void SmUpdater::NotifyInterfaceDrop(SMInterface *pInterface)
   111 {
   112 	if (pInterface == webternet)
   113 	{
   114 		/* Can't be in the thread if we're losing this extension. */
   115 		update_thread->WaitForThread();
   116 		update_thread->DestroyThis();
   117 		update_thread = NULL;
   118 	}
   119 }
   120 
   121 static void PumpUpdate(void *data)
   122 {
   123 	String *str;
   124 	bool new_files = false;
   125 	List<String *>::iterator iter;
   126 
   127 	char path[PLATFORM_MAX_PATH];
   128 	UpdatePart *temp;
   129 	UpdatePart *part = (UpdatePart*)data;
   130 	while (part != NULL)
   131 	{
   132 		if (strstr(part->file, "..") != NULL)
   133 		{
   134 			/* Naughty naughty */
   135 			AddUpdateError("Detected invalid path escape (..): %s", part->file);
   136 			goto skip_create;
   137 		}
   138 		if (part->data == NULL)
   139 		{
   140 			smutils->BuildPath(Path_SM, path, sizeof(path), "gamedata/%s", part->file);
   141 			if (libsys->IsPathDirectory(path))
   142 			{
   143 				goto skip_create;
   144 			}
   145 			if (!libsys->CreateFolder(path))
   146 			{
   147 				AddUpdateError("Could not create folder: %s", path);
   148 			}
   149 			else
   150 			{
   151 				smutils->LogMessage(myself, "Created folder \"%s\" from updater", path);
   152 			}
   153 		}
   154 		else
   155 		{
   156 			smutils->BuildPath(Path_SM, path, sizeof(path), "gamedata/%s", part->file);
   157 			FILE *fp = fopen(path, "wb");
   158 			if (fp == NULL)
   159 			{
   160 				AddUpdateError("Could not open %s for writing", path);
   161 				goto skip_create;
   162 			}
   163 			if (fwrite(part->data, 1, part->length, fp) != part->length)
   164 			{
   165 				AddUpdateError("Could not write file %s", path);
   166 			}
   167 			else
   168 			{
   169 				smutils->LogMessage(myself,
   170 					"Successfully updated gamedata file \"%s\"",
   171 					part->file);
   172 				new_files = true;
   173 			}
   174 			fclose(fp);
   175 		}
   176 skip_create:
   177 		temp = part->next;
   178 		free(part->data);
   179 		free(part->file);
   180 		delete part;
   181 		part = temp;
   182 	}
   183 
   184 	if (update_errors.size())
   185 	{
   186 		smutils->LogError(myself, "--- BEGIN ERRORS FROM AUTOMATIC UPDATER ---");
   187 
   188 		for (iter = update_errors.begin();
   189 			 iter != update_errors.end();
   190 			 iter++)
   191 		{
   192 			str = (*iter);
   193 			smutils->LogError(myself, "%s", str->c_str());
   194 		}
   195 
   196 		smutils->LogError(myself, "--- END ERRORS FROM AUTOMATIC UPDATER ---");
   197 	}
   198 
   199 	if (new_files)
   200 	{
   201 		const char *force_restart = smutils->GetCoreConfigValue("ForceRestartAfterUpdate");
   202 		if (force_restart == NULL || strcasecmp(force_restart, "yes") != 0)
   203 		{
   204 			smutils->LogMessage(myself,
   205 				"SourceMod has been updated, please reload it or restart your server.");
   206 		}
   207 		else
   208 		{
   209 			char buffer[255];
   210 			smutils->Format(buffer,
   211 				sizeof(buffer),
   212 				"meta unload %d\n",
   213 				smutils->GetPluginId());
   214 			gamehelpers->ServerCommand(buffer);
   215 			smutils->Format(buffer,
   216 				sizeof(buffer),
   217 				"changelevel \"%s\"\n",
   218 				gamehelpers->GetCurrentMap());
   219 			gamehelpers->ServerCommand(buffer);
   220 			gamehelpers->ServerCommand("echo SourceMod has been restarted from an automatic update.\n");
   221 		}
   222 	}
   223 }
   224 
   225 void SmUpdater::RunThread(IThreadHandle *pHandle)
   226 {
   227 	UpdateReader ur;
   228 
   229 	ur.PerformUpdate(update_url.c_str());
   230 
   231 	smutils->AddFrameAction(PumpUpdate, ur.DetachParts());
   232 }
   233 
   234 void SmUpdater::OnTerminate(IThreadHandle *pHandle, bool cancel)
   235 {
   236 }
   237 
   238 void AddUpdateError(const char *fmt, ...)
   239 {
   240 	va_list ap;
   241 	char buffer[2048];
   242 
   243 	va_start(ap, fmt);
   244 	smutils->FormatArgs(buffer, sizeof(buffer), fmt, ap);
   245 	va_end(ap);
   246 
   247 	update_errors.push_back(new String(buffer));
   248 }
   249 
   250 const char *SmUpdater::GetExtensionVerString()
   251 {
   252 	return SM_VERSION_STRING;
   253 }
   254 
   255 const char *SmUpdater::GetExtensionDateString()
   256 {
   257 	return SM_BUILD_TIMESTAMP;
   258 }
   259