diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/fix_some_assets.pl | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/scripts/fix_some_assets.pl b/scripts/fix_some_assets.pl new file mode 100755 index 0000000..08fdcb2 --- /dev/null +++ b/scripts/fix_some_assets.pl | |||
@@ -0,0 +1,227 @@ | |||
1 | #!/usr/bin/perl | ||
2 | use strict; | ||
3 | use warnings; | ||
4 | |||
5 | |||
6 | # Took almost two and a half hours on IG. | ||
7 | |||
8 | |||
9 | =pod | ||
10 | # 7/30/2015 3:58PM | ||
11 | Rev B - cleanup | ||
12 | Rev C - added in missing UPDATE | ||
13 | |||
14 | quthor Jeff Kelley <opensim@pescadoo.net> | ||
15 | mods by Fred Beckhusen <fred@mitsi.com> aka Ferd Frederix | ||
16 | |||
17 | This version has minimum Perl module dependencies. | ||
18 | |||
19 | This script connects to an Opensim asset database and scans and fixed bug Mantis-7514 | ||
20 | The files that are fixed have multiple xmlns : in it, such as xmlns:xmlns: | ||
21 | |||
22 | http://opensimulator.org/mantis/view.php?id=7514 | ||
23 | |||
24 | ############################################################################ | ||
25 | # DO A BACKUP BEFORE YOU ATTEMPT THIS. DO A DRY RUN WITH UPDATE SET TO ZERO # | ||
26 | ############################################################################# | ||
27 | |||
28 | Set variable UPDATE to a 1 to write updates to the db. A 0 will show you what it would change and also save the bad | ||
29 | data to disk in a folder name 'corrupt' for you to examine. | ||
30 | |||
31 | For safety, we will never update a record unless the UPDATE flag is on. | ||
32 | |||
33 | ############################################################# | ||
34 | # DO NOT RUN THIS UNTIL YOU SET THE CONSTANTS BELOW # | ||
35 | ############################################################# | ||
36 | |||
37 | =cut | ||
38 | |||
39 | # these are typically found in StandaloneCommon.ini or GridCommon.ini | ||
40 | |||
41 | my $username = '*****'; | ||
42 | my $password = '*****'; | ||
43 | my $hostname = '*****'; # probably you use localhost for standalone, but it could be an IP address or a DNS name for a asset server. | ||
44 | my $robustDB = '*****'; # your MySQl DB name | ||
45 | |||
46 | # for safety, we will never update a record unless the UPDATE flag is on. | ||
47 | use constant LISTALL => 0; # show all assets as they are scanned | ||
48 | use constant UPDATE => 0; # set to 1 if you want to actually update the database | ||
49 | use constant MAX => 16000000; # max size of blob from your MySQL ini - see My.ini : max_allowed_packet = 16M, You may need to set this to 64 MB if you have large assets. My largest was over 10 MB..jeepers! | ||
50 | use constant GETMAX => 1; # set to 1 to query MAX object size (see above line) automatically - much slower to start, but less memory is used overall both at the server and at the client | ||
51 | use constant MANTIS7514 => '/tmp/fix_some_assets.ini'; # path to a temp file to keep track of each run. Delete the file to start at the beginning | ||
52 | |||
53 | # all these modules should already be in core. If not, run "cpan name_of_module', or just go get Strawberry Perl. | ||
54 | use v5.10; # so we can say, and not need to add a newline | ||
55 | use DBI; # database I/O | ||
56 | $|=1; #no buffering STDIO | ||
57 | |||
58 | my $counter = 0; # count of xmlmns corruption | ||
59 | my $havedone = 0; | ||
60 | my $OddCounter = 0; # other corrupt data | ||
61 | |||
62 | mkdir 'corrupt'; # save the data in this folder | ||
63 | |||
64 | my $dbh = DBI->connect( | ||
65 | "dbi:mysql:dbname=$robustDB;host=$hostname", | ||
66 | $username, $password,{RaiseError => 1 , LongReadLen => MAX, LongTruncOk => 0,AutoCommit => 1}, | ||
67 | ) or die $DBI::errstr; | ||
68 | |||
69 | # Get the largest blob in table data in the DB and set our size of memory buffers to that. | ||
70 | if (GETMAX) { | ||
71 | my $max_len = GetLenData (); | ||
72 | say "Largest object is $max_len bytes"; | ||
73 | $dbh->{LongReadLen} = $max_len; | ||
74 | } | ||
75 | |||
76 | my $todo; | ||
77 | my $objectsList = GetAssetList(); # get a hash of all objects | ||
78 | $todo = scalar @$objectsList if ref $objectsList; # count them | ||
79 | say "Found $todo objects"; | ||
80 | |||
81 | for (@$objectsList) { | ||
82 | my $obj_uuid = $_->{id}; | ||
83 | my $obj_name = $_->{name}; | ||
84 | my $obj_time = $_->{create_time}; | ||
85 | |||
86 | $havedone++; # keep track of how many done | ||
87 | |||
88 | my $obj_data = GetAssetData ($obj_uuid); # grab the blob | ||
89 | |||
90 | ValidateXML ($obj_data,$obj_uuid, $obj_name); # repair it and check it | ||
91 | |||
92 | #save where we are at so we can be stopped and run again some other rainy day | ||
93 | open(my $in_file, ">",MANTIS7514) || die 'Cannot save the last file date'; | ||
94 | print $in_file $obj_time; | ||
95 | close $in_file; | ||
96 | } | ||
97 | |||
98 | say "Found $counter corrupt xmlns objects"; | ||
99 | say "Found $OddCounter other possibly corrupted objects"; | ||
100 | say "Done!"; | ||
101 | |||
102 | |||
103 | sub GetAssetList { | ||
104 | |||
105 | my $answref; | ||
106 | |||
107 | my $time = 0; | ||
108 | # read the last run date from a file | ||
109 | if (open(my $in_file, "<",MANTIS7514)) | ||
110 | { | ||
111 | $time = <$in_file> || 0; | ||
112 | close $in_file; | ||
113 | } | ||
114 | my $query = "SELECT id,name, create_time FROM $robustDB.assets WHERE assetType=6 and create_time > $time order by create_time asc;"; | ||
115 | $answref = $dbh->selectall_arrayref ($query, { Slice => {} }); | ||
116 | |||
117 | return $answref; | ||
118 | } | ||
119 | |||
120 | sub GetAssetData { | ||
121 | my $uuid = shift; | ||
122 | my $query = "SELECT data FROM $robustDB.assets WHERE id='$uuid';"; | ||
123 | my $answr = $dbh->selectall_arrayref ($query, { Slice => {} }); | ||
124 | return @$answr[0]->{data}; | ||
125 | } | ||
126 | |||
127 | sub UpdateData { | ||
128 | return unless UPDATE; | ||
129 | my $uuid = shift; | ||
130 | my $data = shift; | ||
131 | |||
132 | my $sth = $dbh->prepare("UPDATE $robustDB.assets set data = ? WHERE id = ?;"); | ||
133 | $sth->execute($data, $uuid) or die $DBI::errstr;; | ||
134 | } | ||
135 | sub GetLenData { | ||
136 | my $len = $dbh->selectrow_array("SELECT MAX(OCTET_LENGTH(data)) FROM $robustDB.assets WHERE assetType=6;"); | ||
137 | } | ||
138 | |||
139 | |||
140 | sub ValidateXML { | ||
141 | my $data = shift; | ||
142 | my $obj_uuid = shift; | ||
143 | my $obj_name = shift || ''; | ||
144 | |||
145 | #### FORCES AN ERROR FOR DEBUG $data =~ s/xmlns:/xmlns:xmlns:/g; # the fix is in !!!!!!!!! | ||
146 | |||
147 | # Test for repeated xmlns, clever RegEx by Jeff Kelley. | ||
148 | my $corrupt = ($data =~ m/((xmlns:){2,}+)/g); | ||
149 | my $err = $1 || ''; | ||
150 | |||
151 | # show them where we are at, if enabled or corrupted. | ||
152 | printf "%d/%d | %s | %s | %s\n", $havedone, $todo, $corrupt ? 'Bad ' : 'Ok ', $obj_uuid, $obj_name if ($corrupt || LISTALL); | ||
153 | |||
154 | if ($corrupt) | ||
155 | { | ||
156 | $counter++; | ||
157 | |||
158 | print ("Found $err\n", 'red') ; | ||
159 | my $original = $data; # so we can save it to disk later, if need be. | ||
160 | |||
161 | $data =~ s/(xmlns:){2,}+/xmlns:/g; # the fix is in | ||
162 | |||
163 | UpdateData($obj_uuid,$data) if UPDATE; # we update if we are enabled and had an error Rev C | ||
164 | |||
165 | save({ | ||
166 | name => "$obj_uuid-$obj_name-before.txt", | ||
167 | uuid => $obj_uuid, | ||
168 | corrupt => $err, | ||
169 | data => $original, | ||
170 | type => 0, | ||
171 | }); | ||
172 | |||
173 | save({ | ||
174 | name => "$obj_uuid-$obj_name-after.txt", | ||
175 | uuid => $obj_uuid, | ||
176 | corrupt => $err, | ||
177 | data => $data, | ||
178 | type => 0, | ||
179 | }); | ||
180 | } | ||
181 | |||
182 | return $corrupt; # in case the caller wants to know | ||
183 | } | ||
184 | |||
185 | sub save { | ||
186 | my $c = shift; | ||
187 | |||
188 | $c->{name} =~ s/[^A-Za-z0-9\-\.]//g; # make a safe file name | ||
189 | |||
190 | # may have to force an UTF-16LE or UTF-BE encoding here but my tests show the data is always UTF-8 or ASCII. | ||
191 | |||
192 | if (open(my $out_file, "> :encoding(UTF-8)",'corrupt/' . $c->{name})) { | ||
193 | binmode ($out_file,":encoding(UTF-8)"); | ||
194 | |||
195 | $c->{data} =~ s/encoding="utf-8"/encoding="utf-16"/; # not right, but that's they way they coded the XML in the DB. | ||
196 | |||
197 | print $out_file $c->{data}; | ||
198 | print $out_file "\0"; # zero termination is in the OAR, lets put it back | ||
199 | } else { | ||
200 | say "Failed to open file"; | ||
201 | die; | ||
202 | } | ||
203 | |||
204 | } | ||
205 | |||
206 | __END__ | ||
207 | |||
208 | BSD License: | ||
209 | Copyright (c) 2015 | ||
210 | Redistribution and use in source and binary forms, with or without | ||
211 | modification, are permitted provided that the following conditions are met: | ||
212 | * Redistributions of source code must retain the above copyright notice, this | ||
213 | list of conditions and the following disclaimer. | ||
214 | * Redistributions in binary form must reproduce the above copyright notice, | ||
215 | this list of conditions and the following disclaimer in the documentation | ||
216 | and/or other materials provided with the distribution. | ||
217 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
218 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
219 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
220 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
221 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
222 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
223 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
224 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
225 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
226 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
227 | Space here is intentionally left blank for note taking | ||