@@ -389,6 +389,11 @@ impl ServerConfig {
389
389
}
390
390
391
391
/// Parse from [SIP002](https://github.com/shadowsocks/shadowsocks-org/issues/27) URL
392
+ ///
393
+ /// Extended formats:
394
+ ///
395
+ /// 1. QRCode URL supported by shadowsocks-android, https://github.com/shadowsocks/shadowsocks-android/issues/51
396
+ /// 2. Plain userinfo:password format supported by go2-shadowsocks2
392
397
pub fn from_url ( encoded : & str ) -> Result < ServerConfig , UrlParseError > {
393
398
let parsed = Url :: parse ( encoded) . map_err ( UrlParseError :: from) ?;
394
399
@@ -397,16 +402,71 @@ impl ServerConfig {
397
402
}
398
403
399
404
let user_info = parsed. username ( ) ;
400
- let account = match decode_config ( user_info, URL_SAFE_NO_PAD ) {
401
- Ok ( account) => match String :: from_utf8 ( account) {
402
- Ok ( ac) => ac,
403
- Err ( ..) => {
404
- return Err ( UrlParseError :: InvalidAuthInfo ) ;
405
+ if user_info. is_empty ( ) {
406
+ // This maybe a QRCode URL, which is ss://BASE64-URL-ENCODE(pass:encrypt@hostname:port)
407
+
408
+ let encoded = match parsed. host_str ( ) {
409
+ Some ( e) => e,
410
+ None => return Err ( UrlParseError :: MissingHost ) ,
411
+ } ;
412
+
413
+ let mut decoded_body = match decode_config ( encoded, URL_SAFE_NO_PAD ) {
414
+ Ok ( b) => match String :: from_utf8 ( b) {
415
+ Ok ( b) => b,
416
+ Err ( ..) => return Err ( UrlParseError :: InvalidServerAddr ) ,
417
+ } ,
418
+ Err ( err) => {
419
+ error ! ( "failed to parse legacy ss://ENCODED with Base64, err: {}" , err) ;
420
+ return Err ( UrlParseError :: InvalidServerAddr ) ;
405
421
}
406
- } ,
407
- Err ( err) => {
408
- error ! ( "Failed to parse UserInfo with Base64, err: {}" , err) ;
409
- return Err ( UrlParseError :: InvalidUserInfo ) ;
422
+ } ;
423
+
424
+ decoded_body. insert_str ( 0 , "ss://" ) ;
425
+ // Parse it like ss://method:password@host:port
426
+ return ServerConfig :: from_url ( & decoded_body) ;
427
+ }
428
+
429
+ let ( method, pwd) = match parsed. password ( ) {
430
+ Some ( password) => {
431
+ // Plain method:password without base64 encoded
432
+
433
+ let m = match percent_encoding:: percent_decode_str ( user_info) . decode_utf8 ( ) {
434
+ Ok ( m) => m,
435
+ Err ( err) => {
436
+ error ! ( "failed to parse percent-encoding method in userinfo, err: {}" , err) ;
437
+ return Err ( UrlParseError :: InvalidAuthInfo ) ;
438
+ }
439
+ } ;
440
+
441
+ let p = match percent_encoding:: percent_decode_str ( password) . decode_utf8 ( ) {
442
+ Ok ( m) => m,
443
+ Err ( err) => {
444
+ error ! ( "failed to parse percent-encoding password in userinfo, err: {}" , err) ;
445
+ return Err ( UrlParseError :: InvalidAuthInfo ) ;
446
+ }
447
+ } ;
448
+
449
+ ( m, p)
450
+ }
451
+ None => {
452
+ let account = match decode_config ( user_info, URL_SAFE_NO_PAD ) {
453
+ Ok ( account) => match String :: from_utf8 ( account) {
454
+ Ok ( ac) => ac,
455
+ Err ( ..) => return Err ( UrlParseError :: InvalidAuthInfo ) ,
456
+ } ,
457
+ Err ( err) => {
458
+ error ! ( "failed to parse UserInfo with Base64, err: {}" , err) ;
459
+ return Err ( UrlParseError :: InvalidUserInfo ) ;
460
+ }
461
+ } ;
462
+
463
+ let mut sp2 = account. splitn ( 2 , ':' ) ;
464
+ let ( m, p) = match ( sp2. next ( ) , sp2. next ( ) ) {
465
+ ( Some ( m) , Some ( p) ) => ( m, p) ,
466
+ _ => return Err ( UrlParseError :: InvalidUserInfo ) ,
467
+ } ;
468
+
469
+ ( m. to_owned ( ) . into ( ) , p. to_owned ( ) . into ( ) )
410
470
}
411
471
} ;
412
472
@@ -418,28 +478,22 @@ impl ServerConfig {
418
478
let port = parsed. port ( ) . unwrap_or ( 8388 ) ;
419
479
let addr = format ! ( "{}:{}" , host, port) ;
420
480
421
- let mut sp2 = account. splitn ( 2 , ':' ) ;
422
- let ( method, pwd) = match ( sp2. next ( ) , sp2. next ( ) ) {
423
- ( Some ( m) , Some ( p) ) => ( m, p) ,
424
- _ => return Err ( UrlParseError :: InvalidUserInfo ) ,
425
- } ;
426
-
427
481
let addr = match addr. parse :: < ServerAddr > ( ) {
428
482
Ok ( a) => a,
429
483
Err ( err) => {
430
- error ! ( "Failed to parse \" {}\" to ServerAddr, err: {:?}" , addr, err) ;
484
+ error ! ( "failed to parse \" {}\" to ServerAddr, err: {:?}" , addr, err) ;
431
485
return Err ( UrlParseError :: InvalidServerAddr ) ;
432
486
}
433
487
} ;
434
488
435
489
let method = method. parse ( ) . expect ( "method" ) ;
436
- let mut svrconfig = ServerConfig :: new ( addr, pwd. to_owned ( ) , method) ;
490
+ let mut svrconfig = ServerConfig :: new ( addr, pwd, method) ;
437
491
438
492
if let Some ( q) = parsed. query ( ) {
439
493
let query = match serde_urlencoded:: from_bytes :: < Vec < ( String , String ) > > ( q. as_bytes ( ) ) {
440
494
Ok ( q) => q,
441
495
Err ( err) => {
442
- error ! ( "Failed to parse QueryString, err: {}" , err) ;
496
+ error ! ( "failed to parse QueryString, err: {}" , err) ;
443
497
return Err ( UrlParseError :: InvalidQueryString ) ;
444
498
}
445
499
} ;
@@ -464,6 +518,10 @@ impl ServerConfig {
464
518
}
465
519
}
466
520
521
+ if let Some ( frag) = parsed. fragment ( ) {
522
+ svrconfig. set_remarks ( frag) ;
523
+ }
524
+
467
525
Ok ( svrconfig)
468
526
}
469
527
0 commit comments