Minor security fix: mod_imagemagick used popen to run covert, change it to use pipe...
[synfig.git] / synfig-core / trunk / src / modules / mod_imagemagick / trgt_imagemagick.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file trgt_imagemagick.cpp
3 **      \brief ppm Target Module
4 **
5 **      \legal
6 ** $Id$
7 **
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 **
21 ** === N O T E S ===========================================================
22 **
23 ** ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #define SYNFIG_TARGET
28
29 #ifdef USING_PCH
30 #       include "pch.h"
31 #else
32 #ifdef HAVE_CONFIG_H
33 #       include <config.h>
34 #endif
35
36 #include <ETL/stringf>
37 #include "trgt_imagemagick.h"
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 #include <algorithm>
43 #include <functional>
44 #include <ETL/clock>
45 #include <ETL/misc>
46
47 #endif
48
49 /* === M A C R O S ========================================================= */
50
51 using namespace synfig;
52 using namespace std;
53 using namespace etl;
54
55 /* === G L O B A L S ======================================================= */
56
57 SYNFIG_TARGET_INIT(imagemagick_trgt);
58 SYNFIG_TARGET_SET_NAME(imagemagick_trgt,"imagemagick");
59 SYNFIG_TARGET_SET_EXT(imagemagick_trgt,"miff");
60 SYNFIG_TARGET_SET_VERSION(imagemagick_trgt,"0.1");
61 SYNFIG_TARGET_SET_CVS_ID(imagemagick_trgt,"$Id$");
62
63 /* === M E T H O D S ======================================================= */
64
65 imagemagick_trgt::imagemagick_trgt(const char *Filename)
66 {
67         pid=-1;
68         file=NULL;
69         filename=Filename;
70         multi_image=false;
71         buffer=NULL;
72         color_buffer=0;
73 }
74
75 imagemagick_trgt::~imagemagick_trgt()
76 {
77         if(file){
78                 fclose(file);
79                 int status;
80                 waitpid(pid,&status,0);
81         }
82         file=NULL;
83         delete [] buffer;
84         delete [] color_buffer;
85 }
86
87 bool
88 imagemagick_trgt::set_rend_desc(RendDesc *given_desc)
89 {
90         if(filename_extension(filename) == ".xpm")
91                 pf=PF_RGB;
92         else
93                 pf=PF_RGB|PF_A;
94
95         desc=*given_desc;
96         return true;
97 }
98
99 bool
100 imagemagick_trgt::init()
101 {
102         imagecount=desc.get_frame_start();
103         if(desc.get_frame_end()-desc.get_frame_start()>0)
104                 multi_image=true;
105
106         delete [] buffer;
107         buffer=new unsigned char[channels(pf)*desc.get_w()];
108         delete [] color_buffer;
109         color_buffer=new Color[desc.get_w()];
110         return true;
111 }
112
113 void
114 imagemagick_trgt::end_frame()
115 {
116         if(file)
117         {
118                 fputc(0,file);
119                 fflush(file);
120                 fclose(file);
121                 int status;
122                 waitpid(pid,&status,0);
123         }
124         file=NULL;
125         imagecount++;
126 }
127
128 bool
129 imagemagick_trgt::start_frame(synfig::ProgressCallback *cb)
130 {
131         const char *msg=_("Unable to open pipe to imagemagick's convert utility");
132
133         string newfilename;
134
135         if (multi_image)
136                 newfilename = (filename_sans_extension(filename) +
137                                            etl::strprintf(".%04d",imagecount) +
138                                            filename_extension(filename));
139         else
140                 newfilename = filename;
141
142         int p[2];
143   
144         if (pipe(p)) {
145                 if(cb) cb->error(N_(msg));
146                 else synfig::error(N_(msg));
147                 return false;
148         };
149   
150         pid = fork();
151   
152         if (pid == -1) {
153                 if(cb) cb->error(N_(msg));
154                 else synfig::error(N_(msg));
155                 return false;
156         }
157         
158         if (pid == 0){
159                 // Child process
160                 // Close pipeout, not needed
161                 close(p[1]);
162                 // Dup pipeout to stdin
163                 if( dup2( p[0], STDIN_FILENO ) == -1 ){
164                         if(cb) cb->error(N_(msg));
165                         else synfig::error(N_(msg));
166                         return false;
167                 }
168                 // Close the unneeded pipeout
169                 close(p[0]);
170                 execlp("convert", "convert",
171                         "-depth", "8",
172                         "-size", strprintf("%dx%d", desc.get_w(), desc.get_h()).c_str(),
173                         ((channels(pf) == 4) ? "rgba:-[0]" : "rgb:-[0]"),
174                         "-density", strprintf("%dx%d", round_to_int(desc.get_x_res()/39.3700787402), round_to_int(desc.get_y_res()/39.3700787402)).c_str(),
175                         newfilename.c_str(),
176                         (const char *)NULL);
177                 // We should never reach here unless the exec failed
178                 if(cb) cb->error(N_(msg));
179                 else synfig::error(N_(msg));
180                 return false;
181         } else {
182                 // Parent process
183                 // Close pipein, not needed
184                 close(p[0]);
185                 // Save pipeout to file handle, will write to it later
186                 file = fdopen(p[1], "wb");
187         }
188
189         if(!file)
190         {
191                 if(cb)cb->error(N_(msg));
192                 else synfig::error(N_(msg));
193                 return false;
194         }
195
196         //etl::yield();
197
198         return true;
199 }
200
201 Color *
202 imagemagick_trgt::start_scanline(int /*scanline*/)
203 {
204         return color_buffer;
205 }
206
207 bool
208 imagemagick_trgt::end_scanline(void)
209 {
210         if(!file)
211                 return false;
212
213         convert_color_format(buffer, color_buffer, desc.get_w(), pf, gamma());
214
215         if(!fwrite(buffer,channels(pf),desc.get_w(),file))
216                 return false;
217
218         return true;
219 }