1 /* lowlevelfunc.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2001-2019 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "lowlevelfunc.h"
23 #include <locale.h>
24 #include <algorithm>
25 #include "utf8.hh"
26 
_allocsafe(size_t size)27 void *_allocsafe(size_t size)
28 { /* gibt Speicher zur�ck oder beendet sofort das Programm */
29   void *ptr;
30 
31   ptr=(void*)malloc(size);
32   if(ptr==NULL) {
33     fprintf( stderr, "Worker Error:No Memory!!!!Aborting\n" );
34     exit(1);
35   }
36   return ptr;
37 }
38 
waittime(unsigned long msec)39 void waittime(unsigned long msec)
40 {
41 #ifdef HAVE_NANOSLEEP
42   struct timespec wt;
43   wt.tv_nsec = ( msec % 1000 ) * 1000000;
44   wt.tv_sec = msec / 1000;
45   nanosleep( &wt, NULL );
46 #else
47 
48 #ifdef HAVE_USLEEP
49   usleep( msec * 1000 );
50 #else
51 
52   /* configure doesn't allow continue without nanosleep and usleep
53    * but worker could run without a working waittime (but not very nice)
54    * just throw a warning
55    */
56 #warning empty wait-function
57 
58 #endif /* HAVE_USLEEP */
59 
60 #endif /* HAVE_NANOSLEEP */
61 }
62 
dupstring(const char * str)63 char *dupstring(const char *str)
64 {
65   char *tstr;
66   tstr=(char*)_allocsafe(strlen(str)+1);
67   strcpy(tstr,str);
68   return tstr;
69 }
70 
MakeLong2NiceStr(loff_t size,std::string & buffer,bool do_nice)71 int MakeLong2NiceStr( loff_t size, std::string &buffer, bool do_nice )
72 {
73   static std::string own_thousands_sep;
74   struct lconv *current_locale;
75   std::string numstr;
76   loff_t tmpval = size;
77   int digit, i;
78 
79   if ( own_thousands_sep.length() < 1 ) {
80     // thousands separator isn't set so try to determine it
81     current_locale = localeconv();
82     /* check if the locale defines thousands_sep */
83     if ( ( current_locale->thousands_sep != NULL ) &&
84 	 ( *( current_locale->thousands_sep ) != 0 ) ) {
85       /* yes, it does so store the string */
86       own_thousands_sep = current_locale->thousands_sep;
87     } else {
88       // default is the dot
89       own_thousands_sep = ".";
90     }
91 
92     // reverse string because we will build the number backwards
93     std::reverse( own_thousands_sep.begin(),
94                   own_thousands_sep.end() );
95   }
96 
97   numstr = "";
98   i = 0;
99   do {
100     digit = abs( (int)( tmpval % 10 ) );
101     tmpval /= 10;
102 
103     if ( ( do_nice == true ) && ( i != 0 ) && ( i % 3 == 0 ) )
104       numstr += own_thousands_sep;
105 
106     numstr += '0' + digit;
107     i++;
108   } while ( tmpval != 0 );
109   if ( size < 0 ) numstr += '-';
110 
111   std::reverse( numstr.begin(), numstr.end() );
112   buffer = numstr;
113 
114   return numstr.length();
115 }
116 
catstring(const char * str1,const char * str2)117 char *catstring(const char *str1,const char *str2)
118 {
119   char *tstr;
120   tstr=(char*)_allocsafe(strlen(str1)+strlen(str2)+1);
121   strcpy(tstr,str1);
122   strcpy(tstr+strlen(str1),str2);
123   return tstr;
124 }
125 
shrinkstring(const char * str,int maxlen,AWidth & lencalc)126 char *shrinkstring( const char *str, int maxlen, AWidth &lencalc )
127 {
128   int width, rwidth, ddd_width, real_width, rest_width, lwidth;
129   int llen;
130   char *newstr;
131   const char *hitstr, *trystr;
132   std::string str1;
133   size_t charlen;
134   int pos;
135 
136   width = lencalc.getWidth( str );
137 
138   if ( width <= maxlen ) return dupstring( str );
139 
140   ddd_width = lencalc.getWidth( "..." );
141 
142   charlen = UTF8::getLenOfCharacter( str );
143   lwidth = lencalc.getWidth( str, charlen );
144 
145   if ( ( lwidth + ddd_width ) <= maxlen) {
146     rest_width = maxlen - ddd_width;
147 
148     llen = lencalc.getStrlen4Width( str, rest_width / 2, &real_width );
149     str1 = "";
150     str1.append( str, llen );
151     str1 += "...";
152 
153     rest_width -= real_width;
154 
155     // now find remaining character from right end
156     hitstr = NULL;
157 
158     pos = strlen( str );
159     UTF8::movePosToPrevChar( str, pos );
160     trystr = str + pos;
161     while ( trystr >= str ) {
162       rwidth = lencalc.getWidth( trystr );
163       if ( rwidth <= rest_width ) hitstr = trystr;
164       else break;
165       if ( UTF8::movePosToPrevChar( str, pos ) < 1 ) break;
166       trystr = str + pos;
167     }
168 
169     if ( hitstr != NULL ) {
170       str1 += hitstr;
171     }
172 
173     newstr = dupstring( str1.c_str() );
174   } else {
175     llen = lencalc.getStrlen4Width( str, maxlen, &real_width );
176     newstr = dupstring( str );
177     newstr[llen] = 0;
178   }
179   return newstr;
180 }
181 
diffgtod(struct timeval * tv1,struct timeval * tv0)182 double diffgtod( struct timeval *tv1, struct timeval *tv0 )
183 {
184   long s, us;
185 
186   if ( ( tv1 == NULL ) || ( tv0 == NULL ) ) return 0.0;
187 
188   s = tv1->tv_sec - tv0->tv_sec;
189   us = tv1->tv_usec - tv0->tv_usec;
190 
191   return ((double)s) * 1000000.0 + (double)us;
192 }
193 
ldiffgtod(struct timeval * tv1,struct timeval * tv0)194 long ldiffgtod( struct timeval *tv1, struct timeval *tv0 )
195 {
196   long s, us;
197 
198   if ( ( tv1 == NULL ) || ( tv0 == NULL ) ) return 0;
199 
200   s = tv1->tv_sec - tv0->tv_sec;
201   us = tv1->tv_usec - tv0->tv_usec;
202 
203   return s * 1000000 + us;
204 }
205 
ldiffgtod_m(const struct timeval * tv1,const struct timeval * tv0)206 long ldiffgtod_m( const struct timeval *tv1, const struct timeval *tv0 )
207 {
208   long s, us;
209 
210   if ( ( tv1 == NULL ) || ( tv0 == NULL ) ) return 0;
211 
212   s = tv1->tv_sec - tv0->tv_sec;
213   us = tv1->tv_usec - tv0->tv_usec;
214 
215   return s * 1000 + us / 1000;
216 }
217 
218 /* this function calculate the quote mode at the end of str
219    0 = no quotes
220    1 = single quote
221    2 = double quote
222    -1 = error
223 */
AGUIX_getQuoteMode(const char * str)224 int AGUIX_getQuoteMode( const char *str )
225 {
226   int quotemode = 0; /* 0 means no quote
227 			1 means in single quotes
228 			2 means in double quotes */
229   int i, len;
230 
231   if ( str == NULL ) return -1;
232 
233   len = strlen( str );
234 
235   for ( i = 0; i < len; i++ ) {
236     switch ( quotemode ) {
237     case 1:
238       // even backslash doesn't matter in single-quotes so just check for closing quote
239       if ( str[i] == '\'' ) {
240 	// you cannot do single quote inside double quote so we return to "no quote"
241 	quotemode = 0;
242       }
243       break;
244     case 2:
245       // this is more complicated
246       // the backslash doesn't protect everything just special characters
247       // anyway if the following char isn't special we don't care on it
248       // and if it's a special we also don't care because it's backslashed
249       // so it's safe to skip next char in any case
250       if ( str[i] == '\\' ) {
251 	i++;
252       } else if ( str[i] == '"' ) {
253 	// remember that we skiped this char if it was backslashed
254 	// so we really have to leave quote mode and for the same reason
255 	// as above it's not possibly to do double quote in single quote
256 	// we can go to "no quotes" mode
257 	quotemode = 0;
258       }
259       break;
260     default:
261       if ( str[i] == '\\' ) {
262 	// not in quotes => ignore next
263 	i++;
264       } else if ( str[i] == '"' ) {
265 	// double quote begin
266 	quotemode = 2;
267       } else if ( str[i] == '\'' ) {
268 	// single quote begin
269 	quotemode = 1;
270       }
271       break;
272     }
273   }
274   return quotemode;
275 }
276 
277 /* this function will cat 2 strings:
278    str1 have to be a string which STARTS unquoted (can begin with quotes
279    but it's interpreted as beginning quotes not ending for a another unknown
280    string!
281    str2 is an untrusted string and will be single quoted in any case
282    depending on quote-status at the end of str1, str2 is correctly
283    quoted in single-quotes so it can be used in shell-script to avoid problems
284    for filenames like test`rm -rf /`
285 
286    str1 is not changed but between str1 and str2 and at the end
287    I add the correct quotes
288    str2 can be changed (single quotes become '\'' (to avoid any known and unknown shell actions) )
289 
290    return string will always end in same quotes like str1
291 */
AGUIX_catTrustedAndUnTrusted(const char * str1,const char * str2)292 char *AGUIX_catTrustedAndUnTrusted( const char *str1, const char *str2 )
293 {
294   int quotemode = 0; /* 0 means no quote
295 			1 means in single quotes
296 			2 means in double quotes */
297   int i, o, lenstr1, lenstr2, sqcount;
298   char *newstr2, *newstr, *tstr1;
299 
300   if ( ( str1 == NULL ) || ( str2 == NULL ) ) return NULL;
301 
302   // first fix backslashed str1 because it a last backslash
303   // will protect the quoting for str2
304   tstr1 = AGUIX_fixBackslashed( str1 );
305   if ( tstr1 == NULL ) return NULL;
306 
307   lenstr1 = strlen( tstr1 );
308 
309   quotemode = AGUIX_getQuoteMode( tstr1 );
310   if ( quotemode < 0 ) return NULL;
311 
312   // now we know in which quote mode str1 is at the end
313   // se we can do the following:
314   // in single quotes we have just to replace singlequotes with '\''
315   // in double quotes we have to close double close and add single quoted filename
316   // in no quotes we just have to add single quoted filename
317 
318   // since filename will always be inside single quotes first replace all single quotes
319   // in filename with '\''
320   lenstr2 = strlen( str2 );
321   sqcount = 0;
322   for ( i = 0; i < lenstr2; i++ ) {
323     if ( str2[i] == '\'' ) sqcount++;
324   }
325   // for every single quote we need 3 additional chars
326   newstr2 = (char*)_allocsafe( lenstr2 + sqcount * 3 +
327                                1 /* null byte */ +
328                                3 /* additional dummy bytes for optimized strlen */ );
329   for ( i = 0, o = 0; i < lenstr2; i++ ) {
330     if ( str2[i] == '\'' ) {
331       newstr2[o++] = '\'';
332       newstr2[o++] = '\\';
333       newstr2[o++] = '\'';
334       newstr2[o++] = '\'';
335     } else {
336       newstr2[o++] = str2[i];
337     }
338   }
339   newstr2[o] = '\0';
340 
341   /* just to keep optimized strlen and co quiet */
342   newstr2[o+1] = '\0';
343   newstr2[o+2] = '\0';
344   newstr2[o+3] = '\0';
345 
346   // newfilename is now okay
347   // space needed for newstr1:
348   // str1len
349   // perhaps a closing double quote and a beginning single quote
350   // strlen( newfilename )
351   // perhaps a closing single quote and beginning double quote
352   newstr = (char*)_allocsafe( lenstr1 + 2 + strlen( newstr2 ) + 2 + 1 );
353   switch ( quotemode ) {
354   case 1:
355     // single quote so no need for quote changing
356     // remember newfilename is correctly quoted to live inside
357     // single quote
358     sprintf( newstr, "%s%s", tstr1, newstr2 );
359     break;
360   case 2:
361     // double quotes so close double quotes, begin single quote
362     // and after newfilename close single quote and start double quote
363     sprintf( newstr, "%s\"'%s'\"", tstr1, newstr2 );
364     break;
365   default:
366     // no quote so just start single quote and after newfilename single quote
367     sprintf( newstr, "%s'%s'", tstr1, newstr2 );
368     break;
369   }
370   _freesafe( newstr2 );
371   _freesafe( tstr1 );
372   return newstr;
373 }
374 
375 /* this function replace all single quotes with '\'' so it can be
376    used in single quotes */
AGUIX_prepareForSingleQuote(const char * str1)377 char *AGUIX_prepareForSingleQuote( const char *str1 )
378 {
379   int i, o, lenstr1, sqcount;
380   char *newstr1;
381 
382   if ( str1 == NULL ) return NULL;
383 
384   // single quotes in filename with '\''
385   lenstr1 = strlen( str1 );
386   sqcount = 0;
387   for ( i = 0; i < lenstr1; i++ ) {
388     if ( str1[i] == '\'' ) sqcount++;
389   }
390   // for every single quote we need 3 additional chars
391   newstr1 = (char*)_allocsafe( lenstr1 + sqcount * 3 + 1 );
392   for ( i = 0, o = 0; i < lenstr1; i++ ) {
393     if ( str1[i] == '\'' ) {
394       newstr1[o++] = '\'';
395       newstr1[o++] = '\\';
396       newstr1[o++] = '\'';
397       newstr1[o++] = '\'';
398     } else {
399       newstr1[o++] = str1[i];
400     }
401   }
402   newstr1[o] = '\0';
403   return newstr1;
404 }
405 
406 /* this function will remove all quotes from the string
407    rules:
408    single quotes: allowed, no special character
409    double quotes: allowed, backslash escapes next character
410    single and double quotes can be mixed
411 */
AGUIX_unquoteString(const char * str1)412 char *AGUIX_unquoteString( const char *str1 )
413 {
414   char *newstr;
415   int quotemode = 0; /* 0 means no quote
416 			1 means in single quotes
417 			2 means in double quotes */
418   int i, o, len;
419 
420   if ( str1 == NULL ) return NULL;
421   newstr = (char*)_allocsafe( strlen( str1 ) + 1 );
422 
423   len = strlen( str1 );
424 
425   for ( i = 0, o = 0; i < len; i++ ) {
426     switch ( quotemode ) {
427     case 1:
428       // even backslash doesn't matter in single-quotes so just check for closing quote
429       if ( str1[i] == '\'' ) {
430 	// you cannot do single quote inside double quote so we return to "no quote"
431 	quotemode = 0;
432       } else {
433 	newstr[o++] = str1[i];
434       }
435       break;
436     case 2:
437       // this is more complicated
438       // the backslash doesn't protect everything just special characters
439       // anyway if the following char isn't special we don't care on it
440       // and if it's a special we also don't care because it's backslashed
441       // so it's safe to skip next char in any case
442       if ( str1[i] == '\\' ) {
443 	// only backslash $, `, ", <backslash
444 	// (just like shell (at least bash))
445 	switch( str1[i + 1] ) {
446 	case '$':
447 	case '`':
448 	case '"':
449 	case '\\':
450 	  i++;
451 	default:
452 	  newstr[o++] = str1[i];
453 	  break;
454 	}
455       } else if ( str1[i] == '"' ) {
456 	// remember that we skiped this char if it was backslashed
457 	// so we really have to leave quote mode and for the same reason
458 	// as above it's not possibly to do double quote in single quote
459 	// we can go to "no quotes" mode
460 	quotemode = 0;
461       } else {
462 	newstr[o++] = str1[i];
463       }
464       break;
465     default:
466       if ( str1[i] == '\\' ) {
467 	// backslash in nonquoted env escapes everything
468 	i++;
469 	newstr[o++] = str1[i];
470       } else if ( str1[i] == '"' ) {
471 	// double quote begin
472 	quotemode = 2;
473       } else if ( str1[i] == '\'' ) {
474 	// single quote begin
475 	quotemode = 1;
476       } else {
477 	newstr[o++] = str1[i];
478       }
479       break;
480     }
481   }
482   newstr[o] = '\0';
483 
484   return newstr;
485 }
486 
487 /*
488  * this function will cat 2 strings
489  * the first one can have single and double quotes
490  * the second will be protected according to the quote-mode at the end
491  * of str1
492  *
493  * what's done here is not enough for direct shell output because special
494  * shell stuff like $... is not protected
495  * use catTrustedAndUnTrusted instead
496  *
497  * this function is enough for secureCommand... from execlass
498  *
499  * following rules apply:
500  * 1.str1 not quoted at the end: whitespaces, single,double quotes and backslash
501  *   are protected with backslash
502  * 2.str1 ends with single quote: single quotes are replaced with '\''
503  * 3.str1 ends with double quote: double quotes and backslash are protected with backslash
504  */
AGUIX_catQuotedAndUnQuoted(const char * str1,const char * str2)505 char *AGUIX_catQuotedAndUnQuoted( const char *str1, const char *str2 )
506 {
507   int quotemode = 0; /* 0 means no quote
508 			1 means in single quotes
509 			2 means in double quotes */
510   int i, o, lenstr1, lenstr2, newlen;
511   char *newstr, *tstr1;
512 
513   if ( ( str1 == NULL ) || ( str2 == NULL ) ) return NULL;
514 
515   // first fix backslashed str1
516   tstr1 = AGUIX_fixBackslashed( str1 );
517   if ( tstr1 == NULL ) return NULL;
518 
519   lenstr1 = strlen( tstr1 );
520 
521   quotemode = AGUIX_getQuoteMode( tstr1 );
522   if ( quotemode < 0 ) return NULL;
523 
524   /* to know the length of the new string
525    * count all whitespaces, single and double quotes in it
526    * and add the needed space to protect them
527    */
528   lenstr2 = strlen( str2 );
529   newlen = 0;
530   for ( i = 0; i < lenstr2; i++, newlen++ ) {
531     if ( str2[i] == '\'' ) {
532       newlen += 3; /* in quotemode 1 I have to protect it
533 		    * with '\'' so I need 3 additional chars */
534     } else if ( str2[i] == '\"' ) {
535       newlen++;
536     } else if ( str2[i] == '\\' ) {
537       newlen++;
538     } else if ( str2[i] == '$' ) {
539       newlen++;
540     } else if ( str2[i] == '&' ) {
541       newlen++;
542     } else if ( str2[i] == '|' ) {
543       newlen++;
544     } else if ( str2[i] == ';' ) {
545       newlen++;
546     } else if ( str2[i] == '>' ) {
547       newlen++;
548     } else if ( str2[i] == '<' ) {
549       newlen++;
550     } else if ( isspace( str2[i] ) ) {
551       newlen++;
552     }
553   }
554 
555   newstr = (char*)_allocsafe( lenstr1 + newlen + 1 );
556   strcpy( newstr, tstr1 );
557   o = lenstr1;
558 
559   for ( i = 0; i < lenstr2; i++ ) {
560     switch ( quotemode ) {
561     case 1:
562       if ( str2[i] == '\'' ) {
563 	newstr[o++] = '\'';
564 	newstr[o++] = '\\';
565 	newstr[o++] = '\'';
566 	newstr[o++] = '\'';
567       } else {
568 	newstr[o++] = str2[i];
569       }
570       break;
571     case 2:
572       if ( str2[i] == '"' ) {
573 	newstr[o++] = '\\';
574 	newstr[o++] = '"';
575       } else if ( str2[i] == '\\' ) {
576 	newstr[o++] = '\\';
577 	newstr[o++] = '\\';
578       } else if ( str2[i] == '$' ) {
579 	newstr[o++] = '\\';
580 	newstr[o++] = '$';
581       } else {
582 	newstr[o++] = str2[i];
583       }
584       break;
585     default:
586       if ( str2[i] == '"' ) {
587 	newstr[o++] = '\\';
588 	newstr[o++] = '"';
589       } else if ( str2[i] == '\'' ) {
590 	newstr[o++] = '\\';
591 	newstr[o++] = '\'';
592       } else if ( str2[i] == '\\' ) {
593 	newstr[o++] = '\\';
594 	newstr[o++] = '\\';
595       } else if ( str2[i] == '$' ) {
596 	newstr[o++] = '\\';
597 	newstr[o++] = '$';
598       } else if ( str2[i] == '&' ) {
599 	newstr[o++] = '\\';
600 	newstr[o++] = '&';
601       } else if ( str2[i] == '|' ) {
602 	newstr[o++] = '\\';
603 	newstr[o++] = '|';
604       } else if ( str2[i] == ';' ) {
605 	newstr[o++] = '\\';
606 	newstr[o++] = ';';
607       } else if ( str2[i] == '<' ) {
608 	newstr[o++] = '\\';
609 	newstr[o++] = '<';
610       } else if ( str2[i] == '>' ) {
611 	newstr[o++] = '\\';
612 	newstr[o++] = '>';
613       } else if ( isspace( str2[i] ) ) {
614 	newstr[o++] = '\\';
615 	newstr[o++] = str2[i];
616       } else {
617 	newstr[o++] = str2[i];
618       }
619       break;
620     }
621   }
622   newstr[o] = '\0';
623 
624   _freesafe( tstr1 );
625 
626   return newstr;
627 }
628 
629 /*
630  * this function will remove a backslash at the end
631  * if this backslash
632  * 1.exists
633  * 2.is not inside single quotes (backslash doesn't matter there)
634  * 3.the backslash is not backslashed (\\)
635  */
AGUIX_fixBackslashed(const char * str1)636 char *AGUIX_fixBackslashed( const char *str1 )
637 {
638   char *newstr;
639   int lenstr1, bscount, i;
640   int quotemode;
641 
642   if ( str1 == NULL ) return NULL;
643 
644   lenstr1 = strlen( str1 );
645   if ( lenstr1 < 1 ) return dupstring( str1 );
646 
647   bscount = 0;
648   for ( i = lenstr1 - 1; i >= 0; i-- ) {
649     if ( str1[ i ] == '\\' ) {
650       bscount++;
651     } else {
652       break;
653     }
654   }
655 
656   // even backslash count is okay because two
657   // protects a backslash
658 
659   quotemode = AGUIX_getQuoteMode( str1 );
660   if ( quotemode < 0 ) return NULL;
661 
662   newstr = dupstring( str1 );
663   if ( quotemode != 1 ) {
664     // backslash is no special character in single quotes
665     if ( ( bscount % 2 ) == 1 ) {
666       // clear last backslash
667       newstr[ lenstr1 - 1 ] = '\0';
668     }
669   }
670   return newstr;
671 }
672 
AGUIX_catTrustedAndUnTrusted2(const std::string & str1,const std::string & str2)673 std::string AGUIX_catTrustedAndUnTrusted2( const std::string &str1,
674                                            const std::string &str2 )
675 {
676     char *tstr = AGUIX_catTrustedAndUnTrusted( str1.c_str(), str2.c_str() );
677 
678     if ( ! tstr ) return "";
679     std::string res = tstr;
680     _freesafe( tstr );
681 
682     return res;
683 }
684 
AGUIX_catQuotedAndUnQuoted2(const std::string & str1,const std::string & str2)685 std::string AGUIX_catQuotedAndUnQuoted2( const std::string &str1,
686                                          const std::string &str2 )
687 {
688     char *tstr = AGUIX_catQuotedAndUnQuoted( str1.c_str(), str2.c_str() );
689 
690     if ( ! tstr ) return "";
691     std::string res = tstr;
692     _freesafe( tstr );
693 
694     return res;
695 }
696 
AGUIX_prepareForSingleQuote2(const std::string str)697 std::string AGUIX_prepareForSingleQuote2( const std::string str )
698 {
699     char *tstr = AGUIX_prepareForSingleQuote( str.c_str() );
700 
701     if ( ! tstr ) return "";
702     std::string res = tstr;
703     _freesafe( tstr );
704 
705     return res;
706 }
707