1 /*****************************************************************************/
2 /*  LibreDWG - free implementation of the DWG file format                    */
3 /*                                                                           */
4 /*  Copyright (C) 2010-2020 Free Software Foundation, Inc.                   */
5 /*                                                                           */
6 /*  This library is free software, licensed under the terms of the GNU       */
7 /*  General Public License as published by the Free Software Foundation,     */
8 /*  either version 3 of the License, or (at your option) any later version.  */
9 /*  You should have received a copy of the GNU General Public License        */
10 /*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
11 /*****************************************************************************/
12 
13 /*
14  * dwgrewrite.c: load a DWG file and rewrite it,
15  * optionally as a different version.
16  *
17  * written by Anderson Pierre Cardoso
18  * modified by Reini Urban
19  */
20 
21 #include "../src/config.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 
29 #include <dwg.h>
30 #include "../src/common.h"
31 #include "suffix.inc"
32 
33 // avoid the slow fork loop, for afl-clang-fast
34 #ifdef __AFL_COMPILER
35 static volatile const char *__afl_persistent_sig = "##SIG_AFL_PERSISTENT##";
36 #endif
37 
38 static int opts = 1;
39 static int help (void);
40 
41 static int
usage(void)42 usage (void)
43 {
44   printf ("\nUsage: dwgrewrite [-v[N]] [--as rNNNN] <dwg_input_file.dwg> "
45           "[<dwg_output_file.dwg>]\n");
46   return 1;
47 }
48 static int
opt_version(void)49 opt_version (void)
50 {
51   printf ("dwgrewrite %s\n", PACKAGE_VERSION);
52   return 0;
53 }
54 static int
help(void)55 help (void)
56 {
57   printf ("\nUsage: dwgrewrite [OPTION]... INFILE [OUTFILE]\n");
58   printf ("Rewrites the DWG as another DWG.\n");
59   printf ("Default OUTFILE: INFILE with <-rewrite.dwg> appended.\n"
60           "\n");
61 #ifdef HAVE_GETOPT_LONG
62   printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
63   printf ("  --as rNNNN                save as version\n");
64   printf ("           Valid versions:\n");
65   printf ("             r12, r14, r2000\n");
66   printf ("           Planned versions:\n");
67   printf ("             r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n");
68   printf ("  -o dwgfile, --file        \n");
69   printf ("           --help           display this help and exit\n");
70   printf ("           --version        output version information and exit\n"
71           "\n");
72 #else
73   printf ("  -v[0-9]     verbosity\n");
74   printf ("  -a rNNNN    save as version\n");
75   printf ("              Valid versions:\n");
76   printf ("                r12, r14, r2000 (default)\n");
77   printf ("              Planned versions:\n");
78   printf ("                r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n");
79   printf ("  -o dwgfile\n");
80   printf ("  -h          display this help and exit\n");
81   printf ("  -i          output version information and exit\n"
82           "\n");
83 #endif
84   printf ("GNU LibreDWG online manual: "
85           "<https://www.gnu.org/software/libredwg/>\n");
86   return 0;
87 }
88 
89 #ifdef __AFL_COMPILER
90 #include "bits.h"
91 #include "decode.h"
92 #include "encode.h"
93 __AFL_FUZZ_INIT();
main(int argc,char * argv[])94 int main (int argc, char *argv[])
95 {
96   Dwg_Data dwg;
97   Bit_Chain dat = { NULL, 0, 0, 0, 0 };
98   Bit_Chain out_dat = { NULL, 0, 0, 0, 0 };
99   FILE *fp;
100 
101   __AFL_INIT();
102   printf ("Fuzzing decode + encode + decode from shared memory\n");
103 
104   while (__AFL_LOOP(10000)) { // llvm_mode persistent, non-forking mode
105 #if 1 // fastest mode via shared mem (crashes still)
106     dat.chain = __AFL_FUZZ_TESTCASE_BUF;
107     dat.size = __AFL_FUZZ_TESTCASE_LEN;
108     //printf ("size: %lu\n", dat.size);
109 #elif 1 // still 1000x faster than the old file-forking fuzzer.
110     /* from stdin: */
111     dat.size = 0;
112     //dat.chain = NULL;
113     dat_read_stream (&dat, stdin);
114 #else
115     /* else from file */
116     fp = fopen (argv[1], "rb");
117     if (!fp)
118       return 0;
119     dat.size = 0;
120     dat_read_file (&dat, fp, argv[1]);
121     fclose (fp);
122 #endif
123     if (dat.size < 100) continue;  // useful minimum input length
124     // dwg in only
125     if (dwg_decode (&dat, &dwg) <= DWG_ERR_CRITICAL) {
126       memset (&out_dat, 0, sizeof (out_dat));
127       bit_chain_set_version (&out_dat, &dat);
128       out_dat.version = R_2000;
129       if (dwg_encode (&dwg, &out_dat) >= DWG_ERR_CRITICAL)
130         exit (0);
131       dwg_decode (&out_dat, &dwg);
132       free (out_dat.chain);
133     }
134     else
135       exit (0);
136   }
137   dwg_free (&dwg);
138 }
139 #define main orig_main
140 int orig_main (int argc, char *argv[]);
141 #endif
142 
main(int argc,char * argv[])143 int main (int argc, char *argv[])
144 {
145   int error;
146   int i = 1;
147   Dwg_Data dwg;
148   char *filename_in;
149   const char *version = NULL;
150   char *filename_out = NULL;
151   int free_fnout = 0;
152   Dwg_Version_Type dwg_version;
153   BITCODE_BL num_objects;
154   int c;
155 #ifdef HAVE_GETOPT_LONG
156   int option_index = 0;
157   static struct option long_options[]
158       = { { "verbose", 1, &opts, 1 }, // optional
159           { "file", 1, 0, 'o' },      { "as", 1, 0, 'a' }, { "help", 0, 0, 0 },
160           { "version", 0, 0, 0 },     { NULL, 0, NULL, 0 } };
161 #endif
162 
163   // check args
164   if (argc < 2)
165     return usage ();
166 
167   while
168 #ifdef HAVE_GETOPT_LONG
169       ((c = getopt_long (argc, argv, ":a:v::o:h", long_options, &option_index))
170        != -1)
171 #else
172       ((c = getopt (argc, argv, ":a:v::o:hi")) != -1)
173 #endif
174     {
175       if (c == -1)
176         break;
177       switch (c)
178         {
179         case ':': // missing arg
180           if (optarg && !strcmp (optarg, "v"))
181             {
182               opts = 1;
183               break;
184             }
185           fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
186                    optopt);
187           break;
188 #ifdef HAVE_GETOPT_LONG
189         case 0:
190           /* This option sets a flag */
191           if (!strcmp (long_options[option_index].name, "verbose"))
192             {
193               if (opts < 0 || opts > 9)
194                 return usage ();
195 #  if defined(USE_TRACING) && defined(HAVE_SETENV)
196               {
197                 char v[2];
198                 *v = opts + '0';
199                 *(v + 1) = 0;
200                 setenv ("LIBREDWG_TRACE", v, 1);
201               }
202 #  endif
203               break;
204             }
205           if (!strcmp (long_options[option_index].name, "version"))
206             return opt_version ();
207           if (!strcmp (long_options[option_index].name, "help"))
208             return help ();
209           break;
210 #else
211         case 'i':
212           return opt_version ();
213 #endif
214         case 'o':
215           filename_out = optarg;
216           break;
217         case 'a':
218           dwg_version = dwg_version_as (optarg);
219           if (dwg_version == R_INVALID)
220             {
221               fprintf (stderr, "Invalid version '%s'\n", argv[1]);
222               return usage ();
223             }
224           version = optarg;
225           break;
226         case 'v': // support -v3 and -v
227           i = (optind > 0 && optind < argc) ? optind - 1 : 1;
228           if (!memcmp (argv[i], "-v", 2))
229             {
230               opts = argv[i][2] ? argv[i][2] - '0' : 1;
231             }
232           if (opts < 0 || opts > 9)
233             return usage ();
234 #if defined(USE_TRACING) && defined(HAVE_SETENV)
235           {
236             char v[2];
237             *v = opts + '0';
238             *(v + 1) = 0;
239             setenv ("LIBREDWG_TRACE", v, 1);
240           }
241 #endif
242           break;
243         case 'h':
244           return help ();
245         case '?':
246           fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
247                    optopt);
248           break;
249         default:
250           return usage ();
251         }
252     }
253   i = optind;
254 
255   memset (&dwg, 0, sizeof (Dwg_Data));
256   dwg.opts = opts & 0xf;
257 
258 #ifdef __AFL_HAVE_MANUAL_CONTROL
259   __AFL_INIT();
260   while (__AFL_LOOP(1000)) {
261 #endif
262 
263   filename_in = argv[i];
264   if (!filename_in)
265     {
266       puts ("No input file specified");
267       return 1;
268     }
269   if (!filename_out)
270     {
271       if (argc > i + 1)
272         filename_out = argv[i + 1];
273       else
274         {
275           free_fnout = 1;
276           filename_out = suffix (filename_in, "-rewrite.dwg");
277         }
278     }
279   if (!filename_out || !strcmp (filename_in, filename_out))
280     {
281       if (free_fnout)
282         free (filename_out);
283       return usage ();
284     }
285 
286   /*
287    * some very simple testing
288    */
289   printf ("Reading DWG file %s\n", filename_in);
290   error = dwg_read_file (filename_in, &dwg); /* 1st read */
291   if (error >= DWG_ERR_CRITICAL)
292     fprintf (stderr, "READ ERROR 0x%x\n", error);
293   num_objects = dwg.num_objects;
294   if (!num_objects)
295     {
296       printf ("Read 0 objects\n");
297       if (error >= DWG_ERR_CRITICAL)
298         {
299           if (free_fnout)
300             free (filename_out);
301           dwg_free (&dwg);
302           return error;
303         }
304     }
305 
306   //if (opts)
307   //  printf ("\n");
308   printf ("Writing DWG file %s", filename_out);
309   if (version)
310     { // forced -as-rXXX
311       printf (" as %s\n", version);
312       if (dwg.header.from_version != dwg.header.version)
313         dwg.header.from_version = dwg.header.version;
314       // else keep from_version
315       dwg.header.version = dwg_version;
316     }
317   else if (dwg.header.version < R_13 || dwg.header.version > R_2000)
318     {
319       // we cannot yet write pre-r13 or 2004+
320       printf (" as r2000\n");
321       dwg.header.version = R_2000;
322     }
323   else
324     {
325       printf ("\n");
326     }
327 
328   {
329     struct stat attrib;
330     if (!stat (filename_out, &attrib)) // exists
331       {
332         if (S_ISREG (attrib.st_mode) &&        // refuse to remove a directory
333             (access (filename_out, W_OK) == 0) // is writable
334 #ifndef _WIN32
335             // refuse to remove a symlink. even with overwrite. security
336             && !S_ISLNK (attrib.st_mode)
337 #endif
338             )
339           unlink (filename_out);
340         else
341           {
342             fprintf (stderr, "ERROR: Not writable file or symlink: %s\n",
343                      filename_out);
344             error |= DWG_ERR_IOERROR;
345           }
346       }
347   }
348 
349   error = dwg_write_file (filename_out, &dwg);
350   if (error >= DWG_ERR_CRITICAL)
351     {
352       printf ("WRITE ERROR 0x%x\n", error);
353 #ifndef IS_RELEASE
354       // try to read the halfway written r2004 file.
355       if (!(version && error == DWG_ERR_SECTIONNOTFOUND))
356 #endif
357       {
358         if (free_fnout)
359           free (filename_out);
360         dwg_free (&dwg);
361         return error;
362       }
363     }
364   dwg_free (&dwg);
365 
366   // try to read again
367   //if (opts)
368   //  printf ("\n");
369   printf ("Re-reading created file %s\n", filename_out);
370   error = dwg_read_file (filename_out, &dwg); /* 2nd read */
371   if (error >= DWG_ERR_CRITICAL)
372     printf ("re-READ ERROR 0x%x\n", error);
373   if (num_objects && (num_objects != dwg.num_objects))
374     printf ("re-READ num_objects: %lu, should be %lu\n",
375             (unsigned long)dwg.num_objects, (unsigned long)num_objects);
376   dwg_free (&dwg);
377 
378 #ifdef __AFL_HAVE_MANUAL_CONTROL
379   }
380 #endif
381 
382   if (free_fnout)
383     free (filename_out);
384   return error >= DWG_ERR_CRITICAL ? error : 0;
385 }
386