1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % DDDD EEEEE L EEEEE GGGG AAA TTTTT EEEEE %
6 % D D E L E G A A T E %
7 % D D EEE L EEE G GG AAAAA T EEE %
8 % D D E L E G G A A T E %
9 % DDDD EEEEE LLLLL EEEEE GGG A A T EEEEE %
10 % %
11 % %
12 % MagickCore Methods to Read/Write/Invoke Delegates %
13 % %
14 % Software Design %
15 % Cristy %
16 % October 1998 %
17 % %
18 % %
19 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
20 % dedicated to making software imaging solutions freely available. %
21 % %
22 % You may not use this file except in compliance with the License. You may %
23 % obtain a copy of the License at %
24 % %
25 % https://imagemagick.org/script/license.php %
26 % %
27 % Unless required by applicable law or agreed to in writing, software %
28 % distributed under the License is distributed on an "AS IS" BASIS, %
29 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
30 % See the License for the specific language governing permissions and %
31 % limitations under the License. %
32 % %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 %
35 % The Delegates methods associate a set of commands with a particular
36 % image format. ImageMagick uses delegates for formats it does not handle
37 % directly.
38 %
39 % Thanks to Bob Friesenhahn for the initial inspiration and design of the
40 % delegates methods.
41 %
42 %
43 */
44
45 /*
46 Include declarations.
47 */
48 #include "MagickCore/studio.h"
49 #include "MagickCore/artifact.h"
50 #include "MagickCore/attribute.h"
51 #include "MagickCore/blob.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/configure.h"
54 #include "MagickCore/constitute.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/delegate-private.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/fx-private.h"
60 #include "MagickCore/image-private.h"
61 #include "MagickCore/linked-list.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/memory_.h"
64 #include "MagickCore/memory-private.h"
65 #include "MagickCore/nt-base-private.h"
66 #include "MagickCore/option.h"
67 #include "MagickCore/policy.h"
68 #include "MagickCore/property.h"
69 #include "MagickCore/resource_.h"
70 #include "MagickCore/semaphore.h"
71 #include "MagickCore/signature.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/token.h"
74 #include "MagickCore/token-private.h"
75 #include "MagickCore/utility.h"
76 #include "MagickCore/utility-private.h"
77 #include "MagickCore/xml-tree.h"
78 #include "MagickCore/xml-tree-private.h"
79
80 /*
81 Define declarations.
82 */
83 #define DelegateFilename "delegates.xml"
84
85 /*
86 Declare delegate map.
87 */
88 static const char
89 *DelegateMap = (const char *)
90 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
91 "<delegatemap>"
92 " <delegate decode=\"bpg\" command=\""bpgdec" -b 16 -o "%o.png" "%i"; mv "%o.png" "%o"\"/>"
93 " <delegate decode=\"png\" encode=\"bpg\" command=\""bpgenc" -b 12 -q %~ -o "%o" "%i"\"/>"
94 " <delegate decode=\"browse\" stealth=\"True\" spawn=\"True\" command=\""xdg-open" https://imagemagick.org/; rm "%i"\"/>"
95 " <delegate decode=\"cdr\" command=\""uniconvertor" "%i" "%o.svg"; mv "%o.svg" "%o"\"/>"
96 " <delegate decode=\"cgm\" command=\""uniconvertor" "%i" "%o.svg"; mv "%o.svg" "%o"\"/>"
97 " <delegate decode=\"https\" command=\""curl" -s -k -L -o "%o" "https:%M"\"/>"
98 " <delegate decode=\"doc\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
99 " <delegate decode=\"docx\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
100 " <delegate decode=\"dng:decode\" command=\""ufraw-batch" --silent --create-id=also --out-type=png --out-depth=16 "--output=%u.png" "%i"\"/>"
101 " <delegate decode=\"dot\" command=\""dot" -Tsvg "%i" -o "%o"\"/>"
102 " <delegate decode=\"dvi\" command=\""dvips" -sstdout=%%stderr -o "%o" "%i"\"/>"
103 " <delegate decode=\"dxf\" command=\""uniconvertor" "%i" "%o.svg"; mv "%o.svg" "%o"\"/>"
104 " <delegate decode=\"edit\" stealth=\"True\" command=\""xterm" -title "Edit Image Comment" -e vi "%o"\"/>"
105 " <delegate decode=\"eps\" encode=\"pdf\" mode=\"bi\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 "-sDEVICE=pdfwrite" "-sOutputFile=%o" "-f%i"\"/>"
106 " <delegate decode=\"eps\" encode=\"ps\" mode=\"bi\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=ps2write" "-sOutputFile=%o" "-f%i"\"/>"
107 " <delegate decode=\"fig\" command=\""uniconvertor" "%i" "%o.svg"; mv "%o.svg" "%o"\"/>"
108 " <delegate decode=\"hpg\" command=\""hp2xx" -sstdout=%%stderr -m eps -f `basename "%o"` "%i"; mv -f `basename "%o"` "%o"\"/>"
109 " <delegate decode=\"hpgl\" command=\""hp2xx" -sstdout=%%stderr -m eps -f `basename "%o"` "%i"; mv -f `basename "%o"` "%o"\"/>"
110 " <delegate decode=\"htm\" command=\""html2ps" -U -o "%o" "%i"\"/>"
111 " <delegate decode=\"html\" command=\""html2ps" -U -o "%o" "%i"\"/>"
112 " <delegate decode=\"ilbm\" command=\""ilbmtoppm" "%i" > "%o"\"/>"
113 " <delegate decode=\"jpg\" encode=\"lep\" mode=\"encode\" command=\""lepton" "%i" "%o"\"/>"
114 " <delegate decode=\"jxr\" command=\"mv "%i" "%i.jxr"; "JxrDecApp" -i "%i.jxr" -o "%o.pnm"; mv "%i.jxr" "%i"; mv "%o.pnm" "%o"\"/>"
115 " <delegate decode=\"lep\" mode=\"decode\" command=\""lepton" "%i" "%o"\"/>"
116 " <delegate decode=\"odt\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
117 " <delegate decode=\"pcl:cmyk\" stealth=\"True\" command=\""pcl6" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pamcmyk32" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
118 " <delegate decode=\"pcl:color\" stealth=\"True\" command=\""pcl6" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=ppmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
119 " <delegate decode=\"pcl:mono\" stealth=\"True\" command=\""pcl6" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
120 " <delegate decode=\"pdf\" encode=\"eps\" mode=\"bi\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 -sPDFPassword="%a" "-sDEVICE=eps2write" "-sOutputFile=%o" "-f%i"\"/>"
121 " <delegate decode=\"pdf\" encode=\"ps\" mode=\"bi\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=ps2write" -sPDFPassword="%a" "-sOutputFile=%o" "-f%i"\"/>"
122 " <delegate decode=\"png\" encode=\"webp\" command=\""cwebp" -quiet -q %Q "%i" -o "%o"\"/>"
123 " <delegate decode=\"pnm\" encode=\"ilbm\" mode=\"encode\" command=\""ppmtoilbm" -24if "%i" > "%o"\"/>"
124 " <delegate decode=\"bmp\" encode=\"jxr\" command=\"mv "%i" "%i.bmp"; "JxrEncApp" -i "%i.bmp" -o "%o.jxr"; mv "%i.bmp" "%i"; mv "%o.jxr" "%o"\"/>"
125 " <delegate decode=\"bmp\" encode=\"wdp\" command=\"mv "%i" "%i.bmp"; "JxrEncApp" -i "%i.bmp" -o "%o.jxr"; mv "%i.bmp" "%i"; mv "%o.jxr" "%o"\"/>"
126 " <delegate decode=\"ppt\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
127 " <delegate decode=\"pptx\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
128 " <delegate decode=\"ps\" encode=\"prt\" command=\""lpr" "%i"\"/>"
129 " <delegate decode=\"ps:alpha\" stealth=\"True\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
130 " <delegate decode=\"ps:cmyk\" stealth=\"True\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pamcmyk32" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
131 " <delegate decode=\"ps:color\" stealth=\"True\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pnmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
132 " <delegate decode=\"ps\" encode=\"eps\" mode=\"bi\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=eps2write" "-sOutputFile=%o" "-f%i"\"/>"
133 " <delegate decode=\"ps\" encode=\"pdf\" mode=\"bi\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pdfwrite" "-sOutputFile=%o" "-f%i"\"/>"
134 " <delegate decode=\"ps\" encode=\"print\" mode=\"encode\" command=\"lpr "%i"\"/>"
135 " <delegate decode=\"ps:mono\" stealth=\"True\" command=\""gs" -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
136 " <delegate decode=\"shtml\" command=\""html2ps" -U -o "%o" "%i"\"/>"
137 " <delegate decode=\"sid\" command=\""mrsidgeodecode" -if sid -i "%i" -of tif -o "%o" > "%u"\"/>"
138 " <delegate decode=\"svg\" command=\""rsvg-convert" -o "%o" "%i"\"/>"
139 " <delegate decode=\"svg:decode\" stealth=\"True\" command=\""inkscape" "%s" --export-png="%s" --export-dpi="%s" --export-background="%s" --export-background-opacity="%s" > "%s" 2>&1\"/>"
140 " <delegate decode=\"tiff\" encode=\"launch\" mode=\"encode\" command=\""gimp" "%i"\"/>"
141 " <delegate decode=\"wdp\" command=\"mv "%i" "%i.jxr"; "JxrDecApp" -i "%i.jxr" -o "%o.bmp"; mv "%i.jxr" "%i"; mv "%o.bmp" "%o"\"/>"
142 " <delegate decode=\"webp\" command=\""dwebp" -pam "%i" -o "%o"\"/>"
143 " <delegate decode=\"xls\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
144 " <delegate decode=\"xlsx\" command=\""soffice" --convert-to pdf -outdir `dirname "%i"` "%i" 2> "%u"; mv "%i.pdf" "%o"\"/>"
145 " <delegate decode=\"xps:cmyk\" stealth=\"True\" command=\""gxps" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=bmpsep8" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
146 " <delegate decode=\"xps:color\" stealth=\"True\" command=\""gxps" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=ppmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
147 " <delegate decode=\"xps:mono\" stealth=\"True\" command=\""gxps" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
148 " <delegate decode=\"mpeg:decode\" command=\""ffmpeg" -v -1 -i "%i" -vframes %S -vcodec pam -an -f rawvideo -y "%u.pam" 2> "%u"\"/>"
149 " <delegate encode=\"mpeg:encode\" stealth=\"True\" command=\""ffmpeg" -v -1 -i "%M%%d.jpg" -plays %I "%u.%m" 2> "%u"\"/>"
150 "</delegatemap>";
151
152 /*
153 Global declaractions.
154 */
155 static LinkedListInfo
156 *delegate_cache = (LinkedListInfo *) NULL;
157
158 static SemaphoreInfo
159 *delegate_semaphore = (SemaphoreInfo *) NULL;
160
161 /*
162 Forward declaractions.
163 */
164 static MagickBooleanType
165 IsDelegateCacheInstantiated(ExceptionInfo *),
166 LoadDelegateCache(LinkedListInfo *,const char *,const char *,const size_t,
167 ExceptionInfo *);
168
169 /*
170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 % %
172 % %
173 % %
174 % A c q u i r e D e l e g a t e C a c h e %
175 % %
176 % %
177 % %
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 %
180 % AcquireDelegateCache() caches one or more delegate configurations which
181 % provides a mapping between delegate attributes and a delegate name.
182 %
183 % The format of the AcquireDelegateCache method is:
184 %
185 % LinkedListInfo *AcquireDelegateCache(const char *filename,
186 % ExceptionInfo *exception)
187 %
188 % A description of each parameter follows:
189 %
190 % o filename: the font file name.
191 %
192 % o exception: return any errors or warnings in this structure.
193 %
194 */
AcquireDelegateCache(const char * filename,ExceptionInfo * exception)195 static LinkedListInfo *AcquireDelegateCache(const char *filename,
196 ExceptionInfo *exception)
197 {
198 LinkedListInfo
199 *cache;
200
201 MagickStatusType
202 status;
203
204 cache=NewLinkedList(0);
205 status=MagickTrue;
206 #if !MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
207 {
208 const StringInfo
209 *option;
210
211 LinkedListInfo
212 *options;
213
214 options=GetConfigureOptions(filename,exception);
215 option=(const StringInfo *) GetNextValueInLinkedList(options);
216 while (option != (const StringInfo *) NULL)
217 {
218 status&=LoadDelegateCache(cache,(const char *)
219 GetStringInfoDatum(option),GetStringInfoPath(option),0,exception);
220 option=(const StringInfo *) GetNextValueInLinkedList(options);
221 }
222 options=DestroyConfigureOptions(options);
223 }
224 #endif
225 if (IsLinkedListEmpty(cache) != MagickFalse)
226 status&=LoadDelegateCache(cache,DelegateMap,"built-in",0,exception);
227 return(cache);
228 }
229
230 /*
231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232 % %
233 % %
234 % %
235 + D e l e g a t e C o m p o n e n t G e n e s i s %
236 % %
237 % %
238 % %
239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240 %
241 % DelegateComponentGenesis() instantiates the delegate component.
242 %
243 % The format of the DelegateComponentGenesis method is:
244 %
245 % MagickBooleanType DelegateComponentGenesis(void)
246 %
247 */
DelegateComponentGenesis(void)248 MagickPrivate MagickBooleanType DelegateComponentGenesis(void)
249 {
250 if (delegate_semaphore == (SemaphoreInfo *) NULL)
251 delegate_semaphore=AcquireSemaphoreInfo();
252 return(MagickTrue);
253 }
254
255 /*
256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257 % %
258 % %
259 % %
260 % D e l e g a t e C o m p o n e n t T e r m i n u s %
261 % %
262 % %
263 % %
264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265 %
266 % DelegateComponentTerminus() destroys the delegate component.
267 %
268 % The format of the DelegateComponentTerminus method is:
269 %
270 % DelegateComponentTerminus(void)
271 %
272 */
273
DestroyDelegate(void * delegate_info)274 static void *DestroyDelegate(void *delegate_info)
275 {
276 DelegateInfo
277 *p;
278
279 p=(DelegateInfo *) delegate_info;
280 if (p->path != (char *) NULL)
281 p->path=DestroyString(p->path);
282 if (p->decode != (char *) NULL)
283 p->decode=DestroyString(p->decode);
284 if (p->encode != (char *) NULL)
285 p->encode=DestroyString(p->encode);
286 if (p->commands != (char *) NULL)
287 p->commands=DestroyString(p->commands);
288 if (p->semaphore != (SemaphoreInfo *) NULL)
289 RelinquishSemaphoreInfo(&p->semaphore);
290 p=(DelegateInfo *) RelinquishMagickMemory(p);
291 return((void *) NULL);
292 }
293
DelegateComponentTerminus(void)294 MagickPrivate void DelegateComponentTerminus(void)
295 {
296 if (delegate_semaphore == (SemaphoreInfo *) NULL)
297 ActivateSemaphoreInfo(&delegate_semaphore);
298 LockSemaphoreInfo(delegate_semaphore);
299 if (delegate_cache != (LinkedListInfo *) NULL)
300 delegate_cache=DestroyLinkedList(delegate_cache,DestroyDelegate);
301 UnlockSemaphoreInfo(delegate_semaphore);
302 RelinquishSemaphoreInfo(&delegate_semaphore);
303 }
304
305 /*
306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307 % %
308 % %
309 % %
310 + E x t e r n a l D e l e g a t e C o m m a n d %
311 % %
312 % %
313 % %
314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315 %
316 % ExternalDelegateCommand() executes the specified command and waits until it
317 % terminates. The returned value is the exit status of the command.
318 %
319 % The format of the ExternalDelegateCommand method is:
320 %
321 % int ExternalDelegateCommand(const MagickBooleanType asynchronous,
322 % const MagickBooleanType verbose,const char *command,
323 % char *message,ExceptionInfo *exception)
324 %
325 % A description of each parameter follows:
326 %
327 % o asynchronous: a value other than 0 executes the parent program
328 % concurrently with the new child process.
329 %
330 % o verbose: a value other than 0 prints the executed command before it is
331 % invoked.
332 %
333 % o command: this string is the command to execute.
334 %
335 % o message: an option buffer to receive any message posted to stdout or
336 % stderr.
337 %
338 % o exception: return any errors here.
339 %
340 */
ExternalDelegateCommand(const MagickBooleanType asynchronous,const MagickBooleanType verbose,const char * command,char * message,ExceptionInfo * exception)341 MagickExport int ExternalDelegateCommand(const MagickBooleanType asynchronous,
342 const MagickBooleanType verbose,const char *command,char *message,
343 ExceptionInfo *exception)
344 {
345 char
346 **arguments,
347 *sanitize_command;
348
349 int
350 number_arguments,
351 status;
352
353 PolicyDomain
354 domain;
355
356 PolicyRights
357 rights;
358
359 ssize_t
360 i;
361
362 status=(-1);
363 arguments=StringToArgv(command,&number_arguments);
364 if (arguments == (char **) NULL)
365 return(status);
366 if (*arguments[1] == '\0')
367 {
368 for (i=0; i < (ssize_t) number_arguments; i++)
369 arguments[i]=DestroyString(arguments[i]);
370 arguments=(char **) RelinquishMagickMemory(arguments);
371 return(-1);
372 }
373 rights=ExecutePolicyRights;
374 domain=DelegatePolicyDomain;
375 if (IsRightsAuthorized(domain,rights,arguments[1]) == MagickFalse)
376 {
377 errno=EPERM;
378 (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
379 "NotAuthorized","`%s'",arguments[1]);
380 for (i=0; i < (ssize_t) number_arguments; i++)
381 arguments[i]=DestroyString(arguments[i]);
382 arguments=(char **) RelinquishMagickMemory(arguments);
383 return(-1);
384 }
385 if (verbose != MagickFalse)
386 {
387 (void) FormatLocaleFile(stderr,"%s\n",command);
388 (void) fflush(stderr);
389 }
390 sanitize_command=SanitizeString(command);
391 if (asynchronous != MagickFalse)
392 (void) ConcatenateMagickString(sanitize_command,"&",MagickPathExtent);
393 if (message != (char *) NULL)
394 *message='\0';
395 #if defined(MAGICKCORE_POSIX_SUPPORT)
396 #if defined(MAGICKCORE_HAVE_POPEN)
397 if ((asynchronous == MagickFalse) && (message != (char *) NULL))
398 {
399 char
400 buffer[MagickPathExtent];
401
402 FILE
403 *file;
404
405 size_t
406 offset;
407
408 offset=0;
409 file=popen_utf8(sanitize_command,"r");
410 if (file == (FILE *) NULL)
411 status=system(sanitize_command);
412 else
413 {
414 while (fgets(buffer,(int) sizeof(buffer),file) != NULL)
415 {
416 size_t
417 length;
418
419 length=MagickMin(MagickPathExtent-offset,strlen(buffer)+1);
420 if (length > 0)
421 {
422 (void) CopyMagickString(message+offset,buffer,length);
423 offset+=length-1;
424 }
425 }
426 status=pclose(file);
427 }
428 }
429 else
430 #endif
431 {
432 #if !defined(MAGICKCORE_HAVE_EXECVP)
433 status=system(sanitize_command);
434 #else
435 if ((asynchronous != MagickFalse) ||
436 (strpbrk(sanitize_command,"&;<>|") != (char *) NULL))
437 status=system(sanitize_command);
438 else
439 {
440 pid_t
441 child_pid;
442
443 /*
444 Call application directly rather than from a shell.
445 */
446 child_pid=(pid_t) fork();
447 if (child_pid == (pid_t) -1)
448 status=system(sanitize_command);
449 else
450 if (child_pid == 0)
451 {
452 status=execvp(arguments[1],arguments+1);
453 _exit(1);
454 }
455 else
456 {
457 int
458 child_status;
459
460 pid_t
461 pid;
462
463 child_status=0;
464 pid=(pid_t) waitpid(child_pid,&child_status,0);
465 if (pid == -1)
466 status=(-1);
467 else
468 {
469 if (WIFEXITED(child_status) != 0)
470 status=WEXITSTATUS(child_status);
471 else
472 if (WIFSIGNALED(child_status))
473 status=(-1);
474 }
475 }
476 }
477 #endif
478 }
479 #elif defined(MAGICKCORE_WINDOWS_SUPPORT)
480 {
481 char
482 *p;
483
484 /*
485 If a command shell is executed we need to change the forward slashes in
486 files to a backslash. We need to do this to keep Windows happy when we
487 want to 'move' a file.
488
489 TODO: This won't work if one of the delegate parameters has a forward
490 slash as aparameter.
491 */
492 p=strstr(sanitize_command,"cmd.exe /c");
493 if (p != (char*) NULL)
494 {
495 p+=10;
496 for ( ; *p != '\0'; p++)
497 if (*p == '/')
498 *p=(*DirectorySeparator);
499 }
500 }
501 status=NTSystemCommand(sanitize_command,message);
502 #elif defined(vms)
503 status=system(sanitize_command);
504 #else
505 # error No suitable system() method.
506 #endif
507 if (status < 0)
508 {
509 if ((message != (char *) NULL) && (*message != '\0'))
510 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
511 "FailedToExecuteCommand","`%s' (%s)",sanitize_command,message);
512 else
513 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
514 "FailedToExecuteCommand","`%s' (%d)",sanitize_command,status);
515 }
516 sanitize_command=DestroyString(sanitize_command);
517 for (i=0; i < (ssize_t) number_arguments; i++)
518 arguments[i]=DestroyString(arguments[i]);
519 arguments=(char **) RelinquishMagickMemory(arguments);
520 return(status);
521 }
522
523 /*
524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
525 % %
526 % %
527 % %
528 % G e t D e l e g a t e C o m m a n d %
529 % %
530 % %
531 % %
532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533 %
534 % GetDelegateCommand() replaces any embedded formatting characters with the
535 % appropriate image attribute and returns the resulting command.
536 %
537 % The format of the GetDelegateCommand method is:
538 %
539 % char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
540 % const char *decode,const char *encode,ExceptionInfo *exception)
541 %
542 % A description of each parameter follows:
543 %
544 % o command: Method GetDelegateCommand returns the command associated
545 % with specified delegate tag.
546 %
547 % o image_info: the image info.
548 %
549 % o image: the image.
550 %
551 % o decode: Specifies the decode delegate we are searching for as a
552 % character string.
553 %
554 % o encode: Specifies the encode delegate we are searching for as a
555 % character string.
556 %
557 % o exception: return any errors or warnings in this structure.
558 %
559 */
560
GetMagickPropertyLetter(ImageInfo * image_info,Image * image,const char letter,ExceptionInfo * exception)561 static char *GetMagickPropertyLetter(ImageInfo *image_info,Image *image,
562 const char letter,ExceptionInfo *exception)
563 {
564 #define WarnNoImageReturn(format,letter) \
565 if (image == (Image *) NULL) \
566 { \
567 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
568 "NoImageForProperty",format,letter); \
569 break; \
570 }
571 #define WarnNoImageInfoReturn(format,letter) \
572 if (image_info == (ImageInfo *) NULL) \
573 { \
574 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
575 "NoImageInfoForProperty",format,letter); \
576 break; \
577 }
578
579 char
580 value[MagickPathExtent];
581
582 const char
583 *string;
584
585 if ((image != (Image *) NULL) && (image->debug != MagickFalse))
586 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
587 else
588 if ((image_info != (ImageInfo *) NULL) &&
589 (image_info->debug != MagickFalse))
590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
591 /*
592 Get properties that are directly defined by images.
593 */
594 *value='\0'; /* formatted string */
595 string=(const char *) value;
596 switch (letter)
597 {
598 case 'a': /* authentication passphase */
599 {
600 WarnNoImageInfoReturn("\"%%%c\"",letter);
601 string=GetImageOption(image_info,"authenticate");
602 break;
603 }
604 case 'b': /* image size read in - in bytes */
605 {
606 WarnNoImageReturn("\"%%%c\"",letter);
607 (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
608 value);
609 if (image->extent == 0)
610 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
611 MagickPathExtent,value);
612 break;
613 }
614 case 'd': /* Directory component of filename */
615 {
616 WarnNoImageReturn("\"%%%c\"",letter);
617 GetPathComponent(image->magick_filename,HeadPath,value);
618 break;
619 }
620 case 'e': /* Filename extension (suffix) of image file */
621 {
622 WarnNoImageReturn("\"%%%c\"",letter);
623 GetPathComponent(image->magick_filename,ExtensionPath,value);
624 break;
625 }
626 case 'f': /* Filename without directory component */
627 {
628 WarnNoImageReturn("\"%%%c\"",letter);
629 GetPathComponent(image->magick_filename,TailPath,value);
630 break;
631 }
632 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */
633 {
634 WarnNoImageReturn("\"%%%c\"",letter);
635 (void) FormatLocaleString(value,MagickPathExtent,
636 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
637 image->page.height,(double) image->page.x,(double) image->page.y);
638 break;
639 }
640 case 'h': /* Image height (current) */
641 {
642 WarnNoImageReturn("\"%%%c\"",letter);
643 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
644 (image->rows != 0 ? image->rows : image->magick_rows));
645 break;
646 }
647 case 'i': /* Filename last used for an image (read or write) */
648 {
649 WarnNoImageReturn("\"%%%c\"",letter);
650 string=image->filename;
651 break;
652 }
653 case 'm': /* Image format (file magick) */
654 {
655 WarnNoImageReturn("\"%%%c\"",letter);
656 string=image->magick;
657 break;
658 }
659 case 'n': /* Number of images in the list. */
660 {
661 if (image != (Image *) NULL)
662 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
663 GetImageListLength(image));
664 break;
665 }
666 case 'o': /* Output Filename */
667 {
668 WarnNoImageInfoReturn("\"%%%c\"",letter);
669 string=image_info->filename;
670 break;
671 }
672 case 'p': /* Image index in current image list */
673 {
674 WarnNoImageReturn("\"%%%c\"",letter);
675 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
676 GetImageIndexInList(image));
677 break;
678 }
679 case 'q': /* Quantum depth of image in memory */
680 {
681 WarnNoImageReturn("\"%%%c\"",letter);
682 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
683 MAGICKCORE_QUANTUM_DEPTH);
684 break;
685 }
686 case 'r': /* Image storage class, colorspace, and alpha enabled. */
687 {
688 ColorspaceType
689 colorspace;
690
691 WarnNoImageReturn("\"%%%c\"",letter);
692 colorspace=image->colorspace;
693 if (SetImageGray(image,exception) != MagickFalse)
694 colorspace=GRAYColorspace; /* FUTURE: this is IMv6 not IMv7 */
695 (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s",
696 CommandOptionToMnemonic(MagickClassOptions,(ssize_t)
697 image->storage_class),CommandOptionToMnemonic(MagickColorspaceOptions,
698 (ssize_t) colorspace),image->alpha_trait != UndefinedPixelTrait ?
699 "Alpha" : "");
700 break;
701 }
702 case 's': /* Image scene number */
703 {
704 WarnNoImageReturn("\"%%%c\"",letter);
705 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
706 image->scene);
707 break;
708 }
709 case 't': /* Base filename without directory or extention */
710 {
711 WarnNoImageReturn("\"%%%c\"",letter);
712 GetPathComponent(image->magick_filename,BasePath,value);
713 break;
714 }
715 case 'u': /* Unique filename */
716 {
717 WarnNoImageInfoReturn("\"%%%c\"",letter);
718 string=image_info->unique;
719 break;
720 }
721 case 'w': /* Image width (current) */
722 {
723 WarnNoImageReturn("\"%%%c\"",letter);
724 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
725 (image->columns != 0 ? image->columns : image->magick_columns));
726 break;
727 }
728 case 'x': /* Image horizontal resolution (with units) */
729 {
730 WarnNoImageReturn("\"%%%c\"",letter);
731 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
732 fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x :
733 image->units == PixelsPerCentimeterResolution ? DefaultResolution/2.54 :
734 DefaultResolution);
735 break;
736 }
737 case 'y': /* Image vertical resolution (with units) */
738 {
739 WarnNoImageReturn("\"%%%c\"",letter);
740 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
741 fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y :
742 image->units == PixelsPerCentimeterResolution ? DefaultResolution/2.54 :
743 DefaultResolution);
744 break;
745 }
746 case 'z': /* Image depth as read in */
747 {
748 WarnNoImageReturn("\"%%%c\"",letter);
749 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
750 (double) image->depth);
751 break;
752 }
753 case 'A': /* Image alpha channel */
754 {
755 WarnNoImageReturn("\"%%%c\"",letter);
756 string=CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t)
757 image->alpha_trait);
758 break;
759 }
760 case 'C': /* Image compression method. */
761 {
762 WarnNoImageReturn("\"%%%c\"",letter);
763 string=CommandOptionToMnemonic(MagickCompressOptions,
764 (ssize_t) image->compression);
765 break;
766 }
767 case 'D': /* Image dispose method. */
768 {
769 WarnNoImageReturn("\"%%%c\"",letter);
770 string=CommandOptionToMnemonic(MagickDisposeOptions,
771 (ssize_t) image->dispose);
772 break;
773 }
774 case 'F':
775 {
776 /*
777 Magick filename - filename given incl. coder & read mods.
778 */
779 WarnNoImageReturn("\"%%%c\"",letter);
780 (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
781 break;
782 }
783 case 'G': /* Image size as geometry = "%wx%h" */
784 {
785 WarnNoImageReturn("\"%%%c\"",letter);
786 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",
787 (double) image->magick_columns,(double) image->magick_rows);
788 break;
789 }
790 case 'H': /* layer canvas height */
791 {
792 WarnNoImageReturn("\"%%%c\"",letter);
793 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
794 (double) image->page.height);
795 break;
796 }
797 case 'I': /* image iterations for animations */
798 {
799 WarnNoImageReturn("\"%%%c\"",letter);
800 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
801 image->iterations);
802 break;
803 }
804 case 'M': /* Magick filename - filename given incl. coder & read mods */
805 {
806 WarnNoImageReturn("\"%%%c\"",letter);
807 string=image->magick_filename;
808 break;
809 }
810 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
811 {
812 WarnNoImageReturn("\"%%%c\"",letter);
813 (void) FormatLocaleString(value,MagickPathExtent,"%+ld%+ld",(long)
814 image->page.x,(long) image->page.y);
815 break;
816 }
817 case 'P': /* layer canvas page size = "%Wx%H" */
818 {
819 WarnNoImageReturn("\"%%%c\"",letter);
820 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",
821 (double) image->page.width,(double) image->page.height);
822 break;
823 }
824 case '~': /* BPG image compression quality */
825 {
826 WarnNoImageReturn("\"%%%c\"",letter);
827 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
828 (100-(image->quality == 0 ? 42 : image->quality))/2);
829 break;
830 }
831 case 'Q': /* image compression quality */
832 {
833 WarnNoImageReturn("\"%%%c\"",letter);
834 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
835 (image->quality == 0 ? 92 : image->quality));
836 break;
837 }
838 case 'S': /* Number of scenes in image list. */
839 {
840 WarnNoImageInfoReturn("\"%%%c\"",letter);
841 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
842 (image_info->number_scenes == 0 ? 2147483647 :
843 image_info->number_scenes));
844 break;
845 }
846 case 'T': /* image time delay for animations */
847 {
848 WarnNoImageReturn("\"%%%c\"",letter);
849 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
850 image->delay);
851 break;
852 }
853 case 'U': /* Image resolution units. */
854 {
855 WarnNoImageReturn("\"%%%c\"",letter);
856 string=CommandOptionToMnemonic(MagickResolutionOptions,
857 (ssize_t) image->units);
858 break;
859 }
860 case 'W': /* layer canvas width */
861 {
862 WarnNoImageReturn("\"%%%c\"",letter);
863 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
864 image->page.width);
865 break;
866 }
867 case 'X': /* layer canvas X offset */
868 {
869 WarnNoImageReturn("\"%%%c\"",letter);
870 (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
871 image->page.x);
872 break;
873 }
874 case 'Y': /* layer canvas Y offset */
875 {
876 WarnNoImageReturn("\"%%%c\"",letter);
877 (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
878 image->page.y);
879 break;
880 }
881 case '%': /* percent escaped */
882 {
883 string="%";
884 break;
885 }
886 case '@': /* Trim bounding box, without actually trimming! */
887 {
888 RectangleInfo
889 page;
890
891 WarnNoImageReturn("\"%%%c\"",letter);
892 page=GetImageBoundingBox(image,exception);
893 (void) FormatLocaleString(value,MagickPathExtent,
894 "%.20gx%.20g%+.20g%+.20g",(double) page.width,(double) page.height,
895 (double) page.x,(double) page.y);
896 break;
897 }
898 case '#':
899 {
900 /*
901 Image signature.
902 */
903 WarnNoImageReturn("\"%%%c\"",letter);
904 (void) SignatureImage(image,exception);
905 string=GetImageProperty(image,"signature",exception);
906 break;
907 }
908 }
909 return(SanitizeDelegateString(string));
910 }
911
InterpretDelegateProperties(ImageInfo * image_info,Image * image,const char * embed_text,ExceptionInfo * exception)912 static char *InterpretDelegateProperties(ImageInfo *image_info,
913 Image *image,const char *embed_text,ExceptionInfo *exception)
914 {
915 #define ExtendInterpretText(string_length) \
916 { \
917 size_t length=(string_length); \
918 if ((size_t) (q-interpret_text+length+1) >= extent) \
919 { \
920 extent+=length; \
921 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
922 MagickPathExtent,sizeof(*interpret_text)); \
923 if (interpret_text == (char *) NULL) \
924 return((char *) NULL); \
925 q=interpret_text+strlen(interpret_text); \
926 } \
927 }
928
929 #define AppendKeyValue2Text(key,value)\
930 { \
931 size_t length=strlen(key)+strlen(value)+2; \
932 if ((size_t) (q-interpret_text+length+1) >= extent) \
933 { \
934 extent+=length; \
935 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
936 MagickPathExtent,sizeof(*interpret_text)); \
937 if (interpret_text == (char *) NULL) \
938 return((char *) NULL); \
939 q=interpret_text+strlen(interpret_text); \
940 } \
941 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(value)); \
942 }
943
944 #define AppendString2Text(string) \
945 { \
946 size_t length=strlen((string)); \
947 if ((size_t) (q-interpret_text+length+1) >= extent) \
948 { \
949 extent+=length; \
950 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
951 MagickPathExtent,sizeof(*interpret_text)); \
952 if (interpret_text == (char *) NULL) \
953 return((char *) NULL); \
954 q=interpret_text+strlen(interpret_text); \
955 } \
956 (void) CopyMagickString(q,(string),extent); \
957 q+=length; \
958 }
959
960 char
961 *interpret_text,
962 *string;
963
964 char
965 *q; /* current position in interpret_text */
966
967 const char
968 *p; /* position in embed_text string being expanded */
969
970 size_t
971 extent; /* allocated length of interpret_text */
972
973 MagickBooleanType
974 number;
975
976 assert(image == NULL || image->signature == MagickCoreSignature);
977 assert(image_info == NULL || image_info->signature == MagickCoreSignature);
978 if ((image != (Image *) NULL) && (image->debug != MagickFalse))
979 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
980 else
981 if ((image_info != (ImageInfo *) NULL) && (image_info->debug != MagickFalse))
982 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
983 if (embed_text == (const char *) NULL)
984 return(ConstantString(""));
985 p=embed_text;
986 while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0'))
987 p++;
988 if (*p == '\0')
989 return(ConstantString(""));
990 /*
991 Translate any embedded format characters.
992 */
993 interpret_text=AcquireString(embed_text); /* new string with extra space */
994 extent=MagickPathExtent; /* allocated space in string */
995 number=MagickFalse; /* is last char a number? */
996 for (q=interpret_text; *p!='\0';
997 number=isdigit((int) ((unsigned char) *p)) ? MagickTrue : MagickFalse,p++)
998 {
999 /*
1000 Interpret escape characters (e.g. Filename: %M).
1001 */
1002 *q='\0';
1003 ExtendInterpretText(MagickPathExtent);
1004 switch (*p)
1005 {
1006 case '\\':
1007 {
1008 switch (*(p+1))
1009 {
1010 case '\0':
1011 continue;
1012 case 'r': /* convert to RETURN */
1013 {
1014 *q++='\r';
1015 p++;
1016 continue;
1017 }
1018 case 'n': /* convert to NEWLINE */
1019 {
1020 *q++='\n';
1021 p++;
1022 continue;
1023 }
1024 case '\n': /* EOL removal UNIX,MacOSX */
1025 {
1026 p++;
1027 continue;
1028 }
1029 case '\r': /* EOL removal DOS,Windows */
1030 {
1031 p++;
1032 if (*p == '\n') /* return-newline EOL */
1033 p++;
1034 continue;
1035 }
1036 default:
1037 {
1038 p++;
1039 *q++=(*p);
1040 }
1041 }
1042 continue;
1043 }
1044 case '&':
1045 {
1046 if (LocaleNCompare("<",p,4) == 0)
1047 {
1048 *q++='<';
1049 p+=3;
1050 }
1051 else
1052 if (LocaleNCompare(">",p,4) == 0)
1053 {
1054 *q++='>';
1055 p+=3;
1056 }
1057 else
1058 if (LocaleNCompare("&",p,5) == 0)
1059 {
1060 *q++='&';
1061 p+=4;
1062 }
1063 else
1064 *q++=(*p);
1065 continue;
1066 }
1067 case '%':
1068 break; /* continue to next set of handlers */
1069 default:
1070 {
1071 *q++=(*p); /* any thing else is 'as normal' */
1072 continue;
1073 }
1074 }
1075 p++; /* advance beyond the percent */
1076 /*
1077 Doubled Percent - or percent at end of string.
1078 */
1079 if ((*p == '\0') || (*p == '\'') || (*p == '"'))
1080 p--;
1081 if (*p == '%')
1082 {
1083 *q++='%';
1084 continue;
1085 }
1086 /*
1087 Single letter escapes %c.
1088 */
1089 if (number != MagickFalse)
1090 {
1091 /*
1092 But only if not preceeded by a number!
1093 */
1094 *q++='%'; /* do NOT substitute the percent */
1095 p--; /* back up one */
1096 continue;
1097 }
1098 string=GetMagickPropertyLetter(image_info,image,*p,exception);
1099 if (string != (char *) NULL)
1100 {
1101 AppendString2Text(string);
1102 string=DestroyString(string);
1103 continue;
1104 }
1105 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1106 "UnknownImageProperty","\"%%%c\"",*p);
1107 }
1108 *q='\0';
1109 return(interpret_text);
1110 }
1111
GetDelegateCommand(const ImageInfo * image_info,Image * image,const char * decode,const char * encode,ExceptionInfo * exception)1112 MagickExport char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
1113 const char *decode,const char *encode,ExceptionInfo *exception)
1114 {
1115 char
1116 *command,
1117 **commands;
1118
1119 const DelegateInfo
1120 *delegate_info;
1121
1122 ssize_t
1123 i;
1124
1125 assert(image_info != (ImageInfo *) NULL);
1126 assert(image_info->signature == MagickCoreSignature);
1127 assert(image != (Image *) NULL);
1128 assert(image->signature == MagickCoreSignature);
1129 if (image->debug != MagickFalse)
1130 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1131
1132 delegate_info=GetDelegateInfo(decode,encode,exception);
1133 if (delegate_info == (const DelegateInfo *) NULL)
1134 {
1135 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1136 "NoTagFound","`%s'",decode ? decode : encode);
1137 return((char *) NULL);
1138 }
1139 commands=StringToList(delegate_info->commands);
1140 if (commands == (char **) NULL)
1141 {
1142 (void) ThrowMagickException(exception,GetMagickModule(),
1143 ResourceLimitError,"MemoryAllocationFailed","`%s'",decode ? decode :
1144 encode);
1145 return((char *) NULL);
1146 }
1147 command=InterpretDelegateProperties((ImageInfo *) image_info,image,
1148 commands[0],exception);
1149 if (command == (char *) NULL)
1150 (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1151 "MemoryAllocationFailed","`%s'",commands[0]);
1152 /*
1153 Relinquish resources.
1154 */
1155 for (i=0; commands[i] != (char *) NULL; i++)
1156 commands[i]=DestroyString(commands[i]);
1157 commands=(char **) RelinquishMagickMemory(commands);
1158 return(command);
1159 }
1160
1161 /*
1162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1163 % %
1164 % %
1165 % %
1166 % G e t D e l e g a t e C o m m a n d s %
1167 % %
1168 % %
1169 % %
1170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171 %
1172 % GetDelegateCommands() returns the commands associated with a delegate.
1173 %
1174 % The format of the GetDelegateCommands method is:
1175 %
1176 % const char *GetDelegateCommands(const DelegateInfo *delegate_info)
1177 %
1178 % A description of each parameter follows:
1179 %
1180 % o delegate_info: The delegate info.
1181 %
1182 */
GetDelegateCommands(const DelegateInfo * delegate_info)1183 MagickExport const char *GetDelegateCommands(const DelegateInfo *delegate_info)
1184 {
1185 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1186
1187 assert(delegate_info != (DelegateInfo *) NULL);
1188 assert(delegate_info->signature == MagickCoreSignature);
1189 return(delegate_info->commands);
1190 }
1191
1192 /*
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 % %
1195 % %
1196 % %
1197 % G e t D e l e g a t e I n f o %
1198 % %
1199 % %
1200 % %
1201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202 %
1203 % GetDelegateInfo() returns any delegates associated with the specified tag.
1204 %
1205 % The format of the GetDelegateInfo method is:
1206 %
1207 % const DelegateInfo *GetDelegateInfo(const char *decode,
1208 % const char *encode,ExceptionInfo *exception)
1209 %
1210 % A description of each parameter follows:
1211 %
1212 % o decode: Specifies the decode delegate we are searching for as a
1213 % character string.
1214 %
1215 % o encode: Specifies the encode delegate we are searching for as a
1216 % character string.
1217 %
1218 % o exception: return any errors or warnings in this structure.
1219 %
1220 */
GetDelegateInfo(const char * decode,const char * encode,ExceptionInfo * exception)1221 MagickExport const DelegateInfo *GetDelegateInfo(const char *decode,
1222 const char *encode,ExceptionInfo *exception)
1223 {
1224 const DelegateInfo
1225 *p;
1226
1227 assert(exception != (ExceptionInfo *) NULL);
1228 if (IsDelegateCacheInstantiated(exception) == MagickFalse)
1229 return((const DelegateInfo *) NULL);
1230 /*
1231 Search for named delegate.
1232 */
1233 LockSemaphoreInfo(delegate_semaphore);
1234 ResetLinkedListIterator(delegate_cache);
1235 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1236 if ((LocaleCompare(decode,"*") == 0) && (LocaleCompare(encode,"*") == 0))
1237 {
1238 UnlockSemaphoreInfo(delegate_semaphore);
1239 return(p);
1240 }
1241 while (p != (const DelegateInfo *) NULL)
1242 {
1243 if (p->mode > 0)
1244 {
1245 if (LocaleCompare(p->decode,decode) == 0)
1246 break;
1247 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1248 continue;
1249 }
1250 if (p->mode < 0)
1251 {
1252 if (LocaleCompare(p->encode,encode) == 0)
1253 break;
1254 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1255 continue;
1256 }
1257 if (LocaleCompare(decode,p->decode) == 0)
1258 if (LocaleCompare(encode,p->encode) == 0)
1259 break;
1260 if (LocaleCompare(decode,"*") == 0)
1261 if (LocaleCompare(encode,p->encode) == 0)
1262 break;
1263 if (LocaleCompare(decode,p->decode) == 0)
1264 if (LocaleCompare(encode,"*") == 0)
1265 break;
1266 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1267 }
1268 if (p != (const DelegateInfo *) NULL)
1269 (void) InsertValueInLinkedList(delegate_cache,0,
1270 RemoveElementByValueFromLinkedList(delegate_cache,p));
1271 UnlockSemaphoreInfo(delegate_semaphore);
1272 return(p);
1273 }
1274
1275 /*
1276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % %
1278 % %
1279 % %
1280 % G e t D e l e g a t e I n f o L i s t %
1281 % %
1282 % %
1283 % %
1284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285 %
1286 % GetDelegateInfoList() returns any delegates that match the specified pattern.
1287 %
1288 % The delegate of the GetDelegateInfoList function is:
1289 %
1290 % const DelegateInfo **GetDelegateInfoList(const char *pattern,
1291 % size_t *number_delegates,ExceptionInfo *exception)
1292 %
1293 % A description of each parameter follows:
1294 %
1295 % o pattern: Specifies a pointer to a text string containing a pattern.
1296 %
1297 % o number_delegates: This integer returns the number of delegates in the
1298 % list.
1299 %
1300 % o exception: return any errors or warnings in this structure.
1301 %
1302 */
1303
1304 #if defined(__cplusplus) || defined(c_plusplus)
1305 extern "C" {
1306 #endif
1307
DelegateInfoCompare(const void * x,const void * y)1308 static int DelegateInfoCompare(const void *x,const void *y)
1309 {
1310 const DelegateInfo
1311 **p,
1312 **q;
1313
1314 int
1315 cmp;
1316
1317 p=(const DelegateInfo **) x,
1318 q=(const DelegateInfo **) y;
1319 cmp=LocaleCompare((*p)->path,(*q)->path);
1320 if (cmp == 0)
1321 {
1322 if ((*p)->decode == (char *) NULL)
1323 if (((*p)->encode != (char *) NULL) &&
1324 ((*q)->encode != (char *) NULL))
1325 return(strcmp((*p)->encode,(*q)->encode));
1326 if (((*p)->decode != (char *) NULL) &&
1327 ((*q)->decode != (char *) NULL))
1328 return(strcmp((*p)->decode,(*q)->decode));
1329 }
1330 return(cmp);
1331 }
1332
1333 #if defined(__cplusplus) || defined(c_plusplus)
1334 }
1335 #endif
1336
GetDelegateInfoList(const char * pattern,size_t * number_delegates,ExceptionInfo * exception)1337 MagickExport const DelegateInfo **GetDelegateInfoList(const char *pattern,
1338 size_t *number_delegates,ExceptionInfo *exception)
1339 {
1340 const DelegateInfo
1341 **delegates;
1342
1343 const DelegateInfo
1344 *p;
1345
1346 ssize_t
1347 i;
1348
1349 /*
1350 Allocate delegate list.
1351 */
1352 assert(number_delegates != (size_t *) NULL);
1353 assert(pattern != (char *) NULL);
1354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
1355
1356 *number_delegates=0;
1357 p=GetDelegateInfo("*","*",exception);
1358 if (p == (const DelegateInfo *) NULL)
1359 return((const DelegateInfo **) NULL);
1360 delegates=(const DelegateInfo **) AcquireQuantumMemory((size_t)
1361 GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
1362 if (delegates == (const DelegateInfo **) NULL)
1363 return((const DelegateInfo **) NULL);
1364 /*
1365 Generate delegate list.
1366 */
1367 LockSemaphoreInfo(delegate_semaphore);
1368 ResetLinkedListIterator(delegate_cache);
1369 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1370 for (i=0; p != (const DelegateInfo *) NULL; )
1371 {
1372 if( (p->stealth == MagickFalse) &&
1373 ( GlobExpression(p->decode,pattern,MagickFalse) != MagickFalse ||
1374 GlobExpression(p->encode,pattern,MagickFalse) != MagickFalse) )
1375 delegates[i++]=p;
1376 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1377 }
1378 UnlockSemaphoreInfo(delegate_semaphore);
1379 qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateInfoCompare);
1380 delegates[i]=(DelegateInfo *) NULL;
1381 *number_delegates=(size_t) i;
1382 return(delegates);
1383 }
1384
1385 /*
1386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387 % %
1388 % %
1389 % %
1390 % G e t D e l e g a t e L i s t %
1391 % %
1392 % %
1393 % %
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395 %
1396 % GetDelegateList() returns any image format delegates that match the
1397 % specified pattern.
1398 %
1399 % The format of the GetDelegateList function is:
1400 %
1401 % char **GetDelegateList(const char *pattern,
1402 % size_t *number_delegates,ExceptionInfo *exception)
1403 %
1404 % A description of each parameter follows:
1405 %
1406 % o pattern: Specifies a pointer to a text string containing a pattern.
1407 %
1408 % o number_delegates: This integer returns the number of delegates
1409 % in the list.
1410 %
1411 % o exception: return any errors or warnings in this structure.
1412 %
1413 */
1414
1415 #if defined(__cplusplus) || defined(c_plusplus)
1416 extern "C" {
1417 #endif
1418
DelegateCompare(const void * x,const void * y)1419 static int DelegateCompare(const void *x,const void *y)
1420 {
1421 const char
1422 **p,
1423 **q;
1424
1425 p=(const char **) x;
1426 q=(const char **) y;
1427 return(LocaleCompare(*p,*q));
1428 }
1429
1430 #if defined(__cplusplus) || defined(c_plusplus)
1431 }
1432 #endif
1433
GetDelegateList(const char * pattern,size_t * number_delegates,ExceptionInfo * exception)1434 MagickExport char **GetDelegateList(const char *pattern,
1435 size_t *number_delegates,ExceptionInfo *exception)
1436 {
1437 char
1438 **delegates;
1439
1440 const DelegateInfo
1441 *p;
1442
1443 ssize_t
1444 i;
1445
1446 /*
1447 Allocate delegate list.
1448 */
1449 assert(pattern != (char *) NULL);
1450 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
1451
1452 assert(number_delegates != (size_t *) NULL);
1453 *number_delegates=0;
1454 p=GetDelegateInfo("*","*",exception);
1455 if (p == (const DelegateInfo *) NULL)
1456 return((char **) NULL);
1457 delegates=(char **) AcquireQuantumMemory((size_t)
1458 GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
1459 if (delegates == (char **) NULL)
1460 return((char **) NULL);
1461 LockSemaphoreInfo(delegate_semaphore);
1462 ResetLinkedListIterator(delegate_cache);
1463 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1464 for (i=0; p != (const DelegateInfo *) NULL; )
1465 {
1466 if( (p->stealth == MagickFalse) &&
1467 GlobExpression(p->decode,pattern,MagickFalse) != MagickFalse )
1468 delegates[i++]=ConstantString(p->decode);
1469 if( (p->stealth == MagickFalse) &&
1470 GlobExpression(p->encode,pattern,MagickFalse) != MagickFalse )
1471 delegates[i++]=ConstantString(p->encode);
1472 p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1473 }
1474 UnlockSemaphoreInfo(delegate_semaphore);
1475 qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateCompare);
1476 delegates[i]=(char *) NULL;
1477 *number_delegates=(size_t) i;
1478 return(delegates);
1479 }
1480
1481 /*
1482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1483 % %
1484 % %
1485 % %
1486 % G e t D e l e g a t e M o d e %
1487 % %
1488 % %
1489 % %
1490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1491 %
1492 % GetDelegateMode() returns the mode of the delegate.
1493 %
1494 % The format of the GetDelegateMode method is:
1495 %
1496 % ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
1497 %
1498 % A description of each parameter follows:
1499 %
1500 % o delegate_info: The delegate info.
1501 %
1502 */
GetDelegateMode(const DelegateInfo * delegate_info)1503 MagickExport ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
1504 {
1505 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1506
1507 assert(delegate_info != (DelegateInfo *) NULL);
1508 assert(delegate_info->signature == MagickCoreSignature);
1509 return(delegate_info->mode);
1510 }
1511
1512 /*
1513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1514 % %
1515 % %
1516 % %
1517 + G e t D e l e g a t e T h r e a d S u p p o r t %
1518 % %
1519 % %
1520 % %
1521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1522 %
1523 % GetDelegateThreadSupport() returns MagickTrue if the delegate supports
1524 % threads.
1525 %
1526 % The format of the GetDelegateThreadSupport method is:
1527 %
1528 % MagickBooleanType GetDelegateThreadSupport(
1529 % const DelegateInfo *delegate_info)
1530 %
1531 % A description of each parameter follows:
1532 %
1533 % o delegate_info: The delegate info.
1534 %
1535 */
GetDelegateThreadSupport(const DelegateInfo * delegate_info)1536 MagickExport MagickBooleanType GetDelegateThreadSupport(
1537 const DelegateInfo *delegate_info)
1538 {
1539 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1540
1541 assert(delegate_info != (DelegateInfo *) NULL);
1542 assert(delegate_info->signature == MagickCoreSignature);
1543 return(delegate_info->thread_support);
1544 }
1545
1546 /*
1547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548 % %
1549 % %
1550 % %
1551 + I s D e l e g a t e C a c h e I n s t a n t i a t e d %
1552 % %
1553 % %
1554 % %
1555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556 %
1557 % IsDelegateCacheInstantiated() determines if the delegate cache is
1558 % instantiated. If not, it instantiates the cache and returns it.
1559 %
1560 % The format of the IsDelegateInstantiated method is:
1561 %
1562 % MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
1563 %
1564 % A description of each parameter follows.
1565 %
1566 % o exception: return any errors or warnings in this structure.
1567 %
1568 */
IsDelegateCacheInstantiated(ExceptionInfo * exception)1569 static MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
1570 {
1571 if (delegate_cache == (LinkedListInfo *) NULL)
1572 {
1573 if (delegate_semaphore == (SemaphoreInfo *) NULL)
1574 ActivateSemaphoreInfo(&delegate_semaphore);
1575 LockSemaphoreInfo(delegate_semaphore);
1576 if (delegate_cache == (LinkedListInfo *) NULL)
1577 delegate_cache=AcquireDelegateCache(DelegateFilename,exception);
1578 UnlockSemaphoreInfo(delegate_semaphore);
1579 }
1580 return(delegate_cache != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
1581 }
1582
1583 /*
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 % %
1586 % %
1587 % %
1588 % I n v o k e D e l e g a t e %
1589 % %
1590 % %
1591 % %
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593 %
1594 % InvokeDelegate replaces any embedded formatting characters with the
1595 % appropriate image attribute and executes the resulting command. MagickFalse
1596 % is returned if the commands execute with success otherwise MagickTrue.
1597 %
1598 % The format of the InvokeDelegate method is:
1599 %
1600 % MagickBooleanType InvokeDelegate(ImageInfo *image_info,Image *image,
1601 % const char *decode,const char *encode,ExceptionInfo *exception)
1602 %
1603 % A description of each parameter follows:
1604 %
1605 % o image_info: the imageInfo.
1606 %
1607 % o image: the image.
1608 %
1609 % o exception: return any errors or warnings in this structure.
1610 %
1611 */
1612
CopyDelegateFile(const char * source,const char * destination,const MagickBooleanType overwrite)1613 static MagickBooleanType CopyDelegateFile(const char *source,
1614 const char *destination,const MagickBooleanType overwrite)
1615 {
1616 int
1617 destination_file,
1618 source_file;
1619
1620 MagickBooleanType
1621 status;
1622
1623 size_t
1624 i;
1625
1626 size_t
1627 length,
1628 quantum;
1629
1630 ssize_t
1631 count;
1632
1633 struct stat
1634 attributes;
1635
1636 unsigned char
1637 *buffer;
1638
1639 /*
1640 Copy source file to destination.
1641 */
1642 assert(source != (const char *) NULL);
1643 assert(destination != (char *) NULL);
1644 if (overwrite == MagickFalse)
1645 {
1646 status=GetPathAttributes(destination,&attributes);
1647 if (status != MagickFalse)
1648 return(MagickTrue);
1649 }
1650 destination_file=open_utf8(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
1651 if (destination_file == -1)
1652 return(MagickFalse);
1653 source_file=open_utf8(source,O_RDONLY | O_BINARY,0);
1654 if (source_file == -1)
1655 {
1656 (void) close(destination_file);
1657 return(MagickFalse);
1658 }
1659 quantum=(size_t) MagickMaxBufferExtent;
1660 if ((fstat(source_file,&attributes) == 0) && (attributes.st_size > 0))
1661 quantum=MagickMin((size_t) attributes.st_size,MagickMaxBufferExtent);
1662 buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
1663 if (buffer == (unsigned char *) NULL)
1664 {
1665 (void) close(source_file);
1666 (void) close(destination_file);
1667 return(MagickFalse);
1668 }
1669 length=0;
1670 for (i=0; ; i+=count)
1671 {
1672 count=(ssize_t) read(source_file,buffer,quantum);
1673 if (count <= 0)
1674 break;
1675 length=(size_t) count;
1676 count=(ssize_t) write(destination_file,buffer,length);
1677 if ((size_t) count != length)
1678 break;
1679 }
1680 (void) close(destination_file);
1681 (void) close(source_file);
1682 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1683 return(i != 0 ? MagickTrue : MagickFalse);
1684 }
1685
InvokeDelegate(ImageInfo * image_info,Image * image,const char * decode,const char * encode,ExceptionInfo * exception)1686 MagickExport MagickBooleanType InvokeDelegate(ImageInfo *image_info,
1687 Image *image,const char *decode,const char *encode,ExceptionInfo *exception)
1688 {
1689 char
1690 *command,
1691 **commands,
1692 input_filename[MagickPathExtent],
1693 output_filename[MagickPathExtent];
1694
1695 const DelegateInfo
1696 *delegate_info;
1697
1698 MagickBooleanType
1699 status,
1700 temporary;
1701
1702 PolicyRights
1703 rights;
1704
1705 ssize_t
1706 i;
1707
1708 /*
1709 Get delegate.
1710 */
1711 assert(image_info != (ImageInfo *) NULL);
1712 assert(image_info->signature == MagickCoreSignature);
1713 assert(image != (Image *) NULL);
1714 assert(image->signature == MagickCoreSignature);
1715 if (image->debug != MagickFalse)
1716 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1717 rights=ExecutePolicyRights;
1718 if (IsRightsAuthorized(DelegatePolicyDomain,rights,decode) == MagickFalse)
1719 {
1720 errno=EPERM;
1721 (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
1722 "NotAuthorized","`%s'",decode);
1723 return(MagickFalse);
1724 }
1725 if (IsRightsAuthorized(DelegatePolicyDomain,rights,encode) == MagickFalse)
1726 {
1727 errno=EPERM;
1728 (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
1729 "NotAuthorized","`%s'",encode);
1730 return(MagickFalse);
1731 }
1732 temporary=*image->filename == '\0' ? MagickTrue : MagickFalse;
1733 if ((temporary != MagickFalse) && (AcquireUniqueFilename(image->filename) ==
1734 MagickFalse))
1735 {
1736 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
1737 image->filename);
1738 return(MagickFalse);
1739 }
1740 delegate_info=GetDelegateInfo(decode,encode,exception);
1741 if (delegate_info == (DelegateInfo *) NULL)
1742 {
1743 if (temporary != MagickFalse)
1744 (void) RelinquishUniqueFileResource(image->filename);
1745 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1746 "NoTagFound","`%s'",decode ? decode : encode);
1747 return(MagickFalse);
1748 }
1749 if (*image_info->filename == '\0')
1750 {
1751 if (AcquireUniqueFilename(image_info->filename) == MagickFalse)
1752 {
1753 if (temporary != MagickFalse)
1754 (void) RelinquishUniqueFileResource(image->filename);
1755 ThrowFileException(exception,FileOpenError,
1756 "UnableToCreateTemporaryFile",image_info->filename);
1757 return(MagickFalse);
1758 }
1759 image_info->temporary=MagickTrue;
1760 }
1761 if ((delegate_info->mode != 0) && (((decode != (const char *) NULL) &&
1762 (delegate_info->encode != (char *) NULL)) ||
1763 ((encode != (const char *) NULL) &&
1764 (delegate_info->decode != (char *) NULL))))
1765 {
1766 char
1767 *magick;
1768
1769 ImageInfo
1770 *clone_info;
1771
1772 Image
1773 *p;
1774
1775 /*
1776 Delegate requires a particular image format.
1777 */
1778 if (AcquireUniqueFilename(image_info->unique) == MagickFalse)
1779 {
1780 ThrowFileException(exception,FileOpenError,
1781 "UnableToCreateTemporaryFile",image_info->unique);
1782 return(MagickFalse);
1783 }
1784 magick=InterpretImageProperties(image_info,image,decode != (char *) NULL ?
1785 delegate_info->encode : delegate_info->decode,exception);
1786 if (magick == (char *) NULL)
1787 {
1788 (void) RelinquishUniqueFileResource(image_info->unique);
1789 if (temporary != MagickFalse)
1790 (void) RelinquishUniqueFileResource(image->filename);
1791 (void) ThrowMagickException(exception,GetMagickModule(),
1792 DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1793 return(MagickFalse);
1794 }
1795 LocaleUpper(magick);
1796 clone_info=CloneImageInfo(image_info);
1797 (void) CopyMagickString((char *) clone_info->magick,magick,
1798 MagickPathExtent);
1799 if (LocaleCompare(magick,"NULL") != 0)
1800 (void) CopyMagickString(image->magick,magick,MagickPathExtent);
1801 magick=DestroyString(magick);
1802 (void) FormatLocaleString(clone_info->filename,MagickPathExtent,"%s:",
1803 delegate_info->decode);
1804 (void) SetImageInfo(clone_info,(unsigned int) GetImageListLength(image),
1805 exception);
1806 (void) CopyMagickString(clone_info->filename,image_info->filename,
1807 MagickPathExtent);
1808 (void) CopyMagickString(image_info->filename,image->filename,
1809 MagickPathExtent);
1810 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
1811 {
1812 (void) FormatLocaleString(p->filename,MagickPathExtent,"%s:%s",
1813 delegate_info->decode,clone_info->filename);
1814 status=WriteImage(clone_info,p,exception);
1815 if (status == MagickFalse)
1816 {
1817 (void) RelinquishUniqueFileResource(image_info->unique);
1818 if (temporary != MagickFalse)
1819 (void) RelinquishUniqueFileResource(image->filename);
1820 clone_info=DestroyImageInfo(clone_info);
1821 (void) ThrowMagickException(exception,GetMagickModule(),
1822 DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1823 return(MagickFalse);
1824 }
1825 if (clone_info->adjoin != MagickFalse)
1826 break;
1827 }
1828 (void) RelinquishUniqueFileResource(image_info->unique);
1829 clone_info=DestroyImageInfo(clone_info);
1830 }
1831 /*
1832 Invoke delegate.
1833 */
1834 commands=StringToList(delegate_info->commands);
1835 if (commands == (char **) NULL)
1836 {
1837 if (temporary != MagickFalse)
1838 (void) RelinquishUniqueFileResource(image->filename);
1839 (void) ThrowMagickException(exception,GetMagickModule(),
1840 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1841 decode ? decode : encode);
1842 return(MagickFalse);
1843 }
1844 command=(char *) NULL;
1845 status=MagickTrue;
1846 (void) CopyMagickString(output_filename,image_info->filename,
1847 MagickPathExtent);
1848 (void) CopyMagickString(input_filename,image->filename,MagickPathExtent);
1849 for (i=0; commands[i] != (char *) NULL; i++)
1850 {
1851 (void) AcquireUniqueSymbolicLink(output_filename,image_info->filename);
1852 if (AcquireUniqueFilename(image_info->unique) == MagickFalse)
1853 {
1854 ThrowFileException(exception,FileOpenError,
1855 "UnableToCreateTemporaryFile",image_info->unique);
1856 break;
1857 }
1858 if (LocaleCompare(decode,"SCAN") != 0)
1859 {
1860 status=AcquireUniqueSymbolicLink(input_filename,image->filename);
1861 if (status == MagickFalse)
1862 {
1863 ThrowFileException(exception,FileOpenError,
1864 "UnableToCreateTemporaryFile",input_filename);
1865 break;
1866 }
1867 }
1868 status=MagickTrue;
1869 command=InterpretDelegateProperties(image_info,image,commands[i],exception);
1870 if (command != (char *) NULL)
1871 {
1872 /*
1873 Execute delegate.
1874 */
1875 if (ExternalDelegateCommand(delegate_info->spawn,image_info->verbose,
1876 command,(char *) NULL,exception) != 0)
1877 status=MagickFalse;
1878 if (delegate_info->spawn != MagickFalse)
1879 {
1880 ssize_t
1881 count;
1882
1883 /*
1884 Wait for input file to 'disappear', or maximum 2 seconds.
1885 */
1886 count=20;
1887 while ((count-- > 0) && (access_utf8(image->filename,F_OK) == 0))
1888 (void) MagickDelay(100); /* sleep 0.1 seconds */
1889 }
1890 command=DestroyString(command);
1891 }
1892 if (LocaleCompare(decode,"SCAN") != 0)
1893 {
1894 if (CopyDelegateFile(image->filename,input_filename,MagickFalse) == MagickFalse)
1895 (void) RelinquishUniqueFileResource(input_filename);
1896 }
1897 if ((strcmp(input_filename,output_filename) != 0) &&
1898 (CopyDelegateFile(image_info->filename,output_filename,MagickTrue) == MagickFalse))
1899 (void) RelinquishUniqueFileResource(output_filename);
1900 if (image_info->temporary != MagickFalse)
1901 (void) RelinquishUniqueFileResource(image_info->filename);
1902 (void) RelinquishUniqueFileResource(image_info->unique);
1903 (void) RelinquishUniqueFileResource(image_info->filename);
1904 (void) RelinquishUniqueFileResource(image->filename);
1905 if (status == MagickFalse)
1906 {
1907 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1908 "DelegateFailed","`%s'",commands[i]);
1909 break;
1910 }
1911 commands[i]=DestroyString(commands[i]);
1912 }
1913 (void) CopyMagickString(image_info->filename,output_filename,
1914 MagickPathExtent);
1915 (void) CopyMagickString(image->filename,input_filename,MagickPathExtent);
1916 /*
1917 Relinquish resources.
1918 */
1919 for ( ; commands[i] != (char *) NULL; i++)
1920 commands[i]=DestroyString(commands[i]);
1921 commands=(char **) RelinquishMagickMemory(commands);
1922 if (temporary != MagickFalse)
1923 (void) RelinquishUniqueFileResource(image->filename);
1924 return(status);
1925 }
1926
1927 /*
1928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1929 % %
1930 % %
1931 % %
1932 % L i s t D e l e g a t e I n f o %
1933 % %
1934 % %
1935 % %
1936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1937 %
1938 % ListDelegateInfo() lists the image formats to a file.
1939 %
1940 % The format of the ListDelegateInfo method is:
1941 %
1942 % MagickBooleanType ListDelegateInfo(FILE *file,ExceptionInfo *exception)
1943 %
1944 % A description of each parameter follows.
1945 %
1946 % o file: An pointer to a FILE.
1947 %
1948 % o exception: return any errors or warnings in this structure.
1949 %
1950 */
ListDelegateInfo(FILE * file,ExceptionInfo * exception)1951 MagickExport MagickBooleanType ListDelegateInfo(FILE *file,
1952 ExceptionInfo *exception)
1953 {
1954 const DelegateInfo
1955 **delegate_info;
1956
1957 char
1958 **commands,
1959 delegate[MagickPathExtent];
1960
1961 const char
1962 *path;
1963
1964 ssize_t
1965 i;
1966
1967 size_t
1968 number_delegates;
1969
1970 ssize_t
1971 j;
1972
1973 if (file == (const FILE *) NULL)
1974 file=stdout;
1975 delegate_info=GetDelegateInfoList("*",&number_delegates,exception);
1976 if (delegate_info == (const DelegateInfo **) NULL)
1977 return(MagickFalse);
1978 path=(const char *) NULL;
1979 for (i=0; i < (ssize_t) number_delegates; i++)
1980 {
1981 if (delegate_info[i]->stealth != MagickFalse)
1982 continue;
1983 if ((path == (const char *) NULL) ||
1984 (LocaleCompare(path,delegate_info[i]->path) != 0))
1985 {
1986 if (delegate_info[i]->path != (char *) NULL)
1987 (void) FormatLocaleFile(file,"\nPath: %s\n\n",delegate_info[i]->path);
1988 (void) FormatLocaleFile(file,"Delegate Command\n");
1989 (void) FormatLocaleFile(file,
1990 "-------------------------------------------------"
1991 "------------------------------\n");
1992 }
1993 path=delegate_info[i]->path;
1994 *delegate='\0';
1995 if (delegate_info[i]->encode != (char *) NULL)
1996 (void) CopyMagickString(delegate,delegate_info[i]->encode,
1997 MagickPathExtent);
1998 (void) ConcatenateMagickString(delegate," ",MagickPathExtent);
1999 delegate[8]='\0';
2000 commands=StringToList(delegate_info[i]->commands);
2001 if (commands == (char **) NULL)
2002 continue;
2003 (void) FormatLocaleFile(file,"%11s%c=%c%s ",delegate_info[i]->decode ?
2004 delegate_info[i]->decode : "",delegate_info[i]->mode <= 0 ? '<' : ' ',
2005 delegate_info[i]->mode >= 0 ? '>' : ' ',delegate);
2006 StripString(commands[0]);
2007 (void) FormatLocaleFile(file,"\"%s\"\n",commands[0]);
2008 for (j=1; commands[j] != (char *) NULL; j++)
2009 {
2010 StripString(commands[j]);
2011 (void) FormatLocaleFile(file," \"%s\"\n",commands[j]);
2012 }
2013 for (j=0; commands[j] != (char *) NULL; j++)
2014 commands[j]=DestroyString(commands[j]);
2015 commands=(char **) RelinquishMagickMemory(commands);
2016 }
2017 (void) fflush(file);
2018 delegate_info=(const DelegateInfo **)
2019 RelinquishMagickMemory((void *) delegate_info);
2020 return(MagickTrue);
2021 }
2022
2023 /*
2024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2025 % %
2026 % %
2027 % %
2028 + L o a d D e l e g a t e C a c h e %
2029 % %
2030 % %
2031 % %
2032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033 %
2034 % LoadDelegateCache() loads the delegate configurations which provides a
2035 % mapping between delegate attributes and a delegate name.
2036 %
2037 % The format of the LoadDelegateCache method is:
2038 %
2039 % MagickBooleanType LoadDelegateCache(LinkedListInfo *cache,
2040 % const char *xml,const char *filename,const size_t depth,
2041 % ExceptionInfo *exception)
2042 %
2043 % A description of each parameter follows:
2044 %
2045 % o xml: The delegate list in XML format.
2046 %
2047 % o filename: The delegate list filename.
2048 %
2049 % o depth: depth of <include /> statements.
2050 %
2051 % o exception: return any errors or warnings in this structure.
2052 %
2053 */
LoadDelegateCache(LinkedListInfo * cache,const char * xml,const char * filename,const size_t depth,ExceptionInfo * exception)2054 static MagickBooleanType LoadDelegateCache(LinkedListInfo *cache,
2055 const char *xml,const char *filename,const size_t depth,
2056 ExceptionInfo *exception)
2057 {
2058 char
2059 keyword[MagickPathExtent],
2060 *token;
2061
2062 const char
2063 *q;
2064
2065 DelegateInfo
2066 *delegate_info;
2067
2068 MagickStatusType
2069 status;
2070
2071 size_t
2072 extent;
2073
2074 /*
2075 Load the delegate map file.
2076 */
2077 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
2078 "Loading delegate configuration file \"%s\" ...",filename);
2079 if (xml == (const char *) NULL)
2080 return(MagickFalse);
2081 status=MagickTrue;
2082 delegate_info=(DelegateInfo *) NULL;
2083 token=AcquireString(xml);
2084 extent=strlen(token)+MagickPathExtent;
2085 for (q=(const char *) xml; *q != '\0'; )
2086 {
2087 /*
2088 Interpret XML.
2089 */
2090 (void) GetNextToken(q,&q,extent,token);
2091 if (*token == '\0')
2092 break;
2093 (void) CopyMagickString(keyword,token,MagickPathExtent);
2094 if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
2095 {
2096 /*
2097 Doctype element.
2098 */
2099 while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
2100 (void) GetNextToken(q,&q,extent,token);
2101 continue;
2102 }
2103 if (LocaleNCompare(keyword,"<!--",4) == 0)
2104 {
2105 /*
2106 Comment element.
2107 */
2108 while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
2109 (void) GetNextToken(q,&q,extent,token);
2110 continue;
2111 }
2112 if (LocaleCompare(keyword,"<include") == 0)
2113 {
2114 /*
2115 Include element.
2116 */
2117 while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
2118 {
2119 (void) CopyMagickString(keyword,token,MagickPathExtent);
2120 (void) GetNextToken(q,&q,extent,token);
2121 if (*token != '=')
2122 continue;
2123 (void) GetNextToken(q,&q,extent,token);
2124 if (LocaleCompare(keyword,"file") == 0)
2125 {
2126 if (depth > MagickMaxRecursionDepth)
2127 (void) ThrowMagickException(exception,GetMagickModule(),
2128 ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
2129 else
2130 {
2131 char
2132 path[MagickPathExtent],
2133 *file_xml;
2134
2135 GetPathComponent(filename,HeadPath,path);
2136 if (*path != '\0')
2137 (void) ConcatenateMagickString(path,DirectorySeparator,
2138 MagickPathExtent);
2139 if (*token == *DirectorySeparator)
2140 (void) CopyMagickString(path,token,MagickPathExtent);
2141 else
2142 (void) ConcatenateMagickString(path,token,MagickPathExtent);
2143 file_xml=FileToXML(path,~0UL);
2144 if (file_xml != (char *) NULL)
2145 {
2146 status&=LoadDelegateCache(cache,file_xml,path,
2147 depth+1,exception);
2148 file_xml=DestroyString(file_xml);
2149 }
2150 }
2151 }
2152 }
2153 continue;
2154 }
2155 if (LocaleCompare(keyword,"<delegate") == 0)
2156 {
2157 /*
2158 Delegate element.
2159 */
2160 delegate_info=(DelegateInfo *) AcquireCriticalMemory(
2161 sizeof(*delegate_info));
2162 (void) memset(delegate_info,0,sizeof(*delegate_info));
2163 delegate_info->path=ConstantString(filename);
2164 delegate_info->thread_support=MagickTrue;
2165 delegate_info->signature=MagickCoreSignature;
2166 continue;
2167 }
2168 if (delegate_info == (DelegateInfo *) NULL)
2169 continue;
2170 if ((LocaleCompare(keyword,"/>") == 0) ||
2171 (LocaleCompare(keyword,"</policy>") == 0))
2172 {
2173 status=AppendValueToLinkedList(cache,delegate_info);
2174 if (status == MagickFalse)
2175 (void) ThrowMagickException(exception,GetMagickModule(),
2176 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2177 delegate_info->commands);
2178 delegate_info=(DelegateInfo *) NULL;
2179 continue;
2180 }
2181 (void) GetNextToken(q,(const char **) NULL,extent,token);
2182 if (*token != '=')
2183 continue;
2184 (void) GetNextToken(q,&q,extent,token);
2185 (void) GetNextToken(q,&q,extent,token);
2186 switch (*keyword)
2187 {
2188 case 'C':
2189 case 'c':
2190 {
2191 if (LocaleCompare((char *) keyword,"command") == 0)
2192 {
2193 char
2194 *commands;
2195
2196 commands=AcquireString(token);
2197 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
2198 if (strchr(commands,'@') != (char *) NULL)
2199 {
2200 char
2201 path[MagickPathExtent];
2202
2203 NTGhostscriptEXE(path,MagickPathExtent);
2204 (void) SubstituteString((char **) &commands,"@PSDelegate@",
2205 path);
2206 (void) SubstituteString((char **) &commands,"\\","/");
2207 }
2208 (void) SubstituteString((char **) &commands,""","\"");
2209 #else
2210 (void) SubstituteString((char **) &commands,""","'");
2211 #endif
2212 (void) SubstituteString((char **) &commands,"&","&");
2213 (void) SubstituteString((char **) &commands,">",">");
2214 (void) SubstituteString((char **) &commands,"<","<");
2215 delegate_info->commands=commands;
2216 break;
2217 }
2218 break;
2219 }
2220 case 'D':
2221 case 'd':
2222 {
2223 if (LocaleCompare((char *) keyword,"decode") == 0)
2224 {
2225 delegate_info->decode=ConstantString(token);
2226 delegate_info->mode=1;
2227 break;
2228 }
2229 break;
2230 }
2231 case 'E':
2232 case 'e':
2233 {
2234 if (LocaleCompare((char *) keyword,"encode") == 0)
2235 {
2236 delegate_info->encode=ConstantString(token);
2237 delegate_info->mode=(-1);
2238 break;
2239 }
2240 break;
2241 }
2242 case 'M':
2243 case 'm':
2244 {
2245 if (LocaleCompare((char *) keyword,"mode") == 0)
2246 {
2247 delegate_info->mode=1;
2248 if (LocaleCompare(token,"bi") == 0)
2249 delegate_info->mode=0;
2250 else
2251 if (LocaleCompare(token,"encode") == 0)
2252 delegate_info->mode=(-1);
2253 break;
2254 }
2255 break;
2256 }
2257 case 'S':
2258 case 's':
2259 {
2260 if (LocaleCompare((char *) keyword,"spawn") == 0)
2261 {
2262 delegate_info->spawn=IsStringTrue(token);
2263 break;
2264 }
2265 if (LocaleCompare((char *) keyword,"stealth") == 0)
2266 {
2267 delegate_info->stealth=IsStringTrue(token);
2268 break;
2269 }
2270 break;
2271 }
2272 case 'T':
2273 case 't':
2274 {
2275 if (LocaleCompare((char *) keyword,"thread-support") == 0)
2276 {
2277 delegate_info->thread_support=IsStringTrue(token);
2278 if (delegate_info->thread_support == MagickFalse)
2279 delegate_info->semaphore=AcquireSemaphoreInfo();
2280 break;
2281 }
2282 break;
2283 }
2284 default:
2285 break;
2286 }
2287 }
2288 token=(char *) RelinquishMagickMemory(token);
2289 return(status != 0 ? MagickTrue : MagickFalse);
2290 }
2291