source: subversion/sites/other/tilesAtHome/Upload/tile2.php @ 3839

Last change on this file since 3839 was 3709, checked in by spaetz, 13 years ago

Only log invalid tokens. Change temp dir directory.

File size: 13.7 KB
Line 
1<?php
2# Tiles upload handler, accepts ZIP files, saves to database
3# OJW 2006, GNU GPL v2 or later
4
5if(0){ // Option to turn off uploads
6  AbortWithError(503, "Uploads temporarily disabled");
7}
8
9include("../lib/log.inc");
10include("../lib/tilenames.inc");
11include("../lib/users.inc");
12include("../lib/versions.inc");
13include("../lib/layers.inc");
14include("../lib/requests.inc");
15include("../lib/checkupload.inc");
16include("../lib/cpu.inc");
17include("../lib/queue.inc");
18include("../lib/tokens.inc");
19
20# Option to turn-off non-single-tileset uploads (was only used for testing)
21if(0){
22  if($_POST["single_tileset"] != "yes"){
23    AbortWithError(401, "We're testing LA2's single-tileset uploads, normal ones are being discarded for now");   
24  }
25}
26
27if(0){
28  $Load = GetLoadAvg();
29  //logMsg("$Load load", 4);
30  if($Load < 0){
31    logMsg("Load average failed", 4);
32  }
33  elseif($Load > 2.6){
34    AbortWithError(503, "Server is very very busy...");
35  }
36}
37
38
39# Option to limit by CPU
40if(0){
41  $Idle = GetLoad("idle");
42  logMsg("$Idle idle", 4);
43  if($Idle < 0){
44    logMsg("Idle count failed", 4);
45  }
46  elseif($Idle < 25){
47    AbortWithError(503, "Server is very very busy...");
48  }
49}
50
51# Get password from posted form data (format mp=user|pass)
52$Password = $_POST["mp"];
53list($User,$Pass) = explode("|", $Password);
54
55$UserID = checkUser($User, $Pass);
56$VersionID = checkVersion($_POST["version"]);
57
58# If credentials are valid
59if($UserID < 1){
60  AbortWithError(401, "Invalid username");
61  exit; # Redundant, failsafe
62}
63
64# Check whether version number is acceptable
65if($VersionID < 0){
66  AbortWithError(401, "Client version not recognised or too old");
67}
68
69# Option to check upload tokens
70if(1){
71  list($Token1, $Token2) = GetTokens(-1, "testing");
72  $Token = $_POST["token"];
73  if($Token == $Token1 || $Token == $Token2){
74    //logMsg("Valid token from user $UserID", 5);
75  }
76  else{
77    logMsg(sprintf("Invalid token from user %d (%s)", $UserID, htmlentities(lookupUser($UserID))), 5);
78  }
79}
80
81
82if(0){
83  // Old method
84  HandleUpload($_FILES['file'], $User, $UserID, $VersionID);
85}
86else
87{
88  // New method
89  if(QueueLength() > MaxQueueLength())
90    AbortWithError(503, "too much stuff in the queue already");
91
92  PlaceInQueue($_FILES['file'], $UserID, $VersionID);
93}
94exit;
95
96
97function PlaceInQueue($Filename, $UserID, $VersionID){
98  $QueueLocation = QueueDirectory();
99  $Name = md5(uniqid(rand(), true));
100 
101  $MetaFile = $QueueLocation . $Name . ".txt";
102  $fp = fopen($MetaFile, "w");
103  if(!$fp)
104      return;
105  fputs($fp, "user = $UserID\nversion = $VersionID\n");
106  fclose($fp);
107 
108  $ZipFile = $QueueLocation . $Name . ".zip";
109  move_uploaded_file($Filename["tmp_name"], $ZipFile);
110}
111
112function AbortWithError($Code, $Message){
113  header(sprintf("HTTP/1.0 %d %s", $Code, $Message));
114  header("Content-type:text/plain");
115  printf("%s\n", $Message);
116  exit;
117}
118
119
120function HandleUpload($File, $User, $UserID, $VersionID){
121
122  # All error-messages etc are plain text for use by clients
123  header("Content-type:text/plain");
124 
125  # Decide on the name of a Temporary directory
126  $Dir = TempDir();
127 
128  # Check the uploaded ZIP file
129  $Size = $File['size'];
130
131  if($Size <= 0){
132    AbortWithError(400, "No file uploaded or file too large");
133  }
134
135  # Keep going if the user presses stop, to ensure temporary directories get erased
136  # see also register_shutdown_function() for another option
137  ignore_user_abort();
138   
139  # Create temporary directory
140  if(!mkdir($Dir)){
141    AbortWithError(503, "Can't create temporary directory");
142  }
143     
144  # Uncompress the uploaded tiles
145  # -j means to ignore any pathnames in the ZIP file
146  # -d $Dir specifies the directory to unzip to
147  # $Filename is the zip file
148  system(sprintf("unzip -j -d %s %s", $Dir, $File['tmp_name']));
149 
150  # Process all the tiles (return number of tiles done)
151  $Count = HandleDir($Dir, $User, $UserID, $VersionID);
152       
153  # Delete the temporary directory and everything inside
154  DelDir($Dir);
155 
156  printf("OK, %d", $Count);
157}
158
159#----------------------------------------------------------------------
160# Delete the temporary directory and everything inside
161#----------------------------------------------------------------------
162function DelDir($Dir){
163  $dp = opendir($Dir);
164  while(($file = readdir($dp)) !== false){
165    if($file != "." && $file != ".."){
166      $Filename = "$Dir/$file";
167      unlink($Filename);
168    }
169  }
170  closedir($dp);
171  rmdir($Dir);
172}
173
174#----------------------------------------------------------------------
175# Processes tiles that are currently sitting in a temp directory
176#----------------------------------------------------------------------
177function HandleDir($Dir, $User, $UserID, $VersionID){
178  $Count = 0;
179  $dp = opendir($Dir);
180  $TileList = array();
181  $BlankTileList = array();
182
183  list($ValidTileset, $TilesetX, $TilesetY, $TilesetLayer) = CheckUploadDir($Dir);
184
185  while(($file = readdir($dp)) !== false){
186    $Filename = "$Dir/$file";
187    $Count += HandleFile($Filename, $User, $VersionID, $TileList, $BlankTileList);
188  }
189  closedir($dp);
190
191  # Connect to the database
192  include("../connect/connect.php");
193
194  if($ValidTileset)
195    SaveTilesetMetadata($TilesetX,$TilesetY,$TilesetLayer, $UserID, $VersionID);
196  else
197    SaveMetadata($TileList, $UserID, $VersionID);
198
199  SaveBlankTiles($BlankTileList, $UserID);
200
201  # Disconnect from database
202  mysql_close();
203
204  return($Count);
205}
206
207#-----------------------------------------------------------------------------------
208# Save metadata for each tile in the upload
209#-----------------------------------------------------------------------------------
210function SaveMetadata($TileList, $UserID, $VersionID){
211 
212  SaveUserStats($UserID, $VersionID, count($TileList));
213 
214  RemoveFromQueue($TileList);
215 
216  # Each element in TileList is a snippet of values (x,y,z,type,size) for each tile
217  foreach($TileList as $SqlSnippet){
218   
219    // Use this line if you need access to separate fields
220    // list($X, $Y, $Z, $Layer, $Size) = explode(",", $CSV);
221
222    $Fields = "x, y, z, type, size, date, user, version, tileset";
223    $Values = sprintf("%s, now(), %d, %d, 0", $SqlSnippet, $UserID, $VersionID);
224 
225    $SQL = sprintf("replace into `tiles_meta` (%s) values (%s);", $Fields, $Values);
226    mysql_query($SQL);
227  }
228}
229
230#------------------------------------------------------------------------------------
231# Save uploaded blank tiles in the database
232#------------------------------------------------------------------------------------
233function SaveBlankTiles($BlankTileList, $UserID){
234  # Each element in BlankTileList is a snippet of values (x,y,z,type,size) for each tile
235  foreach($BlankTileList as $SqlSnippet){
236
237    // TODO: blank tiles can be z-12, which means they can fulfil a request
238    list($X, $Y, $Z, $Layer, $Type) = explode(",", $SqlSnippet);
239    if($Z == 12){
240      moveRequest($X, $Y, REQUEST_ACTIVE, REQUEST_DONE, 0);
241    }
242   
243    # Make a blank tile
244    if( $Type >= 0 )
245    {
246      $Fields = "x, y, z, layer, type, date, user";
247      $Values = sprintf("%s, now(), %d", $SqlSnippet, $UserID);
248
249      $SQL = sprintf("replace into `tiles_blank` (%s) values (%s);", $Fields, $Values);
250    }
251    else
252    {
253      # Delete a blank tile
254      $SQL = sprintf("delete from `tiles_blank` where `x`=%d AND `y`=%s AND `z`=%s AND `layer`=%d", $X, $Y, $Z, $Layer);
255    }
256    DeleteRealTile($X,$Y,$Z,$Layer);
257
258    mysql_query($SQL);
259   
260    logSqlError();
261  }
262}
263
264#------------------------------------------------------------------------------------
265# Delete a tile and its metadata (usually when a blank tile is uploaded in its place)
266#------------------------------------------------------------------------------------
267function DeleteRealTile($X,$Y,$Z,$LayerID){
268 
269  # Delete the meta database entry
270  $SQL = sprintf(
271    "DELETE FROM `tiles_meta` WHERE `x`=%d AND `y`=%d AND `z`=%d AND `type`=%d;",
272      $X,$Y,$Z,$LayerID);
273  mysql_query($SQL);
274  logSqlError();
275 
276  # Delete the image, if exists
277  $NewFilename = TileName($X,$Y,$Z, layerDir($LayerID));
278  if($NewFilename){
279    if(file_exists($NewFilename)){
280      unlink($NewFilename);
281    }
282  }
283}
284
285#-----------------------------------------------------------------------------
286# Save metadata when an entire tileset is uploaded at once
287#-----------------------------------------------------------------------------
288function SaveTilesetMetadata($X,$Y,$Layer,$UserID, $VersionID){
289  SaveUserStats($UserID, $VersionID, 1365);
290 
291  moveRequest($X, $Y, REQUEST_ACTIVE, REQUEST_DONE, 0);
292 
293  $LayerID = checkLayer($Layer);
294
295  $Fields = "x, y, z, type, size, date, user, version, tileset";
296  $Values = sprintf("%d,%d,%d,%d,%d,now(), %d, %d, 1", $X,$Y,12, $LayerID, 0, $UserID, $VersionID);
297 
298  $SQL = sprintf("replace into `tiles_meta` (%s) values (%s);", $Fields, $Values);
299  mysql_query($SQL);
300 
301  logSqlError();
302
303}
304
305#-----------------------------------------------------------------------------
306# Removes completed* tilesets from queue
307# * where completed means "z12 was uploaded"
308#-----------------------------------------------------------------------------
309function RemoveFromQueue($TileList){
310  foreach($TileList as $CSV){
311    list($X, $Y, $Z, $Layer, $Size) = explode(",", $CSV);
312    if($Z == 12){
313   
314      moveRequest($X, $Y, REQUEST_ACTIVE, REQUEST_DONE, 0);
315       
316      #logMsg(sprintf("Moving tile %d, %d from %d to %d", $X, $Y, REQUEST_ACTIVE, REQUEST_DONE), 4);
317      logSqlError();
318    }
319  }
320}
321
322#---------------------------------------------------------------------------
323# Update user info with their latest upload
324#---------------------------------------------------------------------------
325function SaveUserStats($UserID, $VersionID, $NumTiles){
326  $SQL = 
327    "update `tiles_users` set ".
328      "`uploads` = `uploads` + 1, ".
329      sprintf("`tiles` = `tiles` + %d, ", $NumTiles).
330      sprintf("`version` = %d, ", $VersionID).
331      "`last_upload` = now() ".
332    " where ".
333    sprintf("`id`=%d;", $UserID);
334   
335  mysql_query($SQL);
336}
337
338#----------------------------------------------------------------------
339# Processes tile PNG images
340#----------------------------------------------------------------------
341function HandleFile($Filename, $User, $VersionID, &$TileList, &$BlankTileList){
342  if(preg_match("/([a-z]+)_(\d+)_(\d+)_(\d+)\.png/", $Filename, $Matches)){
343    $Layername = $Matches[1];
344    $Z = $Matches[2];
345    $X = $Matches[3];
346    $Y = $Matches[4];
347    $Valid = TileValid($X,$Y,$Z);
348    if($Valid){
349     
350      $Layer = checkLayer($Layername);
351      if($Layer > 0){
352        InsertTile($X,$Y,$Z,$Layer,$User,$Filename, $VersionID, $TileList, $BlankTileList);
353        return(1);
354      }
355      else{
356        logMsg("Invalid layer $Layer from $User ($Layername)", 2);
357      }
358    }
359    else{
360      #logMsg("Invalid tile $Filename from $User", 3);
361    }
362  }
363  return(0);
364}
365
366function InsertTile($X,$Y,$Z,$Layer,$User,$OldFilename, $VersionID, &$TileList, &$BlankTileList){
367  if(!TileValid($X,$Y,$Z)){
368    printf("INVALID %d,%d,%d\n", $X,$Y,$Z);
369    return;
370  }
371 
372  $Size = filesize($OldFilename);
373
374 
375  # Decide on a filename
376  $NewFilename = TileName($X,$Y,$Z, layerDir($Layer));
377  if(!$NewFilename){
378    logMsg("Invalid filename created for $X,$Y,$Z,$Layer",2);
379    return;
380  }
381 
382  if($VersionID < 5){ // Prior to "cambridge", no blank-tile detection
383    # Don't store blank tiles
384    if($Size < 1000){
385      printf("%s -> blank, not saved\n", $OldFilename);
386      return;
387    }
388  }
389 
390  if($Size == 67){
391    # This is a request to delete existing tiles and create a "blank land" tile
392    # TODO: make an enumeration for blank land/sea
393    $SqlSnippet = sprintf("%d,%d,%d,%d,%d", $X, $Y, $Z, $Layer, 2);
394    array_push($BlankTileList, $SqlSnippet);
395    return;
396  }
397  if($Size == 69){
398    # This is a request to create a sea tile
399    $SqlSnippet = sprintf("%d,%d,%d,%d,%d", $X, $Y, $Z, $Layer, 1);
400    array_push($BlankTileList, $SqlSnippet);
401    return;
402  }
403  if($Size == 0){
404    # This is a request to delete a tile (both real and blank)
405    $SqlSnippet = sprintf("%d,%d,%d,%d,%d", $X, $Y, $Z, $Layer, -1);
406    array_push($BlankTileList, $SqlSnippet);
407    return;
408  }
409
410  if($Size < 100){
411    # TODO: WTF is this tile
412    return;
413  }
414 
415  # Remember tile details, in a form that can be added to SQL easily
416  $SqlSnippet = sprintf("%d,%d,%d,%d,%d", $X, $Y, $Z, $Layer, $Size);
417  array_push($TileList, $SqlSnippet);
418
419 
420  # Check directory exists
421  CreateDirectoryToHold($NewFilename);
422 
423  # Move the file to its new home
424  rename($OldFilename, $NewFilename);
425  printf("%s -> %s\n", $OldFilename, $NewFilename);
426
427  # Make world-writeable, so that it's easier to move files using shell
428  # (note: anyone with shell account on dev can access these files anyway
429  #  through their website running as htuser)
430  chmod($NewFilename, 0666);
431}
432
433function CreateDirectoryToHold($Filename){
434  # Get the components of the directory structure
435  $Parts = explode("/", $Filename);
436 
437  # Remove the last element, which is the filename
438  array_pop($Parts);
439  # and the first element, which is a zero-length string
440  array_shift($Parts);
441 
442  $AssumedToExist = 4; // var/www/ojw/Tiles don't get created
443 
444  # For each part...
445  $Dir = "";
446  $Count = 0;
447  foreach($Parts as $Part){
448    $Dir .= "/".$Part;
449    $Count++;
450   
451    if($Count > $AssumedToExist){ 
452      CreateDir($Dir);
453    }
454  }
455}
456
457function CreateDir($Dir){
458  if(file_exists($Dir)){
459    #printf("Directory exists: %s\n", $Dir);
460    return(1);
461  }
462 
463  if(!mkdir($Dir, 0777)){
464    printf("Failed to create directory %s\n", $Dir);
465    return(0);
466  }
467 
468  #printf("Creating dir \"%s\"\n", $Dir);
469  return(1);
470}
471
472#----------------------------------------------------------------------
473# Chooses the name for a temporary directory
474#
475# * everything under one temp dir
476# * md5 gives alphanumeric filename
477# * uniqid means multiple threads are unlikely to conflict
478#----------------------------------------------------------------------
479function TempDir(){
480  return(sprintf("/home/ojw/tiles-ojw/temp/%s", md5(uniqid(rand(), 1))));
481}
482
483?>
Note: See TracBrowser for help on using the repository browser.