1 /***************************************************************************
2 * mgurlparser.cpp
3 *
4 * Tue Sep 26 16:17:23 2006
5 * Copyright 2006 liubin,China
6 * Email multiget@gmail.com
7 ****************************************************************************/
8
9 /*
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program 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
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25
26 #include "mgurlparser.h"
27 #include "common.h"
28 #include <iostream>
29 extern std::string gDefFtpPass;
30
31 using namespace std;
32 ///%CF%D6%B9%DB%D7%AF%D1%CF%C2%DBCD/1-2a.mp3
33
34 #define XDIGIT_TO_XCHAR(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'A'))
35 #define ISXDIGIT(x) ( ((x) >= '0' && (x) <= '9')||\
36 ((x) >= 'a' && (x) <= 'z')||\
37 ((x) >= 'A' && (x) <= 'Z') )
38
39
40 #define XCHAR_TO_XDIGIT(x) (((x) >= '0' && (x) <= '9') ? \
41 ((x) - '0') : (toupper(x) - 'A' + 10))
42
43 enum {
44 urlchr_reserved = 1, // rfc1738 reserved chars
45 urlchr_unsafe = 2 // rfc1738 unsafe chars
46 };
47
48 /* Shorthands for the table: */
49 #define R 1 // reserved char
50 #define U 2 // unsafe char
51 #define RU 3 // R|U
52
53 const static unsigned char urlchr_table[ 256 ] =
54 {
55 U, U, U, U, U, U, U, U, /* NUL SOH STX ETX EOT ENQ ACK BEL */
56 U, U, U, U, U, U, U, U, /* BS HT LF VT FF CR SO SI */
57 U, U, U, U, U, U, U, U, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
58 U, U, U, U, U, U, U, U, /* CAN EM SUB ESC FS GS RS US */
59 U, 0, U, RU, R, U, R, 0, /* SP ! " # $ % & ' */
60 0, 0, 0, R, R, 0, 0, R, /* ( ) * + , - . / */
61 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
62 0, 0, RU, R, U, R, U, R, /* 8 9 : ; < = > ? */
63 RU, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */
64 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
65 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
66 0, 0, 0, RU, U, RU, U, 0, /* X Y Z [ \ ] ^ _ */
67 U, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */
68 0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */
69 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */
70 0, 0, 0, U, U, U, RU, U, /* x y z { | } ~ DEL */
71
72 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
73 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
74 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
75 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
76
77 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
78 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
79 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
80 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
81 };
82
83 #define urlchr_test(c, mask) (urlchr_table[(unsigned char)(c)] & (mask))
84 #define URL_RESERVED_CHAR(c) urlchr_test(c, urlchr_reserved)
85 #define URL_UNSAFE_CHAR(c) urlchr_test(c, urlchr_unsafe)
86
87
CUrlParser()88 CUrlParser::CUrlParser()
89 {
90 m_bValidMirrorUrl = false;
91 }
92
93 //返回false是不支持的协议或错误的地址格式
SetUrl(std::string url)94 bool CUrlParser::SetUrl( std::string url )
95 {
96
97 Trim( url );
98
99 m_bValidMirrorUrl = false;
100 //协议检查
101 m_protocol = UrlType( url );
102
103 if ( m_protocol == UNKNOW_PROTOCOL )
104 {
105 return false;
106 }
107
108 if ( m_protocol == FTP_PROTOCOL )
109 { //ftp
110
111 //提取用户和密码
112
113 if ( !GetUserAndPass( url, m_user, m_pass ) )
114 {
115 return false;
116 }
117
118 //服务器和端口
119 if ( !GetServerAndPort( url, m_server, m_port ) )
120 {
121 return false;
122 }
123
124 if ( !GetRawUrl( url, m_raw ) )
125 {
126 return false;
127 }
128
129 //文件汉路径
130 if ( !GetPathFile( url, m_file ) )
131 {
132 return false;
133 }
134
135 if ( m_file[ m_file.length() - 1 ] == '/' )
136 { //ftp dir not a valid file url
137 m_bValidMirrorUrl = true;
138 return false;
139 }
140
141 //编码文件
142
143 //Precode(m_file,m_escfile);
144 m_escfile = m_file;
145
146 //DBGOUT("m_file="<<m_file);
147 UnEscape( m_escfile ); //m_escfile反而存放非转换串
148
149 //DBGOUT("m_escfile="<<m_escfile);
150 }
151 else if ( m_protocol == HTTP_PROTOCOL )
152 { //http
153
154 //提取用户和密码
155
156 if ( !GetUserAndPass( url, m_user, m_pass ) )
157 {
158 return false;
159 }
160
161 //服务器和端口
162 if ( !GetServerAndPort( url, m_server, m_port ) )
163 {
164 return false;
165 }
166
167 if ( !GetRawUrl( url, m_raw ) )
168 {
169 return false;
170 }
171
172 //文件汉路径
173 if ( !GetPathFile( url, m_file ) )
174 {
175 return false;
176 }
177
178
179 //编码文件
180 Precode( m_file, m_escfile );
181
182 if ( !GetRefer( m_raw, m_refer ) )
183 {
184 return false;
185 }
186
187 }
188 else
189 {
190 return false;
191 }
192
193 return true;
194 }
195
GetUser()196 string CUrlParser::GetUser()
197 {
198 return m_user;
199 }
200
GetPass()201 string CUrlParser::GetPass()
202 {
203 return m_pass;
204 }
205
GetServer()206 string CUrlParser::GetServer()
207 {
208 return m_server;
209 }
210
GetFilePathName()211 string CUrlParser::GetFilePathName()
212 {
213 return m_file;
214 }
215
GetFileName()216 std::string CUrlParser::GetFileName()
217 {
218 //only file name without path
219
220 string::size_type pos;
221
222 pos = m_file.find_last_of( '/' ); //for linux only
223
224 if ( pos == std::string::npos )
225 {
226 std::string uns=m_file;
227 UnEscape(uns);
228 return uns;
229 }
230 else
231 {
232 std::string uns= m_file.substr( pos + 1, m_file.length() - pos - 1 );
233 UnEscape(uns);
234 return uns;
235 }
236
237 /*
238 char fn[ 512 ];
239 strcpy( fn, m_file.c_str() );
240 int i = strlen( fn );
241
242 while ( fn[ i ] != '/' && i > 0 )
243 i--;
244
245 return string( fn + i + 1 );
246 */
247
248 }
249
250 //only http/https need refer
GetRefer()251 std::string CUrlParser::GetRefer()
252 {
253 return m_refer;
254 }
255
GetPort()256 int CUrlParser::GetPort()
257 {
258 return m_port;
259 }
260
261 //目前就支持两种协议,HTTP,FTP
UrlType(std::string url)262 _PROTYPE CUrlParser::UrlType( std::string url )
263 {
264 if ( url.length() < 7 )
265 return UNKNOW_PROTOCOL; //太短了!
266
267 #ifdef WIN32
268
269 if ( strnicmp( url.c_str(), "ftp://", 6 ) == 0 )
270 return FTP_PROTOCOL;
271
272 if ( strnicmp( url.c_str(), "http://", 7 ) == 0 )
273 return HTTP_PROTOCOL;
274
275 #else
276
277 if ( strncasecmp( url.c_str(), "ftp://", 6 ) == 0 )
278 return FTP_PROTOCOL;
279
280 if ( strncasecmp( url.c_str(), "http://", 7 ) == 0 )
281 return HTTP_PROTOCOL;
282
283 #endif
284
285 return UNKNOW_PROTOCOL;
286
287 }
288
GetUrlType()289 _PROTYPE CUrlParser::GetUrlType()
290 {
291 return m_protocol;
292 }
293
294
GetEscFilePathName()295 std::string CUrlParser::GetEscFilePathName()
296 {
297 return m_escfile;
298 }
299
300
301 //新的提取用户和密码的函数
GetUserAndPass(const std::string & fullurl,std::string & user,std::string & pass)302 bool CUrlParser::GetUserAndPass( const std::string& fullurl, std::string& user, std::string& pass )
303 {
304 //从后向前寻找@,如果有则服务器从@后开始,到/结束
305 //如果没有,则从ftp://开始
306
307 //是否太长而无法处理?
308
309 if ( fullurl.length() > 510 )
310 return false;
311
312 //考到url
313 char url[ 512 ];
314
315 strcpy( url, fullurl.c_str() );
316
317 //p是移动的指针
318 char * p = url;
319
320 p += strlen( url );
321
322 //look for @
323 //while ( *p != '@' && *p != 0 )
324 // p++;
325
326 findat:
327 while ( *p != '@' && p != url )
328 p--;
329
330 //根据提交的BUG,有时后面会有这个@字符,添加检查过滤无效的@
331 if ( *p == '@' )
332 {
333 //检查是否前方的'/'位置是否是第2个'/'
334 char *q=p;
335 while ( *q != '/' && q > url )
336 q--;
337
338 if( q == url ) return false; //其实不会出现这个情况
339 char *m = url; //从前向后找第二个'/',应该等于q
340 while ( *m != '/' ) m++;
341 if( q != m + 1 ) { p--; goto findat; }
342 }
343
344 if ( *p == '@' )
345 { //get @
346 char *e = p - 1;
347 char *p = e;
348
349 while ( *p != '/' && p > url )
350 p--;
351
352 if ( p == url )
353 {
354 return false; //没找到前面的斜杠,一个不合法的URL
355 }
356 else
357 {
358 char *s = p + 1;
359 //assert( e > s );
360 //int len=e-s+1;
361 //search ':'
362
363 while ( *p != ':' && p < e )
364 p++;
365
366 if ( p == e )
367 {
368 return false; //无冒号分割用户名和密码,一个不合法的URL
369 }
370
371 if ( 30 < p - s + 1 ) //用户名超过30,有点长了
372 {
373 return false;
374 }
375
376 char temp[ 31 ];
377 memcpy( temp, s, p - s );
378 temp[ p - s ] = 0;
379 user = std::string( temp );
380
381 if ( 30 < e - p + 1 ) //密码超过30,有点长了
382 {
383 return false;
384 }
385
386 memcpy( temp, p + 1, e - p );
387 temp[ e - p ] = 0;
388 pass = std::string( temp );
389 return true;
390 }
391 }
392 else
393 {
394 user = std::string( "anonymous" );
395 pass = gDefFtpPass;
396 return true;
397 }
398
399 return false;
400 }
401
GetServerAndPort(const std::string & fullurl,std::string & server,int & port)402 bool CUrlParser::GetServerAndPort( const std::string& fullurl, std::string& server, int& port )
403 {
404 //从后向前寻找@,如果有则服务器从@后开始,到/结束
405 //如果没有,则从ftp://开始
406
407 //是否太长而无法处理?
408
409 if ( fullurl.length() > 510 )
410 return false;
411
412 //考到url
413 char url[ 512 ];
414
415 strcpy( url, fullurl.c_str() );
416
417 //p是移动的指针
418 char * p = url;
419
420 p += strlen( url );
421
422 //look for @
423 //while ( *p != '@' && *p != 0 )
424 // p++;
425
426 findat:
427 while ( *p != '@' && p != url )
428 p--;
429
430 //根据提交的BUG,有时后面会有这个@字符,添加检查过滤无效的@
431 if ( *p == '@' )
432 {
433 //检查是否前方的'/'位置是否是第2个'/'
434 char *q=p;
435 while ( *q != '/' && q > url )
436 q--;
437
438 if( q == url ) return false; //其实不会出现这个情况
439 char *m = url; //从前向后找第二个'/',应该等于q
440 while ( *m != '/' ) m++;
441 if( q != m + 1 ) { p--; goto findat; }
442 }
443
444 if ( *p == '@' )
445 {
446 char * s = p + 1;
447
448 while ( *p != '/' && *p != 0 )
449 p++;
450
451 if ( *p == '/' )
452 {
453 int len = p - s; //包含端口在内的长度
454 //检查是否包含了端口在内
455 char *ck = p - 1;
456
457 while ( *ck != ':' && ck > s )
458 ck--;
459
460 if ( ck == s )
461 { //无端口
462
463 if ( 256 < len + 1 )
464 return false; //server string too long
465
466 char temp[ 256 ];
467
468 memcpy( temp, s, len );
469
470 temp[ len ] = 0;
471
472 server = std::string( temp );
473
474 //按协议类型给出缺省的端口,以后可括从
475 port = GetDefaultPort( m_protocol );
476
477 return true;
478
479 }
480 else
481 {
482 //有端口
483 int slen = ck - s;
484 int plen = len - slen - 1;
485
486 if ( 256 < slen )
487 return false; //server too long
488
489 char temp[ 256 ];
490
491 memcpy( temp, s, slen );
492
493 temp[ slen ] = 0;
494
495 server = std::string( temp );
496
497 if ( plen > 5 )
498 return false; //max port is 65535
499
500 char prt[ 6 ];
501
502 memcpy( prt, ck + 1, plen );
503
504 prt[ plen ] = 0;
505
506 //检查是否都为数字?
507 for ( int i = 0;i < plen;i++ )
508 {
509 if ( prt[ i ] < '0' || prt[ i ] > '9' )
510 return false; //wrong port
511 }
512
513 port = atoi( prt );
514
515 return true;
516 }
517
518 }
519 else
520 { //url wrong format
521 return false;
522 }
523 }
524 else //到了结尾没有@
525 {
526 p = url;
527
528 while ( *p != '/' && *p != 0 )
529 p++;
530
531 p++;
532
533 while ( *p != '/' && *p != 0 )
534 p++;
535
536 p++;
537
538 char *s = p;
539
540 while ( *p != '/' && *p != 0 )
541 p++;
542
543 if ( *p == '/' )
544 {
545 int len = p - s; //包含端口在内的长度
546 //检查是否包含了端口在内
547 char *ck = p - 1;
548
549 while ( *ck != ':' && ck > s )
550 ck--;
551
552 if ( ck == s )
553 { //无端口
554
555 if ( 256 < len + 1 )
556 return false; //server too long
557
558 char temp[ 256 ];
559
560 memcpy( temp, s, len );
561
562 temp[ len ] = 0;
563
564 server = std::string( temp );
565
566 //按协议类型给出缺省的端口,以后可括从
567 port = GetDefaultPort( m_protocol );
568
569 return true;
570
571 }
572 else
573 {
574 //有端口
575 int slen = ck - s;
576 int plen = len - slen - 1;
577
578 if ( 256 < slen )
579 return false;
580
581 char temp[ 256 ];
582
583 memcpy( temp, s, slen );
584
585 temp[ slen ] = 0;
586
587 server = std::string( temp );
588
589 if ( plen > 5 )
590 return false; //max port is 65535
591
592 char prt[ 6 ];
593
594 memcpy( prt, ck + 1, plen );
595
596 prt[ plen ] = 0;
597
598 //检查是否都为数字?
599 for ( int i = 0;i < plen;i++ )
600 {
601 if ( prt[ i ] < '0' || prt[ i ] > '9' )
602 return false; //wrong port
603 }
604
605 port = atoi( prt );
606
607 return true;
608 }
609 }
610 else
611 {
612 return false;
613 }
614 }
615 }
616
617
GetDefaultPort(_PROTYPE protocol)618 int CUrlParser::GetDefaultPort( _PROTYPE protocol )
619 {
620 //按协议类型给出缺省的端口,以后可括从
621
622 switch ( protocol )
623 {
624
625 case FTP_PROTOCOL:
626 return 21;
627
628 case HTTP_PROTOCOL:
629 return 80;
630
631 default:
632 return 0;
633 }
634
635 }
636
637 //去掉用户和密码的url
GetRawUrl(std::string & fullurl,std::string & rawurl)638 bool CUrlParser::GetRawUrl( std::string& fullurl, std::string& rawurl )
639 {
640 //ftp://user:pa@ss@www.abc.com/fdfas.txt,去掉//到@之间的内容。
641
642 char url[ 512 ];
643 strcpy( url, fullurl.c_str() );
644
645 char* p = url;
646
647 while ( *p != '/' && *p != 0 )
648 p++;
649
650 if ( *p == 0 )
651 return false;
652
653 p++;
654
655 while ( *p != '/' && *p != 0 )
656 p++;
657
658 if ( *p == 0 )
659 return false;
660
661 p++;
662
663 //这里p指向'u'
664 char* h = p;
665
666 p = url + strlen( url );
667
668 findat:
669 while ( *p != '@' && p != url )
670 p--;
671
672 //根据提交的BUG,有时后面会有这个@字符,添加检查过滤无效的@
673 if ( *p == '@' )
674 {
675 //检查是否前方的'/'位置是否是第2个'/'
676 char *q=p;
677 while ( *q != '/' && q > url )
678 q--;
679
680 if( q == url ) return false; //其实不会出现这个情况
681 char *m = url; //从前向后找第二个'/',应该等于q
682 while ( *m != '/' ) m++;
683 if( q != m + 1 ) { p--; goto findat; }
684 }
685
686 if ( p == url )
687 {
688 rawurl = fullurl;
689
690 }
691 else
692 {
693 p++;
694 char temp[ 512 ];
695 memcpy( temp, url, h - url );
696 strcpy( &temp[ h - url ], p );
697 rawurl = std::string( temp );
698 }
699
700 return true;
701 }
702
703 //从url中获得路径和文件的数据
704 //不检查是否为目录,也不进行编码
GetPathFile(std::string & fullurl,std::string & pathfile)705 bool CUrlParser::GetPathFile( std::string& fullurl, std::string& pathfile )
706 {
707 //ftp://dfasdfas.com/dfad/dfs =》/dfad/dfs
708 //找第三个'/'
709
710 char url[ 512 ];
711 strcpy( url, fullurl.c_str() );
712
713 char *p = url;
714
715 while ( *p != '/' && *p != 0 )
716 p++;
717
718 if ( *p == 0 )
719 return false;
720
721 p++;
722
723 while ( *p != '/' && *p != 0 )
724 p++;
725
726 if ( *p == 0 )
727 return false;
728
729 p++;
730
731 while ( *p != '/' && *p != 0 )
732 p++;
733
734 if ( *p == 0 )
735 return false;
736
737 if ( 510 < int( strlen( url ) - ( p - url ) ) )
738 {
739 return false; //too long
740 }
741
742 char temp[ 512 ];
743 memcpy( temp, p, strlen( url ) - ( p - url ) );
744 temp[ strlen( url ) - ( p - url ) ] = 0;
745 pathfile = std::string( temp );
746 return true;
747 }
748
749 //提取refer
GetRefer(std::string rawurl,std::string & refer)750 bool CUrlParser::GetRefer( std::string rawurl, std::string& refer )
751 {
752
753 if ( !m_refer.empty() )
754 return true; //already have set refer
755
756 //找最后一个'/',之前的就算refer
757
758 char url[ 512 ];
759
760 strcpy( url, rawurl.c_str() );
761
762 char* p = url + strlen( url );
763
764 while ( p != url && *p != '/' )
765 p--;
766
767 if ( p == url )
768 {
769 refer.clear();
770 }
771 else
772 {
773 if ( 512 < p - url )
774 return false;
775
776 char temp[ 512 ];
777
778 memcpy( temp, url, p - url );
779
780 temp[ p - url ] = 0;
781
782 refer = std::string( temp );
783
784 }
785
786 return true;
787 }
788
Precode(const std::string & pathfile,std::string & escfile)789 void CUrlParser::Precode( const std::string& pathfile, std::string& escfile )
790 {
791 char * str_new;
792 int str_new_len;
793 char *ptr, *pptr;
794
795 // get the length of the new string
796 ptr = ( char* ) pathfile.c_str();
797 str_new_len = 0;
798
799 while ( *ptr != '\0' )
800 {
801 if ( *ptr == '%' )
802 {
803 if ( isxdigit( ptr[ 1 ] ) && isxdigit( ptr[ 2 ] ) )
804 {
805 str_new_len += 3;
806 ptr += 3;
807 }
808 else
809 {
810 str_new_len += 3;
811 ptr += 1;
812 }
813 }
814 else if ( URL_UNSAFE_CHAR( *ptr ) && !URL_RESERVED_CHAR( *ptr ) )
815 {
816 str_new_len += 3;
817 ptr += 1;
818 }
819 else
820 {
821 str_new_len += 1;
822 ptr += 1;
823 }
824 } // end of while
825
826 /* encode the url */
827 str_new = new char[ str_new_len + 1 ];
828
829 ptr = ( char* ) pathfile.c_str();
830
831 pptr = str_new;
832
833 while ( *ptr != '\0' )
834 {
835 if ( *ptr == '%' )
836 {
837 if ( isxdigit( ptr[ 1 ] ) && isxdigit( ptr[ 2 ] ) )
838 {
839 strncpy( pptr, ptr, 3 );
840 ptr += 3;
841 pptr += 3;
842 }
843 else
844 {
845 strncpy( pptr, "%25", 3 );
846 ptr += 1;
847 pptr += 3;
848 }
849 }
850 else if ( URL_UNSAFE_CHAR( *ptr ) && !URL_RESERVED_CHAR( *ptr ) )
851 {
852 pptr[ 0 ] = '%';
853 pptr[ 1 ] = XDIGIT_TO_XCHAR( ( ( unsigned char ) * ptr ) >> 4 );
854 pptr[ 2 ] = XDIGIT_TO_XCHAR( ( ( unsigned char ) * ptr ) & 0x0f );
855 ptr += 1;
856 pptr += 3;
857 }
858 else
859 {
860 *pptr = *ptr;
861 ptr += 1;
862 pptr += 1;
863 }
864 } // end of while
865
866 *pptr = '\0';
867
868 escfile = std::string( str_new );
869
870 delete[] str_new;
871
872 }
873
GetRawUrl()874 std::string CUrlParser::GetRawUrl()
875 {
876 return m_raw;
877 }
878
879 //把user,pass添加到url中
RebuildUrl(std::string & fullurl,std::string user,std::string pass,std::string & rebuild)880 bool CUrlParser::RebuildUrl(
881 std::string& fullurl, std::string user, std::string pass, std::string& rebuild )
882 {
883 std::string raw;
884
885 if ( user.empty() || pass.empty() )
886 return false;
887
888 if ( !GetRawUrl( fullurl, raw ) )
889 return false;
890
891 string::size_type pos = raw.find( "//", 0 );
892
893 if ( pos == string::npos )
894 return false;
895
896 raw.insert( pos + 2, user + ":" + pass + "@" );
897
898 rebuild = raw;
899
900 return true;
901 }
902
SetRefer(std::string & refer)903 void CUrlParser::SetRefer( std::string& refer )
904 {
905 m_refer = refer;
906 }
907
IsValidMirror()908 bool CUrlParser::IsValidMirror()
909 {
910 return m_bValidMirrorUrl;
911 }
912
UnEscape(std::string & str)913 void CUrlParser::UnEscape( std::string& str )
914 {
915 char buf[ 512 ];
916 strcpy( buf, str.c_str() );
917 UnEscape( buf );
918 str = std::string( buf );
919 }
920
UnEscape(char * s)921 void CUrlParser::UnEscape ( char *s )
922 {
923 char * t = s;
924 char *h = s;
925
926 for ( ; *h; h++, t++ )
927 {
928 if ( *h != '%' )
929 {
930 *t = *h;
931 }
932 else
933 {
934 unsigned char c;
935
936 if ( !h[ 1 ] || !h[ 2 ] || !( ISXDIGIT ( h[ 1 ] ) && ISXDIGIT ( h[ 2 ] ) ) )
937 {
938 *t = *h;
939 continue;
940 }
941
942 //c = X2DIGITS_TO_NUM (h[1], h[2]);
943 // printf("h[1]=%c,h[2]=%c\n",h[1],h[2]);
944
945 unsigned char k, m;
946
947 k = XCHAR_TO_XDIGIT( h[ 1 ] );
948
949 m = XCHAR_TO_XDIGIT( h[ 2 ] );
950
951 c = k * 16 + m;
952
953 // if (c == 0)
954 // {
955 //DBGOUT("c==0");
956 // *t=*h;
957 // continue;
958 // }
959
960 *t = c;
961
962 h += 2;
963 }
964 }
965
966 *t = '\0';
967 }
968