1 #ifndef CORELIB___NCBI_URL__HPP
2 #define CORELIB___NCBI_URL__HPP
3 
4 /*  $Id: ncbi_url.hpp 613672 2020-08-11 16:34:37Z grichenk $
5  * ===========================================================================
6  *
7  *                            PUBLIC DOMAIN NOTICE
8  *               National Center for Biotechnology Information
9  *
10  *  This software/database is a "United States Government Work" under the
11  *  terms of the United States Copyright Act.  It was written as part of
12  *  the author's official duties as a United States Government employee and
13  *  thus cannot be copyrighted.  This software/database is freely available
14  *  to the public for use. The National Library of Medicine and the U.S.
15  *  Government have not placed any restriction on its use or reproduction.
16  *
17  *  Although all reasonable efforts have been taken to ensure the accuracy
18  *  and reliability of the software and data, the NLM and the U.S.
19  *  Government do not and cannot warrant the performance or results that
20  *  may be obtained by using this software or data. The NLM and the U.S.
21  *  Government disclaim all warranties, express or implied, including
22  *  warranties of performance, merchantability or fitness for any particular
23  *  purpose.
24  *
25  *  Please cite the author in any work or product based on this material.
26  *
27  * ===========================================================================
28  *
29  * Authors: Alexey Grichenko, Vladimir Ivanov
30  *
31  * File Description:   URL parsing classes
32  *
33  */
34 
35 /// @file ncbi_url.hpp
36 ///
37 /// URL parsing classes.
38 ///
39 
40 #include <corelib/ncbi_param.hpp>
41 
42 /** @addtogroup UTIL
43  *
44  * @{
45  */
46 
47 BEGIN_NCBI_SCOPE
48 
49 /////////////////////////////////////////////////////////////////////////////
50 ///
51 /// IUrlEncoder::
52 ///
53 /// URL parts encoder/decoder interface. Used by CUrl.
54 ///
55 
56 class IUrlEncoder
57 {
58 public:
~IUrlEncoder(void)59     virtual ~IUrlEncoder(void) {}
60 
61     /// Encode user name
62     virtual string EncodeUser(const string& user) const = 0;
63     /// Decode user name
64     virtual string DecodeUser(const string& user) const = 0;
65     /// Encode password
66     virtual string EncodePassword(const string& password) const = 0;
67     /// Decode password
68     virtual string DecodePassword(const string& password) const = 0;
69     /// Encode path on server
70     virtual string EncodePath(const string& path) const = 0;
71     /// Decode path on server
72     virtual string DecodePath(const string& path) const = 0;
73     /// Encode URL argument name
74     virtual string EncodeArgName(const string& name) const = 0;
75     /// Decode URL argument name
76     virtual string DecodeArgName(const string& name) const = 0;
77     /// Encode URL argument value
78     virtual string EncodeArgValue(const string& value) const = 0;
79     /// Decode URL argument value
80     virtual string DecodeArgValue(const string& value) const = 0;
81     /// Encode fragment
82     virtual string EncodeFragment(const string& value) const = 0;
83     /// Decode fragment
84     virtual string DecodeFragment(const string& value) const = 0;
85 };
86 
87 
88 /// Primitive encoder - all methods return the argument value.
89 /// Used as base class for other encoders.
90 class NCBI_XNCBI_EXPORT CEmptyUrlEncoder : public IUrlEncoder
91 {
92 public:
EncodeUser(const string & user) const93     virtual string EncodeUser(const string& user) const
94         {  return user; }
DecodeUser(const string & user) const95     virtual string DecodeUser(const string& user) const
96         {  return user; }
EncodePassword(const string & password) const97     virtual string EncodePassword(const string& password) const
98         {  return password; }
DecodePassword(const string & password) const99     virtual string DecodePassword(const string& password) const
100         {  return password; }
EncodePath(const string & path) const101     virtual string EncodePath(const string& path) const
102         {  return path; }
DecodePath(const string & path) const103     virtual string DecodePath(const string& path) const
104         {  return path; }
EncodeArgName(const string & name) const105     virtual string EncodeArgName(const string& name) const
106         {  return name; }
DecodeArgName(const string & name) const107     virtual string DecodeArgName(const string& name) const
108         {  return name; }
EncodeArgValue(const string & value) const109     virtual string EncodeArgValue(const string& value) const
110         {  return value; }
DecodeArgValue(const string & value) const111     virtual string DecodeArgValue(const string& value) const
112         {  return value; }
EncodeFragment(const string & value) const113     virtual string EncodeFragment(const string& value) const
114         {  return value; }
DecodeFragment(const string & value) const115     virtual string DecodeFragment(const string& value) const
116         {  return value; }
117 };
118 
119 
120 /// Default encoder, uses the selected encoding for argument names/values
121 /// and eUrlEncode_Path for document path. Other parts of the URL are
122 /// not encoded.
123 class NCBI_XNCBI_EXPORT CDefaultUrlEncoder : public CEmptyUrlEncoder
124 {
125 public:
CDefaultUrlEncoder(NStr::EUrlEncode encode=NStr::eUrlEnc_SkipMarkChars)126     CDefaultUrlEncoder(NStr::EUrlEncode encode = NStr::eUrlEnc_SkipMarkChars)
127         : m_Encode(NStr::EUrlEncode(encode)) { return; }
EncodeUser(const string & user) const128     virtual string EncodeUser(const string& user) const
129         {  return NStr::URLEncode(user, NStr::eUrlEnc_URIUserinfo); }
DecodeUser(const string & user) const130     virtual string DecodeUser(const string& user) const
131         {  return NStr::URLDecode(user, NStr::eUrlDec_Percent); }
EncodePassword(const string & password) const132     virtual string EncodePassword(const string& password) const
133         {  return NStr::URLEncode(password, NStr::eUrlEnc_URIUserinfo); }
DecodePassword(const string & password) const134     virtual string DecodePassword(const string& password) const
135         {  return NStr::URLDecode(password, NStr::eUrlDec_Percent); }
EncodePath(const string & path) const136     virtual string EncodePath(const string& path) const
137         { return NStr::URLEncode(path, NStr::eUrlEnc_URIPath); }
DecodePath(const string & path) const138     virtual string DecodePath(const string& path) const
139         { return NStr::URLDecode(path); }
EncodeArgName(const string & name) const140     virtual string EncodeArgName(const string& name) const
141         { return NStr::URLEncode(name, m_Encode); }
DecodeArgName(const string & name) const142     virtual string DecodeArgName(const string& name) const
143         { return NStr::URLDecode(name,
144             m_Encode == NStr::eUrlEnc_PercentOnly ?
145             NStr::eUrlDec_Percent : NStr::eUrlDec_All); }
EncodeArgValue(const string & value) const146     virtual string EncodeArgValue(const string& value) const
147         { return NStr::URLEncode(value, m_Encode); }
DecodeArgValue(const string & value) const148     virtual string DecodeArgValue(const string& value) const
149         { return NStr::URLDecode(value,
150             m_Encode == NStr::eUrlEnc_PercentOnly ?
151             NStr::eUrlDec_Percent : NStr::eUrlDec_All); }
EncodeFragment(const string & value) const152     virtual string EncodeFragment(const string& value) const
153         { return NStr::URLEncode(value, NStr::eUrlEnc_URIFragment); }
DecodeFragment(const string & value) const154     virtual string DecodeFragment(const string& value) const
155         { return NStr::URLDecode(value, NStr::eUrlDec_Percent); }
156 private:
157     NStr::EUrlEncode m_Encode;
158 };
159 
160 
161 
162 /////////////////////////////////////////////////////////////////////////////
163 ///
164 /// CUrlArgs_Parser::
165 ///
166 /// Base class for arguments parsers.
167 ///
168 
169 class NCBI_XNCBI_EXPORT CUrlArgs_Parser
170 {
171 public:
CUrlArgs_Parser(void)172     CUrlArgs_Parser(void) : m_SemicolonIsNotArgDelimiter(false) {}
~CUrlArgs_Parser(void)173     virtual ~CUrlArgs_Parser(void) {}
174 
175     /// Parse query string, call AddArgument() to store each value.
176     void SetQueryString(const string& query, NStr::EUrlEncode encode);
177     /// Parse query string, call AddArgument() to store each value.
178     void SetQueryString(const string& query,
179                         const IUrlEncoder* encoder = 0);
180 
181     /// Treat semicolon as query string argument separator
SetSemicolonIsNotArgDelimiter(bool enable=true)182     void SetSemicolonIsNotArgDelimiter(bool enable = true)
183     {
184         m_SemicolonIsNotArgDelimiter = enable;
185     }
186 
187 protected:
188     /// Query type flag
189     enum EArgType {
190         eArg_Value, ///< Query contains name=value pairs
191         eArg_Index  ///< Query contains a list of names: name1+name2+name3
192     };
193 
194     /// Process next query argument. Must be overriden to process and store
195     /// the arguments.
196     /// @param position
197     ///   1-based index of the argument in the query.
198     /// @param name
199     ///   Name of the argument.
200     /// @param value
201     ///   Contains argument value if query type is eArg_Value or
202     ///   empty string for eArg_Index.
203     /// @param arg_type
204     ///   Query type flag.
205     virtual void AddArgument(unsigned int  position,
206                              const string& name,
207                              const string& value,
208                              EArgType      arg_type = eArg_Index) = 0;
209 private:
210     void x_SetIndexString(const string& query,
211                           const IUrlEncoder& encoder);
212 
213     bool m_SemicolonIsNotArgDelimiter;
214 };
215 
216 
217 /////////////////////////////////////////////////////////////////////////////
218 ///
219 /// CUrlArgs::
220 ///
221 /// URL arguments list.
222 ///
223 
224 class NCBI_XNCBI_EXPORT CUrlArgs : public CUrlArgs_Parser
225 {
226 public:
227     /// Create an empty arguments set.
228     CUrlArgs(void);
229     /// Parse the query string, store the arguments.
230     CUrlArgs(const string& query, NStr::EUrlEncode decode);
231     /// Parse the query string, store the arguments.
232     CUrlArgs(const string& query, const IUrlEncoder* encoder = 0);
233 
234     /// Ampersand encoding for composed URLs
235     enum EAmpEncoding {
236         eAmp_Char,   ///< Use & to separate arguments
237         eAmp_Entity  ///< Encode '&' as "&amp;"
238     };
239 
240     /// Construct and return complete query string. Use selected amp
241     /// and name/value encodings.
242     string GetQueryString(EAmpEncoding amp_enc,
243                           NStr::EUrlEncode encode) const;
244     /// Construct and return complete query string. Use selected amp
245     /// and name/value encodings.
246     string GetQueryString(EAmpEncoding amp_enc,
247                           const IUrlEncoder* encoder = 0) const;
248 
249     /// Name-value pair.
250     struct SUrlArg
251     {
SUrlArgCUrlArgs::SUrlArg252         SUrlArg(const string& aname, const string& avalue)
253             : name(aname), value(avalue) { }
254         string name;
255         string value;
256     };
257     typedef SUrlArg               TArg;
258     typedef list<TArg>            TArgs;
259     typedef TArgs::iterator       iterator;
260     typedef TArgs::const_iterator const_iterator;
261 
262     /// Check if an argument with the given name exists.
IsSetValue(const string & name) const263     bool IsSetValue(const string& name) const
264         { return FindFirst(name) != m_Args.end(); }
265 
266     /// Get value for the given name. finds first of the arguments with the
267     /// given name. If the name does not exist, is_found is set to false.
268     /// If is_found is null, CUrlArgsException is thrown.
269     const string& GetValue(const string& name, bool* is_found = 0) const;
270 
271     /// Set new value for the first argument with the given name or
272     /// add a new argument.
273     void SetValue(const string& name, const string& value);
274 
275     /// Add new value even if an argument with the same name already exists.
276     void AddValue(const string& name, const string& value);
277 
278     /// Set value, remove any other values for the name.
279     void SetUniqueValue(const string& name, const string& value);
280 
281     /// Get the const list of arguments.
GetArgs(void) const282     const TArgs& GetArgs(void) const
283         { return m_Args; }
284 
285     /// Get the list of arguments.
GetArgs(void)286     TArgs& GetArgs(void)
287         { return m_Args; }
288 
289     /// Find the first argument with the given name. If not found, return
290     /// GetArgs().end().
291     iterator FindFirst(const string& name);
292 
293     /// Take argument name from the iterator, find next argument with the same
294     /// name, return GetArgs().end() if not found.
295     iterator FindNext(const iterator& iter);
296 
297     /// Find the first argument with the given name. If not found, return
298     /// GetArgs().end().
299     const_iterator FindFirst(const string& name) const;
300 
301     /// Take argument name from the iterator, find next argument with the same
302     /// name, return GetArgs().end() if not found.
303     const_iterator FindNext(const const_iterator& iter) const;
304 
305     /// Select case sensitivity of arguments' names.
SetCase(NStr::ECase name_case)306     void SetCase(NStr::ECase name_case)
307         { m_Case = name_case; }
308 
309 protected:
310     virtual void AddArgument(unsigned int  position,
311                              const string& name,
312                              const string& value,
313                              EArgType      arg_type);
314 private:
315     iterator x_Find(const string& name, const iterator& start);
316     const_iterator x_Find(const string& name,
317                           const const_iterator& start) const;
318 
319     NStr::ECase m_Case;
320     bool        m_IsIndex;
321     TArgs       m_Args;
322 };
323 
324 
325 /////////////////////////////////////////////////////////////////////////////
326 ///
327 /// CUrl::
328 ///
329 /// URL parser. Uses CUrlArgs to parse arguments.
330 ///
331 
332 #define NCBI_SCHEME_SERVICE "ncbilb"
333 
334 class NCBI_XNCBI_EXPORT CUrl
335 {
336 public:
337     /// Default constructor
338     CUrl(void);
339 
340     /// Parse the URL.
341     ///
342     /// @param url
343     ///   String to parse as URL:
344     ///   Generic: [scheme://[[user]:[password]@]]host[:port][/path][?args]
345     ///   Special: scheme:[path]
346     ///   The leading '/', if any, is included in path value.
347     /// @param encoder
348     ///   URL encoder object. If not set, the default encoder will be used.
349     ///   @sa CDefaultUrlEncoder
350     CUrl(const string& url, const IUrlEncoder* encoder = 0);
351 
352     /// Parse the URL.
353     ///
354     /// @param url
355     ///   String to parse as URL
356     /// @param encoder
357     ///   URL encoder object. If not set, the default encoder will be used.
358     ///   @sa CDefaultUrlEncoder
359     void SetUrl(const string& url, const IUrlEncoder* encoder = 0);
360 
361     /// Compose the URL.
362     ///
363     /// @param amp_enc
364     ///   Method of encoding ampersand.
365     ///   @sa CUrlArgs::EAmpEncoding
366     /// @param encoder
367     ///   URL encoder object. If not set, the default encoder will be used.
368     ///   @sa CDefaultUrlEncoder
369     string ComposeUrl(CUrlArgs::EAmpEncoding amp_enc,
370                       const IUrlEncoder* encoder = 0) const;
371 
372     // Access parts of the URL
373 
GetScheme(void) const374     string GetScheme(void) const            { return m_Scheme; }
375     void   SetScheme(const string& value);
376 
377     /// Generic schemes use '//' prefix (after optional scheme).
GetIsGeneric(void) const378     bool GetIsGeneric(void) const           { return m_IsGeneric; }
SetIsGeneric(bool value)379     void SetIsGeneric(bool value)           { m_IsGeneric = value; }
380 
GetUser(void) const381     string GetUser(void) const              { return m_User; }
SetUser(const string & value)382     void   SetUser(const string& value)     { m_User = value; }
383 
GetPassword(void) const384     string GetPassword(void) const          { return m_Password; }
SetPassword(const string & value)385     void   SetPassword(const string& value) { m_Password = value; }
386 
GetHost(void) const387     string GetHost(void) const              { return m_Host; }
388     void   SetHost(const string& value);
389 
IsService(void) const390     bool IsService(void) const              { return !m_Service.empty(); }
GetService(void) const391     string GetService(void) const           { return m_Service; }
392     void SetService(const string& value);
393 
GetPort(void) const394     string GetPort(void) const              { return m_Port; }
SetPort(const string & value)395     void   SetPort(const string& value)     { m_Port = value; }
396 
GetPath(void) const397     string GetPath(void) const              { return m_Path; }
SetPath(const string & value)398     void   SetPath(const string& value)     { m_Path = value; }
399 
GetFragment(void) const400     string GetFragment(void) const          { return m_Fragment; }
SetFragment(const string & value)401     void   SetFragment(const string& value) { m_Fragment = value; }
402 
403     /// Get the original (unparsed and undecoded) query string
GetOriginalArgsString(void) const404     string GetOriginalArgsString(void) const
405         { return m_OrigArgs; }
406 
407     /// Check if the URL contains any arguments
HaveArgs(void) const408     bool HaveArgs(void) const
409         { return m_ArgsList.get() != 0  &&  !m_ArgsList->GetArgs().empty(); }
410 
411     /// Get const list of arguments
412     const CUrlArgs& GetArgs(void) const;
413 
414     /// Get list of arguments
415     CUrlArgs& GetArgs(void);
416 
417     CUrl(const CUrl& url);
418     CUrl& operator=(const CUrl& url);
419 
420     /// Return default URL encoder.
421     ///
422     /// @sa CDefaultUrlEncoder
423     static IUrlEncoder* GetDefaultEncoder(void);
424 
425     bool IsEmpty(void) const;
426 
427     /// Flags controlling URL adjustment.
428     /// @sa CUrl::Adjust
429     enum EAdjustFlags {
430         fUser_Replace            = 0x0001, ///< Replace user if set in 'other'
431         fUser_ReplaceIfEmpty     = 0x0002, ///< Replace user only if not yet set
432         fPassword_Replace        = 0x0004, ///< Replace password if set in 'other'
433         fPassword_ReplaceIfEmpty = 0x0008, ///< Replace password only if not yet set
434         fPath_Replace            = 0x0010, ///< Replace path
435         fPath_Append             = 0x0020, ///< Append new path to the existing one
436         fFragment_Replace        = 0x0040, ///< Replace fragment if set in 'other'
437         fFragment_ReplaceIfEmpty = 0x0080, ///< Replace fragment only if not yet set
438         fArgs_Replace            = 0x0100, ///< Discard all args, replace with args from 'other'
439         fArgs_Append             = 0x0200, ///< Append args, allow duplicate names and values
440         fArgs_Merge              = 0x0400, ///< Append new args; replace values of existing args,
441                                            ///< do not allow to set multiple values with the same name
442         fScheme_Replace          = 0x0800  ///< Replace scheme if set in 'other'
443     };
444     typedef int TAdjustFlags;
445 
446     /// Adjust this URL using information from 'other' URL.
447     /// Scheme, host and port are never changed. Other parts can be replaced or merged
448     /// depending on the flags.
449     /// Throw CUrlException if the flags are inconsistent (e.g. both fPath_Replace and fPath_Append are set).
450     void Adjust(const CUrl& other, TAdjustFlags flags);
451 
452 private:
453     // Set values with verification
454     void x_SetScheme(const string& scheme, const IUrlEncoder& encoder);
455     void x_SetUser(const string& user, const IUrlEncoder& encoder);
456     void x_SetPassword(const string& password, const IUrlEncoder& encoder);
457     void x_SetHost(const string& host, const IUrlEncoder& encoder);
458     void x_SetService(const string& service);
459     void x_SetPort(const string& port, const IUrlEncoder& encoder);
460     void x_SetPath(const string& path, const IUrlEncoder& encoder);
461     void x_SetArgs(const string& args, const IUrlEncoder& encoder);
462     void x_SetFragment(const string& fragment, const IUrlEncoder& encoder);
463 
464     bool x_IsHostPort(const string& scheme, string& unparsed, const IUrlEncoder& encoder);
465 
466     string  m_Scheme;
467     bool    m_IsGeneric;  // generic schemes include '//' delimiter
468     string  m_User;
469     string  m_Password;
470     string  m_Host;
471     string  m_Service;
472     string  m_Port;
473     string  m_Path;
474     string  m_Fragment;
475     string  m_OrigArgs;
476     unique_ptr<CUrlArgs> m_ArgsList;
477 };
478 
479 
480 /////////////////////////////////////////////////////////////////////////////
481 ///
482 /// CUrlException --
483 ///
484 ///   Exceptions to be used by CUrl.
485 ///
486 
487 class CUrlException : public CException
488 {
489 public:
490     enum EErrCode {
491         eName,       //< Argument does not exist
492         eNoArgs,     //< CUrl contains no arguments
493         eFlags       //< Inconsistent flags passed to Adjust()
494     };
GetErrCodeString(void) const495     virtual const char* GetErrCodeString(void) const override
496     {
497         switch ( GetErrCode() ) {
498         case eName:    return "Unknown argument name";
499         case eNoArgs:  return "Arguments list is empty";
500         case eFlags:   return "Inconsistent flags set";
501         default:       return CException::GetErrCodeString();
502         }
503     }
504 
505     NCBI_EXCEPTION_DEFAULT(CUrlException, CException);
506 };
507 
508 
509 /////////////////////////////////////////////////////////////////////////////
510 ///
511 /// CUrlParserException --
512 ///
513 ///   Exceptions used by the URL parser
514 
515 class CUrlParserException : public CParseTemplException<CUrlException>
516 {
517 public:
518     enum EErrCode {
519         eFormat    //< Invalid URL format
520     };
521 
GetErrCodeString(void) const522     virtual const char* GetErrCodeString(void) const override
523     {
524         switch (GetErrCode()) {
525         case eFormat:    return "Url format error";
526         default:        return CException::GetErrCodeString();
527         }
528     }
529 
530     NCBI_EXCEPTION_DEFAULT2
531     (CUrlParserException, CParseTemplException<CUrlException>,
532      std::string::size_type);
533 };
534 
535 
536 //////////////////////////////////////////////////////////////////////////////
537 //
538 // Inline functions
539 //
540 //////////////////////////////////////////////////////////////////////////////
541 
542 
543 // CUrl
544 
545 inline
SetHost(const string & host)546 void CUrl::SetHost(const string& host)
547 {
548     m_Service.clear();
549     m_Host = host;
550 }
551 
552 inline
SetService(const string & service)553 void CUrl::SetService(const string& service)
554 {
555     m_Host.clear();
556     m_Service = service;
557     m_IsGeneric = true; // services are always generic
558 }
559 
560 inline
x_SetScheme(const string & scheme,const IUrlEncoder &)561 void CUrl::x_SetScheme(const string& scheme,
562                        const IUrlEncoder& /*encoder*/)
563 {
564     m_Scheme = scheme;
565 }
566 
567 inline
x_SetUser(const string & user,const IUrlEncoder & encoder)568 void CUrl::x_SetUser(const string& user,
569                      const IUrlEncoder& encoder)
570 {
571     m_User = encoder.DecodeUser(user);
572 }
573 
574 inline
x_SetPassword(const string & password,const IUrlEncoder & encoder)575 void CUrl::x_SetPassword(const string& password,
576                          const IUrlEncoder& encoder)
577 {
578     m_Password = encoder.DecodePassword(password);
579 }
580 
581 inline
x_SetHost(const string & host,const IUrlEncoder &)582 void CUrl::x_SetHost(const string& host,
583                      const IUrlEncoder& /*encoder*/)
584 {
585     m_Host = host;
586     m_Service.clear();
587 }
588 
589 inline
x_SetService(const string & service)590 void CUrl::x_SetService(const string& service)
591 {
592     m_Service = NStr::URLDecode(service);
593 }
594 
595 inline
x_SetPort(const string & port,const IUrlEncoder &)596 void CUrl::x_SetPort(const string& port,
597                      const IUrlEncoder& /*encoder*/)
598 {
599     NStr::StringToInt(port);
600     m_Port = port;
601 }
602 
603 inline
x_SetPath(const string & path,const IUrlEncoder & encoder)604 void CUrl::x_SetPath(const string& path,
605                      const IUrlEncoder& encoder)
606 {
607     m_Path = encoder.DecodePath(path);
608 }
609 
610 inline
x_SetFragment(const string & fragment,const IUrlEncoder & encoder)611 void CUrl::x_SetFragment(const string& fragment,
612                          const IUrlEncoder& encoder)
613 {
614     m_Fragment = encoder.DecodeFragment(fragment);
615 }
616 
617 inline
x_SetArgs(const string & args,const IUrlEncoder & encoder)618 void CUrl::x_SetArgs(const string& args,
619                      const IUrlEncoder& encoder)
620 {
621     m_OrigArgs = args;
622     m_ArgsList.reset(new CUrlArgs(m_OrigArgs, &encoder));
623 }
624 
625 
626 inline
GetArgs(void)627 CUrlArgs& CUrl::GetArgs(void)
628 {
629     if ( !m_ArgsList.get() ) {
630         x_SetArgs(kEmptyStr, *GetDefaultEncoder());
631     }
632     return *m_ArgsList;
633 }
634 
635 
636 inline
FindFirst(const string & name) const637 CUrlArgs::const_iterator CUrlArgs::FindFirst(const string& name) const
638 {
639     return x_Find(name, m_Args.begin());
640 }
641 
642 
643 inline
FindFirst(const string & name)644 CUrlArgs::iterator CUrlArgs::FindFirst(const string& name)
645 {
646     return x_Find(name, m_Args.begin());
647 }
648 
649 
650 inline
FindNext(const const_iterator & iter) const651 CUrlArgs::const_iterator CUrlArgs::FindNext(const const_iterator& iter) const
652 {
653     const_iterator next = iter;
654     ++next;
655     return x_Find(iter->name, next);
656 }
657 
658 
659 inline
FindNext(const iterator & iter)660 CUrlArgs::iterator CUrlArgs::FindNext(const iterator& iter)
661 {
662     iterator next = iter;
663     ++next;
664     return x_Find(iter->name, next);
665 }
666 
667 /* @} */
668 
669 END_NCBI_SCOPE
670 
671 #endif  /* CORELIB___NCBI_URL__HPP */
672