Do binary writes instead.
3 * =============================================================================
4 * SourceMod Updater Extension
5 * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
36 #include "extension.h"
39 #include <sh_string.h>
41 #define DEFAULT_UPDATE_URL "http://www.sourcemod.net/update/"
43 using namespace SourceHook;
45 SmUpdater g_Updater; /**< Global singleton for extension's main interface */
46 SMEXT_LINK(&g_Updater);
48 IWebternet *webternet;
49 static List<String *> update_errors;
50 static IThreadHandle *update_thread;
51 static String update_url;
53 bool SmUpdater::SDK_OnLoad(char *error, size_t maxlength, bool late)
55 sharesys->AddDependency(myself, "webternet.ext", true, true);
57 SM_GET_IFACE(WEBTERNET, webternet);
60 params.flags = Thread_Default;
61 params.prio = ThreadPrio_Low;
62 update_thread = threader->MakeThread(this, ¶ms);
64 if (update_thread == NULL)
66 smutils->Format(error, maxlength, "Could not create thread");
70 const char *url = smutils->GetCoreConfigValue("AutoUpdateURL");
73 url = DEFAULT_UPDATE_URL;
75 update_url.assign(url);
80 void SmUpdater::SDK_OnUnload()
82 /* An interface drop might have killed this thread.
83 * But if the handle is still there, we have to wait.
85 if (update_thread != NULL)
87 update_thread->WaitForThread();
88 update_thread->DestroyThis();
91 /* Clear message tables */
92 List<String *>::iterator iter;
93 while (iter != update_errors.end())
95 iter = update_errors.erase(iter);
99 bool SmUpdater::QueryInterfaceDrop(SourceMod::SMInterface *pInterface)
101 if (pInterface == webternet)
109 void SmUpdater::NotifyInterfaceDrop(SMInterface *pInterface)
111 if (pInterface == webternet)
113 /* Can't be in the thread if we're losing this extension. */
114 update_thread->WaitForThread();
115 update_thread->DestroyThis();
116 update_thread = NULL;
120 static void PumpUpdate(void *data)
123 bool new_files = false;
124 List<String *>::iterator iter;
126 char path[PLATFORM_MAX_PATH];
128 UpdatePart *part = (UpdatePart*)data;
131 if (strstr(part->file, "..") != NULL)
133 /* Naughty naughty */
134 AddUpdateError("Detected invalid path escape (..): %s", part->file);
137 if (part->data == NULL)
139 smutils->BuildPath(Path_SM, path, sizeof(path), "gamedata/%s", part->file);
140 if (libsys->IsPathDirectory(path))
144 if (!libsys->CreateFolder(path))
146 AddUpdateError("Could not create folder: %s", path);
150 smutils->LogMessage(myself, "Created folder \"%s\" from updater", path);
155 smutils->BuildPath(Path_SM, path, sizeof(path), "gamedata/%s", part->file);
156 FILE *fp = fopen(path, "wb");
159 AddUpdateError("Could not open %s for writing", path);
162 fwrite(part->data, 1, part->length, fp);
164 smutils->LogMessage(myself,
165 "Successfully updated gamedata file \"%s\"",
177 if (update_errors.size())
179 smutils->LogError(myself, "--- BEGIN ERRORS FROM AUTOMATIC UPDATER ---");
181 for (iter = update_errors.begin();
182 iter != update_errors.end();
186 smutils->LogError(myself, "%s", str->c_str());
189 smutils->LogError(myself, "--- END ERRORS FROM AUTOMATIC UPDATER ---");
194 const char *force_restart = smutils->GetCoreConfigValue("ForceRestartAfterUpdate");
195 if (force_restart == NULL || strcasecmp(force_restart, "yes") != 0)
197 smutils->LogMessage(myself,
198 "SourceMod has been updated, please reload it or restart your server.");
203 smutils->Format(buffer,
206 smutils->GetPluginId());
207 gamehelpers->ServerCommand(buffer);
208 smutils->Format(buffer,
210 "changelevel \"%s\"\n",
211 gamehelpers->GetCurrentMap());
212 gamehelpers->ServerCommand(buffer);
213 gamehelpers->ServerCommand("echo SourceMod has been restarted from an automatic update.\n");
218 void SmUpdater::RunThread(IThreadHandle *pHandle)
222 ur.PerformUpdate(update_url.c_str());
224 smutils->AddFrameAction(PumpUpdate, ur.DetachParts());
227 void SmUpdater::OnTerminate(IThreadHandle *pHandle, bool cancel)
231 void AddUpdateError(const char *fmt, ...)
237 smutils->FormatArgs(buffer, sizeof(buffer), fmt, ap);
240 update_errors.push_back(new String(buffer));