1 /*
2  * lftp - file transfer program
3  *
4  * Copyright (c) 1996-2020 by Alexander V. Lukyanov (lav@yars.free.net)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 #include "xmalloc.h"
22 #include "xstring.h"
23 #include "trio.h"
24 #include <pwd.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <time.h>
30 #include <ctype.h>
31 #include <sys/ioctl.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 
38 #ifdef HAVE_TERMIOS_H
39 #include <termios.h>
40 #endif
41 
42 #ifdef TIME_WITH_SYS_TIME
43 # include <sys/time.h>
44 #endif
45 
46 #if LIBIDN2
47 # include <idn2.h>
48 #endif
49 
50 #ifdef HAVE_DLFCN_H
51 # include <dlfcn.h>
52 #endif
53 
54 CDECL_BEGIN
55 #include "regex.h"
56 #include "human.h"
57 CDECL_END
58 #include "misc.h"
59 #include "ProcWait.h"
60 #include "SignalHook.h"
61 #include "url.h"
62 #include "ResMgr.h"
63 #include "log.h"
64 #include <mbswidth.h>
65 
dir_file(const char * dir,const char * file)66 const char *dir_file(const char *dir,const char *file)
67 {
68    if(dir==0 || dir[0]==0)
69       return file?file:dir;
70    if(file==0 || file[0]==0)
71       return dir;
72    if(file[0]=='/')
73       return file;
74    if(file[0]=='.' && file[1]=='/')
75       file+=2;
76 
77    xstring& buf=xstring::get_tmp();
78    size_t len=strlen(dir);
79    if(len==0)
80       return buf.set(file);
81    if(dir[len-1]=='/')
82       return buf.vset(dir,file,NULL);
83    return buf.vset(dir,"/",file,NULL);
84 }
85 
url_file(const char * url,const char * file)86 const char *url_file(const char *url,const char *file)
87 {
88    static xstring buf;
89 
90    if(buf && url==buf) // it is possible to url_file(url_file(url,dir),file)
91       url=alloca_strdup(url);
92 
93    if(!url || url[0]==0)
94    {
95       buf.set(file?file:"");
96       return buf;
97    }
98    ParsedURL u(url);
99    if(!u.proto)
100    {
101       buf.set(dir_file(url,file));
102       return buf;
103    }
104    if(file && file[0]=='~')
105       u.path.set(file);
106    else
107       u.path.set(dir_file(u.path,file));
108    buf.truncate();
109    return u.CombineTo(buf);
110 }
111 
output_file_name(const char * src,const char * dst,bool dst_local,const char * dst_base,bool make_dirs)112 const char *output_file_name(const char *src,const char *dst,bool dst_local,
113 			     const char *dst_base,bool make_dirs)
114 {
115    bool dst_is_dir=false;
116    if(dst)
117    {
118       if(dst_base)
119 	 dst=url_file(dst_base,dst);
120       ParsedURL u_dst(dst,true);
121       if(u_dst.proto)
122 	 dst_local=false;
123       if(dst_local)
124       {
125 	 dst=expand_home_relative(dst);
126 	 struct stat st;
127 	 if(stat(dst,&st)!=-1 && S_ISDIR(st.st_mode))
128 	    dst_is_dir=true;
129       }
130       else
131       {
132 	 int len=xstrlen(u_dst.path);
133 	 if(len>0 && u_dst.path[len-1]=='/')
134 	    dst_is_dir=true;
135       }
136       if(!dst_is_dir)
137 	 return dst;
138    }
139 
140    ParsedURL u_src(src,true);
141    if(u_src.proto)
142       src=u_src.path;
143    if(!src)
144       return "";  // there will be error anyway.
145    const char *base=basename_ptr(src);
146    if(make_dirs && !dst)
147    {
148       base=src;
149       if(base[0]=='~')
150       {
151 	 base=strchr(base,'/');
152 	 if(!base)
153 	    base="";
154       }
155       while(*base=='/')
156 	 base++;
157    }
158    return url_file(dst?dst:dst_base,base);
159 }
160 
basename_ptr(const char * s)161 const char *basename_ptr(const char *s)
162 {
163    const char *s1=s+strlen(s);
164    while(s1>s && s1[-1]=='/')
165       s1--;
166    while(s1>s && s1[-1]!='/')
167       s1--;
168    return s1;
169 }
170 
expand_home_relative(const char * s)171 const char *expand_home_relative(const char *s)
172 {
173    if(s[0]!='~')
174       return s;
175 
176    const char *home=0;
177    const char *sl=strchr(s+1,'/');
178    static xstring ret_path;
179 
180    if(s[1]==0 || s[1]=='/')
181    {
182       home=get_home();
183    }
184    else
185    {
186       // extract user name and find the home
187       int name_len=(sl?sl-s-1:strlen(s+1));
188       struct passwd *pw=getpwnam(xstring::get_tmp(s+1,name_len));
189       if(pw)
190 	 home=pw->pw_dir;
191    }
192    if(home==0)
193       return s;
194 
195    if(sl)
196       return ret_path.vset(home,sl,NULL);
197    return home;
198 }
199 
create_directories(char * path)200 int   create_directories(char *path)
201 {
202    char  *sl=path;
203    int	 res;
204 
205    if(access(path,0)==0)
206       return 0;
207 
208    for(;;)
209    {
210       sl=strchr(sl,'/');
211       if(sl==path)
212       {
213 	 sl++;
214 	 continue;
215       }
216       if(sl)
217 	 *sl=0;
218       if(access(path,0)==-1)
219       {
220 	 res=mkdir(path,0777);
221 	 if(res==-1)
222 	 {
223 	    if(errno!=EEXIST)
224 	    {
225 	       fprintf(stderr,"mkdir(%s): %s\n",path,strerror(errno));
226 	       if(sl)
227 		  *sl='/';
228 	       return(-1);
229 	    }
230 	 }
231 	 else
232 	    debug((9,"mkdir(%s): ok\n",path));
233       }
234       if(sl)
235 	 *sl++='/';
236       else
237 	 break;
238    }
239    return 0;
240 }
241 
truncate_file_tree(const char * dir)242 void  truncate_file_tree(const char *dir)
243 {
244    fflush(stderr);
245    pid_t pid;
246    switch(pid=fork())
247    {
248    case(0): // child
249       SignalHook::Ignore(SIGINT);
250       SignalHook::Ignore(SIGTSTP);
251       SignalHook::Ignore(SIGQUIT);
252       SignalHook::Ignore(SIGHUP);
253       execlp("rm","rm","-rf",dir,(char*)NULL);
254       perror("execlp(rm)");
255       fflush(stderr);
256       _exit(1);
257    case(-1):   // error
258       perror("fork()");
259       return;
260    default: // parent
261       (new ProcWait(pid))->Auto();  // don't wait for termination
262    }
263 }
264 
fd_width(int fd)265 int fd_width(int fd)
266 {
267    if(fd == -1) return -1;
268    if(!isatty(fd)) return 0;
269 
270 #ifdef TIOCGWINSZ
271    struct winsize sz;
272    sz.ws_col=sz.ws_row=0;
273    ioctl(fd,TIOCGWINSZ,&sz);
274    if(sz.ws_col==0)
275       sz.ws_col=80;
276    return(sz.ws_col);
277 #else /* !TIOCGWINSZ */
278    return 80;
279 #endif
280 }
281 
xgetcwd()282 char *xgetcwd()
283 {
284    char *cwd=getcwd(0,0); // glibc extension
285    if(cwd) {
286       xmalloc_register_block(cwd);
287       return cwd;
288    }
289 
290    int size=256;
291    cwd=(char*)xmalloc(size);
292    for(;;)
293    {
294       if(getcwd(cwd,size))
295 	 return cwd;
296       if(errno!=ERANGE)
297 	 return strcpy(cwd,".");
298       cwd=(char*)xrealloc(cwd,size*=2);
299    }
300 }
301 
xgetcwd_to(xstring & s)302 void xgetcwd_to(xstring& s)
303 {
304    int size=256;
305    for(;;) {
306       s.get_space(size);
307       if(getcwd(s.get_non_const(),size)) {
308 	 s.set_length(strlen(s.get()));
309 	 return;
310       }
311       if(errno!=ERANGE) {
312 	 s.set(".");
313 	 return;
314       }
315       size*=2;
316    }
317 }
318 
parse_perms(const char * s)319 int parse_perms(const char *s)
320 {
321    int p=0;
322 
323    if(strlen(s)!=9
324    && !(strlen(s)==10 && s[9]=='+'))   // ACL tag
325       bad: return -1;
326 
327    switch(s[0])
328    {
329    case('r'): p|=S_IRUSR; break;
330    case('-'): break;
331    default: goto bad;
332    }
333    switch(s[1])
334    {
335    case('w'): p|=S_IWUSR; break;
336    case('-'): break;
337    default: goto bad;
338    }
339    switch(s[2])
340    {
341    case('S'): p|=S_ISUID; break;
342    case('s'): p|=S_ISUID; // fall-through
343    case('x'): p|=S_IXUSR; break;
344    case('-'): break;
345    default: goto bad;
346    }
347    s+=3;
348    switch(s[0])
349    {
350    case('r'): p|=S_IRGRP; break;
351    case('-'): break;
352    default: goto bad;
353    }
354    switch(s[1])
355    {
356    case('w'): p|=S_IWGRP; break;
357    case('-'): break;
358    default: goto bad;
359    }
360    switch(s[2])
361    {
362    case('S'): p|=S_ISGID; break;
363    case('s'): p|=S_ISGID; // fall-through
364    case('x'): p|=S_IXGRP; break;
365    case('-'): break;
366    default: goto bad;
367    }
368    s+=3;
369    switch(s[0])
370    {
371    case('r'): p|=S_IROTH; break;
372    case('-'): break;
373    default: goto bad;
374    }
375    switch(s[1])
376    {
377    case('w'): p|=S_IWOTH; break;
378    case('-'): break;
379    default: goto bad;
380    }
381    switch(s[2])
382    {
383    case('T'): p|=S_ISVTX; break;
384    case('t'): p|=S_ISVTX; // fall-through
385    case('x'): p|=S_IXOTH; break;
386    case('l'): case('L'): p|=S_ISGID; p&=~S_IXGRP; break;
387    case('-'): break;
388    default: goto bad;
389    }
390 
391    return p;
392 }
393 
394 // it does not prepend file type.
format_perms(int p)395 const char *format_perms(int p)
396 {
397    static char s[10];
398    memset(s,'-',9);
399    if(p&0400) s[0]='r';
400    if(p&0200) s[1]='w';
401    if(p&0100) s[2]='x';
402    if(p&0040) s[3]='r';
403    if(p&0020) s[4]='w';
404    if(p&0010) s[5]='x';
405    if(p&0004) s[6]='r';
406    if(p&0002) s[7]='w';
407    if(p&0001) s[8]='x';
408    if(p&01000) s[8]=(p&0001?'t':'T');
409    if(p&02000) s[5]=(p&0010?'s':'S');
410    if(p&04000) s[2]=(p&0100?'s':'S');
411    return s;
412 }
413 
414 const char month_names[][4]={
415    "Jan","Feb","Mar","Apr","May","Jun",
416    "Jul","Aug","Sep","Oct","Nov","Dec",
417    ""
418 };
parse_month(const char * m)419 int parse_month(const char *m)
420 {
421    for(int i=0; month_names[i][0]; i++)
422       if(!strcasecmp(month_names[i],m))
423 	 return(i%12);
424    return -1;
425 }
426 
parse_year_or_time(const char * year_or_time,int * year,int * hour,int * minute)427 int parse_year_or_time(const char *year_or_time,int *year,int *hour,int *minute)
428 {
429    if(year_or_time[2]==':')
430    {
431       if(2!=sscanf(year_or_time,"%2d:%2d",hour,minute))
432 	 return -1;
433       *year=-1;
434    }
435    else
436    {
437       if(1!=sscanf(year_or_time,"%d",year))
438 	 return -1;;
439       *hour=*minute=0;
440    }
441    return 0;
442 }
guess_year(int month,int day,int hour,int minute)443 int guess_year(int month,int day,int hour,int minute)
444 {
445    const struct tm &now=SMTask::now;
446    int year=now.tm_year+1900;
447    if(month     *32+        day
448     > now.tm_mon*32+now.tm_mday+6)
449       year--;
450    return year;
451 }
percent(off_t offset,off_t size)452 int percent(off_t offset,off_t size)
453 {
454    if(offset>=size)
455       return 100;
456    // use floating point to avoid integer overflow.
457    return int(double(offset)*100/size);
458 }
459 
squeeze_file_name(const char * name,int w)460 const char *squeeze_file_name(const char *name,int w)
461 {
462    static xstring buf;
463    int mbflags=0;
464 
465    name=url::remove_password(name);
466 
467    int name_width=mbswidth(name,mbflags);
468    if(name_width<=w)
469       return name;
470 
471    const char *b=basename_ptr(name);
472    int b_width=name_width-mbsnwidth(name,b-name,mbflags);
473    if(b_width<=w-4 && b_width>w-15)
474       return buf.vset(".../",b,NULL);
475    int b_len=strlen(b);
476    while(b_width>(w<3?w-1:w-3) && b_len>0)
477    {
478       int ch_len=mblen(b,b_len);
479       if(ch_len<1)
480 	 ch_len=1;
481       b_width-=mbsnwidth(b,ch_len,mbflags);
482       b+=ch_len;
483       b_len-=ch_len;
484    }
485    if(w>=6)
486       buf.set("...");
487    else
488       buf.set("<");
489    return buf.append(b);
490 }
491 
492 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
493    than local timezone (mktime assumes the latter).
494 
495    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
496    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.  */
497 time_t
mktime_from_utc(const struct tm * t)498 mktime_from_utc (const struct tm *t)
499 {
500    struct tm tc;
501    memcpy(&tc, t, sizeof(struct tm));
502 
503    /* UTC times are never DST; if we say -1, we'll introduce odd localtime-
504     * dependant errors. */
505 
506    tc.tm_isdst = 0;
507 
508    time_t tl = mktime (&tc);
509    if (tl == -1)
510       return -1;
511    time_t tb = mktime (gmtime (&tl));
512 
513    return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
514 }
515 
set_tz(const char * tz)516 static void set_tz(const char *tz)
517 {
518    static char *put_tz;
519    static int put_tz_alloc;
520 
521    if(!tz)
522    {
523       unsetenv("TZ");
524 
525       xfree(put_tz);
526       put_tz=0;
527       put_tz_alloc=0;
528 
529       tzset();
530       return;
531    }
532 
533    int tz_len=strlen(tz)+4;
534    char *new_tz=put_tz;
535    if(tz_len>put_tz_alloc)
536       new_tz=(char*)xmalloc(put_tz_alloc=tz_len);
537    snprintf(new_tz,tz_len,"TZ=%s",tz);
538    if(new_tz!=put_tz)
539    {
540       putenv(new_tz);
541       xfree(put_tz);
542       put_tz=new_tz;
543    }
544    // now initialize libc variables from env TZ.
545    tzset();
546 }
547 static char *saved_tz=0;
save_tz()548 static void save_tz()
549 {
550    xstrset(saved_tz,getenv("TZ"));
551 }
restore_tz()552 static void restore_tz()
553 {
554    set_tz(saved_tz);
555 }
mktime_from_tz(struct tm * t,const char * tz)556 time_t mktime_from_tz(struct tm *t,const char *tz)
557 {
558    if(!tz || !*tz)
559       return mktime(t);
560    if(!strcasecmp(tz,"GMT"))
561       return mktime_from_utc(t);
562    if(isdigit((unsigned char)*tz) || *tz=='+' || *tz=='-')
563    {
564       int tz1_len=strlen(tz)+4;
565       char *tz1=string_alloca(tz1_len);
566       snprintf(tz1,tz1_len,"GMT%s",tz);
567       tz=tz1;
568    }
569    save_tz();
570    set_tz(tz);
571    time_t res=mktime(t);
572    restore_tz();
573    return res;
574 }
575 
re_match(const char * line,const char * a,int flags)576 bool re_match(const char *line,const char *a,int flags)
577 {
578    if(!a || !*a)
579       return false;
580    regex_t re;
581    if(regcomp(&re,a,REG_EXTENDED|REG_NOSUB|flags))
582       return false;
583    bool res=(0==regexec(&re,line,0,0,0));
584    regfree(&re);
585    return res;
586 }
587 
SubstTo(xstring & buf,const char * txt,const subst_t * s)588 xstring& SubstTo(xstring& buf,const char *txt, const subst_t *s)
589 {
590    buf.nset("",0);
591 
592    char str[3];
593    bool last_subst_empty=true;
594 
595    while(*txt)
596    {
597       char ch = *txt++;
598       const char *to_add = NULL;
599       if(ch=='\\' && *txt && *txt!='\\')
600       {
601 	 ch=*txt++;
602 	 if(ch >= '0' && ch < '8') {
603 	    int len;
604 	    unsigned code;
605 	    txt--;
606 	    if(sscanf(txt,"%3o%n",&code,&len)!=1)
607 	       continue; // should never happen.
608 	    ch=code;
609 	    txt+=len;
610 	    str[0]=ch;
611 	    str[1]=0;
612 	    to_add=str;
613 	 } else {
614 	    if(ch=='?')
615 	    {
616 	       if(last_subst_empty)
617 		  txt++;
618 	       to_add="";
619 	    }
620 	    for(int i = 0; s[i].from; i++) {
621 	       if(s[i].from != ch)
622 		  continue;
623 	       to_add=s[i].to;
624 	       if(!to_add)
625 		  to_add = "";
626 	       last_subst_empty = (*to_add==0);
627 	    }
628 	    if(!to_add) {
629 	       str[0]='\\';
630 	       str[1]=ch;
631 	       str[2]=0;
632 	       to_add=str;
633 	    }
634 	 }
635       }
636       else
637       {
638 	 if(ch=='\\' && *txt=='\\')
639 	    txt++;
640 	 str[0]=ch;
641 	 str[1]=0;
642 	 to_add=str;
643       }
644 
645       if(to_add==0)
646 	 continue;
647 
648       buf.append(to_add);
649    }
650    return(buf);
651 }
652 
xgettimeofday(time_t * sec,int * usec)653 void xgettimeofday(time_t *sec, int *usec)
654 {
655 #ifdef HAVE_GETTIMEOFDAY
656    struct timeval tv;
657    gettimeofday(&tv,0);
658    if(sec) *sec = tv.tv_sec;
659    if(usec) *usec = tv.tv_usec;
660 #else
661    if(sec) time(sec);
662    if(usec) *usec = 0;
663 #endif
664 }
665 
xstrftime(const char * format,const struct tm * tm)666 char *xstrftime(const char *format, const struct tm *tm)
667 {
668    char *ret = NULL;
669    int siz = 32;
670 
671    struct tm dummy;
672    memset(&dummy, 0, sizeof(dummy));
673    if(tm == NULL)
674       tm = &dummy;
675 
676    for(;;)
677    {
678       ret = (char *) xrealloc(ret, siz);
679       int res=strftime(ret, siz, format, tm);
680       if(res>0 && res<siz)
681 	 return ret; /* success */
682       /* more space */
683       siz*=2;
684    }
685 }
686 
687 /* /file/name -> /file
688  * /file -> /
689  * file/name -> "file"
690  * file/name/ -> "file"
691  * file -> ""
692  * note: the last differs from dirname(1) (which would return ".")
693  *
694  */
strip_trailing_slashes(xstring & ret)695 void strip_trailing_slashes(xstring& ret)
696 {
697    int len=ret.length();
698    while(len>0 && ret[len-1]=='/')
699       len--;
700    if(len==0 && ret[0]=='/')
701       len=1+(ret[1]=='/');
702    if(len>0)
703       ret.truncate(len);
704 }
dirname_modify(xstring & ret)705 xstring& dirname_modify(xstring &ret)
706 {
707    strip_trailing_slashes(ret);
708    const char *slash=strrchr(ret,'/');
709    if(!slash)
710       ret.truncate(0); /* file with no path */
711    else if(slash==ret)
712       ret.truncate(1); /* the slash is the first character */
713    else
714       ret.truncate(slash-ret);
715    return ret;
716 }
dirname(const char * path)717 xstring& dirname(const char *path)
718 {
719    return dirname_modify(xstring::get_tmp(path));
720 }
721 
last_char(const char * str)722 char last_char(const char *str)
723 {
724    int len=strlen(str);
725    return str[len-(len>0)];
726 }
727 
728 /* How many bytes it will take to store LEN bytes in base64.  */
729 int
base64_length(int len)730 base64_length(int len)
731 {
732   return (4 * (((len) + 2) / 3));
733 }
734 
735 /* Encode the string S of length LENGTH to base64 format and place it
736    to STORE.  STORE will be 0-terminated, and must point to a writable
737    buffer of at least 1+BASE64_LENGTH(length) bytes.  */
738 void
base64_encode(const char * s,char * store,int length)739 base64_encode (const char *s, char *store, int length)
740 {
741   /* Conversion table.  */
742   static const char tbl[64] = {
743     'A','B','C','D','E','F','G','H',
744     'I','J','K','L','M','N','O','P',
745     'Q','R','S','T','U','V','W','X',
746     'Y','Z','a','b','c','d','e','f',
747     'g','h','i','j','k','l','m','n',
748     'o','p','q','r','s','t','u','v',
749     'w','x','y','z','0','1','2','3',
750     '4','5','6','7','8','9','+','/'
751   };
752   int i;
753   unsigned char *p = (unsigned char *)store;
754 
755   /* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
756   for (i = 0; i < length; i += 3)
757     {
758       *p++ = tbl[s[0] >> 2];
759       *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
760       *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
761       *p++ = tbl[s[2] & 0x3f];
762       s += 3;
763     }
764   /* Pad the result if necessary...  */
765   if (i == length + 1)
766     *(p - 1) = '=';
767   else if (i == length + 2)
768     *(p - 1) = *(p - 2) = '=';
769   /* ...and zero-terminate it.  */
770   *p = '\0';
771 }
772 
temporary_network_error(int err)773 bool temporary_network_error(int err)
774 {
775    switch(err)
776    {
777    case(EPIPE):
778    case(EIO):
779    case(ETIMEDOUT):
780 #ifdef ECONNRESET
781    case(ECONNRESET):
782 #endif
783    case(ECONNREFUSED):
784 #ifdef EHOSTUNREACH
785    case(EHOSTUNREACH):
786 #endif
787 #ifdef EHOSTDOWN
788    case(EHOSTDOWN):
789 #endif
790 #ifdef ENETRESET
791    case(ENETRESET):
792 #endif
793 #ifdef ENETUNREACH
794    case(ENETUNREACH):
795 #endif
796 #ifdef ENETDOWN
797    case(ENETDOWN):
798 #endif
799 #ifdef ECONNABORTED
800    case(ECONNABORTED):
801 #endif
802 #ifdef EADDRNOTAVAIL
803    case(EADDRNOTAVAIL):
804 #endif
805       return true;
806    }
807    return false;
808 }
809 
get_home()810 const char *get_home()
811 {
812    static char *home=NULL;
813    if(home)
814       return home;
815    home=getenv("HOME");
816    if(home)
817       return home;
818    struct passwd *pw=getpwuid(getuid());
819    if(pw && pw->pw_dir)
820       return home=pw->pw_dir;
821    return NULL;
822 }
823 
get_lftp_home_nocreate()824 const char *get_lftp_home_nocreate()
825 {
826    static char *lftp_home=NULL;
827 
828    if(lftp_home)
829       return *lftp_home?lftp_home:NULL;
830 
831    lftp_home=getenv("LFTP_HOME");
832    if(!lftp_home)
833    {
834       const char *h=get_home();
835       if(h)
836          lftp_home=xstring::cat(h,"/.lftp",NULL).borrow();
837       else
838          return NULL;
839    }
840    else
841       lftp_home=xstrdup(lftp_home);
842 
843    return *lftp_home?lftp_home:NULL;
844 }
get_lftp_home_if_exists()845 const char *get_lftp_home_if_exists()
846 {
847    const char *home=get_lftp_home_nocreate();
848    struct stat st;
849    if(stat(home,&st)==-1 || !S_ISDIR(st.st_mode))
850       return NULL;
851    return home;
852 }
853 
854 // new XDG directories
get_lftp_dir(char * & cached_dir,const char * env,const char * def)855 const char *get_lftp_dir(char *&cached_dir,const char *env,const char *def)
856 {
857    if(cached_dir)
858       return cached_dir;
859 
860    // use old existing directory for compatibility
861    const char *dir=get_lftp_home_if_exists();
862    if(dir)
863       return cached_dir=xstrdup(dir);
864 
865    // use explicit directory if specified, otherwise use default under home
866    const char *home=getenv(env);
867    if(home) {
868       // explicit XDG dir
869       (void)mkdir(home,0755);
870       dir=xstring::cat(home,"/lftp",NULL);
871    } else {
872       home=get_home();
873       if(!home)
874 	 return NULL;
875       xstring& path=xstring::get_tmp(home);
876       path.append('/');
877       const char *slash=strchr(def,'/');
878       if(slash) {
879 	 path.append(def,slash-def);
880 	 (void)mkdir(path,0755);
881 	 path.append(slash);
882       } else {
883 	 path.append(def);
884       }
885       (void)mkdir(path,0755);
886       dir=path.append("/lftp");
887    }
888    (void)mkdir(dir,0755);
889    return cached_dir=xstrdup(dir);
890 }
get_lftp_config_dir()891 const char *get_lftp_config_dir()
892 {
893    static char *config_dir;
894    return get_lftp_dir(config_dir,"XDG_CONFIG_HOME",".config");
895 }
get_lftp_data_dir()896 const char *get_lftp_data_dir()
897 {
898    static char *data_dir;
899    return get_lftp_dir(data_dir,"XDG_DATA_HOME",".local/share");
900 }
get_lftp_cache_dir()901 const char *get_lftp_cache_dir()
902 {
903    static char *cache_dir;
904    return get_lftp_dir(cache_dir,"XDG_CACHE_HOME",".cache");
905 }
906 
memrchr(const char * buf,char c,size_t len)907 const char *memrchr(const char *buf,char c,size_t len)
908 {
909    buf+=len;
910    while(len-->0)
911       if(*--buf==c)
912 	 return buf;
913    return 0;
914 }
915 
is_shell_special(char c)916 bool is_shell_special(char c)
917 {
918    switch (c)
919    {
920    case '\'':
921    case '(': case ')':
922    case '!': case '{': case '}':		/* reserved words */
923    case '^':
924    case '$': case '`':			/* expansion chars */
925    case '*': case '[': case '?': case ']':	/* globbing chars */
926    case ' ': case '\t': case '\n':		/* IFS white space */
927    case '"': case '\\':		/* quoting chars */
928    case '|': case '&': case ';':		/* shell metacharacters */
929    case '<': case '>':
930    case '#':				/* comment char */
931       return true;
932    }
933    return false;
934 }
935 
shell_encode(const char * string,int len)936 const xstring& shell_encode(const char *string,int len)
937 {
938    if(!string)
939       return xstring::null;
940 
941    static xstring result;
942 
943    result.get_space(2 + 2 * len);
944    char *r = result.get_non_const();
945 
946    if(string[0]=='-' || string[0]=='~')
947    {
948       *r++='.';
949       *r++='/';
950    }
951 
952    int c;
953    for (const char *s = string; s && (c = *s); s++)
954    {
955       if (is_shell_special(c))
956 	 *r++ = '\\';
957       *r++ = c;
958    }
959    result.set_length(r-result);
960    return (result);
961 }
962 
remove_tags(char * buf)963 int remove_tags(char *buf)
964 {
965    int len=strlen(buf);
966    int less = -1;
967    for(int i = 0; i < len;i++)
968    {
969       if(strcmp(buf + i, "&nbsp;") == 0){
970         for(int j = 0; j < 6; j++)buf[i + j] = 0;
971         buf[i] = ' ';
972         i += 6;
973         i--;
974         continue;
975       }
976       if(buf[i] == '<'){
977         less = i;
978         continue;
979 
980       }
981       if(buf[i] == '>'){
982         if(less != -1){
983           for(int j = less; j <= i; j++)buf[j] = 0;
984           less = -1;
985         }
986 
987       }
988    }
989    int zero = 0;
990    for(int i = 0; i < len;i++)
991    {
992      while(zero < i && buf[zero] != 0)zero++;
993      if(buf[i] != 0 and zero != i){
994       buf[zero] = buf[i];
995       buf[i] = 0;
996      }
997    }
998    return ++zero;
999 }
1000 
rtrim(char * s)1001 void rtrim(char *s)
1002 {
1003    int len=strlen(s);
1004    while(len>0 && (s[len-1]==' ' || s[len-1]=='\t' || s[len-1]=='\r'))
1005       s[--len]=0;
1006 }
1007 
in_foreground_pgrp()1008 bool in_foreground_pgrp()
1009 {
1010    static int tty_fd;
1011    if(tty_fd==-1)
1012       return true;
1013    pid_t pg=tcgetpgrp(tty_fd);
1014    if(pg==(pid_t)-1 && !isatty(tty_fd)) {
1015       tty_fd=open("/dev/tty",O_RDONLY);
1016       if(tty_fd==-1)
1017 	 return true;
1018       pg=tcgetpgrp(tty_fd);
1019    }
1020    if(pg==(pid_t)-1 || pg==getpgrp())
1021       return true;
1022    return false;
1023 }
1024 
random_init()1025 void random_init()
1026 {
1027    static bool init;
1028    if(!init)
1029    {
1030       srandom(time(NULL)+getpid());
1031       init=true;
1032    }
1033 }
random01()1034 double random01()
1035 {
1036    return random()/2147483648.0;
1037 }
1038 
1039 #include <sys/utsname.h>
get_nodename()1040 const char *get_nodename()
1041 {
1042    static struct utsname u;
1043    if(uname(&u)==0)
1044       return u.nodename;
1045    return "NODE";
1046 }
1047 
xhuman(long long n)1048 const char *xhuman(long long n)
1049 {
1050    char *buf=xstring::tmp_buf(LONGEST_HUMAN_READABLE + 1);
1051    return human_readable(n, buf, human_autoscale|human_SI, 1, 1);
1052 }
1053 
xidna_to_ascii(const char * name)1054 const char *xidna_to_ascii(const char *name)
1055 {
1056 #if LIBIDN2
1057    if(!name)
1058       return 0;
1059    static xstring_c name_ace_tmp;
1060    name_ace_tmp.unset();
1061    if(idn2_to_ascii_lz(name,name_ace_tmp.buf_ptr(),0)==IDN2_OK) {
1062       xmalloc_register_block((void*)name_ace_tmp.get());
1063       return name_ace_tmp;
1064    }
1065 #endif//LIBIDN2
1066    return name;
1067 }
xtld_name_ok(const char * name)1068 bool xtld_name_ok(const char *name)
1069 {
1070 #if LIBIDN2
1071    if(mbswidth(name,MBSW_REJECT_INVALID|MBSW_REJECT_UNPRINTABLE)<=0)
1072       return false;
1073    if(idn2_lookup_ul(name,NULL,0)==IDN2_OK)
1074       return true;
1075 #endif//LIBIDN2
1076    return false;
1077 }
1078 
is_ipv4_address(const char * s)1079 bool is_ipv4_address(const char *s)
1080 {
1081    struct in_addr addr;
1082    return inet_pton(AF_INET,s,&addr)>0;
1083 }
is_ipv6_address(const char * s)1084 bool is_ipv6_address(const char *s)
1085 {
1086 #if INET6
1087    struct in6_addr addr;
1088    return inet_pton(AF_INET6,s,&addr)>0;
1089 #else
1090    return false;
1091 #endif
1092 }
1093 
lftp_fallocate(int fd,off_t sz)1094 int lftp_fallocate(int fd,off_t sz)
1095 {
1096 #if defined(HAVE_FALLOCATE)
1097    return fallocate(fd,0,0,sz);
1098 #elif defined(HAVE_POSIX_FALLOCATE)
1099    return posix_fallocate(fd,0,sz);
1100 #else
1101    errno=ENOSYS;
1102    return -1;
1103 #endif
1104 }
1105 
call_dynamic_hook(const char * name)1106 void call_dynamic_hook(const char *name) {
1107 #if defined(HAVE_DLOPEN) && defined(RTLD_DEFAULT)
1108    typedef void (*func)();
1109    func f=(func)dlsym(RTLD_DEFAULT,name);
1110    if(f) f();
1111 #endif
1112 }
1113