|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* This file handles the uploading and creation of attachments |
|
5
|
|
|
* as well as the auto management of the attachment directories. |
|
6
|
|
|
* Note to enhance documentation later: |
|
7
|
|
|
* attachment_type = 3 is a thumbnail, etc. |
|
8
|
|
|
* |
|
9
|
|
|
* @name ElkArte Forum |
|
10
|
|
|
* @copyright ElkArte Forum contributors |
|
11
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause |
|
12
|
|
|
* |
|
13
|
|
|
* This file contains code covered by: |
|
14
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
|
15
|
|
|
* license: BSD, See included LICENSE.TXT for terms and conditions. |
|
16
|
|
|
* |
|
17
|
|
|
* @version 1.1.7 |
|
18
|
|
|
* |
|
19
|
|
|
*/ |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* Check and create a directory automatically. |
|
23
|
|
|
* |
|
24
|
|
|
* @package Attachments |
|
25
|
|
|
*/ |
|
26
|
|
|
function automanage_attachments_check_directory() |
|
27
|
|
|
{ |
|
28
|
|
|
global $modSettings, $context; |
|
29
|
|
|
|
|
30
|
|
|
// Not pretty, but since we don't want folders created for every post. |
|
31
|
|
|
// It'll do unless a better solution can be found. |
|
32
|
|
|
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'admin') |
|
33
|
|
|
$doit = true; |
|
34
|
|
|
elseif (empty($modSettings['automanage_attachments'])) |
|
35
|
|
|
return; |
|
36
|
|
|
elseif (!isset($_FILES)) |
|
37
|
|
|
return; |
|
38
|
|
|
elseif (isset($_FILES['attachment'])) |
|
39
|
|
|
{ |
|
40
|
|
|
foreach ($_FILES['attachment']['tmp_name'] as $dummy) |
|
41
|
|
|
{ |
|
42
|
|
|
if (!empty($dummy)) |
|
43
|
|
|
{ |
|
44
|
|
|
$doit = true; |
|
45
|
|
|
break; |
|
46
|
|
|
} |
|
47
|
|
|
} |
|
48
|
|
|
} |
|
49
|
|
|
|
|
50
|
|
|
if (!isset($doit)) |
|
51
|
|
|
return; |
|
52
|
|
|
|
|
53
|
|
|
// Get our date and random numbers for the directory choices |
|
54
|
|
|
$year = date('Y'); |
|
55
|
|
|
$month = date('m'); |
|
56
|
|
|
|
|
57
|
|
|
$rand = md5(mt_rand()); |
|
58
|
|
|
$rand1 = $rand[1]; |
|
59
|
|
|
$rand = $rand[0]; |
|
60
|
|
|
|
|
61
|
|
|
if (!empty($modSettings['attachment_basedirectories']) && !empty($modSettings['use_subdirectories_for_attachments'])) |
|
62
|
|
|
{ |
|
63
|
|
|
if (!is_array($modSettings['attachment_basedirectories'])) |
|
64
|
|
|
$modSettings['attachment_basedirectories'] = Util::unserialize($modSettings['attachment_basedirectories']); |
|
65
|
|
|
|
|
66
|
|
|
$base_dir = array_search($modSettings['basedirectory_for_attachments'], $modSettings['attachment_basedirectories']); |
|
67
|
|
|
} |
|
68
|
|
|
else |
|
69
|
|
|
$base_dir = 0; |
|
70
|
|
|
|
|
71
|
|
|
if ($modSettings['automanage_attachments'] == 1) |
|
72
|
|
|
{ |
|
73
|
|
|
if (!isset($modSettings['last_attachments_directory'])) |
|
74
|
|
|
$modSettings['last_attachments_directory'] = array(); |
|
75
|
|
|
if (!is_array($modSettings['last_attachments_directory'])) |
|
76
|
|
|
$modSettings['last_attachments_directory'] = Util::unserialize($modSettings['last_attachments_directory']); |
|
77
|
|
|
if (!isset($modSettings['last_attachments_directory'][$base_dir])) |
|
78
|
|
|
$modSettings['last_attachments_directory'][$base_dir] = 0; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
$basedirectory = (!empty($modSettings['use_subdirectories_for_attachments']) ? ($modSettings['basedirectory_for_attachments']) : BOARDDIR); |
|
82
|
|
|
|
|
83
|
|
|
// Just to be sure: I don't want directory separators at the end |
|
84
|
|
|
$sep = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? '\/' : DIRECTORY_SEPARATOR; |
|
85
|
|
|
$basedirectory = rtrim($basedirectory, $sep); |
|
86
|
|
|
|
|
87
|
|
|
switch ($modSettings['automanage_attachments']) |
|
88
|
|
|
{ |
|
89
|
|
|
case 1: |
|
90
|
|
|
$updir = $basedirectory . DIRECTORY_SEPARATOR . 'attachments_' . (isset($modSettings['last_attachments_directory'][$base_dir]) ? $modSettings['last_attachments_directory'][$base_dir] : 0); |
|
91
|
|
|
break; |
|
92
|
|
|
case 2: |
|
93
|
|
|
$updir = $basedirectory . DIRECTORY_SEPARATOR . $year; |
|
94
|
|
|
break; |
|
95
|
|
|
case 3: |
|
96
|
|
|
$updir = $basedirectory . DIRECTORY_SEPARATOR . $year . DIRECTORY_SEPARATOR . $month; |
|
97
|
|
|
break; |
|
98
|
|
|
case 4: |
|
99
|
|
|
$updir = $basedirectory . DIRECTORY_SEPARATOR . (empty($modSettings['use_subdirectories_for_attachments']) ? 'attachments-' : 'random_') . $rand; |
|
100
|
|
|
break; |
|
101
|
|
|
case 5: |
|
102
|
|
|
$updir = $basedirectory . DIRECTORY_SEPARATOR . (empty($modSettings['use_subdirectories_for_attachments']) ? 'attachments-' : 'random_') . $rand . DIRECTORY_SEPARATOR . $rand1; |
|
103
|
|
|
break; |
|
104
|
|
|
default: |
|
105
|
|
|
$updir = ''; |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
if (!is_array($modSettings['attachmentUploadDir'])) |
|
109
|
|
|
$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']); |
|
110
|
|
|
|
|
111
|
|
|
if (!in_array($updir, $modSettings['attachmentUploadDir']) && !empty($updir)) |
|
112
|
|
|
$outputCreation = automanage_attachments_create_directory($updir); |
|
113
|
|
|
elseif (in_array($updir, $modSettings['attachmentUploadDir'])) |
|
114
|
|
|
$outputCreation = true; |
|
115
|
|
|
|
|
116
|
|
|
if ($outputCreation) |
|
|
|
|
|
|
117
|
|
|
{ |
|
118
|
|
|
$modSettings['currentAttachmentUploadDir'] = array_search($updir, $modSettings['attachmentUploadDir']); |
|
119
|
|
|
$context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; |
|
120
|
|
|
|
|
121
|
|
|
updateSettings(array( |
|
122
|
|
|
'currentAttachmentUploadDir' => $modSettings['currentAttachmentUploadDir'], |
|
123
|
|
|
)); |
|
124
|
|
|
} |
|
125
|
|
|
|
|
126
|
|
|
return $outputCreation; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
/** |
|
130
|
|
|
* Creates a directory as defined by the admin attach options |
|
131
|
|
|
* |
|
132
|
|
|
* What it does: |
|
133
|
|
|
* |
|
134
|
|
|
* - Attempts to make the directory writable |
|
135
|
|
|
* - Places an .htaccess in new directories for security |
|
136
|
|
|
* |
|
137
|
|
|
* @package Attachments |
|
138
|
|
|
* @param string $updir |
|
139
|
|
|
*/ |
|
140
|
|
|
function automanage_attachments_create_directory($updir) |
|
141
|
|
|
{ |
|
142
|
|
|
global $modSettings, $context; |
|
143
|
|
|
|
|
144
|
|
|
$tree = get_directory_tree_elements($updir); |
|
145
|
|
|
$count = count($tree); |
|
|
|
|
|
|
146
|
|
|
|
|
147
|
|
|
$directory = !empty($tree) ? attachments_init_dir($tree, $count) : false; |
|
148
|
|
|
if ($directory === false) |
|
149
|
|
|
{ |
|
150
|
|
|
// Maybe it's just the folder name |
|
151
|
|
|
$tree = get_directory_tree_elements(BOARDDIR . DIRECTORY_SEPARATOR . $updir); |
|
152
|
|
|
$count = count($tree); |
|
153
|
|
|
|
|
154
|
|
|
$directory = !empty($tree) ? attachments_init_dir($tree, $count) : false; |
|
155
|
|
|
if ($directory === false) |
|
156
|
|
|
return false; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
$directory .= DIRECTORY_SEPARATOR . array_shift($tree); |
|
160
|
|
|
|
|
161
|
|
|
while (!@is_dir($directory) || $count != -1) |
|
162
|
|
|
{ |
|
163
|
|
|
if (!@is_dir($directory)) |
|
164
|
|
|
{ |
|
165
|
|
|
if (!@mkdir($directory, 0755)) |
|
166
|
|
|
{ |
|
167
|
|
|
$context['dir_creation_error'] = 'attachments_no_create'; |
|
168
|
|
|
return false; |
|
169
|
|
|
} |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
$directory .= DIRECTORY_SEPARATOR . array_shift($tree); |
|
173
|
|
|
$count--; |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
|
|
// Try to make it writable |
|
177
|
|
|
if (!is_writable($directory)) |
|
178
|
|
|
{ |
|
179
|
|
|
chmod($directory, 0755); |
|
180
|
|
|
if (!is_writable($directory)) |
|
181
|
|
|
{ |
|
182
|
|
|
chmod($directory, 0775); |
|
183
|
|
|
if (!is_writable($directory)) |
|
184
|
|
|
{ |
|
185
|
|
|
chmod($directory, 0777); |
|
186
|
|
|
if (!is_writable($directory)) |
|
187
|
|
|
{ |
|
188
|
|
|
$context['dir_creation_error'] = 'attachments_no_write'; |
|
189
|
|
|
return false; |
|
190
|
|
|
} |
|
191
|
|
|
} |
|
192
|
|
|
} |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
// Everything seems fine...let's create the .htaccess |
|
196
|
|
|
if (!file_exists($directory . DIRECTORY_SEPARATOR . '.htaccess')) |
|
197
|
|
|
secureDirectory($updir, true); |
|
198
|
|
|
|
|
199
|
|
|
$sep = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? '\/' : DIRECTORY_SEPARATOR; |
|
200
|
|
|
$updir = rtrim($updir, $sep); |
|
201
|
|
|
|
|
202
|
|
|
// Only update if it's a new directory |
|
203
|
|
|
if (!in_array($updir, $modSettings['attachmentUploadDir'])) |
|
204
|
|
|
{ |
|
205
|
|
|
$modSettings['currentAttachmentUploadDir'] = max(array_keys($modSettings['attachmentUploadDir'])) + 1; |
|
206
|
|
|
$modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']] = $updir; |
|
207
|
|
|
|
|
208
|
|
|
updateSettings(array( |
|
209
|
|
|
'attachmentUploadDir' => serialize($modSettings['attachmentUploadDir']), |
|
210
|
|
|
'currentAttachmentUploadDir' => $modSettings['currentAttachmentUploadDir'], |
|
211
|
|
|
), true); |
|
212
|
|
|
$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']); |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
$context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; |
|
216
|
|
|
return true; |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
/** |
|
220
|
|
|
* Determines the current base directory and attachment directory |
|
221
|
|
|
* |
|
222
|
|
|
* What it does: |
|
223
|
|
|
* |
|
224
|
|
|
* - Increments the above directory to the next available slot |
|
225
|
|
|
* - Uses automanage_attachments_create_directory to create the incremental directory |
|
226
|
|
|
* |
|
227
|
|
|
* @package Attachments |
|
228
|
|
|
*/ |
|
229
|
|
|
function automanage_attachments_by_space() |
|
230
|
|
|
{ |
|
231
|
|
|
global $modSettings; |
|
232
|
|
|
|
|
233
|
|
|
if (!isset($modSettings['automanage_attachments']) || (!empty($modSettings['automanage_attachments']) && $modSettings['automanage_attachments'] != 1)) |
|
234
|
|
|
return; |
|
235
|
|
|
|
|
236
|
|
|
$basedirectory = (!empty($modSettings['use_subdirectories_for_attachments']) ? ($modSettings['basedirectory_for_attachments']) : BOARDDIR); |
|
237
|
|
|
|
|
238
|
|
|
// Just to be sure: I don't want directory separators at the end |
|
239
|
|
|
$sep = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? '\/' : DIRECTORY_SEPARATOR; |
|
240
|
|
|
$basedirectory = rtrim($basedirectory, $sep); |
|
241
|
|
|
|
|
242
|
|
|
// Get the current base directory |
|
243
|
|
|
if (!empty($modSettings['use_subdirectories_for_attachments']) && !empty($modSettings['attachment_basedirectories'])) |
|
244
|
|
|
{ |
|
245
|
|
|
$base_dir = array_search($modSettings['basedirectory_for_attachments'], $modSettings['attachment_basedirectories']); |
|
246
|
|
|
$base_dir = !empty($modSettings['automanage_attachments']) ? $base_dir : 0; |
|
247
|
|
|
} |
|
248
|
|
|
else |
|
249
|
|
|
$base_dir = 0; |
|
250
|
|
|
|
|
251
|
|
|
// Get the last attachment directory for that base directory |
|
252
|
|
|
if (empty($modSettings['last_attachments_directory'][$base_dir])) |
|
253
|
|
|
$modSettings['last_attachments_directory'][$base_dir] = 0; |
|
254
|
|
|
|
|
255
|
|
|
// And increment it. |
|
256
|
|
|
$modSettings['last_attachments_directory'][$base_dir]++; |
|
257
|
|
|
|
|
258
|
|
|
$updir = $basedirectory . DIRECTORY_SEPARATOR . 'attachments_' . $modSettings['last_attachments_directory'][$base_dir]; |
|
259
|
|
|
|
|
260
|
|
|
// make sure it exists and is writable |
|
261
|
|
|
if (automanage_attachments_create_directory($updir)) |
|
262
|
|
|
{ |
|
263
|
|
|
$modSettings['currentAttachmentUploadDir'] = array_search($updir, $modSettings['attachmentUploadDir']); |
|
264
|
|
|
updateSettings(array( |
|
265
|
|
|
'last_attachments_directory' => serialize($modSettings['last_attachments_directory']), |
|
266
|
|
|
'currentAttachmentUploadDir' => $modSettings['currentAttachmentUploadDir'], |
|
267
|
|
|
)); |
|
268
|
|
|
$modSettings['last_attachments_directory'] = Util::unserialize($modSettings['last_attachments_directory']); |
|
269
|
|
|
|
|
270
|
|
|
return true; |
|
271
|
|
|
} |
|
272
|
|
|
else |
|
273
|
|
|
return false; |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
/** |
|
277
|
|
|
* Finds the current directory tree for the supplied base directory |
|
278
|
|
|
* |
|
279
|
|
|
* @package Attachments |
|
280
|
|
|
* @param string $directory |
|
281
|
|
|
* @return string[]|boolean on fail else array of directory names |
|
282
|
|
|
*/ |
|
283
|
|
|
function get_directory_tree_elements($directory) |
|
284
|
|
|
{ |
|
285
|
|
|
/* |
|
286
|
|
|
In Windows server both \ and / can be used as directory separators in paths |
|
287
|
|
|
In Linux (and presumably *nix) servers \ can be part of the name |
|
288
|
|
|
So for this reasons: |
|
289
|
|
|
* in Windows we need to explode for both \ and / |
|
290
|
|
|
* while in linux should be safe to explode only for / (aka DIRECTORY_SEPARATOR) |
|
291
|
|
|
*/ |
|
292
|
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') |
|
293
|
|
|
$tree = preg_split('#[\\\/]#', $directory); |
|
294
|
|
|
else |
|
295
|
|
|
{ |
|
296
|
|
|
if (substr($directory, 0, 1) != DIRECTORY_SEPARATOR) |
|
297
|
|
|
return false; |
|
298
|
|
|
|
|
299
|
|
|
$tree = explode(DIRECTORY_SEPARATOR, trim($directory, DIRECTORY_SEPARATOR)); |
|
300
|
|
|
} |
|
301
|
|
|
|
|
302
|
|
|
return $tree; |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* Helper function for automanage_attachments_create_directory |
|
307
|
|
|
* |
|
308
|
|
|
* What it does: |
|
309
|
|
|
* |
|
310
|
|
|
* - Gets the directory w/o drive letter for windows |
|
311
|
|
|
* |
|
312
|
|
|
* @package Attachments |
|
313
|
|
|
* @param string[] $tree |
|
314
|
|
|
* @param int $count |
|
315
|
|
|
*/ |
|
316
|
|
|
function attachments_init_dir(&$tree, &$count) |
|
317
|
|
|
{ |
|
318
|
|
|
$directory = ''; |
|
319
|
|
|
|
|
320
|
|
|
// If on Windows servers the first part of the path is the drive (e.g. "C:") |
|
321
|
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') |
|
322
|
|
|
{ |
|
323
|
|
|
// Better be sure that the first part of the path is actually a drive letter... |
|
324
|
|
|
// ...even if, I should check this in the admin page...isn't it? |
|
325
|
|
|
// ...NHAAA Let's leave space for users' complains! :P |
|
326
|
|
|
if (preg_match('/^[a-z]:$/i', $tree[0])) |
|
327
|
|
|
$directory = array_shift($tree); |
|
328
|
|
|
else |
|
329
|
|
|
return false; |
|
330
|
|
|
|
|
331
|
|
|
$count--; |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
return $directory; |
|
335
|
|
|
} |
|
336
|
|
|
|
|
337
|
|
|
/** |
|
338
|
|
|
* Handles the actual saving of attachments to a directory. |
|
339
|
|
|
* |
|
340
|
|
|
* What it does: |
|
341
|
|
|
* |
|
342
|
|
|
* - Loops through $_FILES['attachment'] array and saves each file to the current attachments folder. |
|
343
|
|
|
* - Validates the save location actually exists. |
|
344
|
|
|
* |
|
345
|
|
|
* @package Attachments |
|
346
|
|
|
* @param int|null $id_msg = null or id of the message with attachments, if any. |
|
347
|
|
|
* If null, this is an upload in progress for a new post. |
|
348
|
|
|
* @throws Elk_Exception |
|
349
|
|
|
*/ |
|
350
|
|
|
function processAttachments($id_msg = null) |
|
351
|
|
|
{ |
|
352
|
|
|
global $context, $modSettings, $txt, $user_info, $ignore_temp, $topic, $board; |
|
353
|
|
|
|
|
354
|
|
|
$attach_errors = ElkArte\Errors\AttachmentErrorContext::context(); |
|
355
|
|
|
$added_initial_error = false; |
|
356
|
|
|
|
|
357
|
|
|
// Make sure we're uploading to the right place. |
|
358
|
|
|
if (!empty($modSettings['automanage_attachments'])) |
|
359
|
|
|
automanage_attachments_check_directory(); |
|
360
|
|
|
|
|
361
|
|
|
if (!is_array($modSettings['attachmentUploadDir'])) |
|
362
|
|
|
{ |
|
363
|
|
|
$attachmentUploadDir = Util::unserialize($modSettings['attachmentUploadDir']); |
|
364
|
|
|
if (!empty($attachmentUploadDir)) |
|
365
|
|
|
{ |
|
366
|
|
|
$modSettings['attachmentUploadDir'] = $attachmentUploadDir; |
|
367
|
|
|
} |
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
$context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']]; |
|
371
|
|
|
|
|
372
|
|
|
// Is the attachments folder actually there? |
|
373
|
|
|
if (!empty($context['dir_creation_error'])) |
|
374
|
|
|
$initial_error = $context['dir_creation_error']; |
|
375
|
|
|
elseif (!is_dir($context['attach_dir'])) |
|
376
|
|
|
{ |
|
377
|
|
|
$initial_error = 'attach_folder_warning'; |
|
378
|
|
|
Errors::instance()->log_error(sprintf($txt['attach_folder_admin_warning'], $context['attach_dir']), 'critical'); |
|
379
|
|
|
} |
|
380
|
|
|
|
|
381
|
|
|
if (!isset($initial_error) && !isset($context['attachments']['quantity'])) |
|
382
|
|
|
{ |
|
383
|
|
|
// If this isn't a new post, check the current attachments. |
|
384
|
|
|
if (!empty($id_msg)) |
|
385
|
|
|
list ($context['attachments']['quantity'], $context['attachments']['total_size']) = attachmentsSizeForMessage($id_msg); |
|
386
|
|
|
else |
|
387
|
|
|
{ |
|
388
|
|
|
$context['attachments']['quantity'] = 0; |
|
389
|
|
|
$context['attachments']['total_size'] = 0; |
|
390
|
|
|
} |
|
391
|
|
|
} |
|
392
|
|
|
|
|
393
|
|
|
// Hmm. There are still files in session. |
|
394
|
|
|
$ignore_temp = false; |
|
395
|
|
|
if (!empty($_SESSION['temp_attachments']['post']['files']) && count($_SESSION['temp_attachments']) > 1) |
|
396
|
|
|
{ |
|
397
|
|
|
// Let's try to keep them. But... |
|
398
|
|
|
$ignore_temp = true; |
|
399
|
|
|
|
|
400
|
|
|
// If new files are being added. We can't ignore those |
|
401
|
|
|
if (!empty($_FILES['attachment']['tmp_name'])) |
|
402
|
|
|
{ |
|
403
|
|
|
foreach ($_FILES['attachment']['tmp_name'] as $dummy) |
|
404
|
|
|
{ |
|
405
|
|
|
if (!empty($dummy)) |
|
406
|
|
|
{ |
|
407
|
|
|
$ignore_temp = false; |
|
408
|
|
|
break; |
|
409
|
|
|
} |
|
410
|
|
|
} |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
// Need to make space for the new files. So, bye bye. |
|
414
|
|
|
if (!$ignore_temp) |
|
415
|
|
|
{ |
|
416
|
|
|
foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) |
|
417
|
|
|
{ |
|
418
|
|
|
if (strpos($attachID, 'post_tmp_' . $user_info['id'] . '_') !== false) |
|
419
|
|
|
@unlink($attachment['tmp_name']); |
|
|
|
|
|
|
420
|
|
|
} |
|
421
|
|
|
|
|
422
|
|
|
$attach_errors->activate()->addError('temp_attachments_flushed'); |
|
423
|
|
|
$_SESSION['temp_attachments'] = array(); |
|
424
|
|
|
} |
|
425
|
|
|
} |
|
426
|
|
|
|
|
427
|
|
|
if (!isset($_FILES['attachment']['name'])) |
|
428
|
|
|
$_FILES['attachment']['tmp_name'] = array(); |
|
429
|
|
|
|
|
430
|
|
|
if (!isset($_SESSION['temp_attachments'])) |
|
431
|
|
|
$_SESSION['temp_attachments'] = array(); |
|
432
|
|
|
|
|
433
|
|
|
// Remember where we are at. If it's anywhere at all. |
|
434
|
|
|
if (!$ignore_temp) |
|
435
|
|
|
$_SESSION['temp_attachments']['post'] = array( |
|
436
|
|
|
'msg' => !empty($id_msg) ? $id_msg : 0, |
|
437
|
|
|
'last_msg' => !empty($_REQUEST['last_msg']) ? $_REQUEST['last_msg'] : 0, |
|
438
|
|
|
'topic' => !empty($topic) ? $topic : 0, |
|
439
|
|
|
'board' => !empty($board) ? $board : 0, |
|
440
|
|
|
); |
|
441
|
|
|
|
|
442
|
|
|
// Loop through $_FILES['attachment'] array and move each file to the current attachments folder. |
|
443
|
|
|
foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) |
|
444
|
|
|
{ |
|
445
|
|
|
if ($_FILES['attachment']['name'][$n] == '') |
|
446
|
|
|
continue; |
|
447
|
|
|
|
|
448
|
|
|
// If we have an initial error, lets just display it. |
|
449
|
|
|
if (!empty($initial_error) && $added_initial_error === false) |
|
450
|
|
|
{ |
|
451
|
|
|
$added_initial_error = true; |
|
452
|
|
|
$_SESSION['temp_attachments']['initial_error'] = $initial_error; |
|
453
|
|
|
|
|
454
|
|
|
// This is a generic error |
|
455
|
|
|
$attach_errors->activate(); |
|
456
|
|
|
$attach_errors->addError('attach_no_upload'); |
|
457
|
|
|
// @todo This is likely the result of some refactoring, verify when $attachment is not set and why |
|
458
|
|
|
if (isset($attachment)) |
|
459
|
|
|
{ |
|
460
|
|
|
$attach_errors->addError(is_array($attachment) ? array($attachment[0], $attachment[1]) : $attachment); |
|
|
|
|
|
|
461
|
|
|
} |
|
462
|
|
|
|
|
463
|
|
|
// And delete the files 'cos they ain't going nowhere. |
|
464
|
|
|
foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) |
|
|
|
|
|
|
465
|
|
|
{ |
|
466
|
|
|
if (file_exists($_FILES['attachment']['tmp_name'][$n])) |
|
467
|
|
|
unlink($_FILES['attachment']['tmp_name'][$n]); |
|
468
|
|
|
} |
|
469
|
|
|
|
|
470
|
|
|
$_FILES['attachment']['tmp_name'] = array(); |
|
471
|
|
|
} |
|
472
|
|
|
|
|
473
|
|
|
// First, let's first check for PHP upload errors. |
|
474
|
|
|
$errors = attachmentUploadChecks($n); |
|
475
|
|
|
|
|
476
|
|
|
// Set the names and destination for this file |
|
477
|
|
|
$attachID = 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand()); |
|
478
|
|
|
$destName = $context['attach_dir'] . '/' . $attachID; |
|
479
|
|
|
|
|
480
|
|
|
// If we are error free, Try to move and rename the file before doing more checks on it. |
|
481
|
|
|
if (empty($errors)) |
|
482
|
|
|
{ |
|
483
|
|
|
$_SESSION['temp_attachments'][$attachID] = array( |
|
484
|
|
|
'name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n]), ENT_COMPAT, 'UTF-8'), |
|
485
|
|
|
'tmp_name' => $destName, |
|
486
|
|
|
'attachid' => $attachID, |
|
487
|
|
|
'public_attachid' => 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand()), |
|
488
|
|
|
'size' => $_FILES['attachment']['size'][$n], |
|
489
|
|
|
'type' => $_FILES['attachment']['type'][$n], |
|
490
|
|
|
'id_folder' => $modSettings['currentAttachmentUploadDir'], |
|
491
|
|
|
'errors' => array(), |
|
492
|
|
|
); |
|
493
|
|
|
|
|
494
|
|
|
// Move the file to the attachments folder with a temp name for now. |
|
495
|
|
|
if (@move_uploaded_file($_FILES['attachment']['tmp_name'][$n], $destName)) |
|
496
|
|
|
@chmod($destName, 0644); |
|
|
|
|
|
|
497
|
|
|
else |
|
498
|
|
|
{ |
|
499
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = 'attach_timeout'; |
|
500
|
|
|
if (file_exists($_FILES['attachment']['tmp_name'][$n])) |
|
501
|
|
|
unlink($_FILES['attachment']['tmp_name'][$n]); |
|
502
|
|
|
} |
|
503
|
|
|
} |
|
504
|
|
|
// Upload error(s) were detected, flag the error, remove the file |
|
505
|
|
|
else |
|
506
|
|
|
{ |
|
507
|
|
|
$_SESSION['temp_attachments'][$attachID] = array( |
|
508
|
|
|
'name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n]), ENT_COMPAT, 'UTF-8'), |
|
509
|
|
|
'tmp_name' => $destName, |
|
510
|
|
|
'errors' => $errors, |
|
511
|
|
|
); |
|
512
|
|
|
|
|
513
|
|
|
if (file_exists($_FILES['attachment']['tmp_name'][$n])) |
|
514
|
|
|
unlink($_FILES['attachment']['tmp_name'][$n]); |
|
515
|
|
|
} |
|
516
|
|
|
|
|
517
|
|
|
// If there were no errors to this point, we apply some additional checks |
|
518
|
|
|
if (empty($_SESSION['temp_attachments'][$attachID]['errors'])) |
|
519
|
|
|
attachmentChecks($attachID); |
|
|
|
|
|
|
520
|
|
|
|
|
521
|
|
|
// Want to correct for phonetographer photos? |
|
522
|
|
|
if (!empty($modSettings['attachment_autorotate']) && empty($_SESSION['temp_attachments'][$attachID]['errors']) && substr($_SESSION['temp_attachments'][$attachID]['type'], 0, 5) === 'image') |
|
523
|
|
|
{ |
|
524
|
|
|
autoRotateImage($_SESSION['temp_attachments'][$attachID]['tmp_name']); |
|
525
|
|
|
} |
|
526
|
|
|
|
|
527
|
|
|
// Sort out the errors for display and delete any associated files. |
|
528
|
|
|
if (!empty($_SESSION['temp_attachments'][$attachID]['errors'])) |
|
529
|
|
|
{ |
|
530
|
|
|
$attach_errors->addAttach($attachID, $_SESSION['temp_attachments'][$attachID]['name']); |
|
531
|
|
|
$log_these = array('attachments_no_create', 'attachments_no_write', 'attach_timeout', 'ran_out_of_space', 'cant_access_upload_path', 'attach_0_byte_file', 'bad_attachment'); |
|
532
|
|
|
|
|
533
|
|
|
foreach ($_SESSION['temp_attachments'][$attachID]['errors'] as $error) |
|
534
|
|
|
{ |
|
535
|
|
|
if (!is_array($error)) |
|
536
|
|
|
{ |
|
537
|
|
|
$attach_errors->addError($error); |
|
538
|
|
|
if (in_array($error, $log_these)) |
|
539
|
|
|
{ |
|
540
|
|
|
Errors::instance()->log_error($_SESSION['temp_attachments'][$attachID]['name'] . ': ' . $txt[$error], 'critical'); |
|
541
|
|
|
|
|
542
|
|
|
// For critical errors, we don't want the file or session data to persist |
|
543
|
|
|
if (file_exists($_SESSION['temp_attachments'][$attachID]['tmp_name'])) |
|
544
|
|
|
{ |
|
545
|
|
|
unlink($_SESSION['temp_attachments'][$attachID]['tmp_name']); |
|
546
|
|
|
} |
|
547
|
|
|
unset($_SESSION['temp_attachments'][$attachID]); |
|
548
|
|
|
} |
|
549
|
|
|
} |
|
550
|
|
|
else |
|
551
|
|
|
$attach_errors->addError(array($error[0], $error[1])); |
|
552
|
|
|
} |
|
553
|
|
|
} |
|
554
|
|
|
} |
|
555
|
|
|
|
|
556
|
|
|
// Mod authors, finally a hook to hang an alternate attachment upload system upon |
|
557
|
|
|
// Upload to the current attachment folder with the file name $attachID or 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand()) |
|
558
|
|
|
// Populate $_SESSION['temp_attachments'][$attachID] with the following: |
|
559
|
|
|
// name => The file name |
|
560
|
|
|
// tmp_name => Path to the temp file ($context['attach_dir'] . '/' . $attachID). |
|
561
|
|
|
// size => File size (required). |
|
562
|
|
|
// type => MIME type (optional if not available on upload). |
|
563
|
|
|
// id_folder => $modSettings['currentAttachmentUploadDir'] |
|
564
|
|
|
// errors => An array of errors (use the index of the $txt variable for that error). |
|
565
|
|
|
// Template changes can be done using "integrate_upload_template". |
|
566
|
|
|
call_integration_hook('integrate_attachment_upload'); |
|
567
|
|
|
} |
|
568
|
|
|
|
|
569
|
|
|
/** |
|
570
|
|
|
* Deletes a temporary attachment from the $_SESSION (and the filesystem) |
|
571
|
|
|
* |
|
572
|
|
|
* @package Attachments |
|
573
|
|
|
* @param string $attach_id the temporary name generated when a file is uploaded |
|
574
|
|
|
* and used in $_SESSION to help identify the attachment itself |
|
575
|
|
|
*/ |
|
576
|
|
|
function removeTempAttachById($attach_id) |
|
577
|
|
|
{ |
|
578
|
|
|
foreach ($_SESSION['temp_attachments'] as $attachID => $attach) |
|
579
|
|
|
{ |
|
580
|
|
|
if ($attachID === $attach_id) |
|
581
|
|
|
{ |
|
582
|
|
|
// This file does exist, so lets terminate it! |
|
583
|
|
|
if (file_exists($attach['tmp_name'])) |
|
584
|
|
|
{ |
|
585
|
|
|
@unlink($attach['tmp_name']); |
|
|
|
|
|
|
586
|
|
|
unset($_SESSION['temp_attachments'][$attachID]); |
|
587
|
|
|
|
|
588
|
|
|
return true; |
|
589
|
|
|
} |
|
590
|
|
|
// Nope can't delete it if we can't find it |
|
591
|
|
|
else |
|
592
|
|
|
return 'attachment_not_found'; |
|
593
|
|
|
} |
|
594
|
|
|
} |
|
595
|
|
|
|
|
596
|
|
|
return 'attachment_not_found'; |
|
597
|
|
|
} |
|
598
|
|
|
|
|
599
|
|
|
/** |
|
600
|
|
|
* Finds and return a temporary attachment by its id |
|
601
|
|
|
* |
|
602
|
|
|
* @package Attachments |
|
603
|
|
|
* @param string $attach_id the temporary name generated when a file is uploaded |
|
604
|
|
|
* and used in $_SESSION to help identify the attachment itself |
|
605
|
|
|
* |
|
606
|
|
|
* @return mixed |
|
607
|
|
|
* @throws Exception |
|
608
|
|
|
*/ |
|
609
|
|
|
function getTempAttachById($attach_id) |
|
610
|
|
|
{ |
|
611
|
|
|
global $modSettings, $user_info; |
|
612
|
|
|
|
|
613
|
|
|
$attach_real_id = null; |
|
614
|
|
|
|
|
615
|
|
|
if (empty($_SESSION['temp_attachments'])) |
|
616
|
|
|
{ |
|
617
|
|
|
throw new \Exception('no_access'); |
|
618
|
|
|
} |
|
619
|
|
|
|
|
620
|
|
|
foreach ($_SESSION['temp_attachments'] as $attachID => $val) |
|
621
|
|
|
{ |
|
622
|
|
|
if ($attachID === 'post') |
|
623
|
|
|
{ |
|
624
|
|
|
continue; |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
|
|
if ($val['public_attachid'] === $attach_id) |
|
628
|
|
|
{ |
|
629
|
|
|
$attach_real_id = $attachID; |
|
630
|
|
|
break; |
|
631
|
|
|
} |
|
632
|
|
|
} |
|
633
|
|
|
|
|
634
|
|
|
if (empty($attach_real_id)) |
|
635
|
|
|
{ |
|
636
|
|
|
throw new \Exception('no_access'); |
|
637
|
|
|
} |
|
638
|
|
|
|
|
639
|
|
|
// The common name form is "post_tmp_123_0ac9a0b1fc18604e8704084656ed5f09" |
|
640
|
|
|
$id_attach = preg_replace('~[^0-9a-zA-Z_]~', '', $attach_real_id); |
|
641
|
|
|
|
|
642
|
|
|
// Permissions: only temporary attachments |
|
643
|
|
|
if (substr($id_attach, 0, 8) !== 'post_tmp') |
|
644
|
|
|
throw new \Exception('no_access'); |
|
645
|
|
|
|
|
646
|
|
|
// Permissions: only author is allowed. |
|
647
|
|
|
$pieces = explode('_', substr($id_attach, 9)); |
|
648
|
|
|
|
|
649
|
|
|
if (!isset($pieces[0]) || $pieces[0] != $user_info['id']) |
|
650
|
|
|
throw new \Exception('no_access'); |
|
651
|
|
|
|
|
652
|
|
|
if (is_array($modSettings['attachmentUploadDir'])) |
|
653
|
|
|
$dirs = $modSettings['attachmentUploadDir']; |
|
654
|
|
|
else |
|
655
|
|
|
$dirs = unserialize($modSettings['attachmentUploadDir']); |
|
656
|
|
|
|
|
657
|
|
|
$attach_dir = $dirs[$modSettings['currentAttachmentUploadDir']]; |
|
658
|
|
|
|
|
659
|
|
|
if (file_exists($attach_dir . '/' . $attach_real_id) && isset($_SESSION['temp_attachments'][$attach_real_id])) |
|
660
|
|
|
{ |
|
661
|
|
|
return $_SESSION['temp_attachments'][$attach_real_id]; |
|
662
|
|
|
} |
|
663
|
|
|
|
|
664
|
|
|
throw new \Exception('no_access'); |
|
665
|
|
|
} |
|
666
|
|
|
|
|
667
|
|
|
/** |
|
668
|
|
|
* Checks if an uploaded file produced any appropriate error code |
|
669
|
|
|
* |
|
670
|
|
|
* What it does: |
|
671
|
|
|
* |
|
672
|
|
|
* - Checks for error codes in the error segment of the file array that is |
|
673
|
|
|
* created by PHP during the file upload. |
|
674
|
|
|
* |
|
675
|
|
|
* @package Attachments |
|
676
|
|
|
* @param int $attachID |
|
677
|
|
|
*/ |
|
678
|
|
|
function attachmentUploadChecks($attachID) |
|
679
|
|
|
{ |
|
680
|
|
|
global $modSettings, $txt; |
|
681
|
|
|
|
|
682
|
|
|
$errors = array(); |
|
683
|
|
|
|
|
684
|
|
|
// Did PHP create any errors during the upload processing of this file? |
|
685
|
|
|
if (!empty($_FILES['attachment']['error'][$attachID])) |
|
686
|
|
|
{ |
|
687
|
|
|
// The file exceeds the max_filesize directive in php.ini |
|
688
|
|
|
if ($_FILES['attachment']['error'][$attachID] == 1) |
|
689
|
|
|
$errors[] = array('file_too_big', array($modSettings['attachmentSizeLimit'])); |
|
690
|
|
|
// The uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form. |
|
691
|
|
|
elseif ($_FILES['attachment']['error'][$attachID] == 2) |
|
692
|
|
|
$errors[] = array('file_too_big', array($modSettings['attachmentSizeLimit'])); |
|
693
|
|
|
// Missing or a full a temp directory on the server |
|
694
|
|
|
elseif ($_FILES['attachment']['error'][$attachID] == 6) |
|
695
|
|
|
Errors::instance()->log_error($_FILES['attachment']['name'][$attachID] . ': ' . $txt['php_upload_error_6'], 'critical'); |
|
696
|
|
|
// One of many errors such as (3)partially uploaded, (4)empty file, |
|
697
|
|
|
else |
|
698
|
|
|
Errors::instance()->log_error($_FILES['attachment']['name'][$attachID] . ': ' . $txt['php_upload_error_' . $_FILES['attachment']['error'][$attachID]]); |
|
699
|
|
|
|
|
700
|
|
|
// If we did not set an user error (3,4,6,7,8) to show then give them a generic one as there is |
|
701
|
|
|
// no need to provide back specifics of a server error, those are logged |
|
702
|
|
|
if (empty($errors)) |
|
703
|
|
|
$errors[] = 'attach_php_error'; |
|
704
|
|
|
} |
|
705
|
|
|
|
|
706
|
|
|
return $errors; |
|
707
|
|
|
} |
|
708
|
|
|
|
|
709
|
|
|
/** |
|
710
|
|
|
* Performs various checks on an uploaded file. |
|
711
|
|
|
* |
|
712
|
|
|
* What it does: |
|
713
|
|
|
* |
|
714
|
|
|
* - Requires that $_SESSION['temp_attachments'][$attachID] be properly populated. |
|
715
|
|
|
* |
|
716
|
|
|
* @package Attachments |
|
717
|
|
|
* |
|
718
|
|
|
* @param int $attachID id of the attachment to check |
|
719
|
|
|
* |
|
720
|
|
|
* @return bool |
|
721
|
|
|
* @throws Elk_Exception attach_check_nag |
|
722
|
|
|
*/ |
|
723
|
|
|
function attachmentChecks($attachID) |
|
724
|
|
|
{ |
|
725
|
|
|
global $modSettings, $context, $attachmentOptions; |
|
726
|
|
|
|
|
727
|
|
|
$db = database(); |
|
728
|
|
|
|
|
729
|
|
|
// No data or missing data .... Not necessarily needed, but in case a mod author missed something. |
|
730
|
|
|
if (empty($_SESSION['temp_attachments'][$attachID])) |
|
731
|
|
|
$error = '$_SESSION[\'temp_attachments\'][$attachID]'; |
|
732
|
|
|
elseif (empty($attachID)) |
|
733
|
|
|
$error = '$attachID'; |
|
734
|
|
|
elseif (empty($context['attachments'])) |
|
735
|
|
|
$error = '$context[\'attachments\']'; |
|
736
|
|
|
elseif (empty($context['attach_dir'])) |
|
737
|
|
|
$error = '$context[\'attach_dir\']'; |
|
738
|
|
|
|
|
739
|
|
|
// Let's get their attention. |
|
740
|
|
|
if (!empty($error)) |
|
741
|
|
|
throw new Elk_Exception('attach_check_nag', 'debug', array($error)); |
|
742
|
|
|
|
|
743
|
|
|
// Just in case this slipped by the first checks, we stop it here and now |
|
744
|
|
|
if ($_SESSION['temp_attachments'][$attachID]['size'] == 0) |
|
745
|
|
|
{ |
|
746
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = 'attach_0_byte_file'; |
|
747
|
|
|
return false; |
|
748
|
|
|
} |
|
749
|
|
|
|
|
750
|
|
|
// First, the dreaded security check. Sorry folks, but this should't be avoided |
|
751
|
|
|
$size = elk_getimagesize($_SESSION['temp_attachments'][$attachID]['tmp_name']); |
|
752
|
|
|
$valid_mime = getValidMimeImageType($size[2]); |
|
753
|
|
|
|
|
754
|
|
|
if ($valid_mime !== '') |
|
755
|
|
|
{ |
|
756
|
|
|
require_once(SUBSDIR . '/Graphics.subs.php'); |
|
757
|
|
|
if (!checkImageContents($_SESSION['temp_attachments'][$attachID]['tmp_name'], !empty($modSettings['attachment_image_paranoid']))) |
|
758
|
|
|
{ |
|
759
|
|
|
// It's bad. Last chance, maybe we can re-encode it? |
|
760
|
|
|
if (empty($modSettings['attachment_image_reencode']) || (!reencodeImage($_SESSION['temp_attachments'][$attachID]['tmp_name'], $size[2]))) |
|
761
|
|
|
{ |
|
762
|
|
|
// Nothing to do: not allowed or not successful re-encoding it. |
|
763
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = 'bad_attachment'; |
|
764
|
|
|
return false; |
|
765
|
|
|
} |
|
766
|
|
|
|
|
767
|
|
|
// Success! However, successes usually come for a price: |
|
768
|
|
|
// we might get a new format for our image... |
|
769
|
|
|
$old_format = $size[2]; |
|
770
|
|
|
$size = elk_getimagesize($attachmentOptions['tmp_name']); |
|
771
|
|
|
|
|
772
|
|
|
if (!(empty($size)) && ($size[2] !== $old_format)) |
|
773
|
|
|
{ |
|
774
|
|
|
$valid_mime = getValidMimeImageType($size[2]); |
|
775
|
|
|
if ($valid_mime !== '') |
|
776
|
|
|
{ |
|
777
|
|
|
$_SESSION['temp_attachments'][$attachID]['type'] = $valid_mime; |
|
778
|
|
|
} |
|
779
|
|
|
} |
|
780
|
|
|
} |
|
781
|
|
|
} |
|
782
|
|
|
|
|
783
|
|
|
// Is there room for this in the directory? |
|
784
|
|
|
if (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit'])) |
|
785
|
|
|
{ |
|
786
|
|
|
// Check the folder size and count. If it hasn't been done already. |
|
787
|
|
|
if (empty($context['dir_size']) || empty($context['dir_files'])) |
|
788
|
|
|
{ |
|
789
|
|
|
$request = $db->query('', ' |
|
790
|
|
|
SELECT COUNT(*), SUM(size) |
|
791
|
|
|
FROM {db_prefix}attachments |
|
792
|
|
|
WHERE id_folder = {int:folder_id} |
|
793
|
|
|
AND attachment_type != {int:type}', |
|
794
|
|
|
array( |
|
795
|
|
|
'folder_id' => $modSettings['currentAttachmentUploadDir'], |
|
796
|
|
|
'type' => 1, |
|
797
|
|
|
) |
|
798
|
|
|
); |
|
799
|
|
|
list ($context['dir_files'], $context['dir_size']) = $db->fetch_row($request); |
|
800
|
|
|
$db->free_result($request); |
|
801
|
|
|
} |
|
802
|
|
|
$context['dir_size'] += $_SESSION['temp_attachments'][$attachID]['size']; |
|
803
|
|
|
$context['dir_files']++; |
|
804
|
|
|
|
|
805
|
|
|
// Are we about to run out of room? Let's notify the admin then. |
|
806
|
|
|
if (empty($modSettings['attachment_full_notified']) && !empty($modSettings['attachmentDirSizeLimit']) && $modSettings['attachmentDirSizeLimit'] > 4000 && $context['dir_size'] > ($modSettings['attachmentDirSizeLimit'] - 2000) * 1024 |
|
|
|
|
|
|
807
|
|
|
|| (!empty($modSettings['attachmentDirFileLimit']) && $modSettings['attachmentDirFileLimit'] * .95 < $context['dir_files'] && $modSettings['attachmentDirFileLimit'] > 500)) |
|
808
|
|
|
{ |
|
809
|
|
|
require_once(SUBSDIR . '/Admin.subs.php'); |
|
810
|
|
|
emailAdmins('admin_attachments_full'); |
|
811
|
|
|
updateSettings(array('attachment_full_notified' => 1)); |
|
812
|
|
|
} |
|
813
|
|
|
|
|
814
|
|
|
// No room left.... What to do now??? |
|
815
|
|
|
if (!empty($modSettings['attachmentDirFileLimit']) && $context['dir_files'] + 2 > $modSettings['attachmentDirFileLimit'] |
|
|
|
|
|
|
816
|
|
|
|| (!empty($modSettings['attachmentDirSizeLimit']) && $context['dir_size'] > $modSettings['attachmentDirSizeLimit'] * 1024)) |
|
817
|
|
|
{ |
|
818
|
|
|
// If we are managing the directories space automatically, lets get to it |
|
819
|
|
|
if (!empty($modSettings['automanage_attachments']) && $modSettings['automanage_attachments'] == 1) |
|
820
|
|
|
{ |
|
821
|
|
|
// Move it to the new folder if we can. |
|
822
|
|
|
if (automanage_attachments_by_space()) |
|
823
|
|
|
{ |
|
824
|
|
|
rename($_SESSION['temp_attachments'][$attachID]['tmp_name'], $context['attach_dir'] . '/' . $attachID); |
|
825
|
|
|
$_SESSION['temp_attachments'][$attachID]['tmp_name'] = $context['attach_dir'] . '/' . $attachID; |
|
826
|
|
|
$_SESSION['temp_attachments'][$attachID]['id_folder'] = $modSettings['currentAttachmentUploadDir']; |
|
827
|
|
|
$context['dir_size'] = 0; |
|
828
|
|
|
$context['dir_files'] = 0; |
|
829
|
|
|
} |
|
830
|
|
|
// Or, let the user know that its not going to happen. |
|
831
|
|
|
else |
|
832
|
|
|
{ |
|
833
|
|
|
if (isset($context['dir_creation_error'])) |
|
834
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = $context['dir_creation_error']; |
|
835
|
|
|
else |
|
836
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = 'ran_out_of_space'; |
|
837
|
|
|
} |
|
838
|
|
|
} |
|
839
|
|
|
else |
|
840
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = 'ran_out_of_space'; |
|
841
|
|
|
} |
|
842
|
|
|
} |
|
843
|
|
|
|
|
844
|
|
|
// Is the file too big? |
|
845
|
|
|
if (!empty($modSettings['attachmentSizeLimit']) && $_SESSION['temp_attachments'][$attachID]['size'] > $modSettings['attachmentSizeLimit'] * 1024) |
|
846
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = array('file_too_big', array(comma_format($modSettings['attachmentSizeLimit'], 0))); |
|
847
|
|
|
|
|
848
|
|
|
// Check the total upload size for this post... |
|
849
|
|
|
$context['attachments']['total_size'] += $_SESSION['temp_attachments'][$attachID]['size']; |
|
850
|
|
|
if (!empty($modSettings['attachmentPostLimit']) && $context['attachments']['total_size'] > $modSettings['attachmentPostLimit'] * 1024) |
|
851
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = array('attach_max_total_file_size', array(comma_format($modSettings['attachmentPostLimit'], 0), comma_format($modSettings['attachmentPostLimit'] - (($context['attachments']['total_size'] - $_SESSION['temp_attachments'][$attachID]['size']) / 1024), 0))); |
|
852
|
|
|
|
|
853
|
|
|
// Have we reached the maximum number of files we are allowed? |
|
854
|
|
|
$context['attachments']['quantity']++; |
|
855
|
|
|
|
|
856
|
|
|
// Set a max limit if none exists |
|
857
|
|
|
if (empty($modSettings['attachmentNumPerPostLimit']) && $context['attachments']['quantity'] >= 50) |
|
858
|
|
|
$modSettings['attachmentNumPerPostLimit'] = 50; |
|
859
|
|
|
|
|
860
|
|
|
if (!empty($modSettings['attachmentNumPerPostLimit']) && $context['attachments']['quantity'] > $modSettings['attachmentNumPerPostLimit']) |
|
861
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = array('attachments_limit_per_post', array($modSettings['attachmentNumPerPostLimit'])); |
|
862
|
|
|
|
|
863
|
|
|
// File extension check |
|
864
|
|
|
if (!empty($modSettings['attachmentCheckExtensions'])) |
|
865
|
|
|
{ |
|
866
|
|
|
$allowed = explode(',', strtolower($modSettings['attachmentExtensions'])); |
|
867
|
|
|
foreach ($allowed as $k => $dummy) |
|
868
|
|
|
$allowed[$k] = trim($dummy); |
|
869
|
|
|
|
|
870
|
|
|
if (!in_array(strtolower(substr(strrchr($_SESSION['temp_attachments'][$attachID]['name'], '.'), 1)), $allowed)) |
|
871
|
|
|
{ |
|
872
|
|
|
$allowed_extensions = strtr(strtolower($modSettings['attachmentExtensions']), array(',' => ', ')); |
|
873
|
|
|
$_SESSION['temp_attachments'][$attachID]['errors'][] = array('cant_upload_type', array($allowed_extensions)); |
|
874
|
|
|
} |
|
875
|
|
|
} |
|
876
|
|
|
|
|
877
|
|
|
// Undo the math if there's an error |
|
878
|
|
|
if (!empty($_SESSION['temp_attachments'][$attachID]['errors'])) |
|
879
|
|
|
{ |
|
880
|
|
|
if (isset($context['dir_size'])) |
|
881
|
|
|
$context['dir_size'] -= $_SESSION['temp_attachments'][$attachID]['size']; |
|
882
|
|
|
if (isset($context['dir_files'])) |
|
883
|
|
|
$context['dir_files']--; |
|
884
|
|
|
|
|
885
|
|
|
$context['attachments']['total_size'] -= $_SESSION['temp_attachments'][$attachID]['size']; |
|
886
|
|
|
$context['attachments']['quantity']--; |
|
887
|
|
|
|
|
888
|
|
|
return false; |
|
889
|
|
|
} |
|
890
|
|
|
|
|
891
|
|
|
return true; |
|
892
|
|
|
} |
|
893
|
|
|
|
|
894
|
|
|
/** |
|
895
|
|
|
* Create an attachment, with the given array of parameters. |
|
896
|
|
|
* |
|
897
|
|
|
* What it does: |
|
898
|
|
|
* |
|
899
|
|
|
* - Adds any additional or missing parameters to $attachmentOptions. |
|
900
|
|
|
* - Renames the temporary file. |
|
901
|
|
|
* - Creates a thumbnail if the file is an image and the option enabled. |
|
902
|
|
|
* |
|
903
|
|
|
* @package Attachments |
|
904
|
|
|
* @param mixed[] $attachmentOptions associative array of options |
|
905
|
|
|
*/ |
|
906
|
|
|
function createAttachment(&$attachmentOptions) |
|
907
|
|
|
{ |
|
908
|
|
|
global $modSettings, $context; |
|
909
|
|
|
|
|
910
|
|
|
$db = database(); |
|
911
|
|
|
|
|
912
|
|
|
require_once(SUBSDIR . '/Graphics.subs.php'); |
|
913
|
|
|
|
|
914
|
|
|
// If this is an image we need to set a few additional parameters. |
|
915
|
|
|
$size = elk_getimagesize($attachmentOptions['tmp_name']); |
|
916
|
|
|
list ($attachmentOptions['width'], $attachmentOptions['height']) = $size; |
|
917
|
|
|
$attachmentOptions['width'] = max(0, $attachmentOptions['width']); |
|
918
|
|
|
$attachmentOptions['height'] = max(0, $attachmentOptions['height']); |
|
919
|
|
|
|
|
920
|
|
|
// If it's an image get the mime type right. |
|
921
|
|
|
if (empty($attachmentOptions['mime_type']) && $attachmentOptions['width']) |
|
922
|
|
|
{ |
|
923
|
|
|
// Got a proper mime type? |
|
924
|
|
|
if (!empty($size['mime'])) |
|
925
|
|
|
{ |
|
926
|
|
|
$attachmentOptions['mime_type'] = $size['mime']; |
|
927
|
|
|
} |
|
928
|
|
|
// Otherwise a valid one? |
|
929
|
|
|
else |
|
930
|
|
|
{ |
|
931
|
|
|
$attachmentOptions['mime_type'] = getValidMimeImageType($size[2]); |
|
932
|
|
|
} |
|
933
|
|
|
} |
|
934
|
|
|
|
|
935
|
|
|
// It is possible we might have a MIME type that isn't actually an image but still have a size. |
|
936
|
|
|
// For example, Shockwave files will be able to return size but be 'application/shockwave' or similar. |
|
937
|
|
|
if (!empty($attachmentOptions['mime_type']) && strpos($attachmentOptions['mime_type'], 'image/') !== 0) |
|
938
|
|
|
{ |
|
939
|
|
|
$attachmentOptions['width'] = 0; |
|
940
|
|
|
$attachmentOptions['height'] = 0; |
|
941
|
|
|
} |
|
942
|
|
|
|
|
943
|
|
|
// Get the hash if no hash has been given yet. |
|
944
|
|
|
if (empty($attachmentOptions['file_hash'])) |
|
945
|
|
|
$attachmentOptions['file_hash'] = getAttachmentFilename($attachmentOptions['name'], 0, null, true); |
|
946
|
|
|
|
|
947
|
|
|
// Assuming no-one set the extension let's take a look at it. |
|
948
|
|
|
if (empty($attachmentOptions['fileext'])) |
|
949
|
|
|
{ |
|
950
|
|
|
$attachmentOptions['fileext'] = strtolower(strrpos($attachmentOptions['name'], '.') !== false ? substr($attachmentOptions['name'], strrpos($attachmentOptions['name'], '.') + 1) : ''); |
|
951
|
|
|
if (strlen($attachmentOptions['fileext']) > 8 || '.' . $attachmentOptions['fileext'] == $attachmentOptions['name']) |
|
952
|
|
|
$attachmentOptions['fileext'] = ''; |
|
953
|
|
|
} |
|
954
|
|
|
|
|
955
|
|
|
$db->insert('', |
|
956
|
|
|
'{db_prefix}attachments', |
|
957
|
|
|
array( |
|
958
|
|
|
'id_folder' => 'int', 'id_msg' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'fileext' => 'string-8', |
|
959
|
|
|
'size' => 'int', 'width' => 'int', 'height' => 'int', |
|
960
|
|
|
'mime_type' => 'string-20', 'approved' => 'int', |
|
961
|
|
|
), |
|
962
|
|
|
array( |
|
963
|
|
|
(int) $attachmentOptions['id_folder'], (int) $attachmentOptions['post'], $attachmentOptions['name'], $attachmentOptions['file_hash'], $attachmentOptions['fileext'], |
|
964
|
|
|
(int) $attachmentOptions['size'], (empty($attachmentOptions['width']) ? 0 : (int) $attachmentOptions['width']), (empty($attachmentOptions['height']) ? '0' : (int) $attachmentOptions['height']), |
|
965
|
|
|
(!empty($attachmentOptions['mime_type']) ? $attachmentOptions['mime_type'] : ''), (int) $attachmentOptions['approved'], |
|
966
|
|
|
), |
|
967
|
|
|
array('id_attach') |
|
968
|
|
|
); |
|
969
|
|
|
$attachmentOptions['id'] = $db->insert_id('{db_prefix}attachments', 'id_attach'); |
|
970
|
|
|
|
|
971
|
|
|
// @todo Add an error here maybe? |
|
972
|
|
|
if (empty($attachmentOptions['id'])) |
|
973
|
|
|
return false; |
|
974
|
|
|
|
|
975
|
|
|
// Now that we have the attach id, let's rename this and finish up. |
|
976
|
|
|
$attachmentOptions['destination'] = getAttachmentFilename(basename($attachmentOptions['name']), $attachmentOptions['id'], $attachmentOptions['id_folder'], false, $attachmentOptions['file_hash']); |
|
977
|
|
|
rename($attachmentOptions['tmp_name'], $attachmentOptions['destination']); |
|
978
|
|
|
|
|
979
|
|
|
// If it's not approved then add to the approval queue. |
|
980
|
|
|
if (!$attachmentOptions['approved']) |
|
981
|
|
|
$db->insert('', |
|
982
|
|
|
'{db_prefix}approval_queue', |
|
983
|
|
|
array( |
|
984
|
|
|
'id_attach' => 'int', 'id_msg' => 'int', |
|
985
|
|
|
), |
|
986
|
|
|
array( |
|
987
|
|
|
$attachmentOptions['id'], (int) $attachmentOptions['post'], |
|
988
|
|
|
), |
|
989
|
|
|
array() |
|
990
|
|
|
); |
|
991
|
|
|
|
|
992
|
|
|
if (empty($modSettings['attachmentThumbnails']) || (empty($attachmentOptions['width']) && empty($attachmentOptions['height']))) |
|
993
|
|
|
return true; |
|
994
|
|
|
|
|
995
|
|
|
// Like thumbnails, do we? |
|
996
|
|
|
if (!empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight']) && ($attachmentOptions['width'] > $modSettings['attachmentThumbWidth'] || $attachmentOptions['height'] > $modSettings['attachmentThumbHeight'])) |
|
997
|
|
|
{ |
|
998
|
|
|
if (createThumbnail($attachmentOptions['destination'], $modSettings['attachmentThumbWidth'], $modSettings['attachmentThumbHeight'])) |
|
999
|
|
|
{ |
|
1000
|
|
|
// Figure out how big we actually made it. |
|
1001
|
|
|
$size = elk_getimagesize($attachmentOptions['destination'] . '_thumb'); |
|
1002
|
|
|
list ($thumb_width, $thumb_height) = $size; |
|
1003
|
|
|
|
|
1004
|
|
|
if (!empty($size['mime'])) |
|
1005
|
|
|
{ |
|
1006
|
|
|
$thumb_mime = $size['mime']; |
|
1007
|
|
|
} |
|
1008
|
|
|
else |
|
1009
|
|
|
{ |
|
1010
|
|
|
$thumb_mime = getValidMimeImageType($size[2]); |
|
1011
|
|
|
} |
|
1012
|
|
|
|
|
1013
|
|
|
$thumb_filename = $attachmentOptions['name'] . '_thumb'; |
|
1014
|
|
|
$thumb_size = filesize($attachmentOptions['destination'] . '_thumb'); |
|
1015
|
|
|
$thumb_file_hash = getAttachmentFilename($thumb_filename, 0, null, true); |
|
1016
|
|
|
$thumb_path = $attachmentOptions['destination'] . '_thumb'; |
|
1017
|
|
|
|
|
1018
|
|
|
// We should check the file size and count here since thumbs are added to the existing totals. |
|
1019
|
|
|
if (!empty($modSettings['automanage_attachments']) && $modSettings['automanage_attachments'] == 1 && !empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit'])) |
|
|
|
|
|
|
1020
|
|
|
{ |
|
1021
|
|
|
$context['dir_size'] = isset($context['dir_size']) ? $context['dir_size'] += $thumb_size : $context['dir_size'] = 0; |
|
1022
|
|
|
$context['dir_files'] = isset($context['dir_files']) ? $context['dir_files']++ : $context['dir_files'] = 0; |
|
1023
|
|
|
|
|
1024
|
|
|
// If the folder is full, try to create a new one and move the thumb to it. |
|
1025
|
|
|
if ($context['dir_size'] > $modSettings['attachmentDirSizeLimit'] * 1024 || $context['dir_files'] + 2 > $modSettings['attachmentDirFileLimit']) |
|
1026
|
|
|
{ |
|
1027
|
|
|
if (automanage_attachments_by_space()) |
|
1028
|
|
|
{ |
|
1029
|
|
|
rename($thumb_path, $context['attach_dir'] . '/' . $thumb_filename); |
|
1030
|
|
|
$thumb_path = $context['attach_dir'] . '/' . $thumb_filename; |
|
1031
|
|
|
$context['dir_size'] = 0; |
|
1032
|
|
|
$context['dir_files'] = 0; |
|
1033
|
|
|
} |
|
1034
|
|
|
} |
|
1035
|
|
|
} |
|
1036
|
|
|
|
|
1037
|
|
|
// If a new folder has been already created. Gotta move this thumb there then. |
|
1038
|
|
|
if ($modSettings['currentAttachmentUploadDir'] != $attachmentOptions['id_folder']) |
|
1039
|
|
|
{ |
|
1040
|
|
|
rename($thumb_path, $context['attach_dir'] . '/' . $thumb_filename); |
|
1041
|
|
|
$thumb_path = $context['attach_dir'] . '/' . $thumb_filename; |
|
1042
|
|
|
} |
|
1043
|
|
|
|
|
1044
|
|
|
// To the database we go! |
|
1045
|
|
|
$db->insert('', |
|
1046
|
|
|
'{db_prefix}attachments', |
|
1047
|
|
|
array( |
|
1048
|
|
|
'id_folder' => 'int', 'id_msg' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'fileext' => 'string-8', |
|
1049
|
|
|
'size' => 'int', 'width' => 'int', 'height' => 'int', 'mime_type' => 'string-20', 'approved' => 'int', |
|
1050
|
|
|
), |
|
1051
|
|
|
array( |
|
1052
|
|
|
$modSettings['currentAttachmentUploadDir'], (int) $attachmentOptions['post'], 3, $thumb_filename, $thumb_file_hash, $attachmentOptions['fileext'], |
|
1053
|
|
|
$thumb_size, $thumb_width, $thumb_height, $thumb_mime, (int) $attachmentOptions['approved'], |
|
1054
|
|
|
), |
|
1055
|
|
|
array('id_attach') |
|
1056
|
|
|
); |
|
1057
|
|
|
$attachmentOptions['thumb'] = $db->insert_id('{db_prefix}attachments', 'id_attach'); |
|
1058
|
|
|
|
|
1059
|
|
|
if (!empty($attachmentOptions['thumb'])) |
|
1060
|
|
|
{ |
|
1061
|
|
|
$db->query('', ' |
|
1062
|
|
|
UPDATE {db_prefix}attachments |
|
1063
|
|
|
SET id_thumb = {int:id_thumb} |
|
1064
|
|
|
WHERE id_attach = {int:id_attach}', |
|
1065
|
|
|
array( |
|
1066
|
|
|
'id_thumb' => $attachmentOptions['thumb'], |
|
1067
|
|
|
'id_attach' => $attachmentOptions['id'], |
|
1068
|
|
|
) |
|
1069
|
|
|
); |
|
1070
|
|
|
|
|
1071
|
|
|
rename($thumb_path, getAttachmentFilename($thumb_filename, $attachmentOptions['thumb'], $modSettings['currentAttachmentUploadDir'], false, $thumb_file_hash)); |
|
1072
|
|
|
} |
|
1073
|
|
|
} |
|
1074
|
|
|
} |
|
1075
|
|
|
|
|
1076
|
|
|
return true; |
|
1077
|
|
|
} |
|
1078
|
|
|
|
|
1079
|
|
|
/** |
|
1080
|
|
|
* Get the avatar with the specified ID. |
|
1081
|
|
|
* |
|
1082
|
|
|
* What it does: |
|
1083
|
|
|
* |
|
1084
|
|
|
* - It gets avatar data (folder, name of the file, filehash, etc) |
|
1085
|
|
|
* from the database. |
|
1086
|
|
|
* - Must return the same values and in the same order as getAttachmentFromTopic() |
|
1087
|
|
|
* |
|
1088
|
|
|
* @package Attachments |
|
1089
|
|
|
* @param int $id_attach |
|
1090
|
|
|
*/ |
|
1091
|
|
|
function getAvatar($id_attach) |
|
1092
|
|
|
{ |
|
1093
|
|
|
$db = database(); |
|
1094
|
|
|
|
|
1095
|
|
|
// Use our cache when possible |
|
1096
|
|
|
$cache = array(); |
|
1097
|
|
|
if (Cache::instance()->getVar($cache, 'getAvatar_id-' . $id_attach)) |
|
1098
|
|
|
$avatarData = $cache; |
|
1099
|
|
|
else |
|
1100
|
|
|
{ |
|
1101
|
|
|
$request = $db->query('', ' |
|
1102
|
|
|
SELECT id_folder, filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member |
|
1103
|
|
|
FROM {db_prefix}attachments |
|
1104
|
|
|
WHERE id_attach = {int:id_attach} |
|
1105
|
|
|
AND id_member > {int:blank_id_member} |
|
1106
|
|
|
LIMIT 1', |
|
1107
|
|
|
array( |
|
1108
|
|
|
'id_attach' => $id_attach, |
|
1109
|
|
|
'blank_id_member' => 0, |
|
1110
|
|
|
) |
|
1111
|
|
|
); |
|
1112
|
|
|
$avatarData = array(); |
|
1113
|
|
|
if ($db->num_rows($request) != 0) |
|
1114
|
|
|
$avatarData = $db->fetch_row($request); |
|
1115
|
|
|
$db->free_result($request); |
|
1116
|
|
|
|
|
1117
|
|
|
Cache::instance()->put('getAvatar_id-' . $id_attach, $avatarData, 900); |
|
1118
|
|
|
} |
|
1119
|
|
|
|
|
1120
|
|
|
return $avatarData; |
|
1121
|
|
|
} |
|
1122
|
|
|
|
|
1123
|
|
|
/** |
|
1124
|
|
|
* Get the specified attachment. |
|
1125
|
|
|
* |
|
1126
|
|
|
* What it does: |
|
1127
|
|
|
* |
|
1128
|
|
|
* - This includes a check of the topic |
|
1129
|
|
|
* - it only returns the attachment if it's indeed attached to a message in the topic given as parameter, and query_see_board... |
|
1130
|
|
|
* - Must return the same values and in the same order as getAvatar() |
|
1131
|
|
|
* |
|
1132
|
|
|
* @package Attachments |
|
1133
|
|
|
* @param int $id_attach |
|
1134
|
|
|
* @param int $id_topic |
|
1135
|
|
|
*/ |
|
1136
|
|
|
function getAttachmentFromTopic($id_attach, $id_topic) |
|
1137
|
|
|
{ |
|
1138
|
|
|
$db = database(); |
|
1139
|
|
|
|
|
1140
|
|
|
// Make sure this attachment is on this board. |
|
1141
|
|
|
$request = $db->query('', ' |
|
1142
|
|
|
SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member |
|
1143
|
|
|
FROM {db_prefix}attachments AS a |
|
1144
|
|
|
INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic}) |
|
1145
|
|
|
INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) |
|
1146
|
|
|
WHERE a.id_attach = {int:attach} |
|
1147
|
|
|
LIMIT 1', |
|
1148
|
|
|
array( |
|
1149
|
|
|
'attach' => $id_attach, |
|
1150
|
|
|
'current_topic' => $id_topic, |
|
1151
|
|
|
) |
|
1152
|
|
|
); |
|
1153
|
|
|
|
|
1154
|
|
|
$attachmentData = array(); |
|
1155
|
|
|
if ($db->num_rows($request) != 0) |
|
1156
|
|
|
{ |
|
1157
|
|
|
$attachmentData = $db->fetch_row($request); |
|
1158
|
|
|
} |
|
1159
|
|
|
$db->free_result($request); |
|
1160
|
|
|
|
|
1161
|
|
|
return $attachmentData; |
|
1162
|
|
|
} |
|
1163
|
|
|
|
|
1164
|
|
|
/** |
|
1165
|
|
|
* Get the thumbnail of specified attachment. |
|
1166
|
|
|
* |
|
1167
|
|
|
* What it does: |
|
1168
|
|
|
* |
|
1169
|
|
|
* - This includes a check of the topic |
|
1170
|
|
|
* - it only returns the attachment if it's indeed attached to a message in the topic given as parameter, and query_see_board... |
|
1171
|
|
|
* - Must return the same values and in the same order as getAvatar() |
|
1172
|
|
|
* |
|
1173
|
|
|
* @package Attachments |
|
1174
|
|
|
* @param int $id_attach |
|
1175
|
|
|
* @param int $id_topic |
|
1176
|
|
|
*/ |
|
1177
|
|
|
function getAttachmentThumbFromTopic($id_attach, $id_topic) |
|
1178
|
|
|
{ |
|
1179
|
|
|
$db = database(); |
|
1180
|
|
|
|
|
1181
|
|
|
// Make sure this attachment is on this board. |
|
1182
|
|
|
$request = $db->query('', ' |
|
1183
|
|
|
SELECT th.id_folder, th.filename, th.file_hash, th.fileext, th.id_attach, th.attachment_type, th.mime_type, |
|
1184
|
|
|
a.id_folder AS attach_id_folder, a.filename AS attach_filename, |
|
1185
|
|
|
a.file_hash AS attach_file_hash, a.fileext AS attach_fileext, |
|
1186
|
|
|
a.id_attach AS attach_id_attach, a.attachment_type AS attach_attachment_type, |
|
1187
|
|
|
a.mime_type AS attach_mime_type, |
|
1188
|
|
|
a.approved, m.id_member |
|
1189
|
|
|
FROM {db_prefix}attachments AS a |
|
1190
|
|
|
INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic}) |
|
1191
|
|
|
INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) |
|
1192
|
|
|
LEFT JOIN {db_prefix}attachments AS th ON (th.id_attach = a.id_thumb) |
|
1193
|
|
|
WHERE a.id_attach = {int:attach}', |
|
1194
|
|
|
array( |
|
1195
|
|
|
'attach' => $id_attach, |
|
1196
|
|
|
'current_topic' => $id_topic, |
|
1197
|
|
|
) |
|
1198
|
|
|
); |
|
1199
|
|
|
$attachmentData = array_fill(0, 9, ''); |
|
1200
|
|
|
if ($db->num_rows($request) != 0) |
|
1201
|
|
|
{ |
|
1202
|
|
|
$fetch = $db->fetch_assoc($request); |
|
1203
|
|
|
|
|
1204
|
|
|
// If there is a hash then the thumbnail exists |
|
1205
|
|
|
if (!empty($fetch['file_hash'])) |
|
1206
|
|
|
{ |
|
1207
|
|
|
$attachmentData = array( |
|
1208
|
|
|
$fetch['id_folder'], |
|
1209
|
|
|
$fetch['filename'], |
|
1210
|
|
|
$fetch['file_hash'], |
|
1211
|
|
|
$fetch['fileext'], |
|
1212
|
|
|
$fetch['id_attach'], |
|
1213
|
|
|
$fetch['attachment_type'], |
|
1214
|
|
|
$fetch['mime_type'], |
|
1215
|
|
|
$fetch['approved'], |
|
1216
|
|
|
$fetch['id_member'], |
|
1217
|
|
|
); |
|
1218
|
|
|
} |
|
1219
|
|
|
// otherwise $modSettings['attachmentThumbnails'] may be (or was) off, so original file |
|
1220
|
|
|
elseif (getValidMimeImageType($fetch['attach_mime_type']) !== '') |
|
1221
|
|
|
{ |
|
1222
|
|
|
$attachmentData = array( |
|
1223
|
|
|
$fetch['attach_id_folder'], |
|
1224
|
|
|
$fetch['attach_filename'], |
|
1225
|
|
|
$fetch['attach_file_hash'], |
|
1226
|
|
|
$fetch['attach_fileext'], |
|
1227
|
|
|
$fetch['attach_id_attach'], |
|
1228
|
|
|
$fetch['attach_attachment_type'], |
|
1229
|
|
|
$fetch['attach_mime_type'], |
|
1230
|
|
|
$fetch['approved'], |
|
1231
|
|
|
$fetch['id_member'], |
|
1232
|
|
|
); |
|
1233
|
|
|
} |
|
1234
|
|
|
} |
|
1235
|
|
|
$db->free_result($request); |
|
1236
|
|
|
|
|
1237
|
|
|
return $attachmentData; |
|
1238
|
|
|
} |
|
1239
|
|
|
|
|
1240
|
|
|
/** |
|
1241
|
|
|
* Returns if the given attachment ID is an image file or not |
|
1242
|
|
|
* |
|
1243
|
|
|
* What it does: |
|
1244
|
|
|
* |
|
1245
|
|
|
* - Given an attachment id, checks that it exists as an attachment |
|
1246
|
|
|
* - Verifies the message its associated is on a board the user can see |
|
1247
|
|
|
* - Sets 'is_image' if the attachment is an image file |
|
1248
|
|
|
* - Returns basic attachment values |
|
1249
|
|
|
* |
|
1250
|
|
|
* @package Attachments |
|
1251
|
|
|
* @param int $id_attach |
|
1252
|
|
|
* |
|
1253
|
|
|
* @returns array|boolean |
|
1254
|
|
|
*/ |
|
1255
|
|
|
function isAttachmentImage($id_attach) |
|
1256
|
|
|
{ |
|
1257
|
|
|
$db = database(); |
|
1258
|
|
|
|
|
1259
|
|
|
// Make sure this attachment is on this board. |
|
1260
|
|
|
$request = $db->query('', ' |
|
1261
|
|
|
SELECT |
|
1262
|
|
|
a.filename, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, a.downloads, a.size, a.width, a.height, |
|
1263
|
|
|
m.id_topic, m.id_board |
|
1264
|
|
|
FROM {db_prefix}attachments as a |
|
1265
|
|
|
INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) |
|
1266
|
|
|
INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) |
|
1267
|
|
|
WHERE id_attach = {int:attach} |
|
1268
|
|
|
AND attachment_type = {int:type} |
|
1269
|
|
|
AND a.approved = {int:approved} |
|
1270
|
|
|
LIMIT 1', |
|
1271
|
|
|
array( |
|
1272
|
|
|
'attach' => $id_attach, |
|
1273
|
|
|
'approved' => 1, |
|
1274
|
|
|
'type' => 0, |
|
1275
|
|
|
) |
|
1276
|
|
|
); |
|
1277
|
|
|
$attachmentData = array(); |
|
1278
|
|
|
if ($db->num_rows($request) != 0) |
|
1279
|
|
|
{ |
|
1280
|
|
|
$attachmentData = $db->fetch_assoc($request); |
|
1281
|
|
|
$attachmentData['is_image'] = substr($attachmentData['mime_type'], 0, 5) === 'image'; |
|
1282
|
|
|
$attachmentData['size'] = byte_format($attachmentData['size']); |
|
1283
|
|
|
} |
|
1284
|
|
|
$db->free_result($request); |
|
1285
|
|
|
|
|
1286
|
|
|
return !empty($attachmentData) ? $attachmentData : false; |
|
1287
|
|
|
} |
|
1288
|
|
|
|
|
1289
|
|
|
/** |
|
1290
|
|
|
* Increase download counter for id_attach. |
|
1291
|
|
|
* |
|
1292
|
|
|
* What it does: |
|
1293
|
|
|
* |
|
1294
|
|
|
* - Does not check if it's a thumbnail. |
|
1295
|
|
|
* |
|
1296
|
|
|
* @package Attachments |
|
1297
|
|
|
* @param int $id_attach |
|
1298
|
|
|
*/ |
|
1299
|
|
|
function increaseDownloadCounter($id_attach) |
|
1300
|
|
|
{ |
|
1301
|
|
|
$db = database(); |
|
1302
|
|
|
|
|
1303
|
|
|
$db->query('attach_download_increase', ' |
|
1304
|
|
|
UPDATE LOW_PRIORITY {db_prefix}attachments |
|
1305
|
|
|
SET downloads = downloads + 1 |
|
1306
|
|
|
WHERE id_attach = {int:id_attach}', |
|
1307
|
|
|
array( |
|
1308
|
|
|
'id_attach' => $id_attach, |
|
1309
|
|
|
) |
|
1310
|
|
|
); |
|
1311
|
|
|
} |
|
1312
|
|
|
|
|
1313
|
|
|
/** |
|
1314
|
|
|
* Saves a file and stores it locally for avatar use by id_member. |
|
1315
|
|
|
* |
|
1316
|
|
|
* What it does: |
|
1317
|
|
|
* |
|
1318
|
|
|
* - supports GIF, JPG, PNG, BMP and WBMP formats. |
|
1319
|
|
|
* - detects if GD2 is available. |
|
1320
|
|
|
* - uses resizeImageFile() to resize to max_width by max_height, and saves the result to a file. |
|
1321
|
|
|
* - updates the database info for the member's avatar. |
|
1322
|
|
|
* - returns whether the download and resize was successful. |
|
1323
|
|
|
* |
|
1324
|
|
|
* @uses subs/Graphics.subs.php |
|
1325
|
|
|
* @package Attachments |
|
1326
|
|
|
* @param string $temporary_path the full path to the temporary file |
|
1327
|
|
|
* @param int $memID member ID |
|
1328
|
|
|
* @param int $max_width |
|
1329
|
|
|
* @param int $max_height |
|
1330
|
|
|
* @return boolean whether the download and resize was successful. |
|
1331
|
|
|
* |
|
1332
|
|
|
*/ |
|
1333
|
|
|
function saveAvatar($temporary_path, $memID, $max_width, $max_height) |
|
1334
|
|
|
{ |
|
1335
|
|
|
global $modSettings; |
|
1336
|
|
|
|
|
1337
|
|
|
$db = database(); |
|
1338
|
|
|
|
|
1339
|
|
|
$ext = !empty($modSettings['avatar_download_png']) ? 'png' : 'jpeg'; |
|
1340
|
|
|
$destName = 'avatar_' . $memID . '_' . time() . '.' . $ext; |
|
1341
|
|
|
|
|
1342
|
|
|
// Just making sure there is a non-zero member. |
|
1343
|
|
|
if (empty($memID)) |
|
1344
|
|
|
return false; |
|
1345
|
|
|
|
|
1346
|
|
|
require_once(SUBSDIR . '/ManageAttachments.subs.php'); |
|
1347
|
|
|
removeAttachments(array('id_member' => $memID)); |
|
1348
|
|
|
|
|
1349
|
|
|
$id_folder = getAttachmentPathID(); |
|
1350
|
|
|
$avatar_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, 0, null, true) : ''; |
|
1351
|
|
|
$db->insert('', |
|
1352
|
|
|
'{db_prefix}attachments', |
|
1353
|
|
|
array( |
|
1354
|
|
|
'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-255', 'fileext' => 'string-8', 'size' => 'int', |
|
1355
|
|
|
'id_folder' => 'int', |
|
1356
|
|
|
), |
|
1357
|
|
|
array( |
|
1358
|
|
|
$memID, empty($modSettings['custom_avatar_enabled']) ? 0 : 1, $destName, $avatar_hash, $ext, 1, |
|
1359
|
|
|
$id_folder, |
|
1360
|
|
|
), |
|
1361
|
|
|
array('id_attach') |
|
1362
|
|
|
); |
|
1363
|
|
|
$attachID = $db->insert_id('{db_prefix}attachments', 'id_attach'); |
|
1364
|
|
|
|
|
1365
|
|
|
// First, the temporary file will have the .tmp extension. |
|
1366
|
|
|
$tempName = getAvatarPath() . '/' . $destName . '.tmp'; |
|
1367
|
|
|
|
|
1368
|
|
|
// The destination filename will depend on whether custom dir for avatars has been set |
|
1369
|
|
|
$destName = getAvatarPath() . '/' . $destName; |
|
1370
|
|
|
$path = getAttachmentPath(); |
|
1371
|
|
|
$destName = empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash . '.elk'; |
|
1372
|
|
|
|
|
1373
|
|
|
// Resize it. |
|
1374
|
|
|
require_once(SUBSDIR . '/Graphics.subs.php'); |
|
1375
|
|
|
if (!empty($modSettings['avatar_download_png'])) |
|
1376
|
|
|
$success = resizeImageFile($temporary_path, $tempName, $max_width, $max_height, 3); |
|
1377
|
|
|
else |
|
1378
|
|
|
$success = resizeImageFile($temporary_path, $tempName, $max_width, $max_height); |
|
1379
|
|
|
|
|
1380
|
|
|
if ($success) |
|
1381
|
|
|
{ |
|
1382
|
|
|
// Remove the .tmp extension from the attachment. |
|
1383
|
|
|
if (rename($tempName, $destName)) |
|
1384
|
|
|
{ |
|
1385
|
|
|
list ($width, $height) = elk_getimagesize($destName); |
|
1386
|
|
|
$mime_type = getValidMimeImageType($ext); |
|
1387
|
|
|
|
|
1388
|
|
|
// Write filesize in the database. |
|
1389
|
|
|
$db->query('', ' |
|
1390
|
|
|
UPDATE {db_prefix}attachments |
|
1391
|
|
|
SET size = {int:filesize}, width = {int:width}, height = {int:height}, |
|
1392
|
|
|
mime_type = {string:mime_type} |
|
1393
|
|
|
WHERE id_attach = {int:current_attachment}', |
|
1394
|
|
|
array( |
|
1395
|
|
|
'filesize' => filesize($destName), |
|
1396
|
|
|
'width' => (int) $width, |
|
1397
|
|
|
'height' => (int) $height, |
|
1398
|
|
|
'current_attachment' => $attachID, |
|
1399
|
|
|
'mime_type' => $mime_type, |
|
1400
|
|
|
) |
|
1401
|
|
|
); |
|
1402
|
|
|
|
|
1403
|
|
|
// Retain this globally in case the script wants it. |
|
1404
|
|
|
$modSettings['new_avatar_data'] = array( |
|
1405
|
|
|
'id' => $attachID, |
|
1406
|
|
|
'filename' => $destName, |
|
1407
|
|
|
'type' => empty($modSettings['custom_avatar_enabled']) ? 0 : 1, |
|
1408
|
|
|
); |
|
1409
|
|
|
return true; |
|
1410
|
|
|
} |
|
1411
|
|
|
else |
|
1412
|
|
|
return false; |
|
1413
|
|
|
} |
|
1414
|
|
|
else |
|
1415
|
|
|
{ |
|
1416
|
|
|
$db->query('', ' |
|
1417
|
|
|
DELETE FROM {db_prefix}attachments |
|
1418
|
|
|
WHERE id_attach = {int:current_attachment}', |
|
1419
|
|
|
array( |
|
1420
|
|
|
'current_attachment' => $attachID, |
|
1421
|
|
|
) |
|
1422
|
|
|
); |
|
1423
|
|
|
|
|
1424
|
|
|
@unlink($tempName); |
|
|
|
|
|
|
1425
|
|
|
return false; |
|
1426
|
|
|
} |
|
1427
|
|
|
} |
|
1428
|
|
|
|
|
1429
|
|
|
/** |
|
1430
|
|
|
* Get the size of a specified image with better error handling. |
|
1431
|
|
|
* |
|
1432
|
|
|
* What it does: |
|
1433
|
|
|
* |
|
1434
|
|
|
* - Uses getimagesize() to determine the size of a file. |
|
1435
|
|
|
* - Attempts to connect to the server first so it won't time out. |
|
1436
|
|
|
* |
|
1437
|
|
|
* @todo see if it's better in subs/Graphics.subs.php, but one step at the time. |
|
1438
|
|
|
* |
|
1439
|
|
|
* @package Attachments |
|
1440
|
|
|
* @param string $url |
|
1441
|
|
|
* @return array or false, the image size as array(width, height), or false on failure |
|
1442
|
|
|
*/ |
|
1443
|
|
|
function url_image_size($url) |
|
1444
|
|
|
{ |
|
1445
|
|
|
// Make sure it is a proper URL. |
|
1446
|
|
|
$url = str_replace(' ', '%20', $url); |
|
1447
|
|
|
|
|
1448
|
|
|
// Can we pull this from the cache... please please? |
|
1449
|
|
|
$temp = array(); |
|
1450
|
|
|
if (Cache::instance()->getVar($temp, 'url_image_size-' . md5($url), 240)) |
|
1451
|
|
|
return $temp; |
|
1452
|
|
|
|
|
1453
|
|
|
$t = microtime(true); |
|
1454
|
|
|
|
|
1455
|
|
|
// Get the host to pester... |
|
1456
|
|
|
preg_match('~^\w+://(.+?)/(.*)$~', $url, $match); |
|
1457
|
|
|
|
|
1458
|
|
|
// Can't figure it out, just try the image size. |
|
1459
|
|
|
if ($url == '' || $url == 'http://' || $url == 'https://') |
|
1460
|
|
|
return false; |
|
|
|
|
|
|
1461
|
|
|
elseif (!isset($match[1])) |
|
1462
|
|
|
$size = elk_getimagesize($url, false); |
|
1463
|
|
|
else |
|
1464
|
|
|
{ |
|
1465
|
|
|
// Try to connect to the server... give it half a second. |
|
1466
|
|
|
$temp = 0; |
|
1467
|
|
|
$fp = @fsockopen($match[1], 80, $temp, $temp, 0.5); |
|
1468
|
|
|
|
|
1469
|
|
|
// Successful? Continue... |
|
1470
|
|
|
if ($fp !== false) |
|
1471
|
|
|
{ |
|
1472
|
|
|
// Send the HEAD request (since we don't have to worry about chunked, HTTP/1.1 is fine here.) |
|
1473
|
|
|
fwrite($fp, 'HEAD /' . $match[2] . ' HTTP/1.1' . "\r\n" . 'Host: ' . $match[1] . "\r\n" . 'User-Agent: PHP/ELK' . "\r\n" . 'Connection: close' . "\r\n\r\n"); |
|
1474
|
|
|
|
|
1475
|
|
|
// Read in the HTTP/1.1 or whatever. |
|
1476
|
|
|
$test = substr(fgets($fp, 11), -1); |
|
1477
|
|
|
fclose($fp); |
|
1478
|
|
|
|
|
1479
|
|
|
// See if it returned a 404/403 or something. |
|
1480
|
|
|
if ($test < 4) |
|
1481
|
|
|
{ |
|
1482
|
|
|
$size = elk_getimagesize($url, false); |
|
1483
|
|
|
|
|
1484
|
|
|
// This probably means allow_url_fopen is off, let's try GD. |
|
1485
|
|
|
if ($size === false && function_exists('imagecreatefromstring')) |
|
1486
|
|
|
{ |
|
1487
|
|
|
include_once(SUBSDIR . '/Package.subs.php'); |
|
1488
|
|
|
|
|
1489
|
|
|
// It's going to hate us for doing this, but another request... |
|
1490
|
|
|
$image = @imagecreatefromstring(fetch_web_data($url)); |
|
1491
|
|
|
if ($image !== false) |
|
1492
|
|
|
{ |
|
1493
|
|
|
$size = array(imagesx($image), imagesy($image)); |
|
1494
|
|
|
imagedestroy($image); |
|
1495
|
|
|
} |
|
1496
|
|
|
} |
|
1497
|
|
|
} |
|
1498
|
|
|
} |
|
1499
|
|
|
} |
|
1500
|
|
|
|
|
1501
|
|
|
// If we didn't get it, we failed. |
|
1502
|
|
|
if (!isset($size)) |
|
1503
|
|
|
$size = false; |
|
1504
|
|
|
|
|
1505
|
|
|
// If this took a long time, we may never have to do it again, but then again we might... |
|
1506
|
|
|
if (microtime(true) - $t > 0.8) |
|
1507
|
|
|
Cache::instance()->put('url_image_size-' . md5($url), $size, 240); |
|
1508
|
|
|
|
|
1509
|
|
|
// Didn't work. |
|
1510
|
|
|
return $size; |
|
1511
|
|
|
} |
|
1512
|
|
|
|
|
1513
|
|
|
/** |
|
1514
|
|
|
* The current attachments path: |
|
1515
|
|
|
* |
|
1516
|
|
|
* What it does: |
|
1517
|
|
|
* - BOARDDIR . '/attachments', if nothing is set yet. |
|
1518
|
|
|
* - if the forum is using multiple attachments directories, |
|
1519
|
|
|
* then the current path is stored as unserialize($modSettings['attachmentUploadDir'])[$modSettings['currentAttachmentUploadDir']] |
|
1520
|
|
|
* - otherwise, the current path is $modSettings['attachmentUploadDir']. |
|
1521
|
|
|
* |
|
1522
|
|
|
* @package Attachments |
|
1523
|
|
|
* @return string |
|
1524
|
|
|
*/ |
|
1525
|
|
|
function getAttachmentPath() |
|
1526
|
|
|
{ |
|
1527
|
|
|
global $modSettings; |
|
1528
|
|
|
|
|
1529
|
|
|
// Make sure this thing exists and it is unserialized |
|
1530
|
|
|
if (empty($modSettings['attachmentUploadDir'])) |
|
1531
|
|
|
$attachmentDir = BOARDDIR . '/attachments'; |
|
1532
|
|
|
elseif (!empty($modSettings['currentAttachmentUploadDir']) && !is_array($modSettings['attachmentUploadDir']) && (@unserialize($modSettings['attachmentUploadDir']) !== false)) |
|
1533
|
|
|
{ |
|
1534
|
|
|
// @todo this is here to prevent the package manager to die when complete the installation of the patch (the new Util class has not yet been loaded so we need the normal one) |
|
1535
|
|
|
if (function_exists('Util::unserialize')) |
|
1536
|
|
|
{ |
|
1537
|
|
|
$attachmentDir = Util::unserialize($modSettings['attachmentUploadDir']); |
|
1538
|
|
|
} |
|
1539
|
|
|
else |
|
1540
|
|
|
{ |
|
1541
|
|
|
$attachmentDir = unserialize($modSettings['attachmentUploadDir']); |
|
1542
|
|
|
} |
|
1543
|
|
|
} |
|
1544
|
|
|
else |
|
1545
|
|
|
$attachmentDir = $modSettings['attachmentUploadDir']; |
|
1546
|
|
|
|
|
1547
|
|
|
return is_array($attachmentDir) ? $attachmentDir[$modSettings['currentAttachmentUploadDir']] : $attachmentDir; |
|
1548
|
|
|
} |
|
1549
|
|
|
|
|
1550
|
|
|
/** |
|
1551
|
|
|
* The avatars path: if custom avatar directory is set, that's it. |
|
1552
|
|
|
* Otherwise, it's attachments path. |
|
1553
|
|
|
* |
|
1554
|
|
|
* @package Attachments |
|
1555
|
|
|
* @return string |
|
1556
|
|
|
*/ |
|
1557
|
|
|
function getAvatarPath() |
|
1558
|
|
|
{ |
|
1559
|
|
|
global $modSettings; |
|
1560
|
|
|
|
|
1561
|
|
|
return empty($modSettings['custom_avatar_enabled']) ? getAttachmentPath() : $modSettings['custom_avatar_dir']; |
|
1562
|
|
|
} |
|
1563
|
|
|
|
|
1564
|
|
|
/** |
|
1565
|
|
|
* Little utility function for the $id_folder computation for attachments. |
|
1566
|
|
|
* |
|
1567
|
|
|
* What it does: |
|
1568
|
|
|
* |
|
1569
|
|
|
* - This returns the id of the folder where the attachment or avatar will be saved. |
|
1570
|
|
|
* - If multiple attachment directories are not enabled, this will be 1 by default. |
|
1571
|
|
|
* |
|
1572
|
|
|
* @package Attachments |
|
1573
|
|
|
* @return int 1 if multiple attachment directories are not enabled, |
|
1574
|
|
|
* or the id of the current attachment directory otherwise. |
|
1575
|
|
|
*/ |
|
1576
|
|
|
function getAttachmentPathID() |
|
1577
|
|
|
{ |
|
1578
|
|
|
global $modSettings; |
|
1579
|
|
|
|
|
1580
|
|
|
// utility function for the endless $id_folder computation for attachments. |
|
1581
|
|
|
return !empty($modSettings['currentAttachmentUploadDir']) ? $modSettings['currentAttachmentUploadDir'] : 1; |
|
1582
|
|
|
} |
|
1583
|
|
|
|
|
1584
|
|
|
/** |
|
1585
|
|
|
* Returns the ID of the folder avatars are currently saved in. |
|
1586
|
|
|
* |
|
1587
|
|
|
* @package Attachments |
|
1588
|
|
|
* @return int 1 if custom avatar directory is enabled, |
|
1589
|
|
|
* and the ID of the current attachment folder otherwise. |
|
1590
|
|
|
* NB: the latter could also be 1. |
|
1591
|
|
|
*/ |
|
1592
|
|
|
function getAvatarPathID() |
|
1593
|
|
|
{ |
|
1594
|
|
|
global $modSettings; |
|
1595
|
|
|
|
|
1596
|
|
|
// Little utility function for the endless $id_folder computation for avatars. |
|
1597
|
|
|
if (!empty($modSettings['custom_avatar_enabled'])) |
|
1598
|
|
|
return 1; |
|
1599
|
|
|
else |
|
1600
|
|
|
return getAttachmentPathID(); |
|
1601
|
|
|
} |
|
1602
|
|
|
|
|
1603
|
|
|
/** |
|
1604
|
|
|
* Get all attachments associated with a set of posts. |
|
1605
|
|
|
* |
|
1606
|
|
|
* What it does: |
|
1607
|
|
|
* - This does not check permissions. |
|
1608
|
|
|
* |
|
1609
|
|
|
* @package Attachments |
|
1610
|
|
|
* @param int[] $messages array of messages ids |
|
1611
|
|
|
* @param bool $includeUnapproved = false |
|
1612
|
|
|
* @param string|null $filter name of a callback function |
|
1613
|
|
|
* @param mixed[] $all_posters |
|
1614
|
|
|
*/ |
|
1615
|
|
|
function getAttachments($messages, $includeUnapproved = false, $filter = null, $all_posters = array()) |
|
1616
|
|
|
{ |
|
1617
|
|
|
global $modSettings; |
|
1618
|
|
|
|
|
1619
|
|
|
$db = database(); |
|
1620
|
|
|
|
|
1621
|
|
|
$attachments = array(); |
|
1622
|
|
|
$request = $db->query('', ' |
|
1623
|
|
|
SELECT |
|
1624
|
|
|
a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, COALESCE(a.size, 0) AS filesize, a.downloads, a.approved, |
|
1625
|
|
|
a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ', |
|
1626
|
|
|
COALESCE(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . ' |
|
1627
|
|
|
FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ' |
|
1628
|
|
|
LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . ' |
|
1629
|
|
|
WHERE a.id_msg IN ({array_int:message_list}) |
|
1630
|
|
|
AND a.attachment_type = {int:attachment_type}', |
|
1631
|
|
|
array( |
|
1632
|
|
|
'message_list' => $messages, |
|
1633
|
|
|
'attachment_type' => 0, |
|
1634
|
|
|
) |
|
1635
|
|
|
); |
|
1636
|
|
|
$temp = array(); |
|
1637
|
|
|
while ($row = $db->fetch_assoc($request)) |
|
1638
|
|
|
{ |
|
1639
|
|
|
if (!$row['approved'] && !$includeUnapproved && (empty($filter) || !call_user_func($filter, $row, $all_posters))) |
|
1640
|
|
|
continue; |
|
1641
|
|
|
|
|
1642
|
|
|
$temp[$row['id_attach']] = $row; |
|
1643
|
|
|
|
|
1644
|
|
|
if (!isset($attachments[$row['id_msg']])) |
|
1645
|
|
|
$attachments[$row['id_msg']] = array(); |
|
1646
|
|
|
} |
|
1647
|
|
|
$db->free_result($request); |
|
1648
|
|
|
|
|
1649
|
|
|
// This is better than sorting it with the query... |
|
1650
|
|
|
ksort($temp); |
|
1651
|
|
|
|
|
1652
|
|
|
foreach ($temp as $row) |
|
1653
|
|
|
$attachments[$row['id_msg']][] = $row; |
|
1654
|
|
|
|
|
1655
|
|
|
return $attachments; |
|
1656
|
|
|
} |
|
1657
|
|
|
|
|
1658
|
|
|
/** |
|
1659
|
|
|
* Get all avatars information... as long as they're in default directory still? |
|
1660
|
|
|
* Not currently used |
|
1661
|
|
|
* |
|
1662
|
|
|
* @deprecated since 1.0 |
|
1663
|
|
|
* |
|
1664
|
|
|
* @return mixed[] avatars information |
|
1665
|
|
|
*/ |
|
1666
|
|
|
function getAvatarsDefault() |
|
1667
|
|
|
{ |
|
1668
|
|
|
$db = database(); |
|
1669
|
|
|
|
|
1670
|
|
|
return $db->fetchQuery(' |
|
1671
|
|
|
SELECT id_attach, id_folder, id_member, filename, file_hash |
|
1672
|
|
|
FROM {db_prefix}attachments |
|
1673
|
|
|
WHERE attachment_type = {int:attachment_type} |
|
1674
|
|
|
AND id_member > {int:guest_id_member}', |
|
1675
|
|
|
array( |
|
1676
|
|
|
'attachment_type' => 0, |
|
1677
|
|
|
'guest_id_member' => 0, |
|
1678
|
|
|
) |
|
1679
|
|
|
); |
|
1680
|
|
|
} |
|
1681
|
|
|
|
|
1682
|
|
|
/** |
|
1683
|
|
|
* Recursive function to retrieve server-stored avatar files |
|
1684
|
|
|
* |
|
1685
|
|
|
* @package Attachments |
|
1686
|
|
|
* @param string $directory |
|
1687
|
|
|
* @param int $level |
|
1688
|
|
|
* @return array |
|
1689
|
|
|
*/ |
|
1690
|
|
|
function getServerStoredAvatars($directory, $level) |
|
1691
|
|
|
{ |
|
1692
|
|
|
global $context, $txt, $modSettings; |
|
1693
|
|
|
|
|
1694
|
|
|
$result = array(); |
|
1695
|
|
|
|
|
1696
|
|
|
// Open the directory.. |
|
1697
|
|
|
$dir = dir($modSettings['avatar_directory'] . (!empty($directory) ? '/' : '') . $directory); |
|
1698
|
|
|
$dirs = array(); |
|
1699
|
|
|
$files = array(); |
|
1700
|
|
|
|
|
1701
|
|
|
if (!$dir) |
|
1702
|
|
|
return array(); |
|
1703
|
|
|
|
|
1704
|
|
|
while ($line = $dir->read()) |
|
1705
|
|
|
{ |
|
1706
|
|
|
if (in_array($line, array('.', '..', 'blank.png', 'index.php'))) |
|
1707
|
|
|
continue; |
|
1708
|
|
|
|
|
1709
|
|
|
if (is_dir($modSettings['avatar_directory'] . '/' . $directory . (!empty($directory) ? '/' : '') . $line)) |
|
1710
|
|
|
$dirs[] = $line; |
|
1711
|
|
|
else |
|
1712
|
|
|
$files[] = $line; |
|
1713
|
|
|
} |
|
1714
|
|
|
$dir->close(); |
|
1715
|
|
|
|
|
1716
|
|
|
// Sort the results... |
|
1717
|
|
|
natcasesort($dirs); |
|
1718
|
|
|
natcasesort($files); |
|
1719
|
|
|
|
|
1720
|
|
|
if ($level == 0) |
|
1721
|
|
|
{ |
|
1722
|
|
|
$result[] = array( |
|
1723
|
|
|
'filename' => 'blank.png', |
|
1724
|
|
|
'checked' => in_array($context['member']['avatar']['server_pic'], array('', 'blank.png')), |
|
1725
|
|
|
'name' => $txt['no_pic'], |
|
1726
|
|
|
'is_dir' => false |
|
1727
|
|
|
); |
|
1728
|
|
|
} |
|
1729
|
|
|
|
|
1730
|
|
|
foreach ($dirs as $line) |
|
1731
|
|
|
{ |
|
1732
|
|
|
$tmp = getServerStoredAvatars($directory . (!empty($directory) ? '/' : '') . $line, $level + 1); |
|
1733
|
|
|
if (!empty($tmp)) |
|
1734
|
|
|
$result[] = array( |
|
1735
|
|
|
'filename' => htmlspecialchars($line, ENT_COMPAT, 'UTF-8'), |
|
1736
|
|
|
'checked' => strpos($context['member']['avatar']['server_pic'], $line . '/') !== false, |
|
1737
|
|
|
'name' => '[' . htmlspecialchars(str_replace('_', ' ', $line), ENT_COMPAT, 'UTF-8') . ']', |
|
1738
|
|
|
'is_dir' => true, |
|
1739
|
|
|
'files' => $tmp |
|
1740
|
|
|
); |
|
1741
|
|
|
unset($tmp); |
|
1742
|
|
|
} |
|
1743
|
|
|
|
|
1744
|
|
|
foreach ($files as $line) |
|
1745
|
|
|
{ |
|
1746
|
|
|
$filename = substr($line, 0, (strlen($line) - strlen(strrchr($line, '.')))); |
|
1747
|
|
|
$extension = substr(strrchr($line, '.'), 1); |
|
1748
|
|
|
|
|
1749
|
|
|
// Make sure it is an image. |
|
1750
|
|
|
if (getValidMimeImageType($extension) === '') |
|
1751
|
|
|
continue; |
|
1752
|
|
|
|
|
1753
|
|
|
$result[] = array( |
|
1754
|
|
|
'filename' => htmlspecialchars($line, ENT_COMPAT, 'UTF-8'), |
|
1755
|
|
|
'checked' => $line == $context['member']['avatar']['server_pic'], |
|
1756
|
|
|
'name' => htmlspecialchars(str_replace('_', ' ', $filename), ENT_COMPAT, 'UTF-8'), |
|
1757
|
|
|
'is_dir' => false |
|
1758
|
|
|
); |
|
1759
|
|
|
if ($level == 1) |
|
1760
|
|
|
$context['avatar_list'][] = $directory . '/' . $line; |
|
1761
|
|
|
} |
|
1762
|
|
|
|
|
1763
|
|
|
return $result; |
|
1764
|
|
|
} |
|
1765
|
|
|
|
|
1766
|
|
|
/** |
|
1767
|
|
|
* Update an attachment's thumbnail |
|
1768
|
|
|
* |
|
1769
|
|
|
* @package Attachments |
|
1770
|
|
|
* @param string $filename |
|
1771
|
|
|
* @param int $id_attach |
|
1772
|
|
|
* @param int $id_msg |
|
1773
|
|
|
* @param int $old_id_thumb = 0 |
|
1774
|
|
|
* @return array The updated information |
|
1775
|
|
|
*/ |
|
1776
|
|
|
function updateAttachmentThumbnail($filename, $id_attach, $id_msg, $old_id_thumb = 0, $real_filename = '') |
|
1777
|
|
|
{ |
|
1778
|
|
|
global $modSettings; |
|
1779
|
|
|
|
|
1780
|
|
|
$attachment = array('id_attach' => $id_attach); |
|
1781
|
|
|
|
|
1782
|
|
|
require_once(SUBSDIR . '/Graphics.subs.php'); |
|
1783
|
|
|
if (createThumbnail($filename, $modSettings['attachmentThumbWidth'], $modSettings['attachmentThumbHeight'])) |
|
1784
|
|
|
{ |
|
1785
|
|
|
// So what folder are we putting this image in? |
|
1786
|
|
|
$id_folder_thumb = getAttachmentPathID(); |
|
1787
|
|
|
|
|
1788
|
|
|
// Calculate the size of the created thumbnail. |
|
1789
|
|
|
$size = elk_getimagesize($filename . '_thumb'); |
|
1790
|
|
|
list ($attachment['thumb_width'], $attachment['thumb_height']) = $size; |
|
1791
|
|
|
$thumb_size = filesize($filename . '_thumb'); |
|
1792
|
|
|
|
|
1793
|
|
|
// Figure out the mime type. |
|
1794
|
|
|
if (!empty($size['mime'])) |
|
1795
|
|
|
{ |
|
1796
|
|
|
$thumb_mime = $size['mime']; |
|
1797
|
|
|
} |
|
1798
|
|
|
else |
|
1799
|
|
|
{ |
|
1800
|
|
|
$thumb_mime = getValidMimeImageType($size[2]); |
|
1801
|
|
|
} |
|
1802
|
|
|
$thumb_ext = substr($thumb_mime, strpos($thumb_mime, '/') + 1); |
|
1803
|
|
|
|
|
1804
|
|
|
$thumb_filename = (!empty($real_filename) ? $real_filename : $filename) . '_thumb'; |
|
1805
|
|
|
$thumb_hash = getAttachmentFilename($thumb_filename, 0, null, true); |
|
1806
|
|
|
|
|
1807
|
|
|
$db = database(); |
|
1808
|
|
|
|
|
1809
|
|
|
// Add this beauty to the database. |
|
1810
|
|
|
$db->insert('', |
|
1811
|
|
|
'{db_prefix}attachments', |
|
1812
|
|
|
array('id_folder' => 'int', 'id_msg' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'size' => 'int', 'width' => 'int', 'height' => 'int', 'fileext' => 'string-8', 'mime_type' => 'string-255'), |
|
1813
|
|
|
array($id_folder_thumb, $id_msg, 3, $thumb_filename, $thumb_hash, (int) $thumb_size, (int) $attachment['thumb_width'], (int) $attachment['thumb_height'], $thumb_ext, $thumb_mime), |
|
1814
|
|
|
array('id_attach') |
|
1815
|
|
|
); |
|
1816
|
|
|
|
|
1817
|
|
|
$attachment['id_thumb'] = $db->insert_id('{db_prefix}attachments', 'id_attach'); |
|
1818
|
|
|
if (!empty($attachment['id_thumb'])) |
|
1819
|
|
|
{ |
|
1820
|
|
|
$db->query('', ' |
|
1821
|
|
|
UPDATE {db_prefix}attachments |
|
1822
|
|
|
SET id_thumb = {int:id_thumb} |
|
1823
|
|
|
WHERE id_attach = {int:id_attach}', |
|
1824
|
|
|
array( |
|
1825
|
|
|
'id_thumb' => $attachment['id_thumb'], |
|
1826
|
|
|
'id_attach' => $attachment['id_attach'], |
|
1827
|
|
|
) |
|
1828
|
|
|
); |
|
1829
|
|
|
|
|
1830
|
|
|
$thumb_realname = getAttachmentFilename($thumb_filename, $attachment['id_thumb'], $id_folder_thumb, false, $thumb_hash); |
|
1831
|
|
|
rename($filename . '_thumb', $thumb_realname); |
|
1832
|
|
|
|
|
1833
|
|
|
// Do we need to remove an old thumbnail? |
|
1834
|
|
|
if (!empty($old_id_thumb)) |
|
1835
|
|
|
{ |
|
1836
|
|
|
require_once(SUBSDIR . '/ManageAttachments.subs.php'); |
|
1837
|
|
|
removeAttachments(array('id_attach' => $old_id_thumb), '', false, false); |
|
1838
|
|
|
} |
|
1839
|
|
|
} |
|
1840
|
|
|
} |
|
1841
|
|
|
|
|
1842
|
|
|
return $attachment; |
|
1843
|
|
|
} |
|
1844
|
|
|
|
|
1845
|
|
|
/** |
|
1846
|
|
|
* Compute and return the total size of attachments to a single message. |
|
1847
|
|
|
* |
|
1848
|
|
|
* @package Attachments |
|
1849
|
|
|
* @param int $id_msg |
|
1850
|
|
|
* @param bool $include_count = true if true, it also returns the attachments count |
|
1851
|
|
|
*/ |
|
1852
|
|
|
function attachmentsSizeForMessage($id_msg, $include_count = true) |
|
1853
|
|
|
{ |
|
1854
|
|
|
$db = database(); |
|
1855
|
|
|
|
|
1856
|
|
|
if ($include_count) |
|
1857
|
|
|
{ |
|
1858
|
|
|
$request = $db->query('', ' |
|
1859
|
|
|
SELECT COUNT(*), SUM(size) |
|
1860
|
|
|
FROM {db_prefix}attachments |
|
1861
|
|
|
WHERE id_msg = {int:id_msg} |
|
1862
|
|
|
AND attachment_type = {int:attachment_type}', |
|
1863
|
|
|
array( |
|
1864
|
|
|
'id_msg' => $id_msg, |
|
1865
|
|
|
'attachment_type' => 0, |
|
1866
|
|
|
) |
|
1867
|
|
|
); |
|
1868
|
|
|
} |
|
1869
|
|
|
else |
|
1870
|
|
|
{ |
|
1871
|
|
|
$request = $db->query('', ' |
|
1872
|
|
|
SELECT COUNT(*) |
|
1873
|
|
|
FROM {db_prefix}attachments |
|
1874
|
|
|
WHERE id_msg = {int:id_msg} |
|
1875
|
|
|
AND attachment_type = {int:attachment_type}', |
|
1876
|
|
|
array( |
|
1877
|
|
|
'id_msg' => $id_msg, |
|
1878
|
|
|
'attachment_type' => 0, |
|
1879
|
|
|
) |
|
1880
|
|
|
); |
|
1881
|
|
|
} |
|
1882
|
|
|
$size = $db->fetch_row($request); |
|
1883
|
|
|
$db->free_result($request); |
|
1884
|
|
|
|
|
1885
|
|
|
return $size; |
|
1886
|
|
|
} |
|
1887
|
|
|
|
|
1888
|
|
|
/** |
|
1889
|
|
|
* This loads an attachment's contextual data including, most importantly, its size if it is an image. |
|
1890
|
|
|
* |
|
1891
|
|
|
* What it does: |
|
1892
|
|
|
* |
|
1893
|
|
|
* - Pre-condition: $attachments array to have been filled with the proper attachment data, as Display() does. |
|
1894
|
|
|
* - It requires the view_attachments permission to calculate image size. |
|
1895
|
|
|
* - It attempts to keep the "aspect ratio" of the posted image in line, even if it has to be resized by |
|
1896
|
|
|
* the max_image_width and max_image_height settings. |
|
1897
|
|
|
* |
|
1898
|
|
|
* @todo change this pre-condition, too fragile and error-prone. |
|
1899
|
|
|
* |
|
1900
|
|
|
* @package Attachments |
|
1901
|
|
|
* @param int $id_msg message number to load attachments for |
|
1902
|
|
|
* @return array of attachments |
|
1903
|
|
|
*/ |
|
1904
|
|
|
function loadAttachmentContext($id_msg) |
|
1905
|
|
|
{ |
|
1906
|
|
|
global $attachments, $modSettings, $scripturl, $topic; |
|
1907
|
|
|
|
|
1908
|
|
|
// Set up the attachment info - based on code by Meriadoc. |
|
1909
|
|
|
$attachmentData = array(); |
|
1910
|
|
|
$have_unapproved = false; |
|
1911
|
|
|
if (isset($attachments[$id_msg]) && !empty($modSettings['attachmentEnable'])) |
|
1912
|
|
|
{ |
|
1913
|
|
|
foreach ($attachments[$id_msg] as $i => $attachment) |
|
1914
|
|
|
{ |
|
1915
|
|
|
$attachmentData[$i] = array( |
|
1916
|
|
|
'id' => $attachment['id_attach'], |
|
1917
|
|
|
'name' => preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($attachment['filename'], ENT_COMPAT, 'UTF-8')), |
|
1918
|
|
|
'downloads' => $attachment['downloads'], |
|
1919
|
|
|
'size' => byte_format($attachment['filesize']), |
|
1920
|
|
|
'byte_size' => $attachment['filesize'], |
|
1921
|
|
|
'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_attach'], |
|
1922
|
|
|
'link' => '<a href="' . $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_attach'] . '">' . htmlspecialchars($attachment['filename'], ENT_COMPAT, 'UTF-8') . '</a>', |
|
1923
|
|
|
'is_image' => !empty($attachment['width']) && !empty($attachment['height']) && !empty($modSettings['attachmentShowImages']), |
|
1924
|
|
|
'is_approved' => $attachment['approved'], |
|
1925
|
|
|
'file_hash' => $attachment['file_hash'], |
|
1926
|
|
|
); |
|
1927
|
|
|
|
|
1928
|
|
|
// If something is unapproved we'll note it so we can sort them. |
|
1929
|
|
|
if (!$attachment['approved']) |
|
1930
|
|
|
$have_unapproved = true; |
|
1931
|
|
|
|
|
1932
|
|
|
if (!$attachmentData[$i]['is_image']) |
|
1933
|
|
|
continue; |
|
1934
|
|
|
|
|
1935
|
|
|
$attachmentData[$i]['real_width'] = $attachment['width']; |
|
1936
|
|
|
$attachmentData[$i]['width'] = $attachment['width']; |
|
1937
|
|
|
$attachmentData[$i]['real_height'] = $attachment['height']; |
|
1938
|
|
|
$attachmentData[$i]['height'] = $attachment['height']; |
|
1939
|
|
|
|
|
1940
|
|
|
// Let's see, do we want thumbs? |
|
1941
|
|
|
if (!empty($modSettings['attachmentThumbnails']) && !empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight']) && ($attachment['width'] > $modSettings['attachmentThumbWidth'] || $attachment['height'] > $modSettings['attachmentThumbHeight']) && strlen($attachment['filename']) < 249) |
|
1942
|
|
|
{ |
|
1943
|
|
|
// A proper thumb doesn't exist yet? Create one! Or, it needs update. |
|
1944
|
|
|
if (empty($attachment['id_thumb']) || $attachment['thumb_width'] > $modSettings['attachmentThumbWidth'] || $attachment['thumb_height'] > $modSettings['attachmentThumbHeight'] || ($attachment['thumb_width'] < $modSettings['attachmentThumbWidth'] && $attachment['thumb_height'] < $modSettings['attachmentThumbHeight'])) |
|
1945
|
|
|
{ |
|
1946
|
|
|
$filename = getAttachmentFilename($attachment['filename'], $attachment['id_attach'], $attachment['id_folder'], false, $attachment['file_hash']); |
|
1947
|
|
|
$attachment = array_merge($attachment, updateAttachmentThumbnail($filename, $attachment['id_attach'], $id_msg, $attachment['id_thumb'], $attachment['filename'])); |
|
1948
|
|
|
} |
|
1949
|
|
|
|
|
1950
|
|
|
// Only adjust dimensions on successful thumbnail creation. |
|
1951
|
|
|
if (!empty($attachment['thumb_width']) && !empty($attachment['thumb_height'])) |
|
1952
|
|
|
{ |
|
1953
|
|
|
$attachmentData[$i]['width'] = $attachment['thumb_width']; |
|
1954
|
|
|
$attachmentData[$i]['height'] = $attachment['thumb_height']; |
|
1955
|
|
|
} |
|
1956
|
|
|
} |
|
1957
|
|
|
|
|
1958
|
|
|
if (!empty($attachment['id_thumb'])) |
|
1959
|
|
|
{ |
|
1960
|
|
|
$attachmentData[$i]['thumbnail'] = array( |
|
1961
|
|
|
'id' => $attachment['id_thumb'], |
|
1962
|
|
|
'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_thumb'] . ';image', |
|
1963
|
|
|
); |
|
1964
|
|
|
} |
|
1965
|
|
|
$attachmentData[$i]['thumbnail']['has_thumb'] = !empty($attachment['id_thumb']); |
|
1966
|
|
|
|
|
1967
|
|
|
// If thumbnails are disabled, check the maximum size of the image. |
|
1968
|
|
|
if (!$attachmentData[$i]['thumbnail']['has_thumb'] && ((!empty($modSettings['max_image_width']) && $attachment['width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachment['height'] > $modSettings['max_image_height']))) |
|
1969
|
|
|
{ |
|
1970
|
|
|
if (!empty($modSettings['max_image_width']) && (empty($modSettings['max_image_height']) || $attachment['height'] * $modSettings['max_image_width'] / $attachment['width'] <= $modSettings['max_image_height'])) |
|
1971
|
|
|
{ |
|
1972
|
|
|
$attachmentData[$i]['width'] = $modSettings['max_image_width']; |
|
1973
|
|
|
$attachmentData[$i]['height'] = floor($attachment['height'] * $modSettings['max_image_width'] / $attachment['width']); |
|
1974
|
|
|
} |
|
1975
|
|
|
elseif (!empty($modSettings['max_image_width'])) |
|
1976
|
|
|
{ |
|
1977
|
|
|
$attachmentData[$i]['width'] = floor($attachment['width'] * $modSettings['max_image_height'] / $attachment['height']); |
|
1978
|
|
|
$attachmentData[$i]['height'] = $modSettings['max_image_height']; |
|
1979
|
|
|
} |
|
1980
|
|
|
} |
|
1981
|
|
|
elseif ($attachmentData[$i]['thumbnail']['has_thumb']) |
|
1982
|
|
|
{ |
|
1983
|
|
|
// Data attributes for use in expandThumb |
|
1984
|
|
|
$attachmentData[$i]['thumbnail']['lightbox'] = 'data-lightboxmessage="' . $id_msg . '" data-lightboximage="' . $attachment['id_attach'] . '"'; |
|
1985
|
|
|
|
|
1986
|
|
|
// If the image is too large to show inline, make it a popup. |
|
1987
|
|
|
// @todo this needs to be removed or depreciated |
|
1988
|
|
|
if (((!empty($modSettings['max_image_width']) && $attachmentData[$i]['real_width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachmentData[$i]['real_height'] > $modSettings['max_image_height']))) |
|
1989
|
|
|
$attachmentData[$i]['thumbnail']['javascript'] = 'return reqWin(\'' . $attachmentData[$i]['href'] . ';image\', ' . ($attachment['width'] + 20) . ', ' . ($attachment['height'] + 20) . ', true);'; |
|
1990
|
|
|
else |
|
1991
|
|
|
$attachmentData[$i]['thumbnail']['javascript'] = 'return expandThumb(' . $attachment['id_attach'] . ');'; |
|
1992
|
|
|
} |
|
1993
|
|
|
|
|
1994
|
|
|
if (!$attachmentData[$i]['thumbnail']['has_thumb']) |
|
1995
|
|
|
$attachmentData[$i]['downloads']++; |
|
1996
|
|
|
} |
|
1997
|
|
|
} |
|
1998
|
|
|
|
|
1999
|
|
|
// Do we need to instigate a sort? |
|
2000
|
|
|
if ($have_unapproved) |
|
2001
|
|
|
usort($attachmentData, 'approved_attach_sort'); |
|
2002
|
|
|
|
|
2003
|
|
|
return $attachmentData; |
|
2004
|
|
|
} |
|
2005
|
|
|
|
|
2006
|
|
|
/** |
|
2007
|
|
|
* A sort function for putting unapproved attachments first. |
|
2008
|
|
|
* |
|
2009
|
|
|
* @package Attachments |
|
2010
|
|
|
* @param mixed[] $a |
|
2011
|
|
|
* @param mixed[] $b |
|
2012
|
|
|
* @return int -1, 0, 1 |
|
2013
|
|
|
*/ |
|
2014
|
|
|
function approved_attach_sort($a, $b) |
|
2015
|
|
|
{ |
|
2016
|
|
|
if ($a['is_approved'] == $b['is_approved']) |
|
2017
|
|
|
return 0; |
|
2018
|
|
|
|
|
2019
|
|
|
return $a['is_approved'] > $b['is_approved'] ? -1 : 1; |
|
2020
|
|
|
} |
|
2021
|
|
|
|
|
2022
|
|
|
/** |
|
2023
|
|
|
* Callback filter for the retrieval of attachments. |
|
2024
|
|
|
* |
|
2025
|
|
|
* What it does: |
|
2026
|
|
|
* This function returns false when: |
|
2027
|
|
|
* - the attachment is unapproved, and |
|
2028
|
|
|
* - the viewer is not the poster of the message where the attachment is |
|
2029
|
|
|
* |
|
2030
|
|
|
* @package Attachments |
|
2031
|
|
|
* @param mixed[] $attachment_info |
|
2032
|
|
|
* @param mixed[] $all_posters |
|
2033
|
|
|
*/ |
|
2034
|
|
|
function filter_accessible_attachment($attachment_info, $all_posters) |
|
2035
|
|
|
{ |
|
2036
|
|
|
global $user_info; |
|
2037
|
|
|
|
|
2038
|
|
|
return !(!$attachment_info['approved'] && (!isset($all_posters[$attachment_info['id_msg']]) || $all_posters[$attachment_info['id_msg']] != $user_info['id'])); |
|
2039
|
|
|
} |
|
2040
|
|
|
|
|
2041
|
|
|
/** |
|
2042
|
|
|
* Older attachments may still use this function. |
|
2043
|
|
|
* |
|
2044
|
|
|
* @package Attachments |
|
2045
|
|
|
* @param string $filename |
|
2046
|
|
|
* @param int $attachment_id |
|
2047
|
|
|
* @param string|null $dir |
|
2048
|
|
|
* @param boolean $new |
|
2049
|
|
|
*/ |
|
2050
|
|
|
function getLegacyAttachmentFilename($filename, $attachment_id, $dir = null, $new = false) |
|
2051
|
|
|
{ |
|
2052
|
|
|
global $modSettings; |
|
2053
|
|
|
|
|
2054
|
|
|
$clean_name = $filename; |
|
2055
|
|
|
|
|
2056
|
|
|
// Sorry, no spaces, dots, or anything else but letters allowed. |
|
2057
|
|
|
$clean_name = preg_replace(array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $clean_name); |
|
2058
|
|
|
|
|
2059
|
|
|
$enc_name = $attachment_id . '_' . strtr($clean_name, '.', '_') . md5($clean_name); |
|
2060
|
|
|
$clean_name = preg_replace('~\.[\.]+~', '.', $clean_name); |
|
2061
|
|
|
|
|
2062
|
|
|
if (empty($attachment_id) || ($new && empty($modSettings['attachmentEncryptFilenames']))) |
|
2063
|
|
|
return $clean_name; |
|
2064
|
|
|
elseif ($new) |
|
2065
|
|
|
return $enc_name; |
|
2066
|
|
|
|
|
2067
|
|
|
// Are we using multiple directories? |
|
2068
|
|
|
if (!empty($modSettings['currentAttachmentUploadDir'])) |
|
2069
|
|
|
{ |
|
2070
|
|
|
if (!is_array($modSettings['attachmentUploadDir'])) |
|
2071
|
|
|
$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']); |
|
2072
|
|
|
$path = $modSettings['attachmentUploadDir'][$dir]; |
|
2073
|
|
|
} |
|
2074
|
|
|
else |
|
2075
|
|
|
$path = $modSettings['attachmentUploadDir']; |
|
2076
|
|
|
|
|
2077
|
|
|
if (file_exists($path . '/' . $enc_name)) |
|
2078
|
|
|
$filename = $path . '/' . $enc_name; |
|
2079
|
|
|
else |
|
2080
|
|
|
$filename = $path . '/' . $clean_name; |
|
2081
|
|
|
|
|
2082
|
|
|
return $filename; |
|
2083
|
|
|
} |
|
2084
|
|
|
|
|
2085
|
|
|
/** |
|
2086
|
|
|
* Binds a set of attachments to a message. |
|
2087
|
|
|
* |
|
2088
|
|
|
* @package Attachments |
|
2089
|
|
|
* @param int $id_msg |
|
2090
|
|
|
* @param int[] $attachment_ids |
|
2091
|
|
|
*/ |
|
2092
|
|
|
function bindMessageAttachments($id_msg, $attachment_ids) |
|
2093
|
|
|
{ |
|
2094
|
|
|
$db = database(); |
|
2095
|
|
|
|
|
2096
|
|
|
$db->query('', ' |
|
2097
|
|
|
UPDATE {db_prefix}attachments |
|
2098
|
|
|
SET id_msg = {int:id_msg} |
|
2099
|
|
|
WHERE id_attach IN ({array_int:attachment_list})', |
|
2100
|
|
|
array( |
|
2101
|
|
|
'attachment_list' => $attachment_ids, |
|
2102
|
|
|
'id_msg' => $id_msg, |
|
2103
|
|
|
) |
|
2104
|
|
|
); |
|
2105
|
|
|
} |
|
2106
|
|
|
|
|
2107
|
|
|
/** |
|
2108
|
|
|
* Get an attachment's encrypted filename. If $new is true, won't check for file existence. |
|
2109
|
|
|
* |
|
2110
|
|
|
* - If new is set returns a hash for the db |
|
2111
|
|
|
* - If no file hash is supplied, determines one and returns it |
|
2112
|
|
|
* - Returns the path to the file |
|
2113
|
|
|
* |
|
2114
|
|
|
* @todo this currently returns the hash if new, and the full filename otherwise. |
|
2115
|
|
|
* Something messy like that. |
|
2116
|
|
|
* @todo and of course everything relies on this behavior and work around it. :P. |
|
2117
|
|
|
* Converters included. |
|
2118
|
|
|
* |
|
2119
|
|
|
* @param string $filename The name of the file |
|
2120
|
|
|
* @param int|null $attachment_id The ID of the attachment |
|
2121
|
|
|
* @param string|null $dir Which directory it should be in (null to use current) |
|
2122
|
|
|
* @param bool $new If this is a new attachment, if so just returns a hash |
|
2123
|
|
|
* @param string $file_hash The file hash |
|
2124
|
|
|
*/ |
|
2125
|
|
|
function getAttachmentFilename($filename, $attachment_id, $dir = null, $new = false, $file_hash = '') |
|
2126
|
|
|
{ |
|
2127
|
|
|
global $modSettings; |
|
2128
|
|
|
|
|
2129
|
|
|
// Just make up a nice hash... |
|
2130
|
|
|
if ($new) |
|
2131
|
|
|
return hash('sha1', hash('md5', $filename . time()) . mt_rand()); |
|
2132
|
|
|
|
|
2133
|
|
|
// In case of files from the old system, do a legacy call. |
|
2134
|
|
|
if (empty($file_hash)) |
|
2135
|
|
|
{ |
|
2136
|
|
|
return getLegacyAttachmentFilename($filename, $attachment_id, $dir, $new); |
|
2137
|
|
|
} |
|
2138
|
|
|
|
|
2139
|
|
|
// Are we using multiple directories? |
|
2140
|
|
|
if (!empty($modSettings['currentAttachmentUploadDir'])) |
|
2141
|
|
|
{ |
|
2142
|
|
|
if (!is_array($modSettings['attachmentUploadDir'])) |
|
2143
|
|
|
$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']); |
|
2144
|
|
|
$path = isset($modSettings['attachmentUploadDir'][$dir]) ? $modSettings['attachmentUploadDir'][$dir] : $modSettings['basedirectory_for_attachments']; |
|
2145
|
|
|
} |
|
2146
|
|
|
else |
|
2147
|
|
|
$path = $modSettings['attachmentUploadDir']; |
|
2148
|
|
|
|
|
2149
|
|
|
return $path . '/' . $attachment_id . '_' . $file_hash . '.elk'; |
|
2150
|
|
|
} |
|
2151
|
|
|
|
|
2152
|
|
|
/** |
|
2153
|
|
|
* Returns the board and the topic the attachment belongs to. |
|
2154
|
|
|
* |
|
2155
|
|
|
* @package Attachments |
|
2156
|
|
|
* @param int $id_attach |
|
2157
|
|
|
* @return int[]|boolean on fail else an array of id_board, id_topic |
|
2158
|
|
|
*/ |
|
2159
|
|
|
function getAttachmentPosition($id_attach) |
|
2160
|
|
|
{ |
|
2161
|
|
|
$db = database(); |
|
2162
|
|
|
|
|
2163
|
|
|
// Make sure this attachment is on this board. |
|
2164
|
|
|
$request = $db->query('', ' |
|
2165
|
|
|
SELECT m.id_board, m.id_topic |
|
2166
|
|
|
FROM {db_prefix}attachments AS a |
|
2167
|
|
|
LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) |
|
2168
|
|
|
LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) |
|
2169
|
|
|
WHERE a.id_attach = {int:attach} |
|
2170
|
|
|
AND {query_see_board} |
|
2171
|
|
|
LIMIT 1', |
|
2172
|
|
|
array( |
|
2173
|
|
|
'attach' => $id_attach, |
|
2174
|
|
|
) |
|
2175
|
|
|
); |
|
2176
|
|
|
|
|
2177
|
|
|
$attachmentData = $db->fetch_assoc($request); |
|
2178
|
|
|
$db->free_result($request); |
|
2179
|
|
|
|
|
2180
|
|
|
if (empty($attachmentData)) |
|
2181
|
|
|
{ |
|
2182
|
|
|
return false; |
|
2183
|
|
|
} |
|
2184
|
|
|
else |
|
2185
|
|
|
{ |
|
2186
|
|
|
return $attachmentData; |
|
2187
|
|
|
} |
|
2188
|
|
|
} |
|
2189
|
|
|
|
|
2190
|
|
|
/** |
|
2191
|
|
|
* Simple wrapper for getimagesize |
|
2192
|
|
|
* |
|
2193
|
|
|
* @param string $file |
|
2194
|
|
|
* @param string|boolean $error return array or false on error |
|
2195
|
|
|
* |
|
2196
|
|
|
* @return array|boolean |
|
2197
|
|
|
*/ |
|
2198
|
|
|
function elk_getimagesize($file, $error = 'array') |
|
2199
|
|
|
{ |
|
2200
|
|
|
$sizes = @getimagesize($file); |
|
2201
|
|
|
|
|
2202
|
|
|
// Can't get it, what shall we return |
|
2203
|
|
|
if (empty($sizes)) |
|
2204
|
|
|
{ |
|
2205
|
|
|
if ($error === 'array') |
|
2206
|
|
|
{ |
|
2207
|
|
|
$sizes = array(-1, -1, -1); |
|
2208
|
|
|
} |
|
2209
|
|
|
else |
|
2210
|
|
|
{ |
|
2211
|
|
|
$sizes = false; |
|
2212
|
|
|
} |
|
2213
|
|
|
} |
|
2214
|
|
|
|
|
2215
|
|
|
return $sizes; |
|
2216
|
|
|
} |
|
2217
|
|
|
|
|
2218
|
|
|
/** |
|
2219
|
|
|
* Checks if we have a known and support mime-type for which we have a thumbnail image |
|
2220
|
|
|
* |
|
2221
|
|
|
* @param string $file_ext |
|
2222
|
|
|
* @param bool $url |
|
2223
|
|
|
* |
|
2224
|
|
|
* @return bool|string |
|
2225
|
|
|
*/ |
|
2226
|
|
|
function returnMimeThumb($file_ext, $url = false) |
|
2227
|
|
|
{ |
|
2228
|
|
|
global $settings; |
|
2229
|
|
|
|
|
2230
|
|
|
// These are not meant to be exhaustive, just some of the most common attached on a forum |
|
2231
|
|
|
static $generics = array( |
|
2232
|
|
|
'arc' => array('tgz', 'zip', 'rar', '7z', 'gz'), |
|
2233
|
|
|
'doc' =>array('doc', 'docx', 'wpd', 'odt'), |
|
2234
|
|
|
'sound' => array('wav', 'mp3', 'pcm', 'aiff', 'wma', 'm4a'), |
|
2235
|
|
|
'video' => array('mp4', 'mgp', 'mpeg', 'mp4', 'wmv', 'flv', 'aiv', 'mov', 'swf'), |
|
2236
|
|
|
'txt' => array('rtf', 'txt', 'log'), |
|
2237
|
|
|
'presentation' => array('ppt', 'pps', 'odp'), |
|
2238
|
|
|
'spreadsheet' => array('xls', 'xlr', 'ods'), |
|
2239
|
|
|
'web' => array('html', 'htm') |
|
2240
|
|
|
); |
|
2241
|
|
|
foreach ($generics as $generic_extension => $generic_types) |
|
2242
|
|
|
{ |
|
2243
|
|
|
if (in_array($file_ext, $generic_types)) |
|
2244
|
|
|
{ |
|
2245
|
|
|
$file_ext = $generic_extension; |
|
2246
|
|
|
break; |
|
2247
|
|
|
} |
|
2248
|
|
|
} |
|
2249
|
|
|
|
|
2250
|
|
|
static $distinct = array('arc', 'doc', 'sound', 'video', 'txt', 'presentation', 'spreadsheet', 'web', |
|
2251
|
|
|
'c', 'cpp', 'css', 'csv', 'java', 'js', 'pdf', 'php', 'sql', 'xml'); |
|
2252
|
|
|
|
|
2253
|
|
|
if (empty($settings)) |
|
2254
|
|
|
{ |
|
2255
|
|
|
loadEssentialThemeData(); |
|
2256
|
|
|
} |
|
2257
|
|
|
|
|
2258
|
|
|
// Return the mine thumbnail if it exists or just the default |
|
2259
|
|
|
if (!in_array($file_ext, $distinct) || !file_exists($settings['theme_dir'] . '/images/mime_images/' . $file_ext . '.png')) |
|
2260
|
|
|
{ |
|
2261
|
|
|
$file_ext = 'default'; |
|
2262
|
|
|
} |
|
2263
|
|
|
|
|
2264
|
|
|
$location = $url ? $settings['theme_url'] : $settings['theme_dir']; |
|
2265
|
|
|
$filename = $location . '/images/mime_images/' . $file_ext . '.png'; |
|
2266
|
|
|
|
|
2267
|
|
|
return $filename; |
|
2268
|
|
|
} |
|
2269
|
|
|
|
|
2270
|
|
|
/** |
|
2271
|
|
|
* Finds in $_SESSION['temp_attachments'] an attachment id from its public id |
|
2272
|
|
|
* |
|
2273
|
|
|
* @param string $public_attachid |
|
2274
|
|
|
* |
|
2275
|
|
|
* @return string |
|
2276
|
|
|
*/ |
|
2277
|
|
|
function getAttachmentIdFromPublic($public_attachid) |
|
2278
|
|
|
{ |
|
2279
|
|
|
if (empty($_SESSION['temp_attachments'])) |
|
2280
|
|
|
{ |
|
2281
|
|
|
return $public_attachid; |
|
2282
|
|
|
} |
|
2283
|
|
|
|
|
2284
|
|
|
foreach ($_SESSION['temp_attachments'] as $key => $val) |
|
2285
|
|
|
{ |
|
2286
|
|
|
if (isset($val['public_attachid']) && $val['public_attachid'] === $public_attachid) |
|
2287
|
|
|
{ |
|
2288
|
|
|
return $key; |
|
2289
|
|
|
} |
|
2290
|
|
|
} |
|
2291
|
|
|
return $public_attachid; |
|
2292
|
|
|
} |
|
2293
|
|
|
|
|
2294
|
|
|
/** |
|
2295
|
|
|
* From either a mime type, an extension or an IMAGETYPE_* constant |
|
2296
|
|
|
* returns a valid image mime type |
|
2297
|
|
|
* |
|
2298
|
|
|
* @param string $mime |
|
2299
|
|
|
* |
|
2300
|
|
|
* @return string |
|
2301
|
|
|
*/ |
|
2302
|
|
|
function getValidMimeImageType($mime) |
|
2303
|
|
|
{ |
|
2304
|
|
|
// These are the only valid image types. |
|
2305
|
|
|
static $validImageTypes = array( |
|
2306
|
|
|
-1 => 'jpg', |
|
2307
|
|
|
// Starting from here are the IMAGETYPE_* constants |
|
2308
|
|
|
1 => 'gif', |
|
2309
|
|
|
2 => 'jpeg', |
|
2310
|
|
|
3 => 'png', |
|
2311
|
|
|
5 => 'psd', |
|
2312
|
|
|
6 => 'bmp', |
|
2313
|
|
|
7 => 'tiff', |
|
2314
|
|
|
8 => 'tiff', |
|
2315
|
|
|
9 => 'jpeg', |
|
2316
|
|
|
14 => 'iff' |
|
2317
|
|
|
); |
|
2318
|
|
|
|
|
2319
|
|
|
if ((int) $mime > 0) |
|
2320
|
|
|
{ |
|
2321
|
|
|
$ext = isset($validImageTypes[$mime]) ? $validImageTypes[$mime] : ''; |
|
2322
|
|
|
} |
|
2323
|
|
|
elseif (strpos($mime, '/')) |
|
2324
|
|
|
{ |
|
2325
|
|
|
$ext = substr($mime, strpos($mime, '/') + 1); |
|
2326
|
|
|
} |
|
2327
|
|
|
else |
|
2328
|
|
|
{ |
|
2329
|
|
|
$ext = $mime; |
|
2330
|
|
|
} |
|
2331
|
|
|
$ext = strtolower($ext); |
|
2332
|
|
|
|
|
2333
|
|
|
foreach ($validImageTypes as $valid_ext) |
|
2334
|
|
|
{ |
|
2335
|
|
|
if ($valid_ext === $ext) |
|
2336
|
|
|
{ |
|
2337
|
|
|
return 'image/' . $ext; |
|
2338
|
|
|
} |
|
2339
|
|
|
} |
|
2340
|
|
|
|
|
2341
|
|
|
return ''; |
|
2342
|
|
|
} |
|
2343
|
|
|
|
|
2344
|
|
|
/** |
|
2345
|
|
|
* This function returns the mimeType of a file using the best means available |
|
2346
|
|
|
* |
|
2347
|
|
|
* @param string $filename |
|
2348
|
|
|
* @return bool|mixed|string |
|
2349
|
|
|
*/ |
|
2350
|
|
|
function get_finfo_mime($filename) |
|
2351
|
|
|
{ |
|
2352
|
|
|
$mimeType = false; |
|
2353
|
|
|
|
|
2354
|
|
|
// Check only existing readable files |
|
2355
|
|
|
if (!file_exists($filename) || !is_readable($filename)) |
|
2356
|
|
|
{ |
|
2357
|
|
|
return $mimeType; |
|
2358
|
|
|
} |
|
2359
|
|
|
|
|
2360
|
|
|
// Try finfo, this is the preferred way |
|
2361
|
|
|
if (function_exists('finfo_open')) |
|
2362
|
|
|
{ |
|
2363
|
|
|
$finfo = finfo_open(FILEINFO_MIME); |
|
2364
|
|
|
$mimeType = finfo_file($finfo, $filename); |
|
2365
|
|
|
finfo_close($finfo); |
|
2366
|
|
|
} |
|
2367
|
|
|
// No finfo? What? lets try the old mime_content_type |
|
2368
|
|
|
elseif (function_exists('mime_content_type')) |
|
2369
|
|
|
{ |
|
2370
|
|
|
$mimeType = mime_content_type($filename); |
|
2371
|
|
|
} |
|
2372
|
|
|
// Try using an exec call |
|
2373
|
|
|
elseif (function_exists('exec')) |
|
2374
|
|
|
{ |
|
2375
|
|
|
$mimeType = @exec("/usr/bin/file -i -b $filename"); |
|
2376
|
|
|
} |
|
2377
|
|
|
|
|
2378
|
|
|
// Still nothing? We should at least be able to get images correct |
|
2379
|
|
|
if (empty($mimeType)) |
|
2380
|
|
|
{ |
|
2381
|
|
|
$imageData = elk_getimagesize($filename, 'none'); |
|
2382
|
|
|
if (!empty($imageData['mime'])) |
|
2383
|
|
|
{ |
|
2384
|
|
|
$mimeType = $imageData['mime']; |
|
2385
|
|
|
} |
|
2386
|
|
|
} |
|
2387
|
|
|
|
|
2388
|
|
|
// Account for long responses like text/plain; charset=us-ascii |
|
2389
|
|
|
if (!empty($mimeType) && strpos($mimeType, ';')) |
|
2390
|
|
|
{ |
|
2391
|
|
|
list($mimeType,) = explode(';', $mimeType); |
|
2392
|
|
|
} |
|
2393
|
|
|
|
|
2394
|
|
|
return $mimeType; |
|
2395
|
|
|
} |
|
2396
|
|
|
|